// 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 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(ModelNode)) { const UScriptStruct* ScriptStruct = UnitNode->GetScriptStruct(); if (ScriptStruct && ScriptStruct->IsChildOf(FRigVMStruct::StaticStruct())) { if (const TSharedPtr StructOnScope = UnitNode->ConstructStructInstance()) { const FRigVMStruct* RigVMStruct = (const FRigVMStruct*)StructOnScope->GetStructMemory(); NodeTitle = FText::FromString(RigVMStruct->GetUnitLabel()); SubTitle = RigVMStruct->GetUnitSubTitle(); } } } else if(URigVMFunctionReferenceNode* FunctionReferenceNode = Cast(ModelNode)) { const FRigVMGraphFunctionHeader& ReferencedHeader = FunctionReferenceNode->GetReferencedFunctionHeader(); { TSoftObjectPtr 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(ModelNode)) { if(!CollapseNode->IsA()) { static const FString CollapseNodeString = TEXT("Collapsed Graph"); SubTitle = CollapseNodeString; } } else if(URigVMVariableNode* VariableNode = Cast(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::Get()) { TArray 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(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& OldPins) { AllocateDefaultPins(); } void URigVMEdGraphNode::RewireOldPinsToNewPins(TArray& InOldPins, TArray& 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& 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(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(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 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 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 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& InPinsToKeep) { TArray SubPins = InParentPin->SubPins; TArray ModelSubPins; TArray 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& 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 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, FPinPair>& InPair) -> bool { return InPair.Key.IsValid(); }); TArray PinsToRemove; for(const auto& Pair : CachedPins) { URigVMPin* ModelPin = Pair.Key.Get(); TArray& 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 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& InModelPins) -> int32 { if(InModelPins.Num() < 2) { return 0; } TArray 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& 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& 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& 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,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(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 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()) { if(URigVMHost* DebuggedHost = Cast(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(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& 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& 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(GetModelNode())) { if(FunctionReferenceNode->RequiresVariableRemapping()) { TArray CurrentExternalVariables = FunctionReferenceNode->GetExternalVariables(false); for(const FRigVMExternalVariable& CurrentExternalVariable : CurrentExternalVariables) { ExternalVariables.Add(MakeShared(CurrentExternalVariable)); } } } } UClass* URigVMEdGraphNode::GetRigVMGeneratedClass() const { if(const URigVMBlueprint* Blueprint = GetTypedOuter()) { if (Blueprint->GeneratedClass) { check(Blueprint->GeneratedClass->IsChildOf(URigVMHost::StaticClass())); return Blueprint->GeneratedClass; } } return nullptr; } UClass* URigVMEdGraphNode::GetRigVMSkeletonGeneratedClass() const { if(const URigVMBlueprint* Blueprint = GetTypedOuter()) { if (Blueprint->SkeletonGeneratedClass) { check(Blueprint->SkeletonGeneratedClass->IsChildOf(URigVMHost::StaticClass())); return Blueprint->SkeletonGeneratedClass; } } return nullptr; } FLinearColor URigVMEdGraphNode::GetNodeOpacityColor() const { if (URigVMNode* ModelNode = GetModelNode()) { if (Cast(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() || ModelNode->IsA() || ModelNode->IsA() || ModelNode->IsA() || ModelNode->IsA() || ModelNode->IsA() || 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()) { return EventIcon; } URigVMNode* InnerNode = ModelNode; while(const URigVMAggregateNode* AggregateNode = Cast(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()) { return FunctionIcon; } if (ModelNode->IsA()) { return CollapsedNodeIcon; } if (ModelNode->IsA()) { return EntryReturnIcon; } const UScriptStruct* MetadataScriptStruct = nullptr; if (const URigVMUnitNode* UnitNode = Cast(ModelNode)) { MetadataScriptStruct = UnitNode->GetScriptStruct(); } else if (const URigVMDispatchNode* DispatchNode = Cast(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(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(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(GetOuter())) { BreakAllNodeLinks(); URigVMBlueprint* RigVMBlueprint = Cast(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(GetOuter())) { return Graph->GetBlueprint(); } return nullptr; } URigVMGraph* URigVMEdGraphNode::GetModel() const { if (URigVMEdGraph* Graph = Cast(GetOuter())) { return Graph->GetModel(); } return nullptr; } URigVMController* URigVMEdGraphNode::GetController() const { if (URigVMEdGraph* Graph = Cast(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(GetOuter())) { if (URigVMGraph* Model = GetModel()) { MutableThis->CachedModelNode = TWeakObjectPtr(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 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(); 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(); } void URigVMEdGraphNode::AutowireNewNode(UEdGraphPin* FromPin) { DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC() Super::AutowireNewNode(FromPin); const URigVMEdGraphSchema* Schema = GetDefault(); 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(GetModelNode())) { OutInputPinIndex = 0; OutOutputPinIndex = Pins.Num()/2; if (Pins[OutOutputPinIndex]->Direction != EGPD_Output || Pins[OutOutputPinIndex]->ParentPin != nullptr) { for (int32 i=0; iDirection == 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& 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& 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& 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(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& 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 EmptyList; return EmptyList; } #if WITH_EDITOR void URigVMEdGraphNode::UpdateProfilingSettings() { bEnableProfiling = false; MicroSeconds = 0; MicroSecondsFrames.Reset(); if(const URigVMEdGraph* RigGraph = Cast(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