403 lines
16 KiB
C++
403 lines
16 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "Dataflow/DataflowEngine.h"
|
|
#include "GeometryCollection/ManagedArrayCollection.h"
|
|
#include "Dataflow/DataflowSelection.h"
|
|
|
|
#include "GeometryCollectionClusteringNodes.generated.h"
|
|
|
|
#if UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_5
|
|
namespace Dataflow = UE::Dataflow;
|
|
#else
|
|
namespace UE_DEPRECATED(5.5, "Use UE::Dataflow instead.") Dataflow {}
|
|
#endif
|
|
|
|
class FGeometryCollection;
|
|
|
|
|
|
UENUM(BlueprintType)
|
|
enum class EClusterSizeMethodEnum : uint8
|
|
{
|
|
Dataflow_ClusterSizeMethod_ByNumber UMETA(DisplayName = "By Number"),
|
|
Dataflow_ClusterSizeMethod_ByFractionOfInput UMETA(DisplayName = "By Fraction Of Input"),
|
|
Dataflow_ClusterSizeMethod_BySize UMETA(DisplayName = "By Size"),
|
|
Dataflow_ClusterSizeMethod_ByGrid UMETA(DisplayName = "By Grid"),
|
|
//~~~
|
|
//256th entry
|
|
Dataflow_Max UMETA(Hidden)
|
|
};
|
|
|
|
/**
|
|
*
|
|
* Automatically group pieces of a fractured Collection into a specified number of clusters
|
|
*
|
|
*/
|
|
USTRUCT(meta = (DataflowGeometryCollection))
|
|
struct FAutoClusterDataflowNode : public FDataflowNode
|
|
{
|
|
GENERATED_USTRUCT_BODY()
|
|
DATAFLOW_NODE_DEFINE_INTERNAL(FAutoClusterDataflowNode, "AutoCluster", "GeometryCollection|Cluster", "")
|
|
DATAFLOW_NODE_RENDER_TYPE("SurfaceRender", FGeometryCollection::StaticType(), "Collection")
|
|
|
|
private:
|
|
/** How to choose the size of the clusters to create */
|
|
UPROPERTY(EditAnywhere, Category = ClusterSize);
|
|
EClusterSizeMethodEnum ClusterSizeMethod = EClusterSizeMethodEnum::Dataflow_ClusterSizeMethod_ByNumber;
|
|
|
|
/** Use a Voronoi diagram with this many Voronoi sites as a guide for deciding cluster boundaries */
|
|
UPROPERTY(EditAnywhere, Category = ClusterSize, meta = (DataflowInput, UIMin = 2, UIMax = 5000, EditCondition = "ClusterSizeMethod == EClusterSizeMethodEnum::Dataflow_ClusterSizeMethod_ByNumber"))
|
|
int32 ClusterSites = 10;
|
|
|
|
/** Choose the number of Voronoi sites used for clustering as a fraction of the number of child bones to process */
|
|
UPROPERTY(EditAnywhere, Category = ClusterSize, meta = (DataflowInput, UIMin = 0.f, UIMax = 0.5f, EditCondition = "ClusterSizeMethod == EClusterSizeMethodEnum::Dataflow_ClusterSizeMethod_ByFractionOfInput"))
|
|
float ClusterFraction = 0.25;
|
|
|
|
/** Choose the Edge-Size of the cube used to groups bones under a cluster (in cm). */
|
|
UPROPERTY(EditAnywhere, Category = ClusterSize, meta = (DataflowInput, DisplayName = "Cluster Size", UIMin = ".01", UIMax = "100", ClampMin = ".0001", ClampMax = "10000", EditCondition = "ClusterSizeMethod == EClusterSizeMethodEnum::Dataflow_ClusterSizeMethod_BySize"))
|
|
float SiteSize = 1;
|
|
|
|
/** Choose the number of cluster sites to distribute along the X axis */
|
|
UPROPERTY(EditAnywhere, Category = ClusterSize, meta = (DataflowInput, ClampMin = "1", UIMax = "20", EditCondition = "ClusterSizeMethod == EClusterSizeMethodEnum::Dataflow_ClusterSizeMethod_ByGrid"))
|
|
int ClusterGridWidth = 2;
|
|
|
|
/** Choose the number of cluster sites to distribute along the Y axis */
|
|
UPROPERTY(EditAnywhere, Category = ClusterSize, meta = (DataflowInput, ClampMin = "1", UIMax = "20", EditCondition = "ClusterSizeMethod == EClusterSizeMethodEnum::Dataflow_ClusterSizeMethod_ByGrid"))
|
|
int ClusterGridDepth = 2;
|
|
|
|
/** Choose the number of cluster sites to distribute along the Z axis */
|
|
UPROPERTY(EditAnywhere, Category = ClusterSize, meta = (DataflowInput, ClampMin = "1", UIMax = "20", EditCondition = "ClusterSizeMethod == EClusterSizeMethodEnum::Dataflow_ClusterSizeMethod_ByGrid"))
|
|
int ClusterGridHeight = 2;
|
|
|
|
/** For a grid distribution, optionally iteratively recenter the grid points to the center of the cluster geometry (technically: applying K-Means iterations) to balance the shape and distribution of the clusters */
|
|
UPROPERTY(EditAnywhere, Category = ClusterSize, meta = (DisplayName = "Grid Drift Iterations", ClampMin = "0", UIMax = "5", EditCondition = "ClusterSizeMethod == EClusterSizeMethodEnum::Dataflow_ClusterSizeMethod_ByGrid"))
|
|
int DriftIterations = 0;
|
|
|
|
/** If a cluster has volume less than this value (in cm) cubed, then the auto-cluster process will attempt to merge it into a neighboring cluster. */
|
|
UPROPERTY(EditAnywhere, Category = ClusterSize, meta = (DataflowInput, ClampMin = "0"))
|
|
float MinimumSize = 0;
|
|
|
|
/** Whether to favor clusters that have a convex shape. (Note: Does not support ByGrid clustering.) */
|
|
UPROPERTY(EditAnywhere, Category = AutoCluster, meta = (EditCondition = "ClusterSizeMethod != EClusterSizeMethodEnum::Dataflow_ClusterSizeMethod_ByGrid"))
|
|
bool bPreferConvexity = false;
|
|
|
|
/** If > 0, cube root of maximum concave volume to add per cluster (ignoring concavity of individual parts) */
|
|
UPROPERTY(EditAnywhere, Category = AutoCluster, meta = (EditCondition = "bPreferConvexity && ClusterSizeMethod != EClusterSizeMethodEnum::Dataflow_ClusterSizeMethod_ByGrid"))
|
|
float ConcavityTolerance = 0;
|
|
|
|
/** If true, bones will only be added to the same cluster if they are physically connected (either directly, or via other bones in the same cluster) */
|
|
UPROPERTY(EditAnywhere, Category = AutoCluster, meta = (DisplayName = "Enforce Cluster Connectivity"))
|
|
bool AutoCluster = true;
|
|
|
|
/** If true, make sure the site parameters are matched as close as possible ( bEnforceConnectivity can make the number of site larger than the requested input may produce without it ) */
|
|
UPROPERTY(EditAnywhere, Category = AutoCluster, meta = (EditCondition = "AutoCluster == true"))
|
|
bool EnforceSiteParameters = true;
|
|
|
|
/** If true, prevent the creation of clusters with only a single child. Either by merging into a neighboring cluster, or not creating the cluster. */
|
|
UPROPERTY(EditAnywhere, Category = AutoCluster)
|
|
bool AvoidIsolated = true;
|
|
|
|
/** Fractured GeometryCollection to cluster */
|
|
UPROPERTY(meta = (DataflowInput, DataflowOutput, DataflowPassthrough = "Collection", DataflowIntrinsic))
|
|
FManagedArrayCollection Collection;
|
|
|
|
/** Bone selection for the clustering */
|
|
UPROPERTY(meta = (DataflowInput, DisplayName = "TransformSelection", DataflowIntrinsic))
|
|
FDataflowTransformSelection TransformSelection;
|
|
|
|
UPROPERTY(EditAnywhere, Category = "Debug Draw");
|
|
FLinearColor Color = FLinearColor::Yellow;
|
|
|
|
UPROPERTY(EditAnywhere, Category = "Debug Draw", meta = (ClampMin = "0.1", ClampMax = "10.0"));
|
|
float LineWidthMultiplier = 2.f;
|
|
|
|
UPROPERTY(EditAnywhere, Category = "Debug Draw - Sphere Covering");
|
|
FLinearColor CenterColor = FLinearColor::Blue;
|
|
|
|
UPROPERTY(EditAnywhere, Category = "Debug Draw - Sphere Covering");
|
|
float CenterSize = 12.f;
|
|
|
|
/** Randomize color per connection */
|
|
UPROPERTY(EditAnywhere, Category = "Debug Draw")
|
|
bool bRandomizeColor = false;
|
|
|
|
/** Random seed */
|
|
UPROPERTY(EditAnywhere, Category = "Debug Draw", meta = (ClampMin = "0", EditCondition = "bRandomizeColor==true", EditConditionHides))
|
|
int32 ColorRandomSeed = 0;
|
|
|
|
virtual void Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const override;
|
|
|
|
#if WITH_EDITOR
|
|
virtual bool CanDebugDraw() const override
|
|
{
|
|
return true;
|
|
}
|
|
virtual bool CanDebugDrawViewMode(const FName& ViewModeName) const override;
|
|
virtual void DebugDraw(UE::Dataflow::FContext& Context, IDataflowDebugDrawInterface& DataflowRenderingInterface, const FDebugDrawParameters& DebugDrawParameters) const override;
|
|
#endif
|
|
public:
|
|
FAutoClusterDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid = FGuid::NewGuid());
|
|
};
|
|
|
|
|
|
/**
|
|
*
|
|
* Flattens selected bones. If no selection is provided, flattens all bones to level 1
|
|
*
|
|
*/
|
|
USTRUCT(meta = (DataflowGeometryCollection))
|
|
struct FClusterFlattenDataflowNode : public FDataflowNode
|
|
{
|
|
GENERATED_USTRUCT_BODY()
|
|
DATAFLOW_NODE_DEFINE_INTERNAL(FClusterFlattenDataflowNode, "Flatten", "GeometryCollection|Cluster", "")
|
|
|
|
public:
|
|
|
|
/** Fractured GeometryCollection to flatten */
|
|
UPROPERTY(meta = (DataflowInput, DataflowOutput, DataflowPassthrough = "Collection", DataflowIntrinsic))
|
|
FManagedArrayCollection Collection;
|
|
|
|
/** If connected, clusters under the selected bones will be flattened. If no selection is provided, all bones will be flattened to level 1. */
|
|
UPROPERTY(meta = (DataflowInput))
|
|
FDataflowTransformSelection OptionalTransformSelection;
|
|
|
|
FClusterFlattenDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid = FGuid::NewGuid())
|
|
: FDataflowNode(InParam, InGuid)
|
|
{
|
|
RegisterInputConnection(&Collection);
|
|
RegisterInputConnection(&OptionalTransformSelection);
|
|
|
|
RegisterOutputConnection(&Collection, &Collection);
|
|
}
|
|
|
|
virtual void Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const override;
|
|
|
|
};
|
|
|
|
/**
|
|
* Uncluster selected nodes
|
|
*/
|
|
USTRUCT(meta = (DataflowGeometryCollection))
|
|
struct FClusterUnclusterDataflowNode : public FDataflowNode
|
|
{
|
|
GENERATED_USTRUCT_BODY()
|
|
DATAFLOW_NODE_DEFINE_INTERNAL(FClusterUnclusterDataflowNode, "Uncluster", "GeometryCollection|Cluster", "")
|
|
|
|
public:
|
|
|
|
/** Fractured GeometryCollection to uncluster */
|
|
UPROPERTY(meta = (DataflowInput, DataflowOutput, DataflowPassthrough = "Collection", DataflowIntrinsic))
|
|
FManagedArrayCollection Collection;
|
|
|
|
/** Bone selection */
|
|
UPROPERTY(meta = (DataflowInput, DisplayName = "TransformSelection"))
|
|
FDataflowTransformSelection TransformSelection;
|
|
|
|
FClusterUnclusterDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid = FGuid::NewGuid())
|
|
: FDataflowNode(InParam, InGuid)
|
|
{
|
|
RegisterInputConnection(&Collection);
|
|
RegisterInputConnection(&TransformSelection);
|
|
RegisterOutputConnection(&Collection, &Collection);
|
|
}
|
|
|
|
virtual void Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const override;
|
|
|
|
};
|
|
|
|
/**
|
|
* Cluster selected nodes under a new parent
|
|
*/
|
|
USTRUCT(meta = (DataflowGeometryCollection))
|
|
struct FClusterDataflowNode : public FDataflowNode
|
|
{
|
|
GENERATED_USTRUCT_BODY()
|
|
DATAFLOW_NODE_DEFINE_INTERNAL(FClusterDataflowNode, "Cluster", "GeometryCollection|Cluster", "")
|
|
|
|
public:
|
|
|
|
/** Collection on which to cluster nodes */
|
|
UPROPERTY(meta = (DataflowInput, DataflowOutput, DataflowPassthrough = "Collection", DataflowIntrinsic))
|
|
FManagedArrayCollection Collection;
|
|
|
|
/** Bone selection */
|
|
UPROPERTY(meta = (DataflowInput, DisplayName = "TransformSelection"))
|
|
FDataflowTransformSelection TransformSelection;
|
|
|
|
FClusterDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid = FGuid::NewGuid())
|
|
: FDataflowNode(InParam, InGuid)
|
|
{
|
|
RegisterInputConnection(&Collection);
|
|
RegisterInputConnection(&TransformSelection);
|
|
RegisterOutputConnection(&Collection, &Collection);
|
|
}
|
|
|
|
virtual void Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const override;
|
|
|
|
};
|
|
|
|
|
|
|
|
UENUM(BlueprintType)
|
|
enum class EClusterNeighborSelectionMethodEnum : uint8
|
|
{
|
|
Dataflow_ClusterNeighborSelectionMethod_LargestNeighbor UMETA(DisplayName = "Largest Neighbor"),
|
|
Dataflow_ClusterNeighborSelectionMethod_NearestCenter UMETA(DisplayName = "Nearest Center")
|
|
};
|
|
|
|
/**
|
|
* Merge selected bones to their neighbors
|
|
*/
|
|
USTRUCT(meta = (DataflowGeometryCollection))
|
|
struct FClusterMergeToNeighborsDataflowNode : public FDataflowNode
|
|
{
|
|
GENERATED_USTRUCT_BODY()
|
|
DATAFLOW_NODE_DEFINE_INTERNAL(FClusterMergeToNeighborsDataflowNode, "ClusterMergeToNeighbors", "GeometryCollection|Cluster", "")
|
|
|
|
public:
|
|
|
|
/** Collection on which to merge bones into a neighboring cluster */
|
|
UPROPERTY(meta = (DataflowInput, DataflowOutput, DataflowPassthrough = "Collection", DataflowIntrinsic))
|
|
FManagedArrayCollection Collection;
|
|
|
|
/** Bone selection */
|
|
UPROPERTY(meta = (DataflowInput, DisplayName = "TransformSelection"))
|
|
FDataflowTransformSelection TransformSelection;
|
|
|
|
/** Method to choose which neighbor to merge */
|
|
UPROPERTY(EditAnywhere, Category = Options, meta = (DisplayName = "Merge To"))
|
|
EClusterNeighborSelectionMethodEnum NeighborSelectionMethod = EClusterNeighborSelectionMethodEnum::Dataflow_ClusterNeighborSelectionMethod_LargestNeighbor;
|
|
|
|
/** Size (cube root of volume) of minimum desired post-merge clusters; if > 0, selected clusters may be merged multiple times until the cluster size is above this value */
|
|
UPROPERTY(EditAnywhere, Category = Options, meta = (DataflowInput, DisplayName = "Min Cluster Size", ClampMin = "0"))
|
|
float MinVolumeCubeRoot = 0.0f;
|
|
|
|
/** Whether to only allow clusters to merge if their bones are connected in the proximity graph */
|
|
UPROPERTY(EditAnywhere, Category = Options, meta = (DataflowInput))
|
|
bool bOnlyToConnected = true;
|
|
|
|
/** Whether to only allow clusters to merge if they have the same parent bone */
|
|
UPROPERTY(EditAnywhere, Category = Options, meta = (DataflowInput))
|
|
bool bOnlySameParent = true;
|
|
|
|
FClusterMergeToNeighborsDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid = FGuid::NewGuid())
|
|
: FDataflowNode(InParam, InGuid)
|
|
{
|
|
RegisterInputConnection(&Collection);
|
|
RegisterInputConnection(&TransformSelection);
|
|
RegisterInputConnection(&MinVolumeCubeRoot);
|
|
RegisterInputConnection(&bOnlyToConnected);
|
|
RegisterInputConnection(&bOnlySameParent);
|
|
|
|
RegisterOutputConnection(&Collection, &Collection);
|
|
}
|
|
|
|
virtual void Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const override;
|
|
|
|
};
|
|
|
|
|
|
/**
|
|
* Merge selected bones under a new parent cluster
|
|
*/
|
|
USTRUCT(meta = (DataflowGeometryCollection))
|
|
struct FClusterMergeDataflowNode : public FDataflowNode
|
|
{
|
|
GENERATED_USTRUCT_BODY()
|
|
DATAFLOW_NODE_DEFINE_INTERNAL(FClusterMergeDataflowNode, "ClusterMerge", "GeometryCollection|Cluster", "")
|
|
|
|
public:
|
|
|
|
/** Collection on which to merge bones into a cluster */
|
|
UPROPERTY(meta = (DataflowInput, DataflowOutput, DataflowPassthrough = "Collection", DataflowIntrinsic))
|
|
FManagedArrayCollection Collection;
|
|
|
|
/** Bone selection */
|
|
UPROPERTY(meta = (DataflowInput, DisplayName = "TransformSelection"))
|
|
FDataflowTransformSelection TransformSelection;
|
|
|
|
FClusterMergeDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid = FGuid::NewGuid())
|
|
: FDataflowNode(InParam, InGuid)
|
|
{
|
|
RegisterInputConnection(&Collection);
|
|
RegisterInputConnection(&TransformSelection);
|
|
RegisterOutputConnection(&Collection, &Collection);
|
|
}
|
|
|
|
virtual void Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const override;
|
|
|
|
};
|
|
|
|
|
|
/**
|
|
* Add a single cluster to the Geometry Collection if it only has a single transform with no clusters
|
|
*/
|
|
USTRUCT(meta = (DataflowGeometryCollection))
|
|
struct FClusterIsolatedRootsDataflowNode : public FDataflowNode
|
|
{
|
|
GENERATED_USTRUCT_BODY()
|
|
DATAFLOW_NODE_DEFINE_INTERNAL(FClusterIsolatedRootsDataflowNode, "ClusterIsolatedRoots", "GeometryCollection|Cluster", "")
|
|
|
|
public:
|
|
|
|
/** Collection to modify */
|
|
UPROPERTY(meta = (DataflowInput, DataflowOutput, DataflowPassthrough = "Collection", DataflowIntrinsic))
|
|
FManagedArrayCollection Collection;
|
|
|
|
FClusterIsolatedRootsDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid = FGuid::NewGuid())
|
|
: FDataflowNode(InParam, InGuid)
|
|
{
|
|
RegisterInputConnection(&Collection);
|
|
RegisterOutputConnection(&Collection, &Collection);
|
|
}
|
|
|
|
virtual void Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const override;
|
|
|
|
};
|
|
|
|
|
|
/**
|
|
* Cluster by grouping the selected bones with their adjacent, neighboring bones.
|
|
*/
|
|
USTRUCT(meta = (DataflowGeometryCollection))
|
|
struct FClusterMagnetDataflowNode : public FDataflowNode
|
|
{
|
|
GENERATED_USTRUCT_BODY()
|
|
DATAFLOW_NODE_DEFINE_INTERNAL(FClusterMagnetDataflowNode, "ClusterMagnet", "GeometryCollection|Cluster", "")
|
|
|
|
public:
|
|
|
|
/** Collection on which to merge bones into a cluster */
|
|
UPROPERTY(meta = (DataflowInput, DataflowOutput, DataflowPassthrough = "Collection", DataflowIntrinsic))
|
|
FManagedArrayCollection Collection;
|
|
|
|
/** Bone selection */
|
|
UPROPERTY(meta = (DataflowInput, DisplayName = "TransformSelection"))
|
|
FDataflowTransformSelection TransformSelection;
|
|
|
|
/** How many layers of neighbors to include in the clusters -- i.e. if 1, only direct neighbors are clustered; if 2, neighbors of neighbors are included, etc. */
|
|
UPROPERTY(EditAnywhere, Category = "Cluster Magnet", meta = (ClampMin = "1", DataflowInput, DisplayName = "Iterations"))
|
|
int32 Iterations = 1;
|
|
|
|
FClusterMagnetDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid = FGuid::NewGuid())
|
|
: FDataflowNode(InParam, InGuid)
|
|
{
|
|
RegisterInputConnection(&Collection);
|
|
RegisterInputConnection(&TransformSelection);
|
|
RegisterInputConnection(&Iterations);
|
|
RegisterOutputConnection(&Collection, &Collection);
|
|
}
|
|
|
|
virtual void Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const override;
|
|
|
|
};
|
|
|
|
|
|
namespace UE::Dataflow
|
|
{
|
|
void GeometryCollectionClusteringNodes();
|
|
}
|
|
|