// Copyright Epic Games, Inc. All Rights Reserved. #include "Dataflow/GeometryCollectionSelectionNodes.h" #include "Dataflow/DataflowCore.h" #if WITH_EDITOR #include "Dataflow/DataflowRenderingViewMode.h" #endif #include "Dataflow/DataflowDebugDrawInterface.h" #include "Engine/StaticMesh.h" #include "GeometryCollection/GeometryCollectionObject.h" #include "GeometryCollection/ManagedArrayCollection.h" #include "GeometryCollection/GeometryCollection.h" #include "GeometryCollection/GeometryCollectionEngineUtility.h" #include "GeometryCollection/GeometryCollectionEngineConversion.h" #include "Logging/LogMacros.h" #include "Templates/SharedPointer.h" #include "UObject/UnrealTypePrivate.h" #include "DynamicMeshToMeshDescription.h" #include "MeshDescriptionToDynamicMesh.h" #include "StaticMeshAttributes.h" #include "DynamicMeshEditor.h" #include "Operations/MeshBoolean.h" #include "EngineGlobals.h" #include "GeometryCollection/GeometryCollectionAlgo.h" #include "GeometryCollection/GeometryCollectionClusteringUtility.h" #include "GeometryCollection/GeometryCollectionConvexUtility.h" #include "Voronoi/Voronoi.h" #include "PlanarCut.h" #include "GeometryCollection/GeometryCollectionProximityUtility.h" #include "FractureEngineClustering.h" #include "FractureEngineSelection.h" #include "GeometryCollection/Facades/CollectionTransformSelectionFacade.h" #include "GeometryCollection/Facades/CollectionHierarchyFacade.h" #include "Internationalization/Regex.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(GeometryCollectionSelectionNodes) namespace UE::Dataflow { void GeometryCollectionSelectionNodes() { DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionAllDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionInfoDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionNoneDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionRandomDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionRootDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionCustomDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionCustomDataflowNode_v2); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionFromIndexArrayDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionParentDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionByPercentageDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionChildrenDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionSiblingsDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionLevelDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionTargetLevelDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionContactDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionLeafDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionClusterDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionClusterDataflowNode_v2); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionBySizeDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionByVolumeDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionInBoxDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionInSphereDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionByFloatAttrDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FSelectFloatArrayIndicesInRangeDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionByIntAttrDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionVertexSelectionCustomDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionFaceSelectionCustomDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionSelectionConvertDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionVertexSelectionByPercentageDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionVertexSelectionSetOperationDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionSelectionByAttrDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FGeometrySelectionToVertexSelectionDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionSelectInternalFacesDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionSelectTransformStringDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionSetTransformStringValueDataflowNode); ; // generic input nodes DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionSelectionSetOperationDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionSelectionInvertDataflowNode); // deprecated DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionInvertDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionFaceSelectionInvertDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCollectionTransformSelectionSetOperationDataflowNode); } } void FCollectionTransformSelectionAllDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); const TArray& SelectionArr = TransformSelectionFacade.SelectAll(); FDataflowTransformSelection NewTransformSelection; NewTransformSelection.InitializeFromCollection(InCollection, false); NewTransformSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(NewTransformSelection), &TransformSelection); } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } void FCollectionTransformSelectionSetOperationDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { const FDataflowTransformSelection& InTransformSelectionA = GetValue(Context, &TransformSelectionA); const FDataflowTransformSelection& InTransformSelectionB = GetValue(Context, &TransformSelectionB); FDataflowTransformSelection NewTransformSelection; if (InTransformSelectionA.Num() == InTransformSelectionB.Num()) { if (Operation == ESetOperationEnum::Dataflow_SetOperation_AND) { InTransformSelectionA.AND(InTransformSelectionB, NewTransformSelection); } else if (Operation == ESetOperationEnum::Dataflow_SetOperation_OR) { InTransformSelectionA.OR(InTransformSelectionB, NewTransformSelection); } else if (Operation == ESetOperationEnum::Dataflow_SetOperation_XOR) { InTransformSelectionA.XOR(InTransformSelectionB, NewTransformSelection); } else if (Operation == ESetOperationEnum::Dataflow_SetOperation_Subtract) { InTransformSelectionA.Subtract(InTransformSelectionB, NewTransformSelection); } } else { // ERROR: INPUT TRANSFORMSELECTIONS HAVE DIFFERENT NUMBER OF ELEMENTS FString ErrorStr = "Input TransformSelections have different number of elements."; UE_LOG(LogTemp, Error, TEXT("[Dataflow ERROR] %s"), *ErrorStr); } SetValue(Context, MoveTemp(NewTransformSelection), &TransformSelection); } } namespace { struct BoneInfo { int32 BoneIndex; int32 Level; }; } static void ExpandRecursive(const int32 BoneIndex, int32 Level, const TManagedArray>& Children, TArray& BoneHierarchy) { BoneHierarchy.Add({ BoneIndex, Level }); TSet ChildrenSet = Children[BoneIndex]; if (ChildrenSet.Num() > 0) { for (auto& Child : ChildrenSet) { ExpandRecursive(Child, Level + 1, Children, BoneHierarchy); } } } static void BuildHierarchicalOutput(const TManagedArray& Parents, const TManagedArray>& Children, const TManagedArray& BoneNames, const FDataflowTransformSelection& TransformSelection, FString& OutputStr) { TArray BoneHierarchy; int32 NumElements = Parents.Num(); for (int32 Index = 0; Index < NumElements; ++Index) { if (Parents[Index] == FGeometryCollection::Invalid) { ExpandRecursive(Index, 0, Children, BoneHierarchy); } } // Get level max int32 LevelMax = -1; int32 BoneNameLengthMax = -1; for (int32 Idx = 0; Idx < BoneHierarchy.Num(); ++Idx) { if (BoneHierarchy[Idx].Level > LevelMax) { LevelMax = BoneHierarchy[Idx].Level; } int32 BoneNameLength = BoneNames[Idx].Len(); if (BoneNameLength > BoneNameLengthMax) { BoneNameLengthMax = BoneNameLength; } } const int32 BoneIndexWidth = 2 + LevelMax * 2 + 6; const int32 BoneNameWidth = BoneNameLengthMax + 2; const int32 SelectedWidth = 10; for (int32 Idx = 0; Idx < BoneHierarchy.Num(); ++Idx) { FString BoneIndexStr, BoneNameStr; BoneIndexStr.Reserve(BoneIndexWidth); BoneNameStr.Reserve(BoneNameWidth); if (BoneHierarchy[Idx].Level == 0) { BoneIndexStr.Appendf(TEXT("[%d]"), BoneHierarchy[Idx].BoneIndex); } else { BoneIndexStr.Appendf(TEXT(" |")); for (int32 Idx1 = 0; Idx1 < BoneHierarchy[Idx].Level; ++Idx1) { BoneIndexStr.Appendf(TEXT("--")); } BoneIndexStr.Appendf(TEXT("[%d]"), BoneHierarchy[Idx].BoneIndex); } BoneIndexStr = BoneIndexStr.RightPad(BoneIndexWidth); BoneNameStr.Appendf(TEXT("%s"), *BoneNames[Idx]); BoneNameStr = BoneNameStr.RightPad(BoneNameWidth); OutputStr.Appendf(TEXT("%s%s%s\n\n"), *BoneIndexStr, *BoneNameStr, (TransformSelection.IsSelected(BoneHierarchy[Idx].BoneIndex) ? TEXT("Selected") : TEXT("---"))); } } void FCollectionTransformSelectionInfoDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&String)) { const FDataflowTransformSelection& InTransformSelection = GetValue(Context, &TransformSelection); const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); FString OutputStr; OutputStr.Appendf(TEXT("\n----------------------------------------\n")); OutputStr.Appendf(TEXT("Number of Elements: %d\n"), InTransformSelection.Num()); // Hierarchical display if (InCollection.HasGroup(FGeometryCollection::TransformGroup) && InCollection.HasAttribute("Parent", FGeometryCollection::TransformGroup) && InCollection.HasAttribute("Children", FGeometryCollection::TransformGroup) && InCollection.HasAttribute("BoneName", FGeometryCollection::TransformGroup)) { if (InTransformSelection.Num() == InCollection.NumElements(FGeometryCollection::TransformGroup)) { const TManagedArray& Parents = InCollection.GetAttribute("Parent", FGeometryCollection::TransformGroup); const TManagedArray>& Children = InCollection.GetAttribute>("Children", FGeometryCollection::TransformGroup); const TManagedArray& BoneNames = InCollection.GetAttribute("BoneName", FGeometryCollection::TransformGroup); BuildHierarchicalOutput(Parents, Children, BoneNames, InTransformSelection, OutputStr); } else { // ERROR: TransformSelection doesn't match the Collection FString ErrorStr = "TransformSelection doesn't match the Collection."; UE_LOG(LogTemp, Error, TEXT("[Dataflow ERROR] %s"), *ErrorStr); } } else // Simple display { for (int32 Idx = 0; Idx < InTransformSelection.Num(); ++Idx) { OutputStr.Appendf(TEXT("%4d: %s\n"), Idx, (InTransformSelection.IsSelected(Idx) ? TEXT("Selected") : TEXT("---"))); } } OutputStr.Appendf(TEXT("----------------------------------------\n")); SetValue(Context, MoveTemp(OutputStr), &String); } } void FCollectionTransformSelectionNoneDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); const TArray& SelectionArr = TransformSelectionFacade.SelectNone(); FDataflowTransformSelection NewTransformSelection; NewTransformSelection.InitializeFromCollection(InCollection, false); NewTransformSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(NewTransformSelection), &TransformSelection); } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } void FCollectionTransformSelectionInvertDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { FDataflowTransformSelection InTransformSelection = GetValue(Context, &TransformSelection); InTransformSelection.Invert(); SetValue(Context, MoveTemp(InTransformSelection), &TransformSelection); } } void FCollectionTransformSelectionRandomDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); float RandomSeedVal = GetValue(Context, &RandomSeed); float RandomThresholdVal = GetValue(Context, &RandomThreshold); GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); const TArray& SelectionArr = TransformSelectionFacade.SelectRandom(bDeterministic, RandomSeedVal, RandomThresholdVal); FDataflowTransformSelection NewTransformSelection; NewTransformSelection.InitializeFromCollection(InCollection, false); NewTransformSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(NewTransformSelection), &TransformSelection); } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } void FCollectionTransformSelectionRootDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); const TArray& SelectionArr = TransformSelectionFacade.SelectRootBones(); FDataflowTransformSelection NewTransformSelection; NewTransformSelection.InitializeFromCollection(InCollection, false); NewTransformSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(NewTransformSelection), &TransformSelection); } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } void FCollectionTransformSelectionCustomDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); if (InCollection.HasGroup(FGeometryCollection::TransformGroup)) { FDataflowTransformSelection NewTransformSelection; NewTransformSelection.InitializeFromCollection(InCollection, false); const FString InBoneIndices = GetValue(Context, &BoneIndicies); TArray Indices; InBoneIndices.ParseIntoArray(Indices, TEXT(" "), true); for (FString IndexStr : Indices) { if (IndexStr.IsNumeric()) { int32 Index = FCString::Atoi(*IndexStr); if (NewTransformSelection.IsValidIndex(Index)) { NewTransformSelection.SetSelected(Index); } else { // ERROR: INVALID INDEX Context.Error(FString::Printf( TEXT("Index %d is not a valid vertex index for transform group size %d."), Index, NewTransformSelection.Num()), this); } } } SetValue(Context, MoveTemp(NewTransformSelection), &TransformSelection); } else { SetValue(Context, FDataflowTransformSelection(), &TransformSelection); } } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } namespace UE::Dataflow::Private { const static uint32 Error_InvalidChars = 1; const static uint32 Error_InvalidFormatInSegment = 2; /* e.g. "0, 2, 5-10, 12-15". If left empty, all will be used */ static bool ParseIndicesStr(const FString& InFramesString, TArray& OutIndices, uint32& OutErrorCode) { static const FRegexPattern AllowedCharsPattern(TEXT("^[-,0-9\\s]+$")); if (!FRegexMatcher(AllowedCharsPattern, InFramesString).FindNext()) { // Input contains invalid characters OutErrorCode = Error_InvalidChars; return false; } static const FRegexPattern SingleNumberPattern(TEXT("^\\s*(\\d+)\\s*$")); static const FRegexPattern RangePattern(TEXT("^\\s*(\\d+)\\s*-\\s*(\\d+)\\s*$")); TArray Segments; InFramesString.ParseIntoArray(Segments, TEXT(","), true); for (const FString& Segment : Segments) { bool bSegmentValid = false; FRegexMatcher SingleNumberMatcher(SingleNumberPattern, Segment); if (SingleNumberMatcher.FindNext()) { const int32 SingleNumber = FCString::Atoi(*SingleNumberMatcher.GetCaptureGroup(1)); OutIndices.Add(SingleNumber); bSegmentValid = true; } else { FRegexMatcher RangeMatcher(RangePattern, Segment); if (RangeMatcher.FindNext()) { const int32 RangeStart = FCString::Atoi(*RangeMatcher.GetCaptureGroup(1)); const int32 RangeEnd = FCString::Atoi(*RangeMatcher.GetCaptureGroup(2)); for (int32 i = RangeStart; i <= RangeEnd; ++i) { OutIndices.Add(i); } bSegmentValid = true; } } if (!bSegmentValid) { // Invalid format in segment OutErrorCode = Error_InvalidFormatInSegment; return false; } } return true; } } FCollectionTransformSelectionCustomDataflowNode_v2::FCollectionTransformSelectionCustomDataflowNode_v2(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid) : FDataflowNode(InParam, InGuid) { RegisterInputConnection(&Collection); RegisterInputConnection(&BoneIndices); RegisterOutputConnection(&Collection, &Collection); RegisterOutputConnection(&TransformSelection); } void FCollectionTransformSelectionCustomDataflowNode_v2::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); if (InCollection.HasGroup(FGeometryCollection::TransformGroup)) { FDataflowTransformSelection NewTransformSelection; NewTransformSelection.InitializeFromCollection(InCollection, false); const FString InBoneIndices = GetValue(Context, &BoneIndices); TArray Indices; uint32 ErrorCode = 0; if (UE::Dataflow::Private::ParseIndicesStr(InBoneIndices, Indices, ErrorCode)) { NewTransformSelection.SetSelected(Indices); } else { if (ErrorCode == UE::Dataflow::Private::Error_InvalidChars) { // Handle Error } else if (ErrorCode == UE::Dataflow::Private::Error_InvalidFormatInSegment) { // Handle Error } } SetValue(Context, MoveTemp(NewTransformSelection), &TransformSelection); } else { SetValue(Context, FDataflowTransformSelection(), &TransformSelection); } } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } void FCollectionTransformSelectionFromIndexArrayDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); if (InCollection.HasGroup(FGeometryCollection::TransformGroup)) { const TArray& InBoneIndices = GetValue(Context, &BoneIndices); FDataflowTransformSelection NewTransformSelection; NewTransformSelection.InitializeFromCollection(InCollection, false); for (int32 SelectedIdx : InBoneIndices) { if (NewTransformSelection.IsValidIndex(SelectedIdx)) { NewTransformSelection.SetSelected(SelectedIdx); } else { Context.Error(FString::Printf( TEXT("Index %d is not a valid vertex index for transform group size %d."), SelectedIdx, NewTransformSelection.Num()), this); } } SetValue(Context, MoveTemp(NewTransformSelection), &TransformSelection); } else { SetValue(Context, FDataflowTransformSelection(), &TransformSelection); } } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } void FCollectionTransformSelectionParentDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); FDataflowTransformSelection InTransformSelection = GetValue(Context, &TransformSelection); GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); TArray SelectionArr = InTransformSelection.AsArray(); TransformSelectionFacade.SelectParent(SelectionArr); InTransformSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(InTransformSelection), &TransformSelection); } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } void FCollectionTransformSelectionByPercentageDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { FDataflowTransformSelection InTransformSelection = GetValue(Context, &TransformSelection); int32 InPercentage = GetValue(Context, &Percentage); float InRandomSeed = GetValue(Context, &RandomSeed); TArray SelectionArr = InTransformSelection.AsArray(); GeometryCollection::Facades::FCollectionTransformSelectionFacade::SelectByPercentage(SelectionArr, InPercentage, bDeterministic, InRandomSeed); InTransformSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(InTransformSelection), &TransformSelection); } } void FCollectionTransformSelectionChildrenDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); FDataflowTransformSelection InTransformSelection = GetValue(Context, &TransformSelection); GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); TArray SelectionArr = InTransformSelection.AsArray(); TransformSelectionFacade.SelectChildren(SelectionArr); InTransformSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(InTransformSelection), &TransformSelection); } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } void FCollectionTransformSelectionSiblingsDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); FDataflowTransformSelection InTransformSelection = GetValue(Context, &TransformSelection); GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); TArray SelectionArr = InTransformSelection.AsArray(); TransformSelectionFacade.SelectSiblings(SelectionArr); InTransformSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(InTransformSelection), &TransformSelection); } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } void FCollectionTransformSelectionLevelDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection) || Out->IsA(&Collection)) { FManagedArrayCollection OutCollection = GetValue(Context, &Collection); FDataflowTransformSelection InTransformSelection = GetValue(Context, &TransformSelection); // make sure there's a level attribute Chaos::Facades::FCollectionHierarchyFacade HierarchyFacade(OutCollection); if (!HierarchyFacade.HasLevelAttribute()) { HierarchyFacade.GenerateLevelAttribute(); } GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(OutCollection); TArray SelectionArr = InTransformSelection.AsArray(); TransformSelectionFacade.SelectLevel(SelectionArr); InTransformSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(InTransformSelection), &TransformSelection); SetValue(Context, MoveTemp(OutCollection), &Collection); } } void FCollectionTransformSelectionTargetLevelDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection) || Out->IsA(&Collection)) { FManagedArrayCollection OutCollection = GetValue(Context, &Collection); // make sure there's a level attribute Chaos::Facades::FCollectionHierarchyFacade HierarchyFacade(OutCollection); if (!HierarchyFacade.HasLevelAttribute()) { HierarchyFacade.GenerateLevelAttribute(); } GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(OutCollection); const int32 InTargetLevel = GetValue(Context, &TargetLevel); const TArray AllAtLevel = TransformSelectionFacade.GetBonesExactlyAtLevel(InTargetLevel, bSkipEmbedded); FDataflowTransformSelection NewTransformSelection; NewTransformSelection.InitializeFromCollection(OutCollection, false); NewTransformSelection.SetFromArray(AllAtLevel); SetValue(Context, MoveTemp(NewTransformSelection), &TransformSelection); SetValue(Context, MoveTemp(OutCollection), &Collection); } } void FCollectionTransformSelectionContactDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); FDataflowTransformSelection InTransformSelection = GetValue(Context, &TransformSelection); GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); TArray SelectionArr = InTransformSelection.AsArray(); TransformSelectionFacade.SelectContact(SelectionArr, bAllowContactInParentLevels); InTransformSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(InTransformSelection), &TransformSelection); } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } void FCollectionTransformSelectionLeafDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); const TArray& SelectionArr = TransformSelectionFacade.SelectLeaf(); FDataflowTransformSelection NewTransformSelection; NewTransformSelection.InitializeFromCollection(InCollection, false); NewTransformSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(NewTransformSelection), &TransformSelection); } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } void FCollectionTransformSelectionClusterDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); // this node used to use SelectCluster() but this was buggy and woudl select the leaves instead // for this reason this node is now deprecated and we need to keep it doing what it sued to : SelectLeaf() // version 2 of the node properly use the right way const TArray& SelectionArr = TransformSelectionFacade.SelectLeaf(); // used to be buggy SelectCluster() - see comment above FDataflowTransformSelection NewTransformSelection; NewTransformSelection.InitializeFromCollection(InCollection, false); NewTransformSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(NewTransformSelection), &TransformSelection); } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } void FCollectionTransformSelectionClusterDataflowNode_v2::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); const TArray& SelectionArr = TransformSelectionFacade.SelectCluster(); FDataflowTransformSelection NewTransformSelection; NewTransformSelection.InitializeFromCollection(InCollection, false); NewTransformSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(NewTransformSelection), &TransformSelection); } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } void FCollectionTransformSelectionBySizeDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); float InSizeMin = GetValue(Context, &SizeMin); float InSizeMax = GetValue(Context, &SizeMax); bool bInsideRange = RangeSetting == ERangeSettingEnum::Dataflow_RangeSetting_InsideRange; GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); const TArray& SelectionArr = TransformSelectionFacade.SelectBySize(InSizeMin, InSizeMax, bInclusive, bInsideRange, bUseRelativeSize); FDataflowTransformSelection NewTransformSelection; NewTransformSelection.InitializeFromCollection(InCollection, false); NewTransformSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(NewTransformSelection), &TransformSelection); } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } ////////////////////////////////////////////////////////////////////////////////////////////////// void FCollectionTransformSelectionByVolumeDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); float InVolumeMin = GetValue(Context, &VolumeMin); float InVolumeMax = GetValue(Context, &VolumeMax); bool bInsideRange = RangeSetting == ERangeSettingEnum::Dataflow_RangeSetting_InsideRange; GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); const TArray& SelectionArr = TransformSelectionFacade.SelectByVolume(InVolumeMin, InVolumeMax, bInclusive, bInsideRange); FDataflowTransformSelection NewTransformSelection; NewTransformSelection.InitializeFromCollection(InCollection, false); NewTransformSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(NewTransformSelection), &TransformSelection); } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } void FCollectionTransformSelectionInBoxDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); const FBox& InBox = GetValue(Context, &Box); const FTransform& InTransform = GetValue(Context, &Transform); GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); TArray SelectionArr; if (Type == ESelectSubjectTypeEnum::Dataflow_SelectSubjectType_Vertices) { SelectionArr = TransformSelectionFacade.SelectVerticesInBox(InBox, InTransform, bAllVerticesMustContainedInBox); } else if (Type == ESelectSubjectTypeEnum::Dataflow_SelectSubjectType_BoundingBox) { SelectionArr = TransformSelectionFacade.SelectBoundingBoxInBox(InBox, InTransform); } else if (Type == ESelectSubjectTypeEnum::Dataflow_SelectSubjectType_Centroid) { SelectionArr = TransformSelectionFacade.SelectCentroidInBox(InBox, InTransform); } FDataflowTransformSelection NewTransformSelection; NewTransformSelection.InitializeFromCollection(InCollection, false); NewTransformSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(NewTransformSelection), &TransformSelection); } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } #if WITH_EDITOR bool FCollectionTransformSelectionInBoxDataflowNode::CanDebugDrawViewMode(const FName& ViewModeName) const { return ViewModeName == UE::Dataflow::FDataflowConstruction3DViewMode::Name; } void FCollectionTransformSelectionInBoxDataflowNode::DebugDraw(UE::Dataflow::FContext& Context, IDataflowDebugDrawInterface& DataflowRenderingInterface, const FDebugDrawParameters& DebugDrawParameters) const { if ((DebugDrawParameters.bNodeIsSelected || DebugDrawParameters.bNodeIsPinned)) { const FBox& InBox = GetValue(Context, &Box); const FTransform& InTransform = GetValue(Context, &Transform); DataflowRenderingInterface.SetLineWidth(1.0); DataflowRenderingInterface.SetWireframe(true); DataflowRenderingInterface.SetWorldPriority(); DataflowRenderingInterface.SetColor(FLinearColor::Red); const FVector TransformedCenter = InBox.GetCenter() + InTransform.GetTranslation(); const FVector ScaledExtent = InBox.GetExtent() * InTransform.GetScale3D(); DataflowRenderingInterface.DrawBox(ScaledExtent, InTransform.GetRotation(), TransformedCenter, 1.0); } } #endif ////////////////////////////////////////////////////////////////////////////////////////////////// void FCollectionTransformSelectionInSphereDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); const FSphere& InSphere = GetValue(Context, &Sphere); const FTransform& InTransform = GetValue(Context, &Transform); GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); TArray SelectionArr; if (Type == ESelectSubjectTypeEnum::Dataflow_SelectSubjectType_Vertices) { SelectionArr = TransformSelectionFacade.SelectVerticesInSphere(InSphere, InTransform, bAllVerticesMustContainedInSphere); } else if (Type == ESelectSubjectTypeEnum::Dataflow_SelectSubjectType_BoundingBox) { SelectionArr = TransformSelectionFacade.SelectBoundingBoxInSphere(InSphere, InTransform); } else if (Type == ESelectSubjectTypeEnum::Dataflow_SelectSubjectType_Centroid) { SelectionArr = TransformSelectionFacade.SelectCentroidInSphere(InSphere, InTransform); } FDataflowTransformSelection NewTransformSelection; NewTransformSelection.InitializeFromCollection(InCollection, false); NewTransformSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(NewTransformSelection), &TransformSelection); } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } #if WITH_EDITOR bool FCollectionTransformSelectionInSphereDataflowNode::CanDebugDrawViewMode(const FName& ViewModeName) const { return ViewModeName == UE::Dataflow::FDataflowConstruction3DViewMode::Name; } void FCollectionTransformSelectionInSphereDataflowNode::DebugDraw(UE::Dataflow::FContext& Context, IDataflowDebugDrawInterface& DataflowRenderingInterface, const FDebugDrawParameters& DebugDrawParameters) const { if ((DebugDrawParameters.bNodeIsSelected || DebugDrawParameters.bNodeIsPinned)) { const FSphere& InSphere = GetValue(Context, &Sphere); const FTransform& InTransform = GetValue(Context, &Transform); DataflowRenderingInterface.SetLineWidth(1.0); DataflowRenderingInterface.SetWireframe(true); DataflowRenderingInterface.SetWorldPriority(); DataflowRenderingInterface.SetColor(FLinearColor::Red); const FVector TransformedCenter = InSphere.Center + InTransform.GetTranslation(); const double ScaledRadius = InSphere.W * InTransform.GetScale3D().GetMax(); DataflowRenderingInterface.DrawSphere(TransformedCenter, ScaledRadius); } } #endif ////////////////////////////////////////////////////////////////////////////////////////////////// void FCollectionTransformSelectionByFloatAttrDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); float InMin = GetValue(Context, &Min); float InMax = GetValue(Context, &Max); bool bInsideRange = RangeSetting == ERangeSettingEnum::Dataflow_RangeSetting_InsideRange; GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); const TArray& SelectionArr = TransformSelectionFacade.SelectByFloatAttribute(GroupName, AttrName, InMin, InMax, bInclusive, bInsideRange); FDataflowTransformSelection NewTransformSelection; NewTransformSelection.InitializeFromCollection(InCollection, false); NewTransformSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(NewTransformSelection), &TransformSelection); } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } void FSelectFloatArrayIndicesInRangeDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&Indices)) { const TArray& InValues = GetValue(Context, &Values); float InMin = GetValue(Context, &Min); float InMax = GetValue(Context, &Max); bool bInsideRange = RangeSetting == ERangeSettingEnum::Dataflow_RangeSetting_InsideRange; TArray OutIndices; for (int32 Idx = 0; Idx < InValues.Num(); ++Idx) { const float FloatValue = InValues[Idx]; if (bInsideRange && FloatValue > Min && FloatValue < Max) { OutIndices.Add(Idx); } else if (!bInsideRange && (FloatValue < Min || FloatValue > Max)) { OutIndices.Add(Idx); } else if (bInclusive && (FloatValue == Min || FloatValue == Max)) { OutIndices.Add(Idx); } } SetValue(Context, MoveTemp(OutIndices), &Indices); } } void FCollectionTransformSelectionByIntAttrDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); int32 InMin = GetValue(Context, &Min); int32 InMax = GetValue(Context, &Max); bool bInsideRange = RangeSetting == ERangeSettingEnum::Dataflow_RangeSetting_InsideRange; GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); const TArray& SelectionArr = TransformSelectionFacade.SelectByIntAttribute(GroupName, AttrName, InMin, InMax, bInclusive, bInsideRange); FDataflowTransformSelection NewTransformSelection; NewTransformSelection.InitializeFromCollection(InCollection, false); NewTransformSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(NewTransformSelection), &TransformSelection); } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } void FCollectionVertexSelectionCustomDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&VertexSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); if (InCollection.HasGroup(FGeometryCollection::VerticesGroup)) { FDataflowVertexSelection NewVertexSelection; NewVertexSelection.InitializeFromCollection(InCollection, false); const FString InVertexIndices = GetValue(Context, &VertexIndicies); TArray Indices; InVertexIndices.ParseIntoArray(Indices, TEXT(" "), true); for (FString IndexStr : Indices) { if (IndexStr.IsNumeric()) { int32 Index = FCString::Atoi(*IndexStr); if (NewVertexSelection.IsValidIndex(Index)) { NewVertexSelection.SetSelected(Index); } else { // ERROR: INVALID INDEX Context.Error(FString::Printf( TEXT("Index %d is not a valid vertex index for vertex group size %d."), Index, NewVertexSelection.Num()), this); } } } SetValue(Context, MoveTemp(NewVertexSelection), &VertexSelection); } else { SetValue(Context, FDataflowVertexSelection(), &VertexSelection); } } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } void FCollectionFaceSelectionCustomDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&FaceSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); if (InCollection.HasGroup(FGeometryCollection::FacesGroup)) { FDataflowFaceSelection NewFaceSelection; NewFaceSelection.InitializeFromCollection(InCollection, false); const FString InFaceIndices = GetValue(Context, &FaceIndicies); TArray Indices; InFaceIndices.ParseIntoArray(Indices, TEXT(" "), true); for (FString& IndexStr : Indices) { if (IndexStr.IsNumeric()) { const int32 Index = FCString::Atoi(*IndexStr); if (NewFaceSelection.IsValidIndex(Index)) { NewFaceSelection.SetSelected(Index); } else { Context.Error(FString::Printf( TEXT("Index %d is not a valid vertex index for face group size %d."), Index, NewFaceSelection.Num()), this); } } } SetValue(Context, MoveTemp(NewFaceSelection), &FaceSelection); } else { SetValue(Context, FDataflowFaceSelection(), &FaceSelection); } } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } void FCollectionSelectionConvertDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&TransformSelection)) { if (IsConnected(&VertexSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); const FDataflowVertexSelection& InVertexSelection = GetValue(Context, &VertexSelection); GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); const TArray& SelectionArr = TransformSelectionFacade.ConvertVertexSelectionToTransformSelection(InVertexSelection.AsArray(), bAllElementsMustBeSelected); FDataflowTransformSelection NewTransformSelection; NewTransformSelection.InitializeFromCollection(InCollection, false); NewTransformSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(NewTransformSelection), &TransformSelection); } else if (IsConnected(&FaceSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); const FDataflowFaceSelection& InFaceSelection = GetValue(Context, &FaceSelection); GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); const TArray& SelectionArr = TransformSelectionFacade.ConvertFaceSelectionToTransformSelection(InFaceSelection.AsArray(), bAllElementsMustBeSelected); FDataflowTransformSelection NewTransformSelection; NewTransformSelection.InitializeFromCollection(InCollection, false); NewTransformSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(NewTransformSelection), &TransformSelection); } else { // Passthrough SafeForwardInput(Context, &TransformSelection, &TransformSelection); } } else if (Out->IsA(&FaceSelection)) { if (IsConnected(&VertexSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); const FDataflowVertexSelection& InVertexSelection = GetValue(Context, &VertexSelection); GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); const TArray& SelectionArr = TransformSelectionFacade.ConvertVertexSelectionToFaceSelection(InVertexSelection.AsArray(), bAllElementsMustBeSelected); FDataflowFaceSelection NewFaceSelection; NewFaceSelection.InitializeFromCollection(InCollection, false); NewFaceSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(NewFaceSelection), &FaceSelection); } else if (IsConnected(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); const FDataflowTransformSelection& InTransformSelection = GetValue(Context, &TransformSelection); GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); const TArray& SelectionArr = TransformSelectionFacade.ConvertTransformSelectionToFaceSelection(InTransformSelection.AsArray()); FDataflowFaceSelection NewFaceSelection; NewFaceSelection.InitializeFromCollection(InCollection, false); NewFaceSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(NewFaceSelection), &FaceSelection); } else { // Passthrough SafeForwardInput(Context, &FaceSelection, &FaceSelection); } } else if (Out->IsA(&VertexSelection)) { if (IsConnected(&FaceSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); const FDataflowFaceSelection& InFaceSelection = GetValue(Context, &FaceSelection); GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); const TArray& SelectionArr = TransformSelectionFacade.ConvertFaceSelectionToVertexSelection(InFaceSelection.AsArray()); FDataflowVertexSelection NewVertexSelection; NewVertexSelection.InitializeFromCollection(InCollection, false); NewVertexSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(NewVertexSelection), &VertexSelection); } else if (IsConnected(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); const FDataflowTransformSelection& InTransformSelection = GetValue(Context, &TransformSelection); GeometryCollection::Facades::FCollectionTransformSelectionFacade TransformSelectionFacade(InCollection); const TArray& SelectionArr = TransformSelectionFacade.ConvertTransformSelectionToVertexSelection(InTransformSelection.AsArray()); FDataflowVertexSelection NewVertexSelection; NewVertexSelection.InitializeFromCollection(InCollection, false); NewVertexSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(NewVertexSelection), &VertexSelection); } else { // Passthrough SafeForwardInput(Context, &VertexSelection, &VertexSelection); } } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } void FCollectionFaceSelectionInvertDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&FaceSelection)) { FDataflowFaceSelection InFaceSelection = GetValue(Context, &FaceSelection); InFaceSelection.Invert(); SetValue(Context, MoveTemp(InFaceSelection), &FaceSelection); } } void FCollectionVertexSelectionByPercentageDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&VertexSelection)) { FDataflowVertexSelection InVertexSelection = GetValue(Context, &VertexSelection); int32 InPercentage = GetValue(Context, &Percentage); float InRandomSeed = GetValue(Context, &RandomSeed); TArray SelectionArr = InVertexSelection.AsArray(); GeometryCollection::Facades::FCollectionTransformSelectionFacade::SelectByPercentage(SelectionArr, InPercentage, bDeterministic, InRandomSeed); InVertexSelection.SetFromArray(SelectionArr); SetValue(Context, MoveTemp(InVertexSelection), &VertexSelection); } } void FCollectionVertexSelectionSetOperationDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&VertexSelection)) { const FDataflowVertexSelection& InVertexSelectionA = GetValue(Context, &VertexSelectionA); const FDataflowVertexSelection& InVertexSelectionB = GetValue(Context, &VertexSelectionB); FDataflowVertexSelection NewVertexSelection; if (InVertexSelectionA.Num() == InVertexSelectionB.Num()) { if (Operation == ESetOperationEnum::Dataflow_SetOperation_AND) { InVertexSelectionA.AND(InVertexSelectionB, NewVertexSelection); } else if (Operation == ESetOperationEnum::Dataflow_SetOperation_OR) { InVertexSelectionA.OR(InVertexSelectionB, NewVertexSelection); } else if (Operation == ESetOperationEnum::Dataflow_SetOperation_XOR) { InVertexSelectionA.XOR(InVertexSelectionB, NewVertexSelection); } else if (Operation == ESetOperationEnum::Dataflow_SetOperation_Subtract) { InVertexSelectionA.Subtract(InVertexSelectionB, NewVertexSelection); } } else { // ERROR: INPUT TRANSFORMSELECTIONS HAVE DIFFERENT NUMBER OF ELEMENTS FString ErrorStr = "Input VertexSelections have different number of elements."; UE_LOG(LogTemp, Error, TEXT("[Dataflow ERROR] %s"), *ErrorStr); } SetValue(Context, MoveTemp(NewVertexSelection), &VertexSelection); } } static void CreateSelectionFromAttr(const FManagedArrayCollection& InCollection, const FName InGroup, const FName InAttribute, const FString InValue, const ESelectionByAttrOperation InOperation, FDataflowSelection& OutSelection) { const FManagedArrayCollection::EArrayType ArrayType = InCollection.GetAttributeType(InAttribute, InGroup); const int32 NumElements = InCollection.NumElements(InGroup); const bool bIsMinOrMaxOperation = (InOperation == ESelectionByAttrOperation::Maximum) || (InOperation == ESelectionByAttrOperation::Minimum); if (ArrayType == FManagedArrayCollection::EArrayType::FFloatType) { const TManagedArray* const Array = InCollection.FindAttributeTyped(InAttribute, InGroup); if (InValue.IsNumeric() || bIsMinOrMaxOperation) { if (InOperation == ESelectionByAttrOperation::Maximum) { float MaxValue = TNumericLimits::Lowest(); int32 MaxIndex = INDEX_NONE; for (int32 Idx = 0; Idx < NumElements; ++Idx) { const float ValueAtIdx = (*Array)[Idx]; if (ValueAtIdx >= MaxValue) { MaxValue = ValueAtIdx; MaxIndex = Idx; } } if (MaxIndex != INDEX_NONE) { OutSelection.SetSelected(MaxIndex); } } else if (InOperation == ESelectionByAttrOperation::Minimum) { float MinValue = TNumericLimits::Max(); int32 MinIndex = INDEX_NONE; for (int32 Idx = 0; Idx < NumElements; ++Idx) { const float ValueAtIdx = (*Array)[Idx]; if (ValueAtIdx <= MinValue) { MinValue = ValueAtIdx; MinIndex = Idx; } } if (MinIndex != INDEX_NONE) { OutSelection.SetSelected(MinIndex); } } else { float FloatValue = FCString::Atof(*InValue); for (int32 Idx = 0; Idx < NumElements; ++Idx) { if ((InOperation == ESelectionByAttrOperation::Equal && (*Array)[Idx] == FloatValue) || (InOperation == ESelectionByAttrOperation::NotEqual && (*Array)[Idx] != FloatValue) || (InOperation == ESelectionByAttrOperation::Greater && (*Array)[Idx] > FloatValue) || (InOperation == ESelectionByAttrOperation::GreaterOrEqual && (*Array)[Idx] >= FloatValue) || (InOperation == ESelectionByAttrOperation::Smaller && (*Array)[Idx] < FloatValue) || (InOperation == ESelectionByAttrOperation::SmallerOrEqual && (*Array)[Idx] <= FloatValue)) { OutSelection.SetSelected(Idx); } } } } else { // Error: Invalid Value specified return; } } else if (ArrayType == FManagedArrayCollection::EArrayType::FInt32Type) { const TManagedArray* const Array = InCollection.FindAttributeTyped(InAttribute, InGroup); if (InValue.IsNumeric() || bIsMinOrMaxOperation) { if (InOperation == ESelectionByAttrOperation::Maximum) { int32 MaxValue = TNumericLimits::Lowest(); int32 MaxIndex = INDEX_NONE; for (int32 Idx = 0; Idx < NumElements; ++Idx) { const int32 ValueAtIdx = (*Array)[Idx]; if (ValueAtIdx >= MaxValue) { MaxValue = ValueAtIdx; MaxIndex = Idx; } } if (MaxIndex != INDEX_NONE) { OutSelection.SetSelected(MaxIndex); } } else if (InOperation == ESelectionByAttrOperation::Minimum) { int32 MinValue = TNumericLimits::Max(); int32 MinIndex = INDEX_NONE; for (int32 Idx = 0; Idx < NumElements; ++Idx) { const int32 ValueAtIdx = (*Array)[Idx]; if (ValueAtIdx <= MinValue) { MinValue = ValueAtIdx; MinIndex = Idx; } } if (MinIndex != INDEX_NONE) { OutSelection.SetSelected(MinIndex); } } else { float IntValue = FCString::Atoi(*InValue); for (int32 Idx = 0; Idx < NumElements; ++Idx) { if ((InOperation == ESelectionByAttrOperation::Equal && (*Array)[Idx] == IntValue) || (InOperation == ESelectionByAttrOperation::NotEqual && (*Array)[Idx] != IntValue) || (InOperation == ESelectionByAttrOperation::Greater && (*Array)[Idx] > IntValue) || (InOperation == ESelectionByAttrOperation::GreaterOrEqual && (*Array)[Idx] >= IntValue) || (InOperation == ESelectionByAttrOperation::Smaller && (*Array)[Idx] < IntValue) || (InOperation == ESelectionByAttrOperation::SmallerOrEqual && (*Array)[Idx] <= IntValue)) { OutSelection.SetSelected(Idx); } } } } else { // Error: Invalid Value specified return; } } else if (ArrayType == FManagedArrayCollection::EArrayType::FStringType) { const TManagedArray* const Array = InCollection.FindAttributeTyped(InAttribute, InGroup); for (int32 Idx = 0; Idx < NumElements; ++Idx) { if ((InOperation == ESelectionByAttrOperation::Equal && (*Array)[Idx] == InValue) || (InOperation == ESelectionByAttrOperation::NotEqual && !((*Array)[Idx] == InValue))) { OutSelection.SetSelected(Idx); } } } else if (ArrayType == FManagedArrayCollection::EArrayType::FBoolType) { const TManagedArray* const Array = InCollection.FindAttributeTyped(InAttribute, InGroup); bool BoolValue = false; if (InValue.IsNumeric()) { float FloatValue = FCString::Atof(*InValue); if (FloatValue > 0.f) { BoolValue = true; } } else { if (InValue == FString("true") || InValue == FString("True")) { BoolValue = true; } } for (int32 Idx = 0; Idx < NumElements; ++Idx) { if ((InOperation == ESelectionByAttrOperation::Equal && (*Array)[Idx] == BoolValue) || (InOperation == ESelectionByAttrOperation::NotEqual && !((*Array)[Idx] == BoolValue))) { OutSelection.SetSelected(Idx); } } } } void FCollectionSelectionByAttrDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&VertexSelection) || Out->IsA(&FaceSelection) || Out->IsA(&TransformSelection) || Out->IsA(&GeometrySelection) || Out->IsA(&MaterialSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); FCollectionAttributeKey InAttributeKey = GetValue(Context, &AttributeKey); FName GroupName = UE::Dataflow::Private::GetAttributeFromEnumAsName(Group); FName AttributeName = FName(Attribute); if (IsConnected(&AttributeKey)) { GroupName = FName(InAttributeKey.Group); AttributeName = FName(InAttributeKey.Attribute); } if (InCollection.HasGroup(GroupName)) { if (InCollection.HasAttribute(AttributeName, GroupName)) { const int32 GroupSize = InCollection.NumElements(GroupName); FDataflowSelection NewGenericSelection; NewGenericSelection.Initialize(GroupSize, false); CreateSelectionFromAttr(InCollection, GroupName, AttributeName, Value, Operation, NewGenericSelection); using namespace UE::Dataflow::Private; FDataflowVertexSelection OutVertexSelection; if (GroupName == UE::Dataflow::Private::GetAttributeFromEnumAsName(ESelectionByAttrGroup::Vertices)) { OutVertexSelection.Initialize(NewGenericSelection); } SetValue(Context, MoveTemp(OutVertexSelection), &VertexSelection); FDataflowFaceSelection OutFaceSelection; if (GroupName == UE::Dataflow::Private::GetAttributeFromEnumAsName(ESelectionByAttrGroup::Faces)) { OutFaceSelection.Initialize(NewGenericSelection); } SetValue(Context, MoveTemp(OutFaceSelection), &FaceSelection); FDataflowTransformSelection OutTransformSelection; if (GroupName == UE::Dataflow::Private::GetAttributeFromEnumAsName(ESelectionByAttrGroup::Transform)) { OutTransformSelection.Initialize(NewGenericSelection); } SetValue(Context, MoveTemp(OutTransformSelection), &TransformSelection); FDataflowGeometrySelection OutGeometrySelection; if (GroupName == UE::Dataflow::Private::GetAttributeFromEnumAsName(ESelectionByAttrGroup::Geometry)) { OutGeometrySelection.Initialize(NewGenericSelection); } SetValue(Context, MoveTemp(OutGeometrySelection), &GeometrySelection); FDataflowMaterialSelection OutMaterialSelection; if (GroupName == UE::Dataflow::Private::GetAttributeFromEnumAsName(ESelectionByAttrGroup::Material)) { OutMaterialSelection.Initialize(NewGenericSelection); } SetValue(Context, MoveTemp(OutMaterialSelection), &MaterialSelection); FDataflowCurveSelection OutCurveSelection; if (GroupName == UE::Dataflow::Private::GetAttributeFromEnumAsName(ESelectionByAttrGroup::Curves)) { OutCurveSelection.Initialize(NewGenericSelection); } SetValue(Context, MoveTemp(OutCurveSelection), &CurveSelection); return; } } SetValue(Context, FDataflowVertexSelection(), &VertexSelection); SetValue(Context, FDataflowFaceSelection(), &FaceSelection); SetValue(Context, FDataflowTransformSelection(), &TransformSelection); SetValue(Context, FDataflowGeometrySelection(), &GeometrySelection); SetValue(Context, FDataflowMaterialSelection(), &MaterialSelection); SetValue(Context, FDataflowCurveSelection(), &CurveSelection); } else if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } } void FGeometrySelectionToVertexSelectionDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&VertexSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); const int32 NumGeometries = InCollection.NumElements(FGeometryCollection::GeometryGroup); FDataflowVertexSelection InVertexSelection; InVertexSelection.InitializeFromCollection(InCollection, false); const TManagedArray* VertexStart = InCollection.FindAttributeTyped("VertexStart", FGeometryCollection::GeometryGroup); const TManagedArray* VertexCount = InCollection.FindAttributeTyped("VertexCount", FGeometryCollection::GeometryGroup); TArray InGeometryIndexArray; if (IsConnected(&GeometrySelection)) { InGeometryIndexArray = GetValue(Context, &GeometrySelection).AsArray(); } else { TArray Indices; GeometryIndices.ParseIntoArray(Indices, TEXT(" "), true); for (FString IndexStr : Indices) { if (IndexStr.IsNumeric()) { int32 Index = FCString::Atoi(*IndexStr); if (Index >= 0 && Index < NumGeometries) { InGeometryIndexArray.Add(Index); } else { // ERROR: INVALID INDEX FString ErrorStr = "Invalid geometry index found."; UE_LOG(LogTemp, Error, TEXT("[Dataflow ERROR] %s"), *ErrorStr); } } } } if (VertexStart && VertexCount) { TArray VertexIndices; for (int32 GeometryIdx : InGeometryIndexArray) { if (ensure(VertexStart->IsValidIndex(GeometryIdx))) { const int32 Start = (*VertexStart)[GeometryIdx]; const int32 Count = (*VertexCount)[GeometryIdx]; for (int32 VertexIdx = Start; VertexIdx < Start + Count; ++VertexIdx) { VertexIndices.Add(VertexIdx); } } } InVertexSelection.SetFromArray(VertexIndices); } SetValue(Context, MoveTemp(InVertexSelection), &VertexSelection); } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FCollectionSelectionSetOperationDataflowNode::FCollectionSelectionSetOperationDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid) : FDataflowNode(InParam, InGuid) { static const FName TypeDependencyGroup("Main"); RegisterInputConnection(&SelectionA) .SetTypeDependencyGroup(TypeDependencyGroup); RegisterInputConnection(&SelectionB) .SetTypeDependencyGroup(TypeDependencyGroup); RegisterOutputConnection(&Selection, &SelectionA) .SetTypeDependencyGroup(TypeDependencyGroup); } void FCollectionSelectionSetOperationDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&Selection)) { const FDataflowSelection& InSelectionA = GetValue(Context, &SelectionA); const FDataflowSelection& InSelectionB = GetValue(Context, &SelectionB); ensure(FindInput(&SelectionA)->GetType() == FindInput(&SelectionB)->GetType()); ensure(FindOutput(&Selection)->GetType() == FindInput(&SelectionA)->GetType()); FDataflowSelection OutSelection; if (InSelectionA.Num() == InSelectionB.Num()) { switch (Operation) { case ESetOperationEnum::Dataflow_SetOperation_AND: InSelectionA.AND(InSelectionB, OutSelection); break; case ESetOperationEnum::Dataflow_SetOperation_OR: InSelectionA.OR(InSelectionB, OutSelection); break; case ESetOperationEnum::Dataflow_SetOperation_XOR: InSelectionA.XOR(InSelectionB, OutSelection); break; case ESetOperationEnum::Dataflow_SetOperation_Subtract: InSelectionA.Subtract(InSelectionB, OutSelection); break; } } else { UE_LOG(LogTemp, Error, TEXT("Dataflow: CollectionSelectionSetOperationDataflowNode : Input Selections have different number of elements.")); } SetValue(Context, MoveTemp(OutSelection), &Selection); } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FCollectionSelectionInvertDataflowNode::FCollectionSelectionInvertDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid) : FDataflowNode(InParam, InGuid) { static const FName TypeDependencyGroup("Main"); RegisterInputConnection(&Selection) .SetTypeDependencyGroup(TypeDependencyGroup); RegisterOutputConnection(&Selection, &Selection) .SetTypeDependencyGroup(TypeDependencyGroup); } void FCollectionSelectionInvertDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&Selection)) { ensure(FindOutput(&Selection)->GetType() == FindInput(&Selection)->GetType()); FDataflowSelection InSelection = GetValue(Context, &Selection); InSelection.Invert(); SetValue(Context, MoveTemp(InSelection), &Selection); } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FCollectionSelectInternalFacesDataflowNode::FCollectionSelectInternalFacesDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid) : FDataflowNode(InParam, InGuid) { RegisterInputConnection(&Collection); RegisterInputConnection(&TransformSelection); RegisterOutputConnection(&Collection, &Collection); RegisterOutputConnection(&FaceSelection); } void FCollectionSelectInternalFacesDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { static const FName InternalAttributeName(TEXT("Internal")); if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } else if (Out->IsA(&FaceSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); const FDataflowTransformSelection& InTransformSelection = GetValue(Context, &TransformSelection); struct FFaceRange { int32 Start; int32 Count; }; const TManagedArray* TransformIndexFromGeometry = InCollection.FindAttribute("TransformIndex", FGeometryCollection::GeometryGroup); const TManagedArray* FaceStart = InCollection.FindAttribute("FaceStart", FGeometryCollection::GeometryGroup); const TManagedArray* FaceCount = InCollection.FindAttribute("FaceCount", FGeometryCollection::GeometryGroup); const TManagedArray* InternalFaces = InCollection.FindAttribute(InternalAttributeName, FGeometryCollection::FacesGroup); const int32 TotalNumFaces = InCollection.NumElements(FGeometryCollection::FacesGroup); FDataflowFaceSelection OutFaceSelection; OutFaceSelection.InitializeFromCollection(InCollection, false); if (TransformIndexFromGeometry && FaceStart && FaceCount && InternalFaces) { TArray FaceRanges; if (IsConnected(&TransformSelection)) { for (int32 GeoIdx = 0; GeoIdx < TransformIndexFromGeometry->Num(); ++GeoIdx) { const int32 TransformIndex = (*TransformIndexFromGeometry)[GeoIdx]; if (InTransformSelection.IsSelected(TransformIndex)) { FaceRanges.Emplace( FFaceRange { .Start=(*FaceStart)[GeoIdx], .Count=(*FaceCount)[GeoIdx] }); } } } else { FaceRanges.Emplace( FFaceRange{ .Start = 0, .Count = TotalNumFaces }); } for (const FFaceRange& FaceRange : FaceRanges) { for (int32 Idx = 0; Idx < FaceRange.Count; ++Idx) { const int32 FaceIndex = (FaceRange.Start + Idx); if ((*InternalFaces)[FaceIndex]) { OutFaceSelection.SetSelected(FaceRange.Start + Idx); } } } } SetValue(Context, OutFaceSelection, &FaceSelection); } } ///////////////////////////////////////////////////////////////////////////////// FCollectionSelectTransformStringDataflowNode::FCollectionSelectTransformStringDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid) : FDataflowNode(InParam, InGuid) { RegisterInputConnection(&Collection); RegisterInputConnection(&SearchText); RegisterInputConnection(&Attribute).SetCanHidePin(true).SetPinIsHidden(true); RegisterOutputConnection(&Collection, &Collection); RegisterOutputConnection(&TransformSelection); } void FCollectionSelectTransformStringDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&Collection)) { SafeForwardInput(Context, &Collection, &Collection); } else if (Out->IsA(&TransformSelection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); const FString& InSearchText = GetValue(Context, &SearchText); const FString& InAttributeName = GetValue(Context, &Attribute); FDataflowTransformSelection OutSelection; OutSelection.InitFromArray(InCollection, {}); const TManagedArray* StringAttribute = InCollection.FindAttributeTyped(FName(InAttributeName), FTransformCollection::TransformGroup); if (StringAttribute) { const int32 Num = StringAttribute->Num(); for (int32 Index = 0; Index < Num; Index++) { const FString& Value = (*StringAttribute)[Index]; bool bSelect = false; switch (Method) { case EDataflowCollectionSelectionByNameMethod::Exact: bSelect = (Value == InSearchText); break; case EDataflowCollectionSelectionByNameMethod::StartsWith: bSelect = Value.StartsWith(InSearchText, ESearchCase::CaseSensitive); break; case EDataflowCollectionSelectionByNameMethod::EndsWith: bSelect = Value.EndsWith(InSearchText, ESearchCase::CaseSensitive); break; case EDataflowCollectionSelectionByNameMethod::Contains: bSelect = Value.Contains(InSearchText, ESearchCase::CaseSensitive); break; } if (bSelect) { OutSelection.SetSelected(Index); } else { OutSelection.SetNotSelected(Index); } } } SetValue(Context, OutSelection, &TransformSelection); } } ////////////////////////////////////////////////////////////////////////////////////// FCollectionSetTransformStringValueDataflowNode::FCollectionSetTransformStringValueDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid) : FDataflowNode(InParam, InGuid) { RegisterInputConnection(&Collection); RegisterInputConnection(&TextToSet); RegisterInputConnection(&TransformSelection); RegisterInputConnection(&Attribute).SetCanHidePin(true).SetPinIsHidden(true); RegisterOutputConnection(&Collection, &Collection); } void FCollectionSetTransformStringValueDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&Collection)) { FManagedArrayCollection CollectionToUpdate = GetValue(Context, &Collection); FString InTextToSet = GetValue(Context, &TextToSet); const FString& InAttributeName = GetValue(Context, &Attribute); FDataflowTransformSelection InSelection = GetValue(Context, &TransformSelection); if (!IsConnected(&TransformSelection)) { InSelection.InitFromArray(CollectionToUpdate, {}); InSelection.Invert(); } const FString StringFormat = InTextToSet .Replace(TEXT("{Current}"), TEXT("{0}")) .Replace(TEXT("{Index}"), TEXT("{1}")); // make sure we have the right format // todo : add the ability to choose the attribute ? TManagedArray* StringAttribute = CollectionToUpdate.FindAttributeTyped(FName(InAttributeName), FTransformCollection::TransformGroup); if (StringAttribute) { const int32 Num = StringAttribute->Num(); int32 SelectionIndex = 0; for (int32 Index = 0; Index < Num; Index++) { if (InSelection.IsSelected(Index)) { const FString& CurrentValue = (*StringAttribute)[Index]; (*StringAttribute)[Index] = FString::Format(*StringFormat, { CurrentValue, SelectionIndex }); SelectionIndex++; } } } SetValue(Context, MoveTemp(CollectionToUpdate), &Collection); } }