Today I got the last major rendering feature running. I also fixed a bug that has been bothering me for weeks. I didn't expect today, or the last two weeks for that matter, to turn out like they did.
I wanted there to be some vivid color in my world. Much of the scenery is monochromatic, so I wanted flowers. Fields of them.
Normally I write about things that go well. Like this week I wrote a bitset template class, nothing super complex, but a few hundred lines of code. To my amazement it compiled without warning and passed its also-brand-new unit-test the first time. That doesn't happen very often. Occasionally I'll write about things that went pretty badly. Then there are the things that go spectacularly off the rails.
I'm not the type to program by the seat of my pants. Before I start, I always take the time I need to research the tech, figure out what pieces I need, and decide how best to build them. This process has paid dividends consistently over the years although I have occasionally taken grief over it from people who were new. (They came around.)
I estimated that implementing my field of flowers would take two days. I was going to implement texture decals on the terrain, nothing fancy: integer scaling, aligned with the terrain grid, and compass point rotations. I needed one day to modify the terrain program to place the decals and one day to implement the decal loading and rendering. I implemented the decal layout no problem, but when I started on the rendering I realized I had made a fundamental mistake with my plan for rendering. What I had planned wasn't going to work - at all. It wasn't fixable. I had to replan.
Given that the decal layout was already working, I looked into decal projection for a couple hours, because it would give me the additional feature of being able to put damage decals on models, which would look awesome. But looking at the complexity of it, both from a geometry perspective and from a texture management perspective, I knew it would take at least a week to implement and debug. And I also realized that my original decal plan had another big issue: my terrain is 50 pixels/metre so a one megabyte texture will only cover 100 square metres. That's not really the field of flowers I envisioned.
So I thought of using my existing terrain texturing system, adding an additional layer for flowers, and adding a way to lay out the flowers with perlin noise maps. The terrain was looking awesome - I thought it might work. So I ripped out the brand new decal layout code I had just finished writing (after shelving it just in case I needed it again in the future.) It took a day to write the new code and then I ran squarely into something an artist buddy dubbed the "looks-like-crap-factor." Art has to look good, and my red flowers on white sand had huge, ugly pink smears around them where the textures blended.
So I tried a simple variation on the theme: removing the blends from the flowers. That took a couple hours to code, gave me crisp clean edges, and fields comprised solely of squares and triangles. Yuk.
I needed a mask. My first attempt was an algorithmic mask. I thought I would get sweeping curves where the blends were. It wasn't exactly what I wanted, but it would at least be a step in the right direction. It took a few hours to code (mainly because it needed additional data from the pipeline) and gave me flowers with a crisp, clean border and perfect geometric shapes almost-but-not-quite identical to my previous attempt. The "looks-like-crap-factor" was still firmly in play.
I needed to take a step back, so I implemented a new faction select UI screen and the logic on the server to save that selection.
That artist buddy I mentioned had also looked at my latest screenshots and pointed out that my shadows were too dark. If you look at the shadows from a direct light, there is actually quite a lot of ambient light in the shadow. So I also took a couple hours and modified my shadows so that they only affect the direct light, not the ambient light for the scene. My original shadows were nearly black, the new shadows are lighter and look much more natural.
So I had taken a bit of time to think about my flowers. The basic flower texturing was working well, but what I really wanted was a mask around it that had irregular flower-shaped chunks out of it. I had a plan, but generating the data to run the mask was going to require some substantial changes to my terrain texture layout system.
Now I also had a longstanding bug in my terrain texturing that would occasionally cause two adjacent terrain blocks to think that a shared vertex had two different textures on it. This would result in an ugly seam line where the two terrain blocks abutted. Now the reason for this was that each terrain block was textured separately (to take advantage of all 8 threads,) and they overlapped on two edges, but they didn't actually share memory. And the texture reduction algorithm didn't guarantee that adjacent blocks would have all the textures they would need to blend between adjacent blocks.
So I spent a couple days and drastically refactored the terrain texture layout and block texture selection system. The big step forward there was in the implementation of a couple of routines that verify that the output of each step is correct. I spent a lot of time with these routines. Having a lot of data is fantastic for debugging. As I worked on the algorithms I would run each change, and each time it would get through a few hundred more blocks successfully before failing again. It took a couple of days before it got all the way through and I am pleased to report that the new texturing is perfect. No more seams.
While I was in there, I added a step to generate the data I needed for my mask. So with my new mask data ready to go, I needed to get the rendering working. This went better than my first attempt a week and a half ago. I took a hint from old tile-based games and designed a set of 16 alpha tiles I could put around the edge of the field. Now the actual tile masks have chewed up edges, holes, and other details to make them more interesting, but they're all based off this template (transparent parts are blue, and the grid is for illustrative purposes):
It only took an hour before the mask was rendering perfecto. The problem was that the flowers were now rendering with big black splotches. My bright idea to save memory and give each type of flowers a unique mask was to combine the flower texture color data with the alpha mask tiles.
I built the tiles with GIMP, and followed the instructions in its help to export a PNG with both color data and an alpha mask, so I was at a bit of a loss as to why my flowers had black blotches. So I took the debugger to the PNG loading code to see what GIMP was putting out. I put a breakpoint right after the decompression step and discovered to my surprise, a whole bunch of black pixel data. The Save color values from transparent pixels
checkbox wasn't working. I confirmed it by doing a comparison of two files saved with different settings which were in fact binary identical. Luckily, after much Googling about, I discovered the trick
to making this feature work. I exported the new texture and voila, black blotches dispatched. I had big fields of flowers with crisp, but chunky edges.
There was still a rendering glitch though: flowers had flower-coloured or terrain colored single-pixel lines around the edges. I was able to fix that by bringing the texture coordinates in from the edges of the mask tiles a pixel and a half. I didn't lay out my mask tiles as smartly as the wang tiles so I get sampling errors into the next tile in some places.
Now, to that longstanding bug I mentioned. For the longest time I've had faint 2-pixel wide borders
around each terrain tile. Earlier this week I had done a little experimentation and discovered that turning off mip-mapping got rid of the borders. With that clue and a whole lot of Googling about, I discovered texture2DGrad and shortly afterwards, this blog piece
which explained my problem to me. Wherever there was a discontinuity in the texture coordinates (ie at each terrain block edge) the video card would select the wrong MIP level for two pixels (that blog explains the why of that.) It took about 10 minutes to confirm I was on the right track and an additional 30 minutes to tweak it to perfection. My terrain is now mipping, seamless, and beautiful.So today marks the end of my big-rendering-feature development!
Now I get to move on to finishing up the gameplay and getting that alpha release out. It's an exciting day.