// Copyright Epic Games, Inc. All Rights Reserved. // Port of geometry3cpp MeshRegionBoundaryLoops #pragma once #include "DynamicMesh/DynamicMesh3.h" #include "DynamicMesh/DynamicMeshOverlay.h" #include "EdgeLoop.h" #include "Util/SparseIndexCollectionTypes.h" namespace UE { namespace Geometry { /** * Extract FEdgeLoops on the boundary of a set of triangles of a mesh. * @todo this was an early port and possibly can be optimized */ class FMeshRegionBoundaryLoops { public: // INPUTS /** Mesh we are finding loops on */ const FDynamicMesh3* Mesh = nullptr; /** Resulting set of loops filled by Compute() */ TArray Loops; // OUTPUTS /** If true, we did not completely succeed in extracting all loops */ bool bFailed = false; public: FMeshRegionBoundaryLoops() {} GEOMETRYCORE_API FMeshRegionBoundaryLoops(const FDynamicMesh3* MeshIn, const TArray& RegionTris, bool bAutoCompute = true); GEOMETRYCORE_API void SetMesh(const FDynamicMesh3* MeshIn, const TArray& RegionTris); /** * Find set of FEdgeLoops on the border of the input triangle set * @return false if errors occurred, in this case output set is incomplete */ GEOMETRYCORE_API bool Compute(); /** @return number of loops found by Compute() */ int32 GetLoopCount() const { return Num(); } /** @return number of loops found by Compute() */ int32 Num() const { return Loops.Num(); } /** @return Loop at the given index */ const FEdgeLoop& operator[](int Index) const { return Loops[Index]; } const TArray& GetLoops() const { return Loops; } /** @return index of loop with maximum number of vertices */ GEOMETRYCORE_API int GetMaxVerticesLoopIndex() const; template using ElementIDAndValue = TPair; template using VidOverlayMap = TMap>; /** * Generates a map from vertex ID's of the loop vertices to corresponding overlay element ID's * and values. The elements chosen are those associated with the internal triangle whose * edge goes out of that vertex in the loop direction. * The function is useful in cases where the triangles inside the loop will be deleted and replaced, * and we want to keep the same UV's on the border vertices. * Does not clear LoopVidsToOverlayElementsOut before use (simply adds to it), so could * be used for multiple loops. * * @returns false if there is an error (if the overlay does not have an element for one of the vids) */ template GEOMETRYCORE_API bool GetLoopOverlayMap(const FEdgeLoop& LoopIn, const TDynamicMeshOverlay& Overlay, VidOverlayMap& LoopVidsToOverlayElementsOut); /** * Given a map generated by GetLoopOverlayMap(), checks that the overlay elements pointed to by the * map still exist in the overlay, and sets the ID to be invalid if not. * This can be called after deleting the triangles inside a loop to update the generated map * and remove any UV elements that no longer exist (because the vertex was on a UV seam). */ template GEOMETRYCORE_API void UpdateLoopOverlayMapValidity(VidOverlayMap& LoopVidsToOverlayElements, const TDynamicMeshOverlay& Overlay); /** * Find the edge loop border around a set of triangles of a Mesh. * This is computed via local walk and so does not create any full-mesh data structures. * However current implementation may not be efficient for large triangle sets. * Algorithm terminates if a non-manifold boundary is detected, and returns false if some triangles are unused. * @param Loop output loop will be stored here. This value is garbage if false is returned. * @return true if a single well-formed loop was found, false if non-manifold or failure case encountered */ static GEOMETRYCORE_API bool GetTriangleSetBoundaryLoop(const FDynamicMesh3& Mesh, const TArray& Tris, FEdgeLoop& Loop); protected: // sets of included triangles and edges FIndexFlagSet Triangles; FIndexFlagSet Edges; TArray edges_roi; bool IsEdgeOnBoundary(int eid) const { return Edges.Contains(eid); } // returns true for both internal and mesh boundary edges // tid_in and tid_out are triangles 'in' and 'out' of set, respectively GEOMETRYCORE_API bool IsEdgeOnBoundary(int eid, int& tid_in, int& tid_out) const; // return same indices as GetEdgeV, but oriented based on attached triangle GEOMETRYCORE_API FIndex2i GetOrientedEdgeVerts(int eID, int tid_in); // returns first two boundary edges, and count of total boundary edges GEOMETRYCORE_API int GetVertexBoundaryEdges(int vID, int& e0, int& e1); // e needs to be large enough (ie call GetVtxBoundaryEdges, or as large as max one-ring) // returns count, ie number of elements of e that were filled GEOMETRYCORE_API int GetAllVertexBoundaryEdges(int vID, TArray& e); // [TODO] cache this : a dictionary? we will not need very many, but we will // need each multiple times! GEOMETRYCORE_API FVector3d GetVertexNormal(int vid); // ok, bdry_edges[0...bdry_edges_count] contains the boundary edges coming out of bowtie_v. // We want to pick the best one to continue the loop that came : to bowtie_v on incoming_e. // If the loops are all sane, then we will get the smallest loops by "turning left" at bowtie_v. GEOMETRYCORE_API int FindLeftTurnEdge(int incoming_e, int bowtie_v, TArray& bdry_edges, int bdry_edges_count, const FIndexFlagSet& used_edges); // This is called when loopV contains one or more "bowtie" vertices. // These vertices *might* be duplicated : loopV (but not necessarily) // If they are, we have to break loopV into subloops that don't contain duplicates. GEOMETRYCORE_API bool TryExtractSubloops(TArray& loopV, const TArray& loopE, const TArray& bowties, TArray& SubLoopsOut); }; } // end namespace UE::Geometry } // end namespace UE