Can't save Level when using UsdAssetUserData

Hi there,

Unreal Editor version 5.6.1 can’t save the level when importing a USD with “Metadata Options -> Collect on Components” enabled, then converting an imported Actor to a Blueprint.

Please let us know whether this issue can be resolved and if there is a temporary workaround we can use in the meantime.

Thanks,

Yurii

Steps to Reproduce

  1. In a new UE project, use “File -> Import Into Level…” and select a USD document (e.g. Kitchen_set).
  2. When the USD Import Options window pops up, make sure that “Metadata Options -> Collect on Components” is ticked.
  3. Import the document into the level.
  4. Select any resulting Actor in the hierarchy that contains a component with a UsdAssetUserData object in the Asset User Data property.
  5. Convert the actor to a Blueprint (e.g. using the button “Convert this actor into a reusable Blueprint Class that can have script behaviour” in the Details panel).
  6. Try to save the level; it will fail. In the Output Log, you will see an error like:
Warning: Referencers of UsdAssetUserData /Game/Kitchen_1_Blueprint.Kitchen_1_Blueprint_C:Kitchen_0_GEN_VARIABLE.UsdAssetUserData:
LogSavePackage: Warning: StaticMeshComponent /Game/Kitchen_1_Blueprint.Kitchen_1_Blueprint_C:Kitchen_0_GEN_VARIABLE (1 refs)
LogSavePackage: Warning: 0) ObjectProperty /Script/Engine.ActorComponent: AssetUserData.AssetUserData
Warning: Can't save '../../../../../../TestUsdAssetUserData/Content/_ExternalActors_/NewMap1/3/GX/TV26G8O538EKHANW1V3BYG.uasset': Illegal reference to private object: 'UsdAssetUserData /Game/Kitchen_1_Blueprint.Kitchen_1_Blueprint_C:Kitchen_0_GEN_VARIABLE.UsdAssetUserData' referenced by 'Kitchen_0_GEN_VARIABLE' (at '/Game/Kitchen_1_Blueprint.Kitchen_1_Blueprint_C') in its 'AssetUserData' property. 

I found a workaround for the issue, but I’m not sure if this would be a proper fix for the underlying issue. When, after converting actors to a Blueprint, I re-create `UUsdAssetUserData` in the components using the Blueprint object as an Outer, but not the component object, this fixes the level save error.

Below, I’m sharing a code snippet that fixes the issue. Please let me know if this approach looks valid for you.

void RefineComponents(
	USCS_Node* Node,
	UBlueprint* Blueprint,
	UBlueprintGeneratedClass* BPGC
)
{
	UActorComponent* TemplateComp = Node->GetActualComponentTemplate(BPGC);
	if (!TemplateComp)
	{
		return;
	}
 
	TemplateComp->Modify();
 
	if (TSubclassOf<UUsdAssetUserData> AssetUserDataClass =
		UsdUnreal::ObjectUtils::GetAssetUserDataClassForObject(TemplateComp->GetClass()))
	{
		if (UUsdAssetUserData* OldUserData =
			UsdUnreal::ObjectUtils::GetAssetUserData(TemplateComp, AssetUserDataClass))
		{
			// Clone the UserData to avoid update bugs on the original object
			TemplateComp->Modify();
 
			//UObject* Outer = TemplateComp; // NOTE: this will make the save level to fail
			Object* Outer = Blueprint;                  // NOTE: this will fix the issue
			UUsdAssetUserData* NewUserData = NewObject<UUsdAssetUserData>(
				Outer, AssetUserDataClass, NAME_None, RF_Transactional);
 
			// Copy content
			NewUserData->StageIdentifierToMetadata = OldUserData->StageIdentifierToMetadata;
 
			// Remove old data, add new one
			TemplateComp->RemoveUserDataOfClass(AssetUserDataClass);
			TemplateComp->AddAssetUserData(NewUserData);
 
			TemplateComp->MarkPackageDirty();
		}
	}
 
	for (const TObjectPtr<class USCS_Node>& Child : Node->ChildNodes)
	{
		RefineComponents(Child, Blueprint, BPGC);
	}
}
 
// ...
 
FString BlueprintPath = // TODO: specify
TArray<AActor*> SelectedActors; // TODO: populate
 
// ...
 
// Convert selected Actors to a Blueprint
FKismetEditorUtilities::FHarvestBlueprintFromActorsParams Params;
Params.bReplaceActors = true;
Params.bOpenBlueprint = false;
Params.ParentClass = AActor::StaticClass();
UBlueprint* Blueprint = FKismetEditorUtilities::HarvestBlueprintFromActors(BlueprintPath, SelectedActors, Params);
 
// Apply the bug workaround
UBlueprintGeneratedClass* BPGC = Cast<UBlueprintGeneratedClass>(Blueprint->GeneratedClass);
const TArray<USCS_Node*>& Roots = Blueprint->SimpleConstructionScript->GetRootNodes();
RefineComponents(Roots[0], Blueprint, BPGC);
 
// Mark as modified & compile
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
FKismetEditorUtilities::CompileBlueprint(Blueprint);
UEditorAssetLibrary::SaveLoadedAsset(Blueprint, false);

Unfortunately, the proposed workaround doesn’t satisfy the workflow we are trying to implement. Apart from converting the imported USD Actors into a Blueprint, we also want to be able to duplicate the Blueprint. Currently, it creates a duplicate, but it can’t save it afterwards.

I attached a BlueprintFunctionLibrary that you can use to reproduce the duplication error. The steps will be the following:

  1. Create a new UE project.
  2. Enable plugins: USD Core, USD Importer.
  3. Add the attached Blueprint Library Function into the project. Compile and run it.
  4. In a fresh level, do “File -> Import Into Level…” and import a USD document (e.g. Kitchen_set)
  5. When imported, select any Actor in the Outliner containing a component with a `UsdAssetUserData` object.
  6. Run the `ConvertSelectedActorsToBlueprint()` utility function (e.g. by executing the Python command `unreal.MyBlueprintFunctionLibrary.convert_selected_actors_to_blueprint()`

Result:

The utility function will create a Blueprint out of the selected Actor. Also, it will create a Blueprint duplicate, but it will fail to save the duplicate. In the Output Log, I see the following error:

LogSavePackage: Warning: Referencers of UsdAssetUserData /Game/Test/MyBP.MyBP:UsdAssetUserData_0:
 LogSavePackage: Warning:   SceneComponent /Game/Test/MyBP_Duplicate.MyBP_Duplicate_C:Kitchen_set_0_GEN_VARIABLE (1 refs)
 LogSavePackage: Warning:     0) ObjectProperty /Script/Engine.ActorComponent:AssetUserData.AssetUserData
 Warning: Can't save '../../../../../../Sandbox/UsdAssetUserDataBug/Content/Test/MyBP_Duplicate.uasset': Illegal reference to private object: 'UsdAssetUserData /Game/Test/MyBP.MyBP:UsdAssetUserData_0' referenced by 'Kitchen_set_0_GEN_VARIABLE' (at '/Game/Test/MyBP_Duplicate.MyBP_Duplicate_C') in its 'AssetUserData' property.
 

If you face any difficulties reproducing the issue, please let me know.

Thanks,

Yurii

Thank YYurii,

We are looking into this and will get back with you on it as soon as possible.

Hi Yurii,

I think your code snippet is almost correct. When you create the NewUserData, if you pass RF_Transactional | RF_Public, you should be able to save your level as well as duplicate your Blueprint.

Thanks for reporting the issue. We’ll do a fix so you won’t need to do a workaround cloning the user data.

Regards,

Anousack

Hi Anousack,

I tested the `RF_Transactional | RF_Public` flag, and it worked. Many thanks for the help!

- Yurii