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

320 lines
7.3 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Nodes/DataLinkEdNode.h"
#include "DataLinkEdGraphLog.h"
#include "DataLinkEdGraphSchema.h"
#include "DataLinkNode.h"
#include "DataLinkUtils.h"
#include "EdGraph/EdGraph.h"
#include "Engine/Engine.h"
#include "Templates/SubclassOf.h"
const FLazyName UDataLinkEdNode::MD_InvalidatesNode = TEXT("InvalidatesNode");
const FLazyName UDataLinkEdNode::PN_Output = TEXT("Output");
namespace UE::DataLinkEdGraph::Private
{
bool FindLinkedNode(const UEdGraphPin* InPin, const UDataLinkEdNode*& OutLinkedNode, const UEdGraphPin*& OutLinkedPin)
{
if (!InPin || InPin->LinkedTo.IsEmpty())
{
return false;
}
UEdGraphPin* LinkedPin = InPin->LinkedTo[0];
if (!LinkedPin)
{
return false;
}
if (const UDataLinkEdNode* EdNode = Cast<UDataLinkEdNode>(LinkedPin->GetOwningNode()))
{
OutLinkedNode = EdNode;
OutLinkedPin = LinkedPin;
return true;
}
return false;
}
int32 GetDataPinCount(const UDataLinkEdNode& InEdNode)
{
int32 DataPinCount = 0;
for (UEdGraphPin* Pin : InEdNode.Pins)
{
if (Pin && Pin->PinType.PinCategory == UDataLinkEdGraphSchema::PC_Data)
{
++DataPinCount;
}
}
return DataPinCount;
}
}
void UDataLinkEdNode::SetTemplateNodeClass(TSubclassOf<UDataLinkNode> InNodeClass, bool bInReconstructNode)
{
UObject* Node = TemplateNode;
if (UE::DataLink::ReplaceObject(Node, this, InNodeClass))
{
TemplateNode = Cast<UDataLinkNode>(Node);
if (bInReconstructNode)
{
ReconstructNode();
}
}
}
void UDataLinkEdNode::ForEachPinConnection(TFunctionRef<void(const UEdGraphPin&, const UDataLinkEdNode&, const UEdGraphPin&)> InFunction) const
{
for (const UEdGraphPin* Pin : Pins)
{
const UDataLinkEdNode* LinkedNode;
const UEdGraphPin* LinkedPin;
if (UE::DataLinkEdGraph::Private::FindLinkedNode(Pin, LinkedNode, LinkedPin))
{
InFunction(*Pin, *LinkedNode, *LinkedPin);
}
}
}
bool UDataLinkEdNode::RequiresPinRecreation() const
{
const int32 DataPinCount = UE::DataLinkEdGraph::Private::GetDataPinCount(*this);
if (!TemplateNode)
{
// If a template node is not set, but there are still Data Pins in place, they need to be cleared off
return DataPinCount != 0;
}
TArray<FDataLinkPin> InputPins;
TArray<FDataLinkPin> OutputPins;
TemplateNode->BuildPins(InputPins, OutputPins);
// Require pin recreation if the number of 'data' pins mismatch the total number of input and output pins of the Template
if (DataPinCount != InputPins.Num() + OutputPins.Num())
{
return true;
}
auto HasMismatchingPin = [this](TConstArrayView<FDataLinkPin> InTemplatePins, EEdGraphPinDirection InDirection)
{
for (const FDataLinkPin& TemplatePin : InTemplatePins)
{
const UEdGraphPin* const FoundPin = FindPin(TemplatePin.Name, InDirection);
// Require pin recreation if the found pin that is supposed to match the template pin by name is not a data pin
// or has a mismatching struct
if (!FoundPin
|| FoundPin->PinType.PinCategory != UDataLinkEdGraphSchema::PC_Data
|| FoundPin->PinType.PinSubCategoryObject != TemplatePin.Struct)
{
return true;
}
}
return false;
};
return HasMismatchingPin(InputPins, EGPD_Input) || HasMismatchingPin(OutputPins, EGPD_Output);
}
void UDataLinkEdNode::AutowireNewNode(UEdGraphPin* InFromPin)
{
if (!InFromPin)
{
return;
}
const UEdGraphSchema* const Schema = GetSchema();
if (!Schema)
{
return;
}
UEdGraphPin* TargetPin = nullptr;
// Iterate in reverse so that the most compatible pin end up being the first pins over the last
for (UEdGraphPin* Pin : ReverseIterate(Pins))
{
if (Pin && Schema->ArePinsCompatible(Pin, InFromPin))
{
TargetPin = Pin;
// If the pin names match, the best possible pin has been found
if (Pin->PinName == InFromPin->PinName)
{
break;
}
}
}
if (!TargetPin)
{
return;
}
if (Schema->TryCreateConnection(InFromPin, TargetPin))
{
InFromPin->GetOwningNode()->NodeConnectionListChanged();
}
else if (Schema->TryCreateConnection(TargetPin, InFromPin))
{
NodeConnectionListChanged();
}
}
void UDataLinkEdNode::ReconstructNode()
{
Super::ReconstructNode();
UpdateMetadata();
RecreatePins();
}
bool UDataLinkEdNode::CanCreateUnderSpecifiedSchema(const UEdGraphSchema* InSchema) const
{
return InSchema && InSchema->IsA<UDataLinkEdGraphSchema>();
}
FText UDataLinkEdNode::GetNodeTitle(ENodeTitleType::Type InTitleType) const
{
return NodeMetadata.GetDisplayName();
}
FText UDataLinkEdNode::GetTooltipText() const
{
return NodeMetadata.GetTooltipText();
}
void UDataLinkEdNode::PinConnectionListChanged(UEdGraphPin* InPin)
{
Super::PinConnectionListChanged(InPin);
NotifyNodeChanged();
}
void UDataLinkEdNode::PostLoad()
{
Super::PostLoad();
UpdateMetadata();
}
void UDataLinkEdNode::PostEditUndo()
{
Super::PostEditUndo();
UpdateMetadata();
RecreatePins();
}
void UDataLinkEdNode::PostEditChangeChainProperty(FPropertyChangedChainEvent& InPropertyChangedEvent)
{
Super::PostEditChangeChainProperty(InPropertyChangedEvent);
// Iterate over each property to see if there's a property that invalidates the node and requires node refresh
for (FProperty* Property : InPropertyChangedEvent.PropertyChain)
{
if (Property && Property->HasMetaData(UDataLinkEdNode::MD_InvalidatesNode))
{
UpdateMetadata();
RecreatePins();
break;
}
}
NotifyNodeChanged();
}
void UDataLinkEdNode::UpdateMetadata()
{
NodeMetadata = FDataLinkNodeMetadata();
if (TemplateNode)
{
TemplateNode->BuildMetadata(NodeMetadata);
}
else
{
NodeMetadata
.SetDisplayName(GetClass()->GetDisplayNameText())
.SetTooltipText(GetClass()->GetToolTipText());
}
}
void UDataLinkEdNode::NotifyNodeChanged()
{
if (UEdGraph* Graph = GetGraph())
{
Graph->NotifyNodeChanged(this);
}
}
void UDataLinkEdNode::RecreatePins()
{
Modify(/*bAlwaysMarkDirty*/false);
const TArray<UEdGraphPin*> PinsCopy = Pins;
TArray<UEdGraphPin*> RemovedPins;
RemovedPins.Reserve(PinsCopy.Num());
// Remove all pins related to input data
for (UEdGraphPin* Pin : PinsCopy)
{
if (Pin)
{
Pins.Remove(Pin);
RemovedPins.Add(Pin);
}
}
AllocateDefaultPins();
// Build Data Link Node Pins and create them on the Ed Node
if (TemplateNode)
{
TArray<FDataLinkPin> InputPins;
TArray<FDataLinkPin> OutputPins;
TemplateNode->BuildPins(InputPins, OutputPins);
CreatePins(EGPD_Input, InputPins);
CreatePins(EGPD_Output, OutputPins);
}
// Rewire the pins to remove and matching new pins
if (const UEdGraphSchema* Schema = GetSchema())
{
for (UEdGraphPin* RemovedPin : RemovedPins)
{
RemovedPin->Modify(/*bAlwaysMarkDirty*/false);
if (UEdGraphPin* NewPin = FindPin(RemovedPin->PinName, RemovedPin->Direction))
{
Schema->MovePinLinks(*RemovedPin, *NewPin);
}
RemovedPin->MarkAsGarbage();
OnPinRemoved(RemovedPin);
}
}
// Refresh the UI for the graph so the pin changes show up
NotifyNodeChanged();
}
void UDataLinkEdNode::CreatePins(EEdGraphPinDirection InPinDirection, TConstArrayView<FDataLinkPin> InDataLinkPins)
{
for (const FDataLinkPin& DataLinkPin : InDataLinkPins)
{
// No need to add if Pin already is there
if (!FindPin(DataLinkPin.Name, InPinDirection))
{
UEdGraphPin* const Pin = CreatePin(InPinDirection
, UDataLinkEdGraphSchema::PC_Data
, ConstCast(DataLinkPin.Struct)
, DataLinkPin.Name);
check(Pin);
Pin->PinFriendlyName = DataLinkPin.GetDisplayName();
}
}
}