You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
- fixes crash when syncing/exportign while a thirdparty rendering plugin is active(like Corena interactive) #jira UETOOL-5109 #preflight 6281cf06e20f09e89463db2b #rb benoit.deschenes [CL 20238823 by kerim borchaev in ue5-main branch]
899 lines
24 KiB
C++
899 lines
24 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "DatasmithMaxSceneParser.h"
|
|
|
|
#include "DatasmithMaxAttributes.h"
|
|
#include "DatasmithMaxSceneExporter.h"
|
|
#include "DatasmithMaxProgressManager.h"
|
|
#include "DatasmithMaxLogger.h"
|
|
#include "DatasmithMaxWriter.h"
|
|
|
|
#include "Misc/Paths.h"
|
|
#include "Algo/Find.h"
|
|
|
|
#include "Windows/AllowWindowsPlatformTypes.h"
|
|
|
|
#if WITH_ITOO_INTERFACE
|
|
#pragma warning( push )
|
|
#pragma warning( disable: 4238 )
|
|
//#include "itoo/forestitreesinterface.h"
|
|
#include "itreesinterface.H"
|
|
#pragma warning( pop )
|
|
|
|
#include "ircinterface.H"
|
|
#endif // WITH_ITOO_INTERFACE
|
|
|
|
MAX_INCLUDES_START
|
|
#include "lslights.h"
|
|
#include "xref/iXrefObj.h"
|
|
MAX_INCLUDES_END
|
|
|
|
const TArray< FString, TInlineAllocator< 4 > > FDatasmithMaxSceneParser::CollisionNodesPrefixes = { TEXT("UBX"), TEXT("UCX"), TEXT("UCP"), TEXT("USP") };
|
|
|
|
class FMaxRendereableNode : public FNoncopyable
|
|
{
|
|
public:
|
|
FMaxRendereableNode(INode* InNode, EMaxEntityType EntityType);
|
|
FMaxRendereableNode(INode* InNode, EMaxEntityType EntityType, const TOptional<FDatasmithMaxStaticMeshAttributes>& InStaticMeshAttributes);
|
|
|
|
INode* Node;
|
|
EMaxExporterInstanceMode Instancemode;
|
|
EMaxTriMode Triangulatemode;
|
|
EMaxEntityType Entitytype;
|
|
INodeTab Instances;
|
|
//if 0 faces this becomes false
|
|
bool bIsReadyToExport;
|
|
|
|
//This is used for railclone
|
|
TUniquePtr<Mesh> MaxMesh;
|
|
//This is used for the forest pack. It allow to use a different node for the static mesh export
|
|
INode* CustomMeshNode;
|
|
//This is used only for the UnrealHISM
|
|
int32 MeshIndex;
|
|
TUniquePtr<TArray<Matrix3>> InstancesTransformPtr;
|
|
|
|
//used for the mesh export when the datasmith attribute modifier is present
|
|
TOptional<FDatasmithMaxStaticMeshAttributes> StaticMeshAttributes;
|
|
|
|
private:
|
|
void SetTriangulateMode();
|
|
};
|
|
|
|
//***************************************************************************
|
|
// IInstanceMgr::GetInstanceMgr()->GetInstances() functions is finding
|
|
// any object references. This function collects actual instances :)
|
|
// returns whether multiple instances are found
|
|
//***************************************************************************
|
|
bool FDatasmithMaxSceneParser::GetActualInstances(INode* pINode, INodeTab* OnlyInstance)
|
|
{
|
|
check(pINode);
|
|
check(OnlyInstance);
|
|
|
|
Object* Obj = pINode->GetObjOrWSMRef();
|
|
INodeTab InstanceAndRef;
|
|
if (IInstanceMgr::GetInstanceMgr()->GetInstances(*pINode, InstanceAndRef) < 2)
|
|
{
|
|
OnlyInstance->AppendNode(pINode);
|
|
return false;
|
|
}
|
|
|
|
for (int k = 0; k < InstanceAndRef.Count(); k++)
|
|
{
|
|
if (Obj == InstanceAndRef[k]->GetObjOrWSMRef())
|
|
{
|
|
OnlyInstance->AppendNode(InstanceAndRef[k]);
|
|
}
|
|
}
|
|
|
|
return OnlyInstance->Count() >= 2;
|
|
}
|
|
|
|
EMaxLightClass FDatasmithMaxSceneParser::GetLightClass(INode* Node)
|
|
{
|
|
EMaxLightClass LightClass = EMaxLightClass::Unknown;
|
|
ObjectState ObjState = Node->EvalWorldState(0);
|
|
|
|
if (ObjState.obj->SuperClassID() == LIGHT_CLASS_ID)
|
|
{
|
|
LightObject* Light = (LightObject*)ObjState.obj;
|
|
if (Light != nullptr)
|
|
{
|
|
Class_ID ClassID = Light->ClassID();
|
|
if (ClassID == THEAOMNICLASS)
|
|
{
|
|
LightClass = EMaxLightClass::TheaLightOmni;
|
|
}
|
|
else if (ClassID == THEASPOTCLASS)
|
|
{
|
|
LightClass = EMaxLightClass::TheaLightSpot;
|
|
}
|
|
else if (ClassID == THEAIESCLASS)
|
|
{
|
|
LightClass = EMaxLightClass::TheaLightIES;
|
|
}
|
|
else if (ClassID == THEAPLANECLASS)
|
|
{
|
|
LightClass = EMaxLightClass::TheaLightPlane;
|
|
}
|
|
else if (ClassID == REGULARSPOTACLASS || ClassID == REGULARSPOTBCLASS)
|
|
{
|
|
LightClass = EMaxLightClass::SpotLight;
|
|
}
|
|
else if (ClassID == REGULARDIRECTACLASS || ClassID == REGULARDIRECTBCLASS)
|
|
{
|
|
LightClass = EMaxLightClass::DirectLight;
|
|
|
|
INode *ParentNode = Node->GetParentNode();
|
|
if (ParentNode)
|
|
{
|
|
ObjectState ParentObjectState = ParentNode->EvalWorldState(0);
|
|
if (ParentObjectState.obj && ParentObjectState.obj->ClassID() == DAYLIGHTASSEMBLYCLASS)
|
|
{
|
|
LightClass = EMaxLightClass::SunEquivalent;
|
|
}
|
|
}
|
|
}
|
|
else if (ClassID == REGULAROMNICLASS)
|
|
{
|
|
LightClass = EMaxLightClass::OmniLight;
|
|
}
|
|
else if (Light->IsSubClassOf(LIGHTSCAPE_LIGHT_CLASS))
|
|
{
|
|
LightClass = EMaxLightClass::PhotometricLight;
|
|
}
|
|
else if (ClassID == PHOTOPLANEACLASS || ClassID == PHOTOPLANEBCLASS)
|
|
{
|
|
LightClass = EMaxLightClass::PhotoplaneLight;
|
|
}
|
|
else if (ClassID == VRAYLIGHTIESCLASS)
|
|
{
|
|
LightClass = EMaxLightClass::VRayLightIES;
|
|
}
|
|
else if (ClassID == VRAYLIGHTCLASS)
|
|
{
|
|
LightClass = EMaxLightClass::VRayLight;
|
|
}
|
|
else if (ClassID == CORONALIGHTCLASS)
|
|
{
|
|
LightClass = EMaxLightClass::CoronaLight;
|
|
}
|
|
else if (ClassID == ARNOLDLIGHTCLASS)
|
|
{
|
|
LightClass = EMaxLightClass::ArnoldLight;
|
|
}
|
|
else if (ClassID == SUNLIGHTACLASS ||
|
|
ClassID == SUNLIGHTBCLASS ||
|
|
ClassID == VRAYSUNCLASS ||
|
|
ClassID == SUNPOSITIONERCLASS ||
|
|
ClassID == CORONASUNCLASS ||
|
|
ClassID == CORONASUNCLASSB)
|
|
{
|
|
LightClass = EMaxLightClass::SunEquivalent;
|
|
}
|
|
else if (ClassID == SKYLIGHTCLASS ||
|
|
ClassID == IESSKYLIGHTCLASS ||
|
|
ClassID == MRSKYLIGHTCLASS)
|
|
{
|
|
LightClass = EMaxLightClass::SkyEquivalent;
|
|
}
|
|
else if (ClassID == MRPORTALLIGHTCLASS)
|
|
{
|
|
LightClass = EMaxLightClass::SkyPortal;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (LightClass == EMaxLightClass::Unknown)
|
|
{
|
|
DatasmithMaxLogger::Get().AddUnsupportedLight(Node);
|
|
}
|
|
|
|
return LightClass;
|
|
}
|
|
|
|
bool FDatasmithMaxSceneParser::CanBeTriMesh(Object* Obj)
|
|
{
|
|
if (Obj == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int CanConvertToTri = Obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0));
|
|
if (CanConvertToTri == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
TriObject* Tri = (TriObject*)Obj->ConvertToType(GetCOREInterface()->GetTime(), Class_ID(TRIOBJ_CLASS_ID, 0));
|
|
if (Tri == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool bResult = true;
|
|
if (Tri->GetMesh().getNumFaces() == 0)
|
|
{
|
|
bResult = false;
|
|
}
|
|
|
|
if (Obj != Tri)
|
|
{
|
|
Tri->DeleteMe();
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
bool FDatasmithMaxSceneParser::HasCollisionName(INode* Node)
|
|
{
|
|
FString NodeName = Node->GetName();
|
|
FString LeftString, RightString;
|
|
|
|
if ( NodeName.Split( TEXT("_"), &LeftString, &RightString ) )
|
|
{
|
|
if ( CollisionNodesPrefixes.Find( LeftString ) != INDEX_NONE )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
FMaxRendereableNode::FMaxRendereableNode(INode* InNode, EMaxEntityType Mode)
|
|
: Node(InNode)
|
|
, Instancemode(EMaxExporterInstanceMode::NotInstanced)
|
|
, Triangulatemode(EMaxTriMode::TriNotChecked)
|
|
, Entitytype(Mode)
|
|
, Instances()
|
|
, bIsReadyToExport(true)
|
|
, MaxMesh(nullptr)
|
|
, CustomMeshNode(nullptr)
|
|
, MeshIndex(-1)
|
|
, InstancesTransformPtr()
|
|
, StaticMeshAttributes()
|
|
{
|
|
SetTriangulateMode();
|
|
}
|
|
|
|
FMaxRendereableNode::FMaxRendereableNode(INode* InNode, EMaxEntityType EntityType, const TOptional<FDatasmithMaxStaticMeshAttributes>& InStaticMeshAttributes)
|
|
: Node(InNode)
|
|
, Instancemode(EMaxExporterInstanceMode::NotInstanced)
|
|
, Triangulatemode(EMaxTriMode::TriNotChecked)
|
|
, Entitytype(EntityType)
|
|
, Instances()
|
|
, bIsReadyToExport(true)
|
|
, MaxMesh(nullptr)
|
|
, CustomMeshNode(nullptr)
|
|
, MeshIndex(-1)
|
|
, InstancesTransformPtr()
|
|
, StaticMeshAttributes(InStaticMeshAttributes)
|
|
{
|
|
SetTriangulateMode();
|
|
}
|
|
|
|
|
|
|
|
|
|
void FMaxRendereableNode::SetTriangulateMode()
|
|
{
|
|
if (Entitytype == EMaxEntityType::Geometry)
|
|
{
|
|
if (Node)
|
|
{
|
|
Object* Obj = Node->EvalWorldState(0).obj;
|
|
if (FDatasmithMaxSceneParser::CanBeTriMesh(Obj))
|
|
{
|
|
Triangulatemode = EMaxTriMode::TriEnabled;
|
|
}
|
|
else
|
|
{
|
|
Triangulatemode = EMaxTriMode::TriDisabled;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FDatasmithMaxSceneParser::FDatasmithMaxSceneParser()
|
|
{
|
|
Status = 1;
|
|
bOnlySelection = false;
|
|
}
|
|
|
|
FDatasmithMaxSceneParser::~FDatasmithMaxSceneParser()
|
|
{
|
|
}
|
|
|
|
DWORD WINAPI DummyFunction(LPVOID Arg)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
void FDatasmithMaxSceneParser::ParseCurrentScene(bool bSelectedOnly, TSharedPtr< FDatasmithMaxProgressManager >& ProgressManager)
|
|
{
|
|
bOnlySelection = bSelectedOnly;
|
|
RenderableNodes.Empty();
|
|
Materials.Empty();
|
|
MaterialNames.Empty();
|
|
EncounteredMaterials.Empty();
|
|
EncounteredTextures.Empty();
|
|
Textures.Empty();
|
|
|
|
if (bSelectedOnly == false && GetCOREInterface()->GetUseEnvironmentMap())
|
|
{
|
|
TexEnum(GetCOREInterface()->GetEnvironmentMap()); //environment is not associated to any Node
|
|
}
|
|
|
|
ProgressManager->SetProgressStart(0);
|
|
ProgressManager->SetProgressEnd(100);
|
|
ProgressManager->SetMainMessage(TEXT("Parsing scene objects"));
|
|
NodeEnum(GetCOREInterface()->GetRootNode(), bSelectedOnly, ProgressManager);
|
|
NodeEnumInstanceClean();
|
|
}
|
|
|
|
int FDatasmithMaxSceneParser::GetRendereableNodesCount()
|
|
{
|
|
return (int)RenderableNodes.Num();
|
|
}
|
|
|
|
EMaxEntityType FDatasmithMaxSceneParser::GetEntityType(int NodeIndex)
|
|
{
|
|
return RenderableNodes[NodeIndex].Entitytype;
|
|
}
|
|
|
|
INode* FDatasmithMaxSceneParser::GetNode(int NodeIndex)
|
|
{
|
|
return RenderableNodes[NodeIndex].Node;
|
|
}
|
|
|
|
INode* FDatasmithMaxSceneParser::GetCustomMeshNode(int NodeIndex)
|
|
{
|
|
return RenderableNodes[NodeIndex].CustomMeshNode;
|
|
}
|
|
|
|
Mesh* FDatasmithMaxSceneParser::GetMaxMesh(int NodeIndex)
|
|
{
|
|
return RenderableNodes[NodeIndex].MaxMesh.Get();
|
|
}
|
|
|
|
int32 FDatasmithMaxSceneParser::GetRailCloneMeshIndex(int NodeIndex) const
|
|
{
|
|
return RenderableNodes[NodeIndex].MeshIndex;
|
|
}
|
|
|
|
|
|
TArray<Matrix3>* FDatasmithMaxSceneParser::GetInstancesTransform(int NodeIndex)
|
|
{
|
|
return RenderableNodes[NodeIndex].InstancesTransformPtr.Get();
|
|
}
|
|
|
|
const TOptional<FDatasmithMaxStaticMeshAttributes>& FDatasmithMaxSceneParser::GetDatasmithStaticMeshAttributes(int NodeIndex) const
|
|
{
|
|
return RenderableNodes[NodeIndex].StaticMeshAttributes;
|
|
}
|
|
|
|
EMaxExporterInstanceMode FDatasmithMaxSceneParser::GetInstanceMode(int NodeIndex)
|
|
{
|
|
return RenderableNodes[NodeIndex].Instancemode;
|
|
}
|
|
|
|
INodeTab FDatasmithMaxSceneParser::GetInstances(int NodeIndex)
|
|
{
|
|
return RenderableNodes[NodeIndex].Instances;
|
|
}
|
|
|
|
bool FDatasmithMaxSceneParser::GetReadyToExport(int NodeIndex)
|
|
{
|
|
return RenderableNodes[NodeIndex].bIsReadyToExport;
|
|
}
|
|
|
|
void FDatasmithMaxSceneParser::SetReadyToExport(int NodeIndex, bool ready)
|
|
{
|
|
RenderableNodes[NodeIndex].bIsReadyToExport=ready;
|
|
}
|
|
|
|
int FDatasmithMaxSceneParser::GetMaterialsCount()
|
|
{
|
|
return (int)Materials.Num();
|
|
}
|
|
|
|
Mtl* FDatasmithMaxSceneParser::GetMaterial(int MaterialIndex)
|
|
{
|
|
return Materials[MaterialIndex];
|
|
}
|
|
|
|
void FDatasmithMaxSceneParser::RestoreMaterialNames()
|
|
{
|
|
for (int i = 0; i < MaterialNames.Num(); i++)
|
|
{
|
|
Materials[i]->SetName(*MaterialNames[i]);
|
|
}
|
|
}
|
|
|
|
int FDatasmithMaxSceneParser::GetTexturesCount()
|
|
{
|
|
return (int)Textures.Num();
|
|
}
|
|
|
|
Texmap* FDatasmithMaxSceneParser::GetTexture(int TextureIndex)
|
|
{
|
|
return Textures[TextureIndex];
|
|
}
|
|
|
|
void FDatasmithMaxSceneParser::MaterialEnum(Mtl* Material, bool bAddMaterial)
|
|
{
|
|
if (Material == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
if (EncounteredMaterials.Contains(Material))
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
if (FDatasmithMaxMatHelper::GetMaterialClass(Material) == EDSMaterialType::XRefMat)
|
|
{
|
|
MaterialEnum(FDatasmithMaxMatHelper::GetRenderedXRefMaterial(Material), true);
|
|
}
|
|
else if (FDatasmithMaxMatHelper::GetMaterialClass(Material) == EDSMaterialType::MultiMat)
|
|
{
|
|
for (int i = 0; i < Material->NumSubMtls(); i++)
|
|
{
|
|
MaterialEnum(Material->GetSubMtl(i), true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bAddMaterial)
|
|
{
|
|
int DuplicateCount = 0;
|
|
FString ProposedName = Material->GetName().data();
|
|
MaterialNames.Add(*ProposedName);
|
|
FDatasmithUtils::SanitizeNameInplace(ProposedName);
|
|
for (int i = 0; i < Materials.Num(); i++)
|
|
{
|
|
if (ProposedName == FDatasmithUtils::SanitizeName(Materials[i]->GetName().data()))
|
|
{
|
|
DuplicateCount++;
|
|
ProposedName = FDatasmithUtils::SanitizeName(Material->GetName().data()) + TEXT("_(") + FString::FromInt(DuplicateCount) + TEXT(")");
|
|
}
|
|
}
|
|
Material->SetName(*ProposedName);
|
|
EncounteredMaterials.Add(Material);
|
|
Materials.Add(Material);
|
|
}
|
|
|
|
bool bAddRecursively = Material->ClassID() == THEARANDOMCLASS || Material->ClassID() == VRAYBLENDMATCLASS || Material->ClassID() == CORONALAYERMATCLASS;
|
|
for (int i = 0; i < Material->NumSubMtls(); i++)
|
|
{
|
|
MaterialEnum(Material->GetSubMtl(i), bAddRecursively);
|
|
}
|
|
|
|
for (int i = 0; i < Material->NumSubTexmaps(); i++)
|
|
{
|
|
Texmap* SubTexture = Material->GetSubTexmap(i);
|
|
if (SubTexture != NULL)
|
|
{
|
|
TexEnum(SubTexture);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FDatasmithMaxSceneParser::LightTexEnum(INode* Node)
|
|
{
|
|
ObjectState ObjState = Node->EvalWorldState(0);
|
|
LightObject* Light = (LightObject*)ObjState.obj;
|
|
|
|
if (Light == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int NumParamBlocks = Light->NumParamBlocks();
|
|
|
|
for (int j = 0; j < NumParamBlocks; j++)
|
|
{
|
|
IParamBlock2* ParamBlock2 = Light->GetParamBlockByID((short)j);
|
|
if (ParamBlock2)
|
|
{
|
|
ParamBlockDesc2* ParamBlockDesc = ParamBlock2->GetDesc();
|
|
|
|
if (ParamBlockDesc != NULL)
|
|
{
|
|
for (int i = 0; i < ParamBlockDesc->count; i++)
|
|
{
|
|
const ParamDef& ParamDefinition = ParamBlockDesc->paramdefs[i];
|
|
|
|
if (FCString::Stricmp(ParamDefinition.int_name, TEXT("texmap")) == 0)
|
|
{
|
|
Texmap* Texture = ParamBlock2->GetTexmap(ParamDefinition.ID, GetCOREInterface()->GetTime());
|
|
if (Texture != NULL)
|
|
{
|
|
TexEnum(Texture);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ParamBlock2->ReleaseDesc();
|
|
}
|
|
}
|
|
}
|
|
|
|
void FDatasmithMaxSceneParser::TexEnum(Texmap* Texture)
|
|
{
|
|
if (Texture == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (EncounteredTextures.Contains(Texture))
|
|
{
|
|
return;
|
|
}
|
|
|
|
EncounteredTextures.Add(Texture);
|
|
Textures.Add(Texture);
|
|
|
|
for (int i = 0; i < Texture->NumSubTexmaps(); i++)
|
|
{
|
|
Texmap *SubTexture = Texture->GetSubTexmap(i);
|
|
if (SubTexture != NULL)
|
|
{
|
|
TexEnum(SubTexture);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FDatasmithMaxSceneParser::NodeEnum(INode* Node, bool bSelectedOnly, TSharedPtr< FDatasmithMaxProgressManager >& ProgressManager)
|
|
{
|
|
// nodes comming from XRef Scenes/Objects could be null
|
|
if (Node == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Is the Node hidden?
|
|
BOOL bIsNodeHidden = Node->IsNodeHidden(TRUE);
|
|
|
|
if (bIsNodeHidden == false)
|
|
{
|
|
for (int XRefChild = 0; XRefChild < Node->GetXRefFileCount(); ++XRefChild)
|
|
{
|
|
FString Path = FDatasmithMaxSceneExporter::GetActualPath(Node->GetXRefFile(XRefChild).GetFileName());
|
|
if (FPaths::FileExists(Path) == false)
|
|
{
|
|
FString Error = FString("XRefScene file \"") + FPaths::GetCleanFilename(*Path) + FString("\" cannot be found");
|
|
DatasmithMaxLogger::Get().AddMissingAssetError(*Error);
|
|
}
|
|
else
|
|
{
|
|
NodeEnum(Node->GetXRefTree(XRefChild), bSelectedOnly, ProgressManager);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get the ObjectState.
|
|
// The ObjectState is the structure Other flows up the pipeline.
|
|
// It contains a matrix, a material index, some flags for channels,
|
|
// and a pointer to the object in the pipeline.
|
|
ObjectState ObjState = Node->EvalWorldState(0);
|
|
|
|
if (ObjState.obj == NULL)
|
|
{
|
|
int NumChildren = Node->NumberOfChildren();
|
|
int LastDisplayedProgress = -1;
|
|
for (int ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex)
|
|
{
|
|
int Progress = (ChildIndex * 100) / NumChildren;
|
|
if (Progress != LastDisplayedProgress)
|
|
{
|
|
LastDisplayedProgress = Progress;
|
|
FString Msg = FString::Printf(TEXT("%d%%"), Progress).Left(255);
|
|
// Show progress bar without the Cancel button
|
|
ProgressManager->ProgressEvent(Progress * 0.01f, *Msg);
|
|
}
|
|
|
|
NodeEnum(Node->GetChildNode(ChildIndex), bSelectedOnly, ProgressManager);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (bIsNodeHidden == false)
|
|
{
|
|
if (ObjState.obj->ClassID() == XREFOBJ_CLASS_ID)
|
|
{
|
|
//max2017 and newer versions allow developers to get the file, with previous versions you only can retrieve the active one
|
|
#ifdef MAX_RELEASE_R19
|
|
FString Path = FDatasmithMaxSceneExporter::GetActualPath(((IXRefObject8*)ObjState.obj)->GetFile(FALSE).GetFileName());
|
|
#else
|
|
FString Path = FDatasmithMaxSceneExporter::GetActualPath(((IXRefObject8*)ObjState.obj)->GetActiveFile().GetFileName());
|
|
#endif
|
|
if (FPaths::FileExists(Path) == false)
|
|
{
|
|
FString Error = FString("XRefObj file \"") + FPaths::GetCleanFilename(*Path) + FString("\" cannot be found");
|
|
DatasmithMaxLogger::Get().AddMissingAssetError(*Error);
|
|
bIsNodeHidden = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Examine the superclass ID in order to figure out what kind
|
|
// of object we are dealing with.
|
|
|
|
if ( (!bSelectedOnly || Node->Selected() != 0) && bIsNodeHidden == false)
|
|
{
|
|
switch (ObjState.obj->SuperClassID())
|
|
{
|
|
case CAMERA_CLASS_ID:
|
|
{
|
|
RenderableNodes.Emplace(Node, EMaxEntityType::Camera);
|
|
break;
|
|
}
|
|
|
|
case LIGHT_CLASS_ID:
|
|
{
|
|
EMaxLightClass lightClass = GetLightClass(Node);
|
|
if (lightClass != EMaxLightClass::Unknown)
|
|
{
|
|
RenderableNodes.Emplace(Node, EMaxEntityType::Light);
|
|
LightTexEnum(Node);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SHAPE_CLASS_ID:
|
|
{
|
|
if ( !ObjState.obj->IsRenderable() || !Node->Renderable() )
|
|
{
|
|
if (Node->NumberOfChildren() > 0)
|
|
{
|
|
RenderableNodes.Emplace(Node, EMaxEntityType::Dummy);
|
|
}
|
|
break;
|
|
}
|
|
RenderableNodes.Emplace(Node, EMaxEntityType::Geometry);
|
|
MaterialEnum(Node->GetMtl(), true);
|
|
break;
|
|
}
|
|
|
|
case GEOMOBJECT_CLASS_ID:
|
|
{
|
|
Class_ID ClassID = ObjState.obj->ClassID();
|
|
|
|
if (ClassID == Class_ID(TARGET_CLASS_ID, 0x0))
|
|
{
|
|
RenderableNodes.Emplace(Node, EMaxEntityType::Dummy);
|
|
}
|
|
else if ( !ObjState.obj->IsRenderable() || !Node->Renderable())
|
|
{
|
|
if (Node->NumberOfChildren() > 0)
|
|
{
|
|
RenderableNodes.Emplace(Node, EMaxEntityType::Dummy);
|
|
}
|
|
}
|
|
else if (ClassID == VRAYPLANE_CLASS_ID ||
|
|
ClassID == VRAYFUR_CLASS_ID ||
|
|
ClassID == VRAYSPHERE_CLASS_ID ||
|
|
ClassID == MENTALPROXY_CLASS_ID ||
|
|
ClassID == PFLOWEVENT_CLASS_ID ||
|
|
ClassID == POPULATE_CLASS_ID)
|
|
{
|
|
if (Node->NumberOfChildren() > 0)
|
|
{
|
|
RenderableNodes.Emplace(Node, EMaxEntityType::Dummy);
|
|
}
|
|
}
|
|
else if (ClassID == ITOOFOREST_CLASS_ID)
|
|
{
|
|
ParseForestNode(Node);
|
|
}
|
|
else if (ClassID == RAILCLONE_CLASS_ID)
|
|
{
|
|
ParseRailcloneNode(Node);
|
|
}
|
|
else if ( !HasCollisionName(Node) )
|
|
{
|
|
RenderableNodes.Emplace(Node, EMaxEntityType::Geometry, FDatasmithMaxStaticMeshAttributes::ExtractStaticMeshAttributes(Node));
|
|
|
|
const FMaxRendereableNode& RenderedNode = RenderableNodes.Last();
|
|
if (RenderedNode.StaticMeshAttributes && RenderedNode.StaticMeshAttributes->GetCustomCollisonNode())
|
|
{
|
|
ToBeRemovedFromRenderableNodes.Add(RenderedNode.StaticMeshAttributes->GetCustomCollisonNode());
|
|
}
|
|
|
|
if (!RenderedNode.StaticMeshAttributes || RenderedNode.StaticMeshAttributes->GetExportMode() == EStaticMeshExportMode::Default)
|
|
{
|
|
MaterialEnum(Node->GetMtl(), true);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case HELPER_CLASS_ID:
|
|
{
|
|
RenderableNodes.Emplace(Node, EMaxEntityType::Dummy);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
if (Node->NumberOfChildren() > 0)
|
|
{
|
|
RenderableNodes.Emplace(Node, EMaxEntityType::Dummy);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// For each child of this Node, we recurse into ourselves
|
|
// until no more children are found.
|
|
for (int ChildIndex = 0; ChildIndex < Node->NumberOfChildren(); ChildIndex++)
|
|
{
|
|
NodeEnum(Node->GetChildNode(ChildIndex), bSelectedOnly, ProgressManager);
|
|
}
|
|
}
|
|
|
|
void FDatasmithMaxSceneParser::NodeEnumInstanceClean()
|
|
{
|
|
for (int CurrentNode = 0; CurrentNode < RenderableNodes.Num(); ++CurrentNode)
|
|
{
|
|
FMaxRendereableNode& RenderableNode = RenderableNodes[CurrentNode];
|
|
if (RenderableNode.Instancemode != EMaxExporterInstanceMode::UnrealHISM)
|
|
{
|
|
if (GetActualInstances(RenderableNode.Node, &RenderableNode.Instances))
|
|
{
|
|
RenderableNode.Instancemode = EMaxExporterInstanceMode::InstanceMaster;
|
|
|
|
// Remove duplicated Instances nodes from the RenderableNodes without preserving the order.
|
|
// The RenderableNodes order only affects the order of the StaticMeshes and ActorMeshes in the .udatasmith file
|
|
for (int32 h = RenderableNodes.Num() - 1; h > CurrentNode; --h)
|
|
{
|
|
if (RenderableNode.Instances.Contains(RenderableNodes[h].Node))
|
|
{
|
|
bool bAllowShrink = false; // As we keep references on elements of this array, we cannot allow a realloc.
|
|
RenderableNodes.RemoveAtSwap(h, 1, bAllowShrink);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RenderableNode.Instancemode = EMaxExporterInstanceMode::NotInstanced;
|
|
}
|
|
}
|
|
|
|
if (ToBeRemovedFromRenderableNodes.Contains(RenderableNode.Node))
|
|
{
|
|
if (RenderableNode.StaticMeshAttributes)
|
|
{
|
|
FString Error = FString(TEXT("The datasmith attributes aren't supported for a collision mesh. (")) + FString((TCHAR*) RenderableNode.Node->GetName()) + FString(")");
|
|
DatasmithMaxLogger::Get().AddGeneralError(*Error);
|
|
}
|
|
|
|
RenderableNodes.RemoveAtSwap(CurrentNode);
|
|
|
|
// We don't want to jump over the node that was swap in the place of the removed node
|
|
--CurrentNode;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if WITH_ITOO_INTERFACE
|
|
|
|
void FDatasmithMaxSceneParser::ParseForestNode(INode* ForestNode)
|
|
{
|
|
TimeValue CurrentTime = GetCOREInterface()->GetTime();
|
|
|
|
ITreesInterface* ITrees = GetTreesInterface(ForestNode->GetObjectRef());
|
|
ITrees->IForestRenderBegin(CurrentTime);
|
|
|
|
TMap<int, FMaxRendereableNode*> AlreadyAddedSpec;
|
|
int NumInstances;
|
|
TForestInstance* ForestInstance = (TForestInstance*)ITrees->IForestGetRenderNodes(NumInstances);
|
|
|
|
ulong ForestHandle = ForestNode->GetHandle();
|
|
FString ForestName = ForestNode->GetName();
|
|
Matrix3 ForestMatrix = ForestNode->GetNodeTM(CurrentTime);
|
|
if (ForestInstance && NumInstances)
|
|
{
|
|
for (int i = 0; i < NumInstances; i++, ForestInstance++)
|
|
{
|
|
if (ForestInstance->node != NULL)
|
|
{
|
|
int VirtualMaster = ITrees->IForestGetSpecID(i);
|
|
if (!AlreadyAddedSpec.Find(VirtualMaster))
|
|
{
|
|
RenderableNodes.Emplace(ForestNode, EMaxEntityType::Geometry, FDatasmithMaxStaticMeshAttributes::ExtractStaticMeshAttributes(ForestInstance->node));
|
|
RenderableNodes.Last().Instancemode = EMaxExporterInstanceMode::UnrealHISM;
|
|
RenderableNodes.Last().bIsReadyToExport = true;
|
|
RenderableNodes.Last().InstancesTransformPtr = MakeUnique<TArray<Matrix3>>();
|
|
AlreadyAddedSpec.Add(VirtualMaster, &RenderableNodes.Last());
|
|
RenderableNodes.Last().MeshIndex = AlreadyAddedSpec.Num();
|
|
RenderableNodes.Last().CustomMeshNode = ForestInstance->node;
|
|
|
|
MaterialEnum(ForestInstance->node->GetMtl(), true);
|
|
}
|
|
|
|
AlreadyAddedSpec[VirtualMaster]->InstancesTransformPtr->Emplace(ForestInstance->tm);
|
|
|
|
}
|
|
}
|
|
}
|
|
ITrees->IForestClearRenderNodes();
|
|
ITrees->IForestRenderEnd(CurrentTime);
|
|
}
|
|
|
|
void FDatasmithMaxSceneParser::ParseRailcloneNode(INode* RailCloneNode)
|
|
{
|
|
if (RailCloneNode == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
TimeValue CurrentTime = GetCOREInterface()->GetTime();
|
|
|
|
IRCStaticInterface* RCStaticInterface = GetRCStaticInterface();
|
|
if (!RCStaticInterface)
|
|
{
|
|
return;
|
|
}
|
|
|
|
RCStaticInterface->IRCRegisterEngine();
|
|
IRCInterface* RCInterface = GetRCInterface(RailCloneNode->GetObjectRef());
|
|
if (!RCInterface)
|
|
{
|
|
return;
|
|
}
|
|
|
|
RCInterface->IRCRenderBegin(CurrentTime);
|
|
|
|
int NumInstances;
|
|
TRCInstance* RCInstance = (TRCInstance *)RCInterface->IRCGetInstances(NumInstances);
|
|
|
|
if (RCInstance && NumInstances > 0)
|
|
{
|
|
MaterialEnum(RailCloneNode->GetMtl(), true);
|
|
int32 NextMeshIndex = 0;
|
|
TMap<Mesh*, int32> RenderableNodeIndicesMap;
|
|
for (int j = 0; j < NumInstances; j++, RCInstance++)
|
|
{
|
|
if (RCInstance && RCInstance->mesh)
|
|
{
|
|
if (int32* RenderableNodeIndexPtr = RenderableNodeIndicesMap.Find(RCInstance->mesh))
|
|
{
|
|
RenderableNodes[*RenderableNodeIndexPtr].InstancesTransformPtr->Emplace(RCInstance->tm);
|
|
}
|
|
else
|
|
{
|
|
RenderableNodeIndicesMap.Add(RCInstance->mesh, RenderableNodes.Num());
|
|
FMaxRendereableNode& RenderableNode = RenderableNodes.Emplace_GetRef(RailCloneNode, EMaxEntityType::Geometry);
|
|
RenderableNode.Instancemode = EMaxExporterInstanceMode::UnrealHISM;
|
|
RenderableNode.bIsReadyToExport = true;
|
|
RenderableNode.MaxMesh = MakeUnique<Mesh>(*RCInstance->mesh);
|
|
RenderableNode.MeshIndex = NextMeshIndex++;
|
|
RenderableNode.InstancesTransformPtr = MakeUnique<TArray<Matrix3>>();
|
|
|
|
RenderableNode.InstancesTransformPtr->Emplace(RCInstance->tm);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RCInterface->IRCClearInstances();
|
|
RCInterface->IRCClearMeshes();
|
|
RCInterface->IRCRenderEnd(CurrentTime);
|
|
}
|
|
|
|
#else // WITH_ITOO_INTERFACE
|
|
|
|
void FDatasmithMaxSceneParser::ParseForestNode(INode*) {}
|
|
void FDatasmithMaxSceneParser::ParseRailcloneNode(INode*) {}
|
|
|
|
#endif // WITH_ITOO_INTERFACE
|
|
|
|
#include "Windows/HideWindowsPlatformTypes.h"
|