Hi,
I’m trying to import an FBX file from a Python script, but no matter how hard I try, it always ends up as a static mesh.
If I set the automated property to False to enable the GUI, I can make it a Skeletal Mesh, but that means I can’t automate it.
I dont understand the reason why does the behavior of the import change so much just with the automated property.
below is my python code.
import unreal
def import_fbx_and_create_blueprint(fbx_file_path, blueprint_name, wheel_bone_name):
# Unreal Editor API
editor_asset_lib = unreal.EditorAssetLibrary()
asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
# Import FBX File with Skeletal Mesh enabled
fbx_import_task = unreal.AssetImportTask()
fbx_import_task.filename = fbx_file_path
fbx_import_task.destination_path = "/Game/ImportedFBX"
fbx_import_task.replace_existing = True
fbx_import_task.automated = True
# Configure FbxImportUI
import_options = unreal.FbxImportUI()
# Explicitly set to import as Skeletal Mesh
import_options.mesh_type_to_import = unreal.FBXImportType.FBXIT_SKELETAL_MESH
import_options.original_import_type = unreal.FBXImportType.FBXIT_SKELETAL_MESH
import_options.import_mesh = True
import_options.import_as_skeletal = True # Crucial setting for Skeletal Mesh
import_options.create_physics_asset = True # Correct placement for create_physics_asset
# Skeletal Mesh settings
import_options.skeletal_mesh_import_data.import_mesh_lo_ds = False
# Static Mesh-specific settings (disabled to avoid default to Static Mesh)
import_options.static_mesh_import_data.combine_meshes = False
# Assign options to the task
fbx_import_task.options = import_options
fbx_import_task.save = True
# Execute import task
unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([fbx_import_task])
# Find the imported skeletal mesh
skeletal_mesh_path = f"{fbx_import_task.destination_path}/{fbx_file_path.split('/')[-1].replace('.fbx', '')}"
skeletal_mesh = editor_asset_lib.load_asset(skeletal_mesh_path)
if not skeletal_mesh:
unreal.log_error(f"Failed to load Skeletal Mesh from {skeletal_mesh_path}")
return
# Create Blueprint
bp_factory = unreal.BlueprintFactory()
blueprint = asset_tools.create_asset(blueprint_name, "/Game", unreal.Blueprint, bp_factory)
# Add SkeletalMeshComponent to Blueprint
unreal.BlueprintEditorLibrary.add_component(
blueprint,
"SkeletalMeshComponent",
skeletal_mesh
)
# Add Variables
unreal.BlueprintEditorLibrary.add_new_variable(
blueprint, "WheelBoneName", unreal.NameProperty
)
unreal.BlueprintEditorLibrary.add_new_variable(
blueprint, "RotationSpeed", unreal.FloatProperty
)
# Save Blueprint
unreal.EditorAssetLibrary.save_loaded_asset(blueprint)
# Place Blueprint in the world at the origin
bp_actor = unreal.EditorLevelLibrary.spawn_actor_from_class(
blueprint.GeneratedClass,
location=unreal.Vector(0.0, 0.0, 0.0), # World origin
rotation=unreal.Rotator(0.0, 0.0, 0.0)
)
print(f"Blueprint {blueprint_name} created and placed in the world origin.")
# Example usage
import_fbx_and_create_blueprint(
"/home/ue4/work/RobotUE5App/Content/output.fbx", # Path to the FBX file
"BP_WheelController", # Name of the Blueprint
"right_wheel_joint_continuous_bone" # Name of the wheel bone
)
Thank you in advance.
I solved this problem.
It seems that the default options for the GUI and the default constructor are different.
My final script is below.
import unreal
def import_fbx_and_create_blueprint(fbx_file_path, blueprint_name, wheel_bone_name):
# Unreal Editor API
editor_asset_lib = unreal.EditorAssetLibrary()
asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
# Determine destination paths
destination_path = "/Game/ImportedFBX"
fbx_file_name = fbx_file_path.split('/')[-1].replace('.fbx', '')
skeletal_mesh_path = f"{destination_path}/{fbx_file_name}"
physics_asset_path = f"{destination_path}/{fbx_file_name}_PhysicsAsset"
# Check for and delete existing Skeletal Mesh and Physics Asset
if editor_asset_lib.does_asset_exist(skeletal_mesh_path):
unreal.log_warning(f"Skeletal Mesh already exists at {skeletal_mesh_path}, deleting...")
editor_asset_lib.delete_asset(skeletal_mesh_path)
if editor_asset_lib.does_asset_exist(physics_asset_path):
unreal.log_warning(f"Physics Asset already exists at {physics_asset_path}, deleting...")
editor_asset_lib.delete_asset(physics_asset_path)
# Import FBX File with Skeletal Mesh enabled
fbx_import_task = unreal.AssetImportTask()
fbx_import_task.filename = fbx_file_path
fbx_import_task.destination_path = "/Game/ImportedFBX"
fbx_import_task.replace_existing = True
fbx_import_task.automated = True
# Configure FbxImportUI
import_options = unreal.FbxImportUI()
# Explicitly set to import as Skeletal Mesh
import_options.mesh_type_to_import = unreal.FBXImportType.FBXIT_SKELETAL_MESH
import_options.original_import_type = unreal.FBXImportType.FBXIT_SKELETAL_MESH
import_options.import_mesh = True
import_options.import_as_skeletal = True # Crucial setting for Skeletal Mesh
import_options.import_rigid_mesh = True
import_options.import_materials = True
import_options.import_textures = True
import_options.automated_import_should_detect_type = False
import_options.create_physics_asset = True
# Skeletal Mesh settings
import_options.skeletal_mesh_import_data.import_mesh_lo_ds = False
import_options.skeletal_mesh_import_data.set_editor_property("use_t0_as_ref_pose", True)
# Static Mesh-specific settings (disabled to avoid default to Static Mesh)
import_options.static_mesh_import_data.combine_meshes = False
import_options.static_mesh_import_data.auto_generate_collision = True
import_options.static_mesh_import_data.one_convex_hull_per_ucx = False
# Assign options to the task
fbx_import_task.options = import_options
fbx_import_task.save = True
# Execute import task
unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([fbx_import_task])
# Find the imported skeletal mesh
skeletal_mesh = editor_asset_lib.load_asset(skeletal_mesh_path)
if not skeletal_mesh:
unreal.log_error(f"Failed to load Skeletal Mesh from {skeletal_mesh_path}")
return
physics_asset = editor_asset_lib.load_asset(physics_asset_path)
physics_asset_data = editor_asset_lib.find_asset_data(physics_asset_path)
if not physics_asset or not physics_asset_data:
unreal.log_error(f"Failed to create PhysicsAsset for {skeletal_mesh.get_name()}")
return
# Delete existing Blueprint
blueprint_path = "/Game/" + blueprint_name
if editor_asset_lib.does_asset_exist(blueprint_path):
success = editor_asset_lib.delete_asset(blueprint_path)
if success:
print(f"Successfully deleted existing Blueprint: {blueprint_path}")
else:
unreal.log_error(f"Failed to delete existing Blueprint: {blueprint_path}")
return
else:
print(f"No existing Blueprint found at {blueprint_path}, skipping deletion.")
# Create Blueprint
bp_factory = unreal.BlueprintFactory()
bp_factory.set_editor_property("parent_class", unreal.Actor)
blueprint = asset_tools.create_asset(blueprint_name, "/Game", unreal.Blueprint, bp_factory)
blueprint_generated_class = unreal.BlueprintEditorLibrary.generated_class(blueprint)
blueprint_default_object = blueprint_generated_class.get_default_object()
subsystem = unreal.get_engine_subsystem(unreal.SubobjectDataSubsystem)
handle = subsystem.k2_gather_subobject_data_for_blueprint(blueprint)[1]
skeletal_mesh_handle, fail_reason = subsystem.add_new_subobject(
unreal.AddNewSubobjectParams(
parent_handle=handle,
new_class=unreal.SkeletalMeshComponent,
blueprint_context=blueprint
)
)
# Set Skeletal Mesh
skeletal_mesh_data = unreal.SubobjectDataBlueprintFunctionLibrary.get_data(skeletal_mesh_handle)
skeletal_mesh_component = unreal.SubobjectDataBlueprintFunctionLibrary.get_object(skeletal_mesh_data)
skeletal_mesh_component.set_editor_property("skeletal_mesh", skeletal_mesh)
# Apply Convex Decomposition Collision
skeletal_mesh_component.set_all_bodies_simulate_physics(True)
# Add custom C++ component under SkeletalMeshComponent
custom_component_handle, _ = subsystem.add_new_subobject(
unreal.AddNewSubobjectParams(
parent_handle=handle,
new_class=unreal.RRBaseRobot,
blueprint_context=blueprint
)
)
# (Optional) Set properties on the custom component
#custom_component_data = unreal.SubobjectDataBlueprintFunctionLibrary.get_data(custom_component_handle)
#if not custom_component_data:
# unreal.log_error("component data is None")
#custom_component = unreal.SubobjectDataBlueprintFunctionLibrary.get_object(custom_component_data)
#custom_object = unreal.RRBaseRobot()
#if not custom_component:
# unreal.log_error("component is None")
#custom_component.set_editor_property("property_name", custom_object)
# Save Blueprint
unreal.EditorAssetLibrary.save_loaded_asset(blueprint)
# Place Blueprint in the world at the origin
bp_actor = unreal.EditorLevelLibrary.spawn_actor_from_class(
blueprint_generated_class,
location=unreal.Vector(0.0, 0.0, 0.0), # World origin
rotation=unreal.Rotator(0.0, 0.0, 0.0)
)
print(f"Blueprint {blueprint_name} created and placed in the world origin.")
# Example usage
import_fbx_and_create_blueprint(
"FBX_PATH", # Path to the FBX file
"BP_ROBOT_NAME", # Name of the Blueprint
"right_wheel_joint_continuous_bone" # Name of the wheel bone
)
But it has the another problem.
I will post another thread.