Godot plugin to export and import meshes as OBJ (.obj
, .mtl
) from games and apps during runtime. Does not use any editor-only classes, and is pure GDScript (not requiring mono version nor any GDExtension shenanigans).
Based on the work of mohammedzero43's CGSExporter and fractilegames's godot-obj-export for the exporter, and includes a wrapper method for the ObjParse
class from Ezcha's gd-obj for importing.
Install the plugin, activate it. This will make the OBJExporter
singleton available to your game/app.
To save a mesh to a .obj
/.mtl
file pair, call:
OBJExporter.save_mesh_to_files(mesh, file_path, file_name_without_extension)
So if you want to save the mesh in a my_meshinstance3d
node into user://model/my_file.obj
and user://model/my_file.mtl
, use:
OBJExporter.save_mesh_to_files($my_meshinstance3d.mesh, "user://model/", "my_file")
Important: the file_path
must exist, so check and create it with DirAccess
first, if needed.
Depending on model complexity, the file generation might take a good while, so it happens asynchronously. Does not use threads, instead it relies on await
to run in the background in the main thread.
Therefore, the OBJExporter
singleton has 3 signals to inform your application:
export_started
:
Emitted when save_mesh_to_files()
is called
export_progress_updated(surf_idx, progress_value)
:
Emitted periodically during file generation (every 60 vertices), to be used to update progress bars and similar. Arguments are surface index (starting at 0
) and a normalized progress value (that is, range from 0.0
to 1.0
) of that surface. There is no argument for overall progress value as calculating this would be unnecessarily costly.
export_completed(object_file, material_file)
:
Emitted when the process is completed and the files are saved to disk. The arguments are the final filenames used (e.g. user://model/my_file.obj
and user://model/my_file.mtl
), composed from the input arguments.
Since OBJExporter
is a singleton and therefore not visible in your scene tree in the editor, connect these signals via code (if used), e.g.:
func _ready():
OBJExporter.export_started.connect(_on_export_started)
OBJExporter.export_completed.connect(_on_export_completed)
OBJExporter.export_progress_updated.connect(_on_export_progress)
To import a mesh from an .obj
file autodetecting the .mtl
file, call:
OBJExporter.load_mesh_from_file(file_path)
Or you can also manually specify the material file:
OBJExporter.load_mesh_from_file(obj_file_path, mtl_file_path)
This method is fully synchronous and will return the mesh (it is a transparent call to ObjParse.load_obj
).
Included in the repo is an example project which exports the built-in mesh from a MeshInstance3D
node.
The exported .obj
/.mtl
, later imported into Blender:
Clicking Import Suzanne
will do exactly what it says on the tin:
Thanks to bananu7 for pointing me in the right direction on indexing vertices with SurfaceTool
.