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

2336 lines
63 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "EdGraph/RigVMEdGraphNode.h"
#include "EdGraph/EdGraphPin.h"
#include "EdGraph/RigVMEdGraph.h"
#include "EdGraph/RigVMEdGraphSchema.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Kismet2/KismetDebugUtilities.h"
#include "Kismet2/WatchedPin.h"
#include "KismetCompiler.h"
#include "BlueprintNodeSpawner.h"
#include "BlueprintActionDatabaseRegistrar.h"
#if WITH_RIGVMLEGACYEDITOR
#include "FindInBlueprintManager.h"
#else
#include "RigVMEditor/Private/Editor/Kismet/RigVMBlueprintCompilationManager.h"
#include "RigVMEditor/Private/Editor/Kismet/RigVMFindInBlueprintManager.h"
#endif
#include "RigVMEditorModule.h"
#include "Textures/SlateIcon.h"
#include "RigVMBlueprint.h"
#include "PropertyPathHelpers.h"
#include "RigVMBlueprintUtils.h"
#include "RigVMStringUtils.h"
#include "RigVMCore/RigVMExecuteContext.h"
#include "RigVMFunctions/RigVMDispatch_CastEnum.h"
#include "RigVMFunctions/RigVMDispatch_CastObject.h"
#include "RigVMModel/Nodes/RigVMAggregateNode.h"
#include "RigVMModel/Nodes/RigVMFunctionReferenceNode.h"
#include "RigVMModel/Nodes/RigVMFunctionEntryNode.h"
#include "RigVMModel/Nodes/RigVMFunctionReturnNode.h"
#include "RigVMModel/Nodes/RigVMCollapseNode.h"
#include "RigVMModel/Nodes/RigVMInvokeEntryNode.h"
#include "RigVMModel/Nodes/RigVMDispatchNode.h"
#include "Stats/StatsHierarchical.h"
#include "Styling/AppStyle.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(RigVMEdGraphNode)
#define LOCTEXT_NAMESPACE "RigVMEdGraphNode"
TAutoConsoleVariable<bool> CVarRigVMDisableCompactNodes(TEXT("RigVM.Graph.DisableCompactNodes"), false, TEXT("When true all nodes are going to be drawn as full nodes."));
URigVMEdGraphNode::URigVMEdGraphNode()
: Dimensions(0.0f, 0.0f)
, NodeTitle(FText::GetEmpty())
, FullNodeTitle(FText::GetEmpty())
, bSubTitleEnabled(true)
, NodeTopologyVersion(INDEX_NONE)
, CachedTitleColor(FLinearColor(0.f, 0.f, 0.f, 0.f))
, CachedNodeColor(FLinearColor(0.f, 0.f, 0.f, 0.f))
#if WITH_EDITOR
, bEnableProfiling(false)
#endif
, CachedTemplate(nullptr)
, MicroSeconds(0)
{
bHasCompilerMessage = false;
ErrorType = (int32)EMessageSeverity::Info + 1;
#if WITH_EDITOR
UpdateProfilingSettings();
#endif
}
FText URigVMEdGraphNode::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
if(NodeTitle.IsEmpty())
{
FString SubTitle;
if(URigVMNode* ModelNode = GetModelNode())
{
if (const URigVMUnitNode* UnitNode = Cast<URigVMUnitNode>(ModelNode))
{
const UScriptStruct* ScriptStruct = UnitNode->GetScriptStruct();
if (ScriptStruct && ScriptStruct->IsChildOf(FRigVMStruct::StaticStruct()))
{
if (const TSharedPtr<FStructOnScope> StructOnScope = UnitNode->ConstructStructInstance())
{
const FRigVMStruct* RigVMStruct = (const FRigVMStruct*)StructOnScope->GetStructMemory();
NodeTitle = FText::FromString(RigVMStruct->GetUnitLabel());
SubTitle = RigVMStruct->GetUnitSubTitle();
}
}
}
else if(URigVMFunctionReferenceNode* FunctionReferenceNode = Cast<URigVMFunctionReferenceNode>(ModelNode))
{
const FRigVMGraphFunctionHeader& ReferencedHeader = FunctionReferenceNode->GetReferencedFunctionHeader();
{
TSoftObjectPtr<URigVMFunctionReferenceNode> RefNodePtr(FunctionReferenceNode);
const FString& PackagePath = ReferencedHeader.LibraryPointer.GetNodeSoftPath().GetLongPackageName();
if(PackagePath != RefNodePtr.GetLongPackageName())
{
SubTitle = FString::Printf(TEXT("From %s"), *PackagePath);
}
else
{
static const FString LocalFunctionString = TEXT("Local Function");
SubTitle = LocalFunctionString;
}
}
}
else if(URigVMCollapseNode* CollapseNode = Cast<URigVMCollapseNode>(ModelNode))
{
if(!CollapseNode->IsA<URigVMAggregateNode>())
{
static const FString CollapseNodeString = TEXT("Collapsed Graph");
SubTitle = CollapseNodeString;
}
}
else if(URigVMVariableNode* VariableNode = Cast<URigVMVariableNode>(ModelNode))
{
SubTitle = VariableNode->GetNodeSubTitle();
if (SubTitle.Len() == 0)
{
if(UBlueprint* Blueprint = GetBlueprint())
{
const FName VariableName = VariableNode->GetVariableName();
for(const FBPVariableDescription& NewVariable : Blueprint->NewVariables)
{
if(NewVariable.VarName == VariableName)
{
FString DefaultValue = NewVariable.DefaultValue;
if(DefaultValue.IsEmpty())
{
static const FString VariableString = TEXT("Variable");
SubTitle = VariableString;
}
else
{
// Change the order of values in rotators so that they match the pin order
if (!NewVariable.VarType.IsContainer() && NewVariable.VarType.PinSubCategoryObject == TBaseStructure<FRotator>::Get())
{
TArray<FString> Values;
DefaultValue.ParseIntoArray(Values, TEXT(","));
if (Values.Num() == 3)
{
Values.Swap(0, 1);
Values.Swap(0, 2);
}
DefaultValue = FString::Join(Values, TEXT(","));
}
SubTitle = FString::Printf(TEXT("Default %s"), *DefaultValue);
}
break;
}
}
}
}
if(SubTitle.Len() > 40)
{
SubTitle = SubTitle.Left(36) + TEXT(" ...");
}
}
if (NodeTitle.IsEmpty())
{
NodeTitle = FText::FromString(ModelNode->GetNodeTitle());
}
}
if(IsDeprecated())
{
NodeTitle = FText::FromString(FString::Printf(TEXT("%s (Deprecated)"), *NodeTitle.ToString()));
}
if(IsOutDated())
{
NodeTitle = FText::FromString(FString::Printf(TEXT("%s (OutDated)"), *NodeTitle.ToString()));
}
FullNodeTitle = NodeTitle;
if(!SubTitle.IsEmpty() && bSubTitleEnabled)
{
FullNodeTitle = FText::FromString(FString::Printf(TEXT("%s\n%s"), *NodeTitle.ToString(), *SubTitle));
}
}
if(TitleType == ENodeTitleType::FullTitle)
{
return FullNodeTitle;
}
return NodeTitle;
}
void URigVMEdGraphNode::ReconstructNode()
{
ReconstructNode_Internal();
}
void URigVMEdGraphNode::ReconstructNode_Internal(bool bForce)
{
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
URigVMEdGraph* RigGraph = Cast<URigVMEdGraph>(GetOuter());
if (RigGraph && !bForce)
{
if (RigGraph->bIsTemporaryGraphForCopyPaste)
{
return;
}
}
#if WITH_EDITOR
UpdateProfilingSettings();
#endif
// Clear previously set messages
ClearErrorInfo();
// Move the existing pins to a saved array.
// This way we can reuse them later
LastEdGraphPins = Pins;
// Also make sure to clean up any indirections
// on them.
for(UEdGraphPin* LastEdGraphPin : LastEdGraphPins)
{
LastEdGraphPin->ParentPin = nullptr;
LastEdGraphPin->SubPins.Reset();
}
Pins.Reset();
// Recreate the new pins
ReallocatePinsDuringReconstruction(LastEdGraphPins);
// Maintain watches up to date
if (const URigVMNode* Node = GetModelNode())
{
const UBlueprint* RigVMBlueprint = GetBlueprint();
for (UEdGraphPin* NewPin : Pins)
{
const FString PinName = NewPin->GetName();
FString Left, Right = PinName;
URigVMPin::SplitPinPathAtStart(PinName, Left, Right);
if (URigVMPin* ModelPin = Node->FindPin(Right))
{
if (ModelPin->RequiresWatch())
{
FKismetDebugUtilities::AddPinWatch(RigVMBlueprint, FBlueprintWatchedPin(NewPin));
}
}
}
}
// LogBlueprint: Warning: UEdGraphPin::DestroyImpl BasicIKSetup_0.UpperBone? causing an issue?
// we may need to clean these pins in the opposite order? child pins first?
RewireOldPinsToNewPins(LastEdGraphPins, Pins);
LastEdGraphPins.Reset();
DrawAsCompactNodeCache.Reset();
// Let subclasses do any additional work
PostReconstructNode();
if(bForce)
{
if (RigGraph)
{
RigGraph->NotifyGraphChanged();
}
}
else
{
InvalidateNodeTitle();
OnNodePinsChanged().Broadcast();
}
}
bool URigVMEdGraphNode::IsDeprecated() const
{
// we longer consider nodes to be deprecated ever,
// instead we refer to them as outdated
return false;
}
bool URigVMEdGraphNode::IsOutDated() const
{
if(URigVMNode* ModelNode = GetModelNode())
{
return ModelNode->IsOutDated();
}
return false;
}
FEdGraphNodeDeprecationResponse URigVMEdGraphNode::GetDeprecationResponse(EEdGraphNodeDeprecationType DeprecationType) const
{
FEdGraphNodeDeprecationResponse Response = Super::GetDeprecationResponse(DeprecationType);
if(URigVMNode* ModelNode = GetModelNode())
{
const FString DeprecatedMetadata = ModelNode->GetDeprecatedMetadata();
if (!DeprecatedMetadata.IsEmpty())
{
FFormatNamedArguments Args;
Args.Add(TEXT("DeprecatedMetadata"), FText::FromString(DeprecatedMetadata));
Response.MessageText = FText::Format(LOCTEXT("RigVMEdGraphNodeDeprecationMessage", "Warning: This node is deprecated from: {DeprecatedMetadata}"), Args);
}
}
return Response;
}
void URigVMEdGraphNode::ReallocatePinsDuringReconstruction(const TArray<UEdGraphPin*>& OldPins)
{
AllocateDefaultPins();
}
void URigVMEdGraphNode::RewireOldPinsToNewPins(TArray<UEdGraphPin*>& InOldPins, TArray<UEdGraphPin*>& InNewPins)
{
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
for(UEdGraphPin* OldPin : InOldPins)
{
const FString Name = OldPin->PinName.ToString();
FString Left, Right;
if (!URigVMPin::SplitPinPathAtStart(Name, Left, Right))
{
Left = Name;
}
const FString OrphanName = FString::Printf(TEXT("%s.%s%s"), *Left, URigVMPin::OrphanPinPrefix, *Right);
for(UEdGraphPin* NewPin : InNewPins)
{
if((OldPin->PinName == NewPin->PinName || NewPin->PinName == OrphanName)
&& OldPin->Direction == NewPin->Direction)
{
if (OldPin->PinType == NewPin->PinType ||
OldPin->PinType.PinSubCategoryObject == RigVMTypeUtils::GetWildCardCPPTypeObject() ||
NewPin->PinType.PinSubCategoryObject == RigVMTypeUtils::GetWildCardCPPTypeObject())
{
// make sure to remove invalid entries from the linked to list
OldPin->LinkedTo.Remove(nullptr);
NewPin->MovePersistentDataFromOldPin(*OldPin);
break;
}
}
else
{
continue;
}
}
}
DestroyPinList(InOldPins);
}
void URigVMEdGraphNode::DestroyPinList(TArray<UEdGraphPin*>& InPins)
{
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
const UBlueprint* Blueprint = GetBlueprint();
bool bNotify = false;
if (Blueprint != nullptr)
{
bNotify = !Blueprint->bIsRegeneratingOnLoad;
}
// Throw away the original pins
for (UEdGraphPin* Pin : InPins)
{
// previously serialized graphs potentially contain
// nullptr in the subpins
Pin->SubPins.Remove(nullptr);
Pin->BreakAllPinLinks(bNotify);
Pin->SubPins.Remove(nullptr);
UEdGraphNode::DestroyPin(Pin);
}
}
void URigVMEdGraphNode::PostReconstructNode()
{
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
for (UEdGraphPin* Pin : Pins)
{
SetupPinDefaultsFromModel(Pin);
}
bCanRenameNode = false;
if(IsOutDated())
{
static const FLinearColor WarningColor = FAppStyle::GetColor("ErrorReporting.WarningBackgroundColor");
SetColorFromModel(WarningColor);
}
else if(URigVMNode* ModelNode = GetModelNode())
{
SetColorFromModel(ModelNode->GetNodeColor());
}
}
void URigVMEdGraphNode::SetColorFromModel(const FLinearColor& InColor)
{
static const FLinearColor TitleToNodeColor(0.35f, 0.35f, 0.35f, 1.f);
CachedNodeColor = InColor * TitleToNodeColor;
CachedTitleColor = InColor;
}
void URigVMEdGraphNode::HandleClearArray(FString InPinPath)
{
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
if(URigVMController* Controller = GetController())
{
Controller->ClearArrayPin(InPinPath);
}
}
void URigVMEdGraphNode::HandleAddArrayElement(FString InPinPath)
{
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
if (URigVMController* Controller = GetController())
{
Controller->OpenUndoBracket(TEXT("Add Array Pin"));
FString PinPath = Controller->AddArrayPin(InPinPath, FString(), true, true);
Controller->SetPinExpansion(InPinPath, true);
Controller->SetPinExpansion(PinPath, true);
Controller->CloseUndoBracket();
}
}
void URigVMEdGraphNode::HandleRemoveArrayElement(FString InPinPath)
{
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
if (URigVMController* Controller = GetController())
{
Controller->RemoveArrayPin(InPinPath, true, true);
}
}
void URigVMEdGraphNode::HandleInsertArrayElement(FString InPinPath)
{
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
if (URigVMController* Controller = GetController())
{
if (const URigVMPin* ArrayElementPin = GetModelPinFromPinPath(InPinPath))
{
if (ArrayElementPin->GetParentPin())
{
Controller->OpenUndoBracket(TEXT("Add Array Pin"));
const FString PinPath = Controller->InsertArrayPin(InPinPath, ArrayElementPin->GetPinIndex() + 1, FString(), true, true);
Controller->SetPinExpansion(InPinPath, true);
Controller->SetPinExpansion(PinPath, true);
Controller->CloseUndoBracket();
}
}
}
}
int32 URigVMEdGraphNode::GetInstructionIndex(bool bAsInput) const
{
if (URigVMEdGraph* RigGraph = Cast<URigVMEdGraph>(GetGraph()))
{
return RigGraph->GetInstructionIndex(this, bAsInput);
}
return INDEX_NONE;
}
const FRigVMTemplate* URigVMEdGraphNode::GetTemplate() const
{
if(CachedTemplate == nullptr)
{
const FRigVMRegistry& Registry = FRigVMRegistry::Get();
if(URigVMTemplateNode* TemplateNode = Cast<URigVMTemplateNode>(GetModelNode()))
{
CachedTemplate = TemplateNode->GetTemplate();
}
else if(const FRigVMTemplate* Template = Registry.FindTemplate(*ModelNodePath))
{
CachedTemplate = Template;
}
}
return CachedTemplate;
}
void URigVMEdGraphNode::ClearErrorInfo()
{
bHasCompilerMessage = false;
// SRigVMGraphNode only updates if the error types do not match so we have
// clear the error type as well, see SRigVMGraphNode::RefreshErrorInfo()
ErrorType = (int32)EMessageSeverity::Info + 1;
ErrorMsg = FString();
ErrorMessageHashes.Reset();
}
void URigVMEdGraphNode::AddErrorInfo(const EMessageSeverity::Type& InSeverity, const FString& InMessage)
{
if (ErrorType < InSeverity)
{
return;
}
const uint32 MessageHash = GetTypeHash(InMessage);
if (ErrorType == InSeverity)
{
if (ErrorMessageHashes.Contains(MessageHash))
{
return;
}
ErrorMessageHashes.Add(MessageHash);
ErrorMsg = FString::Printf(TEXT("%s\n%s"), *ErrorMsg, *InMessage);
}
else
{
ErrorMessageHashes.Reset();
ErrorMessageHashes.Add(MessageHash);
ErrorMsg = InMessage;
ErrorType = InSeverity;
}
}
void URigVMEdGraphNode::SetErrorInfo(const EMessageSeverity::Type& InSeverity, const FString& InMessage)
{
ErrorMessageHashes.Reset();
ErrorMessageHashes.Add(GetTypeHash(InMessage));
ErrorMsg = InMessage;
ErrorType = InSeverity;
}
URigVMPin* URigVMEdGraphNode::FindModelPinFromGraphPin(const UEdGraphPin* InGraphPin) const
{
if(InGraphPin == nullptr)
{
return nullptr;
}
for(const auto& Pair : CachedPins)
{
if(Pair.Key.IsValid())
{
if(Pair.Value.InputPin == InGraphPin ||
Pair.Value.OutputPin == InGraphPin)
{
return Pair.Key.Get();
}
}
}
return GetModelPinFromPinPath(InGraphPin->GetName());
}
UEdGraphPin* URigVMEdGraphNode::FindGraphPinFromModelPin(const URigVMPin* InModelPin, bool bAsInput) const
{
if(InModelPin == nullptr)
{
return nullptr;
}
if(const FPinPair* Pair = CachedPins.Find(InModelPin))
{
return bAsInput ? Pair->InputPin : Pair->OutputPin;
}
const FString PinPath = InModelPin->GetPinPath();
for(UEdGraphPin* GraphPin : Pins)
{
if((GraphPin->Direction == EGPD_Input) == bAsInput)
{
if(GraphPin->GetName() == PinPath)
{
return GraphPin;
}
}
}
return nullptr;
}
UEdGraphPin* URigVMEdGraphNode::FindGraphPinFromCategory(const FString& InCategory, bool bAsInput) const
{
if(const FPinPair* Pair = CachedCategoryPins.Find(InCategory))
{
return bAsInput ? Pair->InputPin : Pair->OutputPin;
}
return nullptr;
}
void URigVMEdGraphNode::SynchronizeGraphPinNameWithModelPin(const URigVMPin* InModelPin, bool bNotify)
{
auto SyncGraphPinLambda = [this](const URigVMPin* InModelPin, bool bAsInput) -> bool
{
if(UEdGraphPin* GraphPin = FindGraphPinFromModelPin(InModelPin, bAsInput))
{
const FString OldPinName = GraphPin->PinName.ToString();
const FString NewPinName = InModelPin->GetPinPath();
const FText DisplayName = FText::FromName(InModelPin->GetDisplayName());
if(OldPinName != NewPinName)
{
PinPathToModelPin.Remove(OldPinName);
GraphPin->PinName = *NewPinName;
GraphPin->PinFriendlyName = DisplayName;
PinPathToModelPin.Add(NewPinName, (URigVMPin*)InModelPin);
return true;
}
if(!GraphPin->PinFriendlyName.EqualTo(DisplayName))
{
GraphPin->PinFriendlyName = DisplayName;
return true;
}
}
return false;
};
bool bResult = false;
if(SyncGraphPinLambda(InModelPin, true))
{
bResult = true;
}
if(SyncGraphPinLambda(InModelPin, false))
{
bResult = true;
}
if(bResult)
{
for (const URigVMPin* ModelSubPin : InModelPin->GetSubPins())
{
SynchronizeGraphPinNameWithModelPin(ModelSubPin, false);
}
if(bNotify)
{
// Notify the node widget that the pins have changed.
OnNodePinsChanged().Broadcast();
}
}
}
void URigVMEdGraphNode::SynchronizeGraphPinValueWithModelPin(const URigVMPin* InModelPin)
{
auto SyncGraphPinLambda = [this](const URigVMPin* InModelPin, bool bAsInput)
{
// If the pin has sub-pins, we may need to remove or rebuild the sub-pins.
if (UEdGraphPin* GraphPin = FindGraphPinFromModelPin(InModelPin, bAsInput))
{
SetupPinDefaultsFromModel(GraphPin, InModelPin);
}
};
SyncGraphPinLambda(InModelPin, true);
SyncGraphPinLambda(InModelPin, false);
}
void URigVMEdGraphNode::SynchronizeGraphPinTypeWithModelPin(const URigVMPin* InModelPin)
{
bool bNotify = false;
auto SyncGraphPinLambda = [this, &bNotify](const URigVMPin* InModelPin, bool bAsInput)
{
// If the pin has sub-pins, we may need to remove or rebuild the sub-pins.
if (UEdGraphPin* GraphPin = FindGraphPinFromModelPin(InModelPin, bAsInput))
{
const FEdGraphPinType NewGraphPinType = GetPinTypeForModelPin(InModelPin);
if(NewGraphPinType != GraphPin->PinType)
{
GraphPin->PinType = NewGraphPinType;
ConfigurePin(GraphPin, InModelPin);
// Create new sub-pins, if required, to reflect the new type.
TArray<UEdGraphPin*> GraphSubPinsToKeep;
if (!InModelPin->GetSubPins().IsEmpty())
{
for (const URigVMPin* ModelSubPin : InModelPin->GetSubPins())
{
if(UEdGraphPin* GraphSubPin = FindGraphPinFromModelPin(ModelSubPin, bAsInput))
{
GraphSubPinsToKeep.Add(GraphSubPin);
const FEdGraphPinType NewGraphSubPinType = GetPinTypeForModelPin(ModelSubPin);
if(NewGraphSubPinType != GraphSubPin->PinType)
{
GraphSubPin->PinType = NewGraphSubPinType;
ConfigurePin(GraphSubPin, ModelSubPin);
}
}
else
{
CreateGraphPinFromModelPin(ModelSubPin, GraphPin->Direction, GraphPin);
}
}
}
// If the graph node had other sub-pins, we need to remove those.
RemoveGraphSubPins(GraphPin, GraphSubPinsToKeep);
bNotify = true;
}
}
};
SyncGraphPinLambda(InModelPin, true);
SyncGraphPinLambda(InModelPin, false);
if(bNotify)
{
// Notify the node widget that the pins have changed.
OnNodePinsChanged().Broadcast();
}
}
void URigVMEdGraphNode::SynchronizeGraphPinExpansionWithModelPin(const URigVMPin* InModelPin)
{
OnNodePinExpansionChanged().Broadcast();
}
void URigVMEdGraphNode::SyncGraphNodeTitleWithModelNodeTitle()
{
InvalidateNodeTitle();
}
void URigVMEdGraphNode::SyncGraphNodeNameWithModelNodeName(const URigVMNode* InModelNode)
{
Rename(*InModelNode->GetName());
ModelNodePath = InModelNode->GetNodePath();
SyncGraphNodeTitleWithModelNodeTitle();
TArray<URigVMPin*> AllModelPins = InModelNode->GetAllPinsRecursively();
for(const URigVMPin* ModelPin : AllModelPins)
{
SynchronizeGraphPinNameWithModelPin(ModelPin);
}
}
bool URigVMEdGraphNode::CreateGraphPinFromModelPin(const URigVMPin* InModelPin, EEdGraphPinDirection InDirection,
UEdGraphPin* InParentPin)
{
// don't create output pins for array elements
if(InDirection == EGPD_Output && InModelPin->IsArrayElement() && !InModelPin->GetParentPin()->IsFixedSizeArray())
{
return false;
}
const FString Category = InModelPin->GetCategory();
if(!Category.IsEmpty())
{
(void)CreateGraphPinFromCategory(Category, EGPD_Input);
if(UEdGraphPin* CategoryPin = FindGraphPinFromCategory(Category, true))
{
InParentPin = CategoryPin;
}
}
const FPinPair PairConst = CachedPins.FindOrAdd((URigVMPin*)InModelPin);
auto CreatePinLambda = [this](const URigVMPin* InModelPin, EEdGraphPinDirection InDirection, UEdGraphPin* InParentPin) -> UEdGraphPin*
{
// check if we already have a pin like this in the last pins arrays
const FString PinPath = InModelPin->GetPinPath();
const FEdGraphPinType PinType = GetPinTypeForModelPin(InModelPin);
UEdGraphPin* GraphPin = nullptr;
UEdGraphPin** ExistingEdGraphPinPtr = LastEdGraphPins.FindByPredicate([PinPath, InDirection, PinType](const UEdGraphPin* ExistingPin) -> bool
{
return !ExistingPin->bWasTrashed &&
ExistingPin->GetName() == PinPath &&
ExistingPin->Direction == InDirection &&
ExistingPin->PinType == PinType;
});
if(ExistingEdGraphPinPtr)
{
GraphPin = *ExistingEdGraphPinPtr;
GraphPin->ParentPin = nullptr;
GraphPin->SubPins.Reset();
ExistingEdGraphPinPtr = nullptr;
LastEdGraphPins.Remove(GraphPin);
Pins.Add(GraphPin);
}
if(GraphPin == nullptr)
{
GraphPin = CreatePin(InDirection, GetPinTypeForModelPin(InModelPin), FName(*InModelPin->GetPinPath()));
}
if (GraphPin)
{
ConfigurePin(GraphPin, InModelPin);
if (InParentPin)
{
InParentPin->SubPins.Add(GraphPin);
GraphPin->ParentPin = InParentPin;
}
for(const URigVMPin* ModelSubPin : InModelPin->GetSubPins())
{
CreateGraphPinFromModelPin(ModelSubPin, InDirection, GraphPin);
}
SetupPinDefaultsFromModel(GraphPin, InModelPin);
}
return GraphPin;
};
UEdGraphPin* InputPin = nullptr;
UEdGraphPin* OutputPin = nullptr;
bool bResult = false;
if (InDirection == EGPD_Input && PairConst.InputPin == nullptr)
{
InputPin = CreatePinLambda(InModelPin, EGPD_Input, InParentPin);
bResult = true;
}
if (InDirection == EGPD_Output && PairConst.OutputPin == nullptr)
{
OutputPin = CreatePinLambda(InModelPin, EGPD_Output, InParentPin);
bResult = true;
}
FPinPair& Pair = CachedPins.FindChecked((URigVMPin*)InModelPin);
Pair.InputPin = InputPin != nullptr ? InputPin : Pair.InputPin;
Pair.OutputPin = OutputPin != nullptr ? OutputPin : Pair.OutputPin;
return Pair.IsValid() && bResult;
}
bool URigVMEdGraphNode::CreateGraphPinFromCategory(const FString& InCategory, EEdGraphPinDirection InDirection)
{
const FPinPair PairConst = CachedCategoryPins.FindOrAdd(InCategory);
auto CreatePinLambda = [this](const FString& InCategory, const FString& InCategoryName, EEdGraphPinDirection InDirection, UEdGraphPin* InParentPin) -> UEdGraphPin*
{
// check if we already have a pin like this in the last pins arrays
static const FEdGraphPinType PinType = GetPinTypeForCategoryPin();
UEdGraphPin* GraphPin = nullptr;
UEdGraphPin** ExistingEdGraphPinPtr = LastEdGraphPins.FindByPredicate([InCategory, InDirection](const UEdGraphPin* ExistingPin) -> bool
{
return !ExistingPin->bWasTrashed &&
ExistingPin->GetName() == InCategory &&
ExistingPin->Direction == InDirection &&
ExistingPin->PinType == PinType;
});
if(ExistingEdGraphPinPtr)
{
GraphPin = *ExistingEdGraphPinPtr;
GraphPin->ParentPin = nullptr;
GraphPin->SubPins.Reset();
ExistingEdGraphPinPtr = nullptr;
LastEdGraphPins.Remove(GraphPin);
Pins.Add(GraphPin);
}
if(GraphPin == nullptr)
{
GraphPin = CreatePin(InDirection, PinType, FName(*InCategory));
}
if (GraphPin)
{
GraphPin->bHidden = false;
GraphPin->PinFriendlyName = FText::FromString(InCategoryName);
GraphPin->bNotConnectable = true;
GraphPin->bOrphanedPin = false;
GraphPin->bDisplayAsMutableRef = false;
if (InParentPin)
{
InParentPin->SubPins.Add(GraphPin);
GraphPin->ParentPin = InParentPin;
}
}
return GraphPin;
};
UEdGraphPin* InputPin = nullptr;
UEdGraphPin* OutputPin = nullptr;
UEdGraphPin* ParentPin = nullptr;
FString CategoryName = InCategory;
TArray<FString> Categories;
if(RigVMStringUtils::SplitNodePath(InCategory, Categories))
{
CategoryName = Categories.Pop();
if(Categories.Num() > 0)
{
const FString ParentCategory = RigVMStringUtils::JoinNodePath(Categories);
(void)CreateGraphPinFromCategory(ParentCategory, InDirection);
ParentPin = FindGraphPinFromCategory(ParentCategory, InDirection == EGPD_Input);
}
}
bool bResult = false;
if (InDirection == EGPD_Input && PairConst.InputPin == nullptr)
{
InputPin = CreatePinLambda(InCategory, CategoryName, EGPD_Input, ParentPin);
bResult = true;
}
if (InDirection == EGPD_Output && PairConst.OutputPin == nullptr)
{
OutputPin = CreatePinLambda(InCategory, CategoryName, EGPD_Output, ParentPin);
bResult = true;
}
FPinPair& Pair = CachedCategoryPins.FindChecked(InCategory);
Pair.InputPin = InputPin != nullptr ? InputPin : Pair.InputPin;
Pair.OutputPin = OutputPin != nullptr ? OutputPin : Pair.OutputPin;
return Pair.IsValid() && bResult;
}
void URigVMEdGraphNode::RemoveGraphSubPins(UEdGraphPin* InParentPin, const TArray<UEdGraphPin*>& InPinsToKeep)
{
TArray<UEdGraphPin*> SubPins = InParentPin->SubPins;
TArray<URigVMPin*> ModelSubPins;
TArray<FString> CategorySubPins;
for (const UEdGraphPin* SubPin: SubPins)
{
if(InPinsToKeep.Contains(SubPin))
{
continue;
}
for(const auto& Pair : CachedPins)
{
if(Pair.Key.IsValid())
{
if(Pair.Value.InputPin == SubPin ||
Pair.Value.OutputPin == SubPin)
{
ModelSubPins.Add(Pair.Key.Get());
}
}
}
for(const auto& Pair : CachedCategoryPins)
{
if(Pair.Value.InputPin == SubPin ||
Pair.Value.OutputPin == SubPin)
{
CategorySubPins.Add(Pair.Key);
}
}
}
for(const URigVMPin* ModelSubPin : ModelSubPins)
{
PinPathToModelPin.Remove(ModelSubPin->GetPinPath());
if(const FPinPair* PinPair = CachedPins.Find(ModelSubPin))
{
auto Traverse = [this](UEdGraphPin* SubPin)
{
if(SubPin == nullptr)
{
return;
}
// Remove this pin from our owned pins
Pins.Remove(SubPin);
if (!SubPin->SubPins.IsEmpty())
{
RemoveGraphSubPins(SubPin);
}
SubPin->MarkAsGarbage();
};
Traverse(PinPair->InputPin);
Traverse(PinPair->OutputPin);
}
CachedPins.Remove(ModelSubPin);
}
for(const FString& CategorySubPin : CategorySubPins)
{
PinPathToModelPin.Remove(CategorySubPin);
if(const FPinPair* PinPair = CachedCategoryPins.Find(CategorySubPin))
{
auto Traverse = [this](UEdGraphPin* SubPin)
{
if(SubPin == nullptr)
{
return;
}
// Remove this pin from our owned pins
Pins.Remove(SubPin);
if (!SubPin->SubPins.IsEmpty())
{
RemoveGraphSubPins(SubPin);
}
SubPin->MarkAsGarbage();
};
Traverse(PinPair->InputPin);
Traverse(PinPair->OutputPin);
}
CachedCategoryPins.Remove(CategorySubPin);
}
InParentPin->SubPins.RemoveAll([InPinsToKeep](const UEdGraphPin* Pin) -> bool
{
return !InPinsToKeep.Contains(Pin);
});
}
bool URigVMEdGraphNode::ModelPinsChanged(bool bForce)
{
UpdatePinLists();
// check if any of the pins need to be added / removed
auto AddMissingPins = [this](const TArray<URigVMPin*>& InModelPins)
{
int32 PinsAdded = 0;
for(const URigVMPin* ModelPin : InModelPins)
{
if(FindGraphPinFromModelPin(ModelPin, true) == nullptr &&
FindGraphPinFromModelPin(ModelPin, false))
{
PinsAdded += ModelPinAdded_Internal(ModelPin) ? 1 : 0;
}
}
return PinsAdded;
};
auto AddMissingCategoryPins = [this]()
{
int32 PinsAdded = 0;
if(const URigVMNode* ModelNode = GetModelNode())
{
for(const FString& Category : ModelNode->GetPinCategories())
{
if(FindGraphPinFromCategory(Category, true) == nullptr &&
FindGraphPinFromCategory(Category, false) == nullptr)
{
const TArray<URigVMPin*> PinsForCategory = ModelNode->GetPinsForCategory(Category);
if(!PinsForCategory.IsEmpty())
{
const ERigVMPinDirection PinDirection = PinsForCategory[0]->GetDirection();
if(PinDirection == ERigVMPinDirection::Input ||
PinDirection == ERigVMPinDirection::Visible ||
PinDirection == ERigVMPinDirection::IO)
{
PinsAdded += CategoryPinAdded_Internal(Category, EGPD_Input) ? 1 : 0;
}
if(PinDirection == ERigVMPinDirection::Output ||
PinDirection == ERigVMPinDirection::IO)
{
PinsAdded += CategoryPinAdded_Internal(Category, EGPD_Output) ? 1 : 0;
}
}
}
}
}
return PinsAdded;
};
auto RemoveObsoletePins = [this]()
{
CachedPins = CachedPins.FilterByPredicate([](const TPair<TWeakObjectPtr<URigVMPin>, FPinPair>& InPair) -> bool
{
return InPair.Key.IsValid();
});
TArray<URigVMPin*> PinsToRemove;
for(const auto& Pair : CachedPins)
{
URigVMPin* ModelPin = Pair.Key.Get();
TArray<URigVMPin*>& List = PinListForPin(ModelPin);
bool bRemove = true;
if (URigVMPin** FoundPin = List.FindByKey(ModelPin->GetRootPin()))
{
if (ModelPin->GetParentPin() == nullptr ||
(*FoundPin)->FindSubPin(ModelPin->GetSegmentPath()))
{
bRemove = false;
}
}
if(bRemove)
{
PinsToRemove.Add(ModelPin);
}
}
int32 PinsRemoved = 0;
for(const URigVMPin* ModelPin : PinsToRemove)
{
PinsRemoved += ModelPinRemoved_Internal(ModelPin) ? 1 : 0;
}
if(const URigVMNode* ModelNode = GetModelNode())
{
TArray<FString> Categories;
CachedCategoryPins.GetKeys(Categories);
for(const FString& Category : Categories)
{
if(!ModelNode->GetPinCategories().Contains(Category))
{
PinsRemoved += CategoryPinRemoved_Internal(Category);
}
else if(ModelNode->GetPinsForCategory(Category).IsEmpty())
{
PinsRemoved += CategoryPinRemoved_Internal(Category);
}
}
}
return PinsRemoved;
};
auto OrderPins = [this](const TArray<URigVMPin*>& InModelPins) -> int32
{
if(InModelPins.Num() < 2)
{
return 0;
}
TArray<UEdGraphPin*> OrderedGraphPins;
OrderedGraphPins.Reserve(InModelPins.Num() * 2);
int32 LastInputIndex = INDEX_NONE;
int32 LastOutputIndex = INDEX_NONE;
int32 PinsToReorder = 0;
for(const URigVMPin* ModelPin : InModelPins)
{
if(UEdGraphPin* InputGraphPin = FindGraphPinFromModelPin(ModelPin, true))
{
OrderedGraphPins.Add(InputGraphPin);
const int32 InputPinIndex = Pins.Find(InputGraphPin);
if(LastInputIndex > InputPinIndex)
{
PinsToReorder++;
}
LastInputIndex = InputPinIndex;
}
if(UEdGraphPin* OutputGraphPin = FindGraphPinFromModelPin(ModelPin, false))
{
OrderedGraphPins.Add(OutputGraphPin);
const int32 OutputPinIndex = Pins.Find(OutputGraphPin);
if(LastOutputIndex > OutputPinIndex)
{
PinsToReorder++;
}
LastOutputIndex = OutputPinIndex;
}
}
if(PinsToReorder > 0)
{
for(UEdGraphPin* GraphPinInOrder : OrderedGraphPins)
{
Pins.Remove(GraphPinInOrder);
}
OrderedGraphPins.Append(Pins);
Swap(OrderedGraphPins, Pins);
}
return PinsToReorder;
};
auto ReparentPins = [this](const TArray<URigVMPin*>& InModelPins) -> int32
{
int32 PinsReparented = 0;
for(const URigVMPin* ModelPin : InModelPins)
{
for(int32 Phase = 0; Phase < 2; Phase++)
{
if(UEdGraphPin* EdGraphPin = FindGraphPinFromModelPin(ModelPin, Phase == 0))
{
UEdGraphPin* ParentEdGraphPin = nullptr;
const FString Category = ModelPin->GetCategory();
if(Category.IsEmpty())
{
if(const URigVMPin* ParentModelPin = ModelPin->GetParentPin())
{
ParentEdGraphPin = FindGraphPinFromModelPin(ParentModelPin, Phase == 0);
}
}
else
{
ParentEdGraphPin = FindGraphPinFromCategory(Category, Phase == 0);
}
if(EdGraphPin->ParentPin != ParentEdGraphPin)
{
if(EdGraphPin->ParentPin)
{
EdGraphPin->ParentPin->SubPins.Remove(EdGraphPin);
EdGraphPin->ParentPin = nullptr;
}
if(ParentEdGraphPin)
{
ParentEdGraphPin->SubPins.Add(EdGraphPin);
EdGraphPin->ParentPin = ParentEdGraphPin;
}
PinsReparented++;
}
}
}
}
return PinsReparented;
};
auto ReparentCategoryPins = [this](const TArray<FString>& InCategories) -> int32
{
int32 PinsReparented = 0;
for(const FString& Category : InCategories)
{
for(int32 Phase = 0; Phase < 2; Phase++)
{
if(UEdGraphPin* EdGraphPin = FindGraphPinFromCategory(Category, Phase == 0))
{
UEdGraphPin* ParentEdGraphPin = nullptr;
FString ParentCategory, CategoryName;
if(RigVMStringUtils::SplitNodePathAtEnd(Category, ParentCategory, CategoryName))
{
ParentEdGraphPin = FindGraphPinFromCategory(ParentCategory, Phase == 0);
}
if(EdGraphPin->ParentPin != ParentEdGraphPin)
{
if(EdGraphPin->ParentPin)
{
EdGraphPin->ParentPin->SubPins.Remove(EdGraphPin);
EdGraphPin->ParentPin = nullptr;
}
if(ParentEdGraphPin)
{
ParentEdGraphPin->SubPins.Add(EdGraphPin);
EdGraphPin->ParentPin = ParentEdGraphPin;
}
PinsReparented++;
}
}
}
}
return PinsReparented;
};
int32 PinsAdded = 0;
PinsAdded += AddMissingCategoryPins();
PinsAdded += AddMissingPins(ExecutePins);
PinsAdded += AddMissingPins(OutputPins);
PinsAdded += AddMissingPins(InputOutputPins);
PinsAdded += AddMissingPins(InputPins);
int32 PinsReparented = 0;
if(const URigVMNode* ModelNode = GetModelNode())
{
PinsReparented += ReparentCategoryPins(ModelNode->GetPinCategories());
}
PinsReparented += ReparentPins(InputPins);
PinsReparented += ReparentPins(InputOutputPins);
PinsReparented += ReparentPins(OutputPins);
PinsReparented += ReparentPins(ExecutePins);
const int32 PinsRemoved = RemoveObsoletePins();
// working through it in the opposite order
// due to the use of Append within the lambda
int32 PinsReordered = 0;
PinsReordered += OrderPins(InputPins);
PinsReordered += OrderPins(InputOutputPins);
PinsReordered += OrderPins(OutputPins);
PinsReordered += OrderPins(ExecutePins);
int32 PinLabelsChanged = 0;
auto SyncPinLabel = [](const URigVMPin* InModelPin, const FPinPair& OutPinPair)
{
int32 PinLabelsChanged = 0;
if(InModelPin)
{
const FName DesiredFriendlyName = InModelPin->GetDisplayName();
if(!DesiredFriendlyName.IsNone())
{
const FText DesiredFriendlyNameText = FText::FromName(DesiredFriendlyName);
for(int32 Phase = 0; Phase < 2; Phase++)
{
UEdGraphPin* Pin = Phase == 0 ? OutPinPair.InputPin : OutPinPair.OutputPin;
if(Pin)
{
if(!Pin->PinFriendlyName.EqualTo(DesiredFriendlyNameText))
{
Pin->PinFriendlyName = DesiredFriendlyNameText;
PinLabelsChanged++;
}
}
}
}
}
return PinLabelsChanged;
};
for(const TPair<FString,FPinPair>& Pair : CachedCategoryPins)
{
if(const URigVMPin* ModelPin = FindModelPinFromGraphPin(Pair.Value.InputPin != nullptr ? Pair.Value.InputPin : Pair.Value.OutputPin))
{
PinLabelsChanged += SyncPinLabel(ModelPin, Pair.Value);
}
}
for(const TPair<TWeakObjectPtr<URigVMPin>,FPinPair>& Pair : CachedPins)
{
if(Pair.Key.IsValid())
{
PinLabelsChanged += SyncPinLabel(Pair.Key.Get(), Pair.Value);
}
}
const bool bResult = (PinsAdded > 0) || (PinLabelsChanged > 0) || (PinsRemoved > 0) || (PinsReordered > 0) || (PinsReparented > 0);
if(bResult || bForce)
{
OnNodePinsChanged().Broadcast();
}
return bResult;
}
bool URigVMEdGraphNode::ModelPinAdded(const URigVMPin* InModelPin)
{
if(InModelPin == nullptr)
{
return false;
}
UpdatePinLists();
bool bResult = ModelPinAdded_Internal(InModelPin);
if(bResult)
{
OnNodePinsChanged().Broadcast();
}
return bResult;
}
bool URigVMEdGraphNode::ModelPinAdded_Internal(const URigVMPin* InModelPin)
{
bool bResult = false;
// conversion nodes don't show sub pins
if(!InModelPin->IsRootPin())
{
if(DrawAsCompactNode())
{
return false;
}
}
UEdGraphPin* InputParentPin = FindGraphPinFromModelPin(InModelPin->GetParentPin(), true);
UEdGraphPin* OutputParentPin = FindGraphPinFromModelPin(InModelPin->GetParentPin(), false);
if(InModelPin->GetDirection() == ERigVMPinDirection::Input ||
InModelPin->GetDirection() == ERigVMPinDirection::Visible ||
InModelPin->GetDirection() == ERigVMPinDirection::IO)
{
if(CreateGraphPinFromModelPin(InModelPin, EGPD_Input, InputParentPin))
{
bResult = true;
}
}
if(InModelPin->GetDirection() == ERigVMPinDirection::Output ||
InModelPin->GetDirection() == ERigVMPinDirection::IO)
{
if(CreateGraphPinFromModelPin(InModelPin, EGPD_Output, OutputParentPin))
{
bResult = true;
}
}
return bResult;
}
bool URigVMEdGraphNode::ModelPinRemoved(const URigVMPin* InModelPin)
{
if(InModelPin == nullptr)
{
return false;
}
UpdatePinLists();
const bool bResult = ModelPinRemoved_Internal(InModelPin);
if(bResult)
{
OnNodePinsChanged().Broadcast();
}
return bResult;
}
bool URigVMEdGraphNode::DrawAsCompactNode() const
{
if(CVarRigVMDisableCompactNodes.GetValueOnAnyThread())
{
return false;
}
if(!DrawAsCompactNodeCache.IsSet())
{
DrawAsCompactNodeCache = false;
if(const URigVMTemplateNode* TemplateModelNode = Cast<URigVMTemplateNode>(GetModelNode()))
{
if(TemplateModelNode->GetNotation() == RigVMTypeUtils::GetCastTemplateNotation() ||
TemplateModelNode->GetNotation() == FRigVMDispatch_CastObject().GetTemplateNotation() ||
TemplateModelNode->GetNotation() == FRigVMDispatch_CastIntToEnum().GetTemplateNotation() ||
TemplateModelNode->GetNotation() == FRigVMDispatch_CastEnumToInt().GetTemplateNotation())
{
DrawAsCompactNodeCache = true;
// if the node has links on any subpin - we can't draw it as a compact node.
const TArray<URigVMLink*> Links = TemplateModelNode->GetLinks();
if(Links.ContainsByPredicate([TemplateModelNode](const URigVMLink* Link)
{
if(const URigVMPin* SourcePin = Link->GetSourcePin())
{
if(SourcePin->GetNode() == TemplateModelNode)
{
return !SourcePin->IsRootPin();
}
}
if(const URigVMPin* TargetPin = Link->GetTargetPin())
{
if(TargetPin->GetNode() == TemplateModelNode)
{
return !TargetPin->IsRootPin();
}
}
return false;
}))
{
DrawAsCompactNodeCache = false;
}
// if the node has any injected nodes - we can't draw it as compact
if(TemplateModelNode->GetPins().ContainsByPredicate([](const URigVMPin* Pin)
{
return Pin->HasInjectedNodes();
}))
{
DrawAsCompactNodeCache = false;
}
}
}
}
return DrawAsCompactNodeCache.GetValue();
}
void URigVMEdGraphNode::SetModelNode(URigVMNode* InModelNode)
{
ModelNodePath = InModelNode->GetNodePath();
CachedModelNode = InModelNode;
SyncGraphNodeTitleWithModelNodeTitle();
//AllocateDefaultPins();
ReconstructNode();
//PostReconstructNode();
}
bool URigVMEdGraphNode::ModelPinRemoved_Internal(const URigVMPin* InModelPin)
{
auto RemoveGraphPin = [this](const URigVMPin* InModelPin, bool bAsInput)
{
if(UEdGraphPin* GraphPin = FindGraphPinFromModelPin(InModelPin, bAsInput))
{
RemoveGraphSubPins(GraphPin);
Pins.Remove(GraphPin);
GraphPin->MarkAsGarbage();
}
};
RemoveGraphPin(InModelPin, true);
RemoveGraphPin(InModelPin, false);
const bool bResult = PinPathToModelPin.Remove(InModelPin->GetPinPath()) > 0;
CachedPins.Remove(InModelPin);
return bResult;
}
bool URigVMEdGraphNode::CategoryPinAdded_Internal(const FString& InCategory, EEdGraphPinDirection InDirection)
{
return CreateGraphPinFromCategory(InCategory, InDirection);
}
bool URigVMEdGraphNode::CategoryPinRemoved_Internal(const FString& InCategory)
{
auto RemoveGraphPin = [this](const FString& InCategory, bool bAsInput)
{
if(UEdGraphPin* GraphPin = FindGraphPinFromCategory(InCategory, bAsInput))
{
check(GraphPin->SubPins.IsEmpty());
Pins.Remove(GraphPin);
GraphPin->MarkAsGarbage();
}
};
RemoveGraphPin(InCategory, true);
RemoveGraphPin(InCategory, false);
const bool bResult = PinPathToModelPin.Remove(InCategory) > 0;
CachedCategoryPins.Remove(InCategory);
return bResult;
}
FLinearColor URigVMEdGraphNode::GetNodeProfilingColor() const
{
#if WITH_EDITOR
if(bEnableProfiling)
{
if(URigVMBlueprint* Blueprint = GetTypedOuter<URigVMBlueprint>())
{
if(URigVMHost* DebuggedHost = Cast<URigVMHost>(Blueprint->GetObjectBeingDebugged()))
{
if(const URigVMNode* ModelNode = GetModelNode())
{
const double CurrentMicroSeconds = ModelNode->GetInstructionMicroSeconds(DebuggedHost->GetRigVMExtendedExecuteContext(), DebuggedHost->GetVM(), FRigVMASTProxy());
MicroSeconds = Blueprint->RigGraphDisplaySettings.AggregateAverage(MicroSecondsFrames, MicroSeconds, CurrentMicroSeconds);
if(MicroSeconds >= 0.0)
{
if(Blueprint->RigGraphDisplaySettings.bAutoDetermineRange)
{
if(CurrentMicroSeconds < Blueprint->RigGraphDisplaySettings.MinMicroSeconds)
{
Blueprint->RigGraphDisplaySettings.MinMicroSeconds = CurrentMicroSeconds;
}
if(CurrentMicroSeconds > Blueprint->RigGraphDisplaySettings.MaxMicroSeconds)
{
Blueprint->RigGraphDisplaySettings.MaxMicroSeconds = CurrentMicroSeconds;
}
}
const double MinMicroSeconds = Blueprint->RigGraphDisplaySettings.LastMinMicroSeconds;
const double MaxMicroSeconds = Blueprint->RigGraphDisplaySettings.LastMaxMicroSeconds;
if(MaxMicroSeconds <= MinMicroSeconds)
{
return FLinearColor::Black;
}
const FLinearColor& MinColor = Blueprint->RigGraphDisplaySettings.MinDurationColor;
const FLinearColor& MaxColor = Blueprint->RigGraphDisplaySettings.MaxDurationColor;
const double T = (MicroSeconds - MinMicroSeconds) / (MaxMicroSeconds - MinMicroSeconds);
return FMath::Lerp<FLinearColor>(MinColor, MaxColor, (float)T);
}
}
}
}
}
else
{
MicroSeconds = 0;
MicroSecondsFrames.Reset();
}
#endif
return FLinearColor::Black;
}
void URigVMEdGraphNode::UpdatePinLists()
{
ExecutePins.Reset();
OutputPins.Reset();
InputOutputPins.Reset();
InputPins.Reset();
if (const URigVMNode* ModelNode = GetModelNode())
{
for(int32 PinListIndex=0; PinListIndex<2; PinListIndex++)
{
const TArray<URigVMPin*>& ModelPins = PinListIndex == 0 ? ModelNode->GetPins() : ModelNode->GetOrphanedPins();
for (URigVMPin* ModelPin : ModelPins)
{
if (ModelPin->ShowInDetailsPanelOnly())
{
continue;
}
if (ModelPin->GetDirection() == ERigVMPinDirection::IO)
{
if (ModelPin->IsStruct())
{
const UScriptStruct* ScriptStruct = ModelPin->GetScriptStruct();
if (ScriptStruct && ScriptStruct->IsChildOf(FRigVMExecutePin::StaticStruct()))
{
ExecutePins.Add(ModelPin);
continue;
}
}
InputOutputPins.Add(ModelPin);
}
else if (ModelPin->GetDirection() == ERigVMPinDirection::Input ||
ModelPin->GetDirection() == ERigVMPinDirection::Visible)
{
InputPins.Add(ModelPin);
}
else if (ModelPin->GetDirection() == ERigVMPinDirection::Output)
{
OutputPins.Add(ModelPin);
}
}
}
}
}
void URigVMEdGraphNode::AllocateDefaultPins()
{
UpdatePinLists();
CachedPins.Reset();
CachedCategoryPins.Reset();
PinPathToModelPin.Reset();
auto CreateGraphPins = [this](const TArray<URigVMPin*>& InModelPins)
{
for (const URigVMPin* ModelPin : InModelPins)
{
ModelPinAdded_Internal(ModelPin);
}
};
CreateGraphPins(ExecutePins);
CreateGraphPins(OutputPins);
CreateGraphPins(InputOutputPins);
CreateGraphPins(InputPins);
// Fill the variable list
ExternalVariables.Reset();
if (URigVMFunctionReferenceNode* FunctionReferenceNode = Cast<URigVMFunctionReferenceNode>(GetModelNode()))
{
if(FunctionReferenceNode->RequiresVariableRemapping())
{
TArray<FRigVMExternalVariable> CurrentExternalVariables = FunctionReferenceNode->GetExternalVariables(false);
for(const FRigVMExternalVariable& CurrentExternalVariable : CurrentExternalVariables)
{
ExternalVariables.Add(MakeShared<FRigVMExternalVariable>(CurrentExternalVariable));
}
}
}
}
UClass* URigVMEdGraphNode::GetRigVMGeneratedClass() const
{
if(const URigVMBlueprint* Blueprint = GetTypedOuter<URigVMBlueprint>())
{
if (Blueprint->GeneratedClass)
{
check(Blueprint->GeneratedClass->IsChildOf(URigVMHost::StaticClass()));
return Blueprint->GeneratedClass;
}
}
return nullptr;
}
UClass* URigVMEdGraphNode::GetRigVMSkeletonGeneratedClass() const
{
if(const URigVMBlueprint* Blueprint = GetTypedOuter<URigVMBlueprint>())
{
if (Blueprint->SkeletonGeneratedClass)
{
check(Blueprint->SkeletonGeneratedClass->IsChildOf(URigVMHost::StaticClass()));
return Blueprint->SkeletonGeneratedClass;
}
}
return nullptr;
}
FLinearColor URigVMEdGraphNode::GetNodeOpacityColor() const
{
if (URigVMNode* ModelNode = GetModelNode())
{
if (Cast<URigVMVariableNode>(ModelNode))
{
return FLinearColor::White;
}
if(GetInstructionIndex(true) == INDEX_NONE)
{
return FLinearColor(0.35f, 0.35f, 0.35f, 0.35f);
}
}
return FLinearColor::White;
}
FLinearColor URigVMEdGraphNode::GetNodeTitleColor() const
{
// return a darkened version of the default node's color
return CachedTitleColor * GetNodeOpacityColor();
}
FLinearColor URigVMEdGraphNode::GetNodeBodyTintColor() const
{
#if WITH_EDITOR
if(bEnableProfiling)
{
return GetNodeProfilingColor();
}
#endif
return CachedNodeColor * GetNodeOpacityColor();
}
bool URigVMEdGraphNode::ShowPaletteIconOnNode() const
{
if (URigVMNode* ModelNode = GetModelNode())
{
return ModelNode->IsEvent() ||
ModelNode->IsA<URigVMInvokeEntryNode>() ||
ModelNode->IsA<URigVMFunctionInterfaceNode>() ||
ModelNode->IsA<URigVMFunctionReferenceNode>() ||
ModelNode->IsA<URigVMCollapseNode>() ||
ModelNode->IsA<URigVMUnitNode>() ||
ModelNode->IsA<URigVMDispatchNode>() ||
ModelNode->IsLoopNode();
}
return false;
}
FSlateIcon URigVMEdGraphNode::GetIconAndTint(FLinearColor& OutColor) const
{
OutColor = FLinearColor::White;
static FSlateIcon FunctionIcon(FAppStyle::GetAppStyleSetName(), "Kismet.AllClasses.FunctionIcon");
static FSlateIcon EventIcon(FAppStyle::GetAppStyleSetName(), "GraphEditor.Event_16x");
static FSlateIcon EntryReturnIcon(FAppStyle::GetAppStyleSetName(), "GraphEditor.Default_16x");
static FSlateIcon CollapsedNodeIcon(FAppStyle::GetAppStyleSetName(), "GraphEditor.SubGraph_16x");
static FSlateIcon TemplateNodeIcon("RigVMEditorStyle", "RigVM.Template");
if (URigVMNode* ModelNode = GetModelNode())
{
if (ModelNode->IsEvent() || ModelNode->IsA<URigVMInvokeEntryNode>())
{
return EventIcon;
}
URigVMNode* InnerNode = ModelNode;
while(const URigVMAggregateNode* AggregateNode = Cast<URigVMAggregateNode>(InnerNode))
{
InnerNode = AggregateNode->GetFirstInnerNode();
}
if (InnerNode)
{
ModelNode = InnerNode;
}
else
{
if(!ModelNode->HasAnyFlags(RF_NeedLoad))
{
const FString Message = FString::Printf(TEXT("Could not find first inner node in aggregate node %s in package %s"), *ModelNode->GetPathName(), *GetPackage()->GetPathName());
FScriptExceptionHandler::Get().HandleException(ELogVerbosity::Warning, *Message, *FString());
}
}
if (ModelNode->IsA<URigVMFunctionReferenceNode>())
{
return FunctionIcon;
}
if (ModelNode->IsA<URigVMCollapseNode>())
{
return CollapsedNodeIcon;
}
if (ModelNode->IsA<URigVMFunctionInterfaceNode>())
{
return EntryReturnIcon;
}
const UScriptStruct* MetadataScriptStruct = nullptr;
if (const URigVMUnitNode* UnitNode = Cast<URigVMUnitNode>(ModelNode))
{
MetadataScriptStruct = UnitNode->GetScriptStruct();
}
else if (const URigVMDispatchNode* DispatchNode = Cast<URigVMDispatchNode>(ModelNode))
{
if (const FRigVMDispatchFactory* Factory = DispatchNode->GetFactory())
{
MetadataScriptStruct = Factory->GetScriptStruct();
}
}
if(MetadataScriptStruct && MetadataScriptStruct->HasMetaDataHierarchical(FRigVMStruct::IconMetaName))
{
FString IconPath;
const int32 NumOfIconPathNames = 4;
FName IconPathNames[NumOfIconPathNames] = {
NAME_None, // StyleSetName
NAME_None, // StyleName
NAME_None, // SmallStyleName
NAME_None // StatusOverlayStyleName
};
// icon path format: StyleSetName|StyleName|SmallStyleName|StatusOverlayStyleName
// the last two names are optional, see FSlateIcon() for reference
MetadataScriptStruct->GetStringMetaDataHierarchical(FRigVMStruct::IconMetaName, &IconPath);
int32 NameIndex = 0;
while (!IconPath.IsEmpty() && NameIndex < NumOfIconPathNames)
{
FString Left;
FString Right;
if (!IconPath.Split(TEXT("|"), &Left, &Right))
{
Left = IconPath;
}
IconPathNames[NameIndex] = FName(*Left);
NameIndex++;
IconPath = Right;
}
return FSlateIcon(IconPathNames[0], IconPathNames[1], IconPathNames[2], IconPathNames[3]);
}
if (const URigVMTemplateNode* TemplateNode = Cast<URigVMTemplateNode>(ModelNode))
{
if(const FRigVMTemplate* Template = TemplateNode->GetTemplate())
{
if(Template->NumPermutations() > 1)
{
return TemplateNodeIcon;
}
}
}
}
return FunctionIcon;
}
void URigVMEdGraphNode::GetNodeContextMenuActions(class UToolMenu* Menu, class UGraphNodeContextMenuContext* Context) const
{
#if WITH_EDITOR
const URigVMEdGraphSchema* Schema = Cast<URigVMEdGraphSchema>(GetSchema());
if(const URigVMBlueprint* Blueprint = GetBlueprint())
{
return Blueprint->GetEditorModule()->GetContextMenuActions(Schema, Menu, Context);
}
IRigVMEditorModule::Get().GetContextMenuActions(Schema, Menu, Context);
#endif
}
bool URigVMEdGraphNode::IsPinExpanded(const FString& InPinPath)
{
if (URigVMPin* ModelPin = GetModelPinFromPinPath(InPinPath))
{
return ModelPin->IsExpanded();
}
return false;
}
void URigVMEdGraphNode::DestroyNode()
{
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
if(URigVMEdGraph* Graph = Cast<URigVMEdGraph>(GetOuter()))
{
BreakAllNodeLinks();
URigVMBlueprint* RigVMBlueprint = Cast<URigVMBlueprint>(Graph->GetOuter());
if(RigVMBlueprint)
{
if(PropertyName_DEPRECATED.IsValid())
{
FRigVMBlueprintUtils::RemoveMemberVariableIfNotUsed(RigVMBlueprint, PropertyName_DEPRECATED);
}
}
}
UEdGraphNode::DestroyNode();
}
void URigVMEdGraphNode::PinDefaultValueChanged(UEdGraphPin* Pin)
{
CopyPinDefaultsToModel(Pin, true, true);
}
void URigVMEdGraphNode::CopyPinDefaultsToModel(UEdGraphPin* Pin, bool bUndo, bool bPrintPythonCommand)
{
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
if (Pin->Direction != EGPD_Input)
{
return;
}
if (const URigVMPin* ModelPin = FindModelPinFromGraphPin(Pin))
{
if (ModelPin->GetSubPins().Num() > 0)
{
return;
}
FString DefaultValue = Pin->DefaultValue;
if(DefaultValue.IsEmpty() && (
Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Object ||
Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Class ||
Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_SoftObject ||
Pin->PinType.PinCategory == UEdGraphSchema_K2::AllObjectTypes ||
Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Interface
))
{
if(Pin->DefaultObject)
{
DefaultValue = Pin->DefaultObject->GetPathName();
}
}
if (DefaultValue == FName(NAME_None).ToString() && Pin->PinType.PinSubCategory == UEdGraphSchema_K2::PC_Name)
{
DefaultValue = FString();
}
if (ModelPin->GetDefaultValue() != DefaultValue)
{
if (URigVMController* Controller = GetController())
{
Controller->SetPinDefaultValue(ModelPin->GetPinPath(), DefaultValue, false, true, false, bPrintPythonCommand);
}
}
}
}
URigVMBlueprint* URigVMEdGraphNode::GetBlueprint() const
{
if(const URigVMEdGraph* Graph = Cast<URigVMEdGraph>(GetOuter()))
{
return Graph->GetBlueprint();
}
return nullptr;
}
URigVMGraph* URigVMEdGraphNode::GetModel() const
{
if (URigVMEdGraph* Graph = Cast<URigVMEdGraph>(GetOuter()))
{
return Graph->GetModel();
}
return nullptr;
}
URigVMController* URigVMEdGraphNode::GetController() const
{
if (URigVMEdGraph* Graph = Cast<URigVMEdGraph>(GetOuter()))
{
return Graph->GetController();
}
return nullptr;
}
URigVMNode* URigVMEdGraphNode::GetModelNode() const
{
URigVMEdGraphNode* MutableThis = (URigVMEdGraphNode*)this;
if (CachedModelNode.IsValid())
{
if (CachedModelNode.Get()->GetOuter() == GetTransientPackage())
{
MutableThis->CachedModelNode.Reset();
}
else
{
return CachedModelNode.Get();
}
}
if (URigVMEdGraph* Graph = Cast<URigVMEdGraph>(GetOuter()))
{
if (URigVMGraph* Model = GetModel())
{
MutableThis->CachedModelNode = TWeakObjectPtr<URigVMNode>(Model->FindNode(ModelNodePath));
return MutableThis->CachedModelNode.Get();
}
}
return nullptr;
}
FName URigVMEdGraphNode::GetModelNodeName() const
{
if (URigVMNode* ModelNode = GetModelNode())
{
return ModelNode->GetFName();
}
return NAME_None;
}
const FString& URigVMEdGraphNode::GetModelNodePath() const
{
return ModelNodePath;
}
URigVMPin* URigVMEdGraphNode::GetModelPinFromPinPath(const FString& InPinPath) const
{
if (TWeakObjectPtr<URigVMPin> const* CachedModelPinPtr = PinPathToModelPin.Find(InPinPath))
{
if(CachedModelPinPtr->IsValid())
{
URigVMPin* CachedModelPin = CachedModelPinPtr->Get();
if (!CachedModelPin->HasAnyFlags(RF_Transient) && CachedModelPin->GetNode())
{
return CachedModelPin;
}
}
}
if (const URigVMNode* ModelNode = GetModelNode())
{
const FString PinPath = InPinPath.RightChop(ModelNode->GetNodePath().Len() + 1);
URigVMPin* ModelPin = ModelNode->FindPin(PinPath);
if (ModelPin)
{
URigVMEdGraphNode* MutableThis = (URigVMEdGraphNode*)this;
MutableThis->PinPathToModelPin.FindOrAdd(InPinPath) = ModelPin;
}
return ModelPin;
}
return nullptr;
}
void URigVMEdGraphNode::HandleAddAggregateElement(const FString& InNodePath)
{
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
if (URigVMController* Controller = GetController())
{
Controller->AddAggregatePin(InNodePath, FString(), FString(), true, true);
}
}
void URigVMEdGraphNode::SetupPinDefaultsFromModel(UEdGraphPin* Pin, const URigVMPin* InModelPin)
{
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
if (Pin->Direction != EGPD_Input)
{
return;
}
if(InModelPin == nullptr)
{
InModelPin = FindModelPinFromGraphPin(Pin);
}
// remove stale subpins
Pin->SubPins.Remove(nullptr);
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
if (InModelPin && IsValid(InModelPin))
{
if (InModelPin->GetSubPins().Num() > 0)
{
return;
}
FString DefaultValueString = InModelPin->GetDefaultValue();
if (DefaultValueString.IsEmpty() && InModelPin->GetCPPType() == TEXT("FName"))
{
DefaultValueString = FName(NAME_None).ToString();
}
K2Schema->GetPinDefaultValuesFromString(Pin->PinType, Pin->GetOwningNodeUnchecked(), DefaultValueString, Pin->DefaultValue, Pin->DefaultObject, Pin->DefaultTextValue);
}
}
FText URigVMEdGraphNode::GetTooltipText() const
{
if(URigVMNode* ModelNode = GetModelNode())
{
FText Tooltip = ModelNode->GetToolTipText();
if(IsOutDated())
{
return FText::Format(
LOCTEXT("OutDatedTooltipFormat", "This node is outdated and can potentially be upgraded.\nFor this right click and choose 'Upgrade Nodes'.\n\n{0}"),
Tooltip);
}
return Tooltip;;
}
return FText::FromString(ModelNodePath);
}
void URigVMEdGraphNode::InvalidateNodeTitle() const
{
NodeTitle = FText();
FullNodeTitle = FText();
NodeTitleDirtied.Broadcast();
}
bool URigVMEdGraphNode::CanCreateUnderSpecifiedSchema(const UEdGraphSchema* InSchema) const
{
return InSchema->IsA<URigVMEdGraphSchema>();
}
void URigVMEdGraphNode::AutowireNewNode(UEdGraphPin* FromPin)
{
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
Super::AutowireNewNode(FromPin);
const URigVMEdGraphSchema* Schema = GetDefault<URigVMEdGraphSchema>();
Schema->AutowireNewNode(this, FromPin);
}
bool URigVMEdGraphNode::IsSelectedInEditor() const
{
URigVMNode* ModelNode = GetModelNode();
if (ModelNode)
{
return ModelNode->IsSelected();
}
return false;
}
bool URigVMEdGraphNode::ShouldDrawNodeAsControlPointOnly(int32& OutInputPinIndex, int32& OutOutputPinIndex) const
{
if (Cast<URigVMRerouteNode>(GetModelNode()))
{
OutInputPinIndex = 0;
OutOutputPinIndex = Pins.Num()/2;
if (Pins[OutOutputPinIndex]->Direction != EGPD_Output || Pins[OutOutputPinIndex]->ParentPin != nullptr)
{
for (int32 i=0; i<Pins.Num(); ++i)
{
if (Pins[i]->Direction == EGPD_Output && Pins[i]->ParentPin == nullptr)
{
OutOutputPinIndex = i;
break;
}
}
}
return true;
}
return false;
}
void URigVMEdGraphNode::BeginDestroy()
{
for(UEdGraphPin* Pin : Pins)
{
Pin->SubPins.Remove(nullptr);
}
Super::BeginDestroy();
}
#if WITH_EDITOR
#if WITH_RIGVMLEGACYEDITOR
void URigVMEdGraphNode::AddPinSearchMetaDataInfo(const UEdGraphPin* Pin, TArray<FSearchTagDataPair>& OutTaggedMetaData) const
{
Super::AddPinSearchMetaDataInfo(Pin, OutTaggedMetaData);
if(const URigVMPin* ModelPin = FindModelPinFromGraphPin(Pin))
{
OutTaggedMetaData.Emplace(FRigVMSearchTags::FiB_PinCategory, FText::FromName(Pin->PinType.PinCategory));
OutTaggedMetaData.Emplace(FRigVMSearchTags::FiB_PinSubCategory, FText::FromName(Pin->PinType.PinSubCategory));
if (Pin->PinType.PinSubCategoryObject.IsValid())
{
OutTaggedMetaData.Emplace(FRigVMSearchTags::FiB_ObjectClass, FText::FromString(Pin->PinType.PinSubCategoryObject->GetPathName()));
}
OutTaggedMetaData.Emplace(FRigVMSearchTags::FiB_IsArray, FText::Format(LOCTEXT("RigVMNodePinIsArray", "{0}"), Pin->PinType.IsArray() ? 1 : 0));
if (ModelPin->IsBoundToVariable())
{
OutTaggedMetaData.Add(FSearchTagDataPair(FRigVMSearchTags::FiB_PinBinding, FText::FromString(ModelPin->GetBoundVariablePath())));
}
}
}
#else
void URigVMEdGraphNode::AddRigVMSearchMetaDataInfo(TArray<struct UBlueprintExtension::FSearchTagDataPair>& OutTaggedMetaData) const
{
// Copied from EdGraphNode::AddSearchMetaDataInfo
if (GetSchema() == nullptr)
{
ensure(false);
return;
}
// Searchable - Primary label for the item in the search results
// OutTaggedMetaData.Add(UBlueprintExtension::FSearchTagDataPair(FRigVMFindInBlueprintSearchTags::FiB_Name, GetNodeTitle(ENodeTitleType::ListView)));
// Searchable - As well as being searchable, this displays in the tooltip for the node
// OutTaggedMetaData.Add(UBlueprintExtension::FSearchTagDataPair(FRigVMFindInBlueprintSearchTags::FiB_ClassName, FText::FromString(GetClass()->GetName())));
// Non-searchable - Used to lookup the node when attempting to jump to it
// OutTaggedMetaData.Add(UBlueprintExtension::FSearchTagDataPair(FRigVMFindInBlueprintSearchTags::FiB_NodeGuid, FText::FromString(NodeGuid.ToString(EGuidFormats::Digits))));
// Non-searchable - Important for matching pin types with icons and colors, stored here so that each pin does not store it
// OutTaggedMetaData.Add(UBlueprintExtension::FSearchTagDataPair(FRigVMFindInBlueprintSearchTags::FiB_SchemaName, FText::FromString(GetSchema()->GetClass()->GetName())));
// Non-Searchable - Used to display the icon and color for this node for better visual identification.
FLinearColor GlyphColor = FLinearColor::White;
FSlateIcon Icon = GetIconAndTint(GlyphColor);
// OutTaggedMetaData.Add(UBlueprintExtension::FSearchTagDataPair(FRigVMFindInBlueprintSearchTags::FiB_Glyph, FText::FromName(Icon.GetStyleName())));
// OutTaggedMetaData.Add(UBlueprintExtension::FSearchTagDataPair(FRigVMFindInBlueprintSearchTags::FiB_GlyphStyleSet, FText::FromName(Icon.GetStyleSetName())));
// OutTaggedMetaData.Add(UBlueprintExtension::FSearchTagDataPair(FRigVMFindInBlueprintSearchTags::FiB_GlyphColor, FText::FromString(GlyphColor.ToString())));
// OutTaggedMetaData.Add(UBlueprintExtension::FSearchTagDataPair(FRigVMFindInBlueprintSearchTags::FiB_Comment, FText::FromString(NodeComment)));
}
void URigVMEdGraphNode::AddRigVMPinSearchMetaDataInfo(const UEdGraphPin* Pin, TArray<UBlueprintExtension::FSearchTagDataPair>& OutTaggedMetaData) const
{
// Copied from EdGraphNode::AddPinSearchMetaDataInfo
{
//Super::AddPinSearchMetaDataInfo(Pin, OutTaggedMetaData);
// Searchable - Primary label for the item in the search results
// OutTaggedMetaData.Add(UBlueprintExtension::FSearchTagDataPair(FRigVMFindInBlueprintSearchTags::FiB_Name, Pin->GetSchema()->GetPinDisplayName(Pin)));
// Searchable - The pin's default value as a text string
// OutTaggedMetaData.Add(UBlueprintExtension::FSearchTagDataPair(FRigVMFindInBlueprintSearchTags::FiB_DefaultValue, Pin->GetDefaultAsText()));
}
if(const URigVMPin* ModelPin = FindModelPinFromGraphPin(Pin))
{
OutTaggedMetaData.Emplace(FText::FromString(TEXT("Data Type")), FText::FromString(ModelPin->GetCPPType()));
if (ModelPin->IsBoundToVariable())
{
OutTaggedMetaData.Add(UBlueprintExtension::FSearchTagDataPair(FText::FromString(TEXT("Binding")), FText::FromString(ModelPin->GetBoundVariablePath())));
}
}
}
#endif
#endif
FEdGraphPinType URigVMEdGraphNode::GetPinTypeForModelPin(const URigVMPin* InModelPin)
{
FEdGraphPinType PinType = RigVMTypeUtils::PinTypeFromCPPType(*InModelPin->GetCPPType(), InModelPin->GetCPPTypeObject());
PinType.bIsConst = InModelPin->IsDefinedAsConstant();
return PinType;
}
FEdGraphPinType URigVMEdGraphNode::GetPinTypeForCategoryPin()
{
static const FEdGraphPinType PinType = RigVMTypeUtils::PinTypeFromCPPType(NAME_None, FRigVMPinCategory::StaticStruct());
return PinType;
}
void URigVMEdGraphNode::ConfigurePin(UEdGraphPin* EdGraphPin, const URigVMPin* ModelPin) const
{
const bool bConnectable =
ModelPin->GetDirection() == ERigVMPinDirection::Input ||
ModelPin->GetDirection() == ERigVMPinDirection::Output ||
ModelPin->GetDirection() == ERigVMPinDirection::IO;
// hide sub-pins on a compacted (knot) reroute node
bool bHidden = ModelPin->GetDirection() == ERigVMPinDirection::Hidden;
if(!bHidden && !ModelPin->IsRootPin())
{
if(Cast<URigVMRerouteNode>(ModelPin->GetNode()))
{
bHidden = true;
}
}
EdGraphPin->bHidden = bHidden;
EdGraphPin->PinFriendlyName = FText::FromName(ModelPin->GetDisplayName());
EdGraphPin->bNotConnectable = !bConnectable || ModelPin->IsFixedSizeArray();
EdGraphPin->bOrphanedPin = ModelPin->IsOrphanPin() ? 1 : 0;
EdGraphPin->bDisplayAsMutableRef = ModelPin->IsWildCard();
}
TArray<URigVMPin*>& URigVMEdGraphNode::PinListForPin(const URigVMPin* InModelPin)
{
if(IsValid(InModelPin))
{
if(InModelPin->IsExecuteContext() && InModelPin->GetDirection() == ERigVMPinDirection::IO)
{
return ExecutePins;
}
if(InModelPin->GetDirection() == ERigVMPinDirection::Input || InModelPin->GetDirection() == ERigVMPinDirection::Visible)
{
return InputPins;
}
if(InModelPin->GetDirection() == ERigVMPinDirection::IO)
{
return InputOutputPins;
}
if(InModelPin->GetDirection() == ERigVMPinDirection::Output)
{
return OutputPins;
}
checkNoEntry();
}
static TArray<URigVMPin*> EmptyList;
return EmptyList;
}
#if WITH_EDITOR
void URigVMEdGraphNode::UpdateProfilingSettings()
{
bEnableProfiling = false;
MicroSeconds = 0;
MicroSecondsFrames.Reset();
if(const URigVMEdGraph* RigGraph = Cast<URigVMEdGraph>(GetOuter()))
{
if(const URigVMBlueprint* RigBlueprint = RigGraph->GetBlueprint())
{
bEnableProfiling = RigBlueprint->VMRuntimeSettings.bEnableProfiling;
}
}
}
#endif
FString URigVMEdGraphNode::GetPinMetaData(FName InPinName, FName InKey)
{
URigVMNode* ModelNode = GetModelNode();
if(ModelNode == nullptr)
{
return FString();
}
FString Left, Right = InPinName.ToString();
URigVMPin::SplitPinPathAtStart(InPinName.ToString(), Left, Right);
URigVMPin* Pin = ModelNode->FindPin(Right);
if(Pin == nullptr)
{
return FString();
}
return Pin->GetMetaData(InKey);
}
#undef LOCTEXT_NAMESPACE