DEV Community

Richard McHale
Richard McHale

Posted on • Updated on

Godot - Batch generation of collision meshes from gtlf model files

Background

While trying to prototype something I grabbed a free set of models in gtlf format, hoping to use them in a MeshLibrary. I immediatly ran into a couple of issues; Firstly Godot didn't have any bulk generation for collsion data accross multiple files. Secondly the structure of the imported gtlf files wasn't quite right to add to a MeshLibrary source scene once I had generated the collision data through the UI. (If there are easy options for either of these let me know.)

Fixing this manually for each file would have taken hours (fixing it in bulk also took hours, but at least I learnt some things).

Solution

Note: This should work for people with a similar problem, but it will need minimal editing of variables as it wasn't worth adding logic to handle cli parameters at this point.

This script will loop through gltf files and create a corresponding tscn file (PackedScene), with collision data generated and a structure that can be using in a MeshLibrary.

  • Create a file called collidable_from_gltf.gd in the root of your project, and paste the script below in (also on GitHub).
  • Edit the source_directory and destination_directory variables to point to the right place.
  • Run the following in the command line in the root of your project, with changes for wherever your godot folder is and version in use.
../godot/Godot_v4.2.1-stable_win64_console.exe -s collidable_from_gltf.gd
Enter fullscreen mode Exit fullscreen mode

Enjoy

Script

extends SceneTree

func create_collidable_from_gltf(gltf_path: String, destination_path: String) -> void:
    print("Creating " + destination_path + " from " + gltf_path)

    # Load the imported gltf and get the MeshInstance3D node, assuming it's the first child node
    var model_resouce = ResourceLoader.load(gltf_path) as PackedScene
    var model_scene = model_resouce.instantiate()
    var source_mesh = model_scene.get_child(0)

    # Copy the mesh as the new root node
    var mesh_node = source_mesh.duplicate() as MeshInstance3D;
    # Create a collision shape (consider using a simpler shape if you care about performance)
    mesh_node.create_trimesh_collision() 

    var scene = PackedScene.new()

    var result = scene.pack(mesh_node)

    if result == OK:
        var error = ResourceSaver.save(scene, destination_path)
        if error != OK:
            push_error("An error occurred while saving the scene to disk.")

    # Free the resources (not doing this will show leaks running this from the command line)
    mesh_node.queue_free()
    model_scene.queue_free()

func get_gltf_file_names(directory: String) -> Array:
    var files = DirAccess.get_files_at(directory)

    var result = []

    for file in files:
        if (file.ends_with(".gltf")):
            result.append(file)

    return result

func _init() -> void:
    var source_directory = "res://models/landscape"
    var destination_directory = "res://meshes/landscape"

    # Make sure this directory exists
    DirAccess.make_dir_recursive_absolute(destination_directory)

    var files = get_gltf_file_names(source_directory)
    for file in files:
        # Chop off the file extension
        var name = file.substr(0, file.length() - 5)

        create_collidable_from_gltf(source_directory + "/" + file, destination_directory + "/" + name + ".tscn")

    quit()

Enter fullscreen mode Exit fullscreen mode

Top comments (0)