Unable to generate LODs on ImporterMesh (automatically or manually)

Godot Version

Godot 4.4 Stable

Question

I’m generate meshes, and I wanted to generate LODs for them as well, however I hit a wall where generate_lods() from ImporterMesh doesn’t create any LODs, and adding a dictionary w/ array of indexes during add_surface() has no effect on the mesh’s current LOD in the viewport.

@tool
extends Node3D

@export_tool_button("Run") var run : Callable = _run # Dont forget to reload project after adding this
@export_range(0,10,1) var x_idxes : int = 4 # x size of mesh sheet
@export_range(0,10,1) var y_idxes : int = 4 # y size of mesh sheet
@export_category("LODS")
@export_range(0.0,100.0, 0.1) var lod_range := 1.0 # distance for the LOD dictionary
@export var generate_lods := false # enable this to try auto-generating lod
@export_range(1,180,1) var norm_merge_ang : int = 25 # angle used for generating lod
var mesh_instance : MeshInstance3D

func _run():
	if mesh_instance != null:
		mesh_instance.queue_free()
	mesh_instance = MeshInstance3D.new()
	add_child(mesh_instance)
	mesh_instance.set_owner(self)
	var importer_mesh = ImporterMesh.new()
	var lod := {lod_range:PackedInt32Array()}
	
	var points : PackedVector3Array
	var ns : PackedVector3Array
	
	var i = 0
	for y in range(0, y_idxes):
		for x in range(0,x_idxes):
			var pos := Vector3(x, 0.0, y)
			lod[lod_range].push_back(i)
			points.push_back(pos)
			ns.push_back(Vector3.UP)
			i+=1
			points.push_back(pos + Vector3(1.0,0.0,0.0))
			ns.push_back(Vector3.UP)
			i+=1
			points.push_back(pos + Vector3(0.0,0.0,1.0))
			ns.push_back(Vector3.UP)
			i+=1
			
			points.push_back(pos + Vector3(1.0,0.0,1.0))
			ns.push_back(Vector3.UP)
			i+=1
			points.push_back(pos + Vector3(0.0,0.0,1.0))
			ns.push_back(Vector3.UP)
			i+=1
			points.push_back(pos + Vector3(1.0,0.0,0.0))
			ns.push_back(Vector3.UP)
			i+=1
	print("num pnts: ", points.size())
	print("lod pnts: ", lod[lod_range])
	var surface_array = []
	surface_array.resize(Mesh.ARRAY_MAX)
	surface_array[Mesh.ARRAY_VERTEX] = points
	surface_array[Mesh.ARRAY_NORMAL] = ns
	importer_mesh.add_surface(Mesh.PRIMITIVE_TRIANGLES, surface_array,[],lod)
	if generate_lods:
		importer_mesh.generate_lods(norm_merge_ang,norm_merge_ang,[])
	print("num_lods: ", importer_mesh.get_surface_lod_count(0))
	mesh_instance.mesh = importer_mesh.get_mesh()
	mesh_instance.lod_bias = 0.01

LMK what I’m doing wrong here. I have absolutely no clue at this point.

Looks like you need to supply the indices too:

I’ve not tested it but you could try using SurfaceTool to generate the surface, call SurfaceTool.index() to generate the indices, and use SurfaceTool.commit_to_arrays() to get the final surface Array

1 Like

@mrcdk Dealing with that resolved my issue!

Here’s a working example for anyone else who stumbles on this:

@tool
extends Node3D

@export_tool_button("Run") var run : Callable = _run # Dont forget to reload project after adding this
@export_range(0,10,1) var x_idxes : int = 4
@export_range(0,10,1) var y_idxes : int = 4
@export_category("LODS")
@export_range(0.1,100.0, 0.1) var lod_bias := 1.0
@export_range(1,180,1) var norm_merge_ang : int = 25
var mesh_instance : MeshInstance3D

func _ready():
	_run()

func _run():
	if mesh_instance != null:
		mesh_instance.queue_free()
	mesh_instance = MeshInstance3D.new()
	add_child(mesh_instance)
	mesh_instance.set_owner(self)
	var importer_mesh = ImporterMesh.new()
	
	var points : PackedVector3Array
	var ns : PackedVector3Array
	var inds : PackedInt32Array
	
	var i : int = 0
	for y in range(0, y_idxes):
		for x in range(0,x_idxes):
			var a_pos := Vector3(x, 0.0, y)
			var b_pos := Vector3(x+1.0, 0.0, y)
			var c_pos := Vector3(x, 0.0, y+1.0)
			var d_pos := Vector3(x+1.0, 0.0, y+1.0)
			
			# Tri 1 = A-B-C
			points.push_back(a_pos)
			ns.push_back(Vector3.UP)
			inds.push_back(i)
			i += 1
			
			points.push_back(b_pos)
			ns.push_back(Vector3.UP)
			inds.push_back(i)
			i += 1
			
			points.push_back(c_pos)
			ns.push_back(Vector3.UP)
			inds.push_back(i)
			i += 1
			
			# Tri 2 = D-C-B
			points.push_back(d_pos)
			ns.push_back(Vector3.UP)
			inds.push_back(i)
			i += 1
			
			points.push_back(c_pos)
			ns.push_back(Vector3.UP)
			inds.push_back(i)
			i += 1
			
			points.push_back(b_pos)
			ns.push_back(Vector3.UP)
			inds.push_back(i)
			i += 1
	
	var surface_array = []
	surface_array.resize(Mesh.ARRAY_MAX)
	surface_array[Mesh.ARRAY_VERTEX] = points
	surface_array[Mesh.ARRAY_NORMAL] = ns
	surface_array[Mesh.ARRAY_INDEX] = inds
	importer_mesh.add_surface(Mesh.PRIMITIVE_TRIANGLES, surface_array)
	importer_mesh.generate_lods(norm_merge_ang,norm_merge_ang,[])
	mesh_instance.mesh = importer_mesh.get_mesh()
	mesh_instance.lod_bias = lod_bias

I didn’t bother making the manual LOD generation work, it just seemed like tedious index tracking. I think the way it would work would be to input the indexes of the points you wish to keep. However, I have a feeling that you would have to input them in a similar triangle-making order as how we put in the positions. I haven’t tried it yet because I’m perfectly happy with the generated results.