Centralized resolving of the FAssetData associated with each Actor

One of the core actions that users make is drag dropping an item from the content browser into the world. This means there is very often a relationship between an actor in the world and the asset it was created from. For us, this relationship is really important as it allows us to externalize what the actor was created from and link that with another external file. For example:

- FAssetData TryGetAssetFromActor(AActor* Actor);

- FString GetProductURIFromAsset(const FAssetData& AssetData);

Unreal tracks these relationships between Assets and Actors but we couldn’t find any centralized exposure of them. You can find one use case here:void UEditorEngine::GetAssetsToSyncToContentBrowser(TArray<FAssetData>& Assets, bool bAllowBrowseToAssetOverride)But the above function is very tied to UI and not something we can call generically with just an Actor reference.

Is there a specific place where this data is managed?

Hello [mention removed]​

Thank you for reaching out.

You haven’t specified an engine version, so I’ll take 5.5.4 as a reference.

Have you checked the official documentation for Asset Registry?

This small snippet shows how to fetch the Assets associated with a class:

FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry"); TArray<FAssetData> AssetData; const UClass* Class = UStaticMesh::StaticClass(); AssetRegistryModule.Get().GetAssetsByClass(Class->GetFName(), AssetData);I’ve also checked the GetAssetsToSyncToContentBrowser function you shared, and that led me to \UnrealEngine\Engine\Source\Runtime\CoreUObject\Private\AssetRegistry\AssetData.cpp, L356:

`FAssetData::FAssetData(const UObject* InAsset, FAssetData::ECreationFlags InCreationFlags, EAssetRegistryTagsCaller Caller)
{
if (InAsset != nullptr)
{
#if WITH_EDITORONLY_DATA
// ClassGeneratedBy TODO: This may be wrong in cooked builds
const UClass* InClass = Cast(InAsset);
if (InClass && InClass->ClassGeneratedBy && !EnumHasAnyFlags(InCreationFlags, FAssetData::ECreationFlags::AllowBlueprintClass))
{
// For Blueprints, the AssetData refers to the UBlueprint and not the UBlueprintGeneratedClass
InAsset = InClass->ClassGeneratedBy;
}
#endif

const UPackage* Package = InAsset->GetPackage();

PackageName = Package->GetFName();
PackagePath = FName(*FPackageName::GetLongPackagePath(Package->GetName()));
AssetName = InAsset->GetFName();
AssetClassPath = InAsset->GetClass()->GetPathName();
#if WITH_EDITORONLY_DATA
PRAGMA_DISABLE_DEPRECATION_WARNINGS
ObjectPath = FName(*InAsset->GetPathName());
PRAGMA_ENABLE_DEPRECATION_WARNINGS
if (InAsset->GetOuter() != Package)
{
OptionalOuterPath = *InAsset->GetOuter()->GetPathName();
}
#endif

if (!EnumHasAnyFlags(InCreationFlags, FAssetData::ECreationFlags::SkipAssetRegistryTagsGathering))
{
FAssetRegistryTagsContextData Context(InAsset, Caller);
InAsset->GetAssetRegistryTags(Context, *this);
}

PackageFlags = Package->GetPackageFlags();
SetChunkIDs(Package->GetChunkIDs());
}
}`Throughout the engine code, calling FAssetData(Actor) looks like the preferred method to get the FAssetData associated with a UObject.

Let me know if this is helpful.

All the best,

[mention removed]​

Hi Rafael,

Thanks for your response and sorry for my delay. I think I failed to provide proper context in my original post. Unfortunately, I’m looking to get the FAssetData associated with the asset that was used the the Actor Factor that created the actor, not the default constructed FAssetData that results in using the function you posted above.

The default behavior of constructing a FAssetData(UObject*) will return either:

  • The package of that object
  • The BP that was used to generate that object (this is closer to what I’m looking for but it only handles blueprints)

So if we use a AStaticMeshActor as an example and you use FAssetData(AActor* SomeActor), you will result in either the map’s package or the actor’s external package. Neither will point to the StaticMesh. Further more, the FAssetData constructed is not retrieved through the asset registry but instead constructed, which means that it will not contain any metadata.

In our scenario, we are saving database metadata on each of our static mesh assets (and other assets), then when the actor factories create those assets, we want to be able to consistently trace back to that FAssetData so we can pull the database metadata again.

So currently, what we have to do to solve this is a mix of logic that contains:

  • A check for the BrowseToAsset override (in case the asset relationship has been modified)
  • A Check for the generated BP class (same as what what the default FAssetData constructor does).
  • Finally, a call to GetReferencedContentObjects:

`/**

  • Used by the “Sync to Content Browser” right-click menu option in the editor.
  • @param Objects Array to add content object references to.
  • @return Whether the object references content (all overrides of this function should return true)
    /
    ENGINE_API virtual bool GetReferencedContentObjects( TArray<UObject
    >& Objects ) const;`

This works, although it seems like there are even more systems within the engine for relating an object within the world to the assets associated with it. For example, the IComponentAssetBroker interface:

`/**

  • This class knows how to get or set the asset on a particular kind of actor component
  • One asset type can be associated with multiple component types, but any given component
  • type only understands how to be created from a single asset type (for now)
    /
    class IComponentAssetBroker
    {
    public:
    /
    * Reports the component class this broker knows how to handle */
    //virtual TSubclassOf GetSupportedComponentClass()=0;

/** Reports the asset class this broker knows how to handle /
virtual UClass
GetSupportedAssetClass()=0;

/** Assign the assigned asset to the supplied component /
virtual bool AssignAssetToComponent(UActorComponent
InComponent, UObject* InAsset)=0;

/** Get the currently assigned asset from the component /
virtual UObject
GetAssetFromComponent(UActorComponent* InComponent)=0;

virtual ~IComponentAssetBroker() {}
};`

I’m hoping there could be a centralized method for using all these systems in the appropriate order. Allowing pipelines to integrate directly into UE’s existing mechanics for associating UObject’s to their primary Assets. This could extend beyond actors and components (anim and subscene sections within sequencer).

All the best,

Adam

Hello [mention removed]​

I apologize for the delayed response.

I’ve been searching for the best option regarding this, but I’m not deeply familiar with these systems.

I’ll escalate the case to Epic.

Hopefully, a subject matter expert or the developers can provide a more definitive answer regarding this.

Thank you for your understanding.

All the best,

[mention removed]​

Hi Adam,

I’ll start by just clarifying a few concepts. Unreal doesn’t keep track of what asset created an actor, but it does indeed at the package level have a system that keep track of the dependencies of the package (file) as it is on disk. Search the engine code base for usage of the asset registry with those function GetReferencers and GetDependencies. To get the asset registry implementation you can simply use the static Get on the interface IAssetRegistry. Once you have your dependencies you can simply use the package to query the AR for the asset data(s) associated to these package. This is how the find reference tool work.

Note that only the levels/world that use OFPA (one file per actor) will have a package per actor. The non OFPA world will use only one package for whole the level. For those you could get the referenced objects by serializing the actor with a custom FArchive that capture the object reference that don’t have the actor in their outer chain.

Otherwise the GetAssetsToSyncToContentBrowser use some per actor type logic to determine what asset matter for a specific actor type. If you want the exact result but without being tied to user selection, I would just recommend duplicating the code here and remove the line where it grab the user selection and replace it by your own actor handles. You can get the handle for an actor by using the AcquireEditorObjectElementHandle function of UEngineElementsLibrary and create your own custom selection set.

For more context the editor use handles for the selection and a setup of interfaces to allow the selection in some tools to interact with some element that might not be UObject based. The implementation details there are still evolving at the moment and this is why the setup is not yet documented.

I hope this will help you,

Julien.

Thanks Julien,

I’ll take a look at the UEngineElementsLibrary and see what it offers. For now we are cloning the code found in GetAssetsToSyncToContentBrowser.

All the best,

Adam