// Copyright Epic Games, Inc. All Rights Reserved. #include "AnimNextRigVMAssetEditorData.h" #include "AnimNextEdGraph.h" #include "AnimNextEdGraphSchema.h" #include "AnimNextRigVMAsset.h" #include "Compilation/AnimNextRigVMAssetCompileContext.h" #include "Compilation/AnimNextGetFunctionHeaderCompileContext.h" #include "Compilation/AnimNextGetVariableCompileContext.h" #include "Compilation/AnimNextGetGraphCompileContext.h" #include "Compilation/AnimNextProcessGraphCompileContext.h" #include "Entries/AnimNextRigVMAssetEntry.h" #include "AnimNextRigVMAssetSchema.h" #include "AnimNextAssetWorkspaceAssetUserData.h" #include "AnimNextScopedCompilerResults.h" #include "RigVMPythonUtils.h" #include "ExternalPackageHelper.h" #include "IAnimNextRigVMGraphInterface.h" #include "IWorkspaceEditor.h" #include "IWorkspaceEditorModule.h" #include "ObjectTools.h" #include "UncookedOnlyUtils.h" #include "Animation/AnimCompressionTypes.h" #include "Animation/Skeleton.h" #include "DataInterface/AnimNextDataInterface_EditorData.h" #include "Entries/AnimNextDataInterfaceEntry.h" #include "Entries/AnimNextEventGraphEntry.h" #include "Entries/AnimNextVariableEntry.h" #include "Graph/RigUnit_AnimNextBeginExecution.h" #include "Misc/TransactionObjectEvent.h" #include "Module/AnimNextEventGraphSchema.h" #include "Module/RigUnit_AnimNextModuleEvents.h" #include "RigVMModel/RigVMFunctionLibrary.h" #include "RigVMModel/RigVMNotifications.h" #include "RigVMModel/Nodes/RigVMAggregateNode.h" #include "RigVMModel/Nodes/RigVMCollapseNode.h" #include "UObject/AssetRegistryTagsContext.h" #include "DataInterface/AnimNextDataInterface_EditorData.h" #include "RigVMFunctions/Execution/RigVMFunction_UserDefinedEvent.h" #include "Misc/UObjectToken.h" #include "UObject/SavePackage.h" #if WITH_EDITOR #include "FileHelpers.h" #include "PackageSourceControlHelper.h" #endif #define LOCTEXT_NAMESPACE "AnimNextRigVMAssetEditorData" void UAnimNextRigVMAssetEditorData::BroadcastModified(EAnimNextEditorDataNotifType InType, UObject* InSubject) { RequestAutoVMRecompilation(); if(!bSuspendEditorDataNotifications) { ModifiedDelegate.Broadcast(this, InType, InSubject); } } void UAnimNextRigVMAssetEditorData::ReportError(const TCHAR* InMessage) { FScriptExceptionHandler::Get().HandleException(ELogVerbosity::Error, InMessage, TEXT("")); } void UAnimNextRigVMAssetEditorData::ReconstructAllNodes() { // Avoid refreshing EdGraph nodes during cook if (GIsCookerLoadingPackage) { return; } if (GetRigVMClient()->GetDefaultModel() == nullptr) { return; } TArray AllNodes; GetAllNodesOfClass(AllNodes); for (URigVMEdGraphNode* Node : AllNodes) { Node->SetFlags(RF_Transient); } for(URigVMEdGraphNode* Node : AllNodes) { Node->ReconstructNode(); } for (URigVMEdGraphNode* Node : AllNodes) { Node->ClearFlags(RF_Transient); } } void UAnimNextRigVMAssetEditorData::Serialize(FArchive& Ar) { RigVMClient.SetDefaultSchemaClass(UAnimNextRigVMAssetSchema::StaticClass()); RigVMClient.SetOuterClientHost(this, GET_MEMBER_NAME_CHECKED(UAnimNextRigVMAssetEditorData, RigVMClient)); const bool bIsDuplicating = (Ar.GetPortFlags() & PPF_Duplicate) != 0; if (bIsDuplicating) { Ar << Entries; } Super::Serialize(Ar); } void UAnimNextRigVMAssetEditorData::Initialize(bool bRecompileVM) { RigVMClient.bDefaultModelCanBeRemoved = true; RigVMClient.SetDefaultSchemaClass(UAnimNextRigVMAssetSchema::StaticClass()); RigVMClient.SetControllerClass(GetControllerClass()); RigVMClient.SetOuterClientHost(this, GET_MEMBER_NAME_CHECKED(UAnimNextRigVMAssetEditorData, RigVMClient)); RigVMClient.SetExternalModelHost(this); URigVMFunctionLibrary* RigVMFunctionLibrary = nullptr; { TGuardValue DisableClientNotifs(RigVMClient.bSuspendNotifications, true); RigVMFunctionLibrary = RigVMClient.GetOrCreateFunctionLibrary(false); } ensure(RigVMFunctionLibrary->GetFunctionHostObjectPathDelegate.IsBound()); if (RigVMClient.GetController(0) == nullptr) { if(RigVMClient.GetDefaultModel()) { RigVMClient.GetOrCreateController(RigVMClient.GetDefaultModel()); } check(RigVMFunctionLibrary); RigVMClient.GetOrCreateController(RigVMFunctionLibrary); if (!FunctionLibraryEdGraph) { FunctionLibraryEdGraph = NewObject(CastChecked(this), NAME_None, RF_Transactional); FunctionLibraryEdGraph->Schema = UAnimNextEdGraphSchema::StaticClass(); FunctionLibraryEdGraph->bAllowRenaming = 0; FunctionLibraryEdGraph->bEditable = 0; FunctionLibraryEdGraph->bAllowDeletion = 0; FunctionLibraryEdGraph->bIsFunctionDefinition = false; FunctionLibraryEdGraph->ModelNodePath = RigVMClient.GetFunctionLibrary()->GetNodePath(); FunctionLibraryEdGraph->Initialize(this); } // Init function library controllers for(URigVMLibraryNode* LibraryNode : RigVMClient.GetFunctionLibrary()->GetFunctions()) { RigVMClient.GetOrCreateController(LibraryNode->GetContainedGraph()); } if(bRecompileVM) { RecompileVM(); } } for(UAnimNextRigVMAssetEntry* Entry : Entries) { Entry->Initialize(this); } InitializeAssetUserData(); } void UAnimNextRigVMAssetEditorData::InitializeAssetUserData() { if (IInterface_AssetUserData* OuterUserData = Cast(GetOuter())) { if(!OuterUserData->HasAssetUserDataOfClass(GetAssetUserDataClass())) { OuterUserData->AddAssetUserDataOfClass(GetAssetUserDataClass()); } } } void UAnimNextRigVMAssetEditorData::PostLoad() { Super::PostLoad(); // Handle deprecation PRAGMA_DISABLE_DEPRECATION_WARNINGS if (NativeInterface_DEPRECATED) { NativeInterfaces.Add(NativeInterface_DEPRECATED); NativeInterface_DEPRECATED = nullptr; } PRAGMA_ENABLE_DEPRECATION_WARNINGS GraphModels.Reset(); PostLoadExternalPackages(); RefreshExternalModels(); Initialize(/*bRecompileVM*/false); GetRigVMClient()->RefreshAllModels(ERigVMLoadType::PostLoad, false, bIsCompiling); GetRigVMClient()->PatchFunctionReferencesOnLoad(); TMap OldHeaders; TArray BackwardsCompatiblePublicFunctions; GetRigVMClient()->PatchFunctionsOnLoad(this, BackwardsCompatiblePublicFunctions, OldHeaders); // Register function references at RigVMBuildData if (URigVMBuildData* BuildData = URigVMBuildData::Get()) { TArray ReferenceNodeDatas; const TArray AllModels = GetAllModels(); for (URigVMGraph* ModelToVisit : AllModels) { for (URigVMNode* Node : ModelToVisit->GetNodes()) { if (URigVMFunctionReferenceNode* ReferenceNode = Cast(Node)) { ReferenceNodeDatas.Add(FRigVMReferenceNodeData(ReferenceNode)); } } } // update the build data from the current function references for (const FRigVMReferenceNodeData& ReferenceNodeData : ReferenceNodeDatas) { BuildData->RegisterFunctionReference(ReferenceNodeData); } BuildData->ClearInvalidReferences(); } // Mark this as being dirty so that we recompile when needed bVMRecompilationRequired = true; // Queue compilation once the package has been fully loaded // This is necessary in case we have external packages that haven't post-loaded yet // However, if we are duplicating the asset OnEndLoadPackage won't be called FCoreUObjectDelegates::OnEndLoadPackage.AddUObject(this, &UAnimNextRigVMAssetEditorData::HandlePackageDone); } void UAnimNextRigVMAssetEditorData::PostLoadExternalPackages() { if(bUsesExternalPackages) { FExternalPackageHelper::LoadObjectsFromExternalPackages(this, [this](UAnimNextRigVMAssetEntry* InLoadedEntry) { check(IsValid(InLoadedEntry)); InLoadedEntry->Initialize(this); Entries.Add(InLoadedEntry); }); } // Internal entries should be empty if we are externally packaged ensure(!bUsesExternalPackages || InternalEntries.IsEmpty()); // Copy any internal entries to the main entries array Entries.Append(InternalEntries); } void UAnimNextRigVMAssetEditorData::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { Super::PostEditChangeProperty(PropertyChangedEvent); BroadcastModified(EAnimNextEditorDataNotifType::PropertyChanged, this); } void UAnimNextRigVMAssetEditorData::PostTransacted(const FTransactionObjectEvent& TransactionEvent) { Super::PostTransacted(TransactionEvent); if (TransactionEvent.GetEventType() == ETransactionObjectEventType::UndoRedo) { BroadcastModified(EAnimNextEditorDataNotifType::UndoRedo, this); } } void UAnimNextRigVMAssetEditorData::GetAssetRegistryTags(FAssetRegistryTagsContext Context) const { Super::GetAssetRegistryTags(Context); { // We may not have compiled yet, so cache exports if we havent already if (!CachedExports.IsSet()) { CachedExports = FAnimNextAssetRegistryExports(); FAnimNextAssetRegistryExports& OutExports = CachedExports.GetValue(); GetAnimNextAssetRegistryTags(Context, OutExports); UE::AnimNext::UncookedOnly::FUtils::GetAssetVariables(this, OutExports); } FString TagValue; FAnimNextAssetRegistryExports::StaticStruct()->ExportText(TagValue, &CachedExports.GetValue(), nullptr, nullptr, PPF_None, nullptr); Context.AddTag(FAssetRegistryTag(UE::AnimNext::ExportsAnimNextAssetRegistryTag, TagValue, FAssetRegistryTag::TT_Hidden)); } { FRigVMGraphFunctionHeaderArray FunctionExports; UE::AnimNext::UncookedOnly::FUtils::GetAssetFunctions(this, FunctionExports); FString TagValue; const FArrayProperty* HeadersProperty = CastField(FRigVMGraphFunctionHeaderArray::StaticStruct()->FindPropertyByName(TEXT("Headers"))); HeadersProperty->ExportText_Direct(TagValue, &(FunctionExports.Headers), &(FunctionExports.Headers), nullptr, PPF_None, nullptr); Context.AddTag(FAssetRegistryTag(UE::AnimNext::AnimNextPublicGraphFunctionsExportsRegistryTag, TagValue, FAssetRegistryTag::TT_Hidden)); } { // Export user defined events as notifies FString NotifyList = USkeleton::AnimNotifyTagDelimiter; for(FName EventName : RigVMClient.GetEntryNames(FRigVMFunction_UserDefinedEvent::StaticStruct())) { NotifyList += FString::Printf(TEXT("%s%s"), *EventName.ToString(), *USkeleton::AnimNotifyTagDelimiter); } Context.AddTag(FAssetRegistryTag(USkeleton::AnimNotifyTag, NotifyList, FAssetRegistryTag::TT_Hidden)); } } bool UAnimNextRigVMAssetEditorData::Rename(const TCHAR* NewName, UObject* NewOuter, ERenameFlags Flags) { FExternalPackageHelper::FRenameExternalObjectsHelperContext Context(this, Flags); return Super::Rename(NewName, NewOuter, Flags); } void UAnimNextRigVMAssetEditorData::PreDuplicate(FObjectDuplicationParameters& DupParams) { UObject::PreDuplicate(DupParams); FExternalPackageHelper::DuplicateExternalPackages(this, DupParams); } void UAnimNextRigVMAssetEditorData::HandlePackageDone(const FEndLoadPackageContext& Context) { if (!Context.LoadedPackages.Contains(GetPackage())) { return; } HandlePackageDone(); } void UAnimNextRigVMAssetEditorData::HandlePackageDone() { FCoreUObjectDelegates::OnEndLoadPackage.RemoveAll(this); ReconstructAllNodes(); // If this is not executed on a node for whatever reason, it will appear transparent in the editor TGuardValue DisableCompilationNotifications(bSuspendCompilationNotifications, true); RecompileVM(); } void UAnimNextRigVMAssetEditorData::RefreshAllModels(ERigVMLoadType InLoadType) { } void UAnimNextRigVMAssetEditorData::OnRigVMRegistryChanged() { GetRigVMClient()->RefreshAllModels(ERigVMLoadType::PostLoad, false, bIsCompiling); //RebuildGraphFromModel(); // TODO zzz : Move from blueprint to client } void UAnimNextRigVMAssetEditorData::RequestRigVMInit() { // TODO zzz : How we do this on AnimNext ? } URigVMGraph* UAnimNextRigVMAssetEditorData::GetModel(const UEdGraph* InEdGraph) const { return RigVMClient.GetModel(InEdGraph); } URigVMGraph* UAnimNextRigVMAssetEditorData::GetModel(const FString& InNodePath) const { return RigVMClient.GetModel(InNodePath); } URigVMGraph* UAnimNextRigVMAssetEditorData::GetDefaultModel() const { return RigVMClient.GetDefaultModel(); } TArray UAnimNextRigVMAssetEditorData::GetAllModels() const { return RigVMClient.GetAllModels(true, true); } URigVMFunctionLibrary* UAnimNextRigVMAssetEditorData::GetLocalFunctionLibrary() const { return RigVMClient.GetFunctionLibrary(); } URigVMFunctionLibrary* UAnimNextRigVMAssetEditorData::GetOrCreateLocalFunctionLibrary(bool bSetupUndoRedo) { return RigVMClient.GetOrCreateFunctionLibrary(bSetupUndoRedo); } URigVMGraph* UAnimNextRigVMAssetEditorData::AddModel(FString InName, bool bSetupUndoRedo, bool bPrintPythonCommand) { TGuardValue EnablePythonPrint(bSuspendPythonMessagesForRigVMClient, !bPrintPythonCommand); return RigVMClient.AddModel(InName, bSetupUndoRedo, bPrintPythonCommand); } bool UAnimNextRigVMAssetEditorData::RemoveModel(FString InName, bool bSetupUndoRedo, bool bPrintPythonCommand) { TGuardValue EnablePythonPrint(bSuspendPythonMessagesForRigVMClient, !bPrintPythonCommand); return RigVMClient.RemoveModel(InName, bSetupUndoRedo, bPrintPythonCommand); } FRigVMGetFocusedGraph& UAnimNextRigVMAssetEditorData::OnGetFocusedGraph() { return RigVMClient.OnGetFocusedGraph(); } const FRigVMGetFocusedGraph& UAnimNextRigVMAssetEditorData::OnGetFocusedGraph() const { return RigVMClient.OnGetFocusedGraph(); } URigVMGraph* UAnimNextRigVMAssetEditorData::GetFocusedModel() const { return RigVMClient.GetFocusedModel(); } URigVMController* UAnimNextRigVMAssetEditorData::GetController(const URigVMGraph* InGraph) const { return RigVMClient.GetController(InGraph); }; URigVMController* UAnimNextRigVMAssetEditorData::GetControllerByName(const FString InGraphName) const { return RigVMClient.GetControllerByName(InGraphName); }; URigVMController* UAnimNextRigVMAssetEditorData::GetOrCreateController(URigVMGraph* InGraph) { return RigVMClient.GetOrCreateController(InGraph); }; URigVMController* UAnimNextRigVMAssetEditorData::GetController(const UEdGraph* InEdGraph) const { return RigVMClient.GetController(InEdGraph); }; URigVMController* UAnimNextRigVMAssetEditorData::GetOrCreateController(const UEdGraph* InEdGraph) { return RigVMClient.GetOrCreateController(InEdGraph); }; TArray UAnimNextRigVMAssetEditorData::GeneratePythonCommands(const FString InNewBlueprintName) { return TArray(); } void UAnimNextRigVMAssetEditorData::SetupPinRedirectorsForBackwardsCompatibility() { } FRigVMGraphModifiedEvent& UAnimNextRigVMAssetEditorData::OnModified() { return RigVMGraphModifiedEvent; } bool UAnimNextRigVMAssetEditorData::IsFunctionPublic(const FName& InFunctionName) const { return GetLocalFunctionLibrary()->IsFunctionPublic(InFunctionName); } void UAnimNextRigVMAssetEditorData::MarkFunctionPublic(const FName& InFunctionName, bool bIsPublic) { if (IsFunctionPublic(InFunctionName) == bIsPublic) { return; } URigVMController* Controller = RigVMClient.GetOrCreateController(GetLocalFunctionLibrary()); Controller->MarkFunctionAsPublic(InFunctionName, bIsPublic); } void UAnimNextRigVMAssetEditorData::RenameGraph(const FString& InNodePath, const FName& InNewName) { if (URigVMGraph* ModelForNodePath = GetModel(InNodePath)) { if (UEdGraph* EdGraph = Cast(GetEditorObjectForRigVMGraph(ModelForNodePath))) { FName OldName = NAME_None; OldName = EdGraph->GetFName(); RigVMClient.RenameModel(InNodePath, InNewName, true); } } } UClass* UAnimNextRigVMAssetEditorData::GetRigVMSchemaClass() const { return UAnimNextRigVMAssetSchema::StaticClass(); } UScriptStruct* UAnimNextRigVMAssetEditorData::GetRigVMExecuteContextStruct() const { return FAnimNextExecuteContext::StaticStruct(); } UClass* UAnimNextRigVMAssetEditorData::GetRigVMEdGraphClass() const { return UAnimNextEdGraph::StaticClass(); } UClass* UAnimNextRigVMAssetEditorData::GetRigVMEdGraphNodeClass() const { return UAnimNextEdGraphNode::StaticClass(); } UClass* UAnimNextRigVMAssetEditorData::GetRigVMEdGraphSchemaClass() const { return UAnimNextEdGraphSchema::StaticClass(); } UClass* UAnimNextRigVMAssetEditorData::GetRigVMEditorSettingsClass() const { return URigVMEditorSettings::StaticClass(); } FRigVMClient* UAnimNextRigVMAssetEditorData::GetRigVMClient() { return &RigVMClient; } const FRigVMClient* UAnimNextRigVMAssetEditorData::GetRigVMClient() const { return &RigVMClient; } IRigVMGraphFunctionHost* UAnimNextRigVMAssetEditorData::GetRigVMGraphFunctionHost() { return this; } const IRigVMGraphFunctionHost* UAnimNextRigVMAssetEditorData::GetRigVMGraphFunctionHost() const { return this; } void UAnimNextRigVMAssetEditorData::HandleRigVMGraphAdded(const FRigVMClient* InClient, const FString& InNodePath) { if(URigVMGraph* RigVMGraph = InClient->GetModel(InNodePath)) { RigVMGraph->SetExecuteContextStruct(GetExecuteContextStruct()); if(!HasAnyFlags(RF_ClassDefaultObject | RF_NeedInitialization | RF_NeedLoad | RF_NeedPostLoad) && GetOuter() != GetTransientPackage()) { CreateEdGraph(RigVMGraph, true); RequestAutoVMRecompilation(); } #if WITH_EDITOR if(!bSuspendPythonMessagesForRigVMClient) { const FString AssetName = RigVMGraph->GetSchema()->GetSanitizedName(GetName(), true, false); RigVMPythonUtils::Print(AssetName, FString::Printf(TEXT("asset.add_graph('%s')"), *RigVMGraph->GetName())); } #endif } } void UAnimNextRigVMAssetEditorData::HandleRigVMGraphRemoved(const FRigVMClient* InClient, const FString& InNodePath) { if(URigVMGraph* RigVMGraph = InClient->GetModel(InNodePath)) { if (UAnimNextRigVMAssetEntry* Entry = FindEntryForRigVMGraph(RigVMGraph)) { if (IAnimNextRigVMGraphInterface* GraphInterface = Cast(Entry)) { GraphInterface->SetRigVMGraph(nullptr); } } GraphModels.Remove(RigVMGraph); RemoveEdGraph(RigVMGraph); RequestAutoVMRecompilation(); #if WITH_EDITOR if(!bSuspendPythonMessagesForRigVMClient) { const FString AssetName = RigVMGraph->GetSchema()->GetSanitizedName(GetName(), true, false); RigVMPythonUtils::Print(AssetName, FString::Printf(TEXT("asset.add_graph('%s')"), *RigVMGraph->GetName())); } #endif } } void UAnimNextRigVMAssetEditorData::HandleRigVMGraphRenamed(const FRigVMClient* InClient, const FString& InOldNodePath, const FString& InNewNodePath) { if (InClient->GetModel(InNewNodePath)) { TArray EdGraphs = GetAllEdGraphs(); for (UEdGraph* EdGraph : EdGraphs) { if (URigVMEdGraph* RigGraph = Cast(EdGraph)) { RigGraph->HandleRigVMGraphRenamed(InOldNodePath, InNewNodePath); } } } } void UAnimNextRigVMAssetEditorData::HandleConfigureRigVMController(const FRigVMClient* InClient, URigVMController* InControllerToConfigure) { InControllerToConfigure->OnModified().AddUObject(this, &UAnimNextRigVMAssetEditorData::HandleModifiedEvent); TWeakObjectPtr WeakThis(this); InControllerToConfigure->GetExternalVariablesDelegate.BindLambda([](URigVMGraph* InGraph) -> TArray { if (InGraph) { if(URigVMHost* RigVMHost = InGraph->GetTypedOuter()) { return RigVMHost->GetExternalVariables(); } } return TArray(); }); // this delegate is used by the controller // to retrieve the current bytecode of the VM InControllerToConfigure->GetCurrentByteCodeDelegate.BindLambda([WeakThis]() -> const FRigVMByteCode* { if (WeakThis.IsValid()) { if(UAnimNextRigVMAsset* Asset = WeakThis->GetTypedOuter()) { if (Asset->VM) { return &Asset->VM->GetByteCode(); } } } return nullptr; }); #if WITH_EDITOR InControllerToConfigure->SetupDefaultUnitNodeDelegates(TDelegate::CreateLambda( [](FRigVMExternalVariable InVariableToCreate, FString InDefaultValue) -> FName { return NAME_None; } )); #endif } UObject* UAnimNextRigVMAssetEditorData::GetEditorObjectForRigVMGraph(const URigVMGraph* InVMGraph) const { if(InVMGraph) { if (InVMGraph->IsA()) { return Cast(FunctionLibraryEdGraph.Get()); } const auto FindSubgraph = ([](const FString SearchGraphNodePath, URigVMEdGraph* EdGraph) -> URigVMEdGraph* { TArray SubGraphs; EdGraph->GetAllChildrenGraphs(SubGraphs); for (UEdGraph* SubGraph : SubGraphs) { if (URigVMEdGraph* RigVMEdGraph = Cast(SubGraph)) { if (RigVMEdGraph->GetRigVMNodePath() == SearchGraphNodePath) { return RigVMEdGraph; } } } return nullptr; }); const FString GraphNodePath = InVMGraph->GetNodePath(); for(UAnimNextRigVMAssetEntry* Entry : Entries) { if(IAnimNextRigVMGraphInterface* GraphInterface = Cast(Entry)) { URigVMEdGraph* EdGraph = GraphInterface->GetEdGraph(); if (const URigVMGraph* RigVMGraph = GraphInterface->GetRigVMGraph()) { if (RigVMGraph == InVMGraph) { return EdGraph; } } if (URigVMEdGraph* RigVMEdGraph = FindSubgraph(GraphNodePath, EdGraph)) { return RigVMEdGraph; } } } for (const TObjectPtr& FunctionEdGraph : FunctionEdGraphs) { if (FunctionEdGraph->ModelNodePath == GraphNodePath) { return FunctionEdGraph; } if (URigVMEdGraph* RigVMEdGraph = FindSubgraph(GraphNodePath, FunctionEdGraph)) { return RigVMEdGraph; } } } return nullptr; } URigVMGraph* UAnimNextRigVMAssetEditorData::GetRigVMGraphForEditorObject(UObject* InObject) const { if(const URigVMEdGraph* Graph = Cast(InObject)) { if (Graph->bIsFunctionDefinition) { if (URigVMLibraryNode* LibraryNode = RigVMClient.GetFunctionLibrary()->FindFunction(*Graph->ModelNodePath)) { return LibraryNode->GetContainedGraph(); } } else { return RigVMClient.GetModel(Graph->ModelNodePath); } } return nullptr; } FRigVMGraphFunctionStore* UAnimNextRigVMAssetEditorData::GetRigVMGraphFunctionStore() { return &GraphFunctionStore; } const FRigVMGraphFunctionStore* UAnimNextRigVMAssetEditorData::GetRigVMGraphFunctionStore() const { return &GraphFunctionStore; } TObjectPtr UAnimNextRigVMAssetEditorData::CreateContainedGraphModel(URigVMCollapseNode* CollapseNode, const FName& Name) { check(CollapseNode); TObjectPtr Model = NewObject(CollapseNode, Name); check(CollapseNode->GetGraph()); if (CollapseNode->GetGraph()->GetSchema() != nullptr) { Model->SetSchemaClass(CollapseNode->GetGraph()->GetSchema()->GetClass()); } else { Model->SetSchemaClass(RigVMClient.GetDefaultSchemaClass()); } URigVMGraph* CollapseNodeModelRootGraph = CollapseNode->GetRootGraph(); check(CollapseNodeModelRootGraph); // If we are a transient asset, or not using external packages dont use external packages if (bUsesExternalPackages && !CollapseNodeModelRootGraph->HasAnyFlags(RF_Transient)) { Model->SetExternalPackage(CollapseNodeModelRootGraph->GetExternalPackage()); } return Model; } void UAnimNextRigVMAssetEditorData::RecompileVM() { using namespace UE::AnimNext::UncookedOnly; if (bIsCompiling) { return; } TGuardValue CompilingGuard(bIsCompiling, true); UAnimNextRigVMAsset* Asset = FUtils::GetAsset(this); FScopedCompilerResults CompilerResults(Asset); VMCompileSettings.SetExecuteContextStruct(FAnimNextExecuteContext::StaticStruct()); FRigVMCompileSettings Settings = (bCompileInDebugMode) ? FRigVMCompileSettings::Fast(VMCompileSettings.GetExecuteContextStruct()) : VMCompileSettings; Settings.SurpressInfoMessages = false; Settings.bWarnAboutDuplicateEvents = true; Settings.ASTSettings.ReportDelegate.BindUObject(this, &UAnimNextRigVMAssetEditorData::HandleReportFromCompiler); Asset->VMRuntimeSettings = VMRuntimeSettings; OnPreCompileAsset(Settings); CachedExports.Reset(); // asset variables and other tags will be updated at the end by AssetRegistry->AssetUpdateTags bWarningsDuringCompilation = false; bErrorsDuringCompilation = false; RigGraphDisplaySettings.MinMicroSeconds = RigGraphDisplaySettings.LastMinMicroSeconds = DBL_MAX; RigGraphDisplaySettings.MaxMicroSeconds = RigGraphDisplaySettings.LastMaxMicroSeconds = (double)INDEX_NONE; FAnimNextRigVMAssetCompileContext CompileContext = {}; { TGuardValue ReentrantGuardSelf(bSuspendModelNotificationsForSelf, true); TGuardValue ReentrantGuardOthers(RigVMClient.bSuspendModelNotificationsForOthers, true); FUtils::RecreateVM(Asset); { FAnimNextGetFunctionHeaderCompileContext GetFunctionHeaderCompileContext(CompileContext); OnPreCompileGetProgrammaticFunctionHeaders(Settings, GetFunctionHeaderCompileContext); } { FAnimNextGetVariableCompileContext GetVariableCompileContext(CompileContext); FUtils::CompileVariables(Settings, Asset, GetVariableCompileContext); } { FAnimNextGetGraphCompileContext GetGraphCompileContext(CompileContext); OnPreCompileGetProgrammaticGraphs(Settings, GetGraphCompileContext); } for(URigVMGraph* ProgrammaticGraph : CompileContext.ProgrammaticGraphs) { check(ProgrammaticGraph != nullptr); } FRigVMClient* VMClient = GetRigVMClient(); CompileContext.AllGraphs = VMClient->GetAllModels(false, false); CompileContext.AllGraphs.Append(CompileContext.ProgrammaticGraphs); { FAnimNextProcessGraphCompileContext ProcessGraphCompileContext(CompileContext); OnPreCompileProcessGraphs(Settings, ProcessGraphCompileContext); } if(CompileContext.AllGraphs.Num() > 0) { URigVMController* Controller = VMClient->GetOrCreateController(CompileContext.AllGraphs[0]); URigVMCompiler* Compiler = URigVMCompiler::StaticClass()->GetDefaultObject(); Compiler->Compile(Settings, CompileContext.AllGraphs, Controller, Asset->VM, Asset->ExtendedExecuteContext, Asset->GetExternalVariables(), &PinToOperandMap); } // Initialize right away, in packaged builds we initialize during PostLoad Asset->VM->Initialize(Asset->ExtendedExecuteContext); Asset->GenerateUserDefinedDependenciesData(Asset->ExtendedExecuteContext); // Notable difference with vanilla RigVM host behavior - we init the VM here at the moment as we only have one 'instance' Asset->InitializeVM(FRigUnit_AnimNextBeginExecution::EventName); if (bErrorsDuringCompilation) { if(Settings.SurpressErrors) { Settings.Reportf(EMessageSeverity::Info, Asset, TEXT("Compilation Errors may be suppressed for AnimNext asset: %s. See VM Compile Settings for more Details"), *Asset->GetName()); } } bVMRecompilationRequired = false; if(Asset->VM) { RigVMCompiledEvent.Broadcast(Asset, Asset->VM, Asset->ExtendedExecuteContext); } FAnimNextAssetRegistryExports Exports; FUtils::GetAssetVariables(this, Exports); #if WITH_EDITOR // Display programmatic graphs if(CVarDumpProgrammaticGraphs.GetValueOnGameThread()) { FUtils::OpenProgrammaticGraphs(this, CompileContext.ProgrammaticGraphs); } else #endif { RemoveProgrammaticGraphs(CompileContext.ProgrammaticGraphs); } RemoveTransientGraphs(CompileContext.AllGraphs); OnPostCompileCleanup(Settings); #if WITH_EDITOR // RefreshBreakpoints(EditorData); #endif // Refresh CachedExports if (IAssetRegistry* AssetRegistry = IAssetRegistry::Get()) { AssetRegistry->AssetUpdateTags(Asset, EAssetRegistryTagsCaller::Fast); } } } void UAnimNextRigVMAssetEditorData::RemoveProgrammaticGraphs(TArrayView InGraphs) { FRigVMClient* VMClient = GetRigVMClient(); for(URigVMGraph* Graph : InGraphs) { VMClient->RemoveController(Graph); Graph->Rename(nullptr, GetTransientPackage(), REN_ForceNoResetLoaders | REN_DoNotDirty | REN_DontCreateRedirectors | REN_NonTransactional); } } void UAnimNextRigVMAssetEditorData::RemoveTransientGraphs(TArrayView InGraphs) { FRigVMClient* VMClient = GetRigVMClient(); for(URigVMGraph* Graph : InGraphs) { if(Graph->HasAnyFlags(RF_Transient)) { VMClient->RemoveController(Graph); Graph->Rename(nullptr, GetTransientPackage(), REN_ForceNoResetLoaders | REN_DoNotDirty | REN_DontCreateRedirectors | REN_NonTransactional); } } } void UAnimNextRigVMAssetEditorData::HandleRemoveNotify(UObject* InAsset, const FString& InFindString, bool bFindWholeWord, ESearchCase::Type InSearchCase) { UAnimNextRigVMAsset* Asset = Cast(InAsset); if(Asset == nullptr) { return; } UAnimNextRigVMAssetEditorData* EditorData = UE::AnimNext::UncookedOnly::FUtils::GetEditorData(Asset); if(EditorData == nullptr) { return; } URigVMController* Controller = EditorData->GetController(); Controller->OpenUndoBracket(LOCTEXT("RemoveNotifyEvents", "Remove Notify Events").ToString()); for(TObjectPtr Model : EditorData->RigVMClient.GetModels()) { for(URigVMNode* Node : Model->GetNodes()) { if(URigVMUnitNode* UnitNode = Cast(Node)) { if(UnitNode->GetScriptStruct()->IsChildOf(FRigVMFunction_UserDefinedEvent::StaticStruct())) { URigVMPin* Pin = UnitNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigVMFunction_UserDefinedEvent, EventName)); FString EventNameString = Pin->GetDefaultValue(); if( (bFindWholeWord && EventNameString.Equals(InFindString, InSearchCase)) || (!bFindWholeWord && EventNameString.Contains(InFindString, InSearchCase))) { Controller->RemoveNode(Node, true, true); } } } } } Controller->CloseUndoBracket(); } void UAnimNextRigVMAssetEditorData::HandleReplaceNotify(UObject* InAsset, const FString& InFindString, const FString& InReplaceString, bool bFindWholeWord, ESearchCase::Type InSearchCase) { UAnimNextRigVMAsset* Asset = Cast(InAsset); if(Asset == nullptr) { return; } UAnimNextRigVMAssetEditorData* EditorData = UE::AnimNext::UncookedOnly::FUtils::GetEditorData(Asset); if(EditorData == nullptr) { return; } URigVMController* Controller = EditorData->GetController(); Controller->OpenUndoBracket(LOCTEXT("ReplaceNotifyEvents", "Replace Notify Events").ToString()); for(TObjectPtr Model : EditorData->RigVMClient.GetModels()) { for(URigVMNode* Node : Model->GetNodes()) { if(URigVMUnitNode* UnitNode = Cast(Node)) { if(UnitNode->GetScriptStruct()->IsChildOf(FRigVMFunction_UserDefinedEvent::StaticStruct())) { URigVMPin* Pin = UnitNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigVMFunction_UserDefinedEvent, EventName)); FString EventNameString = Pin->GetDefaultValue(); if( (bFindWholeWord && EventNameString.Equals(InFindString, InSearchCase)) || (!bFindWholeWord && EventNameString.Contains(InFindString, InSearchCase))) { const FString NewName = EventNameString.Replace(*InFindString, *InReplaceString, InSearchCase); Controller->SetPinDefaultValue(Pin->GetPinPath(), NewName, true, true, false, true); } } } } } Controller->CloseUndoBracket(); } bool UAnimNextRigVMAssetEditorData::IsDirtyForRecompilation() const { if(bVMRecompilationRequired) { return true; } bool bDependencyDirty = false; ForEachEntryOfType([&bDependencyDirty](UAnimNextDataInterfaceEntry* InEntry) { if(InEntry->DataInterface) { UAnimNextRigVMAssetEditorData* EditorData = UE::AnimNext::UncookedOnly::FUtils::GetEditorData(InEntry->DataInterface.Get()); if(EditorData->IsDirtyForRecompilation()) { bDependencyDirty = true; return false; } } return true; }); return bDependencyDirty; } void UAnimNextRigVMAssetEditorData::RecompileVMIfRequired() { if (bVMRecompilationRequired) { RecompileVM(); } } void UAnimNextRigVMAssetEditorData::RequestAutoVMRecompilation() { bVMRecompilationRequired = true; if (bAutoRecompileVM && VMRecompilationBracket == 0) { RecompileVMIfRequired(); } } void UAnimNextRigVMAssetEditorData::SetAutoVMRecompile(bool bAutoRecompile) { bAutoRecompileVM = bAutoRecompile; } bool UAnimNextRigVMAssetEditorData::GetAutoVMRecompile() const { return bAutoRecompileVM; } void UAnimNextRigVMAssetEditorData::IncrementVMRecompileBracket() { VMRecompilationBracket++; } void UAnimNextRigVMAssetEditorData::DecrementVMRecompileBracket() { if (VMRecompilationBracket == 1) { if (bAutoRecompileVM) { RecompileVMIfRequired(); } VMRecompilationBracket = 0; if (InteractionBracketFinished.IsBound()) { InteractionBracketFinished.Broadcast(this); } } else if (VMRecompilationBracket > 0) { VMRecompilationBracket--; } } void UAnimNextRigVMAssetEditorData::HandleModifiedEvent(ERigVMGraphNotifType InNotifType, URigVMGraph* InGraph, UObject* InSubject) { // Skip any notifications we get while compiling (they can come from programmatic graph generation) if(bIsCompiling) { return; } bool bNotifForOthersPending = true; switch(InNotifType) { case ERigVMGraphNotifType::InteractionBracketOpened: { IncrementVMRecompileBracket(); break; } case ERigVMGraphNotifType::InteractionBracketClosed: case ERigVMGraphNotifType::InteractionBracketCanceled: { DecrementVMRecompileBracket(); break; } case ERigVMGraphNotifType::NodeAdded: { if (URigVMCollapseNode* CollapseNode = Cast(InSubject)) { CreateEdGraphForCollapseNode(CollapseNode, false); break; } RequestAutoVMRecompilation(); break; } case ERigVMGraphNotifType::NodeRemoved: { if (URigVMCollapseNode* CollapseNode = Cast(InSubject)) { RemoveEdGraphForCollapseNode(CollapseNode, false); break; } RequestAutoVMRecompilation(); break; } case ERigVMGraphNotifType::NodeRenamed: { if (URigVMCollapseNode* CollapseNode = Cast(InSubject)) { FString NewNodePath = CollapseNode->GetNodePath(true /* recursive */); FString Left, Right = NewNodePath; URigVMNode::SplitNodePathAtEnd(NewNodePath, Left, Right); FString OldNodePath = CollapseNode->GetPreviousFName().ToString(); if (!Left.IsEmpty()) { OldNodePath = URigVMNode::JoinNodePath(Left, OldNodePath); } HandleRigVMGraphRenamed(GetRigVMClient(), OldNodePath, NewNodePath); if (UEdGraph* ContainedEdGraph = Cast(GetEditorObjectForRigVMGraph(CollapseNode->GetContainedGraph()))) { ContainedEdGraph->Rename(*CollapseNode->GetEditorSubGraphName(), nullptr); } } break; } case ERigVMGraphNotifType::LinkAdded: case ERigVMGraphNotifType::LinkRemoved: case ERigVMGraphNotifType::PinArraySizeChanged: case ERigVMGraphNotifType::PinDirectionChanged: { RequestAutoVMRecompilation(); break; } case ERigVMGraphNotifType::PinDefaultValueChanged: { if (InGraph->GetRuntimeAST().IsValid()) { URigVMPin* RootPin = CastChecked(InSubject)->GetRootPin(); FRigVMASTProxy RootPinProxy = FRigVMASTProxy::MakeFromUObject(RootPin); const FRigVMExprAST* Expression = InGraph->GetRuntimeAST()->GetExprForSubject(RootPinProxy); if (Expression == nullptr) { InGraph->ClearAST(); } else if (Expression->NumParents() > 1) { InGraph->ClearAST(); } } RequestAutoVMRecompilation(); // We need to rebuild our metadata when a default value changes break; } case ERigVMGraphNotifType::PinAdded: { if (URigVMPin* Pin = Cast(InSubject)) { if (Pin->IsTraitPin()) { RequestAutoVMRecompilation(); } } break; } case ERigVMGraphNotifType::PinRemoved: { RequestAutoVMRecompilation(); // can not check if it is a trait pin, as it has been already removed break; } } // if the notification still has to be sent... if (bNotifForOthersPending && !RigVMClient.bSuspendModelNotificationsForOthers) { if (RigVMGraphModifiedEvent.IsBound()) { RigVMGraphModifiedEvent.Broadcast(InNotifType, InGraph, InSubject); } } } TSubclassOf UAnimNextRigVMAssetEditorData::GetAssetUserDataClass() const { return UAnimNextAssetWorkspaceAssetUserData::StaticClass(); } TArray UAnimNextRigVMAssetEditorData::GetAllEdGraphs() const { TArray Graphs; for(UAnimNextRigVMAssetEntry* Entry : Entries) { if(IAnimNextRigVMGraphInterface* GraphInterface = Cast(Entry)) { UEdGraph* EdGraph = GraphInterface->GetEdGraph(); Graphs.Add(EdGraph); EdGraph->GetAllChildrenGraphs(Graphs); } } for (URigVMEdGraph* RigVMEdGraph : FunctionEdGraphs) { Graphs.Add(RigVMEdGraph); RigVMEdGraph->GetAllChildrenGraphs(Graphs); } return Graphs; } UAnimNextRigVMAssetEntry* UAnimNextRigVMAssetLibrary::FindEntry(UAnimNextRigVMAsset* InAsset, FName InName) { return UE::AnimNext::UncookedOnly::FUtils::GetEditorData(InAsset)->FindEntry(InName); } UAnimNextRigVMAssetEntry* UAnimNextRigVMAssetEditorData::FindEntry(FName InName) const { if(InName == NAME_None) { ReportError(TEXT("UAnimNextRigVMAssetEditorData::FindEntry: Invalid name supplied.")); return nullptr; } const TObjectPtr* FoundEntry = Entries.FindByPredicate([InName](const UAnimNextRigVMAssetEntry* InEntry) { if (!InEntry) { return false; } return InEntry->GetEntryName() == InName; }); return FoundEntry != nullptr ? *FoundEntry : nullptr; } bool UAnimNextRigVMAssetLibrary::RemoveEntry(UAnimNextRigVMAsset* InAsset, UAnimNextRigVMAssetEntry* InEntry, bool bSetupUndoRedo, bool bPrintPythonCommand) { return UE::AnimNext::UncookedOnly::FUtils::GetEditorData(InAsset)->RemoveEntry(InEntry, bSetupUndoRedo, bPrintPythonCommand); } bool UAnimNextRigVMAssetEditorData::RemoveEntry(UAnimNextRigVMAssetEntry* InEntry, bool bSetupUndoRedo, bool bPrintPythonCommand) { if(InEntry == nullptr) { ReportError(TEXT("UAnimNextRigVMAssetEditorData::RemoveEntry: Invalid entry supplied.")); return false; } TObjectPtr* EntryToRemovePtr = Entries.FindByKey(InEntry); if(EntryToRemovePtr == nullptr) { ReportError(TEXT("UAnimNextRigVMAssetEditorData::RemoveEntry: Asset does not contain the supplied entry.")); return false; } if(bSetupUndoRedo) { Modify(); } // Remove from internal array UAnimNextRigVMAssetEntry* EntryToRemove = *EntryToRemovePtr; bool bResult = true; if(const IAnimNextRigVMGraphInterface* GraphInterface = Cast(EntryToRemove)) { // Remove any graphs if(URigVMGraph* RigVMGraph = GraphInterface->GetRigVMGraph()) { TGuardValue EnablePythonPrint(bSuspendPythonMessagesForRigVMClient, !bPrintPythonCommand); TGuardValue DisableAutoCompile(bAutoRecompileVM, false); bResult = RigVMClient.RemoveModel(RigVMGraph->GetNodePath(), bSetupUndoRedo); } } if (bSetupUndoRedo) { EntryToRemove->Modify(); } RemoveEntryInternal(EntryToRemove); RefreshExternalModels(); // This will cause any external package to be removed when saved EntryToRemove->MarkAsGarbage(); BroadcastModified(EAnimNextEditorDataNotifType::EntryRemoved, this); if (bPrintPythonCommand) { RigVMPythonUtils::Print(GetName(), FString::Printf(TEXT("asset.remove_entry(asset.find_entry('%s'))"), *InEntry->GetEntryName().ToString())); } return bResult; } bool UAnimNextRigVMAssetLibrary::RemoveEntries(UAnimNextRigVMAsset* InAsset, const TArray& InEntries, bool bSetupUndoRedo, bool bPrintPythonCommand) { return UE::AnimNext::UncookedOnly::FUtils::GetEditorData(InAsset)->RemoveEntries(InEntries, bSetupUndoRedo, bPrintPythonCommand); } bool UAnimNextRigVMAssetEditorData::RemoveEntries(TConstArrayView InEntries, bool bSetupUndoRedo, bool bPrintPythonCommand) { bool bResult = false; { TGuardValue DisableEditorDataNotifications(bSuspendEditorDataNotifications, true); TGuardValue DisableAutoCompile(bAutoRecompileVM, false); for(UAnimNextRigVMAssetEntry* Entry : InEntries) { bResult |= RemoveEntry(Entry, bSetupUndoRedo, false); } } BroadcastModified(EAnimNextEditorDataNotifType::EntryRemoved, this); if (bPrintPythonCommand) { FString ArrayStr = TEXT("["); for (int32 Index = 0; Index < InEntries.Num(); ++Index) { ArrayStr += TEXT("asset.find_entry('") + InEntries[Index]->GetEntryName().ToString() + TEXT("')"); if (Index < InEntries.Num() - 1) { ArrayStr += TEXT(", "); } } ArrayStr += TEXT("]"); RigVMPythonUtils::Print(GetName(), FString::Printf(TEXT("asset.remove_entries(%s)"), *ArrayStr)); } return bResult; } bool UAnimNextRigVMAssetLibrary::RemoveAllEntries(UAnimNextRigVMAsset* InAsset, bool bSetupUndoRedo, bool bPrintPythonCommand) { return UE::AnimNext::UncookedOnly::FUtils::GetEditorData(InAsset)->RemoveAllEntries(bSetupUndoRedo, bPrintPythonCommand); } bool UAnimNextRigVMAssetEditorData::RemoveAllEntries(bool bSetupUndoRedo, bool bPrintPythonCommand) { bool bResult = false; { TGuardValue DisableEditorDataNotifications(bSuspendEditorDataNotifications, true); TGuardValue DisableAutoCompile(bAutoRecompileVM, false); TArray EntriesCopy = Entries; for(UAnimNextRigVMAssetEntry* Entry : EntriesCopy) { bResult |= RemoveEntry(Entry, bSetupUndoRedo, false); } } BroadcastModified(EAnimNextEditorDataNotifType::EntryRemoved, this); if (bPrintPythonCommand) { RigVMPythonUtils::Print(GetName(), FString::Printf(TEXT("asset.remove_all_entries()"))); } return bResult; } UObject* UAnimNextRigVMAssetEditorData::CreateNewSubEntry(UAnimNextRigVMAssetEditorData* InEditorData, TSubclassOf InClass) { UObject* NewEntry = NewObject(InEditorData, InClass.Get(), NAME_None, RF_Transactional); // If we are a transient asset, dont use external packages UAnimNextRigVMAsset* Asset = UE::AnimNext::UncookedOnly::FUtils::GetAsset(InEditorData); check(Asset); // Additionally check external packaging flag if(!Asset->HasAnyFlags(RF_Transient) && InEditorData->bUsesExternalPackages) { FExternalPackageHelper::SetPackagingMode(NewEntry, InEditorData, true, false, PKG_None); } return NewEntry; } UAnimNextRigVMAssetEntry* UAnimNextRigVMAssetEditorData::FindEntryForRigVMGraph(const URigVMGraph* InRigVMGraph) const { for(UAnimNextRigVMAssetEntry* Entry : Entries) { if(IAnimNextRigVMGraphInterface* GraphInterface = Cast(Entry)) { if (const URigVMGraph* RigVMGraph = GraphInterface->GetRigVMGraph()) { if(RigVMGraph == InRigVMGraph) { return Entry; } } } } return nullptr; } UAnimNextRigVMAssetEntry* UAnimNextRigVMAssetEditorData::FindEntryForRigVMEdGraph(const URigVMEdGraph* InRigVMEdGraph) const { for (UAnimNextRigVMAssetEntry* Entry : Entries) { if (IAnimNextRigVMGraphInterface* GraphInterface = Cast(Entry)) { if (GraphInterface->GetEdGraph() == InRigVMEdGraph) { return Entry; } } } return nullptr; } void UAnimNextRigVMAssetEditorData::CreateEdGraphForCollapseNode(URigVMCollapseNode* InNode, bool bForce) { check(InNode); URigVMGraph* CollapseNodeGraph = InNode->GetGraph(); check(CollapseNodeGraph); if (bForce) { RemoveEdGraphForCollapseNode(InNode, false); } // For Function node if (InNode->GetGraph()->IsA()) { if (URigVMGraph* ContainedGraph = InNode->GetContainedGraph()) { bool bFunctionGraphExists = false; for (UEdGraph* FunctionGraph : FunctionEdGraphs) { if (URigVMEdGraph* RigFunctionGraph = Cast(FunctionGraph)) { if (RigFunctionGraph->ModelNodePath == ContainedGraph->GetNodePath()) { bFunctionGraphExists = true; break; } } } if (!bFunctionGraphExists) { const FName SubGraphName = RigVMClient.GetUniqueName(this, *InNode->GetName()); // create a sub graph UAnimNextEdGraph* RigFunctionGraph = NewObject(this, SubGraphName, RF_Transactional); RigFunctionGraph->Schema = UAnimNextEdGraphSchema::StaticClass(); RigFunctionGraph->bAllowRenaming = true; RigFunctionGraph->bEditable = true; RigFunctionGraph->bAllowDeletion = true; RigFunctionGraph->ModelNodePath = ContainedGraph->GetNodePath(); RigFunctionGraph->bIsFunctionDefinition = true; RigFunctionGraph->Initialize(this); FunctionEdGraphs.Add(RigFunctionGraph); RigVMClient.GetOrCreateController(ContainedGraph)->ResendAllNotifications(); } } } // --- For Collapse nodes --- else if (URigVMEdGraph* RigEdGraph = Cast(GetEditorObjectForRigVMGraph(InNode->GetGraph()))) { if (URigVMGraph* ContainedGraph = InNode->GetContainedGraph()) { bool bSubGraphExists = false; const FString ContainedGraphNodePath = ContainedGraph->GetNodePath(); for (UEdGraph* SubGraph : RigEdGraph->SubGraphs) { if (UAnimNextEdGraph* SubRigGraph = Cast(SubGraph)) { if (SubRigGraph->ModelNodePath == ContainedGraphNodePath) { bSubGraphExists = true; break; } } } if (!bSubGraphExists) { bool bEditable = true; if (InNode->IsA()) { bEditable = false; } UObject* Outer = FindEntryForRigVMGraph(CollapseNodeGraph->GetRootGraph()); if (Outer == nullptr) { Outer = this; // function library graph has no entry } const FName SubGraphName = RigVMClient.GetUniqueName(Outer, *InNode->GetEditorSubGraphName()); // create a sub graph, no need to set external package if outer is an Entry UAnimNextEdGraph* SubRigGraph = NewObject(Outer, SubGraphName, RF_Transactional); SubRigGraph->Schema = UAnimNextEdGraphSchema::StaticClass(); SubRigGraph->bAllowRenaming = 1; SubRigGraph->bEditable = bEditable; SubRigGraph->bAllowDeletion = 1; SubRigGraph->ModelNodePath = ContainedGraphNodePath; SubRigGraph->bIsFunctionDefinition = false; RigEdGraph->SubGraphs.Add(SubRigGraph); SubRigGraph->Initialize(this); GetOrCreateController(ContainedGraph)->ResendAllNotifications(); } } } } void UAnimNextRigVMAssetEditorData::RemoveEdGraphForCollapseNode(URigVMCollapseNode* InNode, bool bNotify) { check(InNode); if (InNode->GetGraph()->IsA()) { if (URigVMGraph* ContainedGraph = InNode->GetContainedGraph()) { for (UEdGraph* FunctionGraph : FunctionEdGraphs) { if (URigVMEdGraph* RigFunctionGraph = Cast(FunctionGraph)) { if (RigFunctionGraph->ModelNodePath == ContainedGraph->GetNodePath()) { if (URigVMController* SubController = GetController(ContainedGraph)) { SubController->OnModified().RemoveAll(RigFunctionGraph); } if (RigVMGraphModifiedEvent.IsBound() && bNotify) { RigVMGraphModifiedEvent.Broadcast(ERigVMGraphNotifType::NodeRemoved, InNode->GetGraph(), InNode); } FunctionEdGraphs.Remove(RigFunctionGraph); RigFunctionGraph->Rename(nullptr, GetTransientPackage(), REN_ForceNoResetLoaders | REN_DontCreateRedirectors); RigFunctionGraph->MarkAsGarbage(); break; } } } } } else if (URigVMEdGraph* RigGraph = Cast(GetEditorObjectForRigVMGraph(InNode->GetGraph()))) { if (URigVMGraph* ContainedGraph = InNode->GetContainedGraph()) { for (UEdGraph* SubGraph : RigGraph->SubGraphs) { if (URigVMEdGraph* SubRigGraph = Cast(SubGraph)) { if (SubRigGraph->ModelNodePath == ContainedGraph->GetNodePath()) { if (URigVMController* SubController = GetController(ContainedGraph)) { SubController->OnModified().RemoveAll(SubRigGraph); } if (RigVMGraphModifiedEvent.IsBound() && bNotify) { RigVMGraphModifiedEvent.Broadcast(ERigVMGraphNotifType::NodeRemoved, InNode->GetGraph(), InNode); } RigGraph->SubGraphs.Remove(SubRigGraph); SubRigGraph->Rename(nullptr, GetTransientPackage(), REN_ForceNoResetLoaders | REN_DontCreateRedirectors); SubRigGraph->MarkAsGarbage(); break; } } } } } } UEdGraph* UAnimNextRigVMAssetEditorData::CreateEdGraph(URigVMGraph* InRigVMGraph, bool bForce) { check(InRigVMGraph); if(InRigVMGraph->IsA()) { return nullptr; } const bool bIsTransient = InRigVMGraph->HasAnyFlags(RF_Transient); IAnimNextRigVMGraphInterface* Entry = Cast(FindEntryForRigVMGraph(InRigVMGraph)); if(Entry == nullptr && !bIsTransient) { // Not found, we could be adding a new entry, in which case the graph wont be assigned yet check(Entries.Num() > 0); check(Cast(Entries.Last()) != nullptr); check(Cast(Entries.Last())->GetRigVMGraph() == nullptr); Entry = Cast(FindEntryForRigVMGraph(nullptr)); } if(Entry == nullptr && !bIsTransient) { return nullptr; } if(bForce) { RemoveEdGraph(InRigVMGraph); } UObject* Outer = nullptr; EObjectFlags Flags = RF_NoFlags; if(!bIsTransient) { Outer = CastChecked(Entry); Flags = RF_Transactional; } else { // This outer is to allow URigVMEdGraph::GetModel to retrieve the graph in 'preview' scenarios Outer = InRigVMGraph; Flags = RF_Transient; } const FName GraphName = Entry != nullptr ? RigVMClient.GetUniqueName(Outer, Entry->GetGraphName()) : NAME_None; UAnimNextEdGraph* RigFunctionGraph = NewObject(Outer, GraphName, Flags); RigFunctionGraph->Schema = UAnimNextEdGraphSchema::StaticClass(); RigFunctionGraph->bAllowDeletion = true; RigFunctionGraph->bIsFunctionDefinition = false; RigFunctionGraph->ModelNodePath = InRigVMGraph->GetNodePath(); RigFunctionGraph->Initialize(this); if(!bIsTransient) { Entry->SetEdGraph(RigFunctionGraph); if(Entry->GetRigVMGraph() == nullptr) { Entry->SetRigVMGraph(InRigVMGraph); } else { check(Entry->GetRigVMGraph() == InRigVMGraph); } } return RigFunctionGraph; } bool UAnimNextRigVMAssetEditorData::RemoveEdGraph(URigVMGraph* InModel) { if(IAnimNextRigVMGraphInterface* Entry = Cast(FindEntryForRigVMGraph(InModel))) { RigVMClient.DestroyObject(Entry->GetEdGraph()); Entry->SetEdGraph(nullptr); return true; } return false; } UAnimNextVariableEntry* UAnimNextRigVMAssetLibrary::AddVariable(UAnimNextRigVMAsset* InAsset, FName InName, EPropertyBagPropertyType InValueType, EPropertyBagContainerType InContainerType, const UObject* InValueTypeObject, const FString& InDefaultValue, bool bSetupUndoRedo, bool bPrintPythonCommand) { return UE::AnimNext::UncookedOnly::FUtils::GetEditorData(InAsset)->AddVariable(InName, FAnimNextParamType(InValueType, InContainerType, InValueTypeObject), InDefaultValue, bSetupUndoRedo, bPrintPythonCommand); } UAnimNextVariableEntry* UAnimNextRigVMAssetEditorData::AddVariable(FName InName, FAnimNextParamType InType, const FString& InDefaultValue, bool bSetupUndoRedo, bool bPrintPythonCommand) { if(InName == NAME_None) { ReportError(TEXT("UAnimNextRigVMAssetEditorData::AddVariable: Invalid variable name supplied.")); return nullptr; } if(!GetEntryClasses().Contains(UAnimNextVariableEntry::StaticClass()) || !CanAddNewEntry(UAnimNextVariableEntry::StaticClass())) { ReportError(TEXT("UAnimNextRigVMAssetEditorData::AddVariable: Cannot add a variable to this asset - entry is not allowed.")); return nullptr; } // Check for duplicate name FName NewParameterName = InName; auto DuplicateNamePredicate = [&NewParameterName](const UAnimNextRigVMAssetEntry* InEntry) { if (!InEntry) { return false; } return InEntry->GetEntryName() == NewParameterName; }; bool bAlreadyExists = Entries.ContainsByPredicate(DuplicateNamePredicate); int32 NameNumber = InName.GetNumber() + 1; while(bAlreadyExists) { NewParameterName = FName(InName, NameNumber++); bAlreadyExists = Entries.ContainsByPredicate(DuplicateNamePredicate); } UAnimNextVariableEntry* NewEntry = CreateNewSubEntry(this); { TGuardValue DisableEditorDataNotifications(bSuspendEditorDataNotifications, true); TGuardValue DisableAutoCompile(bAutoRecompileVM, false); NewEntry->SetVariableName(NewParameterName, false); NewEntry->SetType(InType, false); if(InDefaultValue.Len() > 0) { NewEntry->SetDefaultValueFromString(InDefaultValue, false); } NewEntry->Initialize(this); } if(bSetupUndoRedo) { NewEntry->Modify(); Modify(); } AddEntryInternal(NewEntry); CustomizeNewAssetEntry(NewEntry); BroadcastModified(EAnimNextEditorDataNotifType::EntryAdded, NewEntry); if (bPrintPythonCommand) { const FString ValueTypeString = InType.GetValueTypeObject() ? FString::Printf(TEXT("unreal.%s.static_%s()"), *InType.GetValueTypeObject()->GetName(), InType.GetValueTypeObject()->IsA() ? TEXT("struct") : TEXT("class")) : TEXT("None"); RigVMPythonUtils::Print(GetName(), FString::Printf(TEXT("asset.add_variable('%s', %s, %s, %s, '%s')"), *InName.ToString(), *RigVMPythonUtils::EnumValueToPythonString(static_cast(InType.GetValueType())), *RigVMPythonUtils::EnumValueToPythonString(static_cast(InType.GetContainerType())), *ValueTypeString, *InDefaultValue)); } return NewEntry; } UAnimNextEventGraphEntry* UAnimNextRigVMAssetLibrary::AddEventGraph(UAnimNextRigVMAsset* InAsset, FName InName, UScriptStruct* InEventStruct, bool bSetupUndoRedo, bool bPrintPythonCommand) { return UE::AnimNext::UncookedOnly::FUtils::GetEditorData(InAsset)->AddEventGraph(InName, InEventStruct, bSetupUndoRedo, bPrintPythonCommand); } UAnimNextEventGraphEntry* UAnimNextRigVMAssetEditorData::AddEventGraph(FName InName, UScriptStruct* InEventStruct, bool bSetupUndoRedo, bool bPrintPythonCommand) { if(InName == NAME_None) { ReportError(TEXT("UAnimNextRigVMAssetEditorData::AddEventGraph: Invalid graph name supplied.")); return nullptr; } if(InEventStruct == nullptr || !InEventStruct->IsChildOf(FRigVMStruct::StaticStruct())) { ReportError(TEXT("UAnimNextRigVMAssetEditorData::AddEventGraph: Invalid event struct name supplied.")); return nullptr; } if(!GetEntryClasses().Contains(UAnimNextEventGraphEntry::StaticClass()) || !CanAddNewEntry(UAnimNextEventGraphEntry::StaticClass())) { ReportError(TEXT("UAnimNextRigVMAssetEditorData::AddEventGraph: Cannot add an event graph to this asset - entry is not allowed.")); return nullptr; } // Check for duplicate name FName NewGraphName = InName; auto DuplicateNamePredicate = [&NewGraphName](const UAnimNextRigVMAssetEntry* InEntry) { return InEntry->GetEntryName() == NewGraphName; }; bool bAlreadyExists = Entries.ContainsByPredicate(DuplicateNamePredicate); int32 NameNumber = InName.GetNumber() + 1; while(bAlreadyExists) { NewGraphName = FName(InName, NameNumber++); bAlreadyExists = Entries.ContainsByPredicate(DuplicateNamePredicate); } UAnimNextEventGraphEntry* NewEntry = CreateNewSubEntry(this); NewEntry->GraphName = NewGraphName; NewEntry->Initialize(this); if(bSetupUndoRedo) { NewEntry->Modify(); Modify(); } AddEntryInternal(NewEntry); // Add new graph { TGuardValue EnablePythonPrint(bSuspendPythonMessagesForRigVMClient, !bPrintPythonCommand); TGuardValue DisableAutoCompile(bAutoRecompileVM, false); // Editor data has to be the graph outer, or RigVM unique name generator will not work URigVMGraph* NewRigVMGraphModel = RigVMClient.CreateModel(URigVMGraph::StaticClass()->GetFName(), UAnimNextEventGraphSchema::StaticClass(), bSetupUndoRedo, this); if (ensure(NewRigVMGraphModel)) { // Then, to avoid the graph losing ref due to external package, set the same package as the Entry if (!NewRigVMGraphModel->HasAnyFlags(RF_Transient)) { NewRigVMGraphModel->SetExternalPackage(CastChecked(NewEntry)->GetExternalPackage()); } ensure(NewRigVMGraphModel); NewEntry->Graph = NewRigVMGraphModel; RefreshExternalModels(); RigVMClient.AddModel(NewRigVMGraphModel, true); URigVMController* Controller = RigVMClient.GetController(NewRigVMGraphModel); UE::AnimNext::UncookedOnly::FUtils::SetupEventGraph(Controller, InEventStruct, NewGraphName); } } CustomizeNewAssetEntry(NewEntry); BroadcastModified(EAnimNextEditorDataNotifType::EntryAdded, NewEntry); if (bPrintPythonCommand) { RigVMPythonUtils::Print(GetName(), FString::Printf(TEXT("asset.add_event_graph('%s', unreal.%s)"), *InName.ToString(), *InEventStruct->GetName())); } return NewEntry; } UAnimNextDataInterfaceEntry* UAnimNextRigVMAssetLibrary::AddDataInterface(UAnimNextRigVMAsset* InAsset, UAnimNextDataInterface* InDataInterface, bool bSetupUndoRedo, bool bPrintPythonCommand) { return UE::AnimNext::UncookedOnly::FUtils::GetEditorData(InAsset)->AddDataInterface(InDataInterface, bSetupUndoRedo, bPrintPythonCommand); } URigVMLibraryNode* UAnimNextRigVMAssetLibrary::AddFunction(UAnimNextRigVMAsset* InAsset, FName InFunctionName, bool bInMutable, bool bSetupUndoRedo, bool bPrintPythonCommand) { return UE::AnimNext::UncookedOnly::FUtils::GetEditorData(InAsset)->AddFunction(InFunctionName, bInMutable, bSetupUndoRedo, bPrintPythonCommand); } UAnimNextDataInterfaceEntry* UAnimNextRigVMAssetEditorData::AddDataInterface(UAnimNextDataInterface* InDataInterface, bool bSetupUndoRedo, bool bPrintPythonCommand) { if(InDataInterface == nullptr) { ReportError(TEXT("UAnimNextRigVMAssetEditorData::AddDataInterface: Invalid data interface supplied.")); return nullptr; } if(!GetEntryClasses().Contains(UAnimNextDataInterfaceEntry::StaticClass()) || !CanAddNewEntry(UAnimNextDataInterfaceEntry::StaticClass())) { ReportError(TEXT("UAnimNextRigVMAssetEditorData::AddDataInterface: Cannot add a data interface to this asset - entry is not allowed.")); return nullptr; } // Check if interface has any public members or if any of its parent interfaces do UAnimNextDataInterface_EditorData* EditorData = UE::AnimNext::UncookedOnly::FUtils::GetEditorData(InDataInterface); if(EditorData == nullptr) { ReportError(TEXT("UAnimNextRigVMAssetEditorData::AddDataInterface: Invalid data interface supplied - asset has no editor data.")); return nullptr; } // Check for circularity auto CheckForCircularity = [this](UAnimNextDataInterface_EditorData* InEditorData, auto& InCheckForCircularity) { if(InEditorData == this) { return true; } for(UAnimNextRigVMAssetEntry* Entry : InEditorData->Entries) { if(UAnimNextDataInterfaceEntry* DataInterfaceEntry = Cast(Entry)) { UAnimNextDataInterface* DataInterface = DataInterfaceEntry->GetDataInterface(); UAnimNextDataInterface_EditorData* EditorData = UE::AnimNext::UncookedOnly::FUtils::GetEditorData(DataInterface); if(InCheckForCircularity(EditorData, InCheckForCircularity)) { return true; } } } return false; }; if(CheckForCircularity(EditorData, CheckForCircularity)) { ReportError(TEXT("UAnimNextRigVMAssetEditorData::AddDataInterface: Circular reference detected.")); return nullptr; } auto CheckForPublicMembers = [](UAnimNextDataInterface_EditorData* InEditorData, auto& InCheckForPublicMembers) { for(UAnimNextRigVMAssetEntry* Entry : InEditorData->Entries) { if(UAnimNextVariableEntry* VariableEntry = Cast(Entry)) { if(VariableEntry->GetExportAccessSpecifier() == EAnimNextExportAccessSpecifier::Public) { return true; } } else if(UAnimNextDataInterfaceEntry* DataInterfaceEntry = Cast(Entry)) { UAnimNextDataInterface* DataInterface = DataInterfaceEntry->GetDataInterface(); UAnimNextDataInterface_EditorData* EditorData = UE::AnimNext::UncookedOnly::FUtils::GetEditorData(DataInterface); if(InCheckForPublicMembers(EditorData, InCheckForPublicMembers)) { return true; } } } return false; }; if(!CheckForPublicMembers(EditorData, CheckForPublicMembers)) { ReportError(TEXT("UAnimNextRigVMAssetEditorData::AddDataInterface: No public variables found.")); return nullptr; } // Check for duplicate interface auto DuplicatePredicate = [InDataInterface](const UAnimNextRigVMAssetEntry* InEntry) { if(const UAnimNextDataInterfaceEntry* InterfaceEntry = Cast(InEntry)) { return InterfaceEntry->DataInterface == InDataInterface; } return false; }; if(Entries.ContainsByPredicate(DuplicatePredicate)) { ReportError(TEXT("UAnimNextRigVMAssetEditorData::AddDataInterface: Data interface already implemented.")); return nullptr; } UAnimNextDataInterfaceEntry* NewEntry = CreateNewSubEntry(this); NewEntry->SetDataInterface(InDataInterface); NewEntry->Initialize(this); if(bSetupUndoRedo) { NewEntry->Modify(); Modify(); } AddEntryInternal(NewEntry); CustomizeNewAssetEntry(NewEntry); BroadcastModified(EAnimNextEditorDataNotifType::EntryAdded, NewEntry); if (bPrintPythonCommand) { RigVMPythonUtils::Print(GetName(), FString::Printf(TEXT("asset.add_data_interface(unreal.find_object(outer=None, name='%s'))"), *InDataInterface->GetPathName())); } return NewEntry; } URigVMLibraryNode* UAnimNextRigVMAssetEditorData::AddFunction(FName InFunctionName, bool bInMutable, bool bInSetupUndoRedo, bool bPrintPythonCommand) { URigVMController* Controller = RigVMClient.GetOrCreateController(GetLocalFunctionLibrary()); URigVMLibraryNode* Node = Controller->AddFunctionToLibrary(InFunctionName, bInMutable, FVector2D::ZeroVector, bInSetupUndoRedo, false); if (bPrintPythonCommand) { RigVMPythonUtils::Print(GetName(), FString::Printf(TEXT("asset.add_function('%s', %s)"), *InFunctionName.ToString(), bInMutable ? TEXT("True") : TEXT("False"))); } return Node; } bool UAnimNextRigVMAssetEditorData::HasPublicVariables() const { for(UAnimNextRigVMAssetEntry* Entry : Entries) { if(UAnimNextVariableEntry* VariableEntry = Cast(Entry)) { if(VariableEntry->GetExportAccessSpecifier() == EAnimNextExportAccessSpecifier::Public) { return true; } } else if(UAnimNextDataInterfaceEntry* DataInterfaceEntry = Cast(Entry)) { if(DataInterfaceEntry->DataInterface) { UAnimNextDataInterface_EditorData* EditorData = UE::AnimNext::UncookedOnly::FUtils::GetEditorData(DataInterfaceEntry->DataInterface.Get()); return EditorData->HasPublicVariables(); } } } return false; } void UAnimNextRigVMAssetEditorData::GetPublicVariables(TArray& OutPublicVariables) const { for(UAnimNextRigVMAssetEntry* Entry : Entries) { if(UAnimNextVariableEntry* VariableEntry = Cast(Entry)) { if(VariableEntry->GetExportAccessSpecifier() == EAnimNextExportAccessSpecifier::Public) { OutPublicVariables.Add(VariableEntry); } } else if(UAnimNextDataInterfaceEntry* DataInterfaceEntry = Cast(Entry)) { if(DataInterfaceEntry->DataInterface) { UAnimNextDataInterface_EditorData* EditorData = UE::AnimNext::UncookedOnly::FUtils::GetEditorData(DataInterfaceEntry->DataInterface.Get()); EditorData->GetPublicVariables(OutPublicVariables); } } } } void UAnimNextRigVMAssetEditorData::GetAllVariables(TArray& OutPublicVariables) const { for(UAnimNextRigVMAssetEntry* Entry : Entries) { if(UAnimNextVariableEntry* VariableEntry = Cast(Entry)) { OutPublicVariables.Add(VariableEntry); } else if(UAnimNextDataInterfaceEntry* DataInterfaceEntry = Cast(Entry)) { if(DataInterfaceEntry->DataInterface) { UAnimNextDataInterface_EditorData* EditorData = UE::AnimNext::UncookedOnly::FUtils::GetEditorData(DataInterfaceEntry->DataInterface.Get()); EditorData->GetPublicVariables(OutPublicVariables); } } } } void UAnimNextRigVMAssetEditorData::HandleReportFromCompiler(EMessageSeverity::Type InSeverity, UObject* InSubject, const FString& InMessage) { FCompilerResultsLog& Log = UE::AnimNext::UncookedOnly::FScopedCompilerResults::GetLog(); UObject* SubjectForMessage = InSubject; if(URigVMNode* ModelNode = Cast(SubjectForMessage)) { if(IRigVMClientHost* RigVMClientHost = ModelNode->GetImplementingOuter()) { if(URigVMNode* OriginalModelNode = Cast(Log.FindSourceObject(ModelNode))) { if(URigVMEdGraph* EdGraph = Cast(RigVMClientHost->GetEditorObjectForRigVMGraph(OriginalModelNode->GetGraph()))) { if(UEdGraphNode* EdNode = EdGraph->FindNodeForModelNodeName(OriginalModelNode->GetFName())) { SubjectForMessage = EdNode; } } } } } TSharedPtr Message; if (InSeverity == EMessageSeverity::Error) { // see UnitTest "ControlRig.Basics.OrphanedPins" to learn why errors are suppressed this way if (VMCompileSettings.SurpressErrors) { Log.bSilentMode = true; } if(InMessage.Contains(TEXT("@@"))) { Message = Log.Error(*InMessage, SubjectForMessage); } else { Message = Log.Error(*InMessage); } // see UnitTest "ControlRig.Basics.OrphanedPins" to learn why errors are suppressed this way if (!VMCompileSettings.SurpressErrors) { FScriptExceptionHandler::Get().HandleException(ELogVerbosity::Error, *InMessage, *FString()); } bErrorsDuringCompilation = true; } else if (InSeverity == EMessageSeverity::Warning) { if(InMessage.Contains(TEXT("@@"))) { Message = Log.Warning(*InMessage, SubjectForMessage); } else { Message = Log.Warning(*InMessage); } FScriptExceptionHandler::Get().HandleException(ELogVerbosity::Warning, *InMessage, *FString()); bWarningsDuringCompilation = true; } else { if(InMessage.Contains(TEXT("@@"))) { Message = Log.Note(*InMessage, SubjectForMessage); } else { Message = Log.Note(*InMessage); } UE_LOG(LogAnimation, Display, TEXT("%s"), *InMessage); } if (URigVMEdGraphNode* EdGraphNode = Cast(SubjectForMessage)) { if(Message.IsValid()) { EdGraphNode->SetErrorInfo(InSeverity, Message->ToText().ToString()); } else { EdGraphNode->SetErrorInfo(InSeverity, InMessage); } EdGraphNode->bHasCompilerMessage = EdGraphNode->ErrorType <= int32(EMessageSeverity::Info); } } void UAnimNextRigVMAssetEditorData::ClearErrorInfoForAllEdGraphs() { for (UEdGraph* Graph : GetAllEdGraphs()) { URigVMEdGraph* RigGraph = Cast(Graph); if (RigGraph == nullptr) { continue; } for (UEdGraphNode* GraphNode : Graph->Nodes) { if (URigVMEdGraphNode* RigVMEdGraphNode = Cast(GraphNode)) { RigVMEdGraphNode->ClearErrorInfo(); } } } } void UAnimNextRigVMAssetEditorData::RefreshExternalModels() { GraphModels.Reset(); for (UAnimNextRigVMAssetEntry* Entry : Entries) { if (IAnimNextRigVMGraphInterface* GraphInterface = Cast(Entry)) { if(URigVMGraph* Model = GraphInterface->GetRigVMGraph()) { GraphModels.Add(Model); } } } } void UAnimNextRigVMAssetEditorData::AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector) { Super::AddReferencedObjects(InThis, Collector); UAnimNextRigVMAssetEditorData* This = CastChecked(InThis); if (This->CachedExports.IsSet()) { // Cached exports may hold references to objects, so make GC aware Collector.AddPropertyReferences(FAnimNextAssetRegistryExports::StaticStruct(), &This->CachedExports.GetValue(), InThis); } } void UAnimNextRigVMAssetEditorData::AddEntryInternal(UAnimNextRigVMAssetEntry* InEntry) { // If we are using external packages, dont persist this entry if(bUsesExternalPackages) { Entries.Add(InEntry); } else { InternalEntries.Add(InEntry); Entries.Add(InEntry); } } void UAnimNextRigVMAssetEditorData::RemoveEntryInternal(UAnimNextRigVMAssetEntry* InEntry) { if(bUsesExternalPackages) { Entries.Remove(InEntry); } else { InternalEntries.Remove(InEntry); Entries.Remove(InEntry); } } #if WITH_EDITOR void UAnimNextRigVMAssetEditorData::SetUseExternalPackages(TArrayView InAssets, bool bInUseExternalPackages) { TArray EditorDatas; for(UAnimNextRigVMAsset* Asset : InAssets) { if(Asset == nullptr) { continue; } UAnimNextRigVMAssetEditorData* EditorData = UE::AnimNext::UncookedOnly::FUtils::GetEditorData(Asset); if(EditorData == nullptr) { continue; } if(bInUseExternalPackages != EditorData->bUsesExternalPackages) { EditorDatas.Add(EditorData); } } if(EditorDatas.Num() == 0) { return; } if(bInUseExternalPackages) { TArray PackagesToCheckOut; TArray PackagesToSave; TArray PackagesToAdd; for(UAnimNextRigVMAssetEditorData* EditorData : EditorDatas) { UPackage* Package = EditorData->GetPackage(); PackagesToCheckOut.Add(Package); PackagesToSave.Add(Package); } // Prompt the user to check out this package, allowing user to decide against this operation if(!FEditorFileUtils::PromptToCheckoutPackages(false, PackagesToCheckOut)) { return; } FScopedSlowTask SlowTask(3.0f, LOCTEXT("ConvertingAssets", "Converting Assets")); SlowTask.MakeDialog(); SlowTask.EnterProgressFrame(1.0f, LOCTEXT("SettingPackagingStatus", "Setting Packaging Status")); for(UAnimNextRigVMAssetEditorData* EditorData : EditorDatas) { EditorData->MarkPackageDirty(); // Set all internal entries to use external packages TArray ExternalPackages; for (UAnimNextRigVMAssetEntry* Entry : EditorData->InternalEntries) { FExternalPackageHelper::SetPackagingMode(Entry, EditorData, bInUseExternalPackages, true, PKG_None); UPackage* ExternalPackage = Entry->GetExternalPackage(); // Switch any graphs to be packaged externally if(IAnimNextRigVMGraphInterface* GraphEntry = Cast(Entry)) { GraphEntry->GetRigVMGraph()->SetExternalPackage(ExternalPackage); } check(ExternalPackage); PackagesToAdd.Add(ExternalPackage); PackagesToSave.Add(ExternalPackage); } // Clear all internal packages, switch to discovery on PostLoad rather than serialized entries EditorData->InternalEntries.Empty(); EditorData->bUsesExternalPackages = bInUseExternalPackages; } SlowTask.EnterProgressFrame(1.0f, LOCTEXT("AddOrRevertVersionControl", "Adding/Reverting In Version Control")); // Add (or revert delete) packages to source control FPackageSourceControlHelper SCCHelper; bool bAdded = SCCHelper.AddToSourceControl(PackagesToAdd); SlowTask.EnterProgressFrame(1.0f, LOCTEXT("SavingPackages", "Saving Packages")); // Finally save all packages, they need to be consistent on disk after this operation FEditorFileUtils::FPromptForCheckoutAndSaveParams SaveParams; SaveParams.bAlreadyCheckedOut = true; SaveParams.bCanBeDeclined = false; SaveParams.bPromptToSave = false; FEditorFileUtils::PromptForCheckoutAndSave(PackagesToSave, SaveParams); } else { // Gather packages we will modify/delete TArray PackagesToCheckOut; TArray PackagesToSave; TArray ObjectsToDelete; for(UAnimNextRigVMAssetEditorData* EditorData : EditorDatas) { UPackage* ThisPackage = EditorData->GetPackage(); PackagesToCheckOut.Add(ThisPackage); for (UAnimNextRigVMAssetEntry* Entry : EditorData->Entries) { UPackage* ExternalPackage = Entry->GetExternalPackage(); check(ExternalPackage); ObjectsToDelete.Add(ExternalPackage); PackagesToCheckOut.Add(ExternalPackage); } } // Prompt the user to check out files, allowing user to decide against this operation if(!FEditorFileUtils::PromptToCheckoutPackages(false, PackagesToCheckOut)) { return; } FScopedSlowTask SlowTask(3.0f, LOCTEXT("ConvertingAssets", "Converting Assets")); SlowTask.MakeDialog(); SlowTask.EnterProgressFrame(1.0f, LOCTEXT("SettingPackagingStatus", "Setting Packaging Status")); for(UAnimNextRigVMAssetEditorData* EditorData : EditorDatas) { EditorData->MarkPackageDirty(); ensure(EditorData->InternalEntries.IsEmpty()); EditorData->InternalEntries.Empty(); // Set all entries to not use external packages for (UAnimNextRigVMAssetEntry* Entry : EditorData->Entries) { FExternalPackageHelper::SetPackagingMode(Entry, EditorData, bInUseExternalPackages, true, PKG_None); // Switch any graphs to be packaged internally if(IAnimNextRigVMGraphInterface* GraphEntry = Cast(Entry)) { GraphEntry->GetRigVMGraph()->SetExternalPackage(nullptr); } } // Ensure we save all of our entries if we are not using external packages EditorData->InternalEntries.Append(EditorData->Entries); EditorData->bUsesExternalPackages = bInUseExternalPackages; PackagesToSave.Add(EditorData->GetPackage()); } SlowTask.EnterProgressFrame(1.0f, LOCTEXT("DeletingOldPackages", "Deleting Old Packages")); // Delete the old external packages ObjectTools::DeleteObjectsUnchecked(ObjectsToDelete); SlowTask.EnterProgressFrame(1.0f, LOCTEXT("SavingPackages", "Saving Packages")); // Finally save our packages, they needs to be consistent on disk after this operation FEditorFileUtils::FPromptForCheckoutAndSaveParams SaveParams; SaveParams.bAlreadyCheckedOut = true; SaveParams.bCanBeDeclined = false; SaveParams.bPromptToSave = false; FEditorFileUtils::PromptForCheckoutAndSave(PackagesToSave, SaveParams); } } #endif #undef LOCTEXT_NAMESPACE