1884 lines
55 KiB
C++
1884 lines
55 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "RigVMModel/RigVMClient.h"
|
|
#include "Misc/TransactionObjectEvent.h"
|
|
#include "Exporters/Exporter.h"
|
|
#include "UObject/ObjectSaveContext.h"
|
|
#include "RigVMModel/RigVMControllerActions.h"
|
|
#include "RigVMModel/Nodes/RigVMDispatchNode.h"
|
|
#include "RigVMCore/RigVMObjectArchive.h"
|
|
#include "EdGraph/RigVMEdGraph.h"
|
|
|
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(RigVMClient)
|
|
|
|
#if WITH_EDITOR
|
|
#include "ScopedTransaction.h"
|
|
#endif
|
|
|
|
UObject* IRigVMClientHost::ResolveUserDefinedTypeById(const FString& InTypeName) const
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
void FRigVMClient::SetDefaultSchemaClass(TSubclassOf<URigVMSchema> InSchemaClass)
|
|
{
|
|
check(InSchemaClass);
|
|
|
|
if(InSchemaClass == DefaultSchemaClass)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DefaultSchemaClass = InSchemaClass;
|
|
|
|
for(TPair<FSoftObjectPath, TObjectPtr<URigVMController>>& ObjectControllerPair : Controllers)
|
|
{
|
|
ObjectControllerPair.Value->SetSchemaClass(InSchemaClass);
|
|
}
|
|
}
|
|
|
|
void FRigVMClient::SetControllerClass(TSubclassOf<URigVMController> InControllerClass)
|
|
{
|
|
check(InControllerClass);
|
|
|
|
if (InControllerClass == ControllerClass)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (URigVMGraph* Model : GetModels())
|
|
{
|
|
RemoveController(Model);
|
|
}
|
|
|
|
ControllerClass = InControllerClass;
|
|
}
|
|
|
|
void FRigVMClient::SetOuterClientHost(UObject* InOuterClientHost, const FName& InOuterClientHostPropertyName)
|
|
{
|
|
OuterClientHost = InOuterClientHost;
|
|
OuterClientPropertyName = InOuterClientHostPropertyName;
|
|
|
|
check(OuterClientHost->Implements<URigVMClientHost>());
|
|
check(GetOuterClientProperty() != nullptr);
|
|
|
|
// create the null graph / default controller.
|
|
// we need this to react to notifs without a valid graph
|
|
// such as interaction brackets
|
|
static const URigVMGraph* NullGraph = nullptr;
|
|
if(!Controllers.Contains(NullGraph))
|
|
{
|
|
CreateController(NullGraph);
|
|
}
|
|
}
|
|
|
|
void FRigVMClient::SetFromDeprecatedData(URigVMGraph* InDefaultGraph, URigVMFunctionLibrary* InFunctionLibrary)
|
|
{
|
|
if(GetDefaultModel() != InDefaultGraph ||
|
|
GetFunctionLibrary() != InFunctionLibrary)
|
|
{
|
|
if(GetDefaultModel() == InDefaultGraph)
|
|
{
|
|
Models.Reset();
|
|
}
|
|
|
|
if(InFunctionLibrary == nullptr)
|
|
{
|
|
Swap(FunctionLibrary, InFunctionLibrary);
|
|
}
|
|
|
|
Reset();
|
|
if(InDefaultGraph)
|
|
{
|
|
AddModel(InDefaultGraph, false);
|
|
}
|
|
if(InFunctionLibrary)
|
|
{
|
|
AddModel(InFunctionLibrary, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FRigVMClient::SetExternalModelHost(IRigVMClientExternalModelHost* InExternalModelHost)
|
|
{
|
|
// We dont allow this to be set dynamically, assume it is set once at creation time
|
|
check(ExternalModelHost == nullptr);
|
|
|
|
ExternalModelHost = InExternalModelHost;
|
|
}
|
|
|
|
void FRigVMClient::Reset()
|
|
{
|
|
for(URigVMGraph* Model : GetModels())
|
|
{
|
|
DestroyObject(Model);
|
|
}
|
|
for(auto Pair : Controllers)
|
|
{
|
|
DestroyObject(Pair.Value);
|
|
}
|
|
DestroyObject(FunctionLibrary);
|
|
|
|
Models.Reset();
|
|
Controllers.Reset();
|
|
FunctionLibrary = nullptr;
|
|
|
|
ResetActionStack();
|
|
}
|
|
|
|
URigVMSchema* FRigVMClient::GetDefaultSchema() const
|
|
{
|
|
check(DefaultSchemaClass);
|
|
return DefaultSchemaClass->GetDefaultObject<URigVMSchema>();
|
|
}
|
|
|
|
URigVMGraph* FRigVMClient::GetDefaultModel() const
|
|
{
|
|
if(GetModels().IsEmpty())
|
|
{
|
|
return nullptr;
|
|
}
|
|
return GetModel(0);
|
|
}
|
|
|
|
URigVMGraph* FRigVMClient::GetModel(int32 InIndex) const
|
|
{
|
|
const TArray<TObjectPtr<URigVMGraph>>& LocalModels = GetModels();
|
|
if(LocalModels.IsValidIndex(InIndex))
|
|
{
|
|
return LocalModels[InIndex];
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
URigVMGraph* FRigVMClient::GetModel(const UEdGraph* InEdGraph) const
|
|
{
|
|
if (InEdGraph == nullptr)
|
|
{
|
|
return GetDefaultModel();
|
|
}
|
|
|
|
//#if WITH_EDITORONLY_DATA
|
|
// if (InEdGraph == FunctionLibraryEdGraph)
|
|
// {
|
|
// return RigVMClient.GetFunctionLibrary();
|
|
// }
|
|
//#endif
|
|
|
|
const URigVMEdGraph* RigGraph = Cast< URigVMEdGraph>(InEdGraph);
|
|
check(RigGraph);
|
|
return GetModel(RigGraph->ModelNodePath);
|
|
|
|
}
|
|
|
|
URigVMGraph* FRigVMClient::GetModel(const FString& InNodePathOrName) const
|
|
{
|
|
if(InNodePathOrName.IsEmpty())
|
|
{
|
|
return GetDefaultModel();
|
|
}
|
|
|
|
TArray<URigVMGraph*> ModelsAndFunctionLibrary = GetAllModels(true, false);
|
|
for(URigVMGraph* Model : ModelsAndFunctionLibrary)
|
|
{
|
|
if(Model->GetNodePath() == InNodePathOrName || Model->GetName() == InNodePathOrName)
|
|
{
|
|
return Model;
|
|
}
|
|
|
|
static constexpr TCHAR NodePathPrefixFormat[] = TEXT("%s|");
|
|
const FString NodePathPrefix = FString::Printf(NodePathPrefixFormat, *Model->GetNodePath());
|
|
if(InNodePathOrName.StartsWith(NodePathPrefix))
|
|
{
|
|
const FString RemainingNodePath = InNodePathOrName.Mid(NodePathPrefix.Len());
|
|
if(const URigVMCollapseNode* CollapseNode = Cast<URigVMCollapseNode>(Model->FindNode(RemainingNodePath)))
|
|
{
|
|
return CollapseNode->GetContainedGraph();
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
URigVMGraph* FRigVMClient::GetModel(const UObject* InEditorSideObject) const
|
|
{
|
|
check(InEditorSideObject);
|
|
|
|
if (InEditorSideObject->Implements<URigVMEditorSideObject>())
|
|
{
|
|
const IRigVMEditorSideObject* UserInterfaceGraph = Cast<IRigVMEditorSideObject>(InEditorSideObject);
|
|
return GetModel(UserInterfaceGraph->GetRigVMNodePath());
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void FRigVMClient::RefreshAllModels(ERigVMLoadType InLoadType, bool bEnablePostLoadHashing, bool& bIsCompiling)
|
|
{
|
|
const bool bIsPostLoad = InLoadType == ERigVMLoadType::PostLoad;
|
|
|
|
// avoid any compute if the current structure hashes match with the serialized ones
|
|
if (bEnablePostLoadHashing && GetStructureHash() == GetSerializedStructureHash())
|
|
{
|
|
if (bIsPostLoad)
|
|
{
|
|
TArray<URigVMGraph*> ModelGraphs = GetAllModels(true, true);
|
|
Algo::Reverse(ModelGraphs);
|
|
for (URigVMGraph* ModelGraph : ModelGraphs)
|
|
{
|
|
URigVMController* Controller = GetOrCreateController(ModelGraph);
|
|
URigVMController::FRestoreLinkedPathSettings Settings;
|
|
Settings.bFollowCoreRedirectors = true;
|
|
Settings.bRelayToOrphanPins = true;
|
|
Controller->ProcessDetachedLinks(Settings);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
TGuardValue<bool> IsCompilingGuard(bIsCompiling, true);
|
|
TGuardValue<bool> ClientIgnoreModificationsGuard(bIgnoreModelNotifications, true);
|
|
|
|
TArray<URigVMGraph*> AllModelsLeavesFirst = GetAllModelsLeavesFirst(true);
|
|
TMap<const URigVMGraph*, TArray<URigVMController::FLinkedPath>> LinkedPaths;
|
|
|
|
if (ensure(IsInGameThread()))
|
|
{
|
|
TArray<URigVMController::FRepopulatePinsNodeData> RepopulatePinsNodesData;
|
|
constexpr int32 REPOPULATE_NODES_NUM_RESERVED = 800;
|
|
RepopulatePinsNodesData.Reserve(REPOPULATE_NODES_NUM_RESERVED);
|
|
|
|
for (URigVMGraph* Graph : AllModelsLeavesFirst)
|
|
{
|
|
URigVMController* Controller = GetOrCreateController(Graph);
|
|
// temporarily disable default value validation during load time, serialized values should always be accepted
|
|
TGuardValue<bool> PerGraphDisablePinDefaultValueValidation(Controller->bValidatePinDefaults, false);
|
|
TGuardValue<bool> GuardEditGraph(Graph->bEditable, true);
|
|
FRigVMControllerNotifGuard NotifGuard(Controller, true);
|
|
LinkedPaths.Add(Graph, Controller->GetLinkedPaths());
|
|
|
|
const TArray<URigVMNode*> Nodes = Graph->GetNodes();
|
|
if (Nodes.Num() > 0)
|
|
{
|
|
RepopulatePinsNodesData.Reset();
|
|
|
|
for (URigVMNode* Node : Nodes)
|
|
{
|
|
Controller->GenerateRepopulatePinsNodeData(RepopulatePinsNodesData, Node, true, true);
|
|
}
|
|
|
|
#if UE_RIGVMCONTROLLER_VERBOSE_REPOPULATE
|
|
UE_LOG(LogRigVMDeveloper, Display, TEXT("--- Graph: [%s/%s] - NumNodes : [%d]"), *Graph->GetOuter()->GetName(), *Graph->GetName(), RepopulatePinsNodesData.Num());
|
|
#endif
|
|
|
|
Controller->OrphanPins(RepopulatePinsNodesData);
|
|
Controller->FastBreakLinkedPaths(LinkedPaths.FindChecked(Graph));
|
|
Controller->RepopulatePins(RepopulatePinsNodesData);
|
|
}
|
|
}
|
|
|
|
if (IRigVMClientHost* ClientHost = Cast<IRigVMClientHost>(GetOuter()))
|
|
{
|
|
ClientHost->SetupPinRedirectorsForBackwardsCompatibility();
|
|
}
|
|
}
|
|
|
|
for (URigVMGraph* Graph : AllModelsLeavesFirst)
|
|
{
|
|
URigVMController* Controller = GetOrCreateController(Graph);
|
|
TGuardValue<bool> GuardEditGraph(Graph->bEditable, true);
|
|
FRigVMControllerNotifGuard NotifGuard(Controller, true);
|
|
{
|
|
URigVMController::FRestoreLinkedPathSettings Settings;
|
|
Settings.bFollowCoreRedirectors = true;
|
|
Settings.bRelayToOrphanPins = true;
|
|
Controller->RestoreLinkedPaths(LinkedPaths.FindChecked(Graph), Settings);
|
|
}
|
|
|
|
for (URigVMNode* ModelNode : Graph->GetNodes())
|
|
{
|
|
Controller->RemoveUnusedOrphanedPins(ModelNode);
|
|
}
|
|
|
|
if (bIsPostLoad)
|
|
{
|
|
for (URigVMNode* ModelNode : Graph->GetNodes())
|
|
{
|
|
if (URigVMTemplateNode* TemplateNode = Cast<URigVMTemplateNode>(ModelNode))
|
|
{
|
|
TemplateNode->InvalidateCache();
|
|
TemplateNode->PostLoad();
|
|
}
|
|
}
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
|
|
if (bIsPostLoad)
|
|
{
|
|
for (URigVMNode* ModelNode : Graph->GetNodes())
|
|
{
|
|
if (URigVMUnitNode* UnitNode = Cast<URigVMUnitNode>(ModelNode))
|
|
{
|
|
if (!UnitNode->HasWildCardPin())
|
|
{
|
|
UScriptStruct* ScriptStruct = UnitNode->GetScriptStruct();
|
|
if (ScriptStruct == nullptr)
|
|
{
|
|
Controller->FullyResolveTemplateNode(UnitNode, INDEX_NONE, false);
|
|
}
|
|
|
|
// Try to find a deprecated template
|
|
if (UnitNode->GetScriptStruct() == nullptr && !UnitNode->TemplateNotation.IsNone())
|
|
{
|
|
const FRigVMTemplate* Template = FRigVMRegistry::Get().FindTemplate(UnitNode->TemplateNotation, true);
|
|
FRigVMTemplate::FTypeMap TypeMap = UnitNode->GetTemplatePinTypeMap();
|
|
|
|
int32 Permutation;
|
|
if (Template->FullyResolve(TypeMap, Permutation))
|
|
{
|
|
const FRigVMFunction* Function = Template->GetPermutation(Permutation);
|
|
UnitNode->ResolvedFunctionName = Function->GetName();
|
|
}
|
|
}
|
|
|
|
if (UnitNode->GetScriptStruct() == nullptr)
|
|
{
|
|
static constexpr TCHAR UnresolvedUnitNodeMessage[] = TEXT("Node %s could not be resolved.");
|
|
Controller->ReportErrorf(UnresolvedUnitNodeMessage, *ModelNode->GetNodePath(true));
|
|
}
|
|
}
|
|
}
|
|
if (URigVMDispatchNode* DispatchNode = Cast<URigVMDispatchNode>(ModelNode))
|
|
{
|
|
if (DispatchNode->GetFactory() == nullptr)
|
|
{
|
|
static constexpr TCHAR UnresolvedDispatchNodeMessage[] = TEXT("Dispatch node %s has no factory..");
|
|
Controller->ReportErrorf(UnresolvedDispatchNodeMessage, *ModelNode->GetNodePath(true));
|
|
}
|
|
else if (!DispatchNode->HasWildCardPin())
|
|
{
|
|
if (DispatchNode->GetResolvedFunction() == nullptr)
|
|
{
|
|
Controller->FullyResolveTemplateNode(DispatchNode, INDEX_NONE, false);
|
|
}
|
|
if (DispatchNode->GetResolvedFunction() == nullptr)
|
|
{
|
|
static constexpr TCHAR UnresolvedDispatchNodeMessage[] = TEXT("Node %s could not be resolved.");
|
|
Controller->ReportErrorf(UnresolvedDispatchNodeMessage, *ModelNode->GetNodePath(true));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
}
|
|
|
|
const TArray<TObjectPtr<URigVMGraph>>& FRigVMClient::GetModels() const
|
|
{
|
|
return ExternalModelHost != nullptr ? ExternalModelHost->GetExternalModels() : Models;
|
|
}
|
|
|
|
TArray<URigVMGraph*> FRigVMClient::GetAllModels(bool bIncludeFunctionLibrary, bool bRecursive) const
|
|
{
|
|
TArray<URigVMGraph*> AllModels = GetModels();
|
|
if(bRecursive)
|
|
{
|
|
for(URigVMGraph* Model : GetModels())
|
|
{
|
|
AllModels.Append(Model->GetContainedGraphs(true /* recursive */));
|
|
}
|
|
}
|
|
if(bIncludeFunctionLibrary && FunctionLibrary)
|
|
{
|
|
AllModels.Add(FunctionLibrary);
|
|
if(bRecursive)
|
|
{
|
|
AllModels.Append(FunctionLibrary->GetContainedGraphs(true /* recursive */));
|
|
}
|
|
}
|
|
return AllModels;
|
|
}
|
|
|
|
TArray<URigVMGraph*> FRigVMClient::GetAllModelsLeavesFirst(bool bIncludeFunctionLibrary) const
|
|
{
|
|
TArray<URigVMGraph*> SortedModels = GetAllModels(bIncludeFunctionLibrary, true);
|
|
URigVMController::SortGraphElementsByGraphDepth(SortedModels, true);
|
|
return SortedModels;
|
|
}
|
|
|
|
URigVMController* FRigVMClient::GetController(int32 InIndex) const
|
|
{
|
|
return GetController(GetModel(InIndex));
|
|
}
|
|
|
|
URigVMController* FRigVMClient::GetController(const FString& InNodePathOrName) const
|
|
{
|
|
return GetController(GetModel(InNodePathOrName));
|
|
}
|
|
|
|
URigVMController* FRigVMClient::GetController(const URigVMGraph* InModel) const
|
|
{
|
|
if(InModel == nullptr)
|
|
{
|
|
InModel = GetDefaultModel();
|
|
}
|
|
|
|
if(InModel)
|
|
{
|
|
const FSoftObjectPath Key(InModel);
|
|
if(const TObjectPtr<URigVMController>* Controller = Controllers.Find(Key))
|
|
{
|
|
checkf(Controller->Get()->GetGraph() == InModel, TEXT("Controller %s contains unexpected graph."), *Controller->GetPathName());
|
|
return Controller->Get();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
URigVMController* FRigVMClient::GetController(const UObject* InEditorSideObject) const
|
|
{
|
|
check(InEditorSideObject);
|
|
|
|
if (InEditorSideObject->Implements<URigVMEditorSideObject>())
|
|
{
|
|
const IRigVMEditorSideObject* UserInterfaceGraph = Cast<IRigVMEditorSideObject>(InEditorSideObject);
|
|
return GetController(UserInterfaceGraph->GetRigVMNodePath());
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
URigVMController* FRigVMClient::GetOrCreateController(int32 InIndex)
|
|
{
|
|
return GetOrCreateController(GetModel(InIndex));
|
|
}
|
|
|
|
URigVMController* FRigVMClient::GetOrCreateController(const FString& InNodePathOrName)
|
|
{
|
|
return GetOrCreateController(GetModel(InNodePathOrName));
|
|
}
|
|
|
|
URigVMController* FRigVMClient::GetOrCreateController(const URigVMGraph* InModel)
|
|
{
|
|
if(InModel == nullptr)
|
|
{
|
|
InModel = GetDefaultModel();
|
|
}
|
|
|
|
if(InModel)
|
|
{
|
|
if(URigVMController* Controller = GetController(InModel))
|
|
{
|
|
// We associate controllers to graphs via soft path, so they can match newly created graphs
|
|
// If this happens make sure the graph is correctly bound to the controller
|
|
if (!InModel->ModifiedEvent.IsBound())
|
|
{
|
|
const_cast<URigVMGraph*>(InModel)->OnModified().AddUObject(Controller, &URigVMController::HandleModifiedEvent);
|
|
}
|
|
return Controller;
|
|
}
|
|
return CreateController(InModel);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
URigVMController* FRigVMClient::GetOrCreateController(const UObject* InEditorSideObject)
|
|
{
|
|
check(InEditorSideObject);
|
|
|
|
if (InEditorSideObject->Implements<URigVMEditorSideObject>())
|
|
{
|
|
const IRigVMEditorSideObject* UserInterfaceGraph = Cast<IRigVMEditorSideObject>(InEditorSideObject);
|
|
return GetOrCreateController(UserInterfaceGraph->GetRigVMNodePath());
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
URigVMController* FRigVMClient::GetControllerByName(const FString InGraphName) const
|
|
{
|
|
if (InGraphName.IsEmpty())
|
|
{
|
|
if (const URigVMGraph* DefaultModel = GetDefaultModel())
|
|
{
|
|
return GetController(DefaultModel);
|
|
}
|
|
}
|
|
|
|
for (const URigVMGraph* Graph : GetAllModels(true, true))
|
|
{
|
|
if (Graph->GetName() == InGraphName || Graph->GetGraphName() == InGraphName)
|
|
{
|
|
return GetController(Graph);
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
bool FRigVMClient::RemoveController(const URigVMGraph* InModel)
|
|
{
|
|
const FSoftObjectPath Key(InModel);
|
|
URigVMController* Controller = GetController(InModel);
|
|
const bool bSuccess = Controllers.Remove(Key) > 0;
|
|
if(Controller)
|
|
{
|
|
Controller->SetActionStack(nullptr);
|
|
Controller->Rename(nullptr, GetTransientPackage(), REN_DoNotDirty | REN_DontCreateRedirectors | REN_NonTransactional);
|
|
Controller->RemoveFromRoot();
|
|
Controller->MarkAsGarbage();
|
|
}
|
|
return bSuccess;
|
|
}
|
|
|
|
URigVMGraph* FRigVMClient::AddModel(const FString InName, bool bSetupUndoRedo, bool bPrintPythonCommand)
|
|
{
|
|
const FString DesiredName = FString::Printf(TEXT("%s %s"), FRigVMClient::RigVMModelPrefix, *InName);
|
|
return AddModel(*DesiredName, bSetupUndoRedo);
|
|
}
|
|
|
|
URigVMGraph* FRigVMClient::AddModel(const FName& InName, bool bSetupUndoRedo, const FObjectInitializer* ObjectInitializer, bool bCreateController)
|
|
{
|
|
check(DefaultSchemaClass);
|
|
|
|
URigVMGraph* NewModel = CreateModel(InName, DefaultSchemaClass, bSetupUndoRedo, GetOuter(), ObjectInitializer, bCreateController);
|
|
AddModel(NewModel, bCreateController);
|
|
return NewModel;
|
|
}
|
|
|
|
URigVMGraph* FRigVMClient::AddModel(const FName& InName, TSubclassOf<URigVMSchema> InSchemaClass, bool bSetupUndoRedo, const FObjectInitializer* ObjectInitializer, bool bCreateController)
|
|
{
|
|
URigVMGraph* NewModel = CreateModel(InName, InSchemaClass, bSetupUndoRedo, GetOuter(), ObjectInitializer, bCreateController);
|
|
AddModel(NewModel, bCreateController);
|
|
return NewModel;
|
|
}
|
|
|
|
URigVMGraph* FRigVMClient::CreateModel(const FName& InName, TSubclassOf<URigVMSchema> InSchemaClass, bool bSetupUndoRedo, UObject* InOuter, const FObjectInitializer* ObjectInitializer, bool bCreateController)
|
|
{
|
|
check(InSchemaClass);
|
|
|
|
#if WITH_EDITOR
|
|
TSharedPtr<FScopedTransaction> Transaction;
|
|
if(bSetupUndoRedo)
|
|
{
|
|
Transaction = MakeShared<FScopedTransaction>(NSLOCTEXT("RigVMClient", "AddModel", "Add new root graph"));
|
|
InOuter->Modify();
|
|
}
|
|
#endif
|
|
|
|
const FName SafeGraphName = GetUniqueName(InName);
|
|
URigVMGraph* Model = nullptr;
|
|
if(ObjectInitializer)
|
|
{
|
|
Model = ObjectInitializer->CreateDefaultSubobject<URigVMGraph>(InOuter, SafeGraphName);
|
|
}
|
|
else
|
|
{
|
|
Model = NewObject<URigVMGraph>(InOuter, SafeGraphName);
|
|
}
|
|
|
|
Model->SetSchemaClass(InSchemaClass);
|
|
|
|
if(bSetupUndoRedo)
|
|
{
|
|
InOuter->Modify();
|
|
UndoRedoIndex++;
|
|
|
|
FRigVMClientAction Action;
|
|
Action.Type = ERigVMClientAction::ERigVMClientAction_AddModel;
|
|
Action.NodePath = Model->GetNodePath();
|
|
UndoStack.Push(Action);
|
|
RedoStack.Reset();
|
|
}
|
|
return Model;
|
|
}
|
|
|
|
TObjectPtr<URigVMGraph> FRigVMClient::CreateContainedGraphModel(URigVMCollapseNode* CollapseNode, const FName& Name)
|
|
{
|
|
TObjectPtr<URigVMGraph> Model = nullptr;
|
|
|
|
check(CollapseNode);
|
|
|
|
if (ExternalModelHost != nullptr)
|
|
{
|
|
Model = ExternalModelHost->CreateContainedGraphModel(CollapseNode, Name);
|
|
}
|
|
else
|
|
{
|
|
Model = NewObject<URigVMGraph>(CollapseNode, Name);
|
|
|
|
// keep schema from collapse node graph, if exists
|
|
if (CollapseNode->GetGraph() != nullptr && CollapseNode->GetGraph()->GetSchema() != nullptr)
|
|
{
|
|
Model->SetSchemaClass(CollapseNode->GetGraph()->GetSchema()->GetClass());
|
|
}
|
|
else
|
|
{
|
|
Model->SetSchemaClass(GetDefaultSchemaClass());
|
|
}
|
|
}
|
|
|
|
return Model;
|
|
}
|
|
|
|
void FRigVMClient::AddModel(URigVMGraph* InModel, bool bCreateController)
|
|
{
|
|
check(InModel);
|
|
|
|
if(InModel->IsA<URigVMFunctionLibrary>())
|
|
{
|
|
check(FunctionLibrary == nullptr);
|
|
FunctionLibrary = Cast<URigVMFunctionLibrary>(InModel);
|
|
}
|
|
else
|
|
{
|
|
if(ExternalModelHost == nullptr)
|
|
{
|
|
Models.Add(InModel);
|
|
}
|
|
}
|
|
|
|
if(InModel->GetSchemaClass() == nullptr)
|
|
{
|
|
InModel->SetSchemaClass(GetDefaultSchemaClass());
|
|
}
|
|
|
|
InModel->SetExecuteContextStruct(InModel->GetSchema()->GetExecuteContextStruct());
|
|
|
|
if(bCreateController)
|
|
{
|
|
CreateController(InModel);
|
|
}
|
|
|
|
if(InModel->IsA<URigVMFunctionLibrary>())
|
|
{
|
|
for(URigVMGraph* Model : GetModels())
|
|
{
|
|
Model->SetDefaultFunctionLibrary(FunctionLibrary);
|
|
}
|
|
InModel->SetDefaultFunctionLibrary(FunctionLibrary);
|
|
}
|
|
else if(FunctionLibrary)
|
|
{
|
|
InModel->SetDefaultFunctionLibrary(FunctionLibrary);
|
|
}
|
|
|
|
if (GetOuter()->Implements<URigVMClientHost>())
|
|
{
|
|
IRigVMClientHost* ClientHost = Cast<IRigVMClientHost>(GetOuter());
|
|
ClientHost->HandleRigVMGraphAdded(this, InModel->GetNodePath());
|
|
}
|
|
|
|
NotifyOuterOfPropertyChange();
|
|
|
|
}
|
|
|
|
URigVMFunctionLibrary* FRigVMClient::GetOrCreateFunctionLibrary(bool bSetupUndoRedo, const FObjectInitializer* ObjectInitializer, bool bCreateController)
|
|
{
|
|
check(DefaultSchemaClass);
|
|
return GetOrCreateFunctionLibrary(DefaultSchemaClass, bSetupUndoRedo, ObjectInitializer, bCreateController);
|
|
}
|
|
|
|
static void SetGetFunctionHostObjectPathDelegate(FRigVMClient* RigVMClient, URigVMFunctionLibrary* FunctionLibrary)
|
|
{
|
|
if (RigVMClient->GetOuter()->Implements<URigVMClientHost>())
|
|
{
|
|
if (IRigVMClientHost* ClientHost = Cast<IRigVMClientHost>(RigVMClient->GetOuter()))
|
|
{
|
|
TWeakObjectPtr<UObject> WeakClientHost = Cast<UObject>(ClientHost);
|
|
FunctionLibrary->GetFunctionHostObjectPathDelegate.BindLambda([WeakClientHost]() -> const FSoftObjectPath
|
|
{
|
|
if (WeakClientHost.IsValid())
|
|
{
|
|
if (IRigVMClientHost* ClientHost = Cast<IRigVMClientHost>(WeakClientHost.Get()))
|
|
{
|
|
return Cast<UObject>(ClientHost->GetRigVMGraphFunctionHost());
|
|
}
|
|
}
|
|
return nullptr;
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
URigVMFunctionLibrary* FRigVMClient::GetOrCreateFunctionLibrary(TSubclassOf<URigVMSchema> InSchemaClass, bool bSetupUndoRedo, const FObjectInitializer* ObjectInitializer, bool bCreateController)
|
|
{
|
|
if(FunctionLibrary)
|
|
{
|
|
if (!FunctionLibrary->GetFunctionHostObjectPathDelegate.IsBound())
|
|
{
|
|
SetGetFunctionHostObjectPathDelegate(this, FunctionLibrary);
|
|
}
|
|
return FunctionLibrary;
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
TSharedPtr<FScopedTransaction> Transaction;
|
|
if(bSetupUndoRedo)
|
|
{
|
|
Transaction = MakeShared<FScopedTransaction>(NSLOCTEXT("RigVMClient", "AddModel", "Add new root graph"));
|
|
GetOuter()->Modify();
|
|
}
|
|
#endif
|
|
|
|
static constexpr TCHAR FunctionLibraryName[] = TEXT("RigVMFunctionLibrary");
|
|
const FName SafeGraphName = GetUniqueName(FunctionLibraryName);
|
|
URigVMFunctionLibrary* NewFunctionLibrary = nullptr;
|
|
if(ObjectInitializer)
|
|
{
|
|
NewFunctionLibrary = ObjectInitializer->CreateDefaultSubobject<URigVMFunctionLibrary>(GetOuter(), SafeGraphName);
|
|
}
|
|
else
|
|
{
|
|
NewFunctionLibrary = NewObject<URigVMFunctionLibrary>(GetOuter(), SafeGraphName);
|
|
}
|
|
|
|
NewFunctionLibrary->SetSchemaClass(InSchemaClass);
|
|
|
|
if (!NewFunctionLibrary->GetFunctionHostObjectPathDelegate.IsBound())
|
|
{
|
|
SetGetFunctionHostObjectPathDelegate(this, NewFunctionLibrary);
|
|
}
|
|
|
|
AddModel(NewFunctionLibrary, bCreateController);
|
|
return NewFunctionLibrary;
|
|
}
|
|
|
|
TArray<FName> FRigVMClient::GetEntryNames(UScriptStruct* InUnitScriptStructFilter) const
|
|
{
|
|
TArray<FName> EntryNames;
|
|
for(const URigVMGraph* Model : GetModels())
|
|
{
|
|
for(const URigVMNode* Node : Model->GetNodes())
|
|
{
|
|
// Filter out unit nodes that are not of the specified type
|
|
if(InUnitScriptStructFilter != nullptr)
|
|
{
|
|
if(const URigVMUnitNode* UnitNode = Cast<URigVMUnitNode>(Node))
|
|
{
|
|
UScriptStruct* ScriptStruct = UnitNode->GetScriptStruct();
|
|
if(ScriptStruct == nullptr || !ScriptStruct->IsChildOf(InUnitScriptStructFilter))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
const FName EntryName = Node->GetEventName();
|
|
if(!EntryName.IsNone())
|
|
{
|
|
EntryNames.Add(EntryName);
|
|
}
|
|
}
|
|
}
|
|
return EntryNames;
|
|
}
|
|
|
|
UScriptStruct* FRigVMClient::GetDefaultExecuteContextStruct() const
|
|
{
|
|
check(GetDefaultSchema());
|
|
return GetDefaultSchema()->GetExecuteContextStruct();
|
|
}
|
|
|
|
void FRigVMClient::SetDefaultExecuteContextStruct(UScriptStruct* InExecuteContextStruct)
|
|
{
|
|
GetDefaultSchema()->SetExecuteContextStruct(InExecuteContextStruct);
|
|
}
|
|
|
|
URigVMGraph* FRigVMClient::GetFocusedModel() const
|
|
{
|
|
#if WITH_EDITOR
|
|
if (OnGetFocusedGraph().IsBound())
|
|
{
|
|
return OnGetFocusedGraph().Execute();
|
|
}
|
|
#endif
|
|
|
|
return GetDefaultModel();
|
|
}
|
|
|
|
bool FRigVMClient::RemoveModel(FString InName, bool bSetupUndoRedo, bool bPrintPythonCommand)
|
|
{
|
|
return RemoveModel(InName, bSetupUndoRedo);
|
|
}
|
|
|
|
bool FRigVMClient::RemoveModel(const FString& InNodePathOrName, bool bSetupUndoRedo)
|
|
{
|
|
if(URigVMGraph* Model = GetModel(InNodePathOrName))
|
|
{
|
|
UObject* ModelOuter = Model->GetOuter();
|
|
check(ModelOuter);
|
|
|
|
if(Model == GetDefaultModel() && !bDefaultModelCanBeRemoved)
|
|
{
|
|
#if WITH_EDITOR
|
|
static constexpr TCHAR Message[] = TEXT("Cannot remove the default model.");
|
|
FScriptExceptionHandler::Get().HandleException(ELogVerbosity::Error, Message, *FString());
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
if(Model == FunctionLibrary)
|
|
{
|
|
#if WITH_EDITOR
|
|
static constexpr TCHAR Message[] = TEXT("Cannot remove the function library.");
|
|
FScriptExceptionHandler::Get().HandleException(ELogVerbosity::Error, Message, *FString());
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
TSharedPtr<FScopedTransaction> Transaction;
|
|
if(bSetupUndoRedo)
|
|
{
|
|
Transaction = MakeShared<FScopedTransaction>(NSLOCTEXT("RigVMClient", "RemoveModel", "Remove a root graph"));
|
|
ModelOuter->Modify();
|
|
}
|
|
#endif
|
|
|
|
if (GetOuter()->Implements<URigVMClientHost>())
|
|
{
|
|
IRigVMClientHost* ClientHost = Cast<IRigVMClientHost>(GetOuter());
|
|
ClientHost->HandleRigVMGraphRemoved(this, InNodePathOrName);
|
|
}
|
|
|
|
if(bSetupUndoRedo)
|
|
{
|
|
ModelOuter->Modify();
|
|
UndoRedoIndex++;
|
|
|
|
FRigVMClientAction Action;
|
|
Action.Type = ERigVMClientAction_RemoveModel;
|
|
Action.NodePath = Model->GetNodePath();
|
|
UndoStack.Push(Action);
|
|
RedoStack.Reset();
|
|
}
|
|
|
|
// clean up the model
|
|
if(ExternalModelHost == nullptr)
|
|
{
|
|
verify(Models.Remove(Model));
|
|
}
|
|
else
|
|
{
|
|
// Should have already been removed from external models
|
|
verify(!ExternalModelHost->GetExternalModels().Contains(Model));
|
|
}
|
|
|
|
NotifyOuterOfPropertyChange();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
FName FRigVMClient::RenameModel(const FString& InNodePathOrName, const FName& InNewName, bool bSetupUndoRedo)
|
|
{
|
|
if(URigVMGraph* Model = GetModel(InNodePathOrName))
|
|
{
|
|
if(Model == FunctionLibrary)
|
|
{
|
|
#if WITH_EDITOR
|
|
static constexpr TCHAR Message[] = TEXT("Cannot rename the function library.");
|
|
FScriptExceptionHandler::Get().HandleException(ELogVerbosity::Error, Message, *FString());
|
|
#endif
|
|
return NAME_None;
|
|
}
|
|
|
|
if(Model->GetFName() == InNewName)
|
|
{
|
|
return InNewName;
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
TSharedPtr<FScopedTransaction> Transaction;
|
|
if(bSetupUndoRedo)
|
|
{
|
|
Transaction = MakeShared<FScopedTransaction>(NSLOCTEXT("RigVMClient", "RenameModel", "Rename a root graph"));
|
|
}
|
|
#endif
|
|
|
|
TObjectPtr<URigVMController>* Controller = Controllers.Find(Model);
|
|
const FString OldNodePath = Model->GetNodePath();
|
|
const FName SafeNewName = GetUniqueName(InNewName);
|
|
Model->Rename(*SafeNewName.ToString(), nullptr, REN_DontCreateRedirectors);
|
|
const FString NewNodePath = Model->GetNodePath();
|
|
if (Controller)
|
|
{
|
|
Controllers.Add(Model, *Controller);
|
|
}
|
|
|
|
if(bSetupUndoRedo)
|
|
{
|
|
GetOuter()->Modify();
|
|
UndoRedoIndex++;
|
|
|
|
FRigVMClientAction Action;
|
|
Action.Type = ERigVMClientAction_RenameModel;
|
|
Action.NodePath = OldNodePath;
|
|
Action.OtherNodePath = NewNodePath;
|
|
UndoStack.Push(Action);
|
|
RedoStack.Reset();
|
|
}
|
|
|
|
if (GetOuter()->Implements<URigVMClientHost>())
|
|
{
|
|
IRigVMClientHost* ClientHost = Cast<IRigVMClientHost>(GetOuter());
|
|
ClientHost->HandleRigVMGraphRenamed(this, OldNodePath, NewNodePath);
|
|
}
|
|
|
|
NotifyOuterOfPropertyChange();
|
|
return SafeNewName;
|
|
}
|
|
|
|
return NAME_None;
|
|
}
|
|
|
|
void FRigVMClient::PostTransacted(const FTransactionObjectEvent& TransactionEvent)
|
|
{
|
|
IRigVMClientHost* ClientHost = CastChecked<IRigVMClientHost>(GetOuter());
|
|
|
|
if (TransactionEvent.GetEventType() == ETransactionObjectEventType::UndoRedo)
|
|
{
|
|
auto PerformAction = [this, ClientHost](const FRigVMClientAction& InAction, bool bUndo)
|
|
{
|
|
switch(InAction.Type)
|
|
{
|
|
case ERigVMClientAction_AddModel:
|
|
case ERigVMClientAction_RemoveModel:
|
|
{
|
|
if(InAction.Type == ERigVMClientAction_RemoveModel)
|
|
{
|
|
bUndo = !bUndo;
|
|
}
|
|
|
|
if(URigVMGraph* Model = GetModel(InAction.NodePath))
|
|
{
|
|
if(bUndo)
|
|
{
|
|
ClientHost->HandleRigVMGraphRemoved(this, InAction.NodePath);
|
|
}
|
|
else
|
|
{
|
|
URigVMController* Controller = GetOrCreateController(Model);
|
|
ClientHost->HandleRigVMGraphAdded(this, InAction.NodePath);
|
|
if(Controller)
|
|
{
|
|
Controller->ResendAllNotifications();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ERigVMClientAction_RenameModel:
|
|
{
|
|
const FString NodePathA = bUndo ? InAction.OtherNodePath : InAction.NodePath;
|
|
const FString NodePathB = bUndo ? InAction.NodePath : InAction.OtherNodePath;
|
|
FString NodeNameB = NodePathB;
|
|
NodeNameB.Split(TEXT("|"), nullptr, &NodeNameB, ESearchCase::IgnoreCase, ESearchDir::FromEnd);
|
|
NodeNameB.RemoveFromEnd(TEXT("::"));
|
|
|
|
RenameModel(NodePathA, *NodeNameB, false);
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
while(UndoStack.Num() != UndoRedoIndex)
|
|
{
|
|
// do we need to undo - or redo?
|
|
if(UndoStack.Num() > UndoRedoIndex)
|
|
{
|
|
FRigVMClientAction Action = UndoStack.Pop();
|
|
PerformAction(Action, true);
|
|
RedoStack.Push(Action);
|
|
}
|
|
else
|
|
{
|
|
FRigVMClientAction Action = RedoStack.Pop();
|
|
PerformAction(Action, false);
|
|
UndoStack.Push(Action);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FRigVMClient::OnCollapseNodeRenamed(const URigVMCollapseNode* InCollapseNode)
|
|
{
|
|
TMap<FSoftObjectPath, TObjectPtr<URigVMController>> OldControllers = Controllers;
|
|
for(const TPair<FSoftObjectPath, TObjectPtr<URigVMController>>& Pair : OldControllers)
|
|
{
|
|
if(URigVMController* Controller = Pair.Value)
|
|
{
|
|
if(const URigVMGraph* Graph = Controller->GetGraph())
|
|
{
|
|
if(Graph->IsInOuter(InCollapseNode))
|
|
{
|
|
const FSoftObjectPath& OldObjectPath = Pair.Key;
|
|
const FSoftObjectPath NewObjectPath(Graph);
|
|
|
|
if(OldObjectPath != NewObjectPath)
|
|
{
|
|
Controllers.Add(NewObjectPath, Controller);
|
|
Controllers.Remove(OldObjectPath);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FRigVMClient::OnCollapseNodeRemoved(const URigVMCollapseNode* InCollapseNode)
|
|
{
|
|
TMap<FSoftObjectPath, TObjectPtr<URigVMController>> OldControllers = Controllers;
|
|
for(const TPair<FSoftObjectPath, TObjectPtr<URigVMController>>& Pair : OldControllers)
|
|
{
|
|
if(URigVMController* Controller = Pair.Value)
|
|
{
|
|
if(const URigVMGraph* Graph = Controller->GetGraph())
|
|
{
|
|
if(Graph->IsInOuter(InCollapseNode))
|
|
{
|
|
RemoveController(Graph);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
URigVMNode* FRigVMClient::FindNode(const FString& InNodePathOrName) const
|
|
{
|
|
for(const URigVMGraph* Model : GetModels())
|
|
{
|
|
if(URigVMNode* Node = Model->FindNode(InNodePathOrName))
|
|
{
|
|
return Node;
|
|
}
|
|
}
|
|
if(FunctionLibrary)
|
|
{
|
|
return FunctionLibrary->FindNode(InNodePathOrName);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
URigVMPin* FRigVMClient::FindPin(const FString& InPinPath) const
|
|
{
|
|
for(const URigVMGraph* Model : GetModels())
|
|
{
|
|
if(URigVMPin* Pin = Model->FindPin(InPinPath))
|
|
{
|
|
return Pin;
|
|
}
|
|
}
|
|
if(FunctionLibrary)
|
|
{
|
|
return FunctionLibrary->FindPin(InPinPath);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
UObject* FRigVMClient::GetOuter() const
|
|
{
|
|
UObject* Outer = OuterClientHost.Get();
|
|
check(Outer);
|
|
return Outer;
|
|
}
|
|
|
|
FProperty* FRigVMClient::GetOuterClientProperty() const
|
|
{
|
|
return GetOuter()->GetClass()->FindPropertyByName(OuterClientPropertyName);
|
|
}
|
|
|
|
void FRigVMClient::NotifyOuterOfPropertyChange(EPropertyChangeType::Type ChangeType) const
|
|
{
|
|
if(bSuspendNotifications)
|
|
{
|
|
return;
|
|
}
|
|
FProperty* Property = GetOuterClientProperty();
|
|
FPropertyChangedEvent PropertyChangedEvent(Property, ChangeType);
|
|
GetOuter()->PostEditChangeProperty(PropertyChangedEvent);
|
|
}
|
|
|
|
URigVMController* FRigVMClient::CreateController(const URigVMGraph* InModel)
|
|
{
|
|
const FString ModelName = InModel ? InModel->GetName() : TEXT("__NullGraph");
|
|
const FName SafeControllerName = GetUniqueName(*FString::Printf(TEXT("%s_Controller"), *ModelName));
|
|
URigVMController* Controller = NewObject<URigVMController>(GetOuter(), ControllerClass, SafeControllerName);
|
|
Controllers.Add(InModel, Controller);
|
|
if(InModel && InModel->GetSchemaClass() != nullptr)
|
|
{
|
|
Controller->SetSchemaClass(InModel->GetSchemaClass());
|
|
}
|
|
else
|
|
{
|
|
check(DefaultSchemaClass);
|
|
Controller->SetSchemaClass(DefaultSchemaClass);
|
|
}
|
|
|
|
Controller->SetActionStack(GetOrCreateActionStack());
|
|
if(InModel)
|
|
{
|
|
Controller->SetGraph((URigVMGraph*)InModel);
|
|
}
|
|
Controller->OnModified().AddLambda([this](ERigVMGraphNotifType InNotifType, URigVMGraph* InGraph, UObject* InSubject)
|
|
{
|
|
HandleGraphModifiedEvent(InNotifType, InGraph, InSubject);
|
|
});
|
|
|
|
if (GetOuter()->Implements<URigVMClientHost>())
|
|
{
|
|
IRigVMClientHost* ClientHost = Cast<IRigVMClientHost>(GetOuter());
|
|
ClientHost->HandleConfigureRigVMController(this, Controller);
|
|
}
|
|
|
|
if(InModel)
|
|
{
|
|
Controller->RemoveStaleNodes();
|
|
}
|
|
return Controller;
|
|
}
|
|
|
|
URigVMActionStack* FRigVMClient::GetOrCreateActionStack()
|
|
{
|
|
if(UObject* Outer = GetOuter())
|
|
{
|
|
if (ActionStack && ActionStack->GetOuter() != Outer)
|
|
{
|
|
ResetActionStack();
|
|
}
|
|
|
|
if(ActionStack == nullptr)
|
|
{
|
|
ActionStack = NewObject<URigVMActionStack>(Outer, TEXT("ActionStack"));
|
|
}
|
|
}
|
|
return ActionStack;
|
|
}
|
|
|
|
void FRigVMClient::ResetActionStack()
|
|
{
|
|
DestroyObject(ActionStack);
|
|
ActionStack = nullptr;
|
|
}
|
|
|
|
FName FRigVMClient::GetUniqueName(const FName& InDesiredName) const
|
|
{
|
|
return GetUniqueName(GetOuter(), InDesiredName);
|
|
}
|
|
|
|
FName FRigVMClient::GetUniqueName(UObject* InOuter, const FName& InDesiredName)
|
|
{
|
|
return URigVMSchema::GetUniqueName(*InDesiredName.ToString(), [InOuter](const FName& InName) -> bool
|
|
{
|
|
return FindObjectWithOuter(InOuter, nullptr, InName) == nullptr;
|
|
}, false, true);
|
|
}
|
|
|
|
void FRigVMClient::DestroyObject(UObject* InObject)
|
|
{
|
|
if(InObject)
|
|
{
|
|
static int32 ObjectIndexToBeDestroyed = 0;
|
|
static constexpr TCHAR ObjectNameFormat[] = TEXT("RigVMClient_ObjectToBeDestroyed_%d");
|
|
const FString NewObjectName = FString::Printf(ObjectNameFormat, ObjectIndexToBeDestroyed++);
|
|
InObject->Rename(*NewObjectName, GetTransientPackage(), REN_DontCreateRedirectors);
|
|
if(!InObject->IsRooted())
|
|
{
|
|
InObject->MarkAsGarbage();
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32 FRigVMClient::GetStructureHash() const
|
|
{
|
|
uint32 Hash = 0;
|
|
const TArray<URigVMGraph*> ModelsAndFunctionLibrary = GetAllModels(true, true);
|
|
for(const URigVMGraph* Model : ModelsAndFunctionLibrary)
|
|
{
|
|
Hash = HashCombine(Hash, Model->GetStructureHash());
|
|
}
|
|
return Hash;
|
|
}
|
|
|
|
uint32 FRigVMClient::GetSerializedStructureHash() const
|
|
{
|
|
uint32 Hash = 0;
|
|
const TArray<URigVMGraph*> ModelsAndFunctionLibrary = GetAllModels(true, true);
|
|
for(const URigVMGraph* Model : ModelsAndFunctionLibrary)
|
|
{
|
|
Hash = HashCombine(Hash, Model->GetSerializedStructureHash());
|
|
}
|
|
return Hash;
|
|
}
|
|
|
|
FRigVMClientPatchResult FRigVMClient::PatchModelsOnLoad()
|
|
{
|
|
FRigVMClientPatchResult Result;
|
|
|
|
TArray<URigVMGraph*> AllModels = GetAllModelsLeavesFirst(true);
|
|
TGuardValue<bool> ClientIgnoreModificationsGuard(bIgnoreModelNotifications, true);
|
|
for(URigVMGraph* Model : AllModels)
|
|
{
|
|
Model->PostLoad();
|
|
Model->SetSchemaClass(GetDefaultSchemaClass());
|
|
|
|
URigVMController* Controller = GetOrCreateController(Model);
|
|
TGuardValue<bool> GuardSuspendTemplateComputation(Controller->bSuspendTemplateComputation, true);
|
|
TGuardValue<bool> GuardIsTransacting(Controller->bIsTransacting, true);
|
|
{
|
|
FRigVMDefaultValueTypeGuard DefaultValueTypeGuard(Controller, ERigVMPinDefaultValueType::KeepValueType);
|
|
Result.Merge(Controller->PatchLocalVariableTypes());
|
|
Result.Merge(Controller->PatchRerouteNodesOnLoad());
|
|
Result.Merge(Controller->PatchUnitNodesOnLoad());
|
|
Result.Merge(Controller->PatchDispatchNodesOnLoad());
|
|
Result.Merge(Controller->PatchBranchNodesOnLoad());
|
|
Result.Merge(Controller->PatchIfSelectNodesOnLoad());
|
|
Result.Merge(Controller->PatchArrayNodesOnLoad());
|
|
Result.Merge(Controller->PatchReduceArrayFloatDoubleConvertsionsOnLoad());
|
|
Result.Merge(Controller->PatchInvalidLinksOnWildcards());
|
|
Result.Merge(Controller->PatchExecutePins());
|
|
Result.Merge(Controller->PatchLazyPins());
|
|
Result.Merge(Controller->PatchUserDefinedStructPinNames());
|
|
|
|
if (URigVMCollapseNode* CollapseNode = Model->GetTypedOuter<URigVMCollapseNode>())
|
|
{
|
|
Result.Merge(Controller->PatchFunctionsWithInvalidReturnPaths());
|
|
}
|
|
}
|
|
Result.Merge(Controller->PatchPinDefaultValues());
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
void FRigVMClient::PatchFunctionReferencesOnLoad()
|
|
{
|
|
// If the asset was copied from one project to another, the function referenced might have a different
|
|
// path, even if the function is internal to the contorl rig. In that case, let's try to find the function
|
|
// in the local function library.
|
|
|
|
for (URigVMGraph* Model : *this)
|
|
{
|
|
TArray<URigVMNode*> Nodes = Model->GetNodes();
|
|
for (URigVMLibraryNode* Library : GetFunctionLibrary()->GetFunctions())
|
|
{
|
|
Nodes.Append(Library->GetContainedNodes());
|
|
}
|
|
|
|
for (int32 i = 0; i < Nodes.Num(); ++i)
|
|
{
|
|
URigVMNode* Node = Nodes[i];
|
|
if (URigVMFunctionReferenceNode* FunctionReferenceNode = Cast<URigVMFunctionReferenceNode>(Node))
|
|
{
|
|
if (!FunctionReferenceNode->ReferencedNodePtr_DEPRECATED.IsValid())
|
|
{
|
|
(void)FunctionReferenceNode->ReferencedNodePtr_DEPRECATED.LoadSynchronous();
|
|
}
|
|
if (!FunctionReferenceNode->ReferencedNodePtr_DEPRECATED)
|
|
{
|
|
if (FunctionLibrary)
|
|
{
|
|
FString FunctionPath = FunctionReferenceNode->ReferencedNodePtr_DEPRECATED.ToSoftObjectPath().GetSubPathString();
|
|
|
|
FString Left, Right;
|
|
if (FunctionPath.Split(TEXT("."), &Left, &Right))
|
|
{
|
|
FString LibraryNodePath = FunctionLibrary->GetNodePath();
|
|
if (Left == FunctionLibrary->GetName())
|
|
{
|
|
if (URigVMLibraryNode* LibraryNode = Cast<URigVMLibraryNode>(FunctionLibrary->FindNode(Right)))
|
|
{
|
|
FunctionReferenceNode->ReferencedNodePtr_DEPRECATED = LibraryNode;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FunctionReferenceNode->ReferencedNodePtr_DEPRECATED.IsValid())
|
|
{
|
|
FunctionReferenceNode->ReferencedFunctionHeader = FunctionReferenceNode->ReferencedNodePtr_DEPRECATED->GetFunctionHeader();
|
|
}
|
|
else if (!FunctionReferenceNode->ReferencedNodePtr_DEPRECATED.IsNull())
|
|
{
|
|
// At least lets make sure we store the path in the header
|
|
FunctionReferenceNode->ReferencedFunctionHeader.LibraryPointer.SetLibraryNodePath(FunctionReferenceNode->ReferencedNodePtr_DEPRECATED.ToSoftObjectPath().ToString());
|
|
}
|
|
|
|
if (FunctionReferenceNode->ReferencedFunctionHeader.LibraryPointer.LibraryNode_DEPRECATED.IsValid())
|
|
{
|
|
FunctionReferenceNode->ReferencedFunctionHeader.LibraryPointer.SetLibraryNodePath(FunctionReferenceNode->ReferencedFunctionHeader.LibraryPointer.LibraryNode_DEPRECATED.ToString());
|
|
}
|
|
|
|
for (URigVMPin* Pin : FunctionReferenceNode->GetPins())
|
|
{
|
|
if (Pin->IsArray())
|
|
{
|
|
Pin->bIsDynamicArray = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (URigVMCollapseNode* CollapseNode = Cast<URigVMCollapseNode>(Node))
|
|
{
|
|
Nodes.Append(CollapseNode->GetContainedNodes());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // WITH_EDITOR
|
|
|
|
void FRigVMClient::PatchFunctionsOnLoad(IRigVMGraphFunctionHost* FunctionHost, TArray<FName>& BackwardsCompatiblePublicFunctions, TMap<URigVMLibraryNode*, FRigVMGraphFunctionHeader>& OldHeaders)
|
|
{
|
|
if (FRigVMGraphFunctionStore* Store = FunctionHost->GetRigVMGraphFunctionStore())
|
|
{
|
|
// Lets rebuild the FunctionStore from the model
|
|
if (FunctionLibrary)
|
|
{
|
|
Store->PublicFunctions.Reset();
|
|
Store->PrivateFunctions.Reset();
|
|
|
|
for (URigVMLibraryNode* LibraryNode : FunctionLibrary->GetFunctions())
|
|
{
|
|
// Fix array pins to be marked as dynamic array
|
|
for (URigVMPin* Pin : LibraryNode->GetPins())
|
|
{
|
|
if (Pin->IsArray())
|
|
{
|
|
Pin->bIsDynamicArray = true;
|
|
}
|
|
}
|
|
if (URigVMFunctionEntryNode* EntryNode = LibraryNode->GetEntryNode())
|
|
{
|
|
for (URigVMPin* Pin : EntryNode->GetPins())
|
|
{
|
|
if (Pin->IsArray())
|
|
{
|
|
Pin->bIsDynamicArray = true;
|
|
}
|
|
}
|
|
}
|
|
if (URigVMFunctionReturnNode* ReturnNode = LibraryNode->GetReturnNode())
|
|
{
|
|
for (URigVMPin* Pin : ReturnNode->GetPins())
|
|
{
|
|
if (Pin->IsArray())
|
|
{
|
|
Pin->bIsDynamicArray = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool bIsPublic = FunctionLibrary->IsFunctionPublic(LibraryNode->GetFName());
|
|
if (!bIsPublic)
|
|
{
|
|
bIsPublic = BackwardsCompatiblePublicFunctions.Contains(LibraryNode->GetFName());
|
|
if (bIsPublic)
|
|
{
|
|
FunctionLibrary->PublicFunctionNames.Add(LibraryNode->GetFName());
|
|
}
|
|
}
|
|
|
|
FRigVMGraphFunctionHeader Header = LibraryNode->GetFunctionHeader(FunctionHost);
|
|
if (FRigVMGraphFunctionHeader* OldHeader = OldHeaders.Find(LibraryNode))
|
|
{
|
|
Header.ExternalVariables = OldHeader->ExternalVariables;
|
|
Header.Dependencies = OldHeader->Dependencies;
|
|
Header.Layout = OldHeader->Layout;
|
|
}
|
|
|
|
const FRigVMVariant* Variant = FunctionLibrary->GetFunctionVariant(LibraryNode->GetFName());
|
|
if (!Variant)
|
|
{
|
|
Header.Variant.Guid = FRigVMVariant::GenerateGUID(Header.LibraryPointer.GetLibraryNodePath());
|
|
FunctionLibrary->FunctionToVariant.FindOrAdd(Header.Name) = Header.Variant;
|
|
}
|
|
else
|
|
{
|
|
Header.Variant = *Variant;
|
|
}
|
|
|
|
FRigVMGraphFunctionData* FunctionData = Store->AddFunction(Header, bIsPublic);
|
|
if (bIsPublic)
|
|
{
|
|
UpdateGraphFunctionSerializedGraph(LibraryNode);
|
|
}
|
|
else
|
|
{
|
|
FunctionData->SerializedCollapsedNode_DEPRECATED.Empty();
|
|
FunctionData->CollapseNodeArchive.Empty();
|
|
}
|
|
}
|
|
|
|
// Update dependencies and external variables if needed
|
|
for (URigVMLibraryNode* LibraryNode : FunctionLibrary->GetFunctions())
|
|
{
|
|
UpdateExternalVariablesForFunction(LibraryNode);
|
|
UpdateDependenciesForFunction(LibraryNode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FRigVMClientPatchResult FRigVMClient::PatchPinDefaultValues()
|
|
{
|
|
FRigVMClientPatchResult Result;
|
|
|
|
TArray<URigVMGraph*> AllModels = GetAllModelsLeavesFirst(true);
|
|
TGuardValue<bool> ClientIgnoreModificationsGuard(bIgnoreModelNotifications, true);
|
|
for(URigVMGraph* Model : AllModels)
|
|
{
|
|
URigVMController* Controller = GetOrCreateController(Model);
|
|
TGuardValue<bool> GuardIsTransacting(Controller->bIsTransacting, true);
|
|
Result.Merge(Controller->PatchPinDefaultValues());
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
void FRigVMClient::ProcessDetachedLinks()
|
|
{
|
|
TArray<URigVMGraph*> AllModels = GetAllModels(true, true);
|
|
for(URigVMGraph* Model : AllModels)
|
|
{
|
|
URigVMController* Controller = GetOrCreateController(Model);
|
|
Controller->ProcessDetachedLinks();
|
|
}
|
|
}
|
|
|
|
void FRigVMClient::PreSave(FObjectPreSaveContext ObjectSaveContext)
|
|
{
|
|
if (!ObjectSaveContext.IsCooking())
|
|
{
|
|
if (IRigVMClientHost* ClientHost = Cast<IRigVMClientHost>(GetOuter()))
|
|
{
|
|
if (IRigVMGraphFunctionHost* FunctionHost = ClientHost->GetRigVMGraphFunctionHost())
|
|
{
|
|
if (FRigVMGraphFunctionStore* Store = FunctionHost->GetRigVMGraphFunctionStore())
|
|
{
|
|
for (URigVMNode* Node : FunctionLibrary->GetNodes())
|
|
{
|
|
if (URigVMLibraryNode* LibraryNode = Cast<URigVMLibraryNode>(Node))
|
|
{
|
|
FRigVMGraphFunctionIdentifier Identifier = LibraryNode->GetFunctionIdentifier();
|
|
if (Store->IsFunctionPublic(Identifier))
|
|
{
|
|
UpdateGraphFunctionSerializedGraph(LibraryNode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FRigVMClient::HandleGraphModifiedEvent(ERigVMGraphNotifType InNotifType, URigVMGraph* InGraph, UObject* InSubject)
|
|
{
|
|
if (bIgnoreModelNotifications)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
|
|
IRigVMClientHost* ClientHost = Cast<IRigVMClientHost>(GetOuter());
|
|
IRigVMGraphFunctionHost* FunctionHost = nullptr;
|
|
FRigVMGraphFunctionStore* FunctionStore = nullptr;
|
|
if (ClientHost)
|
|
{
|
|
FunctionHost = ClientHost->GetRigVMGraphFunctionHost();
|
|
if (FunctionHost)
|
|
{
|
|
FunctionStore = FunctionHost->GetRigVMGraphFunctionStore();
|
|
}
|
|
}
|
|
|
|
switch (InNotifType)
|
|
{
|
|
case ERigVMGraphNotifType::NodeAdded: // A node has been added to the graph (Subject == URigVMNode)
|
|
{
|
|
// A node was added directly into the function library
|
|
if(InGraph->IsA<URigVMFunctionLibrary>())
|
|
{
|
|
if (URigVMCollapseNode* CollapseNode = Cast<URigVMCollapseNode>(InSubject))
|
|
{
|
|
if (GetOuter()->Implements<URigVMClientHost>())
|
|
{
|
|
if (FunctionStore && FunctionHost)
|
|
{
|
|
FunctionStore->AddFunction(CollapseNode->GetFunctionHeader(FunctionHost), false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// A node was added into the contained graph of a function
|
|
else if(URigVMLibraryNode* LibraryNode = Cast<URigVMNode>(InSubject)->FindFunctionForNode())
|
|
{
|
|
DirtyGraphFunctionCompilationData(LibraryNode);
|
|
if (URigVMFunctionReferenceNode* FunctionReference = Cast<URigVMFunctionReferenceNode>(InSubject))
|
|
{
|
|
UpdateDependenciesForFunction(LibraryNode);
|
|
UpdateExternalVariablesForFunction(LibraryNode);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ERigVMGraphNotifType::NodeRemoved: // A node has been removed from the graph (Subject == URigVMNode)
|
|
{
|
|
if(InSubject->GetOuter()->IsA<URigVMFunctionLibrary>())
|
|
{
|
|
if (URigVMCollapseNode* CollapseNode = Cast<URigVMCollapseNode>(InSubject))
|
|
{
|
|
if (GetOuter()->Implements<URigVMClientHost>())
|
|
{
|
|
if (FunctionStore && FunctionHost)
|
|
{
|
|
FunctionStore->RemoveFunction(FRigVMGraphFunctionIdentifier (Cast<UObject>(FunctionHost), CollapseNode->GetPathName()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// A node was added into the contained graph of a function
|
|
else if(URigVMLibraryNode* LibraryNode = Cast<URigVMNode>(InSubject)->FindFunctionForNode())
|
|
{
|
|
DirtyGraphFunctionCompilationData(LibraryNode);
|
|
if (URigVMFunctionReferenceNode* FunctionReference = Cast<URigVMFunctionReferenceNode>(InSubject))
|
|
{
|
|
UpdateDependenciesForFunction(LibraryNode);
|
|
UpdateExternalVariablesForFunction(LibraryNode);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ERigVMGraphNotifType::VariableAdded: // A variable has been added (Subject == URigVMVariableNode)
|
|
case ERigVMGraphNotifType::VariableRemoved: // A variable has been removed (Subject == URigVMVariableNode)
|
|
case ERigVMGraphNotifType::VariableRenamed: // A variable has been renamed (Subject == URigVMVariableNode)
|
|
case ERigVMGraphNotifType::VariableRemappingChanged: // A function reference node's remapping has changed (Subject == URigVMFunctionReferenceNode)
|
|
{
|
|
if(URigVMLibraryNode* LibraryNode = Cast<URigVMNode>(InSubject)->FindFunctionForNode())
|
|
{
|
|
DirtyGraphFunctionCompilationData(LibraryNode);
|
|
UpdateExternalVariablesForFunction(LibraryNode);
|
|
}
|
|
break;
|
|
}
|
|
case ERigVMGraphNotifType::FunctionRenamed: // A node has been renamed in the graph (Subject == URigVMNode)
|
|
{
|
|
if(InSubject->GetOuter()->IsA<URigVMFunctionLibrary>())
|
|
{
|
|
if (URigVMCollapseNode* CollapseNode = Cast<URigVMCollapseNode>(InSubject))
|
|
{
|
|
URigVMBuildData* BuildData = URigVMBuildData::Get();
|
|
if (IRigVMGraphFunctionHost* Host = Cast<IRigVMGraphFunctionHost>(CollapseNode->GetFunctionIdentifier().HostObject.ResolveObject()))
|
|
{
|
|
if (FRigVMGraphFunctionData* Data = Host->GetRigVMGraphFunctionStore()->FindFunctionByName(CollapseNode->GetPreviousFName()))
|
|
{
|
|
const FRigVMGraphFunctionIdentifier PreviousFunctionId = Data->Header.LibraryPointer;
|
|
const FRigVMVariant Variant = Data->Header.Variant;
|
|
Data->Header = CollapseNode->GetFunctionHeader();
|
|
Data->Header.Variant = Variant;
|
|
|
|
if (const FRigVMFunctionReferenceArray* FunctionReferencesPtr = BuildData->GraphFunctionReferences.Find(PreviousFunctionId))
|
|
{
|
|
const FRigVMFunctionReferenceArray& FunctionReferences = *FunctionReferencesPtr;
|
|
BuildData->Modify();
|
|
FRigVMFunctionReferenceArray& NewFunctionReferences = BuildData->GraphFunctionReferences.Add(Data->Header.LibraryPointer, FunctionReferences);
|
|
BuildData->GraphFunctionReferences.Remove(PreviousFunctionId);
|
|
BuildData->MarkPackageDirty();
|
|
|
|
for (int32 i=0; i<NewFunctionReferences.Num(); ++i)
|
|
{
|
|
if (!NewFunctionReferences[i].IsValid())
|
|
{
|
|
NewFunctionReferences[i].LoadSynchronous();
|
|
}
|
|
if (NewFunctionReferences[i].IsValid())
|
|
{
|
|
NewFunctionReferences[i]->ReferencedFunctionHeader = Data->Header;
|
|
NewFunctionReferences[i]->MarkPackageDirty();
|
|
}
|
|
}
|
|
}
|
|
|
|
UpdateFunctionReferences(Data->Header, true, false);
|
|
UpdateGraphFunctionData(CollapseNode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ERigVMGraphNotifType::NodeColorChanged: // A node's color has changed (Subject == URigVMNode)
|
|
case ERigVMGraphNotifType::NodeCategoryChanged: // A node's category has changed (Subject == URigVMNode)
|
|
case ERigVMGraphNotifType::NodeKeywordsChanged: // A node's keywords have changed (Subject == URigVMNode)
|
|
case ERigVMGraphNotifType::NodeDescriptionChanged: // A node's description has changed (Subject == URigVMNode)
|
|
case ERigVMGraphNotifType::NodeTitleChanged: // A node's title has changed (Subject == URigVMNode)
|
|
case ERigVMGraphNotifType::VariantTagsChanged: // The tags in the header of this function variant have changed (Subject == URigVMLibraryNode)
|
|
{
|
|
if(InSubject->GetOuter()->IsA<URigVMFunctionLibrary>())
|
|
{
|
|
if (URigVMCollapseNode* CollapseNode = Cast<URigVMCollapseNode>(InSubject))
|
|
{
|
|
UpdateGraphFunctionData(CollapseNode);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ERigVMGraphNotifType::PinAdded: // A pin has been added to a given node (Subject == URigVMPin)
|
|
case ERigVMGraphNotifType::PinRemoved: // A pin has been removed from a given node (Subject == URigVMPin)
|
|
case ERigVMGraphNotifType::PinRenamed: // A pin has been renamed (Subject == URigVMPin)
|
|
case ERigVMGraphNotifType::PinArraySizeChanged: // An array pin's size has changed (Subject == URigVMPin)
|
|
case ERigVMGraphNotifType::PinDefaultValueChanged: // A pin's default value has changed (Subject == URigVMPin)
|
|
case ERigVMGraphNotifType::PinDirectionChanged: // A pin's direction has changed (Subject == URigVMPin)
|
|
case ERigVMGraphNotifType::PinTypeChanged: // A pin's data type has changed (Subject == URigVMPin)
|
|
case ERigVMGraphNotifType::PinIndexChanged: // A pin's index has changed (Subject == URigVMPin)
|
|
case ERigVMGraphNotifType::PinWatchedChanged: // A pin's watch state has changed (Subject == URigVMPin)
|
|
case ERigVMGraphNotifType::PinDisplayNameChanged: // A pin's display name / UI label has changed (Subject == URigVMPin)
|
|
case ERigVMGraphNotifType::PinCategoryChanged: // A pin's category has changed (Subject == URigVMPin)
|
|
case ERigVMGraphNotifType::PinCategoryExpansionChanged: // A pin category has been collapsed / expanded on the function node (Subject == URigVMNode)
|
|
case ERigVMGraphNotifType::FunctionVariantGuidChanged: // A function has changed its guid (Subject == URigVMLibraryNode)
|
|
{
|
|
URigVMNode* Node = Cast<URigVMNode>(InSubject);
|
|
if (const URigVMPin* Pin = Cast<URigVMPin>(InSubject))
|
|
{
|
|
Node = Cast<URigVMNode>(Pin->GetNode());
|
|
}
|
|
if(Node)
|
|
{
|
|
if (URigVMLibraryNode* LibraryNode = Node->FindFunctionForNode())
|
|
{
|
|
DirtyGraphFunctionCompilationData(LibraryNode);
|
|
}
|
|
if(Node->GetOuter()->IsA<URigVMFunctionLibrary>())
|
|
{
|
|
if (const URigVMCollapseNode* CollapseNode = Cast<URigVMCollapseNode>(Node))
|
|
{
|
|
UpdateGraphFunctionData(CollapseNode);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ERigVMGraphNotifType::PinCategoriesChanged: // A node's category list has changed (Subject == URigVMNode)
|
|
{
|
|
if (URigVMNode* Node = Cast<URigVMNode>(InSubject))
|
|
{
|
|
if(Node->GetOuter()->IsA<URigVMFunctionLibrary>())
|
|
{
|
|
if (const URigVMCollapseNode* CollapseNode = Cast<URigVMCollapseNode>(Node))
|
|
{
|
|
UpdateGraphFunctionData(CollapseNode);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ERigVMGraphNotifType::LinkAdded: // A link has been added (Subject == URigVMLink)
|
|
case ERigVMGraphNotifType::LinkRemoved: // A link has been removed (Subject == URigVMLink)
|
|
{
|
|
if (URigVMLink* Link = Cast<URigVMLink>(InSubject))
|
|
{
|
|
if (URigVMNode* OuterNode = Cast<URigVMNode>(Link->GetGraph()->GetOuter()))
|
|
{
|
|
if (URigVMLibraryNode* LibraryNode = OuterNode->FindFunctionForNode())
|
|
{
|
|
DirtyGraphFunctionCompilationData(LibraryNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case ERigVMGraphNotifType::FunctionAccessChanged: // A function was made public/private
|
|
{
|
|
if (URigVMLibraryNode* LibraryNode = Cast<URigVMLibraryNode>(InSubject))
|
|
{
|
|
if (URigVMFunctionLibrary* Library = Cast<URigVMFunctionLibrary>(InGraph))
|
|
{
|
|
bool bIsPublic = Library->IsFunctionPublic(LibraryNode->GetFName());
|
|
if (FRigVMGraphFunctionStore* Store = FindFunctionStore(LibraryNode))
|
|
{
|
|
Store->MarkFunctionAsPublic(LibraryNode->GetFunctionIdentifier(), bIsPublic);
|
|
if(bIsPublic)
|
|
{
|
|
UpdateGraphFunctionSerializedGraph(LibraryNode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
FRigVMGraphFunctionStore* FRigVMClient::FindFunctionStore(const URigVMLibraryNode* InLibraryNode)
|
|
{
|
|
if (IRigVMClientHost* ClientHost = InLibraryNode->GetImplementingOuter<IRigVMClientHost>())
|
|
{
|
|
if (IRigVMGraphFunctionHost* FunctionHost = ClientHost->GetRigVMGraphFunctionHost())
|
|
{
|
|
return FunctionHost->GetRigVMGraphFunctionStore();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool FRigVMClient::UpdateFunctionReferences(const FRigVMGraphFunctionHeader& Header, bool bUpdateDependencies, bool bUpdateExternalVariables)
|
|
{
|
|
URigVMBuildData* BuildData = URigVMBuildData::Get();
|
|
if (const FRigVMFunctionReferenceArray* FunctionReferenceArray = BuildData->FindFunctionReferences(Header.LibraryPointer))
|
|
{
|
|
for (int32 i=0; i<FunctionReferenceArray->Num(); ++i)
|
|
{
|
|
const TSoftObjectPtr<URigVMFunctionReferenceNode>& Reference = FunctionReferenceArray->FunctionReferences[i];
|
|
|
|
// Only update references that are loaded
|
|
// Other references will be updated when they are loaded
|
|
if (Reference.IsValid())
|
|
{
|
|
URigVMFunctionReferenceNode* Node = Reference.Get();
|
|
|
|
Node->Modify();
|
|
Node->ReferencedFunctionHeader = Header;
|
|
Node->InvalidateCache();
|
|
|
|
if (bUpdateDependencies || bUpdateExternalVariables)
|
|
{
|
|
if (URigVMLibraryNode* LibraryNode = Node->FindFunctionForNode())
|
|
{
|
|
IRigVMClientHost* OtherClientHost = LibraryNode->GetImplementingOuter<IRigVMClientHost>();
|
|
if (bUpdateDependencies)
|
|
{
|
|
OtherClientHost->GetRigVMClient()->UpdateDependenciesForFunction(LibraryNode);
|
|
}
|
|
if (bUpdateExternalVariables)
|
|
{
|
|
OtherClientHost->GetRigVMClient()->UpdateExternalVariablesForFunction(LibraryNode);
|
|
}
|
|
}
|
|
}
|
|
Node->MarkPackageDirty();
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool FRigVMClient::UpdateGraphFunctionData(const URigVMLibraryNode* InLibraryNode)
|
|
{
|
|
if (IRigVMClientHost* ClientHost = InLibraryNode->GetImplementingOuter<IRigVMClientHost>())
|
|
{
|
|
if (IRigVMGraphFunctionHost* FunctionHost = ClientHost->GetRigVMGraphFunctionHost())
|
|
{
|
|
if (FRigVMGraphFunctionStore* Store = FindFunctionStore(InLibraryNode))
|
|
{
|
|
if (FRigVMGraphFunctionData* Data = Store->UpdateFunctionInterface(InLibraryNode->GetFunctionHeader(FunctionHost)))
|
|
{
|
|
UpdateFunctionReferences(Data->Header, false, false);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FRigVMClient::UpdateExternalVariablesForFunction(const URigVMLibraryNode* InLibraryNode)
|
|
{
|
|
if (FRigVMGraphFunctionStore* Store = FindFunctionStore(InLibraryNode))
|
|
{
|
|
FRigVMGraphFunctionIdentifier Identifier = InLibraryNode->GetFunctionIdentifier();
|
|
if (Store->UpdateExternalVariables(Identifier, InLibraryNode->GetExternalVariables()))
|
|
{
|
|
const FRigVMGraphFunctionData* Data = Store->FindFunction(Identifier);
|
|
UpdateFunctionReferences(Data->Header, false, true);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FRigVMClient::UpdateDependenciesForFunction(const URigVMLibraryNode* InLibraryNode)
|
|
{
|
|
if (FRigVMGraphFunctionStore* Store = FindFunctionStore(InLibraryNode))
|
|
{
|
|
TMap<FRigVMGraphFunctionIdentifier, uint32> Dependencies = InLibraryNode->GetDependencies();
|
|
FRigVMGraphFunctionIdentifier Identifier = InLibraryNode->GetFunctionIdentifier();
|
|
if (Store->UpdateDependencies(Identifier, Dependencies))
|
|
{
|
|
const FRigVMGraphFunctionData* Data = Store->FindFunction(Identifier);
|
|
UpdateFunctionReferences(Data->Header, true, false);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FRigVMClient::DirtyGraphFunctionCompilationData(URigVMLibraryNode* InLibraryNode)
|
|
{
|
|
if (FRigVMGraphFunctionStore* Store = FindFunctionStore(InLibraryNode))
|
|
{
|
|
FRigVMGraphFunctionIdentifier Identifier = InLibraryNode->GetFunctionIdentifier();
|
|
if (const FRigVMGraphFunctionData* Data = Store->FindFunction(Identifier))
|
|
{
|
|
Store->RemoveFunctionCompilationData(Identifier);
|
|
|
|
// References to this function will check if the compilation hash matches, and will recompile if they
|
|
// see a different compilation hash. No need to dirty their compilation data.
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FRigVMClient::UpdateGraphFunctionSerializedGraph(URigVMLibraryNode* InLibraryNode)
|
|
{
|
|
if (FRigVMGraphFunctionStore* Store = FindFunctionStore(InLibraryNode))
|
|
{
|
|
const FRigVMGraphFunctionIdentifier Identifier = InLibraryNode->GetFunctionIdentifier();
|
|
if (FRigVMGraphFunctionData* Data = Store->FindFunction(Identifier))
|
|
{
|
|
Data->SerializedCollapsedNode_DEPRECATED.Empty();
|
|
|
|
URigVMController* Controller = GetOrCreateController(InLibraryNode->GetGraph());
|
|
check(Controller);
|
|
Data->CollapseNodeArchive.Reset();
|
|
(void)Controller->ExportFunctionToArchive(Identifier.GetFunctionFName(), Data->CollapseNodeArchive);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FRigVMClient::IsFunctionPublic(URigVMLibraryNode* InLibraryNode)
|
|
{
|
|
if (FRigVMGraphFunctionStore* Store = FindFunctionStore(InLibraryNode))
|
|
{
|
|
return Store->IsFunctionPublic(InLibraryNode->GetFunctionIdentifier());
|
|
}
|
|
return false;
|
|
}
|