// Copyright Epic Games, Inc. All Rights Reserved. #include "RigVMModel/RigVMGraph.h" #include "RigVMModel/RigVMFunctionLibrary.h" #include "RigVMModel/RigVMController.h" #include "RigVMModel/Nodes/RigVMLibraryNode.h" #include "RigVMModel/Nodes/RigVMFunctionEntryNode.h" #include "RigVMModel/Nodes/RigVMFunctionReturnNode.h" #include "UObject/Package.h" #include "RigVMTypeUtils.h" URigVMGraph::URigVMGraph() : DiagnosticsAST(nullptr) , RuntimeAST(nullptr) , bEditable(true) { SetExecuteContextStruct(FRigVMExecuteContext::StaticStruct()); } const TArray& URigVMGraph::GetNodes() const { return Nodes; } const TArray& URigVMGraph::GetLinks() const { return Links; } TArray URigVMGraph::GetContainedGraphs(bool bRecursive) const { TArray Graphs; for (URigVMNode* Node : GetNodes()) { if (URigVMCollapseNode* CollapseNode = Cast(Node)) { Graphs.Add(CollapseNode->GetContainedGraph()); if (bRecursive) { Graphs.Append(CollapseNode->GetContainedGraph()->GetContainedGraphs(true)); } } } return Graphs; } URigVMGraph* URigVMGraph::GetParentGraph() const { if(URigVMCollapseNode* CollapseNode = Cast(GetOuter())) { return CollapseNode->GetGraph(); } return nullptr; } URigVMGraph* URigVMGraph::GetRootGraph() const { if(URigVMGraph* ParentGraph = GetParentGraph()) { return ParentGraph->GetRootGraph(); } return (URigVMGraph*)this; } bool URigVMGraph::IsRootGraph() const { return GetRootGraph() == this; } URigVMFunctionEntryNode* URigVMGraph::GetEntryNode() const { for (URigVMNode* Node : Nodes) { if (URigVMFunctionEntryNode* EntryNode = Cast(Node)) { return EntryNode; } } return nullptr; } URigVMFunctionReturnNode* URigVMGraph::GetReturnNode() const { for (URigVMNode* Node : Nodes) { if (URigVMFunctionReturnNode* ReturnNode = Cast(Node)) { return ReturnNode; } } return nullptr; } TArray URigVMGraph::GetVariableDescriptions() const { TArray Variables; for (URigVMNode* Node : Nodes) { if (URigVMVariableNode* VariableNode = Cast(Node)) { Variables.AddUnique(VariableNode->GetVariableDescription()); } } return Variables; } FString URigVMGraph::GetNodePath() const { if (URigVMCollapseNode* CollapseNode = Cast(GetOuter())) { return CollapseNode->GetNodePath(true /* recursive */); } return FString(); } FString URigVMGraph::GetGraphName() const { if (URigVMCollapseNode* CollapseNode = Cast(GetOuter())) { return CollapseNode->GetNodePath(false /* recursive */); } return FString(); } URigVMNode* URigVMGraph::FindNodeByName(const FName& InNodeName) const { for (URigVMNode* Node : Nodes) { if (Node == nullptr) { continue; } if (Node->GetFName() == InNodeName) { return Node; } } return nullptr; } URigVMNode* URigVMGraph::FindNode(const FString& InNodePath) const { if (InNodePath.IsEmpty()) { return nullptr; } FString Path = InNodePath; if (Path.StartsWith(TEXT("FunctionLibrary::|"))) { if (GetRootGraph()->IsA()) { Path.RightChopInline(18); } else { return nullptr; } } FString Left = Path, Right; URigVMNode::SplitNodePathAtStart(Path, Left, Right); if (Right.IsEmpty()) { return FindNodeByName(*Left); } if (URigVMLibraryNode* LibraryNode = Cast< URigVMLibraryNode>(FindNodeByName(*Left))) { return LibraryNode->GetContainedGraph()->FindNode(Right); } return nullptr; } URigVMPin* URigVMGraph::FindPin(const FString& InPinPath) const { FString Left, Right; if (!URigVMPin::SplitPinPathAtStart(InPinPath, Left, Right)) { Left = InPinPath; } URigVMNode* Node = FindNode(Left); if (Node) { return Node->FindPin(Right); } return nullptr; } URigVMLink* URigVMGraph::FindLink(const FString& InLinkPinPathRepresentation) const { for(URigVMLink* Link : Links) { if(Link->GetPinPathRepresentation() == InLinkPinPathRepresentation) { return Link; } } return nullptr; } bool URigVMGraph::IsNodeSelected(const FName& InNodeName) const { return SelectedNodes.Contains(InNodeName); } const TArray& URigVMGraph::GetSelectNodes() const { return SelectedNodes; } bool URigVMGraph::IsTopLevelGraph() const { if (GetOuter()->IsA()) { return false; } return true; } URigVMFunctionLibrary* URigVMGraph::GetDefaultFunctionLibrary() const { if (DefaultFunctionLibraryPtr.IsValid()) { return CastChecked(DefaultFunctionLibraryPtr.Get()); } if (URigVMLibraryNode* OuterLibraryNode = Cast(GetOuter())) { if (URigVMGraph* OuterGraph = OuterLibraryNode->GetGraph()) { return OuterGraph->GetDefaultFunctionLibrary(); } } return nullptr; } void URigVMGraph::SetDefaultFunctionLibrary(URigVMFunctionLibrary* InFunctionLibrary) { DefaultFunctionLibraryPtr = InFunctionLibrary; } TArray URigVMGraph::GetExternalVariables() const { TArray Variables; for(URigVMNode* Node : GetNodes()) { if(URigVMLibraryNode* LibraryNode = Cast(Node)) { TArray LibraryVariables = LibraryNode->GetExternalVariables(); for(const FRigVMExternalVariable& LibraryVariable : LibraryVariables) { FRigVMExternalVariable::MergeExternalVariable(Variables, LibraryVariable); } } else if(URigVMVariableNode* VariableNode = Cast(Node)) { // Make sure it is not a local variable or input argument if (VariableNode->IsExternalVariable()) { FRigVMExternalVariable::MergeExternalVariable(Variables, VariableNode->GetVariableDescription().ToExternalVariable()); } } } return Variables; } TArray URigVMGraph::GetLocalVariables(bool bIncludeInputArguments) const { if (bIncludeInputArguments) { TArray Variables; Variables.Append(LocalVariables); Variables.Append(GetInputArguments()); return Variables; } return LocalVariables; } TArray URigVMGraph::GetInputArguments() const { TArray Inputs; if (URigVMFunctionEntryNode* EntryNode = GetEntryNode()) { for (URigVMPin* Pin : EntryNode->GetPins()) { FRigVMGraphVariableDescription Description; Description.Name = Pin->GetFName(); Description.CPPType = Pin->GetCPPType(); Description.CPPTypeObject = Pin->GetCPPTypeObject(); Inputs.Add(Description); } } return Inputs; } TArray URigVMGraph::GetOutputArguments() const { TArray Outputs; if (URigVMFunctionReturnNode* ReturnNode = GetReturnNode()) { for (URigVMPin* Pin : ReturnNode->GetPins()) { FRigVMGraphVariableDescription Description; Description.Name = Pin->GetFName(); Description.CPPType = Pin->GetCPPType(); Description.CPPTypeObject = Pin->GetCPPTypeObject(); Outputs.Add(Description); } } return Outputs; } FRigVMGraphModifiedEvent& URigVMGraph::OnModified() { return ModifiedEvent; } void URigVMGraph::SetExecuteContextStruct(UScriptStruct* InExecuteContextStruct) { check(InExecuteContextStruct); ensure(InExecuteContextStruct->IsChildOf(FRigVMExecuteContext::StaticStruct())); ExecuteContextStruct = InExecuteContextStruct; } UScriptStruct* URigVMGraph::GetExecuteContextStruct() const { if (URigVMGraph* RootGraph = GetRootGraph()) { return RootGraph->ExecuteContextStruct.Get(); } return nullptr; } void URigVMGraph::Notify(ERigVMGraphNotifType InNotifType, UObject* InSubject) { ModifiedEvent.Broadcast(InNotifType, this, InSubject); } TSharedPtr URigVMGraph::GetDiagnosticsAST(bool bForceRefresh, TArray InLinksToSkip) { // only refresh the diagnostics AST if have a different set of links to skip if (!bForceRefresh && DiagnosticsAST.IsValid()) { const TArray PreviousLinksToSkip = DiagnosticsAST->GetSettings().LinksToSkip; if (PreviousLinksToSkip.Num() < InLinksToSkip.Num()) { bForceRefresh = true; } else { for (int32 LinkIndex = 0; LinkIndex < InLinksToSkip.Num(); LinkIndex++) { if (PreviousLinksToSkip[LinkIndex] != InLinksToSkip[LinkIndex]) { bForceRefresh = true; break; } } } } if (DiagnosticsAST == nullptr || bForceRefresh) { FRigVMParserASTSettings Settings = FRigVMParserASTSettings::Fast(); Settings.LinksToSkip = InLinksToSkip; DiagnosticsAST = MakeShareable(new FRigVMParserAST(this, nullptr, Settings)); } return DiagnosticsAST; } TSharedPtr URigVMGraph::GetRuntimeAST(const FRigVMParserASTSettings& InSettings, bool bForceRefresh) { if (RuntimeAST == nullptr || bForceRefresh) { RuntimeAST = MakeShareable(new FRigVMParserAST(this, nullptr, InSettings)); } return RuntimeAST; } void URigVMGraph::ClearAST(bool bClearDiagnostics, bool bClearRuntime) { if (bClearDiagnostics) { DiagnosticsAST.Reset(); } if (bClearRuntime) { RuntimeAST.Reset(); } } bool URigVMGraph::IsNameAvailable(const FString& InName) { for (URigVMNode* Node : Nodes) { if (Node->GetName() == InName) { return false; } } return true; } void URigVMGraph::PrepareCycleChecking(URigVMPin* InPin, bool bAsInput) { TArray LinksToSkip; if (InPin) { LinksToSkip = InPin->GetLinks(); } GetDiagnosticsAST(false, LinksToSkip)->PrepareCycleChecking(InPin); } bool URigVMGraph::CanLink(URigVMPin* InSourcePin, URigVMPin* InTargetPin, FString* OutFailureReason, const FRigVMByteCode* InByteCode, ERigVMPinDirection InUserLinkDirection, bool bInAllowWildcard) { if (!URigVMPin::CanLink(InSourcePin, InTargetPin, OutFailureReason, InByteCode, InUserLinkDirection, bInAllowWildcard)) { return false; } return GetDiagnosticsAST()->CanLink(InSourcePin, InTargetPin, OutFailureReason); }