Wednesday, April 25, 2018

Leaving Track Prints

I am currently developing my, still unnamed, indiegame. This game is a 2D top down tank fight. And its main gimmick is the 100% destructible world.

Nice destruction if I say so myself, but notice that the tanks don't leave track prints. Today, I will write about implementing track prints that the tanks leave behind on the terrain as they drive over it.

The first observation to be made here, is that there are a lot of them, for each tank. That means generating and rendering thousands of them, if not tens of thousands or even hundreds of thousands. This immediately tells me that they can't be rendered individually. I need to apply a technique called Instanced Rendering.

Rendering

In instanced rendering, all instances share the model vertex data and have some per-instance data to make them unique. This per-instance data is typically a transformation matrix, but can also include other things like colour if need be. In my case, the per-instance data can be particularly compact because I work in two dimensions.

All the prints will be identical, except for two things: their position in the world, and their orientation. So in theory, three values would be enough: an x and y coordinate, plus a rotation angle. But personally, I find that defining rotation with a vector, like Chipmunk2D does, is more elegant. Hence, I will feed OpenGL a 2D vector for position and 2D vector for orientation.

The next thing to consider is the life-time of the prints. If we create a new print at frame N, then we will need to render it at that frame N and all other frames after it. Up until frame M (M much larger than N) where we need to evict this print to make space. After all, we don't want to run out of resources by creating arbitrary many prints.

The fact that I progressively create the prints, and reclaim resources for the oldest one, leads me to the convenient solution of ring-buffers. We create a Vertex Buffer Object to hold the shared model data plus N instances. When creating instance N+1, we will reuse the slot at position 0. Each frame, we will only write the VBO at the slots that got new data that same frame.

Generating

Having the rendering covered, leaves me the problem of generating the prints. This problem is trickier. The tank has many track-segments touching the ground at any time, all leaving a print. When the tank drives straight, those prints all superimpose, so you would only really need to generate one of them. But when the tank turns, this won't work, and gets worse if it turns-in-place. See below what happens if you leave one print at each side of the tank. The tracks look fine, until the tank does a 180 degrees spin.

And it looks particularly bad if the tank gets bumped hard and moves sideways. I haven't really cracked the problem of generating proper tracks yet. I think the root of the problem lies in the fact that the game's simulation has no concept of the track links. The tank it self is just four rigid bodies, one chassis, one turret and two for the L/R tracks. The links of the tracks are just an animation effect.

So the generation of track prints needs some more work. I'll report back when and if I solve it.