Loading GLTF / GLB Assets using Python

Hello,

I am trying to load glb/gtlf in ue5.6 with python scripting in order to render rgb, normals, depth.

Currently i am having issue with the materials and textures not loading correctly, in the rgb renders i see the object’s geometry / static mesh being loaded but with a generic base texture applied to it.

I am not gonna lie I am quite new to the python API so with the help of chatgpt and reading the documentation here : Unreal Python API Documentation — Unreal Python 5.6 (Experimental) documentation i was able to write the following code python script.

The import_gltf_asset allows me to import the assets, i then return the imported_paths that i can use to later load the assets. The merge_imported_static_meshes function basically reads all the static meshes and combines them using the merge merge_static_mesh_actors function i found in the documentation. The issue is that it seems the materials / textures are not being used correctly during the rendering.

Can any experts on python scripting help me out here ? And fyi for the rendering part i simply used the merged actor and using cameraactor, render targets and SceneCaptureSource i am able to render the objects to render the rgb, depth and normals.

To debug the isssue I also added material_path=None i am able to perfectly apply a custom material so the rendering seems to works just fine. It seems i am not able to render with the original materials present in the gltf/glb files.

I suspect i did something wrong while loading the assets or when i am merging the static meshes into a single actor.


def import_gltf_asset(gltf_file_path, destination_path="/Game/Imported"):
    """
    Import assets into project.
    """
    # list of files to import
    fileNames = [gltf_file_path]
    # create asset tools object
    assetTools = unreal.AssetToolsHelpers.get_asset_tools()
    # create asset import data object        
    assetImportData = unreal.AutomatedAssetImportData()
    # set assetImportData attributes
    assetImportData.destination_path = destination_path
    assetImportData.filenames = fileNames
    assetImportData.replace_existing = True
    imported_assets = assetTools.import_assets_automated(assetImportData)
    
    imported_paths = []
    if imported_assets:
        for asset in imported_assets:
            if asset:
                path = asset.get_path_name()
                imported_paths.append(path)
    return None if not imported_paths else imported_paths


def merge_imported_static_meshes(
    imported_paths,
    location=unreal.Vector(0,0,0),
    rotation=unreal.Rotator(0,0,0),
    base_package_name="/Game/Create/SM_Merged",
    merged_actor_label="Merged Actor",
    material_path=None
):
   
    # First, find and compile all materials in imported assets
    find_and_compile_materials_in_imported_assets(imported_paths)
    
    # Step 0: Load and compile override material if specified
    override_material = None
    if material_path:
        override_material = unreal.EditorAssetLibrary.load_asset(material_path)
        if override_material:
            try:
                unreal.MaterialEditingLibrary.recompile_material(override_material)
                unreal.log(f"Loaded and compiled override material: {material_path}")
            except Exception as e:
                unreal.log_warning(f"Could not recompile override material: {e}")
        else:
            unreal.log_warning(f"Could not load override material: {material_path}")
    
    # Step 1: Find all StaticMesh assets and prepare them
    meshes = []
    for path in imported_paths:
        asset = unreal.EditorAssetLibrary.load_asset(path)
        if isinstance(asset, unreal.StaticMesh):
            unreal.log(f"StaticMesh found: {path}")
            
            # Force materials ready for each mesh
            force_materials_ready(asset)
            
            # Only apply override material if specified, otherwise preserve original materials
            if override_material:
                num_sections = asset.get_num_sections(0)  # LOD 0
                for idx in range(num_sections):
                    asset.set_material(idx, override_material)
                unreal.log(f"Applied override material to mesh {path}")
            else:
                unreal.log(f"Preserving original materials for mesh {path}")
                
            meshes.append(asset)
    
    unreal.log(f"Found {len(meshes)} static meshes")
    if not meshes:
        unreal.log_warning("No StaticMesh assets found to merge.")
        return None
 
    # Step 2: Spawn StaticMeshActors in the level
    actors_to_merge = []
    for i, mesh in enumerate(meshes):
        actor = unreal.EditorLevelLibrary.spawn_actor_from_object(mesh, location, rotation)
        
        # Ensure the spawned actor's mesh component has materials properly set
        mesh_comp = actor.get_component_by_class(unreal.StaticMeshComponent)
        if mesh_comp:
            # Force refresh materials on the component
            mesh_comp.set_static_mesh(mesh)
            unreal.log(f"Refreshed materials on spawned actor {i}")
        
        actors_to_merge.append(actor)
 
    if not actors_to_merge:
        unreal.log_warning("No actors spawned to merge.")
        return None
 
    # Step 3: Set up merge options with material preservation
    staticMeshSubsystem = unreal.get_editor_subsystem(unreal.StaticMeshEditorSubsystem)
    merge_options = unreal.MergeStaticMeshActorsOptions()
    merge_options.base_package_name = base_package_name
    merge_options.destroy_source_actors = True
    merge_options.new_actor_label = merged_actor_label
    merge_options.spawn_merged_actor = True
    
    # Enhanced mesh merging settings for better material preservation
    merge_options.mesh_merging_settings.bake_vertex_data_to_mesh = False
    merge_options.mesh_merging_settings.computed_light_map_resolution = False
    merge_options.mesh_merging_settings.generate_light_map_uv = False
    merge_options.mesh_merging_settings.lod_selection_type = unreal.MeshLODSelectionType.ALL_LO_DS
    merge_options.mesh_merging_settings.merge_physics_data = True
    merge_options.mesh_merging_settings.pivot_point_at_zero = True
    
    # Important: These settings help preserve materials
    merge_options.mesh_merging_settings.bake_vertex_data_to_mesh = False
    merge_options.mesh_merging_settings.use_vertex_data_for_baking_material = False
    merge_options.mesh_merging_settings.use_texture_binning = False

    # Step 4: Merge actors into a single StaticMesh asset
    merged_mesh = staticMeshSubsystem.merge_static_mesh_actors(actors_to_merge, merge_options)
    if merged_mesh:
        unreal.log(f"Merged {len(actors_to_merge)} actors into: {merged_mesh.get_name()}")
        
        # Force compile materials on the merged mesh
        force_materials_ready(merged_mesh)
        
        return merged_mesh
    else:
        unreal.log_warning("Failed to merge actors.")
        return None

imported_paths = import_gltf_asset(object_file_path, destination_path)
actor = merge_imported_static_meshes(imported_paths)

I would go with interchange libraries
like shown here

And make sure the combine_static_meshes option is turned on, that will do the merge on import.
Let me know if importing like that you still have some issues with materials and textures.

You will have to add some code to spawn the imported assets if necessary.