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

179 lines
6.7 KiB
C++

// 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<int32> ChildIndices(TObjectPtr<const USkeletalMesh> SkeletalMesh, int32 StartIndex)
{
int32 IndexCount = 0;
TArray<int32> BranchIndices;
TArray<int32> ToProcess;
int32 CurrentIndex = StartIndex;
while (SkeletalMesh->GetRefSkeleton().IsValidIndex(CurrentIndex))
{
TArray<int32> 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<DataType>(&Collection))
{
DataType OutCollection = GetValue<DataType>(Context, &Collection); // Deep copy
TManagedArray<FIntVector4>* Tetrahedron = OutCollection.FindAttribute<FIntVector4>(FTetrahedralCollection::TetrahedronAttribute, FTetrahedralCollection::TetrahedralGroup);
TManagedArray<int32>* TetrahedronStart =OutCollection.FindAttribute<int32>(FTetrahedralCollection::TetrahedronStartAttribute, FGeometryCollection::GeometryGroup);
TManagedArray<int32>* TetrahedronCount =OutCollection.FindAttribute<int32>(FTetrahedralCollection::TetrahedronCountAttribute, FGeometryCollection::GeometryGroup);
TManagedArray<TArray<int32>>* IncidentElements = OutCollection.FindAttribute<TArray<int32>>(FTetrahedralCollection::IncidentElementsAttribute, FGeometryCollection::VerticesGroup);
TManagedArray<FVector3f>* Vertex = OutCollection.FindAttribute<FVector3f>("Vertex", "Vertices");
TObjectPtr<const USkeletalMesh> SkeletalMesh = GetValue<TObjectPtr<const USkeletalMesh>>(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<Chaos::TTetrahedron<Chaos::FReal>> Tets; // Index 0 == TetMeshStart
TArray<Chaos::TTetrahedron<Chaos::FReal>*> 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<Chaos::FReal>(
(*Vertex)[Tet[0]],
(*Vertex)[Tet[1]],
(*Vertex)[Tet[2]],
(*Vertex)[Tet[3]]);
BVHTetPtrs[i] = &Tets[i];
}
// Init BVH for tetrahedra.
Chaos::TBoundingVolumeHierarchy<
TArray<Chaos::TTetrahedron<Chaos::FReal>*>,
TArray<int32>,
Chaos::FReal,
3> TetBVH(BVHTetPtrs);
FName SkeletalMeshName = FName(SkeletalMesh->GetName());
TArray<FTransform> ComponentPose;
UE::Dataflow::Animation::GlobalTransforms(SkeletalMesh->GetRefSkeleton(), ComponentPose);
TArray<int32> Indices = UE::Dataflow::ChildIndices(SkeletalMesh, GetValue<int32>(Context, &BoneIndexIn));
//
// Do intersection tests against tets
//
int CurrentIndex = 0;
TArray<int32> TetIndex; TetIndex.SetNum(Indices.Num());
TArray<FVector4f> Weights; Weights.SetNum(Indices.Num());
TArray<FVector3f> Offsets; Offsets.SetNum(Indices.Num());
TArray<int32> IndexKey; IndexKey.SetNum(Indices.Num());
TArray<int32> 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<Chaos::FReal, 4> 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);
}
}