MeshLibrary import with GDScript

Godot Version

4.2

Question

I try to create a MeshLibrary with GDScript from a Blender file. So using a EditorScenePostImport script, I create a new MeshLibrary, add the meshes and save it. The script is executed without erros, but unfortunatelly the MeshLibrary seems still be empty, when I add it to a grid.

What is wrong in my script?

@tool # Needed so it runs in editor.
extends EditorScenePostImport

# This sample changes all node names.
# Called right after the scene is imported and gets the root node.
func _post_import(scene):
	var source = get_source_file()
	print_rich("Processing import for scene '" + scene.name + "' from '" + source + "'")
	var meshLib = MeshLibrary.new()
	var dest = source.replace('.blend', '.tres')
	print("Creating new MeshLibrary '" + dest + "'")
	iterate(scene, meshLib)
	var err = ResourceSaver.save(meshLib, dest)
	if err != OK:
		printerr("Error saving MeshLibrary: " + str(err))
	else:
		print("Saved MeshLibrary: " + dest)
	return scene

func iterate(node, meshLib):
	if node != null:
		if node.get_class() == "MeshInstance3D":
			add_mesh(node, meshLib)
		for child in node.get_children():
			iterate(child, meshLib)

func add_mesh(node, meshLib):
	var index = meshLib.get_last_unused_item_id()
	print_rich("Import Mesh: [b]%s[/b] at %d" % [node.name, index])
	meshLib.create_item(index)
	meshLib.set_item_name(index, node.name)
	meshLib.set_item_mesh(index, node.get_mesh())

With a little bit of modification, I was able to get this script to work for me. Thanks for the base work, or I never would have thought this was possible!

Here’s the script I use:

@tool
extends EditorScenePostImport

func _post_import(scene):
	var source = get_source_file()
	print_rich("Processing import for scene '" + scene.name + "' from '" + source + "'")

	# Set up the MeshLibrary file -- delete everything in it, if it already exists
	# otherwise the Godot editor has a hard time realizing we made changes
	var dest = source.replace('.blend', '.tres')
	var meshLib : MeshLibrary
	if ResourceLoader.exists(dest):
		print("Overwriting existing MeshLibrary, removing all mesh instances")
		meshLib = ResourceLoader.load(dest, "MeshLibrary")
		for id in meshLib.get_item_list():
			meshLib.remove_item(id)
	else:
		print_rich("Creating new MeshLibrary [b]%s[/b]" % [dest])
		meshLib = MeshLibrary.new()

	# Iterate through the imported scene to assemble the MeshLibrary
	iterate(scene, meshLib)

	# Save the MeshLibrary
	var err = ResourceSaver.save(meshLib, dest)

	if err != OK:
		printerr("Error saving MeshLibrary: " + str(err))
	else:
		print("Saved MeshLibrary: " + dest)

	return scene

func iterate(node, meshLib):
	if node != null:
		if node.get_class() == "MeshInstance3D":
			add_mesh(node, meshLib)
		for child in node.get_children():
			iterate(child, meshLib)

func add_mesh(node, meshLib):
	var index = meshLib.get_last_unused_item_id()
	print_rich("Import Mesh: [b]%s[/b] at %d" % [node.name, index])
	meshLib.create_item(index)
	meshLib.set_item_name(index, node.name)
	meshLib.set_item_mesh(index, node.get_mesh())

1 Like

Actually, something is really screwed up with this. For one, if you use the same MeshLibrary for manual additions or scenes as well, this import script will wipe those out every time. That same process means that resources get a new sub resource ID every time, which is annoying for source control. So I’ll try to modify this into an approach that matches existing mesh nodes in order to not stomp all over the mesh library on import.

As a separate problem though, sometimes meshes are importing with the wrong position and rotation, and I don’t know what’s going on there.

Well, closing the editor and reopening it fixed the weird position and rotation issue. I’m not sure what was up with that.

Unfortunately new SubResources are still generated with this script, because the mesh is still assigned to each of them. The only way to prevent that would be some kind of mesh equality check, which is well beyond my GDScript abilities. However it will at least preserve existing meshes if you manually add things to the MeshLibrary. Here’s my script:

@tool
extends EditorScenePostImport

func _post_import(scene):
	var source = get_source_file()
	print_rich("Processing import for scene '" + scene.name + "' from '" + source + "'")

	# Set up the MeshLibrary file -- load it if it already exists
	# otherwise the Godot editor has a hard time realizing we made changes
	var dest = source.replace('.blend', '.tres')
	var meshLib : MeshLibrary
	if ResourceLoader.exists(dest):
		print("Loading existing MeshLibrary [b]%s[/b]" % [dest])
		meshLib = ResourceLoader.load(dest, "MeshLibrary")
	else:
		print_rich("Creating new MeshLibrary [b]%s[/b]" % [dest])
		meshLib = MeshLibrary.new()

	# Iterate through the imported scene to assemble the MeshLibrary
	iterate(scene, meshLib)

	# Save the MeshLibrary
	var err = ResourceSaver.save(meshLib, dest)

	if err != OK:
		printerr("Error saving MeshLibrary: " + str(err))
	else:
		print("Saved MeshLibrary: " + dest)

	return scene

func iterate(node, meshLib):
	if node != null:
		if node.get_class() == "MeshInstance3D":
			add_mesh(node, meshLib)
		for child in node.get_children():
			iterate(child, meshLib)

func add_mesh(node : MeshInstance3D, meshLib):
	var index = meshLib.find_item_by_name(node.name)
	if index == -1 :
		index = meshLib.get_last_unused_item_id()
		print_rich("Creating new Mesh: [b]%s[/b] at %d" % [node.name, index])
		meshLib.create_item(index)
		meshLib.set_item_name(index, node.name)
	else:
		print_rich("Update existing Mesh: [b]%s[/b] at %d" % [node.name, index])
		meshLib.set_item_mesh(index, node.get_mesh())