First I have to mention that three weeks ago, I attended an IGDA meetup in my town and it went pretty well. I got to catch up with a friend I met at a previous meetup, and even managed to meet a developer who I only previous knew about on YouTube. The topic for the meetup was game jam survival techniques. It provided some insight on making games under a very tight deadline, and we got to hear some humbling experiences from people that have gone through many of those battles and how they managed through it.
It’s reassuring to know that completing the game is not the only important goal, but moreso knowing how to work as a team and re-evaluating your strengths and weaknesses. The bottleneck that developers overlook, is not their game’s performance but their own performance. Figure out what you can cut to reduce your time in meeting your goals is important. You can shave off some milliseconds in a scene render, but it’s better to cut down on days or weeks in your work for the sake of completion.
Last time I mentioned that I wanted to make a racing game. It will be an off-road racing game with an emphasis on speed, long courses, and arcade-like action. So if the DiRT series is considered to fall somewhere between sim and arcade, I consider my own game to be between arcade and DiRT. I’ve had a few other game ideas in the past, and mostly that puzzle game which I finished but lacked the will to polish it, add more levels and modes, etc. I would want to get back to doing that eventually, but for now I’d like to continue on my main project with a terrain engine and editor for the other game.
Keeping this in mind I decided to take a more headstrong approach in my progress. Working too long at one feature is mind numbing and demotivating, and I seem to work better rearranging and re-organizing my task priorities, to ignore the ones that seem more wasteful. Guess this can be considered agile programming for one person.
A start on geo clipmaps
With that said, I wanted to shift focus on finishing the initial bugs and shortcomings on the terrain. It was tempting to start a map editor alongside it, but that had problems of its own with me trying to get viewports playing nicely with graphic device states and other controls (WinForms definitely isn’t my strongest point). So I continued headstrong into making my terrain look and perform better.
Previously I said that I would start making a basic clipmap system for the terrain. So that’s what I’ve worked on the past few days, and man did I sure make a lot of progress. Remember that desert scene that I had at the beginning? I went back to square one and tested some clipmapping out with wireframes.
First I created a new InnerClipmap class which is responsible for setting vertices for only a portion of the terrain given a position which acts as the center. The Terrain class is responsible for keeping the entire heightmap in memory and drawing whatever the clipmaps send to it. I made the camera and the dude be the center in various tests, and the terrain meshes move in stepped increments along with it.
You cannot see it here, but all those grids are solid squares- they overlap each other in the center. Obviously we cannot draw redundant vertices, as it’s wasteful and produces ugly Z-fighting.
The next step was to “punch” a hole right through those outer clipmaps. I’ve read a few articles about geo clipmaps but I didn’t follow any exactly to a tee. For instance the GPU Gems article suggests to split the clipmaps into parts, and moving those parts individually. That seemed too thorough to me- all I know is, if each clipmap increases in size by a power of two, each hole will be (roughly) between 1/4 and 3/4 of the grid’s width and height dimensions. So, if the vertices fall in those areas, they won’t be added to the index buffer.
Bam, holes are cut! But they are too big- some sides don’t align completely, so inner rows and columns need to be inserted according to some specific positioning rules, which I didn’t get to fix until much later. There’s also another thing to note. I only update the vertex and index buffers when the clipmaps should move, instead of re-calculating every frame. This wasn’t as tough to accomplish. I store its previous center position and if the current position passes a threshold (usually the grid cell size) we re-draw the map.
I went back into triangle fillmode and re-loaded the scene. I intentionally chose a camera position and direction where the clipmaps have little space in between, but there’s still the matter of broken seams as a result of height values not being averaged out where they meet at the ends. But still, things are starting to look good again.
I also decided to switch to a heightmap with more consistent rolling hills and mounds. Basically just a tileable Perlin noise texture that I found online. This allowed me to make the terrain much larger.
Textures, trees and more
Now, there is where I decided to move in another direction for a while. Since I’m now starting to do more serious work with terrain, I figured, it needs a few subtle things to make it even better. So for the first time, my scenes have depth fog. I hardcoded some values for color and exponential depth in the composite shader, so everything that is rendered in the previous passes gets the fog treatment. These values will be user-assignable later on.
Also, I got tired of seeing deserts so I switched to a grass texture. It’s the same one found on Riemer’s tutorial, which did help cut time on getting started with heightmaps. But, man, does it look terrible in the distance.
I solved this problem with a quick fix: sampling the same texture twice on the GPU. The first time, scale the texture small for close distances, and then scale it for farther distances. Then blend those with a similar formula used for the fog. The result is that the ground looks less repetitive in any area.
There’s a basic but very useful function in my terrain code, adapted from an official XNA sample called Tank on a Heightmap. This was another real timesaver for me. Basically, it “pins” down a tank to the surface height no matter where it is on the map. It also adjusted the angle based on the interpolation of normals, but that’s not something I need right now. But I consider this a crucial step to efficiently building worlds and levels for my game.
How do I start using it? Well, for starters, all the previous screenshots are from builds where the dude is always walking on the ground. Basically, I use a function called Terrain.GetHeight(Vector3 position) and it returns the height, which I reassign as the position’s Y component. But wait, I can use it to place many objects on the ground in a loop, and what does a landscape need? Trees! I looked into a folder of tree models that I downloaded for free online, and loaded a pine tree into my project. Added a random with a seed, and set it to a loop. That was fast.
All of a sudden, my efforts became a lot more fruitful. Looking at these trees, even if they’re not real, gave me a feeling of enlightenment and a better focus. I have a much clearer sense of direction for how to approach this project. Here’s another view, with depth of field added. (The speck in the lower left clouds isn’t a glitch- it’s a floating box I forgot to remove)
Unfortunately, this silver lining has its cloud. What you don’t notice from these screenshots is that the trees (or rather, their polygon and vertex count) has slowed the rendering to a crawl. I place anywhere between 1000 and 1500 instanced trees and everything is now running chuggy. At its worst it can drop somewhere below 10 FPS. This is largely contributed by the cascaded shadow mapping, because it re-draws a lot of those trees for every split. But hey, now at least we’re getting somewhere. The engine is now getting a lot of heavy duty work, so it’s time to strengthen it and put it through its paces. Part of the problem is due to the tree meshes, though. Each one is 2992 triangles, heavy on the polycount if they are to be drawn a very large number of times.
I could find a better tree that’s much lighter on polygons, but still, some optimizations are in order. LOD grouping and occlusion culling to the rescue? I know I can brute-force cull 10,000 simple cubes with no problem, so for now brute-force culling isn’t slowing me down. So yeah, on to mesh LODs for one of the solutions, and then to get into some serious editing.