// Copyright Epic Games, Inc. All Rights Reserved. #include "Dataflow/GeometryCollectionClusteringNodes.h" #include "Dataflow/DataflowCore.h" #include "ChaosLog.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 "GeometryCollection/Facades/CollectionTransformSelectionFacade.h" #include "GeometryCollection/Facades/CollectionHierarchyFacade.h" #include "Voronoi/Voronoi.h" #include "PlanarCut.h" #include "GeometryCollection/GeometryCollectionProximityUtility.h" #include "FractureEngineClustering.h" #include "FractureEngineSelection.h" #include "Dataflow/DataflowDebugDrawInterface.h" #include "Dataflow/DataflowDebugDraw.h" #if WITH_EDITOR #include "Dataflow/DataflowRenderingViewMode.h" #endif #include "Dataflow/DataflowEngineUtil.h" #include "Dataflow/GeometryCollectionUtils.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(GeometryCollectionClusteringNodes) namespace UE::Dataflow { void GeometryCollectionClusteringNodes() { DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FAutoClusterDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FClusterFlattenDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FClusterUnclusterDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FClusterDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FClusterMergeToNeighborsDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FClusterMergeDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FClusterIsolatedRootsDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FClusterMagnetDataflowNode); } } FAutoClusterDataflowNode::FAutoClusterDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid) : FDataflowNode(InParam, InGuid) { RegisterInputConnection(&Collection); RegisterInputConnection(&TransformSelection); RegisterInputConnection(&ClusterSites) .SetCanHidePin(true) .SetPinIsHidden(true); RegisterInputConnection(&ClusterFraction) .SetCanHidePin(true) .SetPinIsHidden(true); RegisterInputConnection(&SiteSize) .SetCanHidePin(true) .SetPinIsHidden(true); RegisterInputConnection(&ClusterGridWidth) .SetCanHidePin(true) .SetPinIsHidden(true); RegisterInputConnection(&ClusterGridDepth) .SetCanHidePin(true) .SetPinIsHidden(true); RegisterInputConnection(&ClusterGridHeight) .SetCanHidePin(true) .SetPinIsHidden(true); RegisterInputConnection(&MinimumSize) .SetCanHidePin(true) .SetPinIsHidden(true); RegisterInputConnection(&bPreferConvexity) .SetCanHidePin(true) .SetPinIsHidden(true); RegisterInputConnection(&ConcavityTolerance) .SetCanHidePin(true) .SetPinIsHidden(true); RegisterOutputConnection(&Collection, &Collection); } /* ---------------------------------------------------------------------------------------------------------------------------------*/ void FAutoClusterDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&Collection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); const FDataflowTransformSelection& InTransformSelection = GetValue(Context, &TransformSelection); if (TUniquePtr GeomCollection = TUniquePtr(InCollection.NewCopy())) { Chaos::Facades::FCollectionHierarchyFacade HierarchyFacade(*GeomCollection); HierarchyFacade.GenerateLevelAttribute(); EClusterSizeMethodEnum InClusterSizeMethod = ClusterSizeMethod; int32 InClusterSites = GetValue(Context, &ClusterSites); float InClusterFraction = GetValue(Context, &ClusterFraction); float InSiteSize = GetValue(Context, &SiteSize); bool InAutoCluster = AutoCluster; bool InEnforceSiteParameters = EnforceSiteParameters; bool InAvoidIsolated = AvoidIsolated; int32 InGridX = GetValue(Context, &ClusterGridWidth); int32 InGridY = GetValue(Context, &ClusterGridDepth); int32 InGridZ = GetValue(Context, &ClusterGridHeight); float InMinimumClusterSize = GetValue(Context, &MinimumSize); int32 InKMeansIterations = ClusterSizeMethod == EClusterSizeMethodEnum::Dataflow_ClusterSizeMethod_ByGrid ? DriftIterations : 500; bool bInPreferConvexity = GetValue(Context, &bPreferConvexity); float InConcavityTolerance = GetValue(Context, &ConcavityTolerance); // only cluster if the selection matches if (InTransformSelection.Num() == GeomCollection->NumElements(FTransformCollection::TransformGroup)) { TArray SelectedBones; InTransformSelection.AsArray(SelectedBones); FFractureEngineClustering::AutoCluster(*GeomCollection, SelectedBones, (EFractureEngineClusterSizeMethod)InClusterSizeMethod, InClusterSites, InClusterFraction, InSiteSize, InAutoCluster, InAvoidIsolated, InEnforceSiteParameters, InGridX, InGridY, InGridZ, InMinimumClusterSize, InKMeansIterations, bInPreferConvexity, InConcavityTolerance); FGeometryCollectionProximityUtility ProximityUtility(GeomCollection.Get()); ProximityUtility.UpdateProximity(); } else { UE_LOG(LogChaos, Warning, TEXT("Dataflow: AutoCluster Node input selection size does not match the collection size, skipping clustering")); } SetValue(Context, *GeomCollection, &Collection); } } } #if WITH_EDITOR bool FAutoClusterDataflowNode::CanDebugDrawViewMode(const FName& ViewModeName) const { return ViewModeName == UE::Dataflow::FDataflowConstruction3DViewMode::Name; } void FAutoClusterDataflowNode::DebugDraw(UE::Dataflow::FContext& Context, IDataflowDebugDrawInterface& DataflowRenderingInterface, const FDebugDrawParameters& DebugDrawParameters) const { using namespace UE::Geometry; if ((DebugDrawParameters.bNodeIsSelected || DebugDrawParameters.bNodeIsPinned)) { if (const FDataflowOutput* Output = FindOutput(&Collection)) { const FManagedArrayCollection& OutCollection = Output->GetValue(Context, Collection); UE::Dataflow::Utils::DebugDrawProximity(DataflowRenderingInterface, OutCollection, Color, LineWidthMultiplier, CenterSize, CenterColor, bRandomizeColor, ColorRandomSeed); } } } #endif /* ---------------------------------------------------------------------------------------------------------------------------------*/ void FClusterFlattenDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&Collection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); if (InCollection.NumElements(FTransformCollection::TransformAttribute) > 0) { if (TUniquePtr GeomCollection = TUniquePtr(InCollection.NewCopy())) { Chaos::Facades::FCollectionHierarchyFacade HierarchyFacade(*GeomCollection); HierarchyFacade.GenerateLevelAttribute(); TArray ToFlatten; if (IsConnected(&OptionalTransformSelection)) { const FDataflowTransformSelection& InTransformSelection = GetValue(Context, &OptionalTransformSelection); ToFlatten = InTransformSelection.AsArray(); GeometryCollection::Facades::FCollectionTransformSelectionFacade SelectionFacade(*GeomCollection); SelectionFacade.Sanitize(ToFlatten); SelectionFacade.FilterSelectionBySimulationType(ToFlatten, FGeometryCollection::FST_Clustered); } else { const int32 RootClusterIndex = HierarchyFacade.GetRootIndex(); ToFlatten.Add(RootClusterIndex); } for (int32 ToFlattenIdx : ToFlatten) { TArray LeafBones; FGeometryCollectionClusteringUtility::GetLeafBones(GeomCollection.Get(), ToFlattenIdx, true, LeafBones); FGeometryCollectionClusteringUtility::ClusterBonesUnderExistingNode(GeomCollection.Get(), ToFlattenIdx, LeafBones); } FGeometryCollectionClusteringUtility::RemoveDanglingClusters(GeomCollection.Get()); HierarchyFacade.GenerateLevelAttribute(); SetValue(Context, (const FManagedArrayCollection&)(*GeomCollection), &Collection); } } else { SetValue(Context, InCollection, &Collection); } } } void FClusterUnclusterDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&Collection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); if (InCollection.NumElements(FTransformCollection::TransformAttribute) > 0) { const FDataflowTransformSelection& InTransformSelection = GetValue(Context, &TransformSelection); if (TUniquePtr GeomCollection = TUniquePtr(InCollection.NewCopy())) { Chaos::Facades::FCollectionHierarchyFacade HierarchyFacade(*GeomCollection); HierarchyFacade.GenerateLevelAttribute(); TArray Selection = InTransformSelection.AsArray(); GeometryCollection::Facades::FCollectionTransformSelectionFacade SelectionFacade(*GeomCollection); SelectionFacade.ConvertSelectionToClusterNodes(Selection, false); SelectionFacade.RemoveRootNodes(Selection); if (!Selection.IsEmpty()) { FGeometryCollectionClusteringUtility::CollapseHierarchyOneLevel(GeomCollection.Get(), Selection); FGeometryCollectionClusteringUtility::RemoveDanglingClusters(GeomCollection.Get()); HierarchyFacade.GenerateLevelAttribute(); } SetValue(Context, *GeomCollection, &Collection); } } else { SetValue(Context, InCollection, &Collection); } } } void FClusterDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&Collection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); const FDataflowTransformSelection& InTransformSelection = GetValue(Context, &TransformSelection); if (TUniquePtr GeomCollection = TUniquePtr(InCollection.NewCopy())) { TArray Selection = InTransformSelection.AsArray(); FFractureEngineClustering::ClusterSelected(*GeomCollection, Selection); SetValue(Context, (const FManagedArrayCollection&)(*GeomCollection), &Collection); } } } void FClusterMergeToNeighborsDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&Collection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); const FDataflowTransformSelection& InTransformSelection = GetValue(Context, &TransformSelection); if (InTransformSelection.NumSelected() == 0) { SetValue(Context, InCollection, &Collection); return; } if (TUniquePtr GeomCollection = TUniquePtr(InCollection.NewCopy())) { Chaos::Facades::FCollectionHierarchyFacade HierarchyFacade(*GeomCollection); HierarchyFacade.GenerateLevelAttribute(); double InMinVolumeCubeRoot = (double)GetValue(Context, &MinVolumeCubeRoot); double InMinVolume = InMinVolumeCubeRoot * InMinVolumeCubeRoot * InMinVolumeCubeRoot; bool bInOnlyToConnected = GetValue(Context, &bOnlyToConnected); bool bInOnlySameParent = GetValue(Context, &bOnlySameParent); UE::PlanarCut::ENeighborSelectionMethod InNeighborSelectionMethod = NeighborSelectionMethod == EClusterNeighborSelectionMethodEnum::Dataflow_ClusterNeighborSelectionMethod_LargestNeighbor ? UE::PlanarCut::ENeighborSelectionMethod::LargestNeighbor : UE::PlanarCut::ENeighborSelectionMethod::NearestCenter; TArray Selection = InTransformSelection.AsArray(); TArray Volumes; FindBoneVolumes( *GeomCollection, TArrayView(), Volumes, 1.0, true); MergeClusters( *GeomCollection, Volumes, InMinVolume, Selection, InNeighborSelectionMethod, bInOnlyToConnected, bInOnlySameParent ); SetValue(Context, (const FManagedArrayCollection&)(*GeomCollection), &Collection); return; } SetValue(Context, InCollection, &Collection); } } void FClusterMergeDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&Collection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); const FDataflowTransformSelection& InTransformSelection = GetValue(Context, &TransformSelection); if (TUniquePtr GeomCollection = TUniquePtr(InCollection.NewCopy())) { Chaos::Facades::FCollectionHierarchyFacade HierarchyFacade(*GeomCollection); HierarchyFacade.GenerateLevelAttribute(); TArray Selection = InTransformSelection.AsArray(); FFractureEngineClustering::MergeSelectedClusters(*GeomCollection, Selection); SetValue(Context, (const FManagedArrayCollection&)(*GeomCollection), &Collection); } } } void FClusterIsolatedRootsDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&Collection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); int32 NumTransforms = InCollection.NumElements(FGeometryCollection::TransformGroup); // Only if there is a single transform, re-parent it under a new transform if (NumTransforms == 1) { if (TUniquePtr GeomCollection = TUniquePtr(InCollection.NewCopy())) { FGeometryCollectionClusteringUtility::ClusterAllBonesUnderNewRoot(GeomCollection.Get()); SetValue(Context, (const FManagedArrayCollection&)(*GeomCollection), &Collection); return; } } SetValue(Context, InCollection, &Collection); } } void FClusterMagnetDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&Collection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); if (TUniquePtr GeomCollection = TUniquePtr(InCollection.NewCopy())) { FDataflowTransformSelection InTransformSelection = GetValue(Context, &TransformSelection); int32 InIterations = FMath::Max(1, GetValue(Context, &Iterations)); TArray InSelection = InTransformSelection.AsArray(); if (FFractureEngineClustering::ClusterMagnet(*GeomCollection, InSelection, InIterations)) { SetValue(Context, (const FManagedArrayCollection&)(*GeomCollection), &Collection); return; } } SetValue(Context, InCollection, &Collection); } }