// Copyright Epic Games, Inc. All Rights Reserved. #include "Dataflow/ChaosFleshGenerateSkeletalBindingsNode.h" #include "Chaos/AABBTree.h" #include "Chaos/BoundingVolumeHierarchy.h" #include "Chaos/Tetrahedron.h" #include "Chaos/TriangleMesh.h" #include "ChaosFlesh/TetrahedralCollection.h" #include "Containers/Map.h" #include "Engine/StaticMesh.h" #include "Dataflow/DataflowInputOutput.h" #include "Engine/SkeletalMesh.h" #include "Dataflow/DataflowNodeFactory.h" #include "GeometryCollection/Facades/CollectionTetrahedralSkeletalBindingsFacade.h" #include "Rendering/SkeletalMeshRenderData.h" #include "UObject/PrimaryAssetId.h" DEFINE_LOG_CATEGORY(LogSkeletalBindings); namespace UE::Dataflow { TArray ChildIndices(TObjectPtr SkeletalMesh, int32 StartIndex) { int32 IndexCount = 0; TArray BranchIndices; TArray ToProcess; int32 CurrentIndex = StartIndex; while (SkeletalMesh->GetRefSkeleton().IsValidIndex(CurrentIndex)) { TArray Buffer; SkeletalMesh->GetRefSkeleton().GetDirectChildBones(CurrentIndex, Buffer); if (Buffer.Num()) { ToProcess.Append(Buffer); } if (!IndexCount) // skip the first index to get only the chidren. { BranchIndices.Add(CurrentIndex); } CurrentIndex = INDEX_NONE; if (ToProcess.Num()) { CurrentIndex = ToProcess.Pop(); } } return BranchIndices; } } void FGenerateSkeletalBindings::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { auto UEVertd = [](FVector3f V) { return FVector3d(V.X, V.Y, V.Z); }; auto UEVertf = [](FVector3d V) { return FVector3f(V.X, V.Y, V.Z); }; if (Out->IsA(&Collection)) { DataType OutCollection = GetValue(Context, &Collection); // Deep copy TManagedArray* Tetrahedron = OutCollection.FindAttribute(FTetrahedralCollection::TetrahedronAttribute, FTetrahedralCollection::TetrahedralGroup); TManagedArray* TetrahedronStart =OutCollection.FindAttribute(FTetrahedralCollection::TetrahedronStartAttribute, FGeometryCollection::GeometryGroup); TManagedArray* TetrahedronCount =OutCollection.FindAttribute(FTetrahedralCollection::TetrahedronCountAttribute, FGeometryCollection::GeometryGroup); TManagedArray>* IncidentElements = OutCollection.FindAttribute>(FTetrahedralCollection::IncidentElementsAttribute, FGeometryCollection::VerticesGroup); TManagedArray* Vertex = OutCollection.FindAttribute("Vertex", "Vertices"); TObjectPtr SkeletalMesh = GetValue>(Context, &SkeletalMeshIn); if (SkeletalMesh && Tetrahedron && TetrahedronStart && TetrahedronCount && Vertex) { // Iterate all the tetrahedral meshs for (int32 TetMeshIdx = 0; TetMeshIdx < TetrahedronStart->Num(); TetMeshIdx++) { const int32 TetMeshStart = (*TetrahedronStart)[TetMeshIdx]; const int32 TetMeshCount = (*TetrahedronCount)[TetMeshIdx]; // Build Tetrahedra TArray> Tets; // Index 0 == TetMeshStart TArray*> BVHTetPtrs; Tets.SetNumUninitialized(TetMeshCount); BVHTetPtrs.SetNumUninitialized(TetMeshCount); for (int32 i = 0; i < TetMeshCount; i++) { const int32 Idx = TetMeshStart + i; const FIntVector4& Tet = (*Tetrahedron)[Idx]; Tets[i] = Chaos::TTetrahedron( (*Vertex)[Tet[0]], (*Vertex)[Tet[1]], (*Vertex)[Tet[2]], (*Vertex)[Tet[3]]); BVHTetPtrs[i] = &Tets[i]; } // Init BVH for tetrahedra. Chaos::TBoundingVolumeHierarchy< TArray*>, TArray, Chaos::FReal, 3> TetBVH(BVHTetPtrs); FName SkeletalMeshName = FName(SkeletalMesh->GetName()); TArray ComponentPose; UE::Dataflow::Animation::GlobalTransforms(SkeletalMesh->GetRefSkeleton(), ComponentPose); TArray Indices = UE::Dataflow::ChildIndices(SkeletalMesh, GetValue(Context, &BoneIndexIn)); // // Do intersection tests against tets // int CurrentIndex = 0; TArray TetIndex; TetIndex.SetNum(Indices.Num()); TArray Weights; Weights.SetNum(Indices.Num()); TArray Offsets; Offsets.SetNum(Indices.Num()); TArray IndexKey; IndexKey.SetNum(Indices.Num()); TArray TetIntersections; TetIntersections.Reserve(64); for (int32 i = 0; i < Indices.Num(); i++) { int32 TransformIndex = Indices[i]; FVector TestPosition = ComponentPose[TransformIndex].GetTranslation(); TetIntersections = TetBVH.FindAllIntersections(TestPosition); for (int32 j = 0; j < TetIntersections.Num(); j++) { const int32 TetIdx = TetIntersections[j]; if (!Tets[TetIdx].Outside(TestPosition, 1.0e-2)) // includes boundary { Chaos::TVector WeightsD = Tets[TetIdx].GetBarycentricCoordinates(TestPosition); Weights[CurrentIndex] = FVector4f(WeightsD[0], WeightsD[1], WeightsD[2], WeightsD[3]); TetIndex[CurrentIndex] = TetIdx + TetMeshStart; IndexKey[CurrentIndex] = TransformIndex; // validate { FIntVector4 Parent = (*Tetrahedron)[TetIndex[CurrentIndex]]; FVector3f EmbeddedPos = (*Vertex)[Parent[0]] * Weights[CurrentIndex][0] + (*Vertex)[Parent[1]] * Weights[CurrentIndex][1] + (*Vertex)[Parent[2]] * Weights[CurrentIndex][2] + (*Vertex)[Parent[3]] * Weights[CurrentIndex][3]; ensureMsgf((UEVertf(TestPosition) - EmbeddedPos).SquaredLength() < 1.0, TEXT("ERROR: Skeletal Bindings mis alignment.")); check((UEVertf(TestPosition) - EmbeddedPos).SquaredLength() < 1.0); } CurrentIndex++; break; } } } TetIndex.SetNum(CurrentIndex, EAllowShrinking::No); Weights.SetNum(CurrentIndex, EAllowShrinking::No); IndexKey.SetNum(CurrentIndex, EAllowShrinking::No); UE_LOG(LogSkeletalBindings, Display, TEXT("'%s' - Generated mesh bindings between tet mesh index %d and skeletal mesh - stats:\n" " Skeletal transforms num: %d\n" " Transforms in tetrahedra: %d"), *(SkeletalMeshName.ToString()), TetMeshIdx, Indices.Num(), CurrentIndex); // Store bindings in the collection if (TetIndex.Num()) { using namespace GeometryCollection::Facades; GeometryCollection::Facades::FTetrahedralSkeletalBindings TetBindings(OutCollection); FString MeshBindingsName = FTetrahedralSkeletalBindings::GenerateMeshGroupName(TetMeshIdx, SkeletalMeshName); TetBindings.SetBindings(MeshBindingsName, TetIndex, Weights, IndexKey); } } // end for TetMeshIdx } SetValue(Context, MoveTemp(OutCollection), &Collection); } }