// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #ifdef USE_OPENMODEL #include "CADModelConverter.h" #include "CADOptions.h" #include "IDatasmithSceneElements.h" #include "Misc/Optional.h" #if WITH_EDITOR #include "Editor.h" #include "IMessageLogListing.h" #include "Logging/TokenizedMessage.h" #include "MessageLogModule.h" #endif #if PLATFORM_WINDOWS #include "Windows/AllowWindowsPlatformTypes.h" #endif #include "AlShadingFields.h" #include "AlDagNode.h" #include "AlLayer.h" #include "AlMesh.h" #include "AlMeshNode.h" #include "AlShader.h" #include "AlShell.h" #include "AlShellNode.h" #include "AlSurface.h" #include "AlSurfaceNode.h" #include "AlPersistentID.h" #if PLATFORM_WINDOWS #include "Windows/HideWindowsPlatformTypes.h" #endif class IDatasmithActorElement; struct FMeshDescription; #define LAYER_TYPE TEXT("Layer") #define GROUPNODE_TYPE TEXT("GroupNode") #define MESH_TYPE TEXT("Mesh") #define MESHNODE_TYPE TEXT("MeshNode") #define SHADER_TYPE TEXT("Shader") #define SHELLNODE_TYPE TEXT("ShellNode") #define SHELL_TYPE TEXT("Shell") #define SURFACE_TYPE TEXT("Surface") #define SURFACENODE_TYPE TEXT("SurfaceNode") // Convert distance from UE (in cm) to CADKernel (in mm) #define UNIT_CONVERSION_CM_TO_MM 10. #define UE_TO_CADKERNEL(Distance) (Distance * 10.) #define WIRE_MEMORY_CHECK 0 #define WIRE_ENSURE_ENABLED 0 #if WIRE_ENSURE_ENABLED #define ensureWire(InExpression) ensure(InExpression) #else #define ensureWire(InExpression) #endif namespace UE_DATASMITHWIRETRANSLATOR_NAMESPACE { DECLARE_LOG_CATEGORY_EXTERN(LogWireInterface, Log, All) #if WIRE_MEMORY_CHECK extern TSet DagNodeSet; extern TSet ObjectSet; extern int32 AllocatedObjects; extern int32 MaxAllocatedObjects; #endif typedef double AlMatrix4x4[4][4]; enum class ETesselatorType : uint8 { Fast, Accurate, }; enum class EAlShaderModelType : uint8 { BLINN, LAMBERT, LIGHTSOURCE, PHONG, }; template class TAlObjectPtr : public TSharedPtr { using Super = TSharedPtr; public: TAlObjectPtr(T* Object = nullptr) : Super(Object) { static_assert(std::is_base_of::value); #if WIRE_MEMORY_CHECK ensure(!Object || !ObjectSet.Contains(Object)); if (IsValid() && Super::GetSharedReferenceCount() == 1) { ++AllocatedObjects; if (AllocatedObjects > MaxAllocatedObjects) { MaxAllocatedObjects = AllocatedObjects; } ObjectSet.Add(Object); } #endif } ~TAlObjectPtr() { #if WIRE_MEMORY_CHECK if (Super::IsValid() && Super::GetSharedReferenceCount() == 1) { ensure(ObjectSet.Contains(Super::Get())); --AllocatedObjects; ObjectSet.Remove(Super::Get()); } #endif Super::Reset(); } bool IsValid() const { return Super::IsValid() && AlIsValid((const AlObject*)Super::Get()); } FString GetName() const { return IsValid() ? StringCast(Super::Get()->name()).Get() : FString(); } uint32 GetHash() const { if (IsValid()) { uint32 NameHash = GetTypeHash(GetName()); uint32 TypeHash = GetTypeHash(Super::Get()->type()); return HashCombine(NameHash, TypeHash); } return -1; } FString GetUniqueID(const TCHAR* TypeName = TEXT("Object")) const { return FString(TypeName) + FString::FromInt(GetHash()); } operator bool() const { return IsValid(); } friend uint32 GetTypeHash(const TAlObjectPtr& Object) { return Object.GetHash()/*GetTypeHash(DateTime.Ticks)*/; } }; template inline bool operator==(const TAlObjectPtr& A, const TAlObjectPtr& B) { return (bool)AlAreEqual((const AlObject*)A.Get(), (const AlObject*)B.Get()); } template inline bool operator!=(const TAlObjectPtr& A, const TAlObjectPtr& B) { return !((bool)AlAreEqual((const AlObject*)A.Get(), (const AlObject*)B.Get())); } template<> uint32 TAlObjectPtr::GetHash() const; class FLayerContainer { public: static void Reset(); static TAlObjectPtr FindOrAdd(AlLayer* Layer); private: static TMap> LayerMap; }; enum class EDagNodeType : uint8 { Unknown = 0x00, MeshType = 0x01, SurfaceType = 0x02, ShellType = 0x04, GroupType = 0x08, GeometryType = MeshType | SurfaceType | ShellType, }; ENUM_CLASS_FLAGS(EDagNodeType); class FAlDagNodePtr : public TAlObjectPtr { using Super = TSharedPtr; public: FAlDagNodePtr(AlDagNode* DagNode = nullptr) : TAlObjectPtr(DagNode) , Type(EDagNodeType::Unknown) { #if WIRE_MEMORY_CHECK ensure(!DagNode || !DagNodeSet.Contains(DagNode)); #endif if (AlIsValid(DagNode)) { if (DagNode->type() == AlObjectType::kMeshNodeType && DagNode->asMeshNodePtr() != nullptr) { EnumAddFlags(Type, EDagNodeType::MeshType); } else if (DagNode->type() == AlObjectType::kSurfaceNodeType && DagNode->asSurfaceNodePtr() != nullptr) { EnumAddFlags(Type, EDagNodeType::SurfaceType); } else if (DagNode->type() == AlObjectType::kShellNodeType && DagNode->asShellNodePtr() != nullptr) { EnumAddFlags(Type, EDagNodeType::ShellType); } else if (DagNode->type() == AlObjectType::kGroupNodeType && DagNode->asGroupNodePtr() != nullptr) { EnumAddFlags(Type, EDagNodeType::GroupType); } CachedLayer = FLayerContainer::FindOrAdd(DagNode->layer()); LayerName = CachedLayer ? FString(StringCast(CachedLayer->name()).Get()) : FString(); bCanDeleteObject = DagNode->parentNode() == nullptr; } #if WIRE_MEMORY_CHECK if (DagNode) { DagNodeSet.Add(DagNode); } #endif } ~FAlDagNodePtr() { if (TAlObjectPtr::IsValid()) { if (Super::GetSharedReferenceCount() == 1) { CachedLayer = nullptr; if (bCanDeleteObject) { Super::Get()->deleteObject(); } #if WIRE_MEMORY_CHECK ensure(AllocatedObjects > 0); ensure(DagNodeSet.Contains(Super::Get())); DagNodeSet.Remove(Super::Get()); #endif } } TAlObjectPtr::~TAlObjectPtr(); } FString GetLayerName() const { return LayerName.IsSet() ? LayerName.GetValue() : FString(); } const TAlObjectPtr& GetLayer() const { return CachedLayer; } bool HasSymmetry() const { return CachedLayer ? (bool)CachedLayer->isSymmetric() : false; } bool IsVisible() const { return CachedLayer.IsValid() ? !((bool)CachedLayer->invisible()) : false; } AlDagNode* AsADagNode() const { return static_cast(Super::Get()); } bool HasGeometry() const { return EnumHasAnyFlags(Type, EDagNodeType::GeometryType); } bool IsAGroup() const { return EnumHasAnyFlags(Type, EDagNodeType::GroupType); } bool IsAMesh() const { return EnumHasAnyFlags(Type, EDagNodeType::MeshType); } bool IsASurface() const { return EnumHasAnyFlags(Type, EDagNodeType::SurfaceType); } bool IsAShell() const { return EnumHasAnyFlags(Type, EDagNodeType::ShellType); } bool GetMesh(TAlObjectPtr& OutMesh) const { OutMesh = IsAMesh() ? AsADagNode()->asMeshNodePtr()->mesh() : TAlObjectPtr(); return OutMesh.IsValid(); } bool GetSurface(TAlObjectPtr& OutSurface) const { OutSurface = IsASurface() ? AsADagNode()->asSurfaceNodePtr()->surface() : TAlObjectPtr(); return OutSurface.IsValid(); } bool GetShell(TAlObjectPtr& OutShell) const { OutShell = IsAShell() ? AsADagNode()->asShellNodePtr()->shell() : TAlObjectPtr(); return OutShell.IsValid(); } CADLibrary::FMeshParameters GetMeshParameters() const; void SetActorTransform(IDatasmithActorElement& ActorElement) const { // Node with symmetry cannot be baked with the global transform because the symmetry is done in the parent referential if (HasSymmetry()) { return; } AlMatrix4x4 AlGlobalMatrix; AsADagNode()->globalTransformationMatrix(AlGlobalMatrix); FMatrix GlobalMatrix; double* MatrixFloats = (double*)GlobalMatrix.M; for (int32 IndexI = 0; IndexI < 4; ++IndexI) { for (int32 IndexJ = 0; IndexJ < 4; ++IndexJ) { MatrixFloats[IndexI * 4 + IndexJ] = AlGlobalMatrix[IndexI][IndexJ]; } } FTransform GlobalTransform = FDatasmithUtils::ConvertTransform(FDatasmithUtils::EModelCoordSystem::ZUp_RightHanded, FTransform(GlobalMatrix)); ActorElement.SetTranslation(GlobalTransform.GetTranslation()); ActorElement.SetScale(GlobalTransform.GetScale3D()); ActorElement.SetRotation(GlobalTransform.GetRotation()); } private: mutable TOptional LayerName; mutable TAlObjectPtr CachedLayer; bool bCanDeleteObject = false; EDagNodeType Type; }; class FPatchMesh { public: FPatchMesh(const FString& InName, TAlObjectPtr& InLayer, int32 Count) : Name(InName) , Layer(InLayer) { MeshNodes.Reserve(Count); } bool HasContent() { return Initialize() && !MeshNodes.IsEmpty(); } bool HasSingleContent() const { return MeshNodes.Num() == 1; } bool GetSingleContent(FAlDagNodePtr& OutMeshNode) { if (MeshNodes.Num() == 1) { FAlDagNodePtr& MeshNode = MeshNodes.Last(); OutMeshNode = MoveTemp(MeshNode); MeshNodes.SetNum(0, EAllowShrinking::No); return true; } return false; } const FString& GetName() const { return Name; } uint32 GetHash() const { return Hash; } const FString& GetUniqueID() const { return UniqueID; } const TAlObjectPtr& GetLayer() const { return Layer; } void AddMeshNode(FAlDagNodePtr& MeshNode) { ensureWire(Layer == MeshNode.GetLayer()); MeshNodes.Add(MeshNode); } void IterateOnMeshNodes(const TFunction& Callback) const { for (const FAlDagNodePtr& MeshNode : MeshNodes) { Callback(MeshNode); } } bool Initialize(); private: FString Name; TArray MeshNodes; TAlObjectPtr Layer; uint32 Hash; FString UniqueID; bool bInitialized = false; }; class FBodyNode { public: FBodyNode(const FString& InName, TAlObjectPtr& InLayer, int32 Count) : Name(InName) , Layer(InLayer) { DagNodes.Reserve(Count); } bool HasContent() { return Initialize() && !DagNodes.IsEmpty(); } bool HasSingleContent() const { return DagNodes.Num() == 1; } bool GetSingleContent(FAlDagNodePtr& OutDagNode) const { if (HasSingleContent()) { FAlDagNodePtr& DagNode = DagNodes.Last(); OutDagNode = MoveTemp(DagNode); DagNodes.SetNum(0, EAllowShrinking::No); return true; } return false; } const FString& GetName() const { return Name; } uint32 GetHash() const { return Hash; } const FString& GetUniqueID() const { return UniqueID; } const TAlObjectPtr& GetLayer() const { return Layer; } bool AddNode(FAlDagNodePtr& DagNode); void IterateOnDagNodes(const TFunction& Callback) const { for (const FAlDagNodePtr& DagNode : DagNodes) { if (DagNode) { Callback(DagNode); } } } void IterateOnSlotIndices(const TFunction& Shader)>& Callback) const { for (const TPair>& Entry : SlotIndexToShader) { Callback(Entry.Key, Entry.Value); } } bool Initialize(); int32 GetSlotIndex(const FAlDagNodePtr& DagNode); private: FString Name; mutable TArray DagNodes; TAlObjectPtr Layer; TMap ShaderNameToSlotIndex; TMap> SlotIndexToShader; uint32 Hash; FString UniqueID; bool bInitialized = false; }; enum class ECADModelGeometryType : int32 { DagNode, MeshNode, BodyNode, PatchMesh, }; enum class EAliasObjectReference { LocalReference, ParentReference, WorldReference, }; struct FAliasGeometry : public CADLibrary::FCADModelGeometry { EAliasObjectReference Reference = EAliasObjectReference::LocalReference; }; struct FDagNodeGeometry : public FAliasGeometry { const FAlDagNodePtr& DagNode; FDagNodeGeometry(int32 InType, EAliasObjectReference InReference, const FAlDagNodePtr& InDagNode) : DagNode(InDagNode) { Type = InType; Reference = InReference; } }; struct FBodyNodeGeometry : public FAliasGeometry { TSharedPtr BodyNode; FBodyNodeGeometry(int32 InType, EAliasObjectReference InReference, const TSharedPtr& InBodyNode) { Type = InType; Reference = InReference; BodyNode = InBodyNode; } }; namespace OpenModelUtils { /** Following layer hierarchy, get list of layers an actor would be in as a csv string*/ bool GetCsvLayerString(const TAlObjectPtr& Layer, FString& CsvString); bool ActorHasContent(const TSharedPtr& ActorElement); bool IsValidActor(const TSharedPtr& ActorElement); inline FString UuidToString(const uint32& Uuid) { return FString::Printf(TEXT("0x%08x"), Uuid); } inline uint32 GetTypeHash(AlPersistentID& GroupNodeId) { int IdA, IdB, IdC, IdD; GroupNodeId.id(IdA, IdB, IdC, IdD); return HashCombine(IdA, HashCombine(IdB, HashCombine(IdC, IdD))); } inline uint32 GetAlDagNodeUuid(AlDagNode& DagNode) { if (DagNode.hasPersistentID() == sSuccess) { AlPersistentID* PersistentID; DagNode.persistentID(PersistentID); return GetTypeHash(*PersistentID); } FString Label(StringCast(DagNode.name()).Get()); return GetTypeHash(Label); } bool TransferAlMeshToMeshDescription(const AlMesh& Mesh, const TCHAR* SlotMaterialName, FMeshDescription& MeshDescription, CADLibrary::FMeshParameters& SymmetricParameters, const bool bMerge = false); FAlDagNodePtr TesselateDagLeaf(const AlDagNode& DagLeaf, ETesselatorType TessType, double Tolerance); CADLibrary::FMeshParameters GetMeshParameters(const TAlObjectPtr& Layer); } } #endif