// 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(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 InNodeClass, bool bInReconstructNode) { UObject* Node = TemplateNode; if (UE::DataLink::ReplaceObject(Node, this, InNodeClass)) { TemplateNode = Cast(Node); if (bInReconstructNode) { ReconstructNode(); } } } void UDataLinkEdNode::ForEachPinConnection(TFunctionRef 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 InputPins; TArray 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 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(); } 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 PinsCopy = Pins; TArray 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 InputPins; TArray 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 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(); } } }