Files
UnrealEngine/Engine/Plugins/Experimental/ChaosRigidAsset/Source/ChaosRigidAssetNodes/Private/PhysicsAssetDataflowNodes.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

845 lines
23 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "PhysicsAssetDataflowNodes.h"
#include "Dataflow/DataflowNodeFactory.h"
#include "PhysicsEngine/PhysicsAsset.h"
#include "PhysicsEngine/SkeletalBodySetup.h"
#include "Misc/WildcardString.h"
#include "DataflowAttachment.h"
#include "Dataflow/DataflowDebugDrawInterface.h"
#include "Dataflow/DataflowDebugDrawObject.h"
#include "Animation/Skeleton.h"
#include "SkeletalDebugRendering.h"
#include "PhysicsAssetBuilder.h"
#include "Engine/SkeletalMesh.h"
#include "ReferenceSkeleton.h"
FDataflowPhysicsAssetTerminalNode::FDataflowPhysicsAssetTerminalNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid /*= FGuid::NewGuid()*/)
: FDataflowTerminalNode(InParam, InGuid)
{
RegisterInputConnection(&State);
RegisterInputConnection(&PhysicsAsset);
}
void FDataflowPhysicsAssetTerminalNode::SetAssetValue(TObjectPtr<UObject> Asset, UE::Dataflow::FContext& Context) const
{
UPhysicsAsset* Target = Asset ? Cast<UPhysicsAsset>(Asset->GetOuter()) : nullptr;
if(!Target)
{
Target = GetValue(Context, &PhysicsAsset);
}
if(Target)
{
const FPhysicsAssetDataflowState InState = GetValue(Context, &State);
InState.DebugLog();
UE::Chaos::RigidAsset::FPhysicsAssetBuilder::Make(InState.TargetSkeleton, InState.Bodies.ToArray(), InState.Constraints.ToArray())
.SetTargetAsset(Target)
.Build();
}
}
void FDataflowPhysicsAssetTerminalNode::Evaluate(UE::Dataflow::FContext& Context) const
{
}
void UE::Dataflow::RegisterPhysicsAssetTerminalNode()
{
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FDataflowPhysicsAssetTerminalNode);
}
void UE::Dataflow::RegisterPhysicsAssetNodes()
{
// Asset state
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FDataflowPhysicsAssetMakeState);
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FDataflowPhysicsAssetAddBody);
// Body Setup
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FDataflowMakeBody);
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FDataflowSetBodyGeometry);
// Joints
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FDataflowMakeJoint);
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FDataflowAutoConstrainBodies);
// Body Geometry
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FDataflowAggGeomAddShape);
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FDataflowCreateGeometryForBones);
// Bone Selections
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FDataflowNewBoneSelection);
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FDataflowAppendBoneSelection);
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FDataflowSelectBonesByName);
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FDataflowSelectConnectedBones);
RegisterBoneGeometryGeneratorNodes();
RegisterConstraintGeneratorNodes();
}
FDataflowPhysicsAssetAddBody::FDataflowPhysicsAssetAddBody(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid /*= FGuid::NewGuid()*/)
: FRigidDataflowNode(InParam, InGuid)
{
AddInputs(Body, State);
AddPassthroughs(State);
}
void FDataflowPhysicsAssetAddBody::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
if(Out->IsA(&State))
{
const USkeletalBodySetup* InBody = GetValue(Context, &Body);
FPhysicsAssetDataflowState InState = GetValue(Context, &State);
if(InBody)
{
InState.Bodies.Emplace(Cast<USkeletalBodySetup>(StaticDuplicateObject(InBody, GetTransientPackage())));
}
SetValue(Context, InState, &State);
}
}
struct FDataflowDebugDrawBodySetupObject : public FDataflowDebugDrawBaseObject
{
FDataflowDebugDrawBodySetupObject(IDataflowDebugDrawInterface::FDataflowElementsType& InDataflowElements, TObjectPtr<USkeletalBodySetup> InBody)
: FDataflowDebugDrawBaseObject(InDataflowElements)
, Body(InBody)
{
}
virtual void PopulateDataflowElements() override
{
}
virtual void DrawDataflowElements(FPrimitiveDrawInterface* PDI) override
{
if(!Body)
{
return;
}
const FKAggregateGeom& Geom = Body->AggGeom;
const int32 NumGeoms = Geom.GetElementCount();
for(int32 Index = 0; Index < NumGeoms; ++Index)
{
const FKShapeElem* Elem = Geom.GetElement(Index);
Elem->DrawElemWire(PDI, Elem->GetTransform(), 1.0f, FColor::White);
}
}
virtual FBox ComputeBoundingBox() const override
{
if(!Body)
{
return FBox(EForceInit::ForceInitToZero);
}
return Body->AggGeom.CalcAABB(FTransform::Identity);
}
private:
TObjectPtr<USkeletalBodySetup> Body;
};
struct FDataflowDebugDrawBoneSelectionObject : public FDataflowDebugDrawBaseObject
{
FDataflowDebugDrawBoneSelectionObject(IDataflowDebugDrawInterface::FDataflowElementsType& InDataflowElements, FRigidAssetBoneSelection InSelection)
: FDataflowDebugDrawBaseObject(InDataflowElements)
, Selection(InSelection)
{
}
virtual void PopulateDataflowElements() override
{
}
virtual void DrawDataflowElements(FPrimitiveDrawInterface* PDI) override
{
if(!Selection.Mesh)
{
return;
}
FReferenceSkeleton& RefSkel = Selection.Mesh->GetRefSkeleton();
const int32 NumBones = RefSkel.GetNum();
TArray<FTransform> BoneTransforms;
TArray<FBoneIndexType> RequiredBones;
TArray<FLinearColor> BoneColors;
TArray<int32> SelectedBones;
TArray<TRefCountPtr<HHitProxy>> HitProxies;
RefSkel.GetBoneAbsoluteTransforms(BoneTransforms);
for(int32 BoneIndex = 0; BoneIndex < NumBones; ++BoneIndex)
{
RequiredBones.Add(BoneIndex);
}
for(const FRigidAssetBoneInfo& Bone : Selection.SelectedBones)
{
SelectedBones.Add(Bone.Index);
}
FSkelDebugDrawConfig DrawConfig;
DrawConfig.bUseMultiColorAsDefaultColor = false;
DrawConfig.BoneDrawMode = EBoneDrawMode::All;
DrawConfig.BoneDrawSize = 1.f;
DrawConfig.bAddHitProxy = false;
DrawConfig.bForceDraw = true;
DrawConfig.DefaultBoneColor = FLinearColor::Gray;
DrawConfig.AffectedBoneColor = FLinearColor::Gray;
DrawConfig.SelectedBoneColor = FLinearColor{ 1.0f, 0.75f, 0.1f };
DrawConfig.ParentOfSelectedBoneColor = FLinearColor::Gray;
SkeletalDebugRendering::DrawBones(
PDI, FVector::Zero(),
RequiredBones,
RefSkel,
BoneTransforms,
SelectedBones,
BoneColors,
HitProxies,
DrawConfig
);
}
virtual FBox ComputeBoundingBox() const override
{
if(!Selection.Mesh)
{
return FBox(EForceInit::ForceInitToZero);
}
FReferenceSkeleton& RefSkel = Selection.Mesh->GetRefSkeleton();
TArray<FTransform> BoneTransforms;
RefSkel.GetBoneAbsoluteTransforms(BoneTransforms);
FBox Result;
for(const FTransform& Transform : BoneTransforms)
{
Result += Transform.GetTranslation();
}
return Result;
}
private:
FRigidAssetBoneSelection Selection;
};
struct FDataflowDebugDrawAggGeomObject : public FDataflowDebugDrawBaseObject
{
FDataflowDebugDrawAggGeomObject(IDataflowDebugDrawInterface::FDataflowElementsType& InDataflowElements, const FKAggregateGeom* InGeom)
: FDataflowDebugDrawBaseObject(InDataflowElements)
, Geom(InGeom)
{
}
void PopulateDataflowElements() override
{
}
void DrawDataflowElements(FPrimitiveDrawInterface* PDI) override
{
if(!Geom)
{
return;
}
const int32 NumElems = Geom->GetElementCount();
int32 ElementOffset = DataflowElements.Num();
int32 ElementNum = NumElems;
for(int32 Index = 0; Index < NumElems; ++Index)
{
const FKShapeElem* Elem = Geom->GetElement(Index);
Elem->DrawElemWire(PDI, Elem->GetTransform(), 1.0f, FColor::Cyan);
}
}
FBox ComputeBoundingBox() const override
{
if(!Geom)
{
return {};
}
return Geom->CalcAABB(FTransform::Identity);
}
private:
const FKAggregateGeom* Geom = nullptr;
};
#if WITH_EDITOR
bool FDataflowMakeBody::CanDebugDraw() const
{
return true;
}
bool FDataflowMakeBody::CanDebugDrawViewMode(const FName& ViewModeName) const
{
return true;
}
void FDataflowMakeBody::DebugDraw(UE::Dataflow::FContext& Context, IDataflowDebugDrawInterface& DataflowRenderingInterface, const FDebugDrawParameters& DebugDrawParameters) const
{
if(Body)
{
TRefCountPtr<IDataflowDebugDrawObject> BodyDrawObject(MakeDebugDrawObject<FDataflowDebugDrawBodySetupObject>(DataflowRenderingInterface.ModifyDataflowElements(), Body));
DataflowRenderingInterface.DrawObject(BodyDrawObject);
}
}
#endif
void FDataflowMakeBody::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
if(Out->IsA(&Body))
{
TObjectPtr<USkeletalBodySetup> NewSetup = CastChecked<USkeletalBodySetup>(StaticDuplicateObject(Body, GetTransientPackage()));
NewSetup->BoneName = GetValue(Context, &BoneName);
SetValue(Context, NewSetup, &Body);
}
}
void FDataflowMakeBody::Register(const UE::Dataflow::FNodeParameters& InParam)
{
// Construct the template object for this node
Body = NewObject<USkeletalBodySetup>(GetOwner());
AddInputs(BoneName);
AddOutputs(Body);
}
void FDataflowMakeJoint::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
if(Out->IsA(&Joint))
{
TObjectPtr<UPhysicsConstraintTemplate> NewJoint = CastChecked<UPhysicsConstraintTemplate>(StaticDuplicateObject(Joint, GetTransientPackage()));
SetValue(Context, NewJoint, &Joint);
}
}
void FDataflowMakeJoint::Register(const UE::Dataflow::FNodeParameters& InParam)
{
// Construct the template object for this node
Joint = NewObject<UPhysicsConstraintTemplate>(GetOwner());
AddOutputs(Joint);
}
void FDataflowSelectBonesByName::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
if(Out->IsA(&Selection))
{
FPhysicsAssetDataflowState InState = GetValue(Context, &State);
TObjectPtr<USkeleton> Skeleton = InState.TargetSkeleton;
TObjectPtr<USkeletalMesh> Mesh = InState.TargetMesh;
if(!Skeleton)
{
Context.Error(TEXT("Attempted to make a selection without a Skeleton"), this);
SetValue(Context, FRigidAssetBoneSelection{}, &Selection);
return;
}
if(!Mesh)
{
Context.Error(TEXT("Attempted to make a selection without a SkeletalMesh"), this);
SetValue(Context, FRigidAssetBoneSelection{}, &Selection);
return;
}
const FWildcardString Pattern = SearchString;
const FReferenceSkeleton RefSkel = Skeleton->GetReferenceSkeleton();
FRigidAssetBoneSelection NewSelection;
NewSelection.Skeleton = Skeleton;
NewSelection.Mesh = Mesh;
for(int32 BoneIndex = 0; BoneIndex < RefSkel.GetNum(); ++BoneIndex)
{
FName BoneName = RefSkel.GetBoneName(BoneIndex);
if(Pattern.IsMatch(BoneName.ToString()))
{
NewSelection.SelectedBones.Emplace(BoneName, BoneIndex, RefSkel.GetDepthBetweenBones(BoneIndex, 0));
}
}
NewSelection.SortBones();
SetValue(Context, MoveTemp(NewSelection), &Selection);
}
}
void FDataflowSelectBonesByName::Register(const UE::Dataflow::FNodeParameters& InParam)
{
AddInputs(State);
AddOutputs(Selection);
}
void FDataflowAppendBoneSelection::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
if(Out->IsA(&Selection))
{
const FRigidAssetBoneSelection& InA = GetValue(Context, &A);
const FRigidAssetBoneSelection& InB = GetValue(Context, &B);
if((InA.Skeleton != InB.Skeleton) || (InA.Mesh != InB.Mesh))
{
Context.Error(TEXT("Attmepted to append selections from multiple skeletons or meshes"), this);
SetValue(Context, FRigidAssetBoneSelection{}, &Selection);
return;
}
FRigidAssetBoneSelection NewSelection = InA;
for(const FRigidAssetBoneInfo& Bone : InB.SelectedBones)
{
NewSelection.SelectedBones.AddUnique(Bone);
}
NewSelection.SortBones();
SetValue(Context, MoveTemp(NewSelection), &Selection);
}
}
void FDataflowAppendBoneSelection::Register()
{
AddInputs(A, B);
AddOutputs(Selection);
}
void FDataflowSelectConnectedBones::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
if(Out->IsA(&Selection))
{
const FRigidAssetBoneSelection& InSelection = GetValue(Context, &Selection);
if(!InSelection.Skeleton)
{
Context.Error(TEXT("Attempted to make a selection without a Skeleton"), this);
SafeForwardInput(Context, &Selection, &Selection);
return;
}
const FReferenceSkeleton& RefSkel = InSelection.Skeleton->GetReferenceSkeleton();
FRigidAssetBoneSelection NewSelection = InSelection;
for(const FRigidAssetBoneInfo& Bone : InSelection.SelectedBones)
{
int32 CurrentIndex = Bone.Index;
for(int32 UpCount = 0; UpCount < DistanceUp; ++UpCount)
{
int32 NewIndex = RefSkel.GetParentIndex(CurrentIndex);
if(NewIndex == INDEX_NONE)
{
// Nothing more in this direction
break;
}
if(!NewSelection.ContainsIndex(NewIndex))
{
NewSelection.SelectedBones.Emplace(RefSkel.GetBoneName(NewIndex), NewIndex, RefSkel.GetDepthBetweenBones(NewIndex, 0));
}
CurrentIndex = NewIndex;
}
int32 Level = 0;
TArray<int32> BoneStack;
BoneStack.Add(Bone.Index);
while(Level < DistanceDown)
{
TArray<int32> NextBoneStack;
while(BoneStack.Num() > 0)
{
const int32 Current = BoneStack.Pop(EAllowShrinking::No);
TArray<int32> ChildBoneIndices;
RefSkel.GetDirectChildBones(Current, ChildBoneIndices);
for(const int32 ChildIndex : ChildBoneIndices)
{
if(!NewSelection.ContainsIndex(ChildIndex))
{
NewSelection.SelectedBones.Emplace(RefSkel.GetBoneName(ChildIndex), ChildIndex, RefSkel.GetDepthBetweenBones(ChildIndex, 0));
}
NextBoneStack.Push(ChildIndex);
}
}
BoneStack = MoveTemp(NextBoneStack);
++Level;
}
}
NewSelection.SortBones();
SetValue(Context, MoveTemp(NewSelection), &Selection);
}
}
void FDataflowSelectConnectedBones::Register(const UE::Dataflow::FNodeParameters& InParam)
{
AddInputs(Selection);
AddPassthroughs(Selection);
}
#if WITH_EDITOR
bool FDataflowCreateGeometryForBones::CanDebugDraw() const
{
return true;
}
bool FDataflowCreateGeometryForBones::CanDebugDrawViewMode(const FName& ViewModeName) const
{
return true;
}
void FDataflowCreateGeometryForBones::DebugDraw(UE::Dataflow::FContext& Context, IDataflowDebugDrawInterface& DataflowRenderingInterface, const FDebugDrawParameters& DebugDrawParameters) const
{
const FRigidAssetBoneSelection& InSelection = GetValue(Context, &Selection);
TRefCountPtr<IDataflowDebugDrawObject> DrawObject(MakeDebugDrawObject<FDataflowDebugDrawBoneSelectionObject>(DataflowRenderingInterface.ModifyDataflowElements(), InSelection));
DataflowRenderingInterface.DrawObject(DrawObject);
TObjectPtr<UBoneGeometryGenerator> InGenerator = GetValue(Context, &Generator);
if(InGenerator && InGenerator->CanDebugDraw())
{
InGenerator->DebugDraw(Context, DataflowRenderingInterface, DebugDrawParameters, InSelection);
}
}
#endif
void FDataflowCreateGeometryForBones::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
if(Out->IsA(&State))
{
TObjectPtr<UBoneGeometryGenerator> InGenerator = GetValue(Context, &Generator);
if(!InGenerator)
{
Context.Warning(TEXT("FDataflowCreateGeometryForBones: No method selected for body generation"), this);
SafeForwardInput(Context, &State, &State);
return;
}
const FRigidAssetBoneSelection& InSelection = GetValue(Context, &Selection);
if(!InSelection.Skeleton || !InSelection.Mesh)
{
Context.Error(TEXT("Attempted to make geometry without a skeleton or mesh"), this);
SafeForwardInput(Context, &State, &State);
return;
}
TObjectPtr<USkeletalBodySetup> InTemplate = GetValue(Context, &TemplateBody);
if(!InTemplate)
{
Context.Error(TEXT("Attempted to make a body without a template body setup"));
SafeForwardInput(Context, &State, &State);
return;
}
FPhysicsAssetDataflowState InState = GetValue(Context, &State);
TArray<UE::Chaos::RigidAsset::FSimpleGeometry> Geoms = InGenerator->Build(InSelection);
if(Geoms.Num() != InSelection.SelectedBones.Num())
{
Context.Error(TEXT("Failed to generate bodies for each bone in the selection"));
SafeForwardInput(Context, &State, &State);
return;
}
for(int32 Index = 0; Index < Geoms.Num(); ++Index)
{
const UE::Chaos::RigidAsset::FSimpleGeometry& Geom = Geoms[Index];
const FRigidAssetBoneInfo& Bone = InSelection.SelectedBones[Index];
TObjectPtr<USkeletalBodySetup> NewSetup = CastChecked<USkeletalBodySetup>(StaticDuplicateObject(InTemplate, GetTransientPackage()));
if(!Geom.GeometryElement)
{
Context.Error(TEXT("Generator failed to create usable geometry"));
SafeForwardInput(Context, &State, &State);
return;
}
NewSetup->AggGeom.VisitShapeAndContainer(*Geom.GeometryElement.Get(), [] <typename ElemType> (const ElemType & ElemTyped, TArray<ElemType>&Container)
{
Container.Add(ElemTyped);
});
NewSetup->BoneName = Bone.Name;
InState.Bodies.Add(NewSetup);
}
SetValue(Context, MoveTemp(InState), &State);
}
}
void FDataflowCreateGeometryForBones::Register()
{
AddInputs(Generator, TemplateBody, State, Selection);
AddPassthroughs(State);
}
void FDataflowAutoConstrainBodies::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
TObjectPtr<UConstraintGenerator> InGenerator = GetValue(Context, &Generator);
if(!InGenerator)
{
Context.Warning(TEXT("FDataflowAutoConstrainBodies: No method selected for constraint generation"), this);
SafeForwardInput(Context, &State, &State);
return;
}
const FRigidAssetBoneSelection& InSelection = GetValue(Context, &Selection);
if(!InSelection.Skeleton || !InSelection.Mesh)
{
Context.Error(TEXT("Attempted to make constraints without a skeleton or mesh"), this);
SafeForwardInput(Context, &State, &State);
return;
}
TObjectPtr<UPhysicsConstraintTemplate> InTemplate = GetValue(Context, &TemplateConstraint);
if(!InTemplate)
{
Context.Error(TEXT("Attempted to make a constraint without a template"));
SafeForwardInput(Context, &State, &State);
return;
}
FPhysicsAssetDataflowState InState = GetValue(Context, &State);
TArray<TObjectPtr<UPhysicsConstraintTemplate>> GeneratedConstraints = InGenerator->Build(InTemplate, InSelection);
InState.Constraints.Append(GeneratedConstraints);
SetValue(Context, MoveTemp(InState), &State);
}
void FDataflowAutoConstrainBodies::Register()
{
AddInputs(Generator, TemplateConstraint, State, Selection);
AddPassthroughs(State);
}
#if WITH_EDITOR
bool FDataflowSetBodyGeometry::CanDebugDraw() const
{
return true;
}
bool FDataflowSetBodyGeometry::CanDebugDrawViewMode(const FName& ViewModeName) const
{
return true;
}
void FDataflowSetBodyGeometry::DebugDraw(UE::Dataflow::FContext& Context, IDataflowDebugDrawInterface& DataflowRenderingInterface, const FDebugDrawParameters& DebugDrawParameters) const
{
if(DebugDrawParameters.bNodeIsSelected || DebugDrawParameters.bNodeIsPinned)
{
if(Body)
{
TRefCountPtr<IDataflowDebugDrawObject> BodyDrawObject(MakeDebugDrawObject<FDataflowDebugDrawBodySetupObject>(DataflowRenderingInterface.ModifyDataflowElements(), Body));
DataflowRenderingInterface.DrawObject(BodyDrawObject);
}
}
}
#endif
void FDataflowSetBodyGeometry::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
if(Out->IsA(&Body))
{
TObjectPtr<USkeletalBodySetup> InSetup = GetValue(Context, &Body);
check(InSetup);
TObjectPtr<USkeletalBodySetup> NewSetup = CastChecked<USkeletalBodySetup>(StaticDuplicateObject(InSetup, GetTransientPackage()));
NewSetup->AggGeom = GetValue(Context, &Geometry);
SetValue(Context, NewSetup, &Body);
}
}
void FDataflowSetBodyGeometry::Register()
{
AddInputs(Body, Geometry);
AddPassthroughs(Body);
}
void FDataflowNewBoneSelection::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
if(Out->IsA(&Selection))
{
TObjectPtr<USkeleton> InSkeleton = GetValue(Context, &Skeleton);
TObjectPtr<USkeletalMesh> InMesh = GetValue(Context, &Mesh);
// Unless overridden, get the skeleton from the currently edited asset
if(!InSkeleton)
{
if(TObjectPtr<UPhysicsAsset> PhysAsset = GetAttachmentOwnerAs<UPhysicsAsset>(Context))
{
InSkeleton = PhysAsset->GetPreviewMesh() ? PhysAsset->GetPreviewMesh()->GetSkeleton() : nullptr;
}
else
{
Context.Error(TEXT("Attempted to make a selection without a Skeleton"), this);
SetValue(Context, FRigidAssetBoneSelection{}, &Selection);
return;
}
}
// Unless overridden, pull the skeletal mesh from the selected skeleton
if(!InMesh)
{
if(InSkeleton)
{
InMesh = InSkeleton->GetPreviewMesh();
}
else
{
Context.Error(TEXT("Attempted to make a selection without a Skeletal Mesh"), this);
SetValue(Context, FRigidAssetBoneSelection{}, &Selection);
return;
}
}
if(!InMesh || !InSkeleton)
{
Context.Error(TEXT("Unable to identify a mesh or skeleton"), this);
SetValue(Context, FRigidAssetBoneSelection{}, &Selection);
return;
}
if(InSkeleton != InMesh->GetSkeleton())
{
Context.Error(TEXT("Specified mesh is not compatible with the specified skeleton"), this);
SetValue(Context, FRigidAssetBoneSelection{}, &Selection);
return;
}
FRigidAssetBoneSelection NewSelection;
NewSelection.Skeleton = InSkeleton;
NewSelection.Mesh = InMesh;
SetValue(Context, MoveTemp(NewSelection), &Selection);
}
}
void FDataflowNewBoneSelection::Register(const UE::Dataflow::FNodeParameters& InParam)
{
AddInputs(Skeleton, Mesh);
AddOutputs(Selection);
}
void FDataflowPhysicsAssetMakeState::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
if(Out->IsA(&State))
{
TObjectPtr<USkeletalMesh> InTarget = GetValue(Context, &TargetMesh);
// If there's no override - pick from the context asset
if(!InTarget)
{
if(TObjectPtr<UPhysicsAsset> PhysAsset = GetAttachmentOwnerAs<UPhysicsAsset>(Context))
{
InTarget = PhysAsset->GetPreviewMesh();
if(!InTarget)
{
Context.Error(TEXT("Attempted to make a state without a valid mesh"), this);
SetValue(Context, FPhysicsAssetDataflowState{}, &State);
return;
}
}
else
{
Context.Error(TEXT("Attempted to make a state without a valid skeleton"), this);
SetValue(Context, FPhysicsAssetDataflowState{}, &State);
return;
}
}
SetValue(Context, FPhysicsAssetDataflowState{ InTarget->GetSkeleton(), InTarget }, &State);
}
}
void FDataflowPhysicsAssetMakeState::Register()
{
AddInputs(TargetMesh);
AddOutputs(State);
}
#if WITH_EDITOR
bool FDataflowAggGeomAddShape::CanDebugDraw() const
{
return true;
}
bool FDataflowAggGeomAddShape::CanDebugDrawViewMode(const FName& ViewModeName) const
{
return true;
}
void FDataflowAggGeomAddShape::DebugDraw(UE::Dataflow::FContext& Context, IDataflowDebugDrawInterface& DataflowRenderingInterface, const FDebugDrawParameters& DebugDrawParameters) const
{
TRefCountPtr<IDataflowDebugDrawObject> Obj(MakeDebugDrawObject<FDataflowDebugDrawAggGeomObject>(DataflowRenderingInterface.ModifyDataflowElements(), &AggGeom));
DataflowRenderingInterface.DrawObject(Obj);
}
#endif
void FDataflowAggGeomAddShape::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
using namespace UE::Chaos::RigidAsset;
if(Out->IsA(&AggGeom))
{
const int32 NumShapeInputs = Shapes.Num();
const FKAggregateGeom& InGeom = GetValue(Context, &AggGeom);
FKAggregateGeom NewAggGeom = InGeom;
for(int32 Index = 0; Index < NumShapeInputs; ++Index)
{
const FSimpleGeometry& InShape = GetValue(Context, &Shapes[Index]);
if(InShape.GeometryElement)
{
NewAggGeom.AddElement(*InShape.GeometryElement.Get());
}
}
SetValue(Context, MoveTemp(NewAggGeom), &AggGeom);
}
}
void FDataflowAggGeomAddShape::Register()
{
AddInputs(AggGeom);
AddPassthroughs(AggGeom);
}