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.
In a new UE project, use “File -> Import Into Level…” and select a USD document (e.g. Kitchen_set).
When the USD Import Options window pops up, make sure that “Metadata Options -> Collect on Components” is ticked.
Import the document into the level.
Select any resulting Actor in the hierarchy that contains a component with a UsdAssetUserData object in the Asset User Data property.
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).
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:
Create a new UE project.
Enable plugins: USD Core, USD Importer.
Add the attached Blueprint Library Function into the project. Compile and run it.
In a fresh level, do “File -> Import Into Level…” and import a USD document (e.g. Kitchen_set)
When imported, select any Actor in the Outliner containing a component with a `UsdAssetUserData` object.
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.
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.