// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Dataflow/DataflowNode.h" #include "Dataflow/DataflowObjectInterface.h" #include "ShapeElemNodes.h" #include "DataflowAttachment.h" #include "RigidDataflowNode.generated.h" /** * Intermediate node for rigid asset node implementations encapsulating common functionality */ USTRUCT() struct FRigidDataflowNode : public FDataflowNode { GENERATED_BODY(); public: FRigidDataflowNode() = default; FRigidDataflowNode(const UE::Dataflow::FNodeParameters& Param, FGuid InGuid) : FDataflowNode(Param, InGuid) , Owner(Param.OwningObject) { } // Get the owning UObject for the node, to store UObjects in the asset safely TObjectPtr GetOwner() const { return Owner; } // Multi-input versions of the pin registration functions template void AddInputs(T&&... Args) { (RegisterInputConnection(&Args), ...); } template void AddOutputs(T&&... Args) { (RegisterOutputConnection(&Args), ...); } template void AddPassthroughs(T&&... Args) { (RegisterOutputConnection(&Args, &Args), ...); } protected: // Grab the context owner and return it as T if possible, otherwise nullptr template TObjectPtr GetContextOwnerAs(UE::Dataflow::FContext& InContext) const { if(UE::Dataflow::FEngineContext* EngineContext = InContext.AsType()) { return Cast(EngineContext->Owner); } return nullptr; } TObjectPtr GetContextOwner(UE::Dataflow::FContext& InContext) const { if(UE::Dataflow::FEngineContext* EngineContext = InContext.AsType()) { return EngineContext->Owner; } return nullptr; } // Get the dataflow attachement if it is avaialble for the current asset TObjectPtr GetAttachment(UE::Dataflow::FContext& InContext) const { return GetContextOwnerAs(InContext); } TObjectPtr GetAttachmentOwner(UE::Dataflow::FContext& InContext) const { if(TObjectPtr Attachment = GetAttachment(InContext)) { return Attachment->GetOuter(); } return nullptr; } template TObjectPtr GetAttachmentOwnerAs(UE::Dataflow::FContext& InContext) const { if(TObjectPtr Attachment = GetAttachment(InContext)) { return Cast(Attachment->GetOuter()); } return nullptr; } private: UPROPERTY() TObjectPtr Owner; }; struct IMultiPinArrayConnector { virtual ~IMultiPinArrayConnector() { } virtual void InitPins() = 0; virtual TArray AddPins() = 0; virtual bool CanAddPin() const = 0; virtual TArray GetPinsToRemove() const = 0; virtual void OnPinRemoved(const UE::Dataflow::FPin& Pin) = 0; virtual bool CanRemovePin() const = 0; virtual void PostSerialize(const FArchive& Ar) = 0; }; template struct TMultiPinArrayConnector : public IMultiPinArrayConnector { TMultiPinArrayConnector() = delete; explicit TMultiPinArrayConnector(FRigidDataflowNode& InNode, TArray& InPinContainer, int32 InMinimumNumPins) : Node(InNode) , PinContainer(InPinContainer) , MinimumNumPins(InMinimumNumPins) { } void InitPins() override { check(PinContainer.Num() == 0); for(int32 Index = 0; Index < MinimumNumPins; ++Index) { AddPins(); } } TArray AddPins() override { const int32 Index = PinContainer.AddDefaulted(); UE::Dataflow::TConnectionReference Ref{ &PinContainer[Index], Index, &PinContainer }; const FDataflowInput& Input = Node.RegisterInputArrayConnection(Ref); return { { .Direction = UE::Dataflow::FPin::EDirection::INPUT, .Type = Input.GetType(), .Name = Input.GetName() } }; } bool CanAddPin() const override { return true; } TArray GetPinsToRemove() const override { if(PinContainer.Num() == 0) { return {}; } const int32 Index = PinContainer.Num() - 1; UE::Dataflow::TConnectionReference Ref{ &PinContainer[Index], Index, &PinContainer }; if(const FDataflowInput* Input = Node.FindInput(Ref)) { return { { .Direction = UE::Dataflow::FPin::EDirection::INPUT, .Type = Input->GetType(), .Name = Input->GetName() } }; } return {}; } void OnPinRemoved(const UE::Dataflow::FPin& Pin) override { PinContainer.Pop(); } bool CanRemovePin() const override { return PinContainer.Num() > MinimumNumPins; } void PostSerialize(const FArchive& Ar) override { // Restore pins if(Ar.IsLoading()) { for(int32 Index = 0; Index < PinContainer.Num(); ++Index) { UE::Dataflow::TConnectionReference Ref{ &PinContainer[Index], Index, &PinContainer }; Node.FindOrRegisterInputArrayConnection(Ref); } if(Ar.IsTransacting()) { int32 OriginalNum = Node.GetNumInputs(); const int32 ExpectedNum = PinContainer.Num(); if(OriginalNum > ExpectedNum) { PinContainer.SetNum(OriginalNum); while(OriginalNum-- > ExpectedNum) { const int32 Index = PinContainer.Num() - 1; UE::Dataflow::TConnectionReference Ref{ &PinContainer[Index], Index, &PinContainer }; Node.UnregisterInputConnection(Ref); PinContainer.Pop(EAllowShrinking::No); } check(PinContainer.Num() == ExpectedNum); } } } } private: FRigidDataflowNode& Node; TArray& PinContainer; int32 MinimumNumPins; }; USTRUCT() struct FRigidDataflowMultiInputNode : public FRigidDataflowNode { GENERATED_BODY() FRigidDataflowMultiInputNode() = default; template explicit FRigidDataflowMultiInputNode(TArray& InPinContainer, const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid) : FRigidDataflowNode(InParam, InGuid) { Connector = MakeUnique>(*this, InPinContainer, 0); } void InitPins() { if(Connector) { Connector->InitPins(); } } TArray AddPins() override { if(Connector) { return Connector->AddPins(); } return Super::AddPins(); } bool CanAddPin() const override { if(Connector) { return Connector->CanAddPin(); } return Super::CanAddPin(); } TArray GetPinsToRemove() const override { if(Connector) { return Connector->GetPinsToRemove(); } return Super::GetPinsToRemove(); } void OnPinRemoved(const UE::Dataflow::FPin& Pin) override { if(Connector) { Connector->OnPinRemoved(Pin); return; } Super::OnPinRemoved(Pin); } bool CanRemovePin() const override { if(Connector) { return Connector->CanRemovePin(); } return Super::CanRemovePin(); } void PostSerialize(const FArchive& Ar) override { if(Connector) { Connector->PostSerialize(Ar); } Super::PostSerialize(Ar); } private: TUniquePtr Connector = nullptr; }; template<> struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 { enum { WithCopy = false }; };