// Copyright Epic Games, Inc. All Rights Reserved. #include "Operations/SimpleHoleFiller.h" #include "DynamicMeshEditor.h" #include "CompGeom/PolygonTriangulation.h" #include "ExplicitUseGeometryMathTypes.h" // using UE::Geometry::(math types) using namespace UE::Geometry; bool FSimpleHoleFiller::Fill(int GroupID) { if (GroupID < 0 && Mesh->HasTriangleGroups()) { GroupID = Mesh->AllocateTriangleGroup(); } if (Loop.GetVertexCount() < 3) { return false; } // this just needs one triangle if (Loop.GetVertexCount() == 3) { FIndex3i Tri(Loop.Vertices[0], Loop.Vertices[2], Loop.Vertices[1]); int NewTID = Mesh->AppendTriangle(Tri, GroupID); if (NewTID < 0) { return false; } NewTriangles = { NewTID }; NewVertex = FDynamicMesh3::InvalidID; return true; } // [TODO] 4-case? could check nbr normals to figure out best internal edge... bool bOK = false; if (FillType == EFillType::PolygonEarClipping) { bOK = Fill_EarClip(GroupID); } else { bOK = Fill_Fan(GroupID); } return bOK; } bool FSimpleHoleFiller::Fill_Fan(int GroupID) { // compute centroid FVector3d c = FVector3d::Zero(); for (int i = 0; i < Loop.GetVertexCount(); ++i) { c += Mesh->GetVertex(Loop.Vertices[i]); } c *= 1.0 / Loop.GetVertexCount(); // add centroid vtx NewVertex = Mesh->AppendVertex(c); // stitch triangles FDynamicMeshEditor Editor(Mesh); FDynamicMeshEditResult AddFanResult; if (!Editor.AddTriangleFan_OrderedVertexLoop(NewVertex, Loop.Vertices, GroupID, AddFanResult)) { Mesh->RemoveVertex(NewVertex, true, false); NewVertex = FDynamicMesh3::InvalidID; return false; } NewTriangles = AddFanResult.NewTriangles; return true; } bool FSimpleHoleFiller::Fill_EarClip(int GroupID) { TArray> Vertices; int32 NumVertices = Loop.GetVertexCount(); for (int32 i = 0; i < NumVertices; ++i) { Vertices.Add(Mesh->GetVertex(Loop.Vertices[i])); } TArray Triangles; PolygonTriangulation::TriangulateSimplePolygon(Vertices, Triangles); for (FIndex3i PolyTriangle : Triangles) { FIndex3i MeshTriangle( Loop.Vertices[PolyTriangle.A], Loop.Vertices[PolyTriangle.C], Loop.Vertices[PolyTriangle.B]); // Reversing orientation here!! int32 NewTriangle = Mesh->AppendTriangle(MeshTriangle, GroupID); if (NewTriangle >= 0) { NewTriangles.Add(NewTriangle); } } return true; } bool FSimpleHoleFiller::UpdateAttributes(TArray>& VidUVMaps) { if (!Mesh->HasAttributes() || NewTriangles.Num() == 0) { return false; } FDynamicMeshAttributeSet* Attributes = Mesh->Attributes(); check(VidUVMaps.Num() == Attributes->NumUVLayers()); for (int i = 0; i < Attributes->NumUVLayers(); ++i) { FDynamicMeshUVOverlay* UVLayer = Attributes->GetUVLayer(i); FMeshRegionBoundaryLoops::VidOverlayMap& UVMap = VidUVMaps[i]; for (int32 Tid : NewTriangles) { FIndex3i TriVids = Mesh->GetTriangle(Tid); FIndex3i TriUVs; for (int32 j = 0; j < 3; ++j) { int32 Vid = TriVids[j]; if (!UVMap.Contains(Vid)) { return false; } FMeshRegionBoundaryLoops::ElementIDAndValue& VertUVInfo = UVMap[Vid]; if (VertUVInfo.Key == IndexConstants::InvalidID) { TriUVs[j] = UVLayer->AppendElement(VertUVInfo.Value); VertUVInfo.Key = TriUVs[j]; } else if (UVLayer->IsElement(VertUVInfo.Key)) { TriUVs[j] = VertUVInfo.Key; } else { return false; } } UVLayer->SetTriangle(Tid, TriUVs); } } FDynamicMeshEditor MeshEditor(Mesh); MeshEditor.SetTriangleNormals(NewTriangles); return true; }