Dynamically assigning materials by name in Godot 4.5

There’s a few different ways to assign specific materials to an FBX or glTF object in Godot 4.5.2 (and presumably newer).

  1. You can simply make the object “local”, then you can go through and assign each surface override like so:
var _mat = load(MatSet[Mat0])
$Spectre/SM_SpectreEngines_00/SM_SpectreEngines_00a.set_surface_override_material(0, _mat)
$Spectre/SM_SpectreEngines_00/SM_SpectreEngines_00b.set_surface_override_material(0, _mat)
... etc

This definitely works if you already had to make the mesh local for some other reason, but it’ll explode the size of your TSCN and drastically increase load time:

13M May 31 21:46 ./Spectre_light.tscn
# vs
1.9K Jun  6 20:28 ./Spectre_light.tscn
  1. You can also use that same explicit method while switching to a binary .scn file to reduce size, but at the cost of reducing git effectiveness and readability.

  2. Alternatively, you can use the exact same explicit method, but not make the mesh local, which is still very manual but dramatically reduces load time.

  3. However, the method I stumbled across through experimentation that I find far better is this one:

func _setColor():
	# We load and call it because it has a recursive
	var _mat  = load(ShipData.SmallBodies[shipSpecific]["mats"][Mat0])  
	apply_overrides($Spectre, _mat)
	pass


# This is SO MUCH simpler than the other methods I've found
# Just build the override map from the FBX, as it's seen by Godot
func apply_overrides(_node, _mat):
	var override_map = {
		"Exterior": _mat,
		#"Glass":    load("res://mat_glass.tres"),
		#"Interior": load("res://mat_interior.tres")
	}
	if _node is MeshInstance3D:
		var mesh = _node.mesh
		if mesh:
			for i in mesh.get_surface_count():
				var surf_name = mesh.surface_get_name(i)
				if override_map.has(surf_name):
					_node.set_surface_override_material(i, override_map[surf_name])
	for child in _node.get_children():
		apply_overrides(child, _mat)

So here what we do is just grab the materials as seen in the import wizard:

Then you can override them for the whole mesh very easily just like you would from the importer. The function loops over each item in the FBX/glTF tree, grabs, the meshes, matches the name, and then applies the new material.
It reduces loading time, disk space, and complexity.

1 Like