2022-05-30 11:00:50 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# ifdef NEW_DIRECTLINK_PLUGIN
# include "DatasmithMaxDirectLink.h"
# include "DatasmithSceneFactory.h"
# include "DatasmithMesh.h"
# include "DatasmithUtils.h"
# include "DatasmithMaxClassIDs.h"
# include "DatasmithMaxMeshExporter.h"
# include "DatasmithMaxSceneExporter.h"
# include "DatasmithMaxAttributes.h"
# include "DatasmithMaxSceneParser.h"
# include "DatasmithMaxLogger.h"
# include "Logging/LogMacros.h"
# include "Windows/AllowWindowsPlatformTypes.h"
MAX_INCLUDES_START
# include "max.h"
# include "modstack.h"
# include "iparamb2.h"
# include "MeshNormalSpec.h"
MAX_INCLUDES_END
namespace DatasmithMaxDirectLink
{
namespace GeomUtils
{
class FNullView : public View
{
public :
FNullView ( )
{
worldToView . IdentityMatrix ( ) ; screenW = 640.0f ; screenH = 480.0f ;
}
virtual Point2 ViewToScreen ( Point3 p ) override
{
return Point2 ( p . x , p . y ) ;
}
} ;
FRenderMeshForConversion GetMeshForGeomObject ( TimeValue CurrentTime , INode * Node , const FTransform & Pivot )
{
// todo: baseline exporter uses GetBaseObject which takes result of EvalWorldState
// and searched down DerivedObject pipeline(by taking GetObjRef)
// This is STRANGE as EvalWorldState shouldn't return DerivedObject in the first place(it should return result of pipeline evaluation)
BOOL bNeedsDelete ;
Interval ValidityInterval ;
ValidityInterval . SetInfinite ( ) ;
Mesh * RenderMesh = GetMeshFromRenderMesh ( CurrentTime , Node , ValidityInterval , bNeedsDelete ) ;
FRenderMeshForConversion Result ( Node , RenderMesh , bNeedsDelete ) ;
Result . SetValidityInterval ( ValidityInterval ) ;
Result . SetPivot ( Pivot ) ;
return MoveTemp ( Result ) ;
}
/** Convert Max to UE coordinates, handle scene master unit
* @ param MaxTransform source transform
* @ param UnitMultiplier Master scene unit
*/
FTransform FTransformFromMatrix3 ( const Matrix3 & MaxTransform , float UnitMultiplier )
{
FVector Translation ;
FQuat Rotation ;
FVector Scale ;
FDatasmithMaxSceneExporter : : MaxToUnrealCoordinates ( MaxTransform , Translation , Rotation , Scale , UnitMultiplier ) ;
return FTransform ( Rotation , Translation , Scale ) ;
}
INode * GetCollisionNode ( ISceneTracker & SceneTracker , INode * Node , const FDatasmithMaxStaticMeshAttributes * DatasmithAttributes , bool & bOutFromDatasmithAttribute )
{
if ( DatasmithAttributes )
{
INode * ModifierSpecifiedCustomCollisionNode = DatasmithAttributes - > GetCustomCollisonNode ( ) ;
if ( ModifierSpecifiedCustomCollisionNode )
{
bOutFromDatasmithAttribute = true ;
return ModifierSpecifiedCustomCollisionNode ;
}
}
FString OriginalName = Node - > GetName ( ) ;
for ( const FString & CollisionNodePrefix : FDatasmithMaxSceneParser : : CollisionNodesPrefixes )
{
if ( FNodeTracker * CollisionNode = SceneTracker . GetNodeTrackerByNodeName ( * ( CollisionNodePrefix + TEXT ( " _ " ) + OriginalName ) ) )
{
bOutFromDatasmithAttribute = false ;
return CollisionNode - > Node ;
}
}
return nullptr ;
}
FRenderMeshForConversion GetMeshForCollision ( TimeValue CurrentTime , ISceneTracker & SceneTracker , INode * Node , bool bBakePivot )
{
// source: FDatasmithMaxMeshExporter::ExportMesh
FDatasmithConverter Converter ;
bool bIsCollisionFromDatasmithAttributes ;
TOptional < FDatasmithMaxStaticMeshAttributes > DatasmithAttributes = FDatasmithMaxStaticMeshAttributes : : ExtractStaticMeshAttributes ( Node ) ;
INode * CollisionNode = GetCollisionNode ( SceneTracker , Node , DatasmithAttributes ? & DatasmithAttributes . GetValue ( ) : nullptr , bIsCollisionFromDatasmithAttributes ) ;
FTransform CollisionPivot ;
if ( CollisionNode )
{
FTransform ColliderPivot = FDatasmithMaxSceneExporter : : GetPivotTransform ( CollisionNode , Converter . UnitToCentimeter ) ;
if ( bIsCollisionFromDatasmithAttributes )
{
if ( ! bBakePivot )
{
FTransform RealPivot = FDatasmithMaxSceneExporter : : GetPivotTransform ( Node , Converter . UnitToCentimeter ) ;
ColliderPivot = ColliderPivot * RealPivot . Inverse ( ) ;
}
CollisionPivot = ColliderPivot ;
}
else
{
FTransform FTransformFromMatrix3 ( const Matrix3 & MaxTransform , float UnitMultiplier ) ; // todo: move to header and rename(F is for class!)
FTransform NodeWTM = FTransformFromMatrix3 ( Node - > GetNodeTM ( CurrentTime ) , Converter . UnitToCentimeter ) ;
FTransform ColliderNodeWTM = FTransformFromMatrix3 ( CollisionNode - > GetNodeTM ( CurrentTime ) , Converter . UnitToCentimeter ) ;
// if object-offset has been baked into the mesh data, we want collision mesh data in the mesh's node space
// MeshVert_Node = RealPivot * max_vert_data
// MeshVert_world = NodeWTM * MeshVert_Node
// Collision mesh vertices in world space:
// CollVert_node = ColliderPivot * CollVert_obj
// CollVert_world = ColliderNodeWTM * CollVert_node
// CollVert_mesh = NodeWTM-1 * CollVert_world
FTransform BakedTransform = ColliderPivot * ColliderNodeWTM * NodeWTM . Inverse ( ) ;
if ( ! bBakePivot )
{
// if object-offset has not been baked, we want collision mesh data in the mesh's object space
FTransform RealPivot = FDatasmithMaxSceneExporter : : GetPivotTransform ( Node , Converter . UnitToCentimeter ) ;
BakedTransform = BakedTransform * RealPivot . Inverse ( ) ;
}
CollisionPivot = BakedTransform ;
}
}
if ( ! CollisionNode )
{
return FRenderMeshForConversion ( ) ;
}
return GetMeshForGeomObject ( CurrentTime , CollisionNode , CollisionPivot ) ;
}
// todo: copied from baseline plugin(it has dependencies on converters that are not static in FDatasmithMaxMeshExporter)
void FillDatasmithMeshFromMaxMesh ( FDatasmithMesh & DatasmithMesh , Mesh & MaxMesh , INode * ExportedNode , bool bForceSingleMat , TSet < uint16 > & SupportedChannels , TMap < int32 , int32 > & UVChannelsMap , FTransform Pivot )
{
FDatasmithConverter Converter ;
const int NumFaces = MaxMesh . getNumFaces ( ) ;
const int NumVerts = MaxMesh . getNumVerts ( ) ;
DatasmithMesh . SetVerticesCount ( NumVerts ) ;
DatasmithMesh . SetFacesCount ( NumFaces ) ;
// Vertices
for ( int i = 0 ; i < NumVerts ; i + + )
{
Point3 Point = MaxMesh . getVert ( i ) ;
FVector Vertex = Converter . toDatasmithVector ( Point ) ;
Vertex = Pivot . TransformPosition ( Vertex ) ; // Bake object-offset in the mesh data when possible
DatasmithMesh . SetVertex ( i , Vertex . X , Vertex . Y , Vertex . Z ) ;
}
// Vertex Colors
if ( MaxMesh . curVCChan = = 0 & & MaxMesh . numCVerts > 0 )
{
// Default vertex color channel
for ( int32 i = 0 ; i < NumFaces ; i + + )
{
TVFace & Face = MaxMesh . vcFace [ i ] ;
DatasmithMesh . SetVertexColor ( i * 3 , Converter . toDatasmithColor ( MaxMesh . vertCol [ Face . t [ 0 ] ] ) ) ;
DatasmithMesh . SetVertexColor ( i * 3 + 1 , Converter . toDatasmithColor ( MaxMesh . vertCol [ Face . t [ 1 ] ] ) ) ;
DatasmithMesh . SetVertexColor ( i * 3 + 2 , Converter . toDatasmithColor ( MaxMesh . vertCol [ Face . t [ 2 ] ] ) ) ;
}
}
// UVs
TMap < uint32 , int32 > HashToChannel ;
bool bIsFirstUVChannelValid = true ;
for ( int32 i = 1 ; i < = MaxMesh . getNumMaps ( ) ; + + i )
{
if ( MaxMesh . mapSupport ( i ) = = BOOL ( true ) & & MaxMesh . getNumMapVerts ( i ) > 0 )
{
DatasmithMesh . AddUVChannel ( ) ;
const int32 UVChannelIndex = DatasmithMesh . GetUVChannelsCount ( ) - 1 ;
const int32 UVsCount = MaxMesh . getNumMapVerts ( i ) ;
DatasmithMesh . SetUVCount ( UVChannelIndex , UVsCount ) ;
UVVert * Vertex = MaxMesh . mapVerts ( i ) ;
for ( int32 j = 0 ; j < UVsCount ; + + j )
{
const UVVert & MaxUV = Vertex [ j ] ;
DatasmithMesh . SetUV ( UVChannelIndex , j , MaxUV . x , 1.f - MaxUV . y ) ;
}
TVFace * Faces = MaxMesh . mapFaces ( i ) ;
for ( int32 j = 0 ; j < MaxMesh . getNumFaces ( ) ; + + j )
{
DatasmithMesh . SetFaceUV ( j , UVChannelIndex , Faces [ j ] . t [ 0 ] , Faces [ j ] . t [ 1 ] , Faces [ j ] . t [ 2 ] ) ;
}
if ( UVChannelIndex = = 0 )
{
//Verifying that the UVs are properly unfolded, which is required to calculate the tangent in unreal.
bIsFirstUVChannelValid = FDatasmithMeshUtils : : IsUVChannelValid ( DatasmithMesh , UVChannelIndex ) ;
}
uint32 Hash = DatasmithMesh . GetHashForUVChannel ( UVChannelIndex ) ;
int32 * PointerToChannel = HashToChannel . Find ( Hash ) ;
if ( PointerToChannel )
{
// Remove the channel because there is another one that is identical
DatasmithMesh . RemoveUVChannel ( ) ;
// Map the user-specified UV Channel (in 3dsmax) to the actual UV channel that will be exported to Unreal
UVChannelsMap . Add ( i - 1 , * PointerToChannel ) ;
}
else
{
// Map the user-specified UV Channel (in 3dsmax) to the actual UV channel that will be exported to Unreal
UVChannelsMap . Add ( i - 1 , UVChannelIndex ) ;
HashToChannel . Add ( Hash , UVChannelIndex ) ;
}
}
}
if ( ! bIsFirstUVChannelValid )
{
//DatasmithMaxLogger::Get().AddGeneralError(*FString::Printf(TEXT("%s's UV channel #1 contains degenerated triangles, this can cause issues in Unreal. It is recommended to properly unfold and flatten exported UV data.")
// , static_cast<const TCHAR*>(ExportedNode->GetName())));
}
// Faces
for ( int i = 0 ; i < NumFaces ; i + + )
{
// Create polygons. Assign texture and texture UV indices.
// all faces of the cube have the same texture
Face & MaxFace = MaxMesh . faces [ i ] ;
int MaterialId = bForceSingleMat ? 0 : MaxFace . getMatID ( ) ;
SupportedChannels . Add ( MaterialId ) ;
//Max's channel UI is not zero-based, so we register an incremented ChannelID for better visual consistency after importing in Unreal.
DatasmithMesh . SetFace ( i , MaxFace . getVert ( 0 ) , MaxFace . getVert ( 1 ) , MaxFace . getVert ( 2 ) , MaterialId + 1 ) ;
DatasmithMesh . SetFaceSmoothingMask ( i , ( uint32 ) MaxFace . getSmGroup ( ) ) ;
}
//Normals
MaxMesh . SpecifyNormals ( ) ;
MeshNormalSpec * Normal = MaxMesh . GetSpecifiedNormals ( ) ;
Normal - > MakeNormalsExplicit ( false ) ;
Normal - > CheckNormals ( ) ;
Matrix3 RotationMatrix ;
RotationMatrix . IdentityMatrix ( ) ;
Quat ObjectOffsetRotation = ExportedNode - > GetObjOffsetRot ( ) ;
RotateMatrix ( RotationMatrix , ObjectOffsetRotation ) ;
Point3 Point ;
for ( int i = 0 ; i < NumFaces ; i + + )
{
Point = Normal - > GetNormal ( i , 0 ) . Normalize ( ) * RotationMatrix ;
FVector NormalVector = Converter . toDatasmithVector ( Point ) ;
DatasmithMesh . SetNormal ( i * 3 , NormalVector . X , NormalVector . Y , NormalVector . Z ) ;
Point = Normal - > GetNormal ( i , 1 ) . Normalize ( ) * RotationMatrix ;
NormalVector = Converter . toDatasmithVector ( Point ) ;
DatasmithMesh . SetNormal ( i * 3 + 1 , NormalVector . X , NormalVector . Y , NormalVector . Z ) ;
Point = Normal - > GetNormal ( i , 2 ) . Normalize ( ) * RotationMatrix ;
NormalVector = Converter . toDatasmithVector ( Point ) ;
DatasmithMesh . SetNormal ( i * 3 + 2 , NormalVector . X , NormalVector . Y , NormalVector . Z ) ;
}
}
void SetNormalForAFace ( FDatasmithMesh & DatasmithMesh , int32 Index , const FVector & NormalVector )
{
DatasmithMesh . SetNormal ( Index * 3 , NormalVector . X , NormalVector . Y , NormalVector . Z ) ;
DatasmithMesh . SetNormal ( Index * 3 + 1 , NormalVector . X , NormalVector . Y , NormalVector . Z ) ;
DatasmithMesh . SetNormal ( Index * 3 + 2 , NormalVector . X , NormalVector . Y , NormalVector . Z ) ;
}
bool FillDatasmithMeshFromBoundingBox ( TimeValue CurrentTime , FDatasmithMesh & DatasmithMesh , const GeomUtils : : FRenderMeshForConversion & MaxMesh )
{
if ( ! MaxMesh . GetNode ( ) )
{
return false ;
}
FDatasmithConverter Converter ;
if ( ! MaxMesh . IsValid ( ) )
{
return false ;
}
if ( MaxMesh . GetMesh ( ) - > getNumFaces ( ) = = 0 )
{
return false ;
}
if ( MaxMesh . GetMesh ( ) - > getBoundingBox ( ) . IsEmpty ( ) )
{
MaxMesh . GetMesh ( ) - > buildBoundingBox ( ) ;
}
Box3 BoundingBox = MaxMesh . GetMesh ( ) - > getBoundingBox ( ) ;
DatasmithMesh . SetVerticesCount ( 8 ) ;
DatasmithMesh . SetFacesCount ( 12 ) ;
for ( int32 i = 0 ; i < 8 ; i + + )
{
FVector Vertex = Converter . toDatasmithVector ( BoundingBox [ i ] ) ;
Vertex = MaxMesh . GetPivot ( ) . TransformPosition ( Vertex ) ;
DatasmithMesh . SetVertex ( i , Vertex . X , Vertex . Y , Vertex . Z ) ;
}
// Make a cube from the vertices
Matrix3 RotationMatrix ;
RotationMatrix . IdentityMatrix ( ) ;
Quat ObjectOffsetRotation = MaxMesh . GetNode ( ) - > GetObjOffsetRot ( ) ;
RotateMatrix ( RotationMatrix , ObjectOffsetRotation ) ;
Point3 Point ;
FVector NormalVector ;
// Points toward negative z (without a transform and in max coordinates)
DatasmithMesh . SetFace ( 0 , 1 , 0 , 2 ) ;
DatasmithMesh . SetFace ( 1 , 1 , 2 , 3 ) ;
Point = Point3 ( 0.f , 0.f , - 1.f ) * RotationMatrix ;
NormalVector = Converter . toDatasmithNormal ( Point ) ;
SetNormalForAFace ( DatasmithMesh , 0 , NormalVector ) ;
SetNormalForAFace ( DatasmithMesh , 1 , NormalVector ) ;
// Points toward positive y (without a transform and in max coordinates)
DatasmithMesh . SetFace ( 2 , 3 , 2 , 7 ) ;
DatasmithMesh . SetFace ( 3 , 2 , 6 , 7 ) ;
Point = Point3 ( 0.f , 1.f , 0.f ) * RotationMatrix ;
NormalVector = Converter . toDatasmithNormal ( Point ) ;
SetNormalForAFace ( DatasmithMesh , 2 , NormalVector ) ;
SetNormalForAFace ( DatasmithMesh , 3 , NormalVector ) ;
// Points toward positive x (without a transform and in max coordinates)
DatasmithMesh . SetFace ( 4 , 1 , 3 , 7 ) ;
DatasmithMesh . SetFace ( 5 , 5 , 1 , 7 ) ;
Point = Point3 ( 1.f , 0.f , 0.f ) * RotationMatrix ;
NormalVector = Converter . toDatasmithNormal ( Point ) ;
SetNormalForAFace ( DatasmithMesh , 4 , NormalVector ) ;
SetNormalForAFace ( DatasmithMesh , 5 , NormalVector ) ;
// Points toward negative x (without a transform and in max coordinates)
DatasmithMesh . SetFace ( 6 , 2 , 0 , 6 ) ;
DatasmithMesh . SetFace ( 7 , 0 , 4 , 6 ) ;
Point = Point3 ( - 1.f , 0.f , 0.f ) * RotationMatrix ;
NormalVector = Converter . toDatasmithNormal ( Point ) ;
SetNormalForAFace ( DatasmithMesh , 6 , NormalVector ) ;
SetNormalForAFace ( DatasmithMesh , 7 , NormalVector ) ;
// Points toward negative y (without a transform and in max coordinates)
DatasmithMesh . SetFace ( 8 , 0 , 1 , 5 ) ;
DatasmithMesh . SetFace ( 9 , 4 , 0 , 5 ) ;
Point = Point3 ( 0.f , - 1.f , 0.f ) * RotationMatrix ;
NormalVector = Converter . toDatasmithNormal ( Point ) ;
SetNormalForAFace ( DatasmithMesh , 8 , NormalVector ) ;
SetNormalForAFace ( DatasmithMesh , 9 , NormalVector ) ;
// Points toward positive z (without a transform and in max coordinates)
DatasmithMesh . SetFace ( 10 , 4 , 5 , 6 ) ;
DatasmithMesh . SetFace ( 11 , 6 , 5 , 7 ) ;
Point = Point3 ( 0.f , 0.f , 1.f ) * RotationMatrix ;
NormalVector = Converter . toDatasmithNormal ( Point ) ;
SetNormalForAFace ( DatasmithMesh , 10 , NormalVector ) ;
SetNormalForAFace ( DatasmithMesh , 11 , NormalVector ) ;
return true ;
}
bool CreateDatasmithMeshFromMaxMesh ( FDatasmithMesh & DatasmithMesh , const MeshConversionParams & Params , TSet < uint16 > & SupportedChannels , TMap < int32 , int32 > & UVChannelsMap )
{
bool bResult = false ;
if ( Params . RenderMesh . GetMesh ( ) - > getNumFaces ( ) )
{
// Copy mesh to clean it before filling Datasmith mesh from it
Mesh CachedMesh ;
CachedMesh . DeepCopy ( Params . RenderMesh . GetMesh ( ) , TOPO_CHANNEL | GEOM_CHANNEL | TEXMAP_CHANNEL | VERTCOLOR_CHANNEL ) ;
CachedMesh . DeleteIsoVerts ( ) ;
CachedMesh . RemoveDegenerateFaces ( ) ;
CachedMesh . RemoveIllegalFaces ( ) ;
// Need to invalidate/rebuild strips/edges after topology change(removing bad verts/faces)
CachedMesh . InvalidateStrips ( ) ;
CachedMesh . BuildStripsAndEdges ( ) ;
if ( CachedMesh . getNumFaces ( ) > 0 )
{
FillDatasmithMeshFromMaxMesh ( DatasmithMesh , CachedMesh , Params . Node , Params . bConsolidateMaterialIds , SupportedChannels , UVChannelsMap , Params . RenderMesh . GetPivot ( ) ) ;
bResult = true ; // Set to true, don't care what ExportToUObject does here - we need to move it to a thread anyway
}
CachedMesh . FreeAll ( ) ;
}
return bResult ;
}
// todo: copied from DatasmithMaxMeshExporter.cpp
Object * GetBaseObject ( INode * Node , TimeValue Time )
{
ObjectState ObjState = Node - > EvalWorldState ( Time ) ;
Object * Obj = ObjState . obj ;
if ( Obj )
{
SClass_ID SuperClassID ;
SuperClassID = Obj - > SuperClassID ( ) ;
while ( SuperClassID = = GEN_DERIVOB_CLASS_ID )
{
Obj = ( ( IDerivedObject * ) Obj ) - > GetObjRef ( ) ;
SuperClassID = Obj - > SuperClassID ( ) ;
}
}
return Obj ;
}
# define VRAY_PROXY_DISPLAY_AS_MESH 4 // Value to set on a VRay Mesh Proxy to get the mesh
int SetObjectParamValue ( TimeValue CurrentTime , Object * Obj , const FString & ParamName , int DesiredValue )
{
bool bFoundDisplayValue = false ;
int PrevDisplayValue = DesiredValue ; // Display value to see mesh
int NumParamBlocks = Obj - > NumParamBlocks ( ) ;
for ( short BlockIndex = 0 ; BlockIndex < NumParamBlocks & & ! bFoundDisplayValue ; + + BlockIndex )
{
IParamBlock2 * ParamBlock2 = Obj - > GetParamBlockByID ( BlockIndex ) ;
ParamBlockDesc2 * ParamBlockDesc = ParamBlock2 - > GetDesc ( ) ;
for ( int ParamIndex = 0 ; ParamIndex < ParamBlockDesc - > count ; + + ParamIndex )
{
ParamDef & ParamDefinition = ParamBlockDesc - > paramdefs [ ParamIndex ] ;
if ( FCString : : Stricmp ( ParamDefinition . int_name , * ParamName ) = = 0 )
{
PrevDisplayValue = ParamBlock2 - > GetInt ( ParamDefinition . ID , CurrentTime ) ;
if ( PrevDisplayValue ! = DesiredValue )
{
ParamBlock2 - > SetValue ( ParamDefinition . ID , CurrentTime , DesiredValue ) ;
}
bFoundDisplayValue = true ;
break ;
}
}
ParamBlock2 - > ReleaseDesc ( ) ;
}
return PrevDisplayValue ;
}
Mesh * GetMeshFromRenderMesh ( TimeValue CurrentTime , INode * Node , Interval & ValidityInterval , BOOL & bNeedsDelete )
{
Object * Obj = GetBaseObject ( Node , CurrentTime ) ;
// Read validity interval before changing display settings
// Else VRay will somehow react to a call to ObjectValidity and change RenderMesh contents to what it was originally.
// E.g. display was set 'Box', we change display to 'Mesh' here before calling GetRenderMesh to retrieve actual mesh later(rather than a simple box)
// but if ObjectValidity is called after GetRenderMesh that RenderMesh will become box again!
Interval ObjectValidity = Obj - > ObjectValidity ( CurrentTime ) ;
const Class_ID & ObjectClassID = Obj - > ClassID ( ) ;
const FString VRayProxyParamName ( TEXT ( " display " ) ) ;
const FString BodyObjectViewportMeshParamName ( TEXT ( " RenderViewportMeshRA " ) ) ;
int PreviousMeshDisplayValue = 0 ;
if ( ObjectClassID = = VRAYPROXY_CLASS_ID )
{
// Need the high resolution render mesh associated with the VRay Mesh Proxy for the export
PreviousMeshDisplayValue = SetObjectParamValue ( CurrentTime , Obj , VRayProxyParamName , VRAY_PROXY_DISPLAY_AS_MESH ) ;
}
else if ( ObjectClassID = = BODYOBJECT_CLASS_ID )
{
// Need to make sure we are using the viewport mesh on BodyObject, otherwise the RenderMesh gives a tessellated low resolution mesh.
PreviousMeshDisplayValue = SetObjectParamValue ( CurrentTime , Obj , BodyObjectViewportMeshParamName , 1 ) ;
}
GeomObject * GeomObj = static_cast < GeomObject * > ( Obj ) ;
if ( GeomObj = = nullptr )
{
return nullptr ;
}
FNullView View ;
Mesh * RenderMesh = GeomObj - > GetRenderMesh ( CurrentTime , Node , View , bNeedsDelete ) ;
// Restore display state if different from mesh display
if ( ObjectClassID = = VRAYPROXY_CLASS_ID & & PreviousMeshDisplayValue ! = VRAY_PROXY_DISPLAY_AS_MESH )
{
SetObjectParamValue ( CurrentTime , Obj , VRayProxyParamName , PreviousMeshDisplayValue ) ;
}
else if ( ObjectClassID = = BODYOBJECT_CLASS_ID & & PreviousMeshDisplayValue ! = 1 )
{
SetObjectParamValue ( CurrentTime , Obj , BodyObjectViewportMeshParamName , PreviousMeshDisplayValue ) ;
}
if ( RenderMesh )
{
ValidityInterval & = ObjectValidity ; // Update validity only when actual mesh is returned
}
return RenderMesh ;
}
}
}
namespace DatasmithMaxDirectLink
{
// todo: paralelize calls to ExportToUObject
bool ConvertMaxMeshToDatasmith ( TimeValue CurrentTime , ISceneTracker & Scene , FMeshConverterSource & MeshSource , FMeshConverted & MeshConverted )
{
TOptional < FDatasmithMaxStaticMeshAttributes > DatasmithAttributes = FDatasmithMaxStaticMeshAttributes : : ExtractStaticMeshAttributes ( MeshSource . Node ) ;
MeshConverted . DatasmithMeshElement = FDatasmithSceneFactory : : CreateMesh ( * MeshSource . MeshName ) ;
if ( DatasmithAttributes & & DatasmithAttributes - > GetExportMode ( ) = = EStaticMeshExportMode : : BoundingBox )
{
FDatasmithMesh DatasmithMesh ;
if ( GeomUtils : : FillDatasmithMeshFromBoundingBox ( CurrentTime , DatasmithMesh , MeshSource . RenderMesh ) )
{
Scene . AddMeshElement ( MeshConverted . DatasmithMeshElement , DatasmithMesh , nullptr ) ;
return true ;
}
else
{
LogWarning ( FString ( TEXT ( " Invalid object: " ) ) + MeshSource . Node - > GetName ( ) ) ;
return false ;
}
}
FDatasmithMesh DatasmithMesh ;
MeshConversionParams RenderMeshParams = {
MeshSource . RenderMesh . GetNode ( ) ,
MeshSource . RenderMesh ,
MeshSource . bConsolidateMaterialIds
} ;
if ( ! GeomUtils : : CreateDatasmithMeshFromMaxMesh ( DatasmithMesh , RenderMeshParams , MeshConverted . SupportedChannels , MeshConverted . UVChannelsMap ) )
{
LogWarning ( FString ( TEXT ( " Invalid object: " ) ) + MeshSource . Node - > GetName ( ) ) ;
return false ;
}
// Mapping between the 3ds max channel and the exported mesh channel
if ( DatasmithAttributes )
{
constexpr int32 MaxToUnrealUVOffset = - 1 ;
constexpr int32 DefaultValue = - 1 ;
const int32 SelectedLightmapUVChannel = DatasmithAttributes - > GetLightmapUVChannel ( ) ;
const int32 * ExportedSelectedChannel = MeshConverted . UVChannelsMap . Find ( SelectedLightmapUVChannel + MaxToUnrealUVOffset ) ;
if ( ExportedSelectedChannel )
{
MeshConverted . DatasmithMeshElement - > SetLightmapCoordinateIndex ( * ExportedSelectedChannel ) ;
}
else if ( SelectedLightmapUVChannel ! = DefaultValue )
{
LogWarning ( * FString : : Printf ( TEXT ( " %s won't use the channel %i for its lightmap because it's not supported by the mesh. A new channel will be generated. " )
, static_cast < const TCHAR * > ( MeshSource . Node - > GetName ( ) )
, SelectedLightmapUVChannel ) ) ;
}
}
FDatasmithMesh * DatasmithCollisionMeshPtr = nullptr ;
FDatasmithMesh DatasmithCollisionMesh ;
if ( MeshSource . CollisionMesh . IsValid ( ) )
{
TSet < uint16 > SupportedChannelsDummy ; // ignore map channels for collision mesh
TMap < int32 , int32 > UVChannelsMapDummy ;
MeshConversionParams CollisionParams = {
MeshSource . CollisionMesh . GetNode ( ) ,
MeshSource . CollisionMesh ,
true // Consolidate material ids into single mesh for collision
} ;
if ( GeomUtils : : CreateDatasmithMeshFromMaxMesh ( DatasmithCollisionMesh , CollisionParams , SupportedChannelsDummy , UVChannelsMapDummy ) )
{
DatasmithCollisionMeshPtr = & DatasmithCollisionMesh ;
}
}
Scene . AddMeshElement ( MeshConverted . DatasmithMeshElement , DatasmithMesh , DatasmithCollisionMeshPtr ) ;
return true ;
}
}
# include "Windows/HideWindowsPlatformTypes.h"
# endif // NEW_DIRECTLINK_PLUGIN