SurfaceTool in Godot – Complete Guide

When diving into the world of 3D game development, mastering the tools that help breathe life into your creative visions is key. The SurfaceTool class in Godot 4 stands out as one such instrument, boasting the ability to give structure and form to your imaginations in the digital realm. Whether you aspire to sculpt picturesque landscapes or engineer the next groundbreaking gaming mechanics, understanding how SurfaceTool operates is a cornerstone in that journey.

What is SurfaceTool?

The SurfaceTool is a foundational component when dealing with procedural geometry in Godot. It acts as a helper tool that enables you to construct meshes directly from script – piece by piece, attribute by attribute. The way SurfaceTool works is akin to an artist sculpting a statue or a potter shaping clay on a wheel, it adds layer after layer, vertex by vertex, to create a final 3D form.

What is SurfaceTool used for?

SurfaceTool is your goto toolkit when you need to create custom meshes on the fly. From generating dynamic terrains to creating shapes that evolve during gameplay, it puts the power of realtime geometry manipulation at your fingertips. It’s the bridge between raw 3D vertices and vivid, interactive meshes within your game environment.

Why should I learn it?

Why learn to use SurfaceTool? Understanding this class is instrumental for anyone looking to add unique and dynamic 3D components to their games. By mastering SurfaceTool, you:

  • Unlock the potential for creativity and customization way beyond static models.
  • Gain performance advantages with procedural generation, keeping your game optimized.
  • Acquire a deeper understanding of how 3D models are constructed within Godot, empowering you to solve complex 3D programming challenges.

So, let’s dive into the hands-on part of our tutorial to learn how to wield the SurfaceTool with confidence and bring your 3D game assets to life from scratch!

CTA Small Image
FREE COURSES AT ZENVA
LEARN GAME DEVELOPMENT, PYTHON AND MORE
ACCESS FOR FREE
AVAILABLE FOR A LIMITED TIME ONLY

Initializing the SurfaceTool

First things first, before we can start sculpting our 3D model with SurfaceTool, we have to initialize it. This is how you create a new instance of SurfaceTool and prepare it for use.

var st = SurfaceTool.new()

Beginning the Mesh

To begin adding geometry to our mesh, we must first set the primitive type. This informs the SurfaceTool about the kind of geometric shape we’re creating, such as triangles, lines, or points.

st.begin(Mesh.PRIMITIVE_TRIANGLES)

After signaling the start of our mesh, it’s essential to add vertices to outline our desired shape. Here’s how you can add a single vertex to the SurfaceTool:

st.add_vertex(Vector3(0, 0, 0))

However, a single vertex doesn’t make a shape. We need at least three to make a triangle — the simplest 3D shape:

st.add_vertex(Vector3(0, 1, 0))
st.add_vertex(Vector3(1, 0, 0))
st.add_vertex(Vector3(0, 0, 1))

Defining Normals and UVs

Normals help Godot understand the direction a surface faces, which is important for lighting and rendering. Here’s how you can add a normal to each vertex:

st.add_normal(Vector3(0, 1, 0))

Alongside normals, UV coordinates are crucial as they detail how textures will map onto the geometry. They are specified per vertex:

st.add_uv(Vector2(0, 0))
st.add_uv(Vector2(0, 1))
st.add_uv(Vector2(1, 1))
st.add_uv(Vector2(1, 0))

Committing the Mesh

Once you have defined all your vertices, normals, and UVs, committing the mesh is the next step. This finalizes the mesh and makes it ready to be used in the engine.

var mesh = st.commit()

To use the mesh you’ve committed, you need to assign it to a MeshInstance node, which can then be added to the scene tree:

var mi = MeshInstance.new()
mi.mesh = mesh
add_child(mi)

Building More Complex Geometry

While we’ve covered the basics of initializing SurfaceTool and creating a simple shape, what about more complex geometries? Let’s go through incrementally adding vertices, normals, and UVs to build a quad, one of the basic building blocks of 3D meshes.

// Define the vertices for a quad
st.add_vertex(Vector3(0, 0, 0))
st.add_vertex(Vector3(0, 1, 0))
st.add_vertex(Vector3(1, 1, 0))
st.add_vertex(Vector3(1, 0, 0))

// Add normals facing up
st.add_normal(Vector3(0, 1, 0))

// Define UV coords for texturing the quad
st.add_uv(Vector2(0, 0))
st.add_uv(Vector2(0, 1))
st.add_uv(Vector2(1, 1))
st.add_uv(Vector2(1, 0))

// Commit after completing the definition
var quad_mesh = st.commit()

// Apply the mesh to a MeshInstance
var quad_mi = MeshInstance.new()
quad_mi.mesh = quad_mesh
add_child(quad_mi)

Remember, as the complexity of your shapes increases, so will the number of vertices, normals, and UV coordinates you’ll need to define. With practice, you’ll be able to piece together intricate models tailored to your game’s aesthetic and performance needs.

The SurfaceTool not only allows us to define static geometry but also provides methods to manipulate existing vertex data. This allows for optimization and dynamic alterations which can be particularly useful in games where the environment is subject to change.

One common scenario in game development is the need for creating a shared vertex between multiple faces. This is where the index() method comes in handy. It optimizes the vertex array by removing duplicate vertices:

st.index()

Using generate_normals() can save the hassle of manually calculating and setting vertex normals, especially when dealing with complex shapes:

st.generate_normals()

Creating smooth surfaces often requires you to add multiple segments between two points. The SurfaceTool provides the add_smooth_group() method for this purpose:

st.add_smooth_group(true)

If you need to clear all the data that you’ve previously added in the SurfaceTool because you want to start from scratch, just call the clear() method:

st.clear()

It’s also vital to understand how surfaces are created. When you have, for example, an array of points that make up a shape, such as a cube or a custom polygon, and you want to connect these points to make faces, you will use the add_triangle_fan() method:

// Assuming we have an array of Vector3 points for a hexagon
var hexagon_points = [...]
// We add the points as vertices to the SurfaceTool
foreach point in hexagon_points:
  st.add_vertex(point)

// Then we create the faces using a triangle fan
st.add_triangle_fan(hexagon_points)

Duplicating geometry can be another powerful feature when creating patterns or repeating structures. Suppose you have defined a section of a fence and want to extend it across the perimeter of a field. Instead of manually adding each segment, you can use the copy_from() method to duplicate the geometry:

// Create a section of a fence
// ...

// Now let's duplicate it
var new_fence_section = SurfaceTool.new()
new_fence_section.copy_from(existing_fence_section)

Lastly, tweaking existing geometries is also possible with SurfaceTool. Godot 4 makes this extraordinarily accessible with methods like create_from() and create_from_blend_shape() which allow you to generate new meshes from existing ones, even interpolating between different shapes:

// Create a new SurfaceTool to work with
var st_from_mesh = SurfaceTool.new()
// Assume mesh_instance contains the original mesh
st_from_mesh.create_from(mesh_instance, 0)

// To interpolate from a blend shape, use this:
st_from_mesh.create_from_blend_shape(mesh_instance, "blend_shape", 0, 0.5)

Combining these methods, you can efficiently create and manipulate a wide array of geometries in Godot 4, making your games both visually interesting and performance-friendly. Remember, practice is key; experiment with SurfaceTool’s features, and you’ll be on your way to creating stunning 3D visuals in no time.

Now that we’ve covered some basics and manipulation of geometry with the SurfaceTool, we may want to dive into more advanced usage like create from arrays, merging surface tools, and optimizing with deindex and commit_to_arrays methods.

Let’s look at how we can create a mesh from arrays. This can be useful when you have a pre-calculated set of data that represents your vertices, normals, and UVs:

var vertices_array = PoolVector3Array([Vector3(0, 0, 0), Vector3(0, 1, 0), Vector3(1, 0, 0)])
var normals_array = PoolVector3Array([Vector3(0, 0, 1)] * 3) // Same normal for all vertices
var uvs_array = PoolVector2Array([Vector2(0, 0), Vector2(1, 0), Vector2(1, 1)])

st.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, [vertices_array, normals_array, uvs_array])

When you work with multiple meshes that you want to combine into a single mesh, you don’t need to go through the trouble of manually merging array data. The SurfaceTool can merge another SurfaceTool’s data directly:

var st1 = SurfaceTool.new()
var st2 = SurfaceTool.new()

// ...add some geometry to st1 and st2...

st1.merge_from(st2)

Sometimes you may end up with a mesh that has a lot of redundancy in its vertices, which can be inefficient. Godot SurfaceTool provides a deindex method which can optimize your mesh by removing redundant vertices:

st.deindex()

For the cases where you wish to have the most control over your mesh data or when you need to interact with other parts of Godot that require arrays, you can commit the mesh to arrays. The following code demonstrates how to acquire the vertex and index arrays from your SurfaceTool:

var arrays = st.commit_to_arrays()

var vertex_array = arrays[Mesh.ARRAY_VERTEX]
var index_array = arrays[Mesh.ARRAY_INDEX]

It is also possible to manually define indices for complex mesh structures. When creating surfaces with shared vertices or advanced graphical structures, defining indices can lead to better optimization and rendering performance:

st.add_index(0)
st.add_index(1)
st.add_index(2)

For animations or when your mesh requires to change shape over time, creating a mesh with blend shapes allows for such alterations. With the surface tool, you can define a base shape and then specify differences for one or more blend shapes:

// After defining a base shape, we can add a blend shape
st.set_current_blend_shape("Expanded")

// Then define vertices for the blend shape
st.add_vertex(Vector3(0, 1, 0))
st.add_vertex(Vector3(1, 1, 0))
st.add_vertex(Vector3(1, 0, 0))

Lastly, for full control over your generated mesh’s appearance, remember to include material properties. You can add a material directly to the SurfaceTool before committing the mesh:

var material = SpatialMaterial.new()

// Set material properties as needed
// ...

st.set_material(material)

Through these methods, the SurfaceTool gives you the power to control every aspect of mesh creation and manipulation in Godot. As you can see, Godot’s SurfaceTool is an invaluable resource for any game developer looking to create detailed, performant, and dynamic meshes from within the Godot Engine itself. With a firm understanding of these techniques, you’re well on your way to creating the immersive 3D worlds that will captivate players’ imaginations.

Where to go next?

Congratulations on taking your first (or next) steps with the SurfaceTool in Godot 4! We hope you’re feeling inspired to forge ahead on your game development adventure. The journey doesn’t end here – there’s a universe of coding knowledge waiting for you, and we at Zenva are thrilled to help guide you further.

To continue honing your Godot skills, our Godot Game Development Mini-Degree offers a comprehensive pathway. Explore a variety of topics from 2D and 3D assets, the GDScript programming language, to gameplay mechanics for RPGs, RTSs, and more. With our project-based curriculum, you’ll not only learn but also create your own games, gaining practical experience along the way.

And if you’re hungry for even more, browse through our broader collection of Godot courses. Each course is tailored to fit your learning pace and conveniently accessible on any device. Begin your journey as a beginner or level up your existing skills – either way, you’ll be on the right track to becoming a professional game developer. With Zenva, you can transform your creative ideas into reality. Let’s keep the momentum going and continue crafting amazing game experiences together!

Conclusion

As you’ve seen, wielding Godot’s SurfaceTool can be a transformative experience in your journey as a game developer. Whether you’re manipulating vertices in real-time, creating dynamic terrains, or optimizing your 3D models, knowledge of the SurfaceTool is a true game-changer. With every line of code and each new shape, you’re not just building worlds; you’re honing a craft that will define your future games.

Remember, this is merely a stepping stone; there’s an entire landscape of learning to explore with us at Zenva. Feel free to revisit our Godot Game Development Mini-Degree anytime you need a refresher or want to expand your expertise. Your evolution as a game developer is our mission, and together, we can script the next chapter of your game development saga. So go on, take that knowledge, and let’s turn the virtual into reality!

FREE COURSES
Python Blog Image

FINAL DAYS: Unlock coding courses in Unity, Godot, Unreal, Python and more.