// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Algo/Accumulate.h" #include "Algo/Copy.h" #include "Algo/Find.h" #include "Containers/Array.h" #include "Containers/ArrayView.h" #include "Containers/BitArray.h" #include "Containers/ContainerAllocationPolicies.h" #include "Containers/ContainersFwd.h" #include "Containers/Map.h" #include "Containers/Set.h" #include "Containers/StaticArray.h" #include "Containers/UnrealString.h" #include "CoreFwd.h" #include "CoreTypes.h" #include "HAL/CriticalSection.h" #include "HAL/PlatformCrt.h" #include "Math/Box.h" #include "Math/MathFwd.h" #include "Math/Plane.h" #include "Math/Vector.h" #include "MeshAttributeArray.h" #include "MeshElementArray.h" #include "MeshElementContainer.h" #include "MeshElementIndexer.h" #include "MeshTypes.h" #include "Misc/AssertionMacros.h" #include "Misc/EnumClassFlags.h" #include "Misc/Guid.h" #include "Serialization/CustomVersion.h" #include "Serialization/EditorBulkData.h" #include "Templates/Tuple.h" #include "Templates/UnrealTemplate.h" #include "UObject/EditorObjectVersion.h" #include "UObject/NameTypes.h" #include "UObject/Object.h" #include "UObject/ObjectMacros.h" #include "UObject/ReleaseObjectVersion.h" #include "MeshDescription.generated.h" class FArchive; class UObject; struct FElementIDRemappings; enum { //Remove the _MD when FRawMesh will be remove MAX_MESH_TEXTURE_COORDS_MD = 8, }; /** Define container types */ using FVertexArray = TMeshElementContainer; using FVertexInstanceArray = TMeshElementContainer; using FEdgeArray = TMeshElementContainer; using FUVArray = TMeshElementContainer; using FTriangleArray = TMeshElementContainer; using FPolygonArray = TMeshElementContainer; using FPolygonGroupArray = TMeshElementContainer; /** Define aliases for element attributes */ template using TVertexAttributesRef = TMeshAttributesRef; template using TVertexInstanceAttributesRef = TMeshAttributesRef; template using TEdgeAttributesRef = TMeshAttributesRef; template using TUVAttributesRef = TMeshAttributesRef; template using TTriangleAttributesRef = TMeshAttributesRef; template using TPolygonAttributesRef = TMeshAttributesRef; template using TPolygonGroupAttributesRef = TMeshAttributesRef; template using TVertexAttributesConstRef = TMeshAttributesConstRef; template using TVertexInstanceAttributesConstRef = TMeshAttributesConstRef; template using TEdgeAttributesConstRef = TMeshAttributesConstRef; template using TUVAttributesConstRef = TMeshAttributesConstRef; template using TTriangleAttributesConstRef = TMeshAttributesConstRef; template using TPolygonAttributesConstRef = TMeshAttributesConstRef; template using TPolygonGroupAttributesConstRef = TMeshAttributesConstRef; UENUM() enum class EComputeNTBsOptions : uint32 { None = 0x00000000, // No flags Normals = 0x00000001, //Compute the normals Tangents = 0x00000002, //Compute the tangents WeightedNTBs = 0x00000004, //Use weight angle when computing NTBs to proportionally distribute the vertex instance contribution to the normal/tangent/binormal in a smooth group. i.e. Weight solve the cylinder problem }; ENUM_CLASS_FLAGS(EComputeNTBsOptions); struct FMeshDescription { public: // Mesh description should be a moveable type. // Hence explicitly declare all the below as defaulted, to ensure they will be generated by the compiler. MESHDESCRIPTION_API FMeshDescription(); ~FMeshDescription() = default; MESHDESCRIPTION_API FMeshDescription(const FMeshDescription&); MESHDESCRIPTION_API FMeshDescription& operator=(const FMeshDescription&); FMeshDescription(FMeshDescription&&) = default; FMeshDescription& operator=(FMeshDescription&&) = default; friend MESHDESCRIPTION_API FArchive& operator<<(FArchive& Ar, FMeshDescription& MeshDescription) { MeshDescription.Serialize(Ar); return Ar; } // Serialize the mesh description MESHDESCRIPTION_API void Serialize(FArchive& Ar); // Legacy serialization for old assets MESHDESCRIPTION_API void SerializeLegacy(FArchive& Ar); // Empty the meshdescription MESHDESCRIPTION_API void Empty(); // Operations on all the indexers MESHDESCRIPTION_API void ResetIndexers(); MESHDESCRIPTION_API void BuildIndexers(); MESHDESCRIPTION_API void RebuildIndexers(); // Return whether the mesh description is empty MESHDESCRIPTION_API bool IsEmpty() const; FVertexArray& Vertices() { return static_cast&>(VertexElements->Get()); } const FVertexArray& Vertices() const { return static_cast&>(VertexElements->Get()); } FVertexInstanceArray& VertexInstances() { return static_cast&>(VertexInstanceElements->Get()); } const FVertexInstanceArray& VertexInstances() const { return static_cast&>(VertexInstanceElements->Get()); } FEdgeArray& Edges() { return static_cast&>(EdgeElements->Get()); } const FEdgeArray& Edges() const { return static_cast&>(EdgeElements->Get()); } FUVArray& UVs(int32 Index) { return static_cast&>(UVElements->Get(Index)); } const FUVArray& UVs(int32 Index) const { return static_cast&>(UVElements->Get(Index)); } FTriangleArray& Triangles() { return static_cast&>(TriangleElements->Get()); } const FTriangleArray& Triangles() const { return static_cast&>(TriangleElements->Get()); } FPolygonArray& Polygons() { return static_cast&>(PolygonElements->Get()); } const FPolygonArray& Polygons() const { return static_cast&>(PolygonElements->Get()); } FPolygonGroupArray& PolygonGroups() { return static_cast&>(PolygonGroupElements->Get()); } const FPolygonGroupArray& PolygonGroups() const { return static_cast&>(PolygonGroupElements->Get()); } TAttributesSet& VertexAttributes() { return Vertices().GetAttributes(); } const TAttributesSet& VertexAttributes() const { return Vertices().GetAttributes(); } TAttributesSet& VertexInstanceAttributes() { return VertexInstances().GetAttributes(); } const TAttributesSet& VertexInstanceAttributes() const { return VertexInstances().GetAttributes(); } TAttributesSet& EdgeAttributes() { return Edges().GetAttributes(); } const TAttributesSet& EdgeAttributes() const { return Edges().GetAttributes(); } TAttributesSet& UVAttributes(int32 Index) { return UVs(Index).GetAttributes(); } const TAttributesSet& UVAttributes(int32 Index) const { return UVs(Index).GetAttributes(); } TAttributesSet& TriangleAttributes() { return Triangles().GetAttributes(); } const TAttributesSet& TriangleAttributes() const { return Triangles().GetAttributes(); } TAttributesSet& PolygonAttributes() { return Polygons().GetAttributes(); } const TAttributesSet& PolygonAttributes() const { return Polygons().GetAttributes(); } TAttributesSet& PolygonGroupAttributes() { return PolygonGroups().GetAttributes(); } const TAttributesSet& PolygonGroupAttributes() const { return PolygonGroups().GetAttributes(); } TMap& GetElements() { return Elements; } const TMap& GetElements() const { return Elements; } void SuspendVertexIndexing() { VertexToVertexInstances.Suspend(); VertexToEdges.Suspend(); } void SuspendVertexInstanceIndexing() { VertexInstanceToTriangles.Suspend(); } void SuspendEdgeIndexing() { EdgeToTriangles.Suspend(); } void SuspendPolygonIndexing() { PolygonToTriangles.Suspend(); } void SuspendPolygonGroupIndexing() { PolygonGroupToPolygons.Suspend(); PolygonGroupToTriangles.Suspend(); } void SuspendUVIndexing() { UVToTriangles.Suspend(); } void ResumeVertexIndexing() { VertexToVertexInstances.Resume(); VertexToEdges.Resume(); } void ResumeVertexInstanceIndexing() { VertexInstanceToTriangles.Resume(); } void ResumeEdgeIndexing() { EdgeToTriangles.Resume(); } void ResumePolygonIndexing() { PolygonToTriangles.Resume(); } void ResumePolygonGroupIndexing() { PolygonGroupToPolygons.Resume(); PolygonGroupToTriangles.Resume(); } void ResumeUVIndexing() { UVToTriangles.Resume(); } void BuildVertexIndexers() { VertexToVertexInstances.Build(); VertexToEdges.Build(); } void BuildVertexInstanceIndexers() { VertexInstanceToTriangles.Build(); } void BuildEdgeIndexers() { EdgeToTriangles.Build(); } void BuildPolygonIndexers() { PolygonToTriangles.Build(); } void BuildPolygonGroupIndexers() { PolygonGroupToPolygons.Build(), PolygonGroupToTriangles.Build(); } ////////////////////////////////////////////////////////////////////////// // Create / remove mesh elements /** Reserves space for this number of new vertices */ void ReserveNewVertices(const int32 NumVertices) { VertexElements->Get().Reserve(VertexElements->Get().Num() + NumVertices); } /** Adds a new vertex to the mesh and returns its ID */ FVertexID CreateVertex() { const FVertexID VertexID = VertexElements->Get().Add(); return VertexID; } /** Adds a new vertex to the mesh with the given ID */ void CreateVertexWithID(const FVertexID VertexID) { VertexElements->Get().Insert(VertexID); } /** Deletes a vertex from the mesh */ void DeleteVertex(const FVertexID VertexID) { check(VertexToVertexInstances.Find(VertexID).Num() == 0); check(VertexToEdges.Find(VertexID).Num() == 0); VertexElements->Get().Remove(VertexID); VertexToVertexInstances.RemoveKey(VertexID); VertexToEdges.RemoveKey(VertexID); } /** Returns whether the passed vertex ID is valid */ bool IsVertexValid(const FVertexID VertexID) const { return VertexElements->Get().IsValid(VertexID); } /** Reserves space for this number of new vertex instances */ void ReserveNewVertexInstances(const int32 NumVertexInstances) { VertexInstanceElements->Get().Reserve(VertexInstanceElements->Get().Num() + NumVertexInstances); } /** Adds a new vertex instance to the mesh and returns its ID */ FVertexInstanceID CreateVertexInstance(const FVertexID VertexID) { const FVertexInstanceID VertexInstanceID = VertexInstanceElements->Get().Add(); CreateVertexInstance_Internal(VertexInstanceID, VertexID); return VertexInstanceID; } /** Adds a new vertex instance to the mesh with the given ID */ void CreateVertexInstanceWithID(const FVertexInstanceID VertexInstanceID, const FVertexID VertexID) { VertexInstanceElements->Get().Insert(VertexInstanceID); CreateVertexInstance_Internal(VertexInstanceID, VertexID); } /** Deletes a vertex instance from a mesh */ MESHDESCRIPTION_API void DeleteVertexInstance(const FVertexInstanceID VertexInstanceID, TArray* InOutOrphanedVerticesPtr = nullptr); /** Returns whether the passed vertex instance ID is valid */ bool IsVertexInstanceValid(const FVertexInstanceID VertexInstanceID) const { return VertexInstanceElements->Get().IsValid(VertexInstanceID); } /** Reserves space for this number of new UVs */ void ReserveNewUVs(const int32 NumUVs, const int32 UVChannel = 0) { UVElements->Get(UVChannel).Reserve(UVElements->Get(UVChannel).Num() + NumUVs); } /** Adds a new UV to the mesh and returns its ID */ FUVID CreateUV(const int32 UVChannel = 0) { const FUVID UVID = UVElements->Get(UVChannel).Add(); return UVID; } /** Adds a new UV to the mesh with the given ID */ void CreateUVWithID(const FUVID UVID, const int32 UVChannel = 0) { UVElements->Get(UVChannel).Insert(UVID); } /** Deletes a UV from the mesh */ void DeleteUV(const FUVID UVID, const int32 UVChannel = 0) { check(UVToTriangles.Find(UVID).Num() == 0); UVElements->Get(UVChannel).Remove(UVID); UVToTriangles.RemoveKey(UVID); } /** Returns whether the passed UV ID is valid */ bool IsUVValid(const FUVID UVID, const int32 UVChannel = 0) const { return UVElements->Get(UVChannel).IsValid(UVID); } /** Reserves space for this number of new edges */ void ReserveNewEdges(const int32 NumEdges) { EdgeElements->Get().Reserve(EdgeElements->Get().Num() + NumEdges); } /** Adds a new edge to the mesh and returns its ID */ FEdgeID CreateEdge(const FVertexID VertexID0, const FVertexID VertexID1) { const FEdgeID EdgeID = EdgeElements->Get().Add(); CreateEdge_Internal(EdgeID, VertexID0, VertexID1); return EdgeID; } /** Adds a new edge to the mesh with the given ID */ void CreateEdgeWithID(const FEdgeID EdgeID, const FVertexID VertexID0, const FVertexID VertexID1) { EdgeElements->Get().Insert(EdgeID); CreateEdge_Internal(EdgeID, VertexID0, VertexID1); } /** Deletes an edge from the mesh */ MESHDESCRIPTION_API void DeleteEdge(const FEdgeID EdgeID, TArray* InOutOrphanedVerticesPtr = nullptr); /** Returns whether the passed edge ID is valid */ bool IsEdgeValid(const FEdgeID EdgeID) const { return EdgeElements->Get().IsValid(EdgeID); } /** Reserves space for this number of new triangles */ void ReserveNewTriangles(const int32 NumTriangles) { TriangleElements->Get().Reserve(TriangleElements->Get().Num() + NumTriangles); } /** Adds a new triangle to the mesh and returns its ID. This will also make an encapsulating polygon, and any missing edges. */ FTriangleID CreateTriangle(const FPolygonGroupID PolygonGroupID, TArrayView VertexInstanceIDs, TArray* OutEdgeIDs = nullptr) { const FTriangleID TriangleID = TriangleElements->Get().Add(); CreateTriangle_Internal(TriangleID, PolygonGroupID, VertexInstanceIDs, OutEdgeIDs); return TriangleID; } /** Adds a new triangle to the mesh with the given ID. This will also make an encapsulating polygon, and any missing edges. */ void CreateTriangleWithID(const FTriangleID TriangleID, const FPolygonGroupID PolygonGroupID, TArrayView VertexInstanceIDs, TArray* OutEdgeIDs = nullptr) { TriangleElements->Get().Insert(TriangleID); CreateTriangle_Internal(TriangleID, PolygonGroupID, VertexInstanceIDs, OutEdgeIDs); } /** Deletes a triangle from the mesh */ MESHDESCRIPTION_API void DeleteTriangle(const FTriangleID TriangleID, TArray* InOutOrphanedEdgesPtr = nullptr, TArray* InOutOrphanedVertexInstancesPtr = nullptr, TArray* InOutOrphanedPolygonGroupsPtr = nullptr); /** Deletes triangles from the mesh and remove all orphaned polygon groups, vertex instances, edges and vertices. Will not compact the internal arrays, you must call Compact() manually. */ MESHDESCRIPTION_API void DeleteTriangles(const TArray& Triangles); /** Returns whether the passed triangle ID is valid */ bool IsTriangleValid(const FTriangleID TriangleID) const { return TriangleElements->Get().IsValid(TriangleID); } /** Reserves space for this number of new polygons */ void ReserveNewPolygons(const int32 NumPolygons) { PolygonElements->Get().Reserve(PolygonElements->Get().Num() + NumPolygons); } /** Adds a new polygon to the mesh and returns its ID. This will also make any missing edges, and all constituent triangles. */ FPolygonID CreatePolygon(const FPolygonGroupID PolygonGroupID, TArrayView VertexInstanceIDs, TArray* OutEdgeIDs = nullptr) { const FPolygonID PolygonID = PolygonElements->Get().Add(); CreatePolygon_Internal(PolygonID, PolygonGroupID, VertexInstanceIDs, OutEdgeIDs); return PolygonID; } /** Adds a new polygon to the mesh with the given ID. This will also make any missing edges, and all constituent triangles. */ void CreatePolygonWithID(const FPolygonID PolygonID, const FPolygonGroupID PolygonGroupID, TArrayView VertexInstanceIDs, TArray* OutEdgeIDs = nullptr) { PolygonElements->Get().Insert(PolygonID); CreatePolygon_Internal(PolygonID, PolygonGroupID, VertexInstanceIDs, OutEdgeIDs); } /** Deletes a polygon from the mesh */ MESHDESCRIPTION_API void DeletePolygon(const FPolygonID PolygonID, TArray* InOutOrphanedEdgesPtr = nullptr, TArray* InOutOrphanedVertexInstancesPtr = nullptr, TArray* InOutOrphanedPolygonGroupsPtr = nullptr); /** Deletes polygons from the mesh and remove all orphaned polygon groups, vertex instances, edges and vertices. Will not compact the internal arrays, you must call Compact() manually. */ MESHDESCRIPTION_API void DeletePolygons(const TArray& Polygons); /** Returns whether the passed polygon ID is valid */ bool IsPolygonValid(const FPolygonID PolygonID) const { return PolygonElements->Get().IsValid(PolygonID); } /** Reserves space for this number of new polygon groups */ void ReserveNewPolygonGroups(const int32 NumPolygonGroups) { PolygonGroupElements->Get().Reserve(PolygonGroupElements->Get().Num() + NumPolygonGroups); } /** Adds a new polygon group to the mesh and returns its ID */ FPolygonGroupID CreatePolygonGroup() { const FPolygonGroupID PolygonGroupID = PolygonGroupElements->Get().Add(); return PolygonGroupID; } /** Adds a new polygon group to the mesh with the given ID */ void CreatePolygonGroupWithID(const FPolygonGroupID PolygonGroupID) { PolygonGroupElements->Get().Insert(PolygonGroupID); } /** Deletes a polygon group from the mesh */ void DeletePolygonGroup(const FPolygonGroupID PolygonGroupID) { check(PolygonGroupToPolygons.Find(PolygonGroupID).Num() == 0); PolygonGroupElements->Get().Remove(PolygonGroupID); PolygonGroupToPolygons.RemoveKey(PolygonGroupID); PolygonGroupToTriangles.RemoveKey(PolygonGroupID); } /** Returns whether the passed polygon group ID is valid */ bool IsPolygonGroupValid(const FPolygonGroupID PolygonGroupID) const { return PolygonGroupElements->Get().IsValid(PolygonGroupID); } ////////////////////////////////////////////////////////////////////////// // MeshDescription general functions public: ////////////////////////////////////////////////////////////////////// // Vertex operations /** Returns whether a given vertex is orphaned, i.e. it doesn't form part of any polygon */ MESHDESCRIPTION_API bool IsVertexOrphaned(const FVertexID VertexID) const; /** Returns the edge ID defined by the two given vertex IDs, if there is one; otherwise INDEX_NONE */ MESHDESCRIPTION_API FEdgeID GetVertexPairEdge(const FVertexID VertexID0, const FVertexID VertexID1) const; /** Returns reference to an array of Edge IDs connected to this vertex */ TArrayView GetVertexConnectedEdgeIDs(const FVertexID VertexID) const { return VertexToEdges.Find(VertexID); } UE_DEPRECATED(4.26, "Please use GetVertexConnectedEdgeIDs instead.") TArray GetVertexConnectedEdges(const FVertexID VertexID) const { return TArray(GetVertexConnectedEdgeIDs(VertexID)); } /** Returns number of edges connected to this vertex */ int32 GetNumVertexConnectedEdges(const FVertexID VertexID) const { return VertexToEdges.Find(VertexID).Num(); } /** Returns reference to an array of VertexInstance IDs instanced from this vertex */ TArrayView GetVertexVertexInstanceIDs(const FVertexID VertexID) const { return VertexToVertexInstances.Find(VertexID); } UE_DEPRECATED(4.26, "Please use GetVertexVertexInstanceIDs instead.") TArray GetVertexVertexInstances(const FVertexID VertexID) const { return TArray(GetVertexVertexInstanceIDs(VertexID)); } /** Returns number of vertex instances created from this vertex */ int32 GetNumVertexVertexInstances(const FVertexID VertexID) const { return VertexToVertexInstances.Find(VertexID).Num(); } /** Populates the passed array of TriangleIDs with the triangles connected to this vertex */ template void GetVertexConnectedTriangles(const FVertexID VertexID, TArray& OutConnectedTriangleIDs) const { OutConnectedTriangleIDs.Reset(GetNumVertexConnectedTriangles(VertexID)); for (const FVertexInstanceID& VertexInstanceID : VertexToVertexInstances.Find(VertexID)) { TArrayView ConnectedTris = VertexInstanceToTriangles.Find(VertexInstanceID); OutConnectedTriangleIDs.Append(ConnectedTris.GetData(), ConnectedTris.Num()); } } /** Returns the triangles connected to this vertex as an array with the specified allocator template type. */ template TArray GetVertexConnectedTriangles(const FVertexID VertexID) const { TArray Result; this->GetVertexConnectedTriangles(VertexID, Result); return Result; } /** Returns the triangles connected to this vertex */ TArray GetVertexConnectedTriangles(const FVertexID VertexID) const { TArray Result; this->GetVertexConnectedTriangles(VertexID, Result); return Result; } /** Returns number of triangles connected to this vertex */ int32 GetNumVertexConnectedTriangles(const FVertexID VertexID) const { TArrayView VertexInstances = VertexToVertexInstances.Find(VertexID); return Algo::TransformAccumulate(VertexInstances, [this](const FVertexInstanceID ID) { return VertexInstanceToTriangles.Find(ID).Num(); }, 0); } /** Populates the passed array of PolygonIDs with the polygons connected to this vertex */ template void GetVertexConnectedPolygons(const FVertexID VertexID, TArray& OutConnectedPolygonIDs) const { OutConnectedPolygonIDs.Reset(); for (const FVertexInstanceID& VertexInstanceID : VertexToVertexInstances.Find(VertexID)) { for (const FTriangleID& TriangleID : VertexInstanceToTriangles.Find(VertexInstanceID)) { OutConnectedPolygonIDs.AddUnique(TrianglePolygons[TriangleID]); } } } /** Returns the polygons connected to this vertex as an array with the specified allocator template type. */ template TArray GetVertexConnectedPolygons(const FVertexID VertexID) const { TArray Result; this->GetVertexConnectedPolygons(VertexID, Result); return Result; } /** Returns the polygons connected to this vertex */ TArray GetVertexConnectedPolygons(const FVertexID VertexID) const { TArray Result; this->GetVertexConnectedPolygons(VertexID, Result); return Result; } /** Returns the number of polygons connected to this vertex */ int32 GetNumVertexConnectedPolygons(const FVertexID VertexID) const { return GetVertexConnectedPolygons>(VertexID).Num(); } /** Populates the passed array of VertexIDs with the vertices adjacent to this vertex */ template void GetVertexAdjacentVertices(const FVertexID VertexID, TArray& OutAdjacentVertexIDs) const { TArrayView ConnectedEdgeIDs = VertexToEdges.Find(VertexID); OutAdjacentVertexIDs.SetNumUninitialized(ConnectedEdgeIDs.Num()); int32 Index = 0; for (const FEdgeID& EdgeID : ConnectedEdgeIDs) { TArrayView EdgeVertexIDs = EdgeVertices[EdgeID]; OutAdjacentVertexIDs[Index] = (EdgeVertexIDs[0] == VertexID) ? EdgeVertexIDs[1] : EdgeVertexIDs[0]; Index++; } } /** Returns the vertices adjacent to this vertex as an array with the specified allocator template type. */ template TArray GetVertexAdjacentVertices(const FVertexID VertexID) const { TArray Result; this->GetVertexAdjacentVertices(VertexID, Result); return Result; } /** Returns the vertices adjacent to this vertex */ TArray GetVertexAdjacentVertices(const FVertexID VertexID) const { TArray Result; this->GetVertexAdjacentVertices(VertexID, Result); return Result; } /** Returns the position of this vertex */ FVector3f GetVertexPosition(const FVertexID VertexID) const { return VertexPositions[VertexID]; } TVertexAttributesRef GetVertexPositions() { return VertexPositions; } TVertexAttributesRef GetVertexPositions() const { return VertexPositions; } ////////////////////////////////////////////////////////////////////// // Vertex instance operations /** Returns the vertex ID associated with the given vertex instance */ FVertexID GetVertexInstanceVertex(const FVertexInstanceID VertexInstanceID) const { return VertexInstanceVertices[VertexInstanceID]; } /** Returns the edge ID defined by the two given vertex instance IDs, if there is one; otherwise INDEX_NONE */ MESHDESCRIPTION_API FEdgeID GetVertexInstancePairEdge(const FVertexInstanceID VertexInstanceID0, const FVertexInstanceID VertexInstanceID1) const; /** Returns reference to an array of Triangle IDs connected to this vertex instance */ TArrayView GetVertexInstanceConnectedTriangleIDs(const FVertexInstanceID VertexInstanceID) const { return VertexInstanceToTriangles.Find(VertexInstanceID); } UE_DEPRECATED(4.26, "Please use GetVertexInstanceTriangleIDs() instead.") TArray GetVertexInstanceConnectedTriangles(const FVertexInstanceID VertexInstanceID) const { return TArray(GetVertexInstanceConnectedTriangleIDs(VertexInstanceID)); } /** Returns the number of triangles connected to this vertex instance */ int32 GetNumVertexInstanceConnectedTriangles(const FVertexInstanceID VertexInstanceID) const { return VertexInstanceToTriangles.Find(VertexInstanceID).Num(); } /** Populates the passed array with the polygons connected to this vertex instance */ template void GetVertexInstanceConnectedPolygons(const FVertexInstanceID VertexInstanceID, TArray& OutPolygonIDs) const { OutPolygonIDs.Reset(VertexInstanceToTriangles.Find(VertexInstanceID).Num()); for (const FTriangleID& TriangleID : VertexInstanceToTriangles.Find(VertexInstanceID)) { OutPolygonIDs.AddUnique(TrianglePolygons[TriangleID]); } } /** Returns the polygons connected to this vertex instance as an array with the specified allocator template type. */ template TArray GetVertexInstanceConnectedPolygons(const FVertexInstanceID VertexInstanceID) const { TArray Result; this->GetVertexInstanceConnectedPolygons(VertexInstanceID, Result); return Result; } /** Returns the polygons connected to this vertex instance */ TArray GetVertexInstanceConnectedPolygons(const FVertexInstanceID VertexInstanceID) const { TArray Result; this->GetVertexInstanceConnectedPolygons(VertexInstanceID, Result); return Result; } /** Returns the number of polygons connected to this vertex instance. */ int32 GetNumVertexInstanceConnectedPolygons(const FVertexInstanceID VertexInstanceID) const { return GetVertexInstanceConnectedPolygons>(VertexInstanceID).Num(); } ////////////////////////////////////////////////////////////////////// // Edge operations /** Determine whether a given edge is an internal edge between triangles of a polygon */ bool IsEdgeInternal(const FEdgeID EdgeID) const { TArrayView ConnectedTriangles = EdgeToTriangles.Find(EdgeID); return ConnectedTriangles.Num() == 2 && TrianglePolygons[ConnectedTriangles[0]] == TrianglePolygons[ConnectedTriangles[1]]; } /** Determine whether a given edge is an internal edge between triangles of a specific polygon */ bool IsEdgeInternalToPolygon(const FEdgeID EdgeID, const FPolygonID PolygonID) const { TArrayView ConnectedTriangles = EdgeToTriangles.Find(EdgeID); return ConnectedTriangles.Num() == 2 && TrianglePolygons[ConnectedTriangles[0]] == PolygonID && TrianglePolygons[ConnectedTriangles[1]] == PolygonID; } /** Returns reference to an array of triangle IDs connected to this edge */ TArrayView GetEdgeConnectedTriangleIDs(const FEdgeID EdgeID) const { return EdgeToTriangles.Find(EdgeID); } UE_DEPRECATED(4.26, "Please use GetEdgeConnectedTriangleIDs() instead.") TArray GetEdgeConnectedTriangles(const FEdgeID EdgeID) const { return TArray(GetEdgeConnectedTriangleIDs(EdgeID)); } int32 GetNumEdgeConnectedTriangles(const FEdgeID EdgeID) const { return EdgeToTriangles.Find(EdgeID).Num(); } /** Populates the passed array with polygon IDs connected to this edge */ template void GetEdgeConnectedPolygons(const FEdgeID EdgeID, TArray& OutPolygonIDs) const { OutPolygonIDs.Reset(EdgeToTriangles.Find(EdgeID).Num()); for (const FTriangleID& TriangleID : EdgeToTriangles.Find(EdgeID)) { OutPolygonIDs.AddUnique(TrianglePolygons[TriangleID]); } } /** Returns the polygons connected to this edge as an array with the specified allocator template type. */ template TArray GetEdgeConnectedPolygons(const FEdgeID EdgeID) const { TArray Result; this->GetEdgeConnectedPolygons(EdgeID, Result); return Result; } /** Returns the polygons connected to this edge */ TArray GetEdgeConnectedPolygons(const FEdgeID EdgeID) const { TArray Result; this->GetEdgeConnectedPolygons(EdgeID, Result); return Result; } /** Returns the number of polygons connected to this edge */ int32 GetNumEdgeConnectedPolygons(const FEdgeID EdgeID) const { return GetEdgeConnectedPolygons>(EdgeID).Num(); } /** Returns the vertex ID corresponding to one of the edge endpoints */ FVertexID GetEdgeVertex(const FEdgeID EdgeID, int32 VertexNumber) const { check(VertexNumber == 0 || VertexNumber == 1); TArrayView EdgeVertexIDs = EdgeVertices[EdgeID]; return EdgeVertexIDs[VertexNumber]; } /** Returns a pair of vertex IDs defining the edge */ TArrayView GetEdgeVertices(const FEdgeID EdgeID) const { return EdgeVertices[EdgeID]; } ////////////////////////////////////////////////////////////////////// // Triangle operations /** Get the polygon which contains this triangle */ FPolygonID GetTrianglePolygon(const FTriangleID TriangleID) const { return TrianglePolygons[TriangleID]; } /** Get the polygon group which contains this triangle */ FPolygonGroupID GetTrianglePolygonGroup(const FTriangleID TriangleID) const { return TrianglePolygonGroups[TriangleID]; } /** Determines if this triangle is part of an n-gon */ bool IsTrianglePartOfNgon(const FTriangleID TriangleID) const { return PolygonToTriangles.Find(TrianglePolygons[TriangleID]).Num() > 1; } /** Get the vertex instances which define this triangle */ TArrayView GetTriangleVertexInstances(const FTriangleID TriangleID) const { return TriangleVertexInstances[TriangleID]; } /** Get the specified vertex instance by index */ FVertexInstanceID GetTriangleVertexInstance(const FTriangleID TriangleID, const int32 Index) const { check(Index >= 0 && Index < 3); TArrayView TriVertexInstanceIDs = TriangleVertexInstances[TriangleID]; return TriVertexInstanceIDs[Index]; } /** Populates the passed array with the vertices which define this triangle */ UE_DEPRECATED(4.26, "Use the other form of GetTriangleVertices") void GetTriangleVertices(const FTriangleID TriangleID, TArrayView OutVertexIDs) const { check(OutVertexIDs.Num() >= 3); TArrayView TriVerts = TriangleVertices[TriangleID]; for (int32 Index = 0; Index < 3; ++Index) { OutVertexIDs[Index] = TriVerts[Index]; } } /** Return the vertices which define this triangle */ TArrayView GetTriangleVertices(const FTriangleID TriangleID) const { return TriangleVertices[TriangleID]; } /** Populates the passed array with the edges which define this triangle */ UE_DEPRECATED(4.26, "Use the other form of GetTriangleEdges") void GetTriangleEdges(const FTriangleID TriangleID, TArrayView OutEdgeIDs) const { check(OutEdgeIDs.Num() >= 3); TArrayView TriEdges = TriangleEdges[TriangleID]; for (int32 Index = 0; Index < 3; ++Index) { OutEdgeIDs[Index] = TriEdges[Index]; } } /** Return the edges which form this triangle */ TArrayView GetTriangleEdges(const FTriangleID TriangleID) const { return TriangleEdges[TriangleID]; } /** Populates the passed array with adjacent triangles */ template void GetTriangleAdjacentTriangles(const FTriangleID TriangleID, TArray& OutTriangleIDs) const { OutTriangleIDs.Reset(); for (const FEdgeID& EdgeID : GetTriangleEdges(TriangleID)) { for (const int32& TriangleIndex : EdgeToTriangles.Find(EdgeID)) { FTriangleID OtherTriangleID(TriangleIndex); if (OtherTriangleID != TriangleID) { OutTriangleIDs.Add(OtherTriangleID); } } } } /** Return adjacent triangles into a TArray with the specified allocator */ template TArray GetTriangleAdjacentTriangles(const FTriangleID TriangleID) const { TArray Result; this->GetTriangleAdjacentTriangles(TriangleID, Result); return Result; } /** Return adjacent triangles to this triangle */ TArray GetTriangleAdjacentTriangles(const FTriangleID TriangleID) const { TArray Result; this->GetTriangleAdjacentTriangles(TriangleID, Result); return Result; } /** Return UV indices for this triangle for the given channel */ TArrayView GetTriangleUVIndices(const FTriangleID TriangleID, int32 UVChannel = 0) const { return TriangleUVs.Get(TriangleID, UVChannel); } /** Return the vertex instance which corresponds to the given vertex on the given triangle, or INDEX_NONE */ FVertexInstanceID GetVertexInstanceForTriangleVertex(const FTriangleID TriangleID, const FVertexID VertexID) const { const FVertexInstanceID* VertexInstanceIDPtr = Algo::FindByPredicate( GetTriangleVertexInstances(TriangleID), [this, VertexID](const FVertexInstanceID VertexInstanceID) { return (GetVertexInstanceVertex(VertexInstanceID) == VertexID); }); return VertexInstanceIDPtr ? *VertexInstanceIDPtr : FVertexInstanceID(INDEX_NONE); } /** Reverse the winding order of the vertices of this triangle */ MESHDESCRIPTION_API void ReverseTriangleFacing(const FTriangleID TriangleID); /** Set the UV indices for this triangle */ MESHDESCRIPTION_API void SetTriangleUVIndices(const FTriangleID TriangleID, TArrayView UVIDs, int32 UVChannel = 0); ////////////////////////////////////////////////////////////////////// // Polygon operations /** Return reference to an array of triangle IDs which comprise this polygon */ TArrayView GetPolygonTriangles(const FPolygonID PolygonID) const { return PolygonToTriangles.Find(PolygonID); } UE_DEPRECATED(4.26, "Please use GetPolygonTriangles() instead.") TArray GetPolygonTriangleIDs(const FPolygonID PolygonID) const { return TArray(GetPolygonTriangles(PolygonID)); } /** Return the number of triangles which comprise this polygon */ int32 GetNumPolygonTriangles(const FPolygonID PolygonID) const { return PolygonToTriangles.Find(PolygonID).Num(); } /** Returns reference to an array of VertexInstance IDs forming the perimeter of this polygon */ template void GetPolygonVertexInstances(const FPolygonID PolygonID, TArray& OutVertexInstanceIDs) const { OutVertexInstanceIDs.SetNumUninitialized(GetNumPolygonVertices(PolygonID)); TArrayView Tris = PolygonToTriangles.Find(PolygonID); if (Tris.Num() == 1) { OutVertexInstanceIDs = TriangleVertexInstances[Tris[0]]; } else { TArray, TInlineAllocator<8>> Result; Result.SetNumUninitialized(Tris.Num() + 2); FindPolygonPerimeter(Tris, Result); for (int32 Index = 0; Index < Result.Num(); Index++) { FTriangleID TriangleID = Tris[Result[Index].Get<0>()]; int32 VertexInstanceNumber = Result[Index].Get<1>(); OutVertexInstanceIDs[Index] = TriangleVertexInstances[TriangleID][VertexInstanceNumber]; } } } template TArray GetPolygonVertexInstances(const FPolygonID PolygonID) const { TArray Result; this->GetPolygonVertexInstances(PolygonID, Result); return Result; } TArray GetPolygonVertexInstances(const FPolygonID PolygonID) const { TArray Result; this->GetPolygonVertexInstances(PolygonID, Result); return Result; } /** Returns the number of vertices this polygon has */ int32 GetNumPolygonVertices(const FPolygonID PolygonID) const { TArrayView Tris = PolygonToTriangles.Find(PolygonID); return Tris.Num() + 2; } /** Populates the passed array of VertexIDs with the vertices which form the polygon perimeter */ template void GetPolygonVertices(const FPolygonID PolygonID, TArray& OutVertexIDs) const { OutVertexIDs.SetNumUninitialized(GetNumPolygonVertices(PolygonID)); TArrayView Tris = PolygonToTriangles.Find(PolygonID); if (Tris.Num() == 1) { OutVertexIDs = TriangleVertices[Tris[0]]; } else { TArray, TInlineAllocator<8>> Result; Result.SetNumUninitialized(Tris.Num() + 2); FindPolygonPerimeter(Tris, Result); for (int32 Index = 0; Index < Result.Num(); Index++) { FTriangleID TriangleID = Tris[Result[Index].Get<0>()]; int32 VertexNumber = Result[Index].Get<1>(); OutVertexIDs[Index] = TriangleVertices[TriangleID][VertexNumber]; } } } /** Returns the vertices which form the polygon perimeter as an array templated on the given allocator */ template TArray GetPolygonVertices(const FPolygonID PolygonID) const { TArray Result; this->GetPolygonVertices(PolygonID, Result); return Result; } /** Returns the vertices which form the polygon perimeter */ TArray GetPolygonVertices(const FPolygonID PolygonID) const { TArray Result; this->GetPolygonVertices(PolygonID, Result); return Result; } /** Populates the passed array with the edges which form the polygon perimeter */ template void GetPolygonPerimeterEdges(const FPolygonID PolygonID, TArray& OutEdgeIDs) const { OutEdgeIDs.SetNumUninitialized(GetNumPolygonVertices(PolygonID)); TArrayView Tris = PolygonToTriangles.Find(PolygonID); if (Tris.Num() == 1) { OutEdgeIDs = TriangleEdges[Tris[0]]; } else { TArray, TInlineAllocator<8>> Result; Result.SetNumUninitialized(Tris.Num() + 2); FindPolygonPerimeter(Tris, Result); for (int32 Index = 0; Index < Result.Num(); Index++) { FTriangleID TriangleID = Tris[Result[Index].Get<0>()]; int32 EdgeNumber = Result[Index].Get<1>(); OutEdgeIDs[Index] = TriangleEdges[TriangleID][EdgeNumber]; } } } /** Returns the vertices which form the polygon perimeter as an array templated on the given allocator */ template TArray GetPolygonPerimeterEdges(const FPolygonID PolygonID) const { TArray Result; this->GetPolygonPerimeterEdges(PolygonID, Result); return Result; } /** Returns the vertices which form the polygon perimeter */ TArray GetPolygonPerimeterEdges(const FPolygonID PolygonID) const { TArray Result; this->GetPolygonPerimeterEdges(PolygonID, Result); return Result; } /** Populate the provided array with a list of edges which are internal to the polygon, i.e. those which separate constituent triangles. */ template void GetPolygonInternalEdges(const FPolygonID PolygonID, TArray& OutEdgeIDs) const { OutEdgeIDs.Reset(GetNumPolygonVertices(PolygonID) - 3); if (GetNumPolygonVertices(PolygonID) > 3) { TArray VertexInstanceIDs = GetPolygonVertexInstances(PolygonID); for (const FVertexInstanceID& VertexInstanceID : VertexInstanceIDs) { for (const FEdgeID& EdgeID : GetVertexConnectedEdgeIDs(GetVertexInstanceVertex(VertexInstanceID))) { if (!OutEdgeIDs.Contains(EdgeID) && IsEdgeInternalToPolygon(EdgeID, PolygonID)) { OutEdgeIDs.Add(EdgeID); } } } } } /** Return the internal edges of this polygon, i.e. those which separate constituent triangles */ template TArray GetPolygonInternalEdges(const FPolygonID PolygonID) const { TArray Result; this->GetPolygonInternalEdges(PolygonID, Result); return Result; } /** Return the internal edges of this polygon, i.e. those which separate constituent triangles */ TArray GetPolygonInternalEdges(const FPolygonID PolygonID) const { TArray Result; this->GetPolygonInternalEdges(PolygonID, Result); return Result; } /** Return the number of internal edges in this polygon */ int32 GetNumPolygonInternalEdges(const FPolygonID PolygonID) const { return PolygonToTriangles.Find(PolygonID).Num() - 1; } /** Populates the passed array with adjacent polygons */ template void GetPolygonAdjacentPolygons(const FPolygonID PolygonID, TArray& OutPolygonIDs) const { OutPolygonIDs.Reset(); for (const FEdgeID& EdgeID : GetPolygonPerimeterEdges>(PolygonID)) { for (const FPolygonID& OtherPolygonID : GetEdgeConnectedPolygons>(EdgeID)) { if (OtherPolygonID != PolygonID) { OutPolygonIDs.Add(OtherPolygonID); } } } } /** Return adjacent polygons into a TArray with the specified allocator */ template TArray GetPolygonAdjacentPolygons(const FPolygonID PolygonID) const { TArray Result; this->GetPolygonAdjacentPolygons(PolygonID, Result); return Result; } /** Return adjacent polygons to this polygon */ TArray GetPolygonAdjacentPolygons(const FPolygonID PolygonID) const { TArray Result; this->GetPolygonAdjacentPolygons(PolygonID, Result); return Result; } /** Return the polygon group associated with a polygon */ FPolygonGroupID GetPolygonPolygonGroup(const FPolygonID PolygonID) const { return PolygonPolygonGroups[PolygonID]; } /** Return the vertex instance which corresponds to the given vertex on the given polygon, or INDEX_NONE */ FVertexInstanceID GetVertexInstanceForPolygonVertex(const FPolygonID PolygonID, const FVertexID VertexID) const { TArray VertexInstanceIDs = GetPolygonVertexInstances(PolygonID); const FVertexInstanceID* VertexInstanceIDPtr = VertexInstanceIDs.FindByPredicate( [this, VertexID](const FVertexInstanceID VertexInstanceID) { return (GetVertexInstanceVertex(VertexInstanceID) == VertexID); }); return VertexInstanceIDPtr ? *VertexInstanceIDPtr : FVertexInstanceID(INDEX_NONE); } /** Set the vertex instance at the given index around the polygon to the new value */ MESHDESCRIPTION_API void SetPolygonVertexInstance(const FPolygonID PolygonID, const int32 PerimeterIndex, const FVertexInstanceID VertexInstanceID); MESHDESCRIPTION_API void SetPolygonVertexInstances(const FPolygonID PolygonID, TArrayView VertexInstanceIDs); /** Sets the polygon group associated with a polygon */ void SetPolygonPolygonGroup(const FPolygonID PolygonID, const FPolygonGroupID PolygonGroupID) { FPolygonGroupID OldPolygonGroupID = PolygonPolygonGroups[PolygonID]; PolygonGroupToPolygons.RemoveReferenceFromKey(OldPolygonGroupID, PolygonID); PolygonPolygonGroups[PolygonID] = PolygonGroupID; PolygonGroupToPolygons.AddReferenceToKey(PolygonGroupID, PolygonID); for (const int32& TriangleIndex: PolygonToTriangles.Find(PolygonID)) { const FTriangleID TriangleID(TriangleIndex); check(TrianglePolygonGroups[TriangleID] == OldPolygonGroupID); PolygonGroupToTriangles.RemoveReferenceFromKey(OldPolygonGroupID, TriangleID); TrianglePolygonGroups[TriangleID] = PolygonGroupID; PolygonGroupToTriangles.AddReferenceToKey(PolygonGroupID, TriangleID); } } /** Reverse the winding order of the vertices of this polygon */ MESHDESCRIPTION_API void ReversePolygonFacing(const FPolygonID PolygonID); /** Determines the vertex instances which form the perimeter of a polygon */ MESHDESCRIPTION_API void FindPolygonPerimeter(TArrayView Triangles, TArrayView> Result) const; MESHDESCRIPTION_API void FindPolygonPerimeter(const FPolygonID PolygonID, TArrayView Edges) const; /** Generates triangles and internal edges for the given polygon */ MESHDESCRIPTION_API void ComputePolygonTriangulation(const FPolygonID PolygonID); MESHDESCRIPTION_API void SplitPolygon(FPolygonID PolygonID); ////////////////////////////////////////////////////////////////////// // Polygon group operations /** Returns the polygons associated with the given polygon group */ TArrayView GetPolygonGroupPolygonIDs(const FPolygonGroupID PolygonGroupID) const { return PolygonGroupToPolygons.Find(PolygonGroupID); } UE_DEPRECATED(4.26, "Please use GetPolygonGroupPolygonIDs() instead.") TArray GetPolygonGroupPolygons(const FPolygonGroupID PolygonGroupID) const { return TArray(GetPolygonGroupPolygonIDs(PolygonGroupID)); } /** Returns the triangles associated with the given polygon group */ TArrayView GetPolygonGroupTriangles(const FPolygonGroupID PolygonGroupID) const { return PolygonGroupToTriangles.Find(PolygonGroupID); } /** Returns the number of polygons in this polygon group */ int32 GetNumPolygonGroupPolygons(const FPolygonGroupID PolygonGroupID) const { return PolygonGroupToPolygons.Find(PolygonGroupID).Num(); } /** Returns the number of triangles in this polygon group */ int32 GetNumPolygonGroupTriangles(const FPolygonGroupID PolygonGroupID) const { return PolygonGroupToTriangles.Find(PolygonGroupID).Num(); } /** Remaps polygon groups according to the supplied map */ MESHDESCRIPTION_API void RemapPolygonGroups(const TMap& Remap); /** Transfer the source polygon group data to the destination polygon group. The source data is append to the destination */ MESHDESCRIPTION_API void TransferPolygonGroup(FPolygonGroupID SourceID, FPolygonGroupID DestinationID); ////////////////////////////////////////////////////////////////////// // Whole mesh operations /** Determines if calling Compact() would perform actual compaction or not. */ MESHDESCRIPTION_API bool NeedsCompact() const; /** Compacts the data held in the mesh description, and returns an object describing how the IDs have been remapped. */ MESHDESCRIPTION_API void Compact( FElementIDRemappings& OutRemappings ); /** Remaps the element IDs in the mesh description according to the passed in object */ MESHDESCRIPTION_API void Remap( const FElementIDRemappings& Remappings ); /** Gets the number of UV element channels */ int32 GetNumUVElementChannels() const { return UVElements->GetNumChannels(); } /** Sets the specified number of UV channels */ MESHDESCRIPTION_API void SetNumUVChannels(const int32 NumUVChannels); /** Returns bounds of vertices */ MESHDESCRIPTION_API FBoxSphereBounds GetBounds() const; /** Retriangulates the entire mesh */ MESHDESCRIPTION_API void TriangulateMesh(); /** Reverses the winding order of all polygons in the mesh */ MESHDESCRIPTION_API void ReverseAllPolygonFacing(); MESHDESCRIPTION_API float GetTriangleCornerAngleForVertex(const FTriangleID TriangleID, const FVertexID VertexID) const; MESHDESCRIPTION_API float GetPolygonCornerAngleForVertex(const FPolygonID PolygonID, const FVertexID VertexID) const; MESHDESCRIPTION_API FBox ComputeBoundingBox() const; private: MESHDESCRIPTION_API FPlane ComputePolygonPlane(TArrayView PerimeterVertexInstanceIDs) const; MESHDESCRIPTION_API FVector ComputePolygonNormal(TArrayView PerimeterVertexInstanceIDs) const; private: MESHDESCRIPTION_API void Initialize(); MESHDESCRIPTION_API void InitializeIndexers(); MESHDESCRIPTION_API void Cache(); MESHDESCRIPTION_API void CreateVertexInstance_Internal(const FVertexInstanceID VertexInstanceID, const FVertexID VertexID); MESHDESCRIPTION_API void CreateEdge_Internal(const FEdgeID EdgeID, const FVertexID VertexID0, const FVertexID VertexID1); MESHDESCRIPTION_API void CreateTriangle_Internal(const FTriangleID TriangleID, const FPolygonGroupID PolygonGroupID, TArrayView VertexInstanceIDs, TArray* OutEdgeIDs); MESHDESCRIPTION_API void CreatePolygon_Internal(const FPolygonID PolygonID, const FPolygonGroupID PolygonGroupID, TArrayView VertexInstanceIDs, TArray* OutEdgeIDs); MESHDESCRIPTION_API void CreatePolygonTriangles(const FPolygonID PolygonID, TArrayView VertexInstanceIDs); MESHDESCRIPTION_API void RemovePolygonTriangles(const FPolygonID PolygonID); template