Files
2025-05-18 13:04:45 +08:00

389 lines
15 KiB
C++

// 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<FManagedArrayCollection>(&Collection))
{
const FManagedArrayCollection& InCollection = GetValue<FManagedArrayCollection>(Context, &Collection);
const FDataflowTransformSelection& InTransformSelection = GetValue<FDataflowTransformSelection>(Context, &TransformSelection);
if (TUniquePtr<FGeometryCollection> GeomCollection = TUniquePtr<FGeometryCollection>(InCollection.NewCopy<FGeometryCollection>()))
{
Chaos::Facades::FCollectionHierarchyFacade HierarchyFacade(*GeomCollection);
HierarchyFacade.GenerateLevelAttribute();
EClusterSizeMethodEnum InClusterSizeMethod = ClusterSizeMethod;
int32 InClusterSites = GetValue<int32>(Context, &ClusterSites);
float InClusterFraction = GetValue<float>(Context, &ClusterFraction);
float InSiteSize = GetValue<float>(Context, &SiteSize);
bool InAutoCluster = AutoCluster;
bool InEnforceSiteParameters = EnforceSiteParameters;
bool InAvoidIsolated = AvoidIsolated;
int32 InGridX = GetValue<int32>(Context, &ClusterGridWidth);
int32 InGridY = GetValue<int32>(Context, &ClusterGridDepth);
int32 InGridZ = GetValue<int32>(Context, &ClusterGridHeight);
float InMinimumClusterSize = GetValue<float>(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<int32> 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<const FManagedArrayCollection&>(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<FGeometryCollection> GeomCollection = TUniquePtr<FGeometryCollection>(InCollection.NewCopy<FGeometryCollection>()))
{
Chaos::Facades::FCollectionHierarchyFacade HierarchyFacade(*GeomCollection);
HierarchyFacade.GenerateLevelAttribute();
TArray<int32> 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<int32> 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<FManagedArrayCollection>(&Collection))
{
const FManagedArrayCollection& InCollection = GetValue<FManagedArrayCollection>(Context, &Collection);
if (InCollection.NumElements(FTransformCollection::TransformAttribute) > 0)
{
const FDataflowTransformSelection& InTransformSelection = GetValue<FDataflowTransformSelection>(Context, &TransformSelection);
if (TUniquePtr<FGeometryCollection> GeomCollection = TUniquePtr<FGeometryCollection>(InCollection.NewCopy<FGeometryCollection>()))
{
Chaos::Facades::FCollectionHierarchyFacade HierarchyFacade(*GeomCollection);
HierarchyFacade.GenerateLevelAttribute();
TArray<int32> 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<const FManagedArrayCollection&>(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<FGeometryCollection> GeomCollection = TUniquePtr<FGeometryCollection>(InCollection.NewCopy<FGeometryCollection>()))
{
TArray<int32> 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<FGeometryCollection> GeomCollection = TUniquePtr<FGeometryCollection>(InCollection.NewCopy<FGeometryCollection>()))
{
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<int32> Selection = InTransformSelection.AsArray();
TArray<double> Volumes;
FindBoneVolumes(
*GeomCollection,
TArrayView<const int32>(),
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<FGeometryCollection> GeomCollection = TUniquePtr<FGeometryCollection>(InCollection.NewCopy<FGeometryCollection>()))
{
Chaos::Facades::FCollectionHierarchyFacade HierarchyFacade(*GeomCollection);
HierarchyFacade.GenerateLevelAttribute();
TArray<int32> 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<FGeometryCollection> GeomCollection = TUniquePtr<FGeometryCollection>(InCollection.NewCopy<FGeometryCollection>()))
{
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<FGeometryCollection> GeomCollection = TUniquePtr<FGeometryCollection>(InCollection.NewCopy<FGeometryCollection>()))
{
FDataflowTransformSelection InTransformSelection = GetValue(Context, &TransformSelection);
int32 InIterations = FMath::Max(1, GetValue(Context, &Iterations));
TArray<int32> InSelection = InTransformSelection.AsArray();
if (FFractureEngineClustering::ClusterMagnet(*GeomCollection, InSelection, InIterations))
{
SetValue(Context, (const FManagedArrayCollection&)(*GeomCollection), &Collection);
return;
}
}
SetValue(Context, InCollection, &Collection);
}
}