// Copyright Epic Games, Inc. All Rights Reserved. #include "ChaosClothAsset/SimulationLongRangeAttachmentConfigNode.h" #include "ChaosClothAsset/ClothEngineTools.h" #include "Chaos/CollectionPropertyFacade.h" #include "Dataflow/DataflowInputOutput.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(SimulationLongRangeAttachmentConfigNode) FChaosClothAssetSimulationLongRangeAttachmentConfigNode_v2::FChaosClothAssetSimulationLongRangeAttachmentConfigNode_v2(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid) : FChaosClothAssetSimulationBaseConfigNode(InParam, InGuid) { RegisterCollectionConnections(); RegisterInputConnection(&FixedEndSet.StringValue, GET_MEMBER_NAME_CHECKED(FChaosClothAssetConnectableIStringValue, StringValue)); RegisterInputConnection(&TetherStiffness.WeightMap) .SetCanHidePin(true) .SetPinIsHidden(true); RegisterInputConnection(&TetherScale.WeightMap) .SetCanHidePin(true) .SetPinIsHidden(true); // Start with one set of option pins. for (int32 Index = 0; Index < NumInitialCustomTetherSets; ++Index) { AddPins(); } check(GetNumInputs() == NumRequiredInputs + NumInitialCustomTetherSets * 2); // Update NumRequiredInputs if you add more Inputs. This is used by Serialize. } void FChaosClothAssetSimulationLongRangeAttachmentConfigNode_v2::AddProperties(FPropertyHelper& PropertyHelper) const { PropertyHelper.SetPropertyWeighted(this, &TetherStiffness); PropertyHelper.SetPropertyWeighted(this, &TetherScale); PropertyHelper.SetPropertyBool(this, &bUseGeodesicTethers, {}, ECollectionPropertyFlags::Intrinsic); // Intrinsic since the tethers need to be recalculated. PropertyHelper.SetPropertyString(this, &FixedEndSet); } void FChaosClothAssetSimulationLongRangeAttachmentConfigNode_v2::EvaluateClothCollection(UE::Dataflow::FContext& Context, const TSharedRef& ClothCollection) const { const FString InFixedEndSetString = GetValue(Context, &FixedEndSet.StringValue); const FName InFixedEndSet(InFixedEndSetString); if (bEnableCustomTetherGeneration) { TArray> TetherEndSets; TetherEndSets.SetNumUninitialized(CustomTetherData.Num()); for (int32 Index = 0; Index < CustomTetherData.Num(); ++Index) { TetherEndSets[Index] = MakeTuple(FName(*GetValue(Context, GetDynamicEndConnectionReference(Index))), FName(*GetValue(Context, GetFixedEndConnectionReference(Index)))); } UE::Chaos::ClothAsset::FClothEngineTools::GenerateTethersFromCustomSelectionSets(ClothCollection, InFixedEndSet, TetherEndSets, bUseGeodesicTethers); } else { UE::Chaos::ClothAsset::FClothEngineTools::GenerateTethersFromSelectionSet(ClothCollection, InFixedEndSet, bUseGeodesicTethers); } } TArray FChaosClothAssetSimulationLongRangeAttachmentConfigNode_v2::AddPins() { const int32 Index = CustomTetherData.AddDefaulted(); TArray Pins; Pins.Reserve(2); { const FDataflowInput& Input = RegisterInputArrayConnection(GetFixedEndConnectionReference(Index), GET_MEMBER_NAME_CHECKED(FChaosClothAssetConnectableIStringValue, StringValue)); Pins.Emplace(UE::Dataflow::FPin{ UE::Dataflow::FPin::EDirection::INPUT, Input.GetType(), Input.GetName() }); } { const FDataflowInput& Input = RegisterInputArrayConnection(GetDynamicEndConnectionReference(Index), GET_MEMBER_NAME_CHECKED(FChaosClothAssetConnectableIStringValue, StringValue)); Pins.Emplace(UE::Dataflow::FPin{ UE::Dataflow::FPin::EDirection::INPUT, Input.GetType(), Input.GetName() }); } return Pins; } TArray FChaosClothAssetSimulationLongRangeAttachmentConfigNode_v2::GetPinsToRemove() const { const int32 Index = CustomTetherData.Num() - 1; check(CustomTetherData.IsValidIndex(Index)); TArray Pins; Pins.Reserve(2); if (const FDataflowInput* const Input = FindInput(GetFixedEndConnectionReference(Index))) { Pins.Emplace(UE::Dataflow::FPin{ UE::Dataflow::FPin::EDirection::INPUT, Input->GetType(), Input->GetName() }); } if (const FDataflowInput* const Input = FindInput(GetDynamicEndConnectionReference(Index))) { Pins.Emplace(UE::Dataflow::FPin{ UE::Dataflow::FPin::EDirection::INPUT, Input->GetType(), Input->GetName() }); } return Pins; } void FChaosClothAssetSimulationLongRangeAttachmentConfigNode_v2::OnPinRemoved(const UE::Dataflow::FPin& Pin) { const int32 Index = CustomTetherData.Num() - 1; check(CustomTetherData.IsValidIndex(Index)); const FDataflowInput* const FirstInput = FindInput(GetFixedEndConnectionReference(Index)); const FDataflowInput* const SecondInput = FindInput(GetDynamicEndConnectionReference(Index)); check(FirstInput || SecondInput); const bool bIsFirstInput = FirstInput && FirstInput->GetName() == Pin.Name; const bool bIsSecondInput = SecondInput && SecondInput->GetName() == Pin.Name; if ((bIsFirstInput && !SecondInput) || (bIsSecondInput && !FirstInput)) { // Both inputs removed. Remove array index. CustomTetherData.SetNum(Index); } return Super::OnPinRemoved(Pin); } void FChaosClothAssetSimulationLongRangeAttachmentConfigNode_v2::PostSerialize(const FArchive& Ar) { // Restore the pins when re-loading so they can get properly reconnected if (Ar.IsLoading()) { check(CustomTetherData.Num() >= NumInitialCustomTetherSets); for (int32 Index = 0; Index < NumInitialCustomTetherSets; ++Index) { check(FindInput(GetFixedEndConnectionReference(Index))); check(FindInput(GetDynamicEndConnectionReference(Index))); } for (int32 Index = NumInitialCustomTetherSets; Index < CustomTetherData.Num(); ++Index) { FindOrRegisterInputArrayConnection(GetFixedEndConnectionReference(Index), GET_MEMBER_NAME_CHECKED(FChaosClothAssetConnectableIStringValue, StringValue)); FindOrRegisterInputArrayConnection(GetDynamicEndConnectionReference(Index), GET_MEMBER_NAME_CHECKED(FChaosClothAssetConnectableIStringValue, StringValue)); } if (Ar.IsTransacting()) { const int32 OrigNumRegisteredInputs = GetNumInputs(); check(OrigNumRegisteredInputs >= NumRequiredInputs + NumInitialCustomTetherSets * 2); const int32 OrigNumSets = CustomTetherData.Num(); const int32 OrigNumRegisteredSets = (OrigNumRegisteredInputs - NumRequiredInputs) / 2; if (OrigNumRegisteredSets > OrigNumSets) { ensure(Ar.IsTransacting()); // Temporarily expand SelectionFilterSets so we can get connection references. CustomTetherData.SetNum(OrigNumRegisteredSets); for (int32 Index = OrigNumSets; Index < CustomTetherData.Num(); ++Index) { UnregisterInputConnection(GetDynamicEndConnectionReference(Index)); UnregisterInputConnection(GetFixedEndConnectionReference(Index)); } CustomTetherData.SetNum(OrigNumRegisteredSets); } } else { ensureAlways(CustomTetherData.Num() * 2 + NumRequiredInputs == GetNumInputs()); } } } UE::Dataflow::TConnectionReference FChaosClothAssetSimulationLongRangeAttachmentConfigNode_v2::GetFixedEndConnectionReference(int32 Index) const { return { &CustomTetherData[Index].CustomFixedEndSet.StringValue, Index, &CustomTetherData }; } UE::Dataflow::TConnectionReference FChaosClothAssetSimulationLongRangeAttachmentConfigNode_v2::GetDynamicEndConnectionReference(int32 Index) const { return { &CustomTetherData[Index].CustomDynamicEndSet.StringValue, Index, &CustomTetherData }; } FChaosClothAssetSimulationLongRangeAttachmentConfigNode::FChaosClothAssetSimulationLongRangeAttachmentConfigNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid) : FChaosClothAssetSimulationBaseConfigNode(InParam, InGuid) { RegisterCollectionConnections(); RegisterInputConnection(&FixedEndWeightMap); RegisterInputConnection(&TetherStiffness.WeightMap) .SetCanHidePin(true) .SetPinIsHidden(true); RegisterInputConnection(&TetherScale.WeightMap) .SetCanHidePin(true) .SetPinIsHidden(true); } void FChaosClothAssetSimulationLongRangeAttachmentConfigNode::AddProperties(FPropertyHelper& PropertyHelper) const { PRAGMA_DISABLE_DEPRECATION_WARNINGS // SetProperty functions are templated and cause deprecation warnings with the now deprecated v1 PropertyHelper.SetPropertyWeighted(this, &TetherStiffness); PropertyHelper.SetPropertyWeighted(this, &TetherScale); PropertyHelper.SetPropertyBool(this, &bUseGeodesicTethers, {}, ECollectionPropertyFlags::Intrinsic); // Intrinsic since the tethers need to be recalculated. PropertyHelper.SetPropertyString(this, &FixedEndWeightMap); PRAGMA_ENABLE_DEPRECATION_WARNINGS } void FChaosClothAssetSimulationLongRangeAttachmentConfigNode::EvaluateClothCollection(UE::Dataflow::FContext& Context, const TSharedRef& ClothCollection) const { const FString InFixedEndWeightMapString = GetValue(Context, &FixedEndWeightMap); const FName InFixedEndWeightMap(InFixedEndWeightMapString); UE::Chaos::ClothAsset::FClothEngineTools::GenerateTethers(ClothCollection, InFixedEndWeightMap, bUseGeodesicTethers); }