// Copyright Epic Games, Inc. All Rights Reserved. #include "Dataflow/GeometryCollectionTransferVertexAttributeNode.h" #include "Chaos/Triangle.h" #include "Chaos/TriangleMesh.h" #include "Chaos/HierarchicalSpatialHash.h" #include "Chaos/TriangleCollisionPoint.h" #include "Dataflow/DataflowInputOutput.h" #include "GeometryCollection/GeometryCollectionAlgo.h" #include "GeometryCollection/ManagedArrayAccessor.h" #include "GeometryCollection/Facades/CollectionVertexBoneWeightsFacade.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(GeometryCollectionTransferVertexAttributeNode) #define LOCTEXT_NAMESPACE "FGeometryCollectionTransferVertexAttributeNode" namespace UE::Private { typedef Chaos::FSphere SphereType; typedef Chaos::TBoundingVolumeHierarchy, TArray, Chaos::FReal, 3> BVH; class FTransferFacade { const FManagedArrayCollection& ConstCollection; FManagedArrayCollection* Collection = nullptr; public: FTransferFacade(FManagedArrayCollection& InCollection) : ConstCollection(InCollection) , Collection(&InCollection) , BoneMap(InCollection, FName("BoneMap"), FName("Vertices")) , Vertex(InCollection, FName("Vertex"), FName("Vertices")) , Indices(InCollection, FName("Indices"), FName("Faces")) , Transform(InCollection, FTransformCollection::TransformAttribute, FTransformCollection::TransformGroup) , Parent(InCollection, FTransformCollection::ParentAttribute, FTransformCollection::TransformGroup) , VertexStart(InCollection, "VertexStart", FGeometryCollection::GeometryGroup) , VertexCount(InCollection, "VertexCount", FGeometryCollection::GeometryGroup) , FaceStart(InCollection, "FaceStart", FGeometryCollection::GeometryGroup) , FaceCount(InCollection, "FaceCount", FGeometryCollection::GeometryGroup) {} FTransferFacade(const FManagedArrayCollection& InCollection) : ConstCollection(InCollection) , Collection(nullptr) , BoneMap(InCollection, FName("BoneMap"), FName("Vertices")) , Vertex(InCollection, FName("Vertex"), FName("Vertices")) , Indices(InCollection, FName("Indices"), FName("Faces")) , Transform(InCollection, FTransformCollection::TransformAttribute, FTransformCollection::TransformGroup) , Parent(InCollection, FTransformCollection::ParentAttribute, FTransformCollection::TransformGroup) , VertexStart(InCollection, "VertexStart", FGeometryCollection::GeometryGroup) , VertexCount(InCollection, "VertexCount", FGeometryCollection::GeometryGroup) , FaceStart(InCollection, "FaceStart", FGeometryCollection::GeometryGroup) , FaceCount(InCollection, "FaceCount", FGeometryCollection::GeometryGroup) {} bool IsValid() const { return BoneMap.IsValid() && Vertex.IsValid() && Indices.IsValid() && Transform.IsValid() && Parent.IsValid() && VertexStart.IsValid() && VertexCount.IsValid() && FaceStart.IsValid() && FaceCount.IsValid(); } template const TManagedArray* GetAttributeArray(FString AttributeName, FString Group) const { return ConstCollection.FindAttributeTyped(FName(AttributeName), FName(Group)); } template TManagedArray* GetAttributeArray(FString AttributeName, FString Group) { TManagedArray* TargetAttributeArray = nullptr; if (!Collection->HasAttribute(FName(AttributeName), FName(Group))) { Collection->AddAttribute(FName(AttributeName), FName(Group)); } return Collection->FindAttributeTyped(FName(AttributeName), FName(Group)); } TManagedArrayAccessor BoneMap; TManagedArrayAccessor Vertex; TManagedArrayAccessor Indices; TManagedArrayAccessor Transform; TManagedArrayAccessor Parent; TManagedArrayAccessor VertexStart; TManagedArrayAccessor VertexCount; TManagedArrayAccessor FaceStart; TManagedArrayAccessor FaceCount; }; struct FTransferData { TArray SourceIndices; TArray SourceWeights; float FalloffScale = 0.f; FTransferData() {} FTransferData(TArray SourceIndicesIn, TArray SourceWeightsIn, float FalloffScaleIn) : SourceIndices(SourceIndicesIn), SourceWeights(SourceWeightsIn), FalloffScale(FalloffScaleIn) {} }; struct FTransferProperties { EDataflowTransferVertexAttributeNodeBoundingVolume BoundingVolumeType = EDataflowTransferVertexAttributeNodeBoundingVolume::Dataflow_Max; EDataflowTransferVertexAttributeNodeSourceScale SourceScale = EDataflowTransferVertexAttributeNodeSourceScale::Dataflow_Max; EDataflowTransferVertexAttributeNodeFalloff Falloff = EDataflowTransferVertexAttributeNodeFalloff::Dataflow_Max; float FalloffThreshold = 0.f; float EdgeMultiplier = 0.f; float BoundMultiplier = 0.f; FString TransformNameSuffix; }; static float MaxEdgeLength(TArray& Vert, const TManagedArray& Tri, int32 VertexOffset, int32 TriStart, int32 TriCount) { auto TriInRange = [](const FIntVector3& T, int32 Max) { for (int32 k = 0; k < 3; k++) { if (ensure(0 <= T[k] && T[k] < Max)) { return true; } } return false; }; float Max = 0; int32 TriStop = TriStart + TriCount; for (int32 i = TriStart; i < TriStop; i++) { if (TriInRange(Tri[i] - FIntVector3(VertexOffset), Vert.Num())) { Max = FMath::Max(Max, (Vert[Tri[i][0] - VertexOffset] - Vert[Tri[i][1] - VertexOffset]).SquaredLength()); Max = FMath::Max(Max, (Vert[Tri[i][0] - VertexOffset] - Vert[Tri[i][2] - VertexOffset]).SquaredLength()); Max = FMath::Max(Max, (Vert[Tri[i][1] - VertexOffset] - Vert[Tri[i][2] - VertexOffset]).SquaredLength()); } } return FMath::Sqrt(Max); } static void BuildComponentSpaceVertices(const TManagedArray& LocalSpaceTransform, const TManagedArray& Parent, const TManagedArray& BoneMapArray, const TManagedArray& VertexArray, int32 Start, int32 Count, TArray& ComponentSpaceVertices) { TArray ComponentTransform; GeometryCollectionAlgo::GlobalMatrices(LocalSpaceTransform, Parent, ComponentTransform); ComponentSpaceVertices.SetNumUninitialized(Count); for (int32 i = 0; i < Count; ++i) { int32 j = i + Start; if (0 < BoneMapArray[i] && BoneMapArray[i] < ComponentTransform.Num()) { ComponentSpaceVertices[i] = ComponentTransform[BoneMapArray[j]].TransformPosition(VertexArray[j]); } else { ComponentSpaceVertices[i] = VertexArray[j]; } } } static BVH* BuildParticleSphereBVH(const TArray& Vertices, float Radius) { TArray VertexSpherePtrs; TArray VertexSpheres; VertexSpheres.Init(SphereType(Chaos::FVec3f(0), Radius), Vertices.Num()); VertexSpherePtrs.SetNum(Vertices.Num()); for (int32 i = 0; i < Vertices.Num(); i++) { Chaos::FVec3f SphereCenter(Vertices[i]); SphereType VertexSphere(SphereCenter, Radius); VertexSpheres[i] = SphereType(SphereCenter, Radius); VertexSpherePtrs[i] = &VertexSpheres[i]; } return new BVH(VertexSpherePtrs); } static void TriangleToVertexIntersections( const BVH& VertexBVH, const TArray& ComponentSpaceVertices, const FIntVector3& Triangle, TArray& OutTargetVertexIntersection) { OutTargetVertexIntersection.Empty(); TArray TargetVertexIntersection0 = VertexBVH.FindAllIntersections(ComponentSpaceVertices[Triangle[0]]); TArray TargetVertexIntersection1 = VertexBVH.FindAllIntersections(ComponentSpaceVertices[Triangle[1]]); TArray TargetVertexIntersection2 = VertexBVH.FindAllIntersections(ComponentSpaceVertices[Triangle[2]]); TargetVertexIntersection0.Sort(); TargetVertexIntersection1.Sort(); TargetVertexIntersection2.Sort(); for (int32 k = 0; k < TargetVertexIntersection0.Num(); k++) { if (TargetVertexIntersection1.Contains(TargetVertexIntersection0[k]) && TargetVertexIntersection2.Contains(TargetVertexIntersection0[k])) { OutTargetVertexIntersection.Emplace(TargetVertexIntersection0[k]); } } } static float CalculateFalloffScale(EDataflowTransferVertexAttributeNodeFalloff FalloffSetting, float Threshold, float Distance) { float Denominator = 1.0; if (Distance > Threshold && !FMath::IsNearlyZero(Threshold)) { Denominator = Distance / Threshold; } switch (FalloffSetting) { case EDataflowTransferVertexAttributeNodeFalloff::Linear: return 1. / Denominator; case EDataflowTransferVertexAttributeNodeFalloff::Squared: return 1. / FMath::Square(Denominator); } return 1.0; } static TArray FindSourceToTargetGeometryMap(const FManagedArrayCollection& SourceCollection, const FManagedArrayCollection& TargetCollection, const FString& TransformNameSuffix) { TArray Mapping; const TManagedArray* const SourceName = SourceCollection.FindAttribute(FName("BoneName"), FTransformCollection::TransformGroup); const TManagedArray* const SourceGeometryGroup = SourceCollection.FindAttribute(FName("TransformToGeometryIndex"), FTransformCollection::TransformGroup); const TManagedArray* const TargetName = TargetCollection.FindAttribute(FName("BoneName"), FTransformCollection::TransformGroup); const TManagedArray* const TargetGeometryGroup = TargetCollection.FindAttribute(FName("TransformToGeometryIndex"), FTransformCollection::TransformGroup); if (SourceName && SourceGeometryGroup && TargetName && TargetGeometryGroup) { for (int32 i = 0; i < SourceName->Num(); ++i) { const FString TestName = FString::Printf(TEXT("%s%s"), *(*SourceName)[i], *TransformNameSuffix); for (int32 j = 0; j < TargetName->Num(); ++j) { if ((*TargetName)[j].StartsWith(TestName)) { Mapping.Add(FIntVector2((*SourceGeometryGroup)[i], (*TargetGeometryGroup)[j])); break; } } } } return MoveTemp(Mapping); } static TMap FindSourceToTargetTransformMap(const FManagedArrayCollection& SourceCollection, const FManagedArrayCollection& TargetCollection) { TMap SourceIndexToTargetIndex; TMap TargetNameToIndex; const TManagedArray* const SourceName = SourceCollection.FindAttribute(FName("BoneName"), FTransformCollection::TransformGroup); const TManagedArray* const TargetName = TargetCollection.FindAttribute(FName("BoneName"), FTransformCollection::TransformGroup); if (SourceName && TargetName) { for (int32 j = 0; j < TargetName->Num(); j++) { TargetNameToIndex.Add((*TargetName)[j], j); } for (int32 i = 0; i < SourceName->Num(); i++) { if (int32* TargetIndex = TargetNameToIndex.Find((*SourceName)[i])) { SourceIndexToTargetIndex.Add(i, *TargetIndex); } } } return MoveTemp(SourceIndexToTargetIndex); } static TArray PairedGeometryTransfer(const TArray& PairedGeometry, const FTransferFacade& Source, const FTransferFacade& Target, const FTransferProperties& TransferProperties) { TArray TransferDataArray; TransferDataArray.SetNum(Target.Vertex.Num()); Chaos::FReal SphereFullRadius = 0.f; if (TransferProperties.SourceScale == EDataflowTransferVertexAttributeNodeSourceScale::Asset_Edge || TransferProperties.SourceScale == EDataflowTransferVertexAttributeNodeSourceScale::Asset_Bound) { // Build component space vertices for TargetCollection TArray ComponentSpaceFullTargetVertices; // size of num vertices of the geometry entry. BuildComponentSpaceVertices(Target.Transform.Get(), Target.Parent.Get(), Target.BoneMap.Get(), Target.Vertex.Get(), 0, Target.Vertex.Num(), ComponentSpaceFullTargetVertices); // Build component space vertices for TargetCollection TArray ComponentSpaceFullVertices; // size of num vertices of the geometry entry BuildComponentSpaceVertices(Source.Transform.Get(), Source.Parent.Get(), Source.BoneMap.Get(), Source.Vertex.Get(), 0, Source.Vertex.Num(), ComponentSpaceFullVertices); if (TransferProperties.SourceScale == EDataflowTransferVertexAttributeNodeSourceScale::Asset_Edge) { SphereFullRadius = Chaos::FReal(TransferProperties.EdgeMultiplier * FMath::Max( MaxEdgeLength(ComponentSpaceFullTargetVertices, Target.Indices.Get(), 0, 0, Target.Indices.Num()), MaxEdgeLength(ComponentSpaceFullVertices, Source.Indices.Get(), 0, 0, Source.Indices.Num()))); } else if (TransferProperties.SourceScale == EDataflowTransferVertexAttributeNodeSourceScale::Asset_Bound) { Chaos::FVec3f CoordMaxs(-FLT_MAX); Chaos::FVec3f CoordMins(FLT_MAX); for (int32 i = 0; i < ComponentSpaceFullVertices.Num(); i++) { CoordMaxs = CoordMaxs.ComponentwiseMax(ComponentSpaceFullVertices[i]); CoordMins = CoordMins.ComponentwiseMin(ComponentSpaceFullVertices[i]); } const Chaos::FVec3f CoordDiff = (CoordMaxs - CoordMins) * TransferProperties.BoundMultiplier; SphereFullRadius = FMath::Min3(CoordDiff[0], CoordDiff[1], CoordDiff[2]); } } ParallelFor(PairedGeometry.Num(), [&](int32 Pdx) { const int32 AttributeGeometryIndex = PairedGeometry[Pdx][0]; const int32 TargetGeometryIndex = PairedGeometry[Pdx][1]; if (ensure(0 <= AttributeGeometryIndex && AttributeGeometryIndex < Source.VertexStart.Num())) { if (ensure(0 <= TargetGeometryIndex && TargetGeometryIndex < Target.VertexStart.Num())) { // Build component space vertices for TargetCollection TArray ComponentSpaceTargetVertices; // size of num vertices of the geometry entry. BuildComponentSpaceVertices(Target.Transform.Get(), Target.Parent.Get(), Target.BoneMap.Get(), Target.Vertex.Get(), Target.VertexStart[TargetGeometryIndex], Target.VertexCount[TargetGeometryIndex], ComponentSpaceTargetVertices); // Build component space vertices for SourceCollection TArray ComponentSpaceVertices; // size of num vertices of the geometry entry BuildComponentSpaceVertices(Source.Transform.Get(), Source.Parent.Get(), Source.BoneMap.Get(), Source.Vertex.Get(), Source.VertexStart[AttributeGeometryIndex], Source.VertexCount[AttributeGeometryIndex], ComponentSpaceVertices); // build Sphere based BVH Chaos::FReal SphereRadius = SphereFullRadius; if (TransferProperties.SourceScale == EDataflowTransferVertexAttributeNodeSourceScale::Component_Edge) { SphereRadius = Chaos::FReal(TransferProperties.EdgeMultiplier * FMath::Max( MaxEdgeLength(ComponentSpaceTargetVertices, Target.Indices.Get(), Target.VertexStart[TargetGeometryIndex], Target.FaceStart[TargetGeometryIndex], Target.FaceCount[TargetGeometryIndex]), MaxEdgeLength(ComponentSpaceVertices, Source.Indices.Get(), Source.VertexStart[AttributeGeometryIndex], Source.FaceStart[AttributeGeometryIndex], Source.FaceCount[AttributeGeometryIndex]))); } const int32 TargetVertexStartVal = Target.VertexStart[TargetGeometryIndex]; const int32 TargetVertexCountVal = Target.VertexCount[TargetGeometryIndex]; const int32 VertexStartVal = Source.VertexStart[AttributeGeometryIndex]; const int32 FaceStartVal = Source.FaceStart[AttributeGeometryIndex]; const int32 FaceCountVal = Source.FaceCount[AttributeGeometryIndex]; if (TransferProperties.BoundingVolumeType == EDataflowTransferVertexAttributeNodeBoundingVolume::Triangle) { TArray> ComponentSpaceVerticesTVec3; ComponentSpaceVerticesTVec3.SetNum(ComponentSpaceVertices.Num()); for (int32 SourceIndex = 0; SourceIndex < ComponentSpaceVerticesTVec3.Num(); SourceIndex++) { ComponentSpaceVerticesTVec3[SourceIndex] = Chaos::TVec3(ComponentSpaceVertices[SourceIndex]); } TConstArrayView> ConstComponentSpaceVertices(ComponentSpaceVerticesTVec3); Chaos::FTriangleMesh TriangleMesh; TArray> SourceElements; SourceElements.SetNum(FaceCountVal); for (int32 ElementIndex = 0; ElementIndex < FaceCountVal; ++ElementIndex) { FIntVector3 Element = Source.Indices[FaceStartVal + ElementIndex]; SourceElements[ElementIndex] = Chaos::TVec3(Element[0] - VertexStartVal, Element[1] - VertexStartVal, Element[2] - VertexStartVal); } TriangleMesh.Init(SourceElements); Chaos::FTriangleMesh::TSpatialHashType SpatialHash; TriangleMesh.BuildSpatialHash(ConstComponentSpaceVertices, SpatialHash, SphereRadius); for (int32 TargetIndex = 0; TargetIndex < TargetVertexCountVal; ++TargetIndex) { TArray> Result; if (TriangleMesh.PointClosestTriangleQuery(SpatialHash, ConstComponentSpaceVertices, TargetIndex, Chaos::TVec3(ComponentSpaceTargetVertices[TargetIndex]), SphereRadius / 2.f, SphereRadius / 2.f, [](const int32 PointIndex, const int32 TriangleIndex)->bool {return true; }, Result)) { for (const Chaos::TTriangleCollisionPoint& CollisionPoint : Result) { const float CurrentDistance = abs(CollisionPoint.Phi); const float TriRadius = TransferProperties.FalloffThreshold * MaxEdgeLength(ComponentSpaceVertices, Source.Indices.Get(), VertexStartVal, FaceStartVal + CollisionPoint.Indices[1], 1); const float FalloffScale = CalculateFalloffScale(TransferProperties.Falloff, TriRadius, CurrentDistance); if (!FMath::IsNearlyZero(FalloffScale)) { const int32 TargetCandidateIndex = CollisionPoint.Indices[0] + TargetVertexStartVal; TArray TransferIndices; TArray TransferWeights; for (int32 k = 0; k < 3; k++) { TransferIndices.Add(Source.Indices[FaceStartVal + CollisionPoint.Indices[1]][k]); TransferWeights.Add(CollisionPoint.Bary[k + 1]); } TransferDataArray[TargetCandidateIndex] = FTransferData(TransferIndices, TransferWeights, FalloffScale); break; } } } } } else if (TransferProperties.BoundingVolumeType == EDataflowTransferVertexAttributeNodeBoundingVolume::Vertex) { const float FalloffScale = 1.f; TUniquePtr VertexBVH(BuildParticleSphereBVH(ComponentSpaceVertices, SphereRadius)); for (int32 TargetIdx = 0; TargetIdx < ComponentSpaceTargetVertices.Num(); ++TargetIdx) { float MinDistance = FLT_MAX; int32 MinSourceIdx = INDEX_NONE; for (int32 SourceIdx : VertexBVH->FindAllIntersections(ComponentSpaceTargetVertices[TargetIdx])) { float Distance = (ComponentSpaceTargetVertices[TargetIdx] - ComponentSpaceVertices[SourceIdx]).Size(); if (Distance < MinDistance) { MinDistance = Distance; MinSourceIdx = SourceIdx; } } if (MinSourceIdx != INDEX_NONE) { TArray TransferIndices = { MinSourceIdx + VertexStartVal }; TArray TransferWeights = { 1.f }; TransferDataArray[TargetIdx + TargetVertexStartVal] = FTransferData(TransferIndices, TransferWeights, FalloffScale); } } } } } }); return MoveTemp(TransferDataArray); } static TArray GlobalAssetTransfer(const FTransferFacade& Source, const FTransferFacade& Target, const FTransferProperties& TransferProperties) { TArray TransferDataArray; TransferDataArray.SetNum(Target.Vertex.Num()); // Build component space vertices for TargetCollection TArray ComponentSpaceTargetVertices; BuildComponentSpaceVertices(Target.Transform.Get(), Target.Parent.Get(), Target.BoneMap.Get(), Target.Vertex.Get(), 0, Target.Vertex.Num(), ComponentSpaceTargetVertices); // Build component space vertices for SourceCollection TArray ComponentSpaceVertices; BuildComponentSpaceVertices(Source.Transform.Get(), Source.Parent.Get(), Source.BoneMap.Get(), Source.Vertex.Get(), 0, Source.Vertex.Num(), ComponentSpaceVertices); // build Sphere based BVH Chaos::FReal SphereRadius = 0.f; if (TransferProperties.SourceScale == EDataflowTransferVertexAttributeNodeSourceScale::Asset_Edge || TransferProperties.SourceScale == EDataflowTransferVertexAttributeNodeSourceScale::Component_Edge) { SphereRadius = Chaos::FReal(TransferProperties.EdgeMultiplier * FMath::Max( MaxEdgeLength(ComponentSpaceTargetVertices, Target.Indices.Get(), 0, 0, Target.Indices.Num()), MaxEdgeLength(ComponentSpaceVertices, Source.Indices.Get(), 0, 0, Source.Indices.Num()))); } else if (TransferProperties.SourceScale == EDataflowTransferVertexAttributeNodeSourceScale::Asset_Bound) { Chaos::FVec3f CoordMaxs(-FLT_MAX); Chaos::FVec3f CoordMins(FLT_MAX); for (int32 i = 0; i < ComponentSpaceVertices.Num(); i++) { CoordMaxs = CoordMaxs.ComponentwiseMax(ComponentSpaceVertices[i]); CoordMins = CoordMins.ComponentwiseMin(ComponentSpaceVertices[i]); } const Chaos::FVec3f CoordDiff = (CoordMaxs - CoordMins) * TransferProperties.BoundMultiplier; SphereRadius = FMath::Min3(CoordDiff[0], CoordDiff[1], CoordDiff[2]); } if (TransferProperties.BoundingVolumeType == EDataflowTransferVertexAttributeNodeBoundingVolume::Triangle) { TArray> ComponentSpaceVerticesTVec3; ComponentSpaceVerticesTVec3.SetNum(ComponentSpaceVertices.Num()); for (int32 SourceIndex = 0; SourceIndex < ComponentSpaceVerticesTVec3.Num(); SourceIndex++) { ComponentSpaceVerticesTVec3[SourceIndex] = Chaos::TVec3(ComponentSpaceVertices[SourceIndex]); } TConstArrayView> ConstComponentSpaceVertices(ComponentSpaceVerticesTVec3); Chaos::FTriangleMesh TriangleMesh; TArray> SourceElements; SourceElements.SetNum(Source.Indices.Num()); for (int32 ElementIndex = 0; ElementIndex < Source.Indices.Num(); ++ElementIndex) { SourceElements[ElementIndex] = Chaos::TVec3(Source.Indices[ElementIndex][0], Source.Indices[ElementIndex][1], Source.Indices[ElementIndex][2]); } TriangleMesh.Init(SourceElements); Chaos::FTriangleMesh::TSpatialHashType SpatialHash; TriangleMesh.BuildSpatialHash(ConstComponentSpaceVertices, SpatialHash, SphereRadius); for (int32 TargetIndex = 0; TargetIndex < ComponentSpaceTargetVertices.Num(); ++TargetIndex) { TArray> Result; if (TriangleMesh.PointClosestTriangleQuery(SpatialHash, ConstComponentSpaceVertices, TargetIndex, Chaos::TVec3(ComponentSpaceTargetVertices[TargetIndex]), SphereRadius / 2.f, SphereRadius / 2.f, [](const int32 PointIndex, const int32 TriangleIndex)->bool {return true; }, Result)) { for (const Chaos::TTriangleCollisionPoint& CollisionPoint : Result) { const float CurrentDistance = abs(CollisionPoint.Phi); const float TriRadius = TransferProperties.FalloffThreshold * MaxEdgeLength(ComponentSpaceVertices, Source.Indices.Get(), 0, CollisionPoint.Indices[1], 1); const float FalloffScale = CalculateFalloffScale(TransferProperties.Falloff, TriRadius, CurrentDistance); if (!FMath::IsNearlyZero(FalloffScale)) { const int32 TargetCandidateIndex = CollisionPoint.Indices[0]; TArray TransferIndices; TArray TransferWeights; for (int32 k = 0; k < 3; k++) { TransferIndices.Add(Source.Indices[CollisionPoint.Indices[1]][k]); TransferWeights.Add(CollisionPoint.Bary[k + 1]); } TransferDataArray[TargetCandidateIndex] = FTransferData(TransferIndices, TransferWeights, FalloffScale); break; } } } } } else if (TransferProperties.BoundingVolumeType == EDataflowTransferVertexAttributeNodeBoundingVolume::Vertex) { const float FalloffScale = 1.f; TUniquePtr VertexBVH(BuildParticleSphereBVH(ComponentSpaceVertices, SphereRadius)); for (int32 TargetIdx = 0; TargetIdx < ComponentSpaceTargetVertices.Num(); ++TargetIdx) { float MinDistance = FLT_MAX; int32 MinSourceIdx = INDEX_NONE; for (int32 SourceIdx : VertexBVH->FindAllIntersections(ComponentSpaceTargetVertices[TargetIdx])) { float Distance = (ComponentSpaceTargetVertices[TargetIdx] - ComponentSpaceVertices[SourceIdx]).Size(); if (Distance < MinDistance) { MinDistance = Distance; MinSourceIdx = SourceIdx; } } if (MinSourceIdx != INDEX_NONE) { TArray TransferIndices = { MinSourceIdx }; TArray TransferWeights = { 1.f }; TransferDataArray[TargetIdx] = FTransferData(TransferIndices, TransferWeights, FalloffScale); } } } return MoveTemp(TransferDataArray); } //store barycentric weight info TransferDataArray and returns transfer method used static TArray ComputeTransferData(const FManagedArrayCollection& SourceCollection, const FManagedArrayCollection& TargetCollection, const FTransferProperties& TransferProperties, const EDataflowTransferVertexAttributeNodeTransferMethod& TransferMethod) { TArray TransferDataArray; const FTransferFacade Target(TargetCollection); const FTransferFacade Source(SourceCollection); if (Target.IsValid() && Source.IsValid()) { if (TransferMethod == EDataflowTransferVertexAttributeNodeTransferMethod::Component) { TArray AlignedGeometry = FindSourceToTargetGeometryMap(SourceCollection, TargetCollection, TransferProperties.TransformNameSuffix); if (AlignedGeometry.Num() != SourceCollection.NumElements(FGeometryCollection::GeometryGroup)) { TSet MatchedSourceGeometries; for (const FIntVector2& Pair : AlignedGeometry) { MatchedSourceGeometries.Add(Pair.X); } for (int32 SourceIdx = 0; SourceIdx < SourceCollection.NumElements(FGeometryCollection::GeometryGroup); ++SourceIdx) { if (!MatchedSourceGeometries.Contains(SourceIdx)) { UE_LOG(LogChaosDataflow, Warning, TEXT("Source geometry indexed %d is not matched to any target geometry. Check if this is intentional."), SourceIdx); } } } TransferDataArray = PairedGeometryTransfer(AlignedGeometry, Source, Target, TransferProperties); } else if (TransferMethod == EDataflowTransferVertexAttributeNodeTransferMethod::Global) { TransferDataArray = GlobalAssetTransfer(Source, Target, TransferProperties); } } return TransferDataArray; } } using namespace UE::Private; void FGeometryCollectionTransferVertexAttributeNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { FCollectionAttributeKey Key = GetValue(Context, &AttributeKey, AttributeKey); if (Out->IsA(&Collection)) { FManagedArrayCollection TargetCollection = GetValue(Context, &Collection); const FManagedArrayCollection& SourceCollection = GetValue(Context, &FromCollection); FTransferFacade Target(TargetCollection); const FTransferFacade Source(SourceCollection); const FTransferProperties TransferProperties = { BoundingVolumeType, SourceScale, Falloff, FalloffThreshold, EdgeMultiplier, BoundMultiplier, TransformNameSuffix }; if (Target.IsValid() && Source.IsValid()) { TArray TransferDataArray = ComputeTransferData(SourceCollection, TargetCollection, TransferProperties, TransferMethod); if (TransferMethod == EDataflowTransferVertexAttributeNodeTransferMethod::Global) { Context.Warning(TEXT("Transfer method: global transfer"), this, Out); } else if (TransferMethod == EDataflowTransferVertexAttributeNodeTransferMethod::None) { Context.Warning(TEXT("Transfer failed"), this, Out); } if (const TManagedArray* SourceAttributeFloatArray = Source.GetAttributeArray(Key.Attribute, Key.Group)) { TManagedArray* TargetAttributeArray = Target.GetAttributeArray(Key.Attribute, Key.Group); TargetAttributeArray->Fill(0.f); for (int32 VertexIndex = 0; VertexIndex < TargetAttributeArray->Num(); VertexIndex++) { const FTransferData& TransferData = TransferDataArray[VertexIndex]; if (TransferData.SourceIndices.Num()) { for (int32 SourceLocalIdx = 0; SourceLocalIdx < TransferData.SourceIndices.Num(); ++SourceLocalIdx) { (*TargetAttributeArray)[VertexIndex] += (*SourceAttributeFloatArray)[TransferData.SourceIndices[SourceLocalIdx]] * TransferData.SourceWeights[SourceLocalIdx] * TransferData.FalloffScale; } } } } else if (const TManagedArray* SourceAttributeColorArray = Source.GetAttributeArray(Key.Attribute, Key.Group)) { TManagedArray* TargetAttributeArray = Target.GetAttributeArray(Key.Attribute, Key.Group); TargetAttributeArray->Fill(FLinearColor(0, 0, 0, 0)); for (int32 VertexIndex = 0; VertexIndex < TargetAttributeArray->Num(); VertexIndex++) { const FTransferData& TransferData = TransferDataArray[VertexIndex]; if (TransferData.SourceIndices.Num()) { for (int32 SourceLocalIdx = 0; SourceLocalIdx < TransferData.SourceIndices.Num(); ++SourceLocalIdx) { (*TargetAttributeArray)[VertexIndex] += (*SourceAttributeColorArray)[TransferData.SourceIndices[SourceLocalIdx]] * TransferData.SourceWeights[SourceLocalIdx] * TransferData.FalloffScale; } } } } else { UE_LOG(LogChaosDataflow, Warning, TEXT("TransferVertexAttribute: attribute[%s] of group[%s] has type that is not float or FLinearColor, transfer failed."), *Key.Attribute, *Key.Group); } } SetValue(Context, MoveTemp(TargetCollection), &Collection); } else if (Out->IsA(&AttributeKey)) { SetValue(Context, MoveTemp(Key), &AttributeKey); } } void FGeometryCollectionTransferVertexSkinWeightsNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&Collection)) { FManagedArrayCollection TargetCollection = GetValue(Context, &Collection); const FManagedArrayCollection& SourceCollection = GetValue(Context, &FromCollection); FTransferFacade Target(TargetCollection); const FTransferFacade Source(SourceCollection); const FTransferProperties TransferProperties = { BoundingVolumeType, SourceScale, Falloff, FalloffThreshold, EdgeMultiplier, BoundMultiplier, TransformNameSuffix }; if (Target.IsValid() && Source.IsValid()) { TArray TransferDataArray = ComputeTransferData(SourceCollection, TargetCollection, TransferProperties, TransferMethod); GeometryCollection::Facades::FVertexBoneWeightsFacade SourceVertexBoneWeightsFacade(SourceCollection); GeometryCollection::Facades::FVertexBoneWeightsFacade TargetVertexBoneWeightsFacade(TargetCollection); const TManagedArray>* SourceBoneIndices = SourceVertexBoneWeightsFacade.FindBoneIndices(); const TManagedArray>* SourceBoneWeights = SourceVertexBoneWeightsFacade.FindBoneWeights(); if (SourceBoneIndices && SourceBoneWeights) { // // Compute the bone index mappings. This allows the transfer operator to retarget weights to the correct skeleton. // const TMap SourceBoneToTargetBone = FindSourceToTargetTransformMap(SourceCollection, TargetCollection); TSet MissingSourceBones; for (int32 VertexIndex = 0; VertexIndex < Target.Vertex.Num(); VertexIndex++) { const FTransferData& TransferData = TransferDataArray[VertexIndex]; if (TransferData.SourceIndices.Num()) { TMap BoneWeightBucket; for (int32 SourceLocalIdx = 0; SourceLocalIdx < TransferData.SourceIndices.Num(); ++SourceLocalIdx) { for (int32 BoneLocalIdx = 0; BoneLocalIdx < (*SourceBoneIndices)[TransferData.SourceIndices[SourceLocalIdx]].Num(); ++BoneLocalIdx) { const int32 SourceBoneIndex = (*SourceBoneIndices)[TransferData.SourceIndices[SourceLocalIdx]][BoneLocalIdx]; if (SourceBoneToTargetBone.Contains(SourceBoneIndex)) { const int32 TargetBoneIndex = SourceBoneToTargetBone[SourceBoneIndex]; const float BoneWeight = (*SourceBoneWeights)[TransferData.SourceIndices[SourceLocalIdx]][BoneLocalIdx]; if (BoneWeightBucket.Contains(TargetBoneIndex)) { BoneWeightBucket[TargetBoneIndex] += TransferData.SourceWeights[SourceLocalIdx] * BoneWeight; } else { BoneWeightBucket.Add(TargetBoneIndex, TransferData.SourceWeights[SourceLocalIdx] * BoneWeight); } } else if (!MissingSourceBones.Contains(SourceBoneIndex)) { MissingSourceBones.Add(SourceBoneIndex); UE_LOG(LogChaosDataflow, Warning, TEXT("Target geometry does not contain Source Transform indexed [%d]."), SourceBoneIndex); } } } TArray VertexBoneIndex; TArray VertexBoneWeight; VertexBoneIndex.Reserve(BoneWeightBucket.Num()); VertexBoneWeight.Reserve(BoneWeightBucket.Num()); for (const TPair& Pair : BoneWeightBucket) { VertexBoneIndex.Add(Pair.Key); VertexBoneWeight.Add(Pair.Value); } TargetVertexBoneWeightsFacade.ModifyBoneWeight(VertexIndex, VertexBoneIndex, VertexBoneWeight); } } } } SetValue(Context, MoveTemp(TargetCollection), &Collection); } } void FGeometryCollectionSetKinematicVertexSelectionNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&Collection)) { FManagedArrayCollection InCollection = GetValue(Context, &Collection); if (IsConnected(&VertexSelection)) { FDataflowVertexSelection VertexSelectionIn = GetValue(Context, &VertexSelection); GeometryCollection::Facades::FVertexBoneWeightsFacade VertexBoneWeightsFacade(InCollection); const bool bSetKinematic = (KinematicValue == ESetKinematicVertexSelectionKinematicValue::SetKinematic); VertexBoneWeightsFacade.SetVertexArrayKinematic(VertexSelectionIn.AsArray(), bSetKinematic); } SetValue(Context, MoveTemp(InCollection), &Collection); } } #undef LOCTEXT_NAMESPACE