// Copyright Epic Games, Inc. All Rights Reserved. #include "ChaosClothAsset/BlendVerticesNode.h" #include "ChaosClothAsset/ClothDataflowTools.h" #include "ChaosClothAsset/CollectionClothFacade.h" #include "Dataflow/DataflowInputOutput.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(BlendVerticesNode) #define LOCTEXT_NAMESPACE "ChaosClothAssetBlendVerticesNode" namespace UE::Chaos::ClothAsset::Private { template void BlendValues(float BlendingWeight, const TArrayView& Values, const TConstArrayView& BlendingValues) { const int32 NumValues = FMath::Min(Values.Num(), BlendingValues.Num()); const float OneMinusScalar = 1.f - BlendingWeight; for (int32 Index = 0; Index < NumValues; ++Index) { Values[Index] = OneMinusScalar * Values[Index] + BlendingWeight * BlendingValues[Index]; } } template void BlendNormalizedValues(float BlendingWeight, const TArrayView& Values, const TConstArrayView& BlendingValues) { const int32 NumValues = FMath::Min(Values.Num(), BlendingValues.Num()); const float OneMinusScalar = 1.f - BlendingWeight; for (int32 Index = 0; Index < NumValues; ++Index) { Values[Index] = (OneMinusScalar * Values[Index] + BlendingWeight * BlendingValues[Index]).GetSafeNormal(); } } static void BlendUVSets(float BlendingWeight, const TArrayView>& Values, const TConstArrayView>& BlendingValues) { const int32 NumValues = FMath::Min(Values.Num(), BlendingValues.Num()); for (int32 Index = 0; Index < NumValues; ++Index) { BlendValues(BlendingWeight, TArrayView(Values[Index]), TConstArrayView(BlendingValues[Index])); } } } FChaosClothAssetBlendVerticesNode::FChaosClothAssetBlendVerticesNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid) : FDataflowNode(InParam, InGuid) { RegisterInputConnection(&Collection); RegisterInputConnection(&BlendCollection); RegisterOutputConnection(&Collection, &Collection); } void FChaosClothAssetBlendVerticesNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&Collection)) { using namespace UE::Chaos::ClothAsset; if (BlendingWeight == 0.f) { SafeForwardInput(Context, &Collection, &Collection); return; } // Evaluate in collection FManagedArrayCollection InCollection = GetValue(Context, &Collection); const TSharedRef ClothCollection = MakeShared(MoveTemp(InCollection)); FManagedArrayCollection InBlendCollection = GetValue(Context, &BlendCollection); // Can't use a const reference here sadly since the facade needs a SharedRef to be created const TSharedRef BlendClothCollection = MakeShared(MoveTemp(InBlendCollection)); // Always check for a valid cloth collection/facade to avoid processing non cloth collections FCollectionClothFacade ClothFacade(ClothCollection); const FCollectionClothConstFacade BlendClothFacade(BlendClothCollection); auto LogVertexCountMismatch = [this](const FText& VertexType, int32 CollectionCount, int32 BlendCollectionCount) { UE::Chaos::ClothAsset::FClothDataflowTools::LogAndToastWarning(*this, FText::Format(LOCTEXT("VertexCountMismatchHeadline", "Failed to blend {0} attributes."), VertexType), FText::Format(LOCTEXT("VertexCountMismatchDetails", "Vertex count mismatch {0} != {1}. Set 'Require Same Vertex Counts' to false to disable this check."), CollectionCount, BlendCollectionCount)); }; if (ClothFacade.IsValid()) { if (BlendClothFacade.IsValid()) { if (bBlendSimMesh) { if (bBlend2DSimPositions) { const int32 NumSimVertices2D = ClothFacade.GetNumSimVertices2D(); const int32 NumBlendSimVertices2D = BlendClothFacade.GetNumSimVertices2D(); if (!bRequireSameVertexCounts || NumSimVertices2D == NumBlendSimVertices2D) { TArrayView SimPosition2D = ClothFacade.GetSimPosition2D(); TConstArrayView BlendSimPosition2D = BlendClothFacade.GetSimPosition2D(); ensure(SimPosition2D.Num() == NumSimVertices2D); ensure(BlendSimPosition2D.Num() == NumBlendSimVertices2D); Private::BlendValues(BlendingWeight, SimPosition2D, BlendSimPosition2D); } else { LogVertexCountMismatch(FText::FromString(TEXT("Sim Vertices 2D")), NumSimVertices2D, NumBlendSimVertices2D); } } if (bBlend3DSimPositions || bBlendSimNormals) { const int32 NumSimVertices3D = ClothFacade.GetNumSimVertices3D(); const int32 NumBlendSimVertices3D = BlendClothFacade.GetNumSimVertices3D(); if (!bRequireSameVertexCounts || NumSimVertices3D == NumBlendSimVertices3D) { if (bBlend3DSimPositions) { TArrayView SimPosition3D = ClothFacade.GetSimPosition3D(); TConstArrayView BlendSimPosition3D = BlendClothFacade.GetSimPosition3D(); ensure(SimPosition3D.Num() == NumSimVertices3D); ensure(BlendSimPosition3D.Num() == NumBlendSimVertices3D); Private::BlendValues(BlendingWeight, SimPosition3D, BlendSimPosition3D); } if (bBlendSimNormals) { TArrayView SimNormal = ClothFacade.GetSimNormal(); TConstArrayView BlendSimNormal = BlendClothFacade.GetSimNormal(); ensure(SimNormal.Num() == NumSimVertices3D); ensure(BlendSimNormal.Num() == NumBlendSimVertices3D); Private::BlendNormalizedValues(BlendingWeight, SimNormal, BlendSimNormal); } } else { LogVertexCountMismatch(FText::FromString(TEXT("Sim Vertices 3D")), NumSimVertices3D, NumBlendSimVertices3D); } } } if (bBlendRenderMesh) { const int32 NumRenderVertices = ClothFacade.GetNumRenderVertices(); const int32 NumBlendRenderVertices = BlendClothFacade.GetNumRenderVertices(); if (!bRequireSameVertexCounts || NumRenderVertices == NumBlendRenderVertices) { if (bBlendRenderPositions) { TArrayView RenderPosition = ClothFacade.GetRenderPosition(); TConstArrayView BlendRenderPosition = BlendClothFacade.GetRenderPosition(); ensure(RenderPosition.Num() == NumRenderVertices); ensure(BlendRenderPosition.Num() == NumBlendRenderVertices); Private::BlendValues(BlendingWeight, RenderPosition, BlendRenderPosition); } if (bBlendRenderNormalsAndTangents) { TArrayView RenderNormal = ClothFacade.GetRenderNormal(); TConstArrayView BlendRenderNormal = BlendClothFacade.GetRenderNormal(); ensure(RenderNormal.Num() == NumRenderVertices); ensure(BlendRenderNormal.Num() == NumBlendRenderVertices); Private::BlendNormalizedValues(BlendingWeight, RenderNormal, BlendRenderNormal); TArrayView RenderTangentU = ClothFacade.GetRenderTangentU(); TConstArrayView BlendRenderTangentU = BlendClothFacade.GetRenderTangentU(); ensure(RenderTangentU.Num() == NumRenderVertices); ensure(BlendRenderTangentU.Num() == NumBlendRenderVertices); Private::BlendNormalizedValues(BlendingWeight, RenderTangentU, BlendRenderTangentU); TArrayView RenderTangentV = ClothFacade.GetRenderTangentV(); TConstArrayView BlendRenderTangentV = BlendClothFacade.GetRenderTangentV(); ensure(RenderTangentV.Num() == NumRenderVertices); ensure(BlendRenderTangentV.Num() == NumBlendRenderVertices); Private::BlendNormalizedValues(BlendingWeight, RenderTangentV, BlendRenderTangentV); } if (bBlendRenderUVs) { TArrayView> RenderUVs = ClothFacade.GetRenderUVs(); TConstArrayView> BlendRenderUVs = BlendClothFacade.GetRenderUVs(); ensure(RenderUVs.Num() == NumRenderVertices); ensure(BlendRenderUVs.Num() == NumBlendRenderVertices); Private::BlendUVSets(BlendingWeight, RenderUVs, BlendRenderUVs); } if (bBlendRenderColors) { TArrayView RenderColor = ClothFacade.GetRenderColor(); TConstArrayView BlendRenderColor = BlendClothFacade.GetRenderColor(); ensure(RenderColor.Num() == NumRenderVertices); ensure(BlendRenderColor.Num() == NumBlendRenderVertices); Private::BlendValues(BlendingWeight, RenderColor, BlendRenderColor); } } else { LogVertexCountMismatch(FText::FromString(TEXT("Render Vertices")), NumRenderVertices, NumBlendRenderVertices); } } } else { UE::Chaos::ClothAsset::FClothDataflowTools::LogAndToastWarning(*this, LOCTEXT("InvalidBlendCollectionHeadline", "Invalid Blend Collection"), LOCTEXT("InvalidBlendCollectionDetails", "Input Blend Collection is not a valid Cloth Collection.") ); } } SetValue(Context, MoveTemp(*ClothCollection), &Collection); } } #undef LOCTEXT_NAMESPACE