Procedurally generate meshes with Godot 3.1 – different approaches

As I needed for my Godot 3.x addon I decided to test the different approaches that Godot offers to generate 3D meshes, so far (to my knowledge) there are 3 ways to do that:

  • Use ImmediateGeometry
  • Use SurfaceTool
  • Directly generate an ArrayMesh

So I will describe the above approaches and eventually mention its pros and cons (note, I just found out Godot official documentation for version 3.1 does have a very similar article, so it’s worth check this out here, and actually some of the code examples are form the official documentation).

I might say, the first time was quite frustrating as I had very little knowledge on 3D. If you are just starting with 3D I suggest to learn some basics; Godot have some documentation about 3D math about vertices, transofrms, etc. But to understand how to draw a mesh you just need to know about vertices, UVs, normals, and a few more things. So I suggest to look for OpenGL documentation, wikipedia articles, till you have a clearer idea, then go to the Godot API documentation and you’ll probably understand it much faster.

ImmediateGeometry

This was the first thing I tested as it was the first thing that came out googling.

Code example

With this you draw geometry in a way similar to what you would to with OpenGL commands:

extends ImmediateGeometry

void _process(delta):
    # Clean up before drawing.
    clear()

    # Begin draw.
    begin(Mesh.PRIMITIVE_TRIANGLES)

    # Prepare attributes for add_vertex.
    set_normal( Vector3(0, 0, 1))
    set_uv(Vector2(0, 0))
    # Call last for each vertex, adds the above attributes.
    add_vertex(Vector3(-1, -1, 0))

    set_normal(Vector3(0, 0, 1))
    set_uv(Vector2(0, 1))
    add_vertex(Vector3(-1, 1, 0))

    set_normal(Vector3(0, 0, 1))
    set_uv(Vector2(1, 1))
    add_vertex(Vector3(1, 1, 0))

    # End drawing.
    end()

Pros and cons

Well, in my personal experience using immediate geometry there are mostly disadvantages, for example rendering is slower (especially on mobile I’ve noticed a big difference). But that’s to be expected I guess, as official documentation mentions that this should be used for simple geometries that change frequently (I imagine like once per frame).

SurfaceTool

Then I tested SurfaceTool, mainly because I found an addon that made use of it and tried to imitate the same approach. And with this I finally was able to do what I wanted, and I believed it was fast enough for my needs.

Code example

var st = SurfaceTool.new()

st.begin(Mesh.PRIMITIVE_TRIANGLES)
# TIP: Adding a smooth group will generate the surface as smooth,
# Otherwise it will be generated as flat.
add_smooth_group(true)

# Prepare attributes for add_vertex.
st.add_normal(Vector3(0, 0, 1))
st.add_uv(Vector2(0, 0))
# Call last for each vertex, adds the above attributes.
st.add_vertex(Vector3(-1, -1, 0))

st.add_normal(Vector3(0, 0, 1))
st.add_uv(Vector2(0, 1))
st.add_vertex(Vector3(-1, 1, 0))

st.add_normal(Vector3(0, 0, 1))
st.add_uv(Vector2(1, 1))
st.add_vertex(Vector3(1, 1, 0))

# Create indices, indices are optional.
st.index()

# TIP: If you want normals calculated automatically remember to call this method:
generate_normals()
# TIP: If you use generate_normals you don't need to populate the normals arrray with add_normal.

# Commit to a mesh.
var mesh = st.commit()

The code above is taken from the official docs, I just added a couple of TIPs on how to smooth the surface and how to automatically calculate the normals.

Pros and cons

Well, most likely you will end up using this approach as it has many advantages such as:

  • Normals can be automatically calculated
  • you can choose to use flat or smooth shading
  • generation is quite fast

What you may not like, or why I ended up using ArrayMesh:

  • You can’t smooth between 2 meshes (but you can between surfaces of the same mesh)

ArrayMesh

When I realized that my addon was not fast enough I decided to investigate further, I finally got Godot source code complile on my pc, downloaded more addons that did similar things, and so on. That’s when I decided to try out with ArrayMesh.

Code example

var vertices = PoolVector3Array()
vertices.push_back(Vector3(0, 1, 0))
vertices.push_back(Vector3(1, 0, 0))
vertices.push_back(Vector3(0, 0, 1))
# Initialize the ArrayMesh.
var arr_mesh = ArrayMesh.new()
var arrays = []
arrays.resize(ArrayMesh.ARRAY_MAX)
arrays[ArrayMesh.ARRAY_VERTEX] = vertices
# Create the Mesh.
arr_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
var m = MeshInstance.new()
m.mesh = arr_mesh

Pros and cons

The advantages of using ArrayMesh:

  • fastest generation of the mesh (0.5 seconds on my machine on average)
  • if vertices are indexed, smaller size on disk (half of the SurfaceTool array fan method)
  • normals are generated by code, so better control on how you want them

The problem with this approach is that:

  • Normals have to be calculated manually (write your own code)

Conclusion

It took a while to learn how to use the above methods, but I think it’s worth if you plan to use procedural generation in your Godot projects.

Personally I ended up hating ImmediateGeometry as it has lots of problems.

I am planning to write a couple more articles about SurfaceTool (and also MeshDataTool, they kinda work together in some cases) and especially ArrayMesh (that by the way is a resource, unlike the other 2 API objects). I want to provide little more complex code examples that I am hoping will result useful.

Posted in 3D, Code Tricks, Game Development and tagged , , , , , .

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.