// Copyright Epic Games, Inc. All Rights Reserved. // Port of geometry3Sharp MeshEditor #pragma once #include "DynamicMesh/DynamicMesh3.h" #include "DynamicMesh/DynamicMeshAttributeSet.h" #include "EdgeLoop.h" #include "Util/SparseIndexCollectionTypes.h" #include "DynamicMesh/MeshIndexMappings.h" #include "MeshAdapter.h" namespace UE { namespace Geometry { struct FDynamicSubmesh3; /** * FDynamicMeshEditResult is used to return information about new mesh elements * created by mesh changes, primarily in FDynamicMeshEditor */ struct DYNAMICMESH_API FDynamicMeshEditResult { /** New vertices created by an edit */ TArray NewVertices; /** New triangles created by an edit. Note that this list may be empty if the operation created quads or polygons */ TArray NewTriangles; /** New quads created by an edit, where each quad is a pair of triangle IDs */ TArray NewQuads; /** New polygons created by an edit, where each polygon is a list of triangle IDs */ TArray> NewPolygons; /** New triangle groups created by an edit */ TArray NewGroups; /** New normal overlay elements */ TArray> NewNormalOverlayElements; /** New color overlay elements */ TArray NewColorOverlayElements; /** clear this data structure */ void Reset() { NewVertices.Reset(); NewTriangles.Reset(); NewQuads.Reset(); NewPolygons.Reset(); NewGroups.Reset(); NewNormalOverlayElements.Reset(); NewColorOverlayElements.Reset(); } /** Flatten the triangle/quad/polygon lists into a single list of all triangles */ void GetAllTriangles(TArray& TrianglesOut) const; }; /** * FDynamicMeshEditor implements low-level mesh editing operations. These operations * can be used to construct higher-level operations. For example an Extrude operation * could be implemented via DuplicateTriangles() and StitchLoopMinimal(). */ class DYNAMICMESH_API FDynamicMeshEditor { public: /** The mesh we will be editing */ FDynamicMesh3* Mesh; FDynamicMeshEditor(FDynamicMesh3* MeshIn) { Mesh = MeshIn; } /** * Remove any vertices that are not used by any triangles */ bool RemoveIsolatedVertices(); ////////////////////////////////////////////////////////////////////////// // Create and Remove Triangle Functions ////////////////////////////////////////////////////////////////////////// /** * Stitch together two loops of vertices with a quad-strip of triangles. * Loops must be oriented (ordered) correctly for your use case. * If loop edges are [a,b] and [c,d], then tris added are [b,a,d] and [a,c,d] * @param Loop1 first loop of sequential vertices * @param Loop2 second loop of sequential vertices * @param ResultOut lists of newly created triangles/vertices/etc * @return true if operation succeeded. If a failure occurs, any added triangles are removed via RemoveTriangles */ bool StitchVertexLoopsMinimal(const TArray& VertexLoop1, const TArray& VertexLoop2, FDynamicMeshEditResult& ResultOut); /** * Given a sequence of edges in the form given by ConvertLoopToTriVidPairSequence and a loop of vertices, * stitch the two together. Edges are identified as (TriangleID, (0-2 subindex of vert1, 0-2 subindex of vert2)), * and should match the number of vertex ids in the corresponding vertex loop (each i corresponds to the edge * between vert i and i+1 in the vertex loop). * This works much like StitchVertexLoopsMinimal, but it can be used after one of the loops undergoes * vertex splits (to remove bowties) as long as the "loop" (which may now be broken) can still be * identified by the composing triangles. In particular, this is used in extrude and inset where the * base loop needs to remain as a sequence of Vids (since some of these may be free vertices, unattached * to a triangle) but the offset/inset loop is guaranteed to be attached to triangles yet may have its * Vids change due to bowtie splits. * * If an entry of TriVidPairs1 resolves to verts [a,b] and the corresponding pair of verts in VertexLoop2 * are [c,d], then tris added are [b,a,d] and [a,c,d]. * @param TriVidPairs1 First loop, given as output of ConvertLoopToTriVidPairSequence() * @param VertexLoop2 Second loop, given as vertex IDs * @param ResultOut lists of newly created triangles/vertices/etc * @return true if operation succeeded. If a failure occurs, any added triangles are removed via RemoveTriangles */ bool StitchVertexLoopToTriVidPairSequence( const TArray>>& TriVidPairs1, const TArray& VertexLoop2, FDynamicMeshEditResult& ResultOut); /** * Converts a loop to a sequence of edge identifiers that are both Vid and Eid independent. The identifiers are * of the form (TriangleID, ([0,2] vert sub index, [0,2] vert sub index)), and they are used in some cases * (extrude, inset) where we want to perform vertex splits along a region boundary (to remove bowties) but * need to maintain a record of the original loop path. We don't use (TriangleID, Eid sub index) because that * makes it harder to keep track of individual edge directionality. * * @param VidLoop Vertex IDs of loop to convert. * @param EdgeLoop Edge IDs of loop to convert, must match VidLoop. * @param TriVertPairsOut Output * @return false if not successful (usually due to a mismatch between VidLoop and EdgeLoop). */ static bool ConvertLoopToTriVidPairSequence(const FDynamicMesh3& Mesh, const TArray& VidLoop, const TArray& EdgeLoop, TArray>>& TriVertPairsOut); /** * Stitch together two loops of vertices where vertices are only sparsely corresponded * @param VertexIDs1 first array of sequential vertices * @param MatchedIndices1 indices into the VertexIDs1 array of vertices that have a corresponding match in the VertexIDs2 array; Must be ordered * @param VertexIDs2 second array of sequential vertices * @param MatchedIndices2 indices into the VertexIDs2 array of vertices that have a corresponding match in the VertexIDs1 array; Must be ordered * @param ResultOut lists of newly created triangles/vertices/etc * @return true if operation succeeded. If a failure occurs, any added triangles are removed via RemoveTriangles */ bool StitchSparselyCorrespondedVertexLoops(const TArray& VertexIDs1, const TArray& MatchedIndices1, const TArray& VertexIDs2, const TArray& MatchedIndices2, FDynamicMeshEditResult& ResultOut, bool bReverseOrientation = false); /** * Weld together two loops of vertices. Loops must be oriented (ordered) correctly for your use case. * @param Loop1 first loop of sequential vertices * @param Loop2 second loop of sequential vertices. These vertices and their edges will not exist after the operation. * @return true if operation succeeded, false if any errors ocurred */ bool WeldVertexLoops(const TArray& VertexLoop1, const TArray& VertexLoop2); /** * Fill hole with a triangle fan given an existing (unconnected) center vertex and * an ordered loop of boundary vertices on the hole border. * @param CenterVertex Index of floating vertex in the center of the hole * @param VertexLoop Indices of vertices on the boundary of the hole, in order * @param ResultOut lists of newly created triangles * @return true if operation succeeded. If a failure occurs, any added triangles are removed via RemoveTriangles. */ bool AddTriangleFan_OrderedVertexLoop(int CenterVertex, const TArray& VertexLoop, int GroupID, FDynamicMeshEditResult& ResultOut); /** * Duplicate triangles of a mesh. This duplicates the current groups and also any attributes existing on the triangles. * @param Triangles the triangles to duplicate * @param IndexMaps returned mappings from old to new triangles/vertices/etc (you may initialize to optimize memory usage, etc) * @param ResultOut lists of newly created triangles/vertices/etc */ void DuplicateTriangles(const TArray& Triangles, FMeshIndexMappings& IndexMaps, FDynamicMeshEditResult& ResultOut); /** * Pair of associated vertex and edge loops. Assumption is that loop sizes are the same and have a 1-1 correspondence. * OuterVertices may include unreferenced vertices, and OuterEdges may include InvalidID edges in that case. */ struct FLoopPairSet { TArray OuterVertices; TArray OuterEdges; TArray InnerVertices; TArray InnerEdges; /** If true, some OuterVertices are not referenced by any triangles, and the pair of OuterEdges that would touch that vertex are InvalidID */ bool bOuterIncludesIsolatedVertices; }; /** * Finds boundary loops of connected components of a set of triangles, and duplicates the vertices * along the boundary, such that the triangles become disconnected. * If the triangle set includes boundary vertices, they cannot be disconnected as they will become unreferenced. * The function returns false if boundary vertices are found, unless bHandleBoundaryVertices = true. * In that case, the boundary vertex is duplicated, and the original vertex is kept with the "Inner" loop. * The new unreferenced vertex is left on the "Outer" loop, and the attached edges do not exist so are set as InvalidID. * The FLoopPairSet.bOuterIncludesIsolatedVertices flag is set to true if boundary vertices are encountered. * Note: This function can create a mesh with bowties if you pass in a subset of triangles that would have bowtie connectivity. * (it is impossible to create the paired LoopSetOut with 1:1 vertex pairs without leaving in the bowties?) * @param Triangles set of triangles * @param LoopSetOut set of boundary loops. LoopA is original loop which remains with "outer" triangles, and LoopB is new boundary loop of triangle set * @param bHandleBoundaryVertices if true, boundary vertices are handled as described above, otherwise function aborts and returns false if a boundary vertex is encountered * @return true on success */ bool DisconnectTriangles(const TArray& Triangles, TArray& LoopSetOut, bool bHandleBoundaryVertices); /** * Disconnects triangles (without constructing boundary loops) such that the input Triangles are not connected to any other triangles in the mesh * @param Triangles set of triangles * @param bPreventBowties do some additional processing and vertex splitting as needed to prevent the creation of any new bowties */ void DisconnectTriangles(const TArray& Triangles, bool bPreventBowties = true); /** * Splits all bowties across the whole mesh */ void SplitBowties(FDynamicMeshEditResult& ResultOut); /** * Splits any bowties specifically on the given vertex, and updates (does not reset!) ResultOut with any added vertices */ void SplitBowties(int VertexID, FDynamicMeshEditResult& ResultOut); /** * Splits bowties attached to any of the given triangles, and updates (does not reset!) ResultOut with any added vertices */ void SplitBowtiesAtTriangles(const TArray& TriangleIDs, FDynamicMeshEditResult& ResultOut); /** * In ReinsertSubmesh, a problem can arise where the mesh we are * inserting has duplicate triangles of the base mesh. * * This can lead to problematic behavior later. We can do various things, * like delete and replace that existing triangle, or just use it instead * of adding a new one. Or fail, or ignore it. * * This enum/argument controls the behavior. * However, fundamentally this kind of problem should be handled upstream!! * For example by not trying to remesh areas that contain nonmanifold geometry... */ enum class EDuplicateTriBehavior : uint8 { EnsureContinue, EnsureAbort, UseExisting, Replace }; /** * Update a Base Mesh from a Submesh; See FMeshRegionOperator::BackPropropagate for a usage example. * * Assumes that Submesh has been modified, but boundary loop has been preserved, and that old submesh has already been removed from this mesh. * Just appends new vertices and rewrites triangles. * * @param Submesh The mesh to insert back into its BaseMesh. The original submesh triangles should have already been removed when this function is called * @param SubToNewV Mapping from submesh to vertices in the updated base mesh * @param NewTris If not null, will be filled with IDs of triangles added to the base mesh * @param DuplicateBehavior Choice of what to do if inserting a triangle from the submesh would duplicate a triangle in the base mesh * @return true if submesh successfully inserted, false if any triangles failed (which happens if triangle would result in non-manifold mesh) */ bool ReinsertSubmesh(const FDynamicSubmesh3& Submesh, FOptionallySparseIndexMap& SubToNewV, TArray* NewTris = nullptr, EDuplicateTriBehavior DuplicateBehavior = EDuplicateTriBehavior::EnsureAbort); /** * Remove a list of triangles from the mesh, and optionally any vertices that are now orphaned * @param Triangles the triangles to remove * @param bRemoveIsolatedVerts if true, remove vertices that end up with no triangles * @return true if all removes succeeded */ bool RemoveTriangles(const TArray& Triangles, bool bRemoveIsolatedVerts); /** * Remove any connected components with volume or area below the given thresholds * @param MinVolume Remove components with less volume than this * @param MinArea Remove components with less area than this * @return number of components removed */ int RemoveSmallComponents(double MinVolume, double MinArea = 0.0, int MinTriangleCount = 0); /** * Remove a list of triangles from the mesh, and optionally any vertices that are now orphaned * @param Triangles the triangles to remove * @param bRemoveIsolatedVerts if true, remove vertices that end up with no triangles * @param OnRemoveTriFunc called for each triangle to be removed * @return true if all removes succeeded */ bool RemoveTriangles(const TArray& Triangles, bool bRemoveIsolatedVerts, TFunctionRef OnRemoveTriFunc); ////////////////////////////////////////////////////////////////////////// // Normal utility functions ////////////////////////////////////////////////////////////////////////// /** * Reverse the orientation of the given triangles, and optionally flip relevant normals * @param Triangles the triangles to modify * @param bInvertNormals if ture we call InvertTriangleNormals() */ void ReverseTriangleOrientations(const TArray& Triangles, bool bInvertNormals); /** * Flip the normals of the given triangles. This includes their vertex normals, if they * exist, as well as any per-triangle attribute normals. @todo currently creates full-mesh bit arrays, could be more efficient on subsets * @param Triangles the triangles to modify */ void InvertTriangleNormals(const TArray& Triangles); /** * Calculate and set the per-triangle normals of the two input quads. * Average of the two face normals is used unless the quad is planar * @param QuadTris pair of triangle IDs. If second ID is invalid, it is ignored * @param bIsPlanar if the quad is known to be planar, operation is more efficient * @return the normal vector that was set */ FVector3f ComputeAndSetQuadNormal(const FIndex2i& QuadTris, bool bIsPlanar = false); /** * Create and set new shared per-triangle normals for a pair of triangles that share one edge (ie a quad) * @param QuadTris pair of triangle IDs. If second ID is invalid, it is ignored * @param Normal normal vector to set */ void SetQuadNormals(const FIndex2i& QuadTris, const FVector3f& Normal); /** * Create and set new shared per-triangle normals for a list of triangles * @param Triangles list of triangle IDs * @param Normal normal vector to set */ void SetTriangleNormals(const TArray& Triangles, const FVector3f& Normal); /** * Create and set new shared per-triangle normals for a list of triangles. * Normal at each vertex is calculated based only on average of triangles in set * @param Triangles list of triangle IDs */ void SetTriangleNormals(const TArray& Triangles); /** * For a 'tube' of triangles connecting loops of corresponded vertices, set smooth normals such that corresponding vertices have corresponding normals */ void SetTubeNormals(const TArray& Triangles, const TArray& VertexIDs1, const TArray& MatchedIndices1, const TArray& VertexIDs2, const TArray& MatchedIndices2); ////////////////////////////////////////////////////////////////////////// // UV utility functions ////////////////////////////////////////////////////////////////////////// /** * Project the two triangles of the quad onto a plane defined by the ProjectionFrame and use that to create/set new shared per-triangle UVs. * UVs are translated so that their bbox min-corner is at origin, and scaled by given scale factor * @param QuadTris pair of triangle IDs. If second ID is invalid, it is ignored * @param ProjectFrame vertices are projected into XY axes of this frame * @param UVScaleFactor UVs are scaled by this uniform scale factor * @param UVTranslation UVs are translated after scaling * @param UVLayerIndex which UV layer to operate on (must exist) */ void SetQuadUVsFromProjection(const FIndex2i& QuadTris, const FFrame3d& ProjectionFrame, float UVScaleFactor = 1.0f, const FVector2f& UVTranslation = FVector2f::Zero(), int UVLayerIndex = 0); /** * Project triangles onto a plane defined by the ProjectionFrame and use that to create/set new shared per-triangle UVs. * UVs can be translated so that their bbox min-corner is at origin, and scaled by given scale factor * This is an older function signature that forwards to the more specific one. * * @param Triangles TArray of triangle IDs * @param ProjectFrame vertices are projected into XY axes of this frame * @param UVScaleFactor UVs are scaled by this uniform scale factor * @param UVTranslation UVs are translated after scaling * @param bShiftToOrigin Whether to translate the UV coordinates to make their bounding box min corner be (0,0) before applying UVTranslation * @param UVLayerIndex which UV layer to operate on (must exist) */ void SetTriangleUVsFromProjection(const TArray& Triangles, const FFrame3d& ProjectionFrame, float UVScaleFactor = 1.0f, const FVector2f& UVTranslation = FVector2f::Zero(), bool bShiftToOrigin = true, int32 UVLayerIndex = 0); /** * Project triangles onto a plane defined by the ProjectionFrame and use that to create/set new shared per-triangle UVs. * * @param Triangles TArray of triangle IDs * @param ProjectFrame Vertices are projected into XY axes of this frame * @param UVScaleFactor UVs are scaled by these factors * @param UVTranslation UVs are translated after scaling by these amounts * @param UVLayerIndex Which UV layer to operate on (must exist) * @param bShiftToOrigin Whether to translate the UV coordinates to make their bounding box min corner be (0,0) before applying UVTranslation * @param bNormalizeBeforeScaling Whether to place the UV coordinates into the range [0,1] before applying UVScaleFactor and UVTranslation */ void SetTriangleUVsFromProjection(const TArray& Triangles, const FFrame3d& ProjectionFrame, const FVector2f& UVScale = FVector2f::One(), const FVector2f& UVTranslation = FVector2f::Zero(), int UVLayerIndex = 0, bool bShiftToOrigin = true, bool bNormalizeBeforeScaling = false); /** * For triangles connecting loops of corresponded vertices, set UVs in a cylindrical pattern so that the U coordinate starts at 0 for the first corresponded pair of vertices, and cycles around to 1 * Assumes Triangles array stores indices of triangles in progressively filling the tube, starting with VertexIDs*[0]. (This is used to set the UVs correctly at the seam joining the start & end of the loop) */ void SetGeneralTubeUVs(const TArray& Triangles, const TArray& VertexIDs1, const TArray& MatchedIndices1, const TArray& VertexIDs2, const TArray& MatchedIndices2, const TArray& UValues, const FVector3f& VDir, float UVScaleFactor = 1.0f, const FVector2f& UVTranslation = FVector2f::Zero(), int UVLayerIndex = 0); /** * Rescale UVs for the whole mesh, for the given UV attribute layer * @param UVScale Scale factor to multiply into UVs. If in world space, this is in centimeters relative to the average UV scale * @param bWorldSpace If true, UVs are rescaled relative to an absolute world scale. * @param UVLayerIndex which UV layer to operate on (must exist) * @param ToWorld Optionally transform vertices for world space scaling */ void RescaleAttributeUVs(float UVScale = 1.0f, bool bWorldSpace = false, int UVLayerIndex = 0, TOptional ToWorld = TOptional()); ////////////////////////////////////////////////////////////////////////// // mesh element copying / duplication ////////////////////////////////////////////////////////////////////////// /** * Find "new" vertex for input vertex under Index mapping, or create new if missing * @param VertexID the source vertex we want a copy of * @param IndexMaps source/destination mapping of already-duplicated vertices * @param ResultOut newly-created vertices are stored here * @return index of duplicate vertex */ int FindOrCreateDuplicateVertex(int VertexID, FMeshIndexMappings& IndexMaps, FDynamicMeshEditResult& ResultOut); /** * Find "new" group for input group under Index mapping, or create new if missing * @param TriangleID the source triangle whose group we want a copy of * @param IndexMaps source/destination mapping of already-duplicated groups * @param ResultOut newly-created groups are stored here * @return index of duplicate group */ int FindOrCreateDuplicateGroup(int TriangleID, FMeshIndexMappings& IndexMaps, FDynamicMeshEditResult& ResultOut); /** * Find "new" UV for input UV element under Index mapping, or create new if missing * @param ElementID the source UV we want a duplicate of * @param UVLayerIndex which UV layer to consider * @param IndexMaps source/destination mapping of already-duplicated UVs * @return index of duplicate UV in given UV layer */ int FindOrCreateDuplicateUV(int ElementID, int UVLayerIndex, FMeshIndexMappings& IndexMaps); /** * Find "new" normal for input normal element under Index mapping, or create new if missing * @param ElementID the source normal we want a duplicate of * @param NormalLayerIndex which normal layer to consider * @param IndexMaps source/destination mapping of already-duplicated normals * @param ResultOut any newly created element indices are stored in NewNormalOverlayElements here. Note that * NewNormalOverlayElements must have size > NormalLayerIndex. * @return index of duplicate normal in given normal layer */ int FindOrCreateDuplicateNormal(int ElementID, int NormalLayerIndex, FMeshIndexMappings& IndexMaps, FDynamicMeshEditResult* ResultOut = nullptr); /** * Find "new" color for input color element under Index mapping, or create new if missing * @param ElementID the source color we want a duplicate of * @param IndexMaps source/destination mapping of already-duplicated colors * @param ResultOut any newly created element indices are stored in NewColorOverlayElements here. * @return index of duplicate color in given color layer */ int FindOrCreateDuplicateColor(int ElementID, FMeshIndexMappings& IndexMaps, FDynamicMeshEditResult* ResultOut); /** * Copy all attribute-layer values from one triangle to another, using the IndexMaps to track and re-use shared attribute values. * @param FromTriangleID source triangle * @param ToTriangleID destination triangle * @param IndexMaps mappings passed to FindOrCreateDuplicateX functions to track already-created attributes * @param ResultOut information about new attributes is stored here (@todo finish populating this, at time of writing only normal overlay elements get tracked) */ void CopyAttributes(int FromTriangleID, int ToTriangleID, FMeshIndexMappings& IndexMaps, FDynamicMeshEditResult& ResultOut); /** * Append input mesh to our internal mesh * @param AppendMesh mesh to append * @param IndexMapsOut mesh element index mappings generated in this append operation * @param PositionTransform optional transformation function applied to mesh vertex positions * @param NormalTransform optional transformation function applied to mesh normals */ void AppendMesh(const FDynamicMesh3* AppendMesh, FMeshIndexMappings& IndexMapsOut, TFunction PositionTransform = nullptr, TFunction NormalTransform = nullptr); /** * Append input mesh to our internal Mesh. If the internal Mesh has attributes enabled, * per-triangle normals will be computed and set. No other attributes are initialized. * @param AppendMesh mesh to append * @param IndexMapsOut mesh element index mappings generated in this append operation * @param PositionTransform optional transformation function applied to mesh vertex positions */ void AppendMesh(const TTriangleMeshAdapter* AppendMesh, FMeshIndexMappings& IndexMapsOut, TFunction PositionTransform = nullptr); /** * Append normals from one attribute overlay to another. * Assumes that AppendMesh has already been appended to Mesh. * Note that this function has no dependency on .Mesh, it could be static * @param AppendMesh mesh that owns FromNormals attribute overlay * @param FromNormals Normals overlay we want to append from (owned by AppendMesh) * @param ToNormals Normals overlay we want to append to (owned by Mesh) * @param VertexMap map from AppendMesh vertex IDs to vertex IDs applicable to ToNormals (ie of .Mesh) * @param TriangleMap map from AppendMesh triangle IDs to triangle IDs applicable to ToNormals (ie of .Mesh) * @param NormalTransform optional transformation function applied to mesh normals * @param NormalMapOut Mapping from element IDs of FromNormals to new element IDs in ToNormals */ void AppendNormals(const FDynamicMesh3* AppendMesh, const FDynamicMeshNormalOverlay* FromNormals, FDynamicMeshNormalOverlay* ToNormals, const FIndexMapi& VertexMap, const FIndexMapi& TriangleMap, TFunction NormalTransform, FIndexMapi& NormalMapOut); /** * Append UVs from one attribute overlay to another. * Assumes that AppendMesh has already been appended to Mesh. * Note that this function has no dependency on .Mesh, it could be static * @param AppendMesh mesh that owns FromUVs attribute overlay * @param FromUVs UV overlay we want to append from (owned by AppendMesh) * @param ToUVs UV overlay we want to append to (owned by Mesh) * @param VertexMap map from AppendMesh vertex IDs to vertex IDs applicable to ToUVs (ie of .Mesh) * @param TriangleMap map from AppendMesh triangle IDs to triangle IDs applicable to ToUVs (ie of .Mesh) * @param UVMapOut Mapping from element IDs of FromUVs to new element IDs in ToUVs */ void AppendUVs(const FDynamicMesh3* AppendMesh, const FDynamicMeshUVOverlay* FromUVs, FDynamicMeshUVOverlay* ToUVs, const FIndexMapi& VertexMap, const FIndexMapi& TriangleMap, FIndexMapi& UVMapOut); /** * Append Colors from one attribute overlay to another. * Assumes that AppendMesh has already been appended to Mesh. * Note that this function has no dependency on .Mesh, it could be static * @param AppendMesh mesh that owns FromOverlay attribute overlay * @param FromOverlay Color overlay we want to append from (owned by AppendMesh) * @param ToOverlay Color overlay we want to append to (owned by Mesh) * @param VertexMap map from AppendMesh vertex IDs to vertex IDs applicable to ToOverlay (ie of .Mesh) * @param TriangleMap map from AppendMesh triangle IDs to triangle IDs applicable to ToOverlay (ie of .Mesh) * @param ColorMapOut Mapping from element IDs of FromUVs to new element IDs in ToOverlay */ void AppendColors(const FDynamicMesh3* AppendMesh, const FDynamicMeshColorOverlay* FromOverlay, FDynamicMeshColorOverlay* ToOverlay, const FIndexMapi& VertexMap, const FIndexMapi& TriangleMap, FIndexMapi& ColorMapOut); /** * Append triangles of an existing mesh. This duplicates the current groups and also any attributes existing on the triangles. * @param SourceMesh the mesh to copy from * @param SourceTriangles the triangles to copy * @param IndexMaps returned mappings from old to new triangles/vertices/etc (you may initialize to optimize memory usage, etc) * @param ResultOut lists of newly created triangles/vertices/etc * @param bComputeTriangleMap if true, computes the triangle map section of IndexMaps (which is not needed for the append to work, so is optional) */ void AppendTriangles(const FDynamicMesh3* SourceMesh, const TArrayView& SourceTriangles, FMeshIndexMappings& IndexMaps, FDynamicMeshEditResult& ResultOut, bool bComputeTriangleMap = true); /** * Create multiple meshes out of the source mesh by splitting triangles out. * Static because it creates multiple output meshes, so doesn't quite fit in the FDynamicMeshEditor model of operating on a single mesh * * @param SourceMesh * @param SplitMeshes * @param TriIDToMeshID * @return true if needed split, false if there were not multiple mesh ids so no split was needed */ static bool SplitMesh(const FDynamicMesh3* SourceMesh, TArray& SplitMeshes, TFunctionRef TriIDToMeshID, int DeleteMeshID = -1); }; } // end namespace UE::Geometry } // end namespace UE