// Copyright Epic Games, Inc. All Rights Reserved. #include "Dataflow/ChaosFleshCreateTetrahedralCollectionNode.h" #include "Chaos/Utilities.h" #include "ChaosFlesh/ChaosFlesh.h" #include "ChaosFlesh/FleshCollection.h" #include "ChaosFlesh/FleshCollectionUtility.h" #include "ChaosLog.h" #include "DynamicMesh/DynamicMesh3.h" #include "DynamicMesh/DynamicMeshAABBTree3.h" #include "Engine/SkeletalMesh.h" #include "Engine/StaticMesh.h" #include "FTetWildWrapper.h" #include "Generate/IsosurfaceStuffing.h" #include "GeometryCollection/GeometryCollectionAlgo.h" #include "MeshDescription.h" #include "MeshDescriptionToDynamicMesh.h" #include "Rendering/SkeletalMeshLODImporterData.h" #include "Rendering/SkeletalMeshModel.h" #include "SkeletalMeshLODModelToDynamicMesh.h" // MeshModelingBlueprints #include "Spatial/FastWinding.h" #include "Spatial/MeshAABBTree3.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(ChaosFleshCreateTetrahedralCollectionNode) //============================================================================= // FGenerateTetrahedralCollectionDataflowNodes //============================================================================= void FGenerateTetrahedralCollectionDataflowNodes::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&Collection)) { TUniquePtr InCollection(GetValue(Context, &Collection).NewCopy()); TObjectPtr InStaticMesh(GetValue>(Context, &StaticMesh)); TObjectPtr InSkeletalMesh(FindInput(&SkeletalMesh) ? GetValue>(Context, &SkeletalMesh) : nullptr); if (InStaticMesh || InSkeletalMesh) { #if WITH_EDITORONLY_DATA UE::Geometry::FDynamicMesh3 DynamicMesh; if (InStaticMesh) { // make a mesh description for UE::Geometry tools FMeshDescriptionToDynamicMesh GetSourceMesh; bool bUsingHiResSource = InStaticMesh->IsHiResMeshDescriptionValid(); const FMeshDescription* UseSourceMeshDescription = (bUsingHiResSource) ? InStaticMesh->GetHiResMeshDescription() : InStaticMesh->GetMeshDescription(0); GetSourceMesh.Convert(UseSourceMeshDescription, DynamicMesh); } else if (InSkeletalMesh) { // Check first if we have bulk data available and non-empty. constexpr int32 LODIndex = 0; FMeshDescription SourceMesh; if (InSkeletalMesh->HasMeshDescription(LODIndex)) { InSkeletalMesh->CloneMeshDescription(LODIndex, SourceMesh); } FMeshDescriptionToDynamicMesh Converter; Converter.Convert(&SourceMesh, DynamicMesh); } if (!bComputeByComponent) { if (Method == TetMeshingMethod::IsoStuffing) { EvaluateIsoStuffing(Context, InCollection, DynamicMesh); } else if (Method == TetMeshingMethod::TetWild) { EvaluateTetWild(Context, InCollection, DynamicMesh); } else { ensureMsgf(false, TEXT("FGenerateTetrahedralCollectionDataflowNodes unsupported Method.")); } } else { TArray> ConnectedComponents; TArray Faces; Faces.SetNum(DynamicMesh.TriangleCount()); for (int32 i = 0; i < DynamicMesh.TriangleCount(); ++i) { Faces[i] = FIntVector3(DynamicMesh.GetTriangle(i)); } Chaos::Utilities::FindConnectedRegions(Faces, ConnectedComponents); TArray> CollectionBuffer; for (int32 i = 0; i < ConnectedComponents.Num(); i++) { CollectionBuffer.Add(TUniquePtr(new FFleshCollection())); //CollectionBuffer[i]->AddElement(1, FGeometryCollection::TransformGroup); } ParallelFor(ConnectedComponents.Num(), [&](int32 i) { UE::Geometry::FDynamicMesh3 ComponentDynamicMesh; TArray ComponentFaces; ComponentFaces.SetNum(ConnectedComponents[i].Num()); for (FVector V : DynamicMesh.VerticesItr()) { ComponentDynamicMesh.AppendVertex(V); } for (int32 j = 0; j < ConnectedComponents[i].Num(); j++) { int32 ElementIndex = ConnectedComponents[i][j]; for (int32 ie = 0; ie < 3; ie++) { ComponentDynamicMesh.AppendTriangle(Faces[ElementIndex][0], Faces[ElementIndex][1], Faces[ElementIndex][2]); } } ComponentDynamicMesh.CompactInPlace(); if (Method == TetMeshingMethod::IsoStuffing) { EvaluateIsoStuffing(Context, CollectionBuffer[i], ComponentDynamicMesh); } else if (Method == TetMeshingMethod::TetWild) { EvaluateTetWild(Context, CollectionBuffer[i], ComponentDynamicMesh); } }); for (int32 i = 0; i < ConnectedComponents.Num(); i++) { if (CollectionBuffer[i]->NumElements(FGeometryCollection::VerticesGroup)) { TSet VertexToDeleteSet; GeometryCollectionAlgo::ComputeStaleVertices(CollectionBuffer[i].Get(), VertexToDeleteSet); TArray SortedVertices = VertexToDeleteSet.Array(); SortedVertices.Sort(); if (VertexToDeleteSet.Num()) CollectionBuffer[i]->RemoveElements(FGeometryCollection::VerticesGroup, SortedVertices); int32 GeomIndex = InCollection->AppendGeometry(*CollectionBuffer[i].Get()); } } } #else ensureMsgf(false, TEXT("FGenerateTetrahedralCollectionDataflowNodes is an editor only node.")); #endif } // end if InStaticMesh || InSkeletalMesh SetValue(Context, *InCollection, &Collection); } } void FGenerateTetrahedralCollectionDataflowNodes::EvaluateIsoStuffing( UE::Dataflow::FContext& Context, TUniquePtr& InCollection, const UE::Geometry::FDynamicMesh3& DynamicMesh) const { #if WITH_EDITORONLY_DATA if (NumCells > 0 && (-.5 <= OffsetPercent && OffsetPercent <= 0.5)) { // Tet mesh generation UE::Geometry::TIsosurfaceStuffing IsosurfaceStuffing; UE::Geometry::FDynamicMeshAABBTree3 Spatial(&DynamicMesh); UE::Geometry::TFastWindingTree FastWinding(&Spatial); UE::Geometry::FAxisAlignedBox3d Bounds = Spatial.GetBoundingBox(); IsosurfaceStuffing.Bounds = FBox(Bounds); double CellSize = Bounds.MaxDim() / NumCells; IsosurfaceStuffing.CellSize = CellSize; IsosurfaceStuffing.IsoValue = .5 + OffsetPercent; IsosurfaceStuffing.Implicit = [&FastWinding, &Spatial](FVector3d Pos) { FVector3d Nearest = Spatial.FindNearestPoint(Pos); double WindingSign = FastWinding.FastWindingNumber(Pos) - .5; return FVector3d::Distance(Nearest, Pos) * FMathd::SignNonZero(WindingSign); }; UE_LOG(LogChaosFlesh, Display, TEXT("Generating tet mesh via IsoStuffing...")); IsosurfaceStuffing.Generate(); if (IsosurfaceStuffing.Tets.Num() > 0) { TArray Vertices; Vertices.SetNumUninitialized(IsosurfaceStuffing.Vertices.Num()); TArray Elements; Elements.SetNumUninitialized(IsosurfaceStuffing.Tets.Num()); TArray SurfaceElements = UE::Dataflow::GetSurfaceTriangles(IsosurfaceStuffing.Tets, !bDiscardInteriorTriangles); for (int32 Tdx = 0; Tdx < IsosurfaceStuffing.Tets.Num(); ++Tdx) { Elements[Tdx] = IsosurfaceStuffing.Tets[Tdx]; } for (int32 Vdx = 0; Vdx < IsosurfaceStuffing.Vertices.Num(); ++Vdx) { Vertices[Vdx] = IsosurfaceStuffing.Vertices[Vdx]; } TUniquePtr TetCollection(FTetrahedralCollection::NewTetrahedralCollection(Vertices, SurfaceElements, Elements)); InCollection->AppendGeometry(*TetCollection.Get()); UE_LOG(LogChaosFlesh, Display, TEXT("Generated tet mesh via IsoStuffing, num vertices: %d num tets: %d"), Vertices.Num(), Elements.Num()); } else { UE_LOG(LogChaosFlesh, Warning, TEXT("IsoStuffing produced 0 tetrahedra.")); } } #else ensureMsgf(false, TEXT("FGenerateTetrahedralCollectionDataflowNodes is an editor only node.")); #endif } void FGenerateTetrahedralCollectionDataflowNodes::EvaluateTetWild( UE::Dataflow::FContext& Context, TUniquePtr& InCollection, const UE::Geometry::FDynamicMesh3& DynamicMesh) const { #if WITH_EDITORONLY_DATA if (/* placeholder for conditions for exec */true) { // Pull out Vertices and Triangles TArray Verts; TArray Tris; for (FVector V : DynamicMesh.VerticesItr()) { Verts.Add(V); } for (UE::Geometry::FIndex3i Tri : DynamicMesh.TrianglesItr()) { Tris.Emplace(Tri.A, Tri.B, Tri.C); } // Tet mesh generation UE::Geometry::FTetWild::FTetMeshParameters Params; Params.bCoarsen = bCoarsen; Params.bExtractManifoldBoundarySurface = bExtractManifoldBoundarySurface; Params.bSkipSimplification = bSkipSimplification; Params.EpsRel = EpsRel; Params.MaxIts = MaxIterations; Params.StopEnergy = StopEnergy; Params.IdealEdgeLengthRel = IdealEdgeLengthRel; Params.bInvertOutputTets = bInvertOutputTets; TArray TetVerts; TArray Tets; FProgressCancel Progress; UE_LOG(LogChaosFlesh, Display,TEXT("Generating tet mesh via TetWild...")); if (UE::Geometry::FTetWild::ComputeTetMesh(Params, Verts, Tris, TetVerts, Tets, &Progress)) { TArray SurfaceElements = UE::Dataflow::GetSurfaceTriangles(Tets, !bDiscardInteriorTriangles); TUniquePtr TetCollection(FTetrahedralCollection::NewTetrahedralCollection(TetVerts, SurfaceElements, Tets)); InCollection->AppendGeometry(*TetCollection.Get()); UE_LOG(LogChaosFlesh, Display, TEXT("Generated tet mesh via TetWild, num vertices: %d num tets: %d"), TetVerts.Num(), Tets.Num()); } else { UE_LOG(LogChaosFlesh, Error, TEXT("TetWild tetrahedral mesh generation failed.")); } } #else ensureMsgf(false, TEXT("FGenerateTetrahedralCollectionDataflowNodes is an editor only node.")); #endif }