Files
UnrealEngine/Engine/Plugins/Mutable/Source/MutableRuntime/Public/MuR/Mesh.h
2025-05-18 13:04:45 +08:00

629 lines
18 KiB
C++

// 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 <type_traits>
#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<FSurfaceSubMesh>);
MUTABLE_DEFINE_POD_SERIALISABLE(FSurfaceSubMesh);
MUTABLE_DEFINE_POD_VECTOR_SERIALISABLE(FSurfaceSubMesh);
struct FMeshSurface
{
TArray<FSurfaceSubMesh, TInlineAllocator<1>> 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<FTriangleInfo>& InTriangles,
const uint32 InFirstTriangle,
TArray<uint32>& OutTriangleIndices,
const TArray<FVector2f>& InUVs,
const TMultiMap<int32, uint32>& InVertexToTriangleMap);
/** Create a map from vertices into vertices, collapsing vertices that have the same position. */
MUTABLERUNTIME_API void MeshCreateCollapsedVertexMap(const FMesh* Mesh, TArray<int32>& 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<uint64>::Max();
public:
//-----------------------------------------------------------------------------------------
// Life cycle
//-----------------------------------------------------------------------------------------
/** Create a new empty mesh that repreents an external resource mesh. */
static UE_API TSharedPtr<FMesh> CreateAsReference(uint32 ID, bool bForceLoad);
/** Deep clone this mesh. */
UE_API TSharedPtr<FMesh> Clone() const;
/** Clone with flags allowing to not include some parts in the cloned mesh */
UE_API TSharedPtr<FMesh> 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<FMesh> 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<const FLayout> );
//!
UE_API int32 GetLayoutCount() const;
//!
UE_API TSharedPtr<const FLayout> GetLayout( int32 Index) const;
//!
UE_API void SetLayout( int32 Index, TSharedPtr<const FLayout> );
//! \}
//! \name Skeleton information
//! \{
UE_API void SetSkeleton(TSharedPtr<const FSkeleton> );
UE_API TSharedPtr<const FSkeleton> GetSkeleton() const;
//! \}
//! \name PhysicsBody information
//! \{
UE_API void SetPhysicsBody(TSharedPtr<const FPhysicsBody> );
UE_API TSharedPtr<const FPhysicsBody> GetPhysicsBody() const;
UE_API int32 AddAdditionalPhysicsBody(TSharedPtr<const FPhysicsBody>);
UE_API TSharedPtr<const FPhysicsBody> 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<uint64>& 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<FBoneName>& InBoneMap);
//! Return an array containing the bonemaps of all surfaces in the mesh.
UE_API const TArray<FBoneName>& 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<typename Type>
using TMemoryTrackedArray = TArray<Type, FDefaultMemoryTrackingAllocator<MemoryCounters::FMeshMemoryCounter>>;
/** 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<TPair<EMeshBufferType, FMeshBufferSet>> AdditionalBuffers;
TArray<FMeshSurface> Surfaces;
/** Externally provided SkeletonIDs of the skeletons required by this mesh. */
TArray<uint32> 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<const FSkeleton> Skeleton;
TSharedPtr<const FPhysicsBody> PhysicsBody;
/** Additional physics bodies referenced by the mesh that don't merge. */
TArray<TSharedPtr<const FPhysicsBody>> 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<TSharedPtr<const FLayout>> Layouts;
TArray<FString> Tags;
/** Opaque handle to external resources. */
TArray<uint64> 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<FBonePose> BonePoses;
/** Array containing the bonemaps of all surfaces in the mesh. */
TArray<FBoneName> 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<int32> FirstMatch;
/** The matches of every vertex in a sequence */
TArray<int32> 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<uint32> 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