2020-12-14 15:48:27 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "MetasoundUObjectRegistry.h"
# include "Algo/Copy.h"
2021-06-16 11:21:13 -04:00
# include "AssetData.h"
2020-12-14 15:48:27 -04:00
# include "AssetRegistry/AssetRegistryModule.h"
2020-12-26 19:37:43 -04:00
# include "Async/Async.h"
2021-01-13 10:48:59 -04:00
# include "Async/TaskGraphInterfaces.h"
2020-12-14 15:48:27 -04:00
# include "CoreMinimal.h"
2021-06-16 11:21:13 -04:00
# include "Engine/AssetManager.h"
# include "Metasound.h"
# include "MetasoundAssetBase.h"
# include "MetasoundFrontendRegistries.h"
# include "MetasoundFrontendTransform.h"
# include "MetasoundSource.h"
2020-12-14 15:48:27 -04:00
# include "UObject/Object.h"
2021-06-16 11:21:13 -04:00
2020-12-14 15:48:27 -04:00
namespace Metasound
{
2021-06-16 11:21:13 -04:00
namespace AssetSubsystemPrivate
{
static bool GetAssetClassInfo ( const FAssetData & InAssetData , Frontend : : FNodeClassInfo & OutInfo )
{
using namespace Metasound ;
using namespace Metasound : : Frontend ;
bool bSuccess = true ;
OutInfo . Type = EMetasoundFrontendClassType : : External ;
OutInfo . AssetPath = InAssetData . ObjectPath ;
FString AssetClassID ;
bSuccess & = InAssetData . GetTagValue ( AssetTags : : AssetClassID , AssetClassID ) ;
OutInfo . AssetClassID = FGuid ( AssetClassID ) ;
OutInfo . ClassName = FMetasoundFrontendClassName ( FName ( ) , * AssetClassID , FName ( ) ) ;
int32 RegistryVersionMajor = 0 ;
bSuccess & = InAssetData . GetTagValue ( AssetTags : : RegistryVersionMajor , RegistryVersionMajor ) ;
OutInfo . Version . Major = RegistryVersionMajor ;
int32 RegistryVersionMinor = 0 ;
bSuccess & = InAssetData . GetTagValue ( AssetTags : : RegistryVersionMinor , RegistryVersionMinor ) ;
OutInfo . Version . Minor = RegistryVersionMinor ;
# if WITH_EDITORONLY_DATA
auto ParseTypesString = [ & ] ( const FName AssetTag , TArray < FName > & OutTypes )
{
FString TypesString ;
if ( InAssetData . GetTagValue ( AssetTag , TypesString ) )
{
TArray < FString > DataTypeStrings ;
TypesString . ParseIntoArray ( DataTypeStrings , * AssetTags : : ArrayDelim ) ;
Algo : : Transform ( DataTypeStrings , OutTypes , [ ] ( const FString & DataType ) { return * DataType ; } ) ;
return true ;
}
return false ;
} ;
OutInfo . InputTypes . Reset ( ) ;
bSuccess & = ParseTypesString ( AssetTags : : RegistryInputTypes , OutInfo . InputTypes ) ;
OutInfo . OutputTypes . Reset ( ) ;
bSuccess & = ParseTypesString ( AssetTags : : RegistryOutputTypes , OutInfo . OutputTypes ) ;
# endif // WITH_EDITORONLY_DATA
return bSuccess ;
}
}
}
void UMetaSoundAssetSubsystem : : Initialize ( FSubsystemCollectionBase & InCollection )
{
// TODO: Enable Composition
// FCoreDelegates::OnPostEngineInit.AddUObject(this, &UMetaSoundAssetSubsystem::PostEngineInit);
}
void UMetaSoundAssetSubsystem : : PostEngineInit ( )
{
if ( UAssetManager * AssetManager = UAssetManager : : GetIfValid ( ) )
{
AssetManager - > CallOrRegister_OnCompletedInitialScan ( FSimpleMulticastDelegate : : FDelegate : : CreateUObject ( this , & UMetaSoundAssetSubsystem : : PostInitAssetScan ) ) ;
}
else
{
UE_LOG ( LogMetaSound , Error , TEXT ( " Cannot initialize MetaSoundAssetSubsystem: Enable AssetManager or disable MetaSound plugin " ) ) ;
}
}
void UMetaSoundAssetSubsystem : : PostInitAssetScan ( )
{
UAssetManager & AssetManager = UAssetManager : : Get ( ) ;
FAssetManagerSearchRules Rules ;
Rules . AssetScanPaths . Add ( TEXT ( " /Game " ) ) ;
Rules . AssetBaseClass = UMetaSound : : StaticClass ( ) ;
TArray < FAssetData > MetaSoundAssets ;
AssetManager . SearchAssetRegistryPaths ( MetaSoundAssets , Rules ) ;
for ( const FAssetData & AssetData : MetaSoundAssets )
{
AddOrUpdateAsset ( AssetData ) ;
}
Rules . AssetBaseClass = UMetaSoundSource : : StaticClass ( ) ;
TArray < FAssetData > MetaSoundSourceAssets ;
AssetManager . SearchAssetRegistryPaths ( MetaSoundSourceAssets , Rules ) ;
for ( const FAssetData & AssetData : MetaSoundSourceAssets )
{
AddOrUpdateAsset ( AssetData ) ;
}
}
void UMetaSoundAssetSubsystem : : Deinitialize ( )
{
}
void UMetaSoundAssetSubsystem : : AddOrUpdateAsset ( const FAssetData & InAssetData )
{
using namespace Metasound ;
using namespace Metasound : : AssetSubsystemPrivate ;
using namespace Metasound : : Frontend ;
2021-06-16 11:34:06 -04:00
// TODO: Set to false for builds once registering without loading asset is supported.
2021-06-16 11:21:13 -04:00
static const bool bLoadRequiredToRegisterAssetClasses = true ;
FNodeClassInfo ClassInfo ;
bool bClassInfoFound = GetAssetClassInfo ( InAssetData , ClassInfo ) ;
if ( ! bClassInfoFound | | bLoadRequiredToRegisterAssetClasses )
{
UObject * Object = nullptr ;
FSoftObjectPath Path ( InAssetData . ObjectPath ) ;
if ( InAssetData . IsAssetLoaded ( ) )
{
Object = Path . ResolveObject ( ) ;
}
else
{
if ( ! bLoadRequiredToRegisterAssetClasses )
{
UE_LOG ( LogMetaSound , Warning ,
TEXT ( " Failed to find serialized MetaSound asset registry data for asset '%s'. "
" Forcing synchronous load which increases load times. Re-save asset to avoid this. " ) ,
* InAssetData . ObjectPath . ToString ( ) ) ;
}
Object = Path . TryLoad ( ) ;
}
if ( ensure ( Object ) )
{
FMetasoundAssetBase * MetaSoundAsset = Metasound : : IMetasoundUObjectRegistry : : Get ( ) . GetObjectAsAssetBase ( Object ) ;
check ( MetaSoundAsset ) ;
FDocumentHandle Document = MetaSoundAsset - > GetDocumentHandle ( ) ;
// Must version to ensure registration uses the correct key,
// which must be based off of most up-to-date document model
// for safety.
const FName AssetName = Object - > GetFName ( ) ;
const FString AssetPath = Object - > GetPathName ( ) ;
FVersionDocument ( AssetName , AssetPath ) . Transform ( Document ) ;
MetaSoundAsset - > RegisterGraphWithFrontend ( ) ;
}
}
}
void UMetaSoundAssetSubsystem : : RemoveAsset ( const FAssetData & InAssetData )
{
using namespace Metasound ;
using namespace Metasound : : AssetSubsystemPrivate ;
using namespace Metasound : : Frontend ;
FNodeClassInfo ClassInfo ;
if ( ! GetAssetClassInfo ( InAssetData , ClassInfo ) )
{
UObject * Object = nullptr ;
FSoftObjectPath Path ( InAssetData . ObjectPath ) ;
if ( InAssetData . IsAssetLoaded ( ) )
{
Object = Path . ResolveObject ( ) ;
}
else
{
Object = Path . TryLoad ( ) ;
}
if ( ensure ( Object ) )
{
FMetasoundAssetBase * MetaSoundAsset = Metasound : : IMetasoundUObjectRegistry : : Get ( ) . GetObjectAsAssetBase ( Object ) ;
check ( MetaSoundAsset ) ;
FDocumentHandle Document = MetaSoundAsset - > GetDocumentHandle ( ) ;
// Must version to ensure registration uses the correct key,
// which must be based off of most up-to-date document model
// for safety.
const FName AssetName = Object - > GetFName ( ) ;
const FString AssetPath = Object - > GetPathName ( ) ;
FVersionDocument ( AssetName , AssetPath ) . Transform ( Document ) ;
ClassInfo = MetaSoundAsset - > GetAssetClassInfo ( ) ;
}
}
const FNodeRegistryKey RegistryKey = FMetasoundFrontendRegistryContainer : : Get ( ) - > GetRegistryKey ( ClassInfo ) ;
FMetasoundFrontendRegistryContainer : : Get ( ) - > UnregisterNode ( RegistryKey ) ;
}
namespace Metasound
{
2020-12-14 15:48:27 -04:00
class FMetasoundUObjectRegistry : public IMetasoundUObjectRegistry
{
public :
void RegisterUClassArchetype ( TUniquePtr < IMetasoundUObjectRegistryEntry > & & InEntry ) override
{
if ( InEntry . IsValid ( ) )
{
FName ArchetypeName = InEntry - > GetArchetypeName ( ) ;
EntriesByArchetype . Add ( ArchetypeName , InEntry . Get ( ) ) ;
Entries . Add ( InEntry . Get ( ) ) ;
Storage . Add ( MoveTemp ( InEntry ) ) ;
}
}
TArray < UClass * > GetUClassesForArchetype ( const FName & InArchetypeName ) const override
{
TArray < UClass * > Classes ;
TArray < const IMetasoundUObjectRegistryEntry * > EntriesForArchetype ;
EntriesByArchetype . MultiFind ( InArchetypeName , EntriesForArchetype ) ;
for ( const IMetasoundUObjectRegistryEntry * Entry : EntriesForArchetype )
{
if ( nullptr ! = Entry )
{
if ( UClass * Class = Entry - > GetUClass ( ) )
{
Classes . Add ( Class ) ;
}
}
}
return Classes ;
}
2021-06-08 10:52:31 -04:00
UObject * NewObject ( UClass * InClass , const FMetasoundFrontendDocument & InDocument , const FMetasoundFrontendArchetype & InArchetype , const FString & InPath ) const override
2020-12-14 15:48:27 -04:00
{
auto IsChildClassOfRegisteredClass = [ & ] ( const IMetasoundUObjectRegistryEntry * Entry )
{
return Entry - > IsChildClass ( InClass ) ;
} ;
TArray < const IMetasoundUObjectRegistryEntry * > EntriesForClass = FindEntriesByPredicate ( IsChildClassOfRegisteredClass ) ;
for ( const IMetasoundUObjectRegistryEntry * Entry : EntriesForClass )
{
2021-06-08 10:52:31 -04:00
if ( Entry - > GetArchetypeName ( ) = = InArchetype . Name )
2020-12-14 15:48:27 -04:00
{
return NewObject ( * Entry , InDocument , InPath ) ;
}
}
return nullptr ;
}
bool IsRegisteredClass ( UObject * InObject ) const override
{
return ( nullptr ! = GetEntryByUObject ( InObject ) ) ;
}
FMetasoundAssetBase * GetObjectAsAssetBase ( UObject * InObject ) const override
{
if ( const IMetasoundUObjectRegistryEntry * Entry = GetEntryByUObject ( InObject ) )
{
return Entry - > Cast ( InObject ) ;
}
return nullptr ;
}
const FMetasoundAssetBase * GetObjectAsAssetBase ( const UObject * InObject ) const override
{
if ( const IMetasoundUObjectRegistryEntry * Entry = GetEntryByUObject ( InObject ) )
{
return Entry - > Cast ( InObject ) ;
}
return nullptr ;
}
private :
2021-01-13 10:48:59 -04:00
UObject * NewObject ( const IMetasoundUObjectRegistryEntry & InEntry , const FMetasoundFrontendDocument & InDocument , const FString & InPath ) const
2020-12-14 15:48:27 -04:00
{
UPackage * PackageToSaveTo = nullptr ;
if ( GIsEditor )
{
FText InvalidPathReason ;
bool const bValidPackageName = FPackageName : : IsValidLongPackageName ( InPath , false , & InvalidPathReason ) ;
if ( ! ensureAlwaysMsgf ( bValidPackageName , TEXT ( " Tried to generate a Metasound UObject with an invalid package path/name Falling back to transient package, which means we won't be able to save this asset. " ) ) )
{
PackageToSaveTo = GetTransientPackage ( ) ;
}
else
{
PackageToSaveTo = CreatePackage ( * InPath ) ;
}
}
else
{
PackageToSaveTo = GetTransientPackage ( ) ;
}
2021-01-28 19:02:51 -04:00
UObject * NewMetasoundObject = InEntry . NewObject ( PackageToSaveTo , * InDocument . RootGraph . Metadata . ClassName . GetFullName ( ) . ToString ( ) ) ;
2020-12-14 15:48:27 -04:00
FMetasoundAssetBase * NewAssetBase = InEntry . Cast ( NewMetasoundObject ) ;
if ( ensure ( nullptr ! = NewAssetBase ) )
{
NewAssetBase - > SetDocument ( InDocument ) ;
2021-06-08 10:52:31 -04:00
const FMetasoundFrontendArchetype & Archetype = NewAssetBase - > GetArchetype ( ) ;
if ( ensure ( NewAssetBase - > IsArchetypeSupported ( Archetype ) ) )
{
NewAssetBase - > ConformDocumentToArchetype ( ) ;
}
2020-12-14 15:48:27 -04:00
}
# if WITH_EDITOR
AsyncTask ( ENamedThreads : : GameThread , [ NewMetasoundObject ] ( )
{
FAssetRegistryModule : : AssetCreated ( NewMetasoundObject ) ;
NewMetasoundObject - > MarkPackageDirty ( ) ;
// todo: how do you get the package for a uobject and save it? I forget
} ) ;
# endif
return NewMetasoundObject ;
}
const IMetasoundUObjectRegistryEntry * FindEntryByPredicate ( TFunction < bool ( const IMetasoundUObjectRegistryEntry * ) > InPredicate ) const
{
const IMetasoundUObjectRegistryEntry * const * Entry = Entries . FindByPredicate ( InPredicate ) ;
if ( nullptr = = Entry )
{
return nullptr ;
}
return * Entry ;
}
TArray < const IMetasoundUObjectRegistryEntry * > FindEntriesByPredicate ( TFunction < bool ( const IMetasoundUObjectRegistryEntry * ) > InPredicate ) const
{
TArray < const IMetasoundUObjectRegistryEntry * > FoundEntries ;
Algo : : CopyIf ( Entries , FoundEntries , InPredicate ) ;
return FoundEntries ;
}
const IMetasoundUObjectRegistryEntry * GetEntryByUObject ( const UObject * InObject ) const
{
auto IsChildClassOfRegisteredClass = [ & ] ( const IMetasoundUObjectRegistryEntry * Entry )
{
if ( nullptr = = Entry )
{
return false ;
}
return Entry - > IsChildClass ( InObject ) ;
} ;
return FindEntryByPredicate ( IsChildClassOfRegisteredClass ) ;
}
TArray < TUniquePtr < IMetasoundUObjectRegistryEntry > > Storage ;
TMultiMap < FName , const IMetasoundUObjectRegistryEntry * > EntriesByArchetype ;
TArray < const IMetasoundUObjectRegistryEntry * > Entries ;
} ;
IMetasoundUObjectRegistry & IMetasoundUObjectRegistry : : Get ( )
{
static FMetasoundUObjectRegistry Registry ;
return Registry ;
}
}