// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Concepts/ConvertibleTo.h" #include "PhysicsEngine/AggregateGeom.h" #include "PhysicsEngine/PhysicsConstraintTemplate.h" #include "GeometryCollection/ManagedArrayCollection.h" #include "GeometryCollection/ManagedArrayAccessor.h" #include "Dataflow/DataflowTerminalNode.h" #include "PhysicsAssetDataflowState.h" #include "Generators/BoneGeometryGenerators.h" #include "Generators/ConstraintGenerators.h" #include "BoneSelection.h" #include "RigidDataflowNode.h" #include "ShapeElemNodes.h" #include "PhysicsAssetDataflowNodes.generated.h" class USkeleton; class USkeletalMesh; class UPhysicsAsset; class USkeletalBodySetup; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * The asset state is the object that holds the intermediate data that is built throughout the execution of the * dataflow graph. It's analogous to a collection used in other dataflow graphs. This graph doesn't use a * collection due to needing support for copy-on-write with UObjects * Asset state objects are lightweight and should be passed by-value as they support copy-on-write so each * state instance is fairly small and references its shared state */ USTRUCT() struct FDataflowPhysicsAssetMakeState : public FRigidDataflowNode { GENERATED_BODY() DATAFLOW_NODE_DEFINE_INTERNAL(FDataflowPhysicsAssetMakeState, "PhysicsAssetMakeState", "PhysicsAsset", "") public: FDataflowPhysicsAssetMakeState(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid = FGuid::NewGuid()) : FRigidDataflowNode(InParam, InGuid) { Register(); } private: // Optional mesh input if creating a state for a mesh that isn't currently being edited UPROPERTY(EditAnywhere, Category = Config, meta = (DataflowInput)) TObjectPtr TargetMesh; // Resulting state for the mesh UPROPERTY(meta = (DataflowOutput)) FPhysicsAssetDataflowState State; void Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const override; void Register(); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Add a shape to an aggregate geometry */ USTRUCT() struct FDataflowAggGeomAddShape : public FRigidDataflowMultiInputNode { GENERATED_BODY() DATAFLOW_NODE_DEFINE_INTERNAL(FDataflowAggGeomAddShape, "AggGeomAddShape", "PhysicsAsset", "") DATAFLOW_NODE_RENDER_TYPE("GeomRender", FName("FKAggregateGeom"), "AggGeom") public: FDataflowAggGeomAddShape(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid = FGuid::NewGuid()) : FRigidDataflowMultiInputNode(Shapes, InParam, InGuid) // -V1050 { InitPins(); Register(); } private: // Shapes to add to the aggregate geometry UPROPERTY() TArray Shapes; // The aggregate geometry to add the shape to UPROPERTY(meta = (DataflowInput, DataflowOutput, DataflowPassthrough = AggGeom)) FKAggregateGeom AggGeom; #if WITH_EDITOR bool CanDebugDraw() const override; bool CanDebugDrawViewMode(const FName& ViewModeName) const override; void DebugDraw(UE::Dataflow::FContext& Context, IDataflowDebugDrawInterface& DataflowRenderingInterface, const FDebugDrawParameters& DebugDrawParameters) const override; #endif void Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const override; void Register(); }; template<> struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 { enum { WithCopy = false }; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Terminal node that converts an asset state into the final asset */ USTRUCT(meta = (DataflowTerminal)) struct FDataflowPhysicsAssetTerminalNode : public FDataflowTerminalNode { GENERATED_BODY() DATAFLOW_NODE_DEFINE_INTERNAL(FDataflowPhysicsAssetTerminalNode, "PhysicsAssetTerminal", "Terminal", "") public: FDataflowPhysicsAssetTerminalNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid = FGuid::NewGuid()); private: // The state to convert into the final asset UPROPERTY(meta = (DataflowInput)) FPhysicsAssetDataflowState State; // Override target asset to write to - optional, defaults to the current asset in the editor UPROPERTY(EditAnywhere, Category = Asset, meta = (DataflowInput)) TObjectPtr PhysicsAsset = nullptr; // FDataflowTerminalNode void SetAssetValue(TObjectPtr Asset, UE::Dataflow::FContext& Context) const override; void Evaluate(UE::Dataflow::FContext& Context) const override; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Add a body setup to an asset state */ USTRUCT() struct FDataflowPhysicsAssetAddBody : public FRigidDataflowNode { GENERATED_BODY() DATAFLOW_NODE_DEFINE_INTERNAL(FDataflowPhysicsAssetAddBody, "PhysicsAssetAddBody", "PhysicsAsset", "") public: FDataflowPhysicsAssetAddBody(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid = FGuid::NewGuid()); private: // Body to add UPROPERTY(meta = (DataflowInput)) TObjectPtr Body; // State to write to UPROPERTY(meta = (DataflowInput, DataflowOutput, DataflowPassthrough = State)) FPhysicsAssetDataflowState State; void Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const override; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Make a single body setup from a template */ USTRUCT() struct FDataflowMakeBody : public FRigidDataflowNode { GENERATED_BODY() DATAFLOW_NODE_DEFINE_INTERNAL(FDataflowMakeBody, "Make Body Setup", "PhysicsAsset", "") public: FDataflowMakeBody(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid = FGuid::NewGuid()) : FRigidDataflowNode(InParam, InGuid) { Register(InParam); } private: #if WITH_EDITOR bool CanDebugDraw() const override; bool CanDebugDrawViewMode(const FName& ViewModeName) const override; void DebugDraw(UE::Dataflow::FContext& Context, IDataflowDebugDrawInterface& DataflowRenderingInterface, const FDebugDrawParameters& DebugDrawParameters) const override; #endif // Template body setup for details panel and output new body instance UPROPERTY(EditAnywhere, Instanced, Category = Body, meta = (DataflowOutput)) TObjectPtr Body; // Bone that this body should be bound to UPROPERTY(EditAnywhere, Category = Body, meta = (DataflowInput)) FName BoneName; void Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const override; void Register(const UE::Dataflow::FNodeParameters& InParam); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Make a single joint setup from a template */ USTRUCT() struct FDataflowMakeJoint : public FRigidDataflowNode { GENERATED_BODY() DATAFLOW_NODE_DEFINE_INTERNAL(FDataflowMakeJoint, "Make Joint", "PhysicsAsset", "") public: FDataflowMakeJoint(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid = FGuid::NewGuid()) : FRigidDataflowNode(InParam, InGuid) { Register(InParam); } private: UPROPERTY(EditAnywhere, Instanced, Category = Body, meta = (DataflowOutput)) TObjectPtr Joint; void Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const override; void Register(const UE::Dataflow::FNodeParameters& InParam); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Select bones in a mesh given a name search pattern */ USTRUCT() struct FDataflowSelectBonesByName : public FRigidDataflowNode { GENERATED_BODY() DATAFLOW_NODE_DEFINE_INTERNAL(FDataflowSelectBonesByName, "Select Bones by Name", "PhysicsAsset", "") DATAFLOW_NODE_RENDER_TYPE("GeomRender", FName("FBoneSelection"), "Selection") public: FDataflowSelectBonesByName(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid = FGuid::NewGuid()) : FRigidDataflowNode(InParam, InGuid) { Register(InParam); } private: // State to select bones within UPROPERTY(meta = (DataflowInput)) FPhysicsAssetDataflowState State; // Resulting selection UPROPERTY(meta = (DataflowOutput)) FRigidAssetBoneSelection Selection; // Search pattern to use. Supports wildcards (? - match single character, * - match sequence) UPROPERTY(EditAnywhere, Category = Parameters) FString SearchString; void Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const override; void Register(const UE::Dataflow::FNodeParameters& InParam); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Append a bone selection to another */ USTRUCT() struct FDataflowAppendBoneSelection : public FRigidDataflowNode { GENERATED_BODY() DATAFLOW_NODE_DEFINE_INTERNAL(FDataflowAppendBoneSelection, "Append Selected Bones", "PhysicsAsset", "") DATAFLOW_NODE_RENDER_TYPE("GeomRender", FName("FBoneSelection"), "Selection") public: FDataflowAppendBoneSelection(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid = FGuid::NewGuid()) : FRigidDataflowNode(InParam, InGuid) { Register(); } private: // First selection UPROPERTY(meta = (DataflowInput)) FRigidAssetBoneSelection A; // Second selection UPROPERTY(meta = (DataflowInput)) FRigidAssetBoneSelection B; // Resulting selection, a combination of A and B UPROPERTY(meta = (DataflowOutput)) FRigidAssetBoneSelection Selection; void Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const override; void Register(); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Select bones connected to each bone already in a selection. */ USTRUCT() struct FDataflowSelectConnectedBones : public FRigidDataflowNode { GENERATED_BODY() DATAFLOW_NODE_DEFINE_INTERNAL(FDataflowSelectConnectedBones, "Select Connected Bones", "PhysicsAsset", "") DATAFLOW_NODE_RENDER_TYPE("GeomRender", FName("FBoneSelection"), "Selection") public: FDataflowSelectConnectedBones(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid = FGuid::NewGuid()) : FRigidDataflowNode(InParam, InGuid) { Register(InParam); } private: // Disance to walk up the hierarchy to find bones UPROPERTY(EditAnywhere, Category = Settings) int32 DistanceUp = 0; // Distance to walk down the hierarchy to find bones UPROPERTY(EditAnywhere, Category = Settings) int32 DistanceDown = 0; // Resulting bone selction UPROPERTY(meta = (DataflowInput, DataflowOutput, DataflowPassthrough = "Selection")) FRigidAssetBoneSelection Selection; void Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const override; void Register(const UE::Dataflow::FNodeParameters& InParam); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Apply a geometry generator to a bone selection, creating bodies for each bone * The method of geometry generation is generic and offloaded to a "Generator" object/ * @see UBoneGeometryGenerator for implementation and creating your own generator node. */ USTRUCT() struct FDataflowCreateGeometryForBones : public FRigidDataflowNode { GENERATED_BODY() DATAFLOW_NODE_DEFINE_INTERNAL(FDataflowCreateGeometryForBones, "Create Geometry for Bones", "PhysicsAsset", "") DATAFLOW_NODE_RENDER_TYPE("GeomRender", FName("FPhysicsAssetDataflowState"), "State") public: FDataflowCreateGeometryForBones(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid = FGuid::NewGuid()) : FRigidDataflowNode(InParam, InGuid) { Register(); } private: UPROPERTY(meta=(DataflowInput)) TObjectPtr Generator; UPROPERTY(meta=(DataflowInput)) TObjectPtr TemplateBody; UPROPERTY(EditAnywhere, Category=Generation) bool bCreateMissingBodies = true; UPROPERTY(meta = (DataflowInput, DataflowOutput, DataflowPassthrough = "State")) FPhysicsAssetDataflowState State; UPROPERTY(meta = (DataflowInput)) FRigidAssetBoneSelection Selection; #if WITH_EDITOR bool CanDebugDraw() const override; bool CanDebugDrawViewMode(const FName& ViewModeName) const override; void DebugDraw(UE::Dataflow::FContext& Context, IDataflowDebugDrawInterface& DataflowRenderingInterface, const FDebugDrawParameters& DebugDrawParameters) const override; #endif void Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const override; void Register(); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Apply a constraint generator to a bone selection, creating constraints for each pair of bodies * The method of constraint generation is generic and offloaded to a "Generator" object/ * @see UConstraintGenerator for implementation and creating your own generator node. */ USTRUCT() struct FDataflowAutoConstrainBodies : public FRigidDataflowNode { GENERATED_BODY(); DATAFLOW_NODE_DEFINE_INTERNAL(FDataflowAutoConstrainBodies, "Create Constraints for Bones", "PhysicsAsset", "") public: FDataflowAutoConstrainBodies(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid = FGuid::NewGuid()) : FRigidDataflowNode(InParam, InGuid) { Register(); } private: // Generation method to use UPROPERTY(meta=(DataflowInput)) TObjectPtr Generator; // Template constraint to take properties from UPROPERTY(meta=(DataflowInput)) TObjectPtr TemplateConstraint; // State to apply the joints to UPROPERTY(meta = (DataflowInput, DataflowOutput, DataflowPassthrough = "State")) FPhysicsAssetDataflowState State; // Optional selection, restricts the bodies that will attempt to auto constrain UPROPERTY(meta = (DataflowInput)) FRigidAssetBoneSelection Selection; void Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const override; void Register(); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Given a body setup, replace the internal aggregate geometry with the provided geometry */ USTRUCT() struct FDataflowSetBodyGeometry : public FRigidDataflowNode { GENERATED_BODY() DATAFLOW_NODE_DEFINE_INTERNAL(FDataflowSetBodyGeometry, "Set Body Geometry", "PhysicsAsset", "") public: FDataflowSetBodyGeometry(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid = FGuid::NewGuid()) : FRigidDataflowNode(InParam, InGuid) { Register(); } private: // The body to modify UPROPERTY(meta = (DataflowInput, DataflowOutput, DataflowPassthrough = "Body")) TObjectPtr Body; // The Geometry to set on the body UPROPERTY(meta = (DataflowInput)) FKAggregateGeom Geometry; #if WITH_EDITOR bool CanDebugDraw() const override; bool CanDebugDrawViewMode(const FName& ViewModeName) const override; void DebugDraw(UE::Dataflow::FContext& Context, IDataflowDebugDrawInterface& DataflowRenderingInterface, const FDebugDrawParameters& DebugDrawParameters) const override; #endif void Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const override; void Register(); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Create a new, empty selection for the specified mesh */ USTRUCT() struct FDataflowNewBoneSelection : public FRigidDataflowNode { GENERATED_BODY() DATAFLOW_NODE_DEFINE_INTERNAL(FDataflowNewBoneSelection, "New Bone Selection", "PhysicsAsset", "") public: FDataflowNewBoneSelection(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid = FGuid::NewGuid()) : FRigidDataflowNode(InParam, InGuid) { Register(InParam); } private: // The created selection UPROPERTY(meta = (DataflowOutput)) FRigidAssetBoneSelection Selection; // The skeleton to target if not using the currently edited asset UPROPERTY(meta = (DataflowInput)) TObjectPtr Skeleton = nullptr; // The Mesh to target if not using the currently edited asset UPROPERTY(meta = (DataflowInput)) TObjectPtr Mesh = nullptr; void Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const override; void Register(const UE::Dataflow::FNodeParameters& InParam); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// namespace UE::Dataflow { void RegisterPhysicsAssetTerminalNode(); void RegisterPhysicsAssetNodes(); }