Problems Generating ArrayMesh at Runtime

Godot Version

4.2

Question

I am currently trying to explode an icosphere into its constituent triangles so I can interact with each one by clicking on them and changing their color.

To start I took an Icosphere plugin made by M.A.G. Gen and generated an icosphere. As a child MeshInstance3D ArrayMesh I added the following code, but no triangles are visible in the viewport.

I’m brand new to Godot and would appreciate some help as to where I am going wrong.

The child instances are being generated at runtime, and I can see that they have random materials in the inspector, but I’m not sure if the triangle surfaces are generating. I’m also not getting any errors.

extends MeshInstance3D
# Get the original icosphere mesh
@onready var original_mesh = get_parent().mesh.surface_get_arrays(0)

func _ready():
	
	# Extract vertices and indices
	var vertices = original_mesh[Mesh.ARRAY_VERTEX]
	var indices = original_mesh[Mesh.ARRAY_INDEX]
	
	# Iterate through indices and create separate MeshInstance nodes for each triangle
	for i in range(0, indices.size(), 3):  # Assuming triangles are defined by groups of 3 indices
		#Retreive vertex information from icosphere parent
		var triangle_vertices = PackedVector3Array()
		triangle_vertices.append(vertices[indices[i]])
		triangle_vertices.append(vertices[indices[i + 1]])
		triangle_vertices.append(vertices[indices[i + 2]])

		# Create a new MeshInstance node
		var triangle_mesh_instance = MeshInstance3D.new()
		add_child(triangle_mesh_instance)
		
		#Declare surface array for child mesh
		var surface_array = []
		surface_array.resize(Mesh.ARRAY_MAX)
		
		#Set vertex information to that of the parent vertecies
		surface_array[Mesh.ARRAY_VERTEX] = triangle_vertices
		
		#Set normals to normalized vertex information
		var normals = PackedVector3Array()
		normals.append(triangle_vertices[0].normalized())
		normals.append(triangle_vertices[1].normalized())
		normals.append(triangle_vertices[2].normalized())
		surface_array[Mesh.ARRAY_NORMAL] = normals
		
		#Set indices
		var indicies_child = PackedInt32Array()
		indicies_child.append(0)
		indicies_child.append(1)
		indicies_child.append(2)
		surface_array[Mesh.ARRAY_INDEX] = indices
		
		# Create a new mesh for the triangle
		var triangle_mesh = ArrayMesh.new()
		triangle_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array)
		
				# Assign the mesh to the MeshInstance
		triangle_mesh_instance.mesh = triangle_mesh
		
		#Assign random color to face for debug purposes
		var tri_material = StandardMaterial3D.new()
		tri_material.albedo_color = Color(randf(), randf(), randf(), 1.0)
		triangle_mesh_instance.set_surface_override_material(0,tri_material)
		

I couldn’t get it to work with ArrayMesh but using MeshDataTool and SurfaceTool in combination seems to do what you want to achieve. The code below creates copies the normals of the original mesh, so they are smooth shaded. I added comments on how to get flat shaded triangles.

extends MeshInstance3D
# Get the node with the mesh
@export var original : MeshInstance3D

func _ready():
	var mesh_data_tool := MeshDataTool.new()
	mesh_data_tool.create_from_surface(original.mesh, 0)
	
	for face_index in mesh_data_tool.get_face_count():
		var surface_tool = SurfaceTool.new()
		surface_tool.begin(Mesh.PRIMITIVE_TRIANGLES)
		
		for i in 3:
			var vtx_index = mesh_data_tool.get_face_vertex(face_index, i)
			surface_tool.set_uv(mesh_data_tool.get_vertex_uv(vtx_index))
			surface_tool.set_normal(mesh_data_tool.get_vertex_normal(vtx_index))
			surface_tool.set_tangent(mesh_data_tool.get_vertex_tangent(vtx_index))
			surface_tool.add_vertex(mesh_data_tool.get_vertex(vtx_index))
			
		# Use the generate functions below and omit adding the normals and tangents
		# above to get flat shaded triangles
		#surface_tool.generate_normals()
		#surface_tool.generate_tangents()
		
		var new_mesh := surface_tool.commit()
		
		var node = MeshInstance3D.new()
		node.mesh = new_mesh
		add_child(node)
1 Like

Thank you very much! That looks much simpler than what I was doing with my ArrayMesh. I will test it shortly.

I’ve seen the SurfaceTool used online but was unsure what its use case was. What differentiates the use cases of ArrayMesh and the SurfaceTool?

The main difference is the API. For ArrayMesh you have to specify arrays for vertices, indicies, uvs etc. while SurfaceTool you specify one vertex after the other. SurfaceTool also offers a few more methods to make life easier.

This documentation page might be worth a read: Procedural geometry docs

Desktop2024.03.24-16.19.09.01-ezgif.com-video-to-gif-converter

Thank you a ton @omggomb I was able to get a working prototype of the functionality I was going for!

1 Like