// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Containers/Array.h" #include "Containers/Map.h" #include "HAL/PlatformMath.h" #include "Math/Transform.h" #include "Math/VectorRegister.h" #include "Misc/AssertionMacros.h" #include "Misc/EnumClassFlags.h" #include "MuR/Layout.h" #include "MuR/MeshBufferSet.h" #include "MuR/PhysicsBody.h" #include "MuR/Ptr.h" #include "MuR/RefCounted.h" #include "MuR/Serialisation.h" #include "MuR/Skeleton.h" #include "Templates/Tuple.h" #include #define UE_API MUTABLERUNTIME_API class FString; namespace mu { // Forward references class FMesh; class FLayout; class FSkeleton; class FPhysicsBody; struct FSurfaceSubMesh { int32 VertexBegin = 0; int32 VertexEnd = 0; int32 IndexBegin = 0; int32 IndexEnd = 0; uint32 ExternalId = 0; friend bool operator==(const FSurfaceSubMesh& Lhs, const FSurfaceSubMesh& Rhs) { return FMemory::Memcmp(&Lhs, &Rhs, sizeof(FSurfaceSubMesh)) == 0; } }; static_assert(std::has_unique_object_representations_v); MUTABLE_DEFINE_POD_SERIALISABLE(FSurfaceSubMesh); MUTABLE_DEFINE_POD_VECTOR_SERIALISABLE(FSurfaceSubMesh); struct FMeshSurface { TArray> SubMeshes; uint32 BoneMapIndex = 0; uint32 BoneMapCount = 0; uint32 Id = 0; friend bool operator==(const FMeshSurface& Lhs, const FMeshSurface& Rhs) { return Lhs.Id == Rhs.Id && Lhs.BoneMapIndex == Rhs.BoneMapIndex && Lhs.BoneMapCount == Rhs.BoneMapCount && Lhs.SubMeshes == Rhs.SubMeshes; } inline void Serialise(FOutputArchive& Arch) const; inline void Unserialise(FInputArchive& Arch); }; /** Helper structs for mesh utility methods below. */ struct FTriangleInfo { /** Vertex indices in the original mesh. */ uint32 Indices[3]; /** Vertex indices in the collapsed vertex list of the mesh. */ uint32 CollapsedIndices[3]; /** Optional data with layout block indices. */ uint16 BlockIndices[3]; /** Optional data with a flag indicating the UVs have changed during layout for this trioangle. */ bool bUVsFixed = false; }; /** Fill an array with the indices of all triangles belonging to the same UV island as InFirstTriangle. */ MUTABLERUNTIME_API void GetUVIsland(TArray& InTriangles, const uint32 InFirstTriangle, TArray& OutTriangleIndices, const TArray& InUVs, const TMultiMap& InVertexToTriangleMap); /** Create a map from vertices into vertices, collapsing vertices that have the same position. */ MUTABLERUNTIME_API void MeshCreateCollapsedVertexMap(const FMesh* Mesh, TArray& CollapsedVertices); enum class EBoneUsageFlags : uint32 { None = 0, Root = 1 << 1, Skinning = 1 << 2, SkinningParent = 1 << 3, Physics = 1 << 4, PhysicsParent = 1 << 5, Deform = 1 << 6, DeformParent = 1 << 7, Reshaped = 1 << 8 }; ENUM_CLASS_FLAGS(EBoneUsageFlags); //! enum class EMeshBufferType { None, SkeletonDeformBinding, PhysicsBodyDeformBinding, PhysicsBodyDeformSelection, PhysicsBodyDeformOffsets, MeshLaplacianData, MeshLaplacianOffsets, UniqueVertexMap }; //! enum class EShapeBindingMethod : uint32 { ReshapeClosestProject = 0, ClipDeformClosestProject = 1, ClipDeformClosestToSurface = 2, ClipDeformNormalProject = 3 }; enum class EVertexColorUsage : uint32 { None = 0, ReshapeMaskWeight = 1, ReshapeClusterId = 2 }; enum class EMeshCopyFlags : uint32 { None = 0, WithSkeletalMesh = 1 << 1, WithSurfaces = 1 << 2, WithSkeleton = 1 << 3, WithPhysicsBody = 1 << 4, WithFaceGroups = 1 << 5, WithTags = 1 << 6, WithVertexBuffers = 1 << 7, WithIndexBuffers = 1 << 8, // deprecated WithFaceBuffers = 1 << 9, WithAdditionalBuffers = 1 << 10, WithLayouts = 1 << 11, WithPoses = 1 << 12, WithBoneMap = 1 << 13, WithSkeletonIDs = 1 << 14, WithAdditionalPhysics = 1 << 15, WithStreamedResources = 1 << 16, AllFlags = 0xFFFFFFFF }; ENUM_CLASS_FLAGS(EMeshCopyFlags); enum class EMeshContentFlags : uint8 { None = 0, GeometryData = 1 << 0, PoseData = 1 << 1, PhysicsData = 1 << 2, MetaData = 1 << 3, LastFlag = MetaData, AllFlags = (LastFlag << 1) - 1, }; ENUM_CLASS_FLAGS(EMeshContentFlags); /** Optimised mesh formats that are identified in some operations to chose a faster version. */ enum class EMeshFlags : uint32 { None = 0, /** The mesh is formatted to be used for planar and cilyndrical projection */ ProjectFormat = 1 << 0, /** The mesh is formatted to be used for wrapping projection */ ProjectWrappingFormat = 1 << 1, /** The mesh is a reference to an external resource mesh. */ IsResourceReference = 1 << 2, /** The mesh is a reference to an external resource mesh and must be loaded when first referenced. */ IsResourceForceLoad = 1 << 3, }; /** Mesh object containing any number of buffers with any number of channels. * The buffers can be per-index or per-vertex. * The mesh also includes layout information for every texture channel for internal usage, and it can be ignored. * The meshes are always assumed to be triangle list primitives. */ class FMesh : public FResource { public: static constexpr uint64 InvalidVertexId = TNumericLimits::Max(); public: //----------------------------------------------------------------------------------------- // Life cycle //----------------------------------------------------------------------------------------- /** Create a new empty mesh that repreents an external resource mesh. */ static UE_API TSharedPtr CreateAsReference(uint32 ID, bool bForceLoad); /** Deep clone this mesh. */ UE_API TSharedPtr Clone() const; /** Clone with flags allowing to not include some parts in the cloned mesh */ UE_API TSharedPtr Clone(EMeshCopyFlags Flags) const; /** Copy form another mesh. */ UE_API void CopyFrom(const FMesh& From, EMeshCopyFlags Flags = EMeshCopyFlags::AllFlags); /** Serialisation */ static UE_API void Serialise(const FMesh* InMesh, FOutputArchive& Arch ); static UE_API TSharedPtr StaticUnserialise(FInputArchive& Arch); // Resource interface UE_API int32 GetDataSize() const override; //----------------------------------------------------------------------------------------- // Own interface //----------------------------------------------------------------------------------------- /** Return true if this is a reference to an engine image. */ UE_API bool IsReference() const; /** If true, this is a reference that must be resolved at compile time. */ UE_API bool IsForceLoad() const; /** Return the id of the engine referenced mesh. Only valid if IsReference. */ UE_API uint32 GetReferencedMesh() const; /** Mesh references can actually reference a morphed mesh. In that case you can set and get the morph with these functions. */ UE_API void SetReferencedMorph(const FString& MorphName); UE_API const FString& GetReferencedMorph() const; //! \name Buffers //! \{ //! UE_API int32 GetIndexCount() const; /** Index buffers. They are owned by this mesh. */ UE_API FMeshBufferSet& GetIndexBuffers(); UE_API const FMeshBufferSet& GetIndexBuffers() const; // UE_API int32 GetVertexCount() const; /** Vertex buffers. They are owned by this mesh. */ UE_API FMeshBufferSet& GetVertexBuffers(); UE_API const FMeshBufferSet& GetVertexBuffers() const; UE_API int32 GetFaceCount() const; /** * Get the number of surfaces defined in this mesh. Surfaces are buffer-contiguous mesh * fragments that share common properties (usually material) */ UE_API int32 GetSurfaceCount() const; UE_API void GetSurface(int32 SurfaceIndex, int32& OutFirstVertex, int32& OutVertexCount, int32& OutFirstIndex, int32& OutIndexCount, int32& OutFirstBone, int32& OutBoneCount) const; /** * Return an internal id that can be used to match mesh surfaces and instance surfaces. * Only valid for meshes that are part of instances. */ UE_API uint32 GetSurfaceId(int32 SurfaceIndex) const; //! \} /** Return true if the mesh has unique vertex IDs and they stored in an implicit way. * This is relevant for some mesh operations that will need to make them explicit so that the result is still correct. */ UE_API bool AreVertexIdsImplicit() const; UE_API bool AreVertexIdsExplicit() const; /** Create an explicit vertex buffer for vertex IDs if they are implicit. */ UE_API void MakeVertexIdsRelative(); /** Ensure the format of an empty mesh includes explicit IDs. The mesh cannot have any vertex data. */ UE_API void MakeIdsExplicit(); //! \name Texture layouts //! \{ //! UE_API void AddLayout( TSharedPtr ); //! UE_API int32 GetLayoutCount() const; //! UE_API TSharedPtr GetLayout( int32 Index) const; //! UE_API void SetLayout( int32 Index, TSharedPtr ); //! \} //! \name Skeleton information //! \{ UE_API void SetSkeleton(TSharedPtr ); UE_API TSharedPtr GetSkeleton() const; //! \} //! \name PhysicsBody information //! \{ UE_API void SetPhysicsBody(TSharedPtr ); UE_API TSharedPtr GetPhysicsBody() const; UE_API int32 AddAdditionalPhysicsBody(TSharedPtr); UE_API TSharedPtr GetAdditionalPhysicsBody(int32 I) const; //int32 GetAdditionalPhysicsBodyExternalId(int32 I) const; //! \} //! \name Tags //! \{ //! UE_API void SetTagCount( int32 Count ); //! UE_API int32 GetTagCount() const; //! UE_API const FString& GetTag( int32 TagIndex ) const; //! UE_API void SetTag( int32 TagIndex, const FString& Name ); //! UE_API void AddStreamedResource(uint64 ResourceId); //! UE_API const TArray& GetStreamedResources() const; //! UE_API int32 FindBonePose(const FBoneName& BoneName) const; //! UE_API void SetBonePoseCount(int32 Count); //! UE_API int32 GetBonePoseCount() const; //! UE_API void SetBonePose(int32 Index, const FBoneName& BoneName, FTransform3f Transform, EBoneUsageFlags BoneUsageFlags); //! @return - Bone identifier of the pose at 'Index'. UE_API const FBoneName& GetBonePoseId(int32 BoneIndex) const; //! Return a matrix stored per bone. It is a set of 16-float values. UE_API void GetBonePoseTransform(int32 BoneIndex, FTransform3f& Transform) const; //! UE_API EBoneUsageFlags GetBoneUsageFlags(int32 BoneIndex) const; //! Set the bonemap of this mesh UE_API void SetBoneMap(const TArray& InBoneMap); //! Return an array containing the bonemaps of all surfaces in the mesh. UE_API const TArray& GetBoneMap() const; //! UE_API int32 GetSkeletonIDsCount() const; //! UE_API int32 GetSkeletonID(int32 SkeletonIndex) const; //! UE_API void AddSkeletonID(int32 SkeletonID); //! \} /** * Get an internal identifier used to reference this mesh in operations like deferred * mesh building, or instance updating. */ UE_API uint32 GetId() const; public: template using TMemoryTrackedArray = TArray>; /** Non-persistent internal id unique for a mesh generated for a specific state and parameter values. */ mutable uint32 InternalId = 0; /** * This is bit - mask on the EMeshFlags enumeration, marking what static formats are compatible with this one and other properties. * It should be reset after any operation that modifies the format. */ mutable EMeshFlags Flags = EMeshFlags::None; /** Only valid if the right flags are set, this identifies a referenced mesh. */ uint32 ReferenceID = 0; /** If the mesh is a reference the referenced morph name is stored here. Otherwise it is an empty string. */ FString ReferencedMorph; /** * Prefix for the unique IDs related to this mesh (vertices and layout blocks). Useful if the mesh stores them in an implicit, or relative way. * See MeshVertexIdIterator for details. */ uint32 MeshIDPrefix = 0; FMeshBufferSet VertexBuffers; FMeshBufferSet IndexBuffers; /** Additional buffers used for temporary or custom data in different algorithms. */ TArray> AdditionalBuffers; TArray Surfaces; /** Externally provided SkeletonIDs of the skeletons required by this mesh. */ TArray SkeletonIDs; /** * This skeleton and physicsbody are not owned and may be used by other meshes, so it cannot be modified * once the mesh has been fully created. */ TSharedPtr Skeleton; TSharedPtr PhysicsBody; /** Additional physics bodies referenced by the mesh that don't merge. */ TArray> AdditionalPhysicsBodies; /** * Texture Layout blocks attached to this mesh. They are const because they could be shared with * other meshes, so they need to be cloned and replaced if a modification is needed. */ TArray> Layouts; TArray Tags; /** Opaque handle to external resources. */ TArray StreamedResources; struct FBonePose { // Identifier built from the bone FName. FBoneName BoneId; EBoneUsageFlags BoneUsageFlags = EBoneUsageFlags::None; FTransform3f BoneTransform; inline void Serialise(FOutputArchive& arch) const; inline void Unserialise(FInputArchive& arch); inline bool operator==(const FBonePose& Other) const { return BoneUsageFlags == Other.BoneUsageFlags && BoneId == Other.BoneId && BoneTransform.Equals(Other.BoneTransform); } }; /** * This is the pose used by this mesh fragment, used to update the transforms of the final skeleton * taking into consideration the meshes being used. */ TMemoryTrackedArray BonePoses; /** Array containing the bonemaps of all surfaces in the mesh. */ TArray BoneMap; inline void Serialise(FOutputArchive& arch) const; inline void Unserialise(FInputArchive& arch); inline bool operator==(const FMesh& Other) const { bool bEqual = true; if (bEqual) bEqual = (ReferenceID == Other.ReferenceID); if (bEqual) bEqual = (MeshIDPrefix == Other.MeshIDPrefix); if (bEqual) bEqual = (IndexBuffers == Other.IndexBuffers); if (bEqual) bEqual = (VertexBuffers == Other.VertexBuffers); if (bEqual) bEqual = (Layouts.Num() == Other.Layouts.Num()); if (bEqual) bEqual = (BonePoses.Num() == Other.BonePoses.Num()); if (bEqual) bEqual = (BoneMap.Num() == Other.BoneMap.Num()); if (bEqual && Skeleton != Other.Skeleton) { if (Skeleton && Other.Skeleton) { bEqual = (*Skeleton == *Other.Skeleton); } else { bEqual = false; } } if (bEqual && PhysicsBody != Other.PhysicsBody) { if (PhysicsBody && Other.PhysicsBody) { bEqual = (*PhysicsBody == *Other.PhysicsBody); } else { bEqual = false; } } if (bEqual) bEqual = (StreamedResources == Other.StreamedResources); if (bEqual) bEqual = (Surfaces == Other.Surfaces); if (bEqual) bEqual = (Tags == Other.Tags); if (bEqual) bEqual = (SkeletonIDs == Other.SkeletonIDs); for (int32 i = 0; bEqual && i < Layouts.Num(); ++i) { bEqual &= (*Layouts[i]) == (*Other.Layouts[i]); } bEqual &= AdditionalBuffers.Num() == Other.AdditionalBuffers.Num(); for (int32 i = 0; bEqual && i < AdditionalBuffers.Num(); ++i) { bEqual &= AdditionalBuffers[i] == Other.AdditionalBuffers[i]; } bEqual &= BonePoses.Num() == Other.BonePoses.Num(); for (int32 i = 0; bEqual && i < BonePoses.Num(); ++i) { bEqual &= BonePoses[i] == Other.BonePoses[i]; } if (bEqual) bEqual = BoneMap == Other.BoneMap; bEqual &= AdditionalPhysicsBodies.Num() == Other.AdditionalPhysicsBodies.Num(); for (int32 i = 0; bEqual && i < AdditionalPhysicsBodies.Num(); ++i) { bEqual &= *AdditionalPhysicsBodies[i] == *Other.AdditionalPhysicsBodies[i]; } return bEqual; } /** Compare the mesh with another one, but ignore internal data like generated vertex indices. */ UE_API bool IsSimilar(const FMesh& Other) const; /** * Make a map from the vertices in this mesh to thefirst matching vertex of the given * mesh. If non is found, the index is set to -1. */ struct FVertexMatchMap { /** One for every vertex */ TArray FirstMatch; /** The matches of every vertex in a sequence */ TArray Matches; bool DoMatch(int32 Vertex, int32 OtherVertex) const; }; UE_API void GetVertexMap(const FMesh& Other, FVertexMatchMap& VertexMap, float Tolerance = 1e-3f) const; /** Compare the vertex attributes to check if they match. */ UE_API UE::Math::TIntVector3 GetFaceVertexIndices(int32 FaceIndex) const; /** * Return true if the given mesh has the same vertex and index formats, and in the same * buffer structure. */ UE_API bool HasCompatibleFormat(const FMesh* Other) const; /** Update the flags identifying the mesh format as some of the optimised formats. */ UE_API void ResetStaticFormatFlags() const; /** Create the surface data if not present. */ UE_API void EnsureSurfaceData(); /** Check mesh buffer data for possible inconsistencies */ UE_API void CheckIntegrity() const; /** Return true if this mesh is closed. It is still closed if it has dangling vertices. */ UE_API bool IsClosed() const; /** * Change the buffer descriptions so that all buffer indices start at 0 and are in the * same order than memory. */ UE_API void ResetBufferIndices(); /** Debug: get a text representation of the mesh */ UE_API void Log(FString& Out, int32 VertexLimit) const; }; MUTABLE_DEFINE_ENUM_SERIALISABLE(EBoneUsageFlags) MUTABLE_DEFINE_ENUM_SERIALISABLE(EMeshBufferType) MUTABLE_DEFINE_ENUM_SERIALISABLE(EShapeBindingMethod) MUTABLE_DEFINE_ENUM_SERIALISABLE(EVertexColorUsage) } #undef UE_API