958 lines
32 KiB
C++
958 lines
32 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "RigVMModel/RigVMBuildData.h"
|
|
#include "RigVMBlueprint.h"
|
|
#include "RigVMBlueprintGeneratedClass.h"
|
|
#include "AssetRegistry/AssetRegistryModule.h"
|
|
#include "RigVMModel/RigVMFunctionLibrary.h"
|
|
#include "UObject/UObjectIterator.h"
|
|
#include "RigVMModel/RigVMClient.h"
|
|
#include "UObject/StrongObjectPtr.h"
|
|
#include "RigVMBlueprint.h"
|
|
|
|
#if WITH_EDITOR
|
|
#include "Editor.h"
|
|
#include "Editor/EditorEngine.h"
|
|
#include "Subsystems/EditorAssetSubsystem.h"
|
|
#endif
|
|
|
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(RigVMBuildData)
|
|
|
|
static const FLazyName FunctionReferenceNodeDataName = TEXT("FunctionReferenceNodeData");
|
|
|
|
// When the object system has been completely loaded, collect all the references between RigVM graphs
|
|
static FDelayedAutoRegisterHelper GRigVMBuildDataSingletonHelper(EDelayedRegisterRunPhase::EndOfEngineInit, []() -> void
|
|
{
|
|
URigVMBuildData::Get()->InitializeIfNeeded();
|
|
});
|
|
|
|
FRigVMReferenceNodeData::FRigVMReferenceNodeData(URigVMFunctionReferenceNode* InReferenceNode)
|
|
{
|
|
check(InReferenceNode);
|
|
ReferenceNodePtr = TSoftObjectPtr<URigVMFunctionReferenceNode>(InReferenceNode);
|
|
ReferenceNodePath = ReferenceNodePtr.ToString();
|
|
ReferencedFunctionIdentifier = InReferenceNode->GetReferencedFunctionHeader().LibraryPointer;
|
|
}
|
|
|
|
TSoftObjectPtr<URigVMFunctionReferenceNode> FRigVMReferenceNodeData::GetReferenceNodeObjectPath()
|
|
{
|
|
if(ReferenceNodePtr.IsNull())
|
|
{
|
|
ReferenceNodePtr = TSoftObjectPtr<URigVMFunctionReferenceNode>(FSoftObjectPath(ReferenceNodePath));
|
|
}
|
|
return ReferenceNodePtr;
|
|
}
|
|
|
|
URigVMFunctionReferenceNode* FRigVMReferenceNodeData::GetReferenceNode()
|
|
{
|
|
if(ReferenceNodePtr.IsNull())
|
|
{
|
|
ReferenceNodePtr = TSoftObjectPtr<URigVMFunctionReferenceNode>(FSoftObjectPath(ReferenceNodePath));
|
|
}
|
|
if(!ReferenceNodePtr.IsValid())
|
|
{
|
|
ReferenceNodePtr.LoadSynchronous();
|
|
}
|
|
if(ReferenceNodePtr.IsValid())
|
|
{
|
|
return ReferenceNodePtr.Get();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool URigVMBuildData::bInitialized = false;
|
|
|
|
URigVMBuildData::URigVMBuildData()
|
|
: UObject()
|
|
, bIsRunningUnitTest(false)
|
|
{
|
|
}
|
|
|
|
TArray<UClass*> URigVMBuildData::FindAllRigVMAssetClasses()
|
|
{
|
|
// Find all classes which implement IRigVMClientHost
|
|
TArray<UClass*> ImplementedClasses;
|
|
for (TObjectIterator<UClass> ClassIterator; ClassIterator; ++ClassIterator)
|
|
{
|
|
if (ClassIterator->ImplementsInterface(URigVMClientHost::StaticClass()))
|
|
{
|
|
ImplementedClasses.AddUnique(*ClassIterator);
|
|
|
|
const UObject* CDO = ClassIterator->GetDefaultObject();
|
|
if(const URigVMBlueprint* BlueprintCDO = Cast<URigVMBlueprint>(CDO))
|
|
{
|
|
if(UClass* GeneratedClass = BlueprintCDO->GetRigVMBlueprintGeneratedClass())
|
|
{
|
|
ImplementedClasses.AddUnique(GeneratedClass);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ImplementedClasses;
|
|
}
|
|
|
|
void URigVMBuildData::SetupRigVMGraphFunctionPointers()
|
|
{
|
|
FRigVMGraphFunctionIdentifier::GetVariantRefsByGuidFunc = [this](const FGuid& InGuid)
|
|
{
|
|
return FindFunctionVariantRefs(InGuid);
|
|
};
|
|
FRigVMGraphFunctionHeader::FindFunctionHeaderFromPathFunc = [this](const FSoftObjectPath& InObjectPath, const FName& InFunctionName, bool *bOutIsPublic)
|
|
{
|
|
const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
|
|
TArray<FAssetData> Assets;
|
|
AssetRegistryModule.GetRegistry().GetAssetsByPath(*InObjectPath.ToString(), Assets, true);
|
|
if(!Assets.IsEmpty())
|
|
{
|
|
const FString FunctionNameString = InFunctionName.ToString();
|
|
const TArray<FRigVMGraphFunctionHeader> Headers = GetFunctionHeadersForAsset(Assets[0]);
|
|
for(const FRigVMGraphFunctionHeader& Header : Headers)
|
|
{
|
|
if(Header.LibraryPointer.GetFunctionName() == FunctionNameString)
|
|
{
|
|
if(bOutIsPublic)
|
|
{
|
|
// if this isn't loaded - it means that the asset is not loaded thus the function can only be public
|
|
*bOutIsPublic = true;
|
|
|
|
const FSoftObjectPath LibraryNodeSoftPath = Header.LibraryPointer.GetNodeSoftPath();
|
|
if(const URigVMLibraryNode* LibraryNode = Cast<URigVMLibraryNode>(LibraryNodeSoftPath.ResolveObject()))
|
|
{
|
|
if(const URigVMFunctionLibrary* FunctionLibrary = LibraryNode->GetTypedOuter<URigVMFunctionLibrary>())
|
|
{
|
|
*bOutIsPublic = FunctionLibrary->IsFunctionPublic(Header.LibraryPointer.GetFunctionFName());
|
|
}
|
|
}
|
|
}
|
|
return Header;
|
|
}
|
|
}
|
|
}
|
|
if(bOutIsPublic)
|
|
{
|
|
*bOutIsPublic = false;
|
|
}
|
|
return FRigVMGraphFunctionHeader();
|
|
};
|
|
FRigVMGraphFunctionData::GetFunctionHostFromObjectFunc = [this](UObject* InObject) -> IRigVMGraphFunctionHost*
|
|
{
|
|
if(IRigVMGraphFunctionHost* FunctionHost = Cast<IRigVMGraphFunctionHost>(InObject))
|
|
{
|
|
return FunctionHost;
|
|
}
|
|
if(IRigVMGraphFunctionHost* OuterFunctionHost = InObject->GetImplementingOuter<IRigVMGraphFunctionHost>())
|
|
{
|
|
return OuterFunctionHost;
|
|
}
|
|
if(IRigVMClientHost* ClientHost = Cast<IRigVMClientHost>(InObject))
|
|
{
|
|
if(IRigVMGraphFunctionHost* FunctionHost = ClientHost->GetRigVMGraphFunctionHost())
|
|
{
|
|
return FunctionHost;
|
|
}
|
|
}
|
|
if(IRigVMClientHost* OuterClientHost = InObject->GetImplementingOuter<IRigVMClientHost>())
|
|
{
|
|
if(IRigVMGraphFunctionHost* FunctionHost = OuterClientHost->GetRigVMGraphFunctionHost())
|
|
{
|
|
return FunctionHost;
|
|
}
|
|
}
|
|
if(URigVMBlueprint* Blueprint = Cast<URigVMBlueprint>(InObject))
|
|
{
|
|
return Blueprint->GetRigVMGraphFunctionHost();
|
|
}
|
|
if(URigVMBlueprint* Blueprint = InObject->GetTypedOuter<URigVMBlueprint>())
|
|
{
|
|
return Blueprint->GetRigVMGraphFunctionHost();
|
|
}
|
|
return nullptr;
|
|
};
|
|
}
|
|
|
|
void URigVMBuildData::TearDownRigVMGraphFunctionPointers()
|
|
{
|
|
FRigVMGraphFunctionIdentifier::GetVariantRefsByGuidFunc.Reset();
|
|
FRigVMGraphFunctionHeader::FindFunctionHeaderFromPathFunc.Reset();
|
|
FRigVMGraphFunctionData::GetFunctionHostFromObjectFunc.Reset();
|
|
}
|
|
|
|
TArray<FRigVMGraphFunctionHeader> URigVMBuildData::GetFunctionHeadersForAsset(const FAssetData& InAssetData)
|
|
{
|
|
TArray<FRigVMGraphFunctionHeader> Result;
|
|
if (!InAssetData.IsInstanceOf(URigVMBlueprint::StaticClass()) &&
|
|
!InAssetData.IsInstanceOf(URigVMBlueprintGeneratedClass::StaticClass()))
|
|
{
|
|
return Result;
|
|
}
|
|
|
|
// If the asset is loaded, gather the function variants from the function store
|
|
// which will include private functions
|
|
if (InAssetData.IsAssetLoaded())
|
|
{
|
|
UObject* AssetObject = InAssetData.GetAsset();
|
|
URigVMBlueprintGeneratedClass* GeneratedClass = Cast<URigVMBlueprintGeneratedClass>(AssetObject);
|
|
if (!GeneratedClass)
|
|
{
|
|
if (URigVMBlueprint* RigVMBlueprint = Cast<URigVMBlueprint>(AssetObject))
|
|
{
|
|
GeneratedClass = RigVMBlueprint->GetRigVMBlueprintGeneratedClass();
|
|
}
|
|
}
|
|
|
|
if (GeneratedClass)
|
|
{
|
|
for (int32 Pass=0; Pass<2; Pass++)
|
|
{
|
|
TArray<FRigVMGraphFunctionData>& Functions = (Pass == 0)
|
|
? GeneratedClass->GraphFunctionStore.PrivateFunctions
|
|
: GeneratedClass->GraphFunctionStore.PublicFunctions;
|
|
|
|
for (FRigVMGraphFunctionData& Data : Functions)
|
|
{
|
|
Result.Add(Data.Header);
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
}
|
|
|
|
// If asset is not loaded, gather function variants from metadata
|
|
const FString PublicGraphFunctionsString = InAssetData.GetTagValueRef<FString>(TEXT("PublicGraphFunctions"));
|
|
if(!PublicGraphFunctionsString.IsEmpty())
|
|
{
|
|
static const FName FunctionsPropertyName = GET_MEMBER_NAME_CHECKED(URigVMBlueprint, PublicGraphFunctions);
|
|
const FArrayProperty* PublicGraphFunctionsProperty = CastField<FArrayProperty>(URigVMBlueprint::StaticClass()->FindPropertyByName(FunctionsPropertyName));
|
|
PublicGraphFunctionsProperty->ImportText_Direct(*PublicGraphFunctionsString, &Result, nullptr, EPropertyPortFlags::PPF_None);
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
URigVMBuildData* URigVMBuildData::Get()
|
|
{
|
|
// static in a function scope ensures that the GC system is initiated before
|
|
// the build data constructor is called
|
|
static TStrongObjectPtr<URigVMBuildData> sBuildData;
|
|
if(!sBuildData.IsValid() && IsInGameThread())
|
|
{
|
|
sBuildData = TStrongObjectPtr<URigVMBuildData>(
|
|
NewObject<URigVMBuildData>(
|
|
GetTransientPackage(),
|
|
TEXT("RigVMBuildData"),
|
|
RF_Transient));
|
|
}
|
|
return sBuildData.Get();
|
|
|
|
}
|
|
|
|
void URigVMBuildData::InitializeIfNeeded()
|
|
{
|
|
if (bInitialized)
|
|
{
|
|
return;
|
|
}
|
|
|
|
TArray<UClass*> ImplementedClasses = FindAllRigVMAssetClasses();
|
|
|
|
// Loop the classes
|
|
for (UClass* Class : ImplementedClasses)
|
|
{
|
|
const FArrayProperty* ReferenceNodeDataProperty =
|
|
CastField<FArrayProperty>(Class->FindPropertyByName(FunctionReferenceNodeDataName));
|
|
if(ReferenceNodeDataProperty)
|
|
{
|
|
const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
|
|
|
|
// find all assets of this class in the project
|
|
TArray<FAssetData> AssetDatas;
|
|
FARFilter AssetFilter;
|
|
AssetFilter.ClassPaths.Add(Class->GetClassPathName());
|
|
AssetRegistryModule.Get().GetAssets(AssetFilter, AssetDatas);
|
|
|
|
// loop over all found assets
|
|
for(const FAssetData& AssetData : AssetDatas)
|
|
{
|
|
RegisterReferencesFromAsset(AssetData);
|
|
}
|
|
}
|
|
}
|
|
|
|
const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
|
|
AssetRegistryModule.Get().OnAssetAdded().AddStatic(&URigVMBuildData::RegisterReferencesFromAsset);
|
|
|
|
bInitialized = true;
|
|
}
|
|
|
|
void URigVMBuildData::RegisterReferencesFromAsset(const FAssetData& InAssetData)
|
|
{
|
|
URigVMBuildData* BuildData = URigVMBuildData::Get();
|
|
|
|
// It's faster to check for a key directly then trying to get the class
|
|
FAssetDataTagMapSharedView::FFindTagResult FoundValue = InAssetData.TagsAndValues.FindTag(FunctionReferenceNodeDataName);
|
|
|
|
if (FoundValue.IsSet())
|
|
{
|
|
if (UClass* Class = InAssetData.GetClass())
|
|
{
|
|
const FArrayProperty* ReferenceNodeDataProperty =
|
|
CastField<FArrayProperty>(Class->FindPropertyByName(FunctionReferenceNodeDataName));
|
|
if(ReferenceNodeDataProperty)
|
|
{
|
|
const FString ReferenceNodeDataString = FoundValue.AsString();
|
|
if(ReferenceNodeDataString.IsEmpty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// See if it has reference node data, and register the references
|
|
TArray<FRigVMReferenceNodeData> ReferenceNodeDatas;
|
|
ReferenceNodeDataProperty->ImportText_Direct(*ReferenceNodeDataString, &ReferenceNodeDatas, nullptr, EPropertyPortFlags::PPF_None);
|
|
for(FRigVMReferenceNodeData& ReferenceNodeData : ReferenceNodeDatas)
|
|
{
|
|
if (ReferenceNodeData.ReferencedFunctionIdentifier.GetNodeSoftPath().IsValid())
|
|
{
|
|
BuildData->RegisterFunctionReference(ReferenceNodeData.ReferencedFunctionIdentifier, ReferenceNodeData.GetReferenceNodeObjectPath());
|
|
}
|
|
else if (ReferenceNodeData.ReferencedHeader_DEPRECATED.IsValid())
|
|
{
|
|
BuildData->RegisterFunctionReference(ReferenceNodeData.ReferencedHeader_DEPRECATED.LibraryPointer, ReferenceNodeData.GetReferenceNodeObjectPath());
|
|
}
|
|
else if (!ReferenceNodeData.ReferencedFunctionPath_DEPRECATED.IsEmpty())
|
|
{
|
|
BuildData->RegisterFunctionReference(ReferenceNodeData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const FRigVMFunctionReferenceArray* URigVMBuildData::FindFunctionReferences(const FRigVMGraphFunctionIdentifier& InFunction) const
|
|
{
|
|
return GraphFunctionReferences.Find(InFunction);
|
|
}
|
|
|
|
void URigVMBuildData::ForEachFunctionReference(const FRigVMGraphFunctionIdentifier& InFunction,
|
|
TFunction<void(URigVMFunctionReferenceNode*)> PerReferenceFunction,
|
|
bool bLoadIfNecessary) const
|
|
{
|
|
if (const FRigVMFunctionReferenceArray* ReferencesEntry = FindFunctionReferences(InFunction))
|
|
{
|
|
for (int32 ReferenceIndex = 0; ReferenceIndex < ReferencesEntry->Num(); ReferenceIndex++)
|
|
{
|
|
const TSoftObjectPtr<URigVMFunctionReferenceNode>& Reference = ReferencesEntry->operator [](ReferenceIndex);
|
|
if (bLoadIfNecessary && !Reference.IsValid())
|
|
{
|
|
Reference.LoadSynchronous();
|
|
}
|
|
if (Reference.IsValid())
|
|
{
|
|
PerReferenceFunction(Reference.Get());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void URigVMBuildData::ForEachFunctionReferenceSoftPtr(const FRigVMGraphFunctionIdentifier& InFunction,
|
|
TFunction<void(TSoftObjectPtr<URigVMFunctionReferenceNode>)> PerReferenceFunction) const
|
|
{
|
|
if (const FRigVMFunctionReferenceArray* ReferencesEntry = FindFunctionReferences(InFunction))
|
|
{
|
|
for (int32 ReferenceIndex = 0; ReferenceIndex < ReferencesEntry->Num(); ReferenceIndex++)
|
|
{
|
|
const TSoftObjectPtr<URigVMFunctionReferenceNode>& Reference = ReferencesEntry->operator [](ReferenceIndex);
|
|
PerReferenceFunction(Reference);
|
|
}
|
|
}
|
|
}
|
|
|
|
void URigVMBuildData::RegisterFunctionReference(const FRigVMGraphFunctionIdentifier& InFunction, URigVMFunctionReferenceNode* InReference)
|
|
{
|
|
if(InReference == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const TSoftObjectPtr<URigVMFunctionReferenceNode> ReferenceKey(InReference);
|
|
|
|
RegisterFunctionReference(InFunction, ReferenceKey);
|
|
}
|
|
|
|
void URigVMBuildData::RegisterFunctionReference(const FRigVMGraphFunctionIdentifier& InFunction,
|
|
TSoftObjectPtr<URigVMFunctionReferenceNode> InReference)
|
|
{
|
|
if(InReference.IsNull())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(FRigVMFunctionReferenceArray* ReferenceEntry = GraphFunctionReferences.Find(InFunction))
|
|
{
|
|
if(ReferenceEntry->FunctionReferences.Contains(InReference))
|
|
{
|
|
return;
|
|
}
|
|
|
|
Modify();
|
|
ReferenceEntry->FunctionReferences.Add(InReference);
|
|
}
|
|
else
|
|
{
|
|
Modify();
|
|
FRigVMFunctionReferenceArray NewReferenceEntry;
|
|
NewReferenceEntry.FunctionReferences.Add(InReference);
|
|
GraphFunctionReferences.Add(InFunction, NewReferenceEntry);
|
|
}
|
|
|
|
MarkPackageDirty();
|
|
}
|
|
|
|
void URigVMBuildData::RegisterFunctionReference(FRigVMReferenceNodeData InReferenceNodeData)
|
|
{
|
|
if (InReferenceNodeData.ReferencedFunctionIdentifier.GetNodeSoftPath().IsValid())
|
|
{
|
|
return RegisterFunctionReference(InReferenceNodeData.ReferencedFunctionIdentifier, InReferenceNodeData.GetReferenceNodeObjectPath());
|
|
}
|
|
|
|
if (!InReferenceNodeData.ReferencedFunctionIdentifier.GetNodeSoftPath().IsValid())
|
|
{
|
|
InReferenceNodeData.ReferencedFunctionIdentifier = InReferenceNodeData.ReferencedHeader_DEPRECATED.LibraryPointer;
|
|
}
|
|
|
|
if (!InReferenceNodeData.ReferencedFunctionIdentifier.GetNodeSoftPath().IsValid())
|
|
{
|
|
InReferenceNodeData.ReferencedFunctionIdentifier.SetLibraryNodePath(InReferenceNodeData.ReferencedFunctionPath_DEPRECATED);
|
|
}
|
|
|
|
check(InReferenceNodeData.ReferencedFunctionIdentifier.GetNodeSoftPath().IsValid());
|
|
|
|
FString LibraryNodePath = InReferenceNodeData.ReferencedFunctionIdentifier.GetLibraryNodePath();
|
|
TSoftObjectPtr<URigVMLibraryNode> LibraryNodePtr = TSoftObjectPtr<URigVMLibraryNode>(FSoftObjectPath(LibraryNodePath));
|
|
|
|
// Try to find a FunctionIdentifier with the same LibraryNodePath
|
|
bool bFound = false;
|
|
for (TPair< FRigVMGraphFunctionIdentifier, FRigVMFunctionReferenceArray >& Pair : GraphFunctionReferences)
|
|
{
|
|
if (Pair.Key.GetLibraryNodePath() == LibraryNodePath)
|
|
{
|
|
Pair.Value.FunctionReferences.Add(InReferenceNodeData.GetReferenceNodeObjectPath());
|
|
bFound = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Otherwise, lets add a new identifier, even if it has no function host
|
|
if (!bFound)
|
|
{
|
|
FRigVMGraphFunctionIdentifier Pointer(nullptr, LibraryNodePath);
|
|
if (LibraryNodePtr.IsValid())
|
|
{
|
|
Pointer.HostObject = Cast<UObject>(LibraryNodePtr.Get()->GetFunctionHeader().GetFunctionHost());
|
|
}
|
|
FRigVMFunctionReferenceArray RefArray;
|
|
RefArray.FunctionReferences.Add(InReferenceNodeData.GetReferenceNodeObjectPath());
|
|
GraphFunctionReferences.Add(Pointer, RefArray);
|
|
}
|
|
}
|
|
|
|
void URigVMBuildData::UnregisterFunctionReference(const FRigVMGraphFunctionIdentifier& InFunction,
|
|
URigVMFunctionReferenceNode* InReference)
|
|
{
|
|
if(InReference == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const TSoftObjectPtr<URigVMFunctionReferenceNode> ReferenceKey(InReference);
|
|
|
|
return UnregisterFunctionReference(InFunction, ReferenceKey);
|
|
}
|
|
|
|
void URigVMBuildData::UnregisterFunctionReference(const FRigVMGraphFunctionIdentifier& InFunction,
|
|
TSoftObjectPtr<URigVMFunctionReferenceNode> InReference)
|
|
{
|
|
if(InReference.IsNull())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(FRigVMFunctionReferenceArray* ReferenceEntry = GraphFunctionReferences.Find(InFunction))
|
|
{
|
|
if(!ReferenceEntry->FunctionReferences.Contains(InReference))
|
|
{
|
|
return;
|
|
}
|
|
|
|
Modify();
|
|
ReferenceEntry->FunctionReferences.Remove(InReference);
|
|
MarkPackageDirty();
|
|
}
|
|
}
|
|
|
|
void URigVMBuildData::ClearInvalidReferences()
|
|
{
|
|
if (bIsRunningUnitTest)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Modify(false);
|
|
|
|
// check each function's each reference
|
|
int32 NumRemoved = 0;
|
|
for (TTuple<FRigVMGraphFunctionIdentifier, FRigVMFunctionReferenceArray>& FunctionReferenceInfo : GraphFunctionReferences)
|
|
{
|
|
FRigVMFunctionReferenceArray* ReferencesEntry = &FunctionReferenceInfo.Value;
|
|
|
|
static FString sTransientPackagePrefix;
|
|
if(sTransientPackagePrefix.IsEmpty())
|
|
{
|
|
sTransientPackagePrefix = GetTransientPackage()->GetPathName();
|
|
}
|
|
static const FString sTempPrefix = TEXT("/Temp/");
|
|
|
|
NumRemoved += ReferencesEntry->FunctionReferences.RemoveAll([](TSoftObjectPtr<URigVMFunctionReferenceNode> Referencer)
|
|
{
|
|
// ignore keys / references within the transient package
|
|
const FString ReferencerString = Referencer.ToString();
|
|
return ReferencerString.StartsWith(sTransientPackagePrefix) || ReferencerString.StartsWith(sTempPrefix);
|
|
});
|
|
}
|
|
|
|
if (NumRemoved > 0)
|
|
{
|
|
MarkPackageDirty();
|
|
}
|
|
}
|
|
|
|
TArray<FRigVMVariantRef> URigVMBuildData::GatherAllFunctionVariantRefs() const
|
|
{
|
|
TArray<FRigVMVariantRef> Result;
|
|
const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
|
|
TArray<FAssetData> Assets;
|
|
FARFilter AssetFilter;
|
|
AssetFilter.ClassPaths.Add(URigVMBlueprint::StaticClass()->GetClassPathName());
|
|
AssetFilter.ClassPaths.Add(URigVMBlueprintGeneratedClass::StaticClass()->GetClassPathName());
|
|
AssetFilter.bRecursiveClasses = true;
|
|
AssetRegistryModule.Get().GetAssets(AssetFilter, Assets);
|
|
|
|
for (const FAssetData& Asset : Assets)
|
|
{
|
|
if(Asset.IsAssetLoaded())
|
|
{
|
|
if(const IRigVMClientHost* ClientHost = Cast<IRigVMClientHost>(Asset.GetAsset()))
|
|
{
|
|
if(const URigVMFunctionLibrary* FunctionLibrary = ClientHost->GetLocalFunctionLibrary())
|
|
{
|
|
const TArray<URigVMLibraryNode*> Functions = FunctionLibrary->GetFunctions();
|
|
for(const URigVMLibraryNode* Function : Functions)
|
|
{
|
|
FRigVMVariant FunctionVariant = Function->GetFunctionVariant();
|
|
if(!FunctionVariant.Guid.IsValid())
|
|
{
|
|
FunctionVariant.Guid = FRigVMVariant::GenerateGUID(Function->GetPathName());
|
|
}
|
|
Result.Add(FRigVMVariantRef(Function->GetPathName(), FunctionVariant));
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
TArray<FRigVMVariantRef> VariantRefs = URigVMBuildData::GatherFunctionVariantRefsForAsset(Asset);
|
|
Result.Append(VariantRefs);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
TArray<FRigVMVariantRef> URigVMBuildData::GatherFunctionVariantRefsForAsset(const FAssetData& InAssetData) const
|
|
{
|
|
TArray<FRigVMVariantRef> Result;
|
|
|
|
const TArray<FRigVMGraphFunctionHeader> FunctionHeaders = GetFunctionHeadersForAsset(InAssetData);
|
|
for(const FRigVMGraphFunctionHeader& FunctionHeader : FunctionHeaders)
|
|
{
|
|
if (FunctionHeader.LibraryPointer.IsValid())
|
|
{
|
|
FRigVMVariantRef& VariantRef = Result.Add_GetRef(FRigVMVariantRef());
|
|
VariantRef.Variant = FunctionHeader.Variant;
|
|
VariantRef.ObjectPath = FunctionHeader.LibraryPointer.GetNodeSoftPath();
|
|
if (!VariantRef.Variant.Guid.IsValid())
|
|
{
|
|
VariantRef.Variant.Guid = FRigVMVariant::GenerateGUID(VariantRef.ObjectPath.ToString());
|
|
}
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
TArray<FRigVMVariantRef> URigVMBuildData::FindFunctionVariantRefs(const FGuid& InGuid) const
|
|
{
|
|
TArray<FRigVMVariantRef> Result = GatherAllFunctionVariantRefs();
|
|
Result = Result.FilterByPredicate([InGuid](const FRigVMVariantRef& VariantRef) { return VariantRef.Variant.Guid == InGuid; });
|
|
return Result;
|
|
}
|
|
|
|
FRigVMGraphFunctionIdentifier URigVMBuildData::GetFunctionIdentifierForVariant(const FRigVMVariantRef& InVariantRef) const
|
|
{
|
|
FString SubPathString = InVariantRef.ObjectPath.GetSubPathString();
|
|
if(!SubPathString.IsEmpty())
|
|
{
|
|
int32 LastSlashIndex = INDEX_NONE;
|
|
if(SubPathString.FindLastChar(TEXT('.'), LastSlashIndex))
|
|
{
|
|
SubPathString = SubPathString.Mid(LastSlashIndex + 1);
|
|
}
|
|
|
|
const FRigVMGraphFunctionHeader Header = FRigVMGraphFunctionHeader::FindGraphFunctionHeader(InVariantRef.ObjectPath, *SubPathString, nullptr, nullptr);
|
|
if(Header.IsValid())
|
|
{
|
|
return Header.LibraryPointer;
|
|
}
|
|
}
|
|
return FRigVMGraphFunctionIdentifier();
|
|
}
|
|
|
|
FRigVMVariantRef URigVMBuildData::CreateFunctionVariant(const FRigVMGraphFunctionIdentifier& InFunctionIdentifier, FName InName)
|
|
{
|
|
const FSoftObjectPath LibraryNodePath = FSoftObjectPath(InFunctionIdentifier.GetLibraryNodePath());
|
|
if(const URigVMLibraryNode* LibraryNode = Cast<URigVMLibraryNode>(LibraryNodePath.TryLoad()))
|
|
{
|
|
if(URigVMFunctionLibrary* FunctionLibrary = Cast<URigVMFunctionLibrary>(LibraryNode->GetGraph()))
|
|
{
|
|
if(URigVMBlueprint* RigVMBlueprint = FunctionLibrary->GetTypedOuter<URigVMBlueprint>())
|
|
{
|
|
if(URigVMController* Controller = RigVMBlueprint->GetOrCreateController(FunctionLibrary))
|
|
{
|
|
if(const URigVMLibraryNode* VariantNode = Controller->CreateFunctionVariant(InFunctionIdentifier.GetFunctionFName(), InName))
|
|
{
|
|
return VariantNode->GetFunctionVariantRef();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return FRigVMVariantRef();
|
|
}
|
|
|
|
TArray<FRigVMVariantRef> URigVMBuildData::GatherAllAssetVariantRefs() const
|
|
{
|
|
TArray<FRigVMVariantRef> Result;
|
|
const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
|
|
TArray<FAssetData> Assets;
|
|
FARFilter AssetFilter;
|
|
AssetFilter.ClassPaths.Add(URigVMBlueprint::StaticClass()->GetClassPathName());
|
|
AssetFilter.bRecursiveClasses = true;
|
|
AssetRegistryModule.Get().GetAssets(AssetFilter, Assets);
|
|
|
|
for (const FAssetData& Asset : Assets)
|
|
{
|
|
Result.Add(GetVariantRefForAsset(Asset));
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
TArray<FRigVMVariantRef> URigVMBuildData::FindAssetVariantRefs(const FGuid& InGuid) const
|
|
{
|
|
TArray<FRigVMVariantRef> Result = GatherAllAssetVariantRefs();
|
|
Result = Result.FilterByPredicate([InGuid](const FRigVMVariantRef& VariantRef) { return VariantRef.Variant.Guid == InGuid; });
|
|
return Result;
|
|
}
|
|
|
|
FRigVMVariantRef URigVMBuildData::CreateAssetVariant(const FAssetData& InAssetData, FName InName)
|
|
{
|
|
FRigVMVariantRef TargetVariantRef;
|
|
|
|
#if WITH_EDITOR
|
|
const FRigVMVariantRef SourceVariantRef = GetVariantRefForAsset(InAssetData);
|
|
if(!SourceVariantRef.IsValid())
|
|
{
|
|
return SourceVariantRef;
|
|
}
|
|
|
|
UEditorAssetSubsystem* EditorAssetSubsystem = GEditor->GetEditorSubsystem<UEditorAssetSubsystem>();
|
|
|
|
const FString SourcePackageLongName = InAssetData.GetSoftObjectPath().GetWithoutSubPath().ToString();
|
|
FString SourcePackageDirectory, SourcePackagePath, SourcePackageName;
|
|
FPackageName::SplitLongPackageName(SourcePackageLongName, SourcePackageDirectory, SourcePackagePath, SourcePackageName);
|
|
|
|
int32 LastPeriodPos = INDEX_NONE;
|
|
if(SourcePackageName.FindChar(TEXT('.'), LastPeriodPos))
|
|
{
|
|
SourcePackageName = SourcePackageName.Left(LastPeriodPos);
|
|
}
|
|
|
|
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
|
|
|
|
int32 Suffix = 1;
|
|
const FString DestinationAssetNameBase = InName.IsNone() ? SourcePackageName : InName.ToString();
|
|
FString DestinationAssetName = DestinationAssetNameBase;
|
|
FString DestinationPackageLongName;
|
|
do
|
|
{
|
|
DestinationPackageLongName = SourcePackageDirectory + SourcePackagePath + DestinationAssetName + TEXT(".") + DestinationAssetName;
|
|
DestinationAssetName = DestinationAssetNameBase + TEXT("_") + FString::FromInt(++Suffix);
|
|
}
|
|
while (AssetRegistryModule.Get().GetAssetByObjectPath(FSoftObjectPath(DestinationPackageLongName)).IsValid());
|
|
|
|
if(UObject* DuplicatedAsset = EditorAssetSubsystem->DuplicateAsset(SourcePackageLongName, DestinationPackageLongName))
|
|
{
|
|
if(URigVMBlueprint* RigVMBlueprint = Cast<URigVMBlueprint>(DuplicatedAsset))
|
|
{
|
|
// since we duplicated the asset the variant guid will be the same too,
|
|
// but to be sure we'll run the join code path as well.
|
|
(void)RigVMBlueprint->JoinAssetVariant(SourceVariantRef.Variant.Guid);
|
|
TargetVariantRef = RigVMBlueprint->GetAssetVariantRef();
|
|
}
|
|
}
|
|
#endif
|
|
return TargetVariantRef;
|
|
}
|
|
|
|
FAssetData URigVMBuildData::GetAssetDataForPath(const FSoftObjectPath& InObjectPath) const
|
|
{
|
|
#if WITH_EDITOR
|
|
UEditorAssetSubsystem* EditorAssetSubsystem = GEditor->GetEditorSubsystem<UEditorAssetSubsystem>();
|
|
return EditorAssetSubsystem->FindAssetData(InObjectPath.GetWithoutSubPath().ToString());
|
|
#else
|
|
return FAssetData();
|
|
#endif
|
|
}
|
|
|
|
FRigVMVariantRef URigVMBuildData::GetVariantRefForAsset(const FAssetData& InAssetData) const
|
|
{
|
|
FRigVMVariant AssetVariant;
|
|
if(InAssetData.IsAssetLoaded())
|
|
{
|
|
if(const URigVMBlueprint* Blueprint = Cast<URigVMBlueprint>(InAssetData.GetAsset()))
|
|
{
|
|
AssetVariant = Blueprint->GetAssetVariant();
|
|
}
|
|
}
|
|
|
|
if (!AssetVariant.Guid.IsValid())
|
|
{
|
|
static const FName AssetVariantPropertyName = GET_MEMBER_NAME_CHECKED(URigVMBlueprint, AssetVariant);
|
|
const FProperty* AssetVariantProperty = CastField<FProperty>(URigVMBlueprint::StaticClass()->FindPropertyByName(AssetVariantPropertyName));
|
|
const FString VariantStr = InAssetData.GetTagValueRef<FString>(AssetVariantPropertyName);
|
|
if(!VariantStr.IsEmpty())
|
|
{
|
|
AssetVariantProperty->ImportText_Direct(*VariantStr, &AssetVariant, nullptr, EPropertyPortFlags::PPF_None);
|
|
}
|
|
}
|
|
|
|
if (!AssetVariant.Guid.IsValid())
|
|
{
|
|
AssetVariant.Guid = FRigVMVariant::GenerateGUID(InAssetData.PackageName.ToString());
|
|
}
|
|
|
|
return FRigVMVariantRef(InAssetData.ToSoftObjectPath(), AssetVariant);
|
|
}
|
|
|
|
FAssetData URigVMBuildData::GetAssetDataForVariant(const FRigVMVariantRef& InVariantRef) const
|
|
{
|
|
return GetAssetDataForPath(InVariantRef.ObjectPath);
|
|
}
|
|
|
|
FRigVMVariantRef URigVMBuildData::SplitVariantFromSet(const FRigVMVariantRef& InVariantRef)
|
|
{
|
|
FRigVMVariantRef Result = InVariantRef;
|
|
if(Result.IsValid())
|
|
{
|
|
FAssetData AssetData = GetAssetDataForVariant(Result);
|
|
if(URigVMBlueprint* RigVMBlueprint = Cast<URigVMBlueprint>(AssetData.GetAsset()))
|
|
{
|
|
if(UObject* Subject = InVariantRef.ObjectPath.TryLoad())
|
|
{
|
|
if(Subject == RigVMBlueprint)
|
|
{
|
|
if(RigVMBlueprint->SplitAssetVariant())
|
|
{
|
|
Result.Variant = RigVMBlueprint->GetAssetVariant();
|
|
}
|
|
}
|
|
else if(URigVMLibraryNode* FunctionNode = Cast<URigVMLibraryNode>(Subject))
|
|
{
|
|
if(URigVMFunctionLibrary* FunctionLibrary = Cast<URigVMFunctionLibrary>(FunctionNode->GetGraph()))
|
|
{
|
|
if(URigVMController* Controller = RigVMBlueprint->GetOrCreateController(FunctionLibrary))
|
|
{
|
|
if(Controller->SplitFunctionVariant(FunctionNode->GetFName()))
|
|
{
|
|
Result.Variant = FunctionNode->GetFunctionVariant();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
FRigVMVariantRef URigVMBuildData::JoinVariantSet(const FRigVMVariantRef& InVariantRef, const FGuid& InGuid)
|
|
{
|
|
FRigVMVariantRef Result = InVariantRef;
|
|
if(Result.IsValid() && InGuid.IsValid())
|
|
{
|
|
FAssetData AssetData = GetAssetDataForVariant(Result);
|
|
if(URigVMBlueprint* RigVMBlueprint = Cast<URigVMBlueprint>(AssetData.GetAsset()))
|
|
{
|
|
if(UObject* Subject = InVariantRef.ObjectPath.TryLoad())
|
|
{
|
|
if(Subject == RigVMBlueprint)
|
|
{
|
|
if(RigVMBlueprint->JoinAssetVariant(InGuid))
|
|
{
|
|
Result.Variant = RigVMBlueprint->GetAssetVariant();
|
|
}
|
|
}
|
|
else if(URigVMLibraryNode* FunctionNode = Cast<URigVMLibraryNode>(Subject))
|
|
{
|
|
if(URigVMFunctionLibrary* FunctionLibrary = Cast<URigVMFunctionLibrary>(FunctionNode->GetGraph()))
|
|
{
|
|
if(URigVMController* Controller = RigVMBlueprint->GetOrCreateController(FunctionLibrary))
|
|
{
|
|
if(Controller->JoinFunctionVariant(FunctionNode->GetFName(), InGuid))
|
|
{
|
|
Result.Variant = FunctionNode->GetFunctionVariant();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
|
|
TArray<FRigVMGraphFunctionIdentifier> URigVMBuildData::GetAllFunctionIdentifiers(bool bOnlyPublic) const
|
|
{
|
|
static const FName PublicGraphFunctionsName = TEXT("PublicGraphFunctions");
|
|
TArray<FRigVMGraphFunctionIdentifier> Identifiers;
|
|
TArray<UClass*> ImplementedClasses = FindAllRigVMAssetClasses();
|
|
|
|
const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
|
|
|
|
// find all assets of this class in the project
|
|
TArray<FAssetData> AssetDatas;
|
|
FARFilter AssetFilter;
|
|
for(const UClass* Class : ImplementedClasses)
|
|
{
|
|
AssetFilter.ClassPaths.AddUnique(Class->GetClassPathName());
|
|
}
|
|
AssetRegistryModule.Get().GetAssets(AssetFilter, AssetDatas);
|
|
|
|
// loop over all found assets
|
|
TSet<FName> PackagesProcessed;
|
|
for(const FAssetData& AssetData : AssetDatas)
|
|
{
|
|
// Avoid duplication
|
|
if (PackagesProcessed.Contains(AssetData.PackageName))
|
|
{
|
|
continue;
|
|
}
|
|
PackagesProcessed.Add(AssetData.PackageName);
|
|
|
|
if(AssetData.IsAssetLoaded())
|
|
{
|
|
IRigVMGraphFunctionHost* FunctionHost = Cast<IRigVMGraphFunctionHost>(AssetData.GetAsset());
|
|
if(FunctionHost == nullptr)
|
|
{
|
|
if(IRigVMClientHost* ClientHost = Cast<IRigVMClientHost>(AssetData.GetAsset()))
|
|
{
|
|
FunctionHost = ClientHost->GetRigVMGraphFunctionHost();
|
|
}
|
|
}
|
|
if(FunctionHost)
|
|
{
|
|
if(const FRigVMGraphFunctionStore* Store = FunctionHost->GetRigVMGraphFunctionStore())
|
|
{
|
|
for(const FRigVMGraphFunctionData& Data : Store->PublicFunctions)
|
|
{
|
|
Identifiers.Add(Data.Header.LibraryPointer);
|
|
}
|
|
if(!bOnlyPublic)
|
|
{
|
|
for(const FRigVMGraphFunctionData& Data : Store->PrivateFunctions)
|
|
{
|
|
Identifiers.Add(Data.Header.LibraryPointer);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
FString PublicGraphFunctionsString = AssetData.GetTagValueRef<FString>(PublicGraphFunctionsName);
|
|
if (PublicGraphFunctionsString.IsEmpty())
|
|
{
|
|
PublicGraphFunctionsString = AssetData.GetTagValueRef<FString>(PublicGraphFunctionsName);
|
|
}
|
|
if (!PublicGraphFunctionsString.IsEmpty())
|
|
{
|
|
const FArrayProperty* Property = CastField<FArrayProperty>(AssetData.GetClass()->FindPropertyByName(PublicGraphFunctionsName));
|
|
if (Property)
|
|
{
|
|
TArray<FRigVMGraphFunctionHeader> PublicFunctions;
|
|
Property->ImportText_Direct(*PublicGraphFunctionsString, &PublicFunctions, nullptr, EPropertyPortFlags::PPF_None);
|
|
for(const FRigVMGraphFunctionHeader& PublicFunction : PublicFunctions)
|
|
{
|
|
Identifiers.Add(PublicFunction.LibraryPointer);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const FString& HeadersString = PublicGraphFunctionsString;
|
|
const FArrayProperty* HeadersArrayProperty = CastField<FArrayProperty>(FRigVMGraphFunctionHeaderArray::StaticStruct()->FindPropertyByName(TEXT("Headers")));
|
|
TArray<FRigVMGraphFunctionHeader> PublicFunctions;
|
|
HeadersArrayProperty->ImportText_Direct(*HeadersString, &PublicFunctions, nullptr, EPropertyPortFlags::PPF_None);
|
|
|
|
for(const FRigVMGraphFunctionHeader& PublicFunction : PublicFunctions)
|
|
{
|
|
Identifiers.Add(PublicFunction.LibraryPointer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Identifiers;
|
|
}
|
|
|
|
#endif
|
|
|
|
TArray<FRigVMGraphFunctionIdentifier> URigVMBuildData::GetUsedFunctionIdentifiers(bool bOnlyPublic) const
|
|
{
|
|
TArray<FRigVMGraphFunctionIdentifier> Identifiers;
|
|
#if WITH_EDITOR
|
|
Identifiers = GetAllFunctionIdentifiers().FilterByPredicate([this](const FRigVMGraphFunctionIdentifier& Identifier)
|
|
{
|
|
const FRigVMFunctionReferenceArray* References = FindFunctionReferences(Identifier);
|
|
return References != nullptr && References->Num() > 0;
|
|
});
|
|
#else
|
|
// outside of editor we can't do much - just get the keys
|
|
// of the used references array
|
|
GraphFunctionReferences.GenerateKeyArray(Identifiers);
|
|
#endif
|
|
return Identifiers;
|
|
}
|
|
|
|
FRigVMFunctionReferenceArray URigVMBuildData::GetAllFunctionReferences() const
|
|
{
|
|
FRigVMFunctionReferenceArray FunctionReferences;
|
|
for (const TTuple<FRigVMGraphFunctionIdentifier, FRigVMFunctionReferenceArray>& FunctionReferenceInfo : GraphFunctionReferences)
|
|
{
|
|
FunctionReferences.FunctionReferences.Append(FunctionReferenceInfo.Value.FunctionReferences);
|
|
}
|
|
return FunctionReferences;
|
|
}
|
|
|
|
|
|
|