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)