2418 lines
73 KiB
C++
2418 lines
73 KiB
C++
// 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<URigVMEdGraphNode*> 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<bool> 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<UAnimNextEdGraph>(CastChecked<UObject>(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<IInterface_AssetUserData>(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<URigVMLibraryNode*, FRigVMGraphFunctionHeader> OldHeaders;
|
|
TArray<FName> BackwardsCompatiblePublicFunctions;
|
|
GetRigVMClient()->PatchFunctionsOnLoad(this, BackwardsCompatiblePublicFunctions, OldHeaders);
|
|
|
|
// Register function references at RigVMBuildData
|
|
if (URigVMBuildData* BuildData = URigVMBuildData::Get())
|
|
{
|
|
TArray<FRigVMReferenceNodeData> ReferenceNodeDatas;
|
|
const TArray<URigVMGraph*> AllModels = GetAllModels();
|
|
for (URigVMGraph* ModelToVisit : AllModels)
|
|
{
|
|
for (URigVMNode* Node : ModelToVisit->GetNodes())
|
|
{
|
|
if (URigVMFunctionReferenceNode* ReferenceNode = Cast<URigVMFunctionReferenceNode>(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<UAnimNextRigVMAssetEntry>(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<FArrayProperty>(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<bool> 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<URigVMGraph*> 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<bool> EnablePythonPrint(bSuspendPythonMessagesForRigVMClient, !bPrintPythonCommand);
|
|
return RigVMClient.AddModel(InName, bSetupUndoRedo, bPrintPythonCommand);
|
|
}
|
|
|
|
bool UAnimNextRigVMAssetEditorData::RemoveModel(FString InName, bool bSetupUndoRedo, bool bPrintPythonCommand)
|
|
{
|
|
TGuardValue<bool> 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<FString> UAnimNextRigVMAssetEditorData::GeneratePythonCommands(const FString InNewBlueprintName)
|
|
{
|
|
return TArray<FString>();
|
|
}
|
|
|
|
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<UEdGraph>(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<IAnimNextRigVMGraphInterface>(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<UEdGraph*> EdGraphs = GetAllEdGraphs();
|
|
for (UEdGraph* EdGraph : EdGraphs)
|
|
{
|
|
if (URigVMEdGraph* RigGraph = Cast<URigVMEdGraph>(EdGraph))
|
|
{
|
|
RigGraph->HandleRigVMGraphRenamed(InOldNodePath, InNewNodePath);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void UAnimNextRigVMAssetEditorData::HandleConfigureRigVMController(const FRigVMClient* InClient, URigVMController* InControllerToConfigure)
|
|
{
|
|
InControllerToConfigure->OnModified().AddUObject(this, &UAnimNextRigVMAssetEditorData::HandleModifiedEvent);
|
|
|
|
TWeakObjectPtr<UAnimNextRigVMAssetEditorData> WeakThis(this);
|
|
|
|
InControllerToConfigure->GetExternalVariablesDelegate.BindLambda([](URigVMGraph* InGraph) -> TArray<FRigVMExternalVariable> {
|
|
if (InGraph)
|
|
{
|
|
if(URigVMHost* RigVMHost = InGraph->GetTypedOuter<URigVMHost>())
|
|
{
|
|
return RigVMHost->GetExternalVariables();
|
|
}
|
|
}
|
|
return TArray<FRigVMExternalVariable>();
|
|
});
|
|
|
|
// 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<UAnimNextRigVMAsset>())
|
|
{
|
|
if (Asset->VM)
|
|
{
|
|
return &Asset->VM->GetByteCode();
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
|
|
});
|
|
|
|
#if WITH_EDITOR
|
|
InControllerToConfigure->SetupDefaultUnitNodeDelegates(TDelegate<FName(FRigVMExternalVariable, FString)>::CreateLambda(
|
|
[](FRigVMExternalVariable InVariableToCreate, FString InDefaultValue) -> FName
|
|
{
|
|
return NAME_None;
|
|
}
|
|
));
|
|
#endif
|
|
}
|
|
|
|
UObject* UAnimNextRigVMAssetEditorData::GetEditorObjectForRigVMGraph(const URigVMGraph* InVMGraph) const
|
|
{
|
|
if(InVMGraph)
|
|
{
|
|
if (InVMGraph->IsA<URigVMFunctionLibrary>())
|
|
{
|
|
return Cast<UObject>(FunctionLibraryEdGraph.Get());
|
|
}
|
|
|
|
const auto FindSubgraph = ([](const FString SearchGraphNodePath, URigVMEdGraph* EdGraph) -> URigVMEdGraph*
|
|
{
|
|
TArray<UEdGraph*> SubGraphs;
|
|
EdGraph->GetAllChildrenGraphs(SubGraphs);
|
|
for (UEdGraph* SubGraph : SubGraphs)
|
|
{
|
|
if (URigVMEdGraph* RigVMEdGraph = Cast<URigVMEdGraph>(SubGraph))
|
|
{
|
|
if (RigVMEdGraph->GetRigVMNodePath() == SearchGraphNodePath)
|
|
{
|
|
return RigVMEdGraph;
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
});
|
|
|
|
const FString GraphNodePath = InVMGraph->GetNodePath();
|
|
for(UAnimNextRigVMAssetEntry* Entry : Entries)
|
|
{
|
|
if(IAnimNextRigVMGraphInterface* GraphInterface = Cast<IAnimNextRigVMGraphInterface>(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<URigVMEdGraph>& 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<URigVMEdGraph>(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<URigVMGraph> UAnimNextRigVMAssetEditorData::CreateContainedGraphModel(URigVMCollapseNode* CollapseNode, const FName& Name)
|
|
{
|
|
check(CollapseNode);
|
|
|
|
TObjectPtr<URigVMGraph> Model = NewObject<URigVMGraph>(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<bool> CompilingGuard(bIsCompiling, true);
|
|
|
|
UAnimNextRigVMAsset* Asset = FUtils::GetAsset<UAnimNextRigVMAsset>(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<bool> ReentrantGuardSelf(bSuspendModelNotificationsForSelf, true);
|
|
TGuardValue<bool> 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<URigVMCompiler>();
|
|
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<URigVMGraph*> 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<URigVMGraph*> 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<UAnimNextRigVMAsset>(InAsset);
|
|
if(Asset == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
UAnimNextRigVMAssetEditorData* EditorData = UE::AnimNext::UncookedOnly::FUtils::GetEditorData<UAnimNextRigVMAssetEditorData>(Asset);
|
|
if(EditorData == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
URigVMController* Controller = EditorData->GetController();
|
|
Controller->OpenUndoBracket(LOCTEXT("RemoveNotifyEvents", "Remove Notify Events").ToString());
|
|
|
|
for(TObjectPtr<URigVMGraph> Model : EditorData->RigVMClient.GetModels())
|
|
{
|
|
for(URigVMNode* Node : Model->GetNodes())
|
|
{
|
|
if(URigVMUnitNode* UnitNode = Cast<URigVMUnitNode>(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<UAnimNextRigVMAsset>(InAsset);
|
|
if(Asset == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
UAnimNextRigVMAssetEditorData* EditorData = UE::AnimNext::UncookedOnly::FUtils::GetEditorData<UAnimNextRigVMAssetEditorData>(Asset);
|
|
if(EditorData == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
URigVMController* Controller = EditorData->GetController();
|
|
Controller->OpenUndoBracket(LOCTEXT("ReplaceNotifyEvents", "Replace Notify Events").ToString());
|
|
|
|
for(TObjectPtr<URigVMGraph> Model : EditorData->RigVMClient.GetModels())
|
|
{
|
|
for(URigVMNode* Node : Model->GetNodes())
|
|
{
|
|
if(URigVMUnitNode* UnitNode = Cast<URigVMUnitNode>(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<UAnimNextDataInterfaceEntry>([&bDependencyDirty](UAnimNextDataInterfaceEntry* InEntry)
|
|
{
|
|
if(InEntry->DataInterface)
|
|
{
|
|
UAnimNextRigVMAssetEditorData* EditorData = UE::AnimNext::UncookedOnly::FUtils::GetEditorData<UAnimNextRigVMAssetEditorData>(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<URigVMCollapseNode>(InSubject))
|
|
{
|
|
CreateEdGraphForCollapseNode(CollapseNode, false);
|
|
break;
|
|
}
|
|
RequestAutoVMRecompilation();
|
|
break;
|
|
}
|
|
case ERigVMGraphNotifType::NodeRemoved:
|
|
{
|
|
if (URigVMCollapseNode* CollapseNode = Cast<URigVMCollapseNode>(InSubject))
|
|
{
|
|
RemoveEdGraphForCollapseNode(CollapseNode, false);
|
|
break;
|
|
}
|
|
RequestAutoVMRecompilation();
|
|
break;
|
|
}
|
|
case ERigVMGraphNotifType::NodeRenamed:
|
|
{
|
|
if (URigVMCollapseNode* CollapseNode = Cast<URigVMCollapseNode>(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<UEdGraph>(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<URigVMPin>(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<URigVMPin>(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<UAssetUserData> UAnimNextRigVMAssetEditorData::GetAssetUserDataClass() const
|
|
{
|
|
return UAnimNextAssetWorkspaceAssetUserData::StaticClass();
|
|
}
|
|
|
|
TArray<UEdGraph*> UAnimNextRigVMAssetEditorData::GetAllEdGraphs() const
|
|
{
|
|
TArray<UEdGraph*> Graphs;
|
|
for(UAnimNextRigVMAssetEntry* Entry : Entries)
|
|
{
|
|
if(IAnimNextRigVMGraphInterface* GraphInterface = Cast<IAnimNextRigVMGraphInterface>(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<UAnimNextRigVMAssetEntry>* 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<UAnimNextRigVMAssetEntry>* 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<IAnimNextRigVMGraphInterface>(EntryToRemove))
|
|
{
|
|
// Remove any graphs
|
|
if(URigVMGraph* RigVMGraph = GraphInterface->GetRigVMGraph())
|
|
{
|
|
TGuardValue<bool> EnablePythonPrint(bSuspendPythonMessagesForRigVMClient, !bPrintPythonCommand);
|
|
TGuardValue<bool> 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<UAnimNextRigVMAssetEntry*>& InEntries, bool bSetupUndoRedo, bool bPrintPythonCommand)
|
|
{
|
|
return UE::AnimNext::UncookedOnly::FUtils::GetEditorData(InAsset)->RemoveEntries(InEntries, bSetupUndoRedo, bPrintPythonCommand);
|
|
}
|
|
|
|
bool UAnimNextRigVMAssetEditorData::RemoveEntries(TConstArrayView<UAnimNextRigVMAssetEntry*> InEntries, bool bSetupUndoRedo, bool bPrintPythonCommand)
|
|
{
|
|
bool bResult = false;
|
|
{
|
|
TGuardValue<bool> DisableEditorDataNotifications(bSuspendEditorDataNotifications, true);
|
|
TGuardValue<bool> 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<bool> DisableEditorDataNotifications(bSuspendEditorDataNotifications, true);
|
|
TGuardValue<bool> DisableAutoCompile(bAutoRecompileVM, false);
|
|
TArray<UAnimNextRigVMAssetEntry*> 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<UObject> InClass)
|
|
{
|
|
UObject* NewEntry = NewObject<UObject>(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<IAnimNextRigVMGraphInterface>(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<IAnimNextRigVMGraphInterface>(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<URigVMFunctionLibrary>())
|
|
{
|
|
if (URigVMGraph* ContainedGraph = InNode->GetContainedGraph())
|
|
{
|
|
bool bFunctionGraphExists = false;
|
|
for (UEdGraph* FunctionGraph : FunctionEdGraphs)
|
|
{
|
|
if (URigVMEdGraph* RigFunctionGraph = Cast<URigVMEdGraph>(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<UAnimNextEdGraph>(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<URigVMEdGraph>(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<UAnimNextEdGraph>(SubGraph))
|
|
{
|
|
if (SubRigGraph->ModelNodePath == ContainedGraphNodePath)
|
|
{
|
|
bSubGraphExists = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bSubGraphExists)
|
|
{
|
|
bool bEditable = true;
|
|
if (InNode->IsA<URigVMAggregateNode>())
|
|
{
|
|
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<UAnimNextEdGraph>(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<URigVMFunctionLibrary>())
|
|
{
|
|
if (URigVMGraph* ContainedGraph = InNode->GetContainedGraph())
|
|
{
|
|
for (UEdGraph* FunctionGraph : FunctionEdGraphs)
|
|
{
|
|
if (URigVMEdGraph* RigFunctionGraph = Cast<URigVMEdGraph>(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<URigVMEdGraph>(GetEditorObjectForRigVMGraph(InNode->GetGraph())))
|
|
{
|
|
if (URigVMGraph* ContainedGraph = InNode->GetContainedGraph())
|
|
{
|
|
for (UEdGraph* SubGraph : RigGraph->SubGraphs)
|
|
{
|
|
if (URigVMEdGraph* SubRigGraph = Cast<URigVMEdGraph>(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<URigVMFunctionLibrary>())
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
const bool bIsTransient = InRigVMGraph->HasAnyFlags(RF_Transient);
|
|
IAnimNextRigVMGraphInterface* Entry = Cast<IAnimNextRigVMGraphInterface>(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<IAnimNextRigVMGraphInterface>(Entries.Last()) != nullptr);
|
|
check(Cast<IAnimNextRigVMGraphInterface>(Entries.Last())->GetRigVMGraph() == nullptr);
|
|
Entry = Cast<IAnimNextRigVMGraphInterface>(FindEntryForRigVMGraph(nullptr));
|
|
}
|
|
|
|
if(Entry == nullptr && !bIsTransient)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
if(bForce)
|
|
{
|
|
RemoveEdGraph(InRigVMGraph);
|
|
}
|
|
|
|
UObject* Outer = nullptr;
|
|
EObjectFlags Flags = RF_NoFlags;
|
|
if(!bIsTransient)
|
|
{
|
|
Outer = CastChecked<UObject>(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<UAnimNextEdGraph>(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<IAnimNextRigVMGraphInterface>(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<UAnimNextVariableEntry>(this);
|
|
{
|
|
TGuardValue<bool> DisableEditorDataNotifications(bSuspendEditorDataNotifications, true);
|
|
TGuardValue<bool> 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<UScriptStruct>() ? TEXT("struct") : TEXT("class"))
|
|
: TEXT("None");
|
|
RigVMPythonUtils::Print(GetName(),
|
|
FString::Printf(TEXT("asset.add_variable('%s', %s, %s, %s, '%s')"),
|
|
*InName.ToString(),
|
|
*RigVMPythonUtils::EnumValueToPythonString<EPropertyBagPropertyType>(static_cast<int64>(InType.GetValueType())),
|
|
*RigVMPythonUtils::EnumValueToPythonString<EPropertyBagContainerType>(static_cast<int64>(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<UAnimNextEventGraphEntry>(this);
|
|
NewEntry->GraphName = NewGraphName;
|
|
NewEntry->Initialize(this);
|
|
|
|
if(bSetupUndoRedo)
|
|
{
|
|
NewEntry->Modify();
|
|
Modify();
|
|
}
|
|
|
|
AddEntryInternal(NewEntry);
|
|
|
|
// Add new graph
|
|
{
|
|
TGuardValue<bool> EnablePythonPrint(bSuspendPythonMessagesForRigVMClient, !bPrintPythonCommand);
|
|
TGuardValue<bool> 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<UObject>(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<UAnimNextDataInterface_EditorData>(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<UAnimNextDataInterfaceEntry>(Entry))
|
|
{
|
|
UAnimNextDataInterface* DataInterface = DataInterfaceEntry->GetDataInterface();
|
|
UAnimNextDataInterface_EditorData* EditorData = UE::AnimNext::UncookedOnly::FUtils::GetEditorData<UAnimNextDataInterface_EditorData>(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<UAnimNextVariableEntry>(Entry))
|
|
{
|
|
if(VariableEntry->GetExportAccessSpecifier() == EAnimNextExportAccessSpecifier::Public)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else if(UAnimNextDataInterfaceEntry* DataInterfaceEntry = Cast<UAnimNextDataInterfaceEntry>(Entry))
|
|
{
|
|
UAnimNextDataInterface* DataInterface = DataInterfaceEntry->GetDataInterface();
|
|
UAnimNextDataInterface_EditorData* EditorData = UE::AnimNext::UncookedOnly::FUtils::GetEditorData<UAnimNextDataInterface_EditorData>(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<UAnimNextDataInterfaceEntry>(InEntry))
|
|
{
|
|
return InterfaceEntry->DataInterface == InDataInterface;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
if(Entries.ContainsByPredicate(DuplicatePredicate))
|
|
{
|
|
ReportError(TEXT("UAnimNextRigVMAssetEditorData::AddDataInterface: Data interface already implemented."));
|
|
return nullptr;
|
|
}
|
|
|
|
UAnimNextDataInterfaceEntry* NewEntry = CreateNewSubEntry<UAnimNextDataInterfaceEntry>(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<UAnimNextVariableEntry>(Entry))
|
|
{
|
|
if(VariableEntry->GetExportAccessSpecifier() == EAnimNextExportAccessSpecifier::Public)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else if(UAnimNextDataInterfaceEntry* DataInterfaceEntry = Cast<UAnimNextDataInterfaceEntry>(Entry))
|
|
{
|
|
if(DataInterfaceEntry->DataInterface)
|
|
{
|
|
UAnimNextDataInterface_EditorData* EditorData = UE::AnimNext::UncookedOnly::FUtils::GetEditorData<UAnimNextDataInterface_EditorData>(DataInterfaceEntry->DataInterface.Get());
|
|
return EditorData->HasPublicVariables();
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void UAnimNextRigVMAssetEditorData::GetPublicVariables(TArray<UAnimNextVariableEntry*>& OutPublicVariables) const
|
|
{
|
|
for(UAnimNextRigVMAssetEntry* Entry : Entries)
|
|
{
|
|
if(UAnimNextVariableEntry* VariableEntry = Cast<UAnimNextVariableEntry>(Entry))
|
|
{
|
|
if(VariableEntry->GetExportAccessSpecifier() == EAnimNextExportAccessSpecifier::Public)
|
|
{
|
|
OutPublicVariables.Add(VariableEntry);
|
|
}
|
|
}
|
|
else if(UAnimNextDataInterfaceEntry* DataInterfaceEntry = Cast<UAnimNextDataInterfaceEntry>(Entry))
|
|
{
|
|
if(DataInterfaceEntry->DataInterface)
|
|
{
|
|
UAnimNextDataInterface_EditorData* EditorData = UE::AnimNext::UncookedOnly::FUtils::GetEditorData<UAnimNextDataInterface_EditorData>(DataInterfaceEntry->DataInterface.Get());
|
|
EditorData->GetPublicVariables(OutPublicVariables);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimNextRigVMAssetEditorData::GetAllVariables(TArray<UAnimNextVariableEntry*>& OutPublicVariables) const
|
|
{
|
|
for(UAnimNextRigVMAssetEntry* Entry : Entries)
|
|
{
|
|
if(UAnimNextVariableEntry* VariableEntry = Cast<UAnimNextVariableEntry>(Entry))
|
|
{
|
|
OutPublicVariables.Add(VariableEntry);
|
|
}
|
|
else if(UAnimNextDataInterfaceEntry* DataInterfaceEntry = Cast<UAnimNextDataInterfaceEntry>(Entry))
|
|
{
|
|
if(DataInterfaceEntry->DataInterface)
|
|
{
|
|
UAnimNextDataInterface_EditorData* EditorData = UE::AnimNext::UncookedOnly::FUtils::GetEditorData<UAnimNextDataInterface_EditorData>(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<URigVMNode>(SubjectForMessage))
|
|
{
|
|
if(IRigVMClientHost* RigVMClientHost = ModelNode->GetImplementingOuter<IRigVMClientHost>())
|
|
{
|
|
if(URigVMNode* OriginalModelNode = Cast<URigVMNode>(Log.FindSourceObject(ModelNode)))
|
|
{
|
|
if(URigVMEdGraph* EdGraph = Cast<URigVMEdGraph>(RigVMClientHost->GetEditorObjectForRigVMGraph(OriginalModelNode->GetGraph())))
|
|
{
|
|
if(UEdGraphNode* EdNode = EdGraph->FindNodeForModelNodeName(OriginalModelNode->GetFName()))
|
|
{
|
|
SubjectForMessage = EdNode;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TSharedPtr<FTokenizedMessage> 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<URigVMEdGraphNode>(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<URigVMEdGraph>(Graph);
|
|
if (RigGraph == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for (UEdGraphNode* GraphNode : Graph->Nodes)
|
|
{
|
|
if (URigVMEdGraphNode* RigVMEdGraphNode = Cast<URigVMEdGraphNode>(GraphNode))
|
|
{
|
|
RigVMEdGraphNode->ClearErrorInfo();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimNextRigVMAssetEditorData::RefreshExternalModels()
|
|
{
|
|
GraphModels.Reset();
|
|
|
|
for (UAnimNextRigVMAssetEntry* Entry : Entries)
|
|
{
|
|
if (IAnimNextRigVMGraphInterface* GraphInterface = Cast<IAnimNextRigVMGraphInterface>(Entry))
|
|
{
|
|
if(URigVMGraph* Model = GraphInterface->GetRigVMGraph())
|
|
{
|
|
GraphModels.Add(Model);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimNextRigVMAssetEditorData::AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector)
|
|
{
|
|
Super::AddReferencedObjects(InThis, Collector);
|
|
|
|
UAnimNextRigVMAssetEditorData* This = CastChecked<UAnimNextRigVMAssetEditorData>(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<UAnimNextRigVMAsset*> InAssets, bool bInUseExternalPackages)
|
|
{
|
|
TArray<UAnimNextRigVMAssetEditorData*> EditorDatas;
|
|
for(UAnimNextRigVMAsset* Asset : InAssets)
|
|
{
|
|
if(Asset == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
UAnimNextRigVMAssetEditorData* EditorData = UE::AnimNext::UncookedOnly::FUtils::GetEditorData<UAnimNextRigVMAssetEditorData>(Asset);
|
|
if(EditorData == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(bInUseExternalPackages != EditorData->bUsesExternalPackages)
|
|
{
|
|
EditorDatas.Add(EditorData);
|
|
}
|
|
}
|
|
|
|
if(EditorDatas.Num() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(bInUseExternalPackages)
|
|
{
|
|
TArray<UPackage*> PackagesToCheckOut;
|
|
TArray<UPackage*> PackagesToSave;
|
|
TArray<UPackage*> 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<UPackage*> 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<IAnimNextRigVMGraphInterface>(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<UPackage*> PackagesToCheckOut;
|
|
TArray<UPackage*> PackagesToSave;
|
|
TArray<UObject*> 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<IAnimNextRigVMGraphInterface>(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
|