I’ve added a ball trail to IsoPutt. It serves two purposes: one, it’s like how in Super Meat Boy you can see all of your failed attempts. In IsoPutt, the ball trail doesnt reset when you fail the level, so you see all the lines you’ve made.
Additionally, I’m trying to make the ground look a bit more like grass. I think I have some further iteration to do on this… perhaps using a foliage texture for the brush on the ground.
This post describes how I created this ball trail effect in IsoPutt using the Godot engine. At a high level, it involves using barycentric coordinates to transform a world position into a uv coordinate, then uses a shader on the green with a decal map, two color indicies, and a palette texture in order to draw the trails.
The ball’s touch darkens the green texture underneath it.
Drawing Under the Ball
In order to figure out where on the green’s UV map to draw, I needed to convert the ball’s collision position into a UV coordinate.
In the ball’s _physics_process, we have this block of code:
The BallTrail class is a script extending from MeshInstance. If ball touches the green, it looks for the BallTrail node, and if it finds it, calls draw_on_point.
draw_on_point uses barycentric coordinates to find which triangle the ball is on, then gets the UV coordinates and calls into draw_on_uv in order to draw a spot on the decal map. Note that this function, and draw_on_uv, only really work on the default Godot cube. To extend them to a more general solution, you’d have to find if the brush lies on multiple triangles. IsoPutt’s greens are all secretely just cubes, so it works well enough for this game.
This function uses the MeshDataTool class to access the vertex and triangle data. That class provides a nice interface over raw mesh data access.
Note that the MeshDataTool only works on ArrayMesh, so if you have a different mesh (eg: CubeMesh), then you must convert it to an array mesh first by using some code like this:
Barycentric Coordinates
Barycentric coordinates are a way to represent a point on a triangle. I learnt what they were by reading this great writeup by scratch-a-pixel. In essence, they are a bridge between the world space coordinate system and the uv space coordinate system.
UV Coordinates
Once you can calculate the barycentric coordinate for a point inside a triangle, you can easily find the UV coordinate of any point inside the triangle. To map the barycentric coordinate onto UV space, you must provide the UV points for the 3 verticies of the triangle.
Drawing the Decal
Now we take the UV Coordinate and draw onto a black and white texture called a decal map. White means trail, black means no-trail. The decal map has to be the right size for the size of the mesh we are drawing on. For IsoPutt’s green cube meshes, I use the meshes globalscale * 26.5, such that a cube mesh with a scale of 10x10 has a 256x256 decal map.
Note that the brush size is 4x6. That’s what’s needed to draw squares on the default Godot CubeMesh’s UV Map. Pretty weird. 😐
The Green’s Shader
Now that we have the decal map, we need to sample it inside the green’s shader and select between the regular green color and the trail color. This shader uses a single color palette texture and two different color indicies to reference the green color and the trail color. It does this to integrate into IsoPutt’s color pallete system: swatchd. This system allows you to reference centralized colors by name. It’s a great way to manage colors in a game, because then all the colors are editable from one centralized place.