Skip to content
← Back to Blog tutorials

Godot 4 EditorImportPlugin: custom GLB import pipelines

By BitSoul3D4 min read134 views

Building a custom GLB import pipeline in Godot 4 puts you in full control — automatic LOD generation, collision meshes, shadow proxies, and material overrides applied the moment a file lands in your project. EditorImportPlugin is the hook that makes it possible.

Godot 4 EditorImportPlugin: custom GLB import pipelines

What EditorImportPlugin does and when to use it

Godot's built-in GLB importer handles most assets well, but it runs identically on every file. When you have 747 assets from a source like the BitSoul marketplace and need consistent treatment — stripped UV2 channels, forced double-sided materials, or named collision nodes — writing a custom importer pays for itself on the first batch import.

EditorImportPlugin is a GDScript (or C#) class that registers itself with the editor. It declares the file extensions it handles, a priority value that overrides the default importer, and a _import() method that receives the source path and writes to a .godot-internal resource file. The four methods you must implement: _get_importer_name() returns a unique reverse-domain string; _get_recognized_extensions() returns ["glb"]; _get_priority() returns a float above 1.0 to supersede the built-in GLB importer; _get_import_order() returns 0 for most cases.

@tool
extends EditorImportPlugin

func _get_importer_name():             return "com.mystudio.glb_game_ready"
func _get_recognized_extensions():    return ["glb"]
func _get_priority() -> float:        return 2.0   # > 1.0 overrides the default importer
func _get_import_order() -> int:      return 0

Place this script in addons/glb_importer/ alongside a plugin.cfg and enable it in Project → Project Settings → Plugins.

What EditorImportPlugin does and when to use it — illustrated

Implementing LOD generation in _import()

The _import() method receives the source GLB path and an options dictionary from _get_import_options(). Load the scene via GLTFDocument, walk every MeshInstance3D with a recursive helper (_get_all_children(scene)), then call generate_lods() on each ImporterMesh. Always call scene.queue_free() after packing — the editor does not garbage-collect the generated scene automatically.

func _import(source_file, save_path, options, platform_variants, gen_files) -> Error:
    var doc   := GLTFDocument.new()
    var state := GLTFState.new()
    var err   := doc.append_from_file(source_file, state)
    if err != OK: return err

    var scene := doc.generate_scene(state)
    for node in _get_all_children(scene):
        if node is MeshInstance3D:
            var im := node.mesh.get_importable_mesh()
            if im:
                im.generate_lods(options.get("lod_normal_merge_angle", 25.0),
                                 options.get("lod_normal_split_angle",  60.0), [])
                node.mesh = im.get_mesh()

    var packed := PackedScene.new()
    packed.pack(scene)
    scene.queue_free()
    return ResourceSaver.save(packed, "%s.%s" % [save_path, _get_save_extension()])

generate_lods() takes merge angle, split angle, and an optional LOD ratio array — pass [] to let Godot choose automatically (typically 0.25, 0.1, 0.04). get_importable_mesh() returns null for meshes that weren't imported as ImporterMesh, so always check before calling.

Adding automatic collision meshes

After the LOD loop, add StaticBody3D collision for each prop mesh: create a StaticBody3D, add a child CollisionShape3D with shape = node.mesh.create_convex_shape(true, true) (both flags clean and simplify the hull), set .owner = scene on both nodes, then call node.add_child(body). For concave meshes — stairs, hollow props — use create_trimesh_shape() instead; it's more accurate but more expensive at runtime.

Declaring import options for per-asset control

Return import options from _get_import_options(_path, _preset_index) as an Array[Dictionary], each entry with name and default_value keys. They appear as editable fields in the Import dock, letting artists override settings per file without touching code. Commit .import sidecar files to version control so teammates get identical results.

OptionDefaultEffect
generate_collisiontrueAdds StaticBody3D + hull shape
lod_normal_merge_angle25.0Merge angle for LOD generation
lod_normal_split_angle60.0Split angle for LOD generation
force_double_sidedfalseDisables back-face culling on all surfaces
shadow_proxy_lod2Which LOD level is used as shadow caster

Declaring import options for per-asset control — illustrated

Workflow checklist for shipping a custom importer

StepActionGotcha
Register pluginAdd plugin.cfg, enable in Project SettingsPriority must be > 1.0 to override default GLB importer
Define optionsReturn typed dicts from _get_import_options()Missing type key causes silent failures in older 4.x builds
LOD generationCall generate_lods() on ImporterMesh, reassign .meshOnly works if the original mesh was imported as ImporterMesh; check with get_importable_mesh()
CollisionUse create_convex_shape(true, true) for propsConcave meshes need create_trimesh_shape() — more expensive at runtime
Re-import triggerEdit .import file or call EditorFileSystem.reimport_files()Godot does not auto-reimport after plugin code changes
Version controlCommit .import sidecarsWithout them, collaborators fall back to default importer settings

The BitSoul marketplace ships assets as clean GLB with consistent naming — ideal input for a pipeline like this, since node names are predictable and the plugin can branch on them (e.g., nodes prefixed UCX_ get collision-only treatment, matching Unreal conventions).

Automating import with EditorImportPlugin turns asset integration from a per-file chore into a one-time engineering task. Write it once, import 500 assets the right way every time.


This post is part of the Ultimate Guide to Free 3D Game Assets — BitSoul's complete reference for formats, texturing, rigging, optimization, and engine integration.


Need drop-in assets for this workflow? Grab the 98-model Character Pack on the BitSoul marketplace and drop them straight into your project.

Tags: characters

Skip the modeling — download it instead

A free BitSoul3D account gets you 2 GLB downloads every month for personal use plus 25 one-time AI Engine credits, no card required. PBR-textured GLB downloads with a full 3D preview before you buy, for Unreal, Unity, Godot or Blender — OBJ and 3D-printable STL come with any purchase or paid plan.

Browse 1,051 models — from $4.99 → or start free (2 downloads a month)