Procedural geometry generation

Users often ask how to generate geometry from code. This is not very complicated, but it’s not obvious. Godot provides a few classes entirely dedicated to make it this easy. Still, the best tool for the job depends entirely on the use case.

SurfaceTool

This is the most common helper. SurfaceTool is a class you can instantiate to generate Meshes, specifically Mesh Surfaces.

It has a similar API to OpenGL 1.x, and it’s meant for static content. This means, the mesh is generated once and then used.

Here is a simple example of how to use it to add a single triangle.

GDScript

  1. var st = SurfaceTool.new()
  2. st.begin(Mesh.PRIMITIVE_TRIANGLES)
  3. # Prepare attributes for add_vertex.
  4. st.add_normal(Vector3(0, 0, 1))
  5. st.add_uv(Vector2(0, 0))
  6. # Call last for each vertex, adds the above attributes.
  7. st.add_vertex(Vector3(-1, -1, 0))
  8. st.add_normal(Vector3(0, 0, 1))
  9. st.add_uv(Vector2(0, 1))
  10. st.add_vertex(Vector3(-1, 1, 0))
  11. st.add_normal(Vector3(0, 0, 1))
  12. st.add_uv(Vector2(1, 1))
  13. st.add_vertex(Vector3(1, 1, 0))
  14. # Create indices, indices are optional.
  15. st.index()
  16. # Commit to a mesh.
  17. var mesh = st.commit()

Just explore the APIs and the possibilities.

ImmediateGeometry

Unlike SurfaceTool, ImmediateGeometry is an actual node. It’s similar in the “OpenGL 1.x” style API, but it’s actually designed to create content on the fly and modify it every frame efficiently.

Generating complex geometry (several thousand vertices) with this node is inefficient, even if it’s done only once. Instead, ImmediateGeometry is designed to generate simple geometry that changes every frame.

It’s used similar to SurfaceTool.

GDScript

  1. extends ImmediateGeometry
  2. void _process(delta):
  3. # Clean up before drawing.
  4. clear()
  5. # Begin draw.
  6. begin(Mesh.PRIMITIVE_TRIANGLES)
  7. # Prepare attributes for add_vertex.
  8. set_normal( Vector3(0, 0, 1))
  9. set_uv(Vector2(0, 0))
  10. # Call last for each vertex, adds the above attributes.
  11. add_vertex(Vector3(-1, -1, 0))
  12. set_normal(Vector3(0, 0, 1))
  13. set_uv(Vector2(0, 1))
  14. add_vertex(Vector3(-1, 1, 0))
  15. set_normal(Vector3(0, 0, 1))
  16. set_uv(Vector2(1, 1))
  17. add_vertex(Vector3(1, 1, 0))
  18. # End drawing.
  19. end()

Arrays

Lastly, the final way to do this is to create arrays themselves. This is the most efficient way to create static geometry, and is only recommended when SurfaceTool is not fast enough.

Similar code as before, but draw a square using indices:

GDScript

  1. var arrays = []
  2. arrays.resize(Mesh.ARRAY_MAX)
  3. var normal_array = []
  4. var uv_array = []
  5. var vertex_array = []
  6. var index_array = []
  7. normal_array.resize(4)
  8. uv_array.resize(4)
  9. vertex_array.resize(4)
  10. index_array.resize(6)
  11. normal_array[0] = Vector3(0, 0, 1)
  12. uv_array[0] = Vector2(0, 0)
  13. vertex_array[0] = Vector3(-1, -1, 0)
  14. normal_array[1] = Vector3(0, 0, 1)
  15. uv_array[1] = Vector2(0,1)
  16. vertex_array[1] = Vector3(-1, 1, 0)
  17. normal_array[2] = Vector3(0, 0, 1)
  18. uv_array[2] = Vector2(1, 1)
  19. vertex_array[2] = Vector3(1, 1, 0)
  20. normal_array[3] = Vector3(0, 0, 1)
  21. uv_array[3] = Vector2(1, 0)
  22. vertex_array[3] = Vector3(1, -1, 0)
  23. # Indices are optional in Godot, but if they exist they are used.
  24. index_array[0] = 0
  25. index_array[1] = 1
  26. index_array[2] = 2
  27. index_array[3] = 2
  28. index_array[4] = 3
  29. index_array[5] = 0
  30. arrays[Mesh.ARRAY_VERTEX] = vertex_array
  31. arrays[Mesh.ARRAY_NORMAL] = normal_array
  32. arrays[Mesh.ARRAY_TEX_UV] = uv_array
  33. arrays[Mesh.ARRAY_INDEX] = index_array
  34. var mesh = ArrayMesh.new()
  35. mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES,arrays)