Files
UnrealEngine/Engine/Plugins/Experimental/ChaosFlesh/Source/ChaosFleshNodes/Private/Dataflow/ChaosFleshGenerateSurfaceBindingsNode.cpp
2025-05-18 13:04:45 +08:00

824 lines
29 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Dataflow/ChaosFleshGenerateSurfaceBindingsNode.h"
#include "Dataflow/DataflowInputOutput.h"
#include "Dataflow/DataflowNodeFactory.h"
#include "Chaos/AABBTree.h"
#include "Chaos/BoundingVolumeHierarchy.h"
#include "Chaos/Tetrahedron.h"
#include "Chaos/TriangleMesh.h"
#include "ChaosFlesh/FleshCollectionEngineUtility.h"
#include "ChaosFlesh/TetrahedralCollection.h"
#include "Containers/Map.h"
#include "Engine/StaticMesh.h"
#include "Engine/SkeletalMesh.h"
#include "GeometryCollection/Facades/CollectionTetrahedralBindingsFacade.h"
#include "GeometryCollection/GeometryCollectionEngineConversion.h"
#include "IndexTypes.h"
#include "MeshDescription.h"
#include "MeshDescriptionToDynamicMesh.h"
#include "Rendering/SkeletalMeshLODModel.h"
#include "Rendering/SkeletalMeshModel.h"
#include "Rendering/SkeletalMeshRenderData.h"
#include "UObject/PrimaryAssetId.h"
#include "Dataflow/ChaosFleshNodesUtility.h"
#include "UDynamicMesh.h"
#include "DynamicMesh/InfoTypes.h"
#include "DynamicMesh/MeshNormals.h"
#include "Chaos/TriangleCollisionPoint.h"
#include "Chaos/HierarchicalSpatialHash.h"
DEFINE_LOG_CATEGORY(LogMeshBindings);
void
BuildVertexToVertexAdjacencyBuffer(
const UE::Geometry::FDynamicMesh3 DynamicMesh,
TArray<TArray<uint32>>& OutNeighborNodes)
{
OutNeighborNodes.SetNum(DynamicMesh.VertexCount());
for (int32 TriIdx = 0; TriIdx < DynamicMesh.TriangleCount(); ++TriIdx)
{
const UE::Geometry::FIndex3i& Tri = DynamicMesh.GetTriangle(TriIdx);
TArray<uint32>& N0 = OutNeighborNodes[Tri[0]];
N0.AddUnique(Tri[1]);
N0.AddUnique(Tri[2]);
TArray<uint32>& N1 = OutNeighborNodes[Tri[1]];
N1.AddUnique(Tri[0]);
N1.AddUnique(Tri[2]);
TArray<uint32>& N2 = OutNeighborNodes[Tri[2]];
N2.AddUnique(Tri[0]);
N2.AddUnique(Tri[1]);
}
}
void UnloadMeshDescription(const FMeshDescription& SourceMesh,
TArray<FVector3f>& OutVertices, TArray<FIntVector>& OutTriangles, TArray<TArray<uint32>>& OutNeighborNodes)
{
FDynamicMesh3 DynamicMesh;
FMeshDescriptionToDynamicMesh Converter;
Converter.Convert(&SourceMesh, DynamicMesh);
OutVertices.SetNumUninitialized(DynamicMesh.VertexCount());
for (int32 VertexIdx = 0; VertexIdx < DynamicMesh.VertexCount(); ++VertexIdx)
{
const FVector3d& Pos = DynamicMesh.GetVertex(VertexIdx);
OutVertices[VertexIdx].Set(Pos[0], Pos[1], Pos[2]);
}
OutTriangles.SetNumUninitialized(DynamicMesh.TriangleCount());
for (int32 TriIdx = 0; TriIdx < DynamicMesh.TriangleCount(); ++TriIdx)
{
const UE::Geometry::FIndex3i& Tri = DynamicMesh.GetTriangle(TriIdx);
OutTriangles[TriIdx] = FIntVector(Tri[0], Tri[1], Tri[2]);
}
BuildVertexToVertexAdjacencyBuffer(DynamicMesh, OutNeighborNodes);
}
void
BuildVertexToVertexAdjacencyBuffer(
const FSkeletalMeshLODRenderData& LodRenderData,
TArray<TArray<uint32>>& OutNeighborNodes)
{
const FRawStaticIndexBuffer16or32Interface* IndexBuffer = LodRenderData.MultiSizeIndexContainer.GetIndexBuffer();
const uint32 IndexCount = IndexBuffer->Num();
const FPositionVertexBuffer& VertexBuffer = LodRenderData.StaticVertexBuffers.PositionVertexBuffer;
const uint32 VertexCount = LodRenderData.StaticVertexBuffers.PositionVertexBuffer.GetNumVertices();
OutNeighborNodes.SetNum(0); // clear, to init clean
OutNeighborNodes.SetNum(VertexCount);
int32 BaseTriangle = 0;
int32 BaseVertex = 0;
for (int32 SectionIndex = 0; SectionIndex < LodRenderData.RenderSections.Num(); ++SectionIndex)
{
FSkelMeshRenderSection const& RenderSection = LodRenderData.RenderSections[SectionIndex];
int32 NumTriangles = RenderSection.NumTriangles;
int32 NumVertices = RenderSection.NumVertices;
TArray<uint32> RedirectionArray;
RedirectionArray.SetNum(VertexCount);
TMap<FVector, int32 /*UniqueVertexIndex*/> UniqueIndexMap;
for (int32 TriangleIt = BaseTriangle; TriangleIt < BaseTriangle + NumTriangles; ++TriangleIt)
{
const uint32 V[3] =
{
IndexBuffer->Get(TriangleIt * 3 + 0),
IndexBuffer->Get(TriangleIt * 3 + 1),
IndexBuffer->Get(TriangleIt * 3 + 2)
};
const FVector P[3] =
{
(FVector)VertexBuffer.VertexPosition(V[0]),
(FVector)VertexBuffer.VertexPosition(V[1]),
(FVector)VertexBuffer.VertexPosition(V[2])
};
for (int32 i = 0; i < 3; ++i)
{
const uint32 VertexIndex = RedirectionArray[V[i]] = UniqueIndexMap.FindOrAdd(P[i], V[i]);
TArray<uint32>& AdjacentVertices = OutNeighborNodes[VertexIndex];
for (int32 a = 1; a < 3; ++a)
{
const uint32 AdjacentVertexIndex = V[(i + a) % 3];
if (VertexIndex != AdjacentVertexIndex)
{
AdjacentVertices.AddUnique(AdjacentVertexIndex);
}
}
}
}
for (int32 VertexIt = BaseVertex + 1; VertexIt < BaseVertex + NumVertices; ++VertexIt)
{
// if this vertex has a sibling we copy the data over
const int32 SiblingIndex = RedirectionArray[VertexIt];
if (SiblingIndex != VertexIt)
{
for (int32 i = 0; i < OutNeighborNodes[SiblingIndex].Num(); i++)
{
const uint32 OtherNode = OutNeighborNodes[SiblingIndex][i];
if (OtherNode != VertexIt)
{
OutNeighborNodes[VertexIt].AddUnique(OtherNode);
}
}
}
}
BaseTriangle += NumTriangles;
BaseVertex += NumVertices;
}
}
void
BuildTriangles(
const FSkeletalMeshLODRenderData& LodRenderData,
TArray<FIntVector>& TriangleMesh)
{
const FRawStaticIndexBuffer16or32Interface* IndexBuffer = LodRenderData.MultiSizeIndexContainer.GetIndexBuffer();
const uint32 IndexCount = IndexBuffer->Num();
TriangleMesh.Empty(); // clear, to init clean
int32 BaseTriangle = 0;
for (int32 SectionIndex = 0; SectionIndex < LodRenderData.RenderSections.Num(); ++SectionIndex)
{
FSkelMeshRenderSection const& RenderSection = LodRenderData.RenderSections[SectionIndex];
int32 NumTriangles = RenderSection.NumTriangles;
TriangleMesh.SetNum(BaseTriangle + NumTriangles);
for (int32 TriangleIt = BaseTriangle; TriangleIt < BaseTriangle + NumTriangles; ++TriangleIt)
{
TriangleMesh[TriangleIt] = FIntVector
(
IndexBuffer->Get(TriangleIt * 3 + 0),
IndexBuffer->Get(TriangleIt * 3 + 1),
IndexBuffer->Get(TriangleIt * 3 + 2)
);
}
BaseTriangle += NumTriangles;
}
}
void
FGenerateSurfaceBindings::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
using namespace UE::Geometry;
auto FloatVert = [](FVector3d V) { return FVector3f(V.X, V.Y, V.Z); };
auto DoubleVert = [](FVector3f V) { return FVector3d(V.X, V.Y, V.Z); };
auto TVec3Vert = [](FVector3f V) { return Chaos::TVec3<Chaos::FRealDouble>(V.X, V.Y, V.Z); };
if (Out->IsA(&Collection))
{
TUniquePtr<FTetrahedralCollection> InCollection(GetValue(Context, &Collection).NewCopy<FTetrahedralCollection>());
const TManagedArray<FIntVector4>* Tetrahedron =
InCollection->FindAttribute<FIntVector4>(
FTetrahedralCollection::TetrahedronAttribute, FTetrahedralCollection::TetrahedralGroup);
const TManagedArray<int32>* TetrahedronStart =
InCollection->FindAttribute<int32>(
FTetrahedralCollection::TetrahedronStartAttribute, FGeometryCollection::GeometryGroup);
const TManagedArray<int32>* TetrahedronCount =
InCollection->FindAttribute<int32>(
FTetrahedralCollection::TetrahedronCountAttribute, FGeometryCollection::GeometryGroup);
const TManagedArray<TArray<int32>>* IncidentElements =
InCollection->FindAttribute<TArray<int32>>(
FTetrahedralCollection::IncidentElementsAttribute, FGeometryCollection::VerticesGroup);
const TManagedArray<FIntVector>* Triangle =
InCollection->FindAttribute<FIntVector>(
"Indices", FGeometryCollection::FacesGroup);
const TManagedArray<int32>* FacesStart =
InCollection->FindAttribute<int32>(
"FaceStart", FGeometryCollection::GeometryGroup);
const TManagedArray<int32>* FacesCount =
InCollection->FindAttribute<int32>(
"FaceCount", FGeometryCollection::GeometryGroup);
const TManagedArray<FVector3f>* Vertex =
InCollection->FindAttribute<FVector3f>(
"Vertex", "Vertices");
TObjectPtr<const USkeletalMesh> SkeletalMesh = GetValue(Context, &SkeletalMeshIn);
TObjectPtr<const UStaticMesh> StaticMesh = GetValue(Context, &StaticMeshIn);
TObjectPtr<UDynamicMesh> OutSKMDynamicMesh = NewObject<UDynamicMesh>();
OutSKMDynamicMesh->Reset();
FDynamicMesh3& OutSKMDynamicMesh3 = OutSKMDynamicMesh->GetMeshRef();
const bool UseSkeletalMesh = SkeletalMesh != nullptr;
const bool UseStaticMesh = StaticMesh != nullptr;
if ((UseSkeletalMesh || UseStaticMesh) &&
Tetrahedron && TetrahedronStart && TetrahedronCount &&
Triangle && FacesStart && FacesCount &&
Vertex)
{
// Extract positions to bind
FString MeshId;
TArray<TArray<FVector3f>> RenderMeshVertices;
TArray<TArray<FIntVector>> RenderMeshTriangles;
TArray<TArray<TArray<uint32>>> RenderMeshNeighborNodes;
if (UseSkeletalMesh)
{
FPrimaryAssetId Id = SkeletalMesh->GetPrimaryAssetId();
MeshId = ChaosFlesh::GetMeshId(SkeletalMesh, bUseSkeletalMeshImportModel);
if (!bUseSkeletalMeshImportModel)
{
FSkeletalMeshRenderData* RenderData = SkeletalMesh->GetResourceForRendering();
RenderMeshVertices.SetNum(RenderData->LODRenderData.Num());
RenderMeshTriangles.SetNum(RenderData->LODRenderData.Num());
RenderMeshNeighborNodes.SetNum(RenderData->LODRenderData.Num());
for (int32 i = 0; i < RenderData->LODRenderData.Num(); i++)
{
FSkeletalMeshLODRenderData* LODRenderData = &RenderData->LODRenderData[i];
const FPositionVertexBuffer& PositionVertexBuffer =
LODRenderData->StaticVertexBuffers.PositionVertexBuffer;
TArray<FVector3f>& Vertices = RenderMeshVertices[i];
Vertices.SetNumUninitialized(PositionVertexBuffer.GetNumVertices());
for (uint32 j = 0; j < PositionVertexBuffer.GetNumVertices(); j++)
{
const FVector3f& Pos = PositionVertexBuffer.VertexPosition(j);
Vertices[j] = Pos;
}
TArray<TArray<uint32>>& NeighborNodes = RenderMeshNeighborNodes[i];
BuildVertexToVertexAdjacencyBuffer(*LODRenderData, NeighborNodes);
BuildTriangles(*LODRenderData, RenderMeshTriangles[i]);
}
}
#if WITH_EDITOR
else // Import Model
{
const int32 LODIndex = 0;
RenderMeshVertices.SetNum(1);
RenderMeshTriangles.SetNum(1);
RenderMeshNeighborNodes.SetNum(1);
// Check first if we have bulk data available and non-empty.
FMeshDescription SourceMesh;
#if WITH_EDITORONLY_DATA
if (SkeletalMesh->HasMeshDescription(LODIndex))
{
SkeletalMesh->CloneMeshDescription(LODIndex, SourceMesh);
}
else
#endif
{
// Fall back on the LOD model directly if no bulk data exists. When we commit
// the mesh description, we override using the bulk data. This can happen for older
// skeletal meshes, from UE 4.24 and earlier.
const FSkeletalMeshModel* SkeletalMeshModel = SkeletalMesh->GetImportedModel();
if (SkeletalMeshModel && SkeletalMeshModel->LODModels.IsValidIndex(LODIndex))
{
SkeletalMeshModel->LODModels[LODIndex].GetMeshDescription(SkeletalMesh, LODIndex, SourceMesh);
}
}
UnloadMeshDescription(SourceMesh, RenderMeshVertices[LODIndex], RenderMeshTriangles[LODIndex], RenderMeshNeighborNodes[LODIndex]);
}
#endif
}
else // StaticMesh
{
MeshId = ChaosFlesh::GetMeshId(StaticMesh);
const int32 LODIndex = 0;
RenderMeshVertices.SetNum(1);
RenderMeshTriangles.SetNum(1);
RenderMeshNeighborNodes.SetNum(1);
FMeshDescription* MeshDescription = FGeometryCollectionEngineConversion::GetMaxResMeshDescriptionWithNormalsAndTangents(StaticMesh);
if (MeshDescription)
{
UnloadMeshDescription(*MeshDescription, RenderMeshVertices[LODIndex], RenderMeshTriangles[LODIndex], RenderMeshNeighborNodes[LODIndex]);
}
else
{
Context.Warning(FString::Printf(
TEXT("No MeshDescription found in Static Mesh [%s]."),
*StaticMesh->GetName()),
this, Out);
}
}
TArray<FString> GeometryGroupGuidsLocal = GetValue(Context, &GeometryGroupGuidsIn);
const TManagedArray<FString>* Guids = InCollection->FindAttribute<FString>("Guid", FGeometryCollection::GeometryGroup);
// Build Tetrahedra
TArray<Chaos::TTetrahedron<Chaos::FReal>> Tets; // Index 0 == TetMeshStart
TArray<Chaos::TTetrahedron<Chaos::FReal>*> BVHTetPtrs;
//
// Init boundary mesh for projections.
//
TArray<FIntVector> Triangles;
Chaos::FTriangleMesh TetBoundaryMesh;
Chaos::FTriangleMesh::TBVHType<Chaos::FRealDouble> TetBoundaryBVH;
TArray<Chaos::FVec3> VertexD;
TConstArrayView<Chaos::TVec3<Chaos::FRealDouble>> VertexDView(VertexD);
TArray<Chaos::FVec3> PointNormals;
Chaos::THierarchicalSpatialHash<int32, Chaos::FRealDouble> SpatialHash;
TArray<int32> GeometryGroupSelected;
if (IsConnected(&TransformSelection))
{
FDataflowTransformSelection InTransformSelection = GetValue(Context, &TransformSelection);
if (InTransformSelection.Num() == InCollection->NumElements(FGeometryCollection::TransformGroup))
{
GeometryGroupSelected = InCollection->TransformSelectionToGeometryIndices(InTransformSelection.AsArray());
}
else
{
UE_LOG(LogMeshBindings, Error,
TEXT("TransformSelection size: %d is different from Collection Transform group size: %d"),
InTransformSelection.Num(),
InCollection->NumElements(FGeometryCollection::TransformGroup));
return;
}
}
else
{
for (int32 TetMeshIdx = 0; TetMeshIdx < TetrahedronStart->Num(); ++TetMeshIdx)
{
GeometryGroupSelected.Add(TetMeshIdx);
}
}
TArray<int32> UsedTetsIndexToGlobalTetIndex;
TArray<int32> GlobalTetIndexToUsedTetsIndex;
GlobalTetIndexToUsedTetsIndex.Init(INDEX_NONE, Tetrahedron->Num());
int32 NumGuidHit = 0;
for (const int32& TetMeshIdx: GeometryGroupSelected)
{
if (GeometryGroupGuidsLocal.Num() && Guids)
{
if (!(*Guids)[TetMeshIdx].IsEmpty() && !GeometryGroupGuidsLocal.Contains((*Guids)[TetMeshIdx]))
{
continue;
}
else
{
NumGuidHit++;
}
}
const int32 TetMeshStart = (*TetrahedronStart)[TetMeshIdx];
const int32 TetMeshCount = (*TetrahedronCount)[TetMeshIdx];
if (TetMeshCount > 0)
{
for (int32 i = 0; i < TetMeshCount; i++)
{
const int32 Idx = TetMeshStart + i;
const FIntVector4& Tet = (*Tetrahedron)[Idx];
GlobalTetIndexToUsedTetsIndex[Idx] = Tets.Num();
Tets.Add(Chaos::TTetrahedron<Chaos::FReal>(
(*Vertex)[Tet[0]],
(*Vertex)[Tet[1]],
(*Vertex)[Tet[2]],
(*Vertex)[Tet[3]]));
UsedTetsIndexToGlobalTetIndex.Add(Idx);
}
if (bDoSurfaceProjection)
{
for (int32 FaceIdx = (*FacesStart)[TetMeshIdx]; FaceIdx < (*FacesStart)[TetMeshIdx] + (*FacesCount)[TetMeshIdx]; ++FaceIdx)
{
Triangles.Add((*Triangle)[FaceIdx]);
}
}
}
}
if (GeometryGroupGuidsLocal.Num() && NumGuidHit == 0)
{
UE_LOG(LogMeshBindings, Error,
TEXT("GeometryGroupGuids contains %d guids but none was matched (empty guids are ignored)."),
GeometryGroupGuidsLocal.Num());
return;
}
// Init BVH for tetrahedra.
BVHTetPtrs.SetNumUninitialized(Tets.Num());
for (int32 TetIdx = 0; TetIdx < Tets.Num(); ++TetIdx)
{
BVHTetPtrs[TetIdx] = &Tets[TetIdx];
}
Chaos::TBoundingVolumeHierarchy<
TArray<Chaos::TTetrahedron<Chaos::FReal>*>,
TArray<int32>,
Chaos::FReal,
3> TetBVH(BVHTetPtrs);
// Init BVH for surface triangle mesh.
if (bDoSurfaceProjection)
{
//Todo(Chaosflesh): refactor reinterpret_cast in case the memory layout of the vector types not the same
TetBoundaryMesh.Init(
reinterpret_cast<const TArray<Chaos::TVec3<int32>>&>(Triangles), 0, -1, false);
// Promote vertices to double because that's what FTriangleMesh wants.
VertexD.SetNumUninitialized(Vertex->Num());
for (int32 i = 0; i < VertexD.Num(); i++)
{
VertexD[i] = Chaos::FVec3((*Vertex)[i][0], (*Vertex)[i][1], (*Vertex)[i][2]);
}
VertexDView = TConstArrayView<Chaos::TVec3<Chaos::FRealDouble>>(VertexD);
PointNormals = TetBoundaryMesh.GetPointNormals(VertexDView, false, true);
TetBoundaryMesh.BuildBVH(VertexDView, TetBoundaryBVH);
TetBoundaryMesh.BuildSpatialHash(VertexDView, SpatialHash, Chaos::FRealDouble(SurfaceProjectionSearchRadius));
}
//
// Do intersection tests against tets, then the surface.
//
TArray<TArray<FIntVector4>> Parents; Parents.SetNum(RenderMeshVertices.Num());
TArray<TArray<FVector4f>> Weights; Weights.SetNum(RenderMeshVertices.Num());
TArray<TArray<FVector3f>> Offsets; Offsets.SetNum(RenderMeshVertices.Num());
TArray<TArray<float>> Masks; Masks.SetNum(RenderMeshVertices.Num());
TArray<int32> Orphans;
TArray<int32> OrphansCopy;
int32 TetHits = 0;
int32 TriHits = 0;
int32 Adoptions = 0;
int32 NumOrphans = 0;
for (int32 LOD = 0; LOD < RenderMeshVertices.Num(); LOD++)
{
Parents[LOD].SetNumUninitialized(RenderMeshVertices[LOD].Num());
Weights[LOD].SetNumUninitialized(RenderMeshVertices[LOD].Num());
Offsets[LOD].SetNumUninitialized(RenderMeshVertices[LOD].Num());
Masks[LOD].SetNumUninitialized(RenderMeshVertices[LOD].Num());
int32 NumTetNotCollocated = 0;
TArray<int32> TetIntersections; TetIntersections.Reserve(64);
for (int32 i = 0; i < RenderMeshVertices[LOD].Num(); i++)
{
Parents[LOD][i] = FIntVector4(INDEX_NONE);
Weights[LOD][i] = FVector4f(0);
Offsets[LOD][i] = FVector3f(0);
Masks[LOD][i] = 0.0; // Shader does skinning for this vertex
const FVector3f& Pos = RenderMeshVertices[LOD][i];
Chaos::TVec3<Chaos::FReal> PosD(Pos[0], Pos[1], Pos[2]);
TetIntersections = TetBVH.FindAllIntersections(PosD);
int32 j = 0;
for (; j < TetIntersections.Num(); j++)
{
const int32 TetIdx = TetIntersections[j];
if (!Tets[TetIdx].Outside(Pos, 0.f)) // includes boundary
{
Chaos::TVector<Chaos::FReal, 4> WeightsD = Tets[TetIdx].GetBarycentricCoordinates(Pos);
const int32 GlobalTetIndex = UsedTetsIndexToGlobalTetIndex[TetIdx];
FVector3f EmbeddedPos =
(*Vertex)[(*Tetrahedron)[GlobalTetIndex][0]] * WeightsD[0] +
(*Vertex)[(*Tetrahedron)[GlobalTetIndex][1]] * WeightsD[1] +
(*Vertex)[(*Tetrahedron)[GlobalTetIndex][2]] * WeightsD[2] +
(*Vertex)[(*Tetrahedron)[GlobalTetIndex][3]] * WeightsD[3];
if ((Pos - EmbeddedPos).SquaredLength() < UE_SMALL_NUMBER)
{
TetHits++;
Parents[LOD][i] = (*Tetrahedron)[GlobalTetIndex];
Weights[LOD][i] = FVector4f(WeightsD[0], WeightsD[1], WeightsD[2], WeightsD[3]);
Offsets[LOD][i] = FVector3f(0);
Masks[LOD][i] = 1.0; // Shader does sim for this vertex
break;
}
else
{
NumTetNotCollocated++;
if (NumTetNotCollocated == 1)
{
UE_LOG(LogMeshBindings, Error,
TEXT("Vertex position does not collocate with interpolated position, for example LOD %d, SKM vertex %d, tetrahedron %d, distance = %.4f)"),
LOD,
i,
TetIdx,
(Pos - EmbeddedPos).Length());
}
}
}
}
if (j == TetIntersections.Num())
{
bool bSuccess = false;
if (bDoSurfaceProjection)
{
TArray<Chaos::TTriangleCollisionPoint<Chaos::FRealDouble>> Result;
//PointClosestTriangleQuery instead of SmoothProject
if (TetBoundaryMesh.PointClosestTriangleQuery(SpatialHash, VertexDView, i, TVec3Vert(Pos), Chaos::FRealDouble(SurfaceProjectionSearchRadius), Chaos::FRealDouble(SurfaceProjectionSearchRadius),
[](const int32 PointIndex, const int32 TriangleIndex)->bool
{
// use all nearby triangles
return true;
},
Result))
{
for (const Chaos::TTriangleCollisionPoint<Chaos::FRealDouble>& CollisionPoint : Result)
{
const FIntVector& Tri = Triangles[CollisionPoint.Indices[1]];
TriHits++;
Parents[LOD][i][0] = Tri[0];
Parents[LOD][i][1] = Tri[1];
Parents[LOD][i][2] = Tri[2];
Parents[LOD][i][3] = INDEX_NONE;
Weights[LOD][i][0] = CollisionPoint.Bary[1];
Weights[LOD][i][1] = CollisionPoint.Bary[2];
Weights[LOD][i][2] = CollisionPoint.Bary[3];
Weights[LOD][i][3] = 0.0;
const FVector3f EmbeddedPos =
Weights[LOD][i][0] * Vertex->GetConstArray()[Tri[0]] +
Weights[LOD][i][1] * Vertex->GetConstArray()[Tri[1]] +
Weights[LOD][i][2] * Vertex->GetConstArray()[Tri[2]];
Offsets[LOD][i] = EmbeddedPos - Pos;
Masks[LOD][i] = 1.0; // Shader does sim for this vertex
bSuccess = true;
break;
}
}
}
if (!bSuccess)
{
// Despair...
Orphans.Add(i);
Parents[LOD][i][0] = INDEX_NONE;
Parents[LOD][i][1] = INDEX_NONE;
Parents[LOD][i][2] = INDEX_NONE;
Parents[LOD][i][3] = INDEX_NONE;
Weights[LOD][i][0] = 0.0;
Weights[LOD][i][1] = 0.0;
Weights[LOD][i][2] = 0.0;
Weights[LOD][i][3] = 0.0;
Offsets[LOD][i][0] = 0.0;
Offsets[LOD][i][1] = 0.0;
Offsets[LOD][i][2] = 0.0;
Masks[LOD][i] = 0.0; // Shader does skinning for this vertex
}
} // if !TetIntersections
} // end for all vertices
//
// Advancing front orphan reparenting
//
if (!RenderMeshNeighborNodes.IsValidIndex(LOD))
{
continue;
}
const TArray<TArray<uint32>>& NeighborNodes = RenderMeshNeighborNodes[LOD];
OrphansCopy = Orphans;
TBitArray<> IsOrphan(false, RenderMeshVertices[LOD].Num());
while (bDoOrphanReparenting && Orphans.Num())
{
for (int32 Orphan : Orphans)
{
IsOrphan[Orphan] = true;
}
// Find the orphan with the fewest number of orphan neighbors, and the
// most non-orphans in their 1 ring.
int32 Orphan = INDEX_NONE;
int32 NumOrphanNeighbors = TNumericLimits<int32>::Max();
int32 NumNonOrphanNeighbors = 0;
for (int32 i = 0; i < Orphans.Num(); i++)
{
int32 CurrOrphan = Orphans[i];
if (!NeighborNodes.IsValidIndex(CurrOrphan))
{
continue;
}
const TArray<uint32>& Neighbors = NeighborNodes[CurrOrphan];
int32 OrphanCount = 0;
int32 NonOrphanCount = 0;
for (int32 j = 0; j < Neighbors.Num(); j++)
{
if (IsOrphan[Neighbors[j]])
{
OrphanCount++;
}
else
{
NonOrphanCount++;
}
}
if (OrphanCount <= NumOrphanNeighbors && NonOrphanCount > NumNonOrphanNeighbors)
{
Orphan = CurrOrphan;
NumOrphanNeighbors = OrphanCount;
NumNonOrphanNeighbors = NonOrphanCount;
}
}
if (Orphan == INDEX_NONE)
{
// We only have orphans with no neighbors left.
break;
}
const FVector3f& Pos = RenderMeshVertices[LOD][Orphan];
Chaos::TVec3<Chaos::FReal> PosD(Pos[0], Pos[1], Pos[2]);
// Use the parent simplices of non-orphan neighbors as test candidates.
Chaos::FReal CurrDist = TNumericLimits<Chaos::FReal>::Max();
const TArray<uint32>& Neighbors = NeighborNodes[Orphan];
bool FoundBinding = false;
for (int32 i = 0; i < Neighbors.Num(); i++)
{
const uint32 Neighbor = Neighbors[i];
if (IsOrphan[Neighbor])
{
continue;
}
const FIntVector4& P = Parents[LOD][Neighbor];
int32 NumValid = 0;
for (int32 j = 0; j < 4; j++)
{
NumValid += P[j] != INDEX_NONE ? 1 : 0;
}
if (NumValid == 0)
{
continue;
}
else
{
// Find tets that share parent indices
for (int32 j = 0; j < 4; j++)
{
const int32 ParentIdx = P[j];
if (IncidentElements->GetConstArray().IsValidIndex(ParentIdx))
{
const TArray<int32>& NeighborTets = (*IncidentElements)[ParentIdx];
for (int32 k = 0; k < NeighborTets.Num(); k++)
{
const int32 TetIdx = NeighborTets[k];
const int32 UsedTetIdx = GlobalTetIndexToUsedTetsIndex[TetIdx];
if (ensure(Tets.IsValidIndex(UsedTetIdx)))
{
const Chaos::FTetrahedron& Tet = Tets[UsedTetIdx];
Chaos::TVec4<Chaos::FReal> W;
Chaos::TVec3<Chaos::FReal> EmbeddedPos = Tet.FindClosestPointAndBary(PosD, W, 0 /*Tolerance*/); //Tolerance should be small negative number or zero
Chaos::TVec3<Chaos::FReal> O = EmbeddedPos - PosD;
Chaos::FReal Dist = O.SquaredLength();
if (Dist < CurrDist) // Closest neighbor tet
{
CurrDist = Dist;
Parents[LOD][Orphan] = (*Tetrahedron)[TetIdx];
Weights[LOD][Orphan] = FVector4f(W[0], W[1], W[2], W[3]);
Offsets[LOD][Orphan] = FVector3f(O[0], O[1], O[2]);
Masks[LOD][i] = 1.0; // Shader does sim for this vertex
FoundBinding = true;
}
}
}
}
}
}
} // end for all neighbors
// Whether or not we successfully reparented, remove the orphan from the list.
IsOrphan[Orphan] = false;
Orphans.Remove(Orphan);
if (FoundBinding)
{
Adoptions++;
}
else
{
NumOrphans++;
}
} // end while(Orphans)
NumOrphans += Orphans.Num();
if (Orphans.Num() > 0)
{
UE_LOG(LogMeshBindings, Error,
TEXT("'%s' - Generated mesh bindings between tet mesh and %s mesh of '%s' LOD %d - stats:\n"
" Render vertices num: %d\n"
" Vertices in tetrahedra: %d\n"
" Vertices bound to tet surface: %d\n"
" Orphaned vertices reparented: %d\n"
" Vertices orphaned: %d"),
*GetName().ToString(),
bUseSkeletalMeshImportModel ? TEXT("import") : TEXT("render"),
*MeshId, LOD,
RenderMeshVertices[LOD].Num(), TetHits, TriHits, Adoptions, NumOrphans);
}
else
{
UE_LOG(LogMeshBindings, Display,
TEXT("'%s' - Generated mesh bindings between tet mesh and %s mesh of '%s' LOD %d - stats:\n"
" Render vertices num: %d\n"
" Vertices in tetrahedra: %d\n"
" Vertices bound to tet surface: %d\n"
" Orphaned vertices reparented: %d\n"
" Vertices orphaned: %d"),
*GetName().ToString(),
bUseSkeletalMeshImportModel ? TEXT("import") : TEXT("render"),
* MeshId, LOD,
RenderMeshVertices[LOD].Num(), TetHits, TriHits, Adoptions, NumOrphans);
}
if (NumTetNotCollocated)
{
UE_LOG(LogMeshBindings, Error,
TEXT("%d vertex positions do not collocate with interpolated position for LOD %d"),
NumTetNotCollocated,
LOD);
}
} // end for all LOD
// Stash bindings in the geometry collection
GeometryCollection::Facades::FTetrahedralBindings TetBindings(*InCollection);
TetBindings.DefineSchema();
FName MeshName(*MeshId, MeshId.Len());
for (int32 LOD = 0; LOD < RenderMeshVertices.Num(); LOD++)
{
TetBindings.AddBindingsGroup(/*TetMeshIdx = */ 0, MeshName, LOD);
TetBindings.SetBindingsData(Parents[LOD], Weights[LOD], Offsets[LOD], Masks[LOD]);
}
//Write DynamicMesh
if (RenderMeshVertices.Num())
{
OutSKMDynamicMesh3.EnableAttributes();
OutSKMDynamicMesh3.EnableVertexColors(FVector3f(1, 0, 0));
TBitArray<> WasOrphan(false, RenderMeshVertices[0].Num());
for (int32 OrphanIdx : OrphansCopy)
{
WasOrphan[OrphanIdx] = true;
}
for (int32 VertexIndex = 0; VertexIndex < RenderMeshVertices[0].Num(); ++VertexIndex)
{
FVertexInfo VertexInfo;
VertexInfo.Position = DoubleVert(RenderMeshVertices[0][VertexIndex]);
VertexInfo.bHaveC = true;
if (Parents[0][VertexIndex][0] == INDEX_NONE)
{
VertexInfo.Color = FVector3f(1, 0, 0); //red if orphan
}
else
{
if (Parents[0][VertexIndex][3] == INDEX_NONE)
{
VertexInfo.Color = FVector3f(0, 0, 1); //blue if on surface
}
else
{
VertexInfo.Color = FVector3f(0, 1, 0); //green if in tet
}
if (WasOrphan[VertexIndex])
{
VertexInfo.Color += FVector3f(1, 0, 0); //add red if was orphan
}
}
OutSKMDynamicMesh3.AppendVertex(VertexInfo);
}
for (int32 TriangleIndex = 0; TriangleIndex < RenderMeshTriangles[0].Num(); ++TriangleIndex)
{
OutSKMDynamicMesh3.AppendTriangle(FIndex3i(
RenderMeshTriangles[0][TriangleIndex][0],
RenderMeshTriangles[0][TriangleIndex][1],
RenderMeshTriangles[0][TriangleIndex][2])
);
}
// Compute normals
OutSKMDynamicMesh3.EnableVertexNormals(FVector3f(1, 0, 0));
FMeshNormals MeshNormals(&OutSKMDynamicMesh3);
MeshNormals.ComputeVertexNormals();
for (int32 VertexIndex = 0; VertexIndex < RenderMeshVertices[0].Num(); ++VertexIndex)
{
OutSKMDynamicMesh3.SetVertexNormal(VertexIndex, FloatVert(MeshNormals[VertexIndex]));
}
}
}
SetValue<const FManagedArrayCollection&>(Context, *InCollection, &Collection);
SetValue(Context, OutSKMDynamicMesh, &SKMDynamicMesh);
}
}