I was excited to get our content creation pipeline developed to the point where I could begin to prototype some game mechanics and actually put together something playable. My goal was to be able to create a terrain in the Editor, publish it, and have the game load that data and display it identically. I also wanted to make sure that if the map maker used any custom textures or brushes that other players didn't need to have those assets installed. Each map should work all by itself so long as it doesn't rely on a mod the player doesn't have.
The Editor uses a fairly involved solution for its texture painting component. In order to save resources on rendering and also to achieve my goal of making maps self-contained entities, I decided to "flatten" texture data and embed it in the map file. This was a fairly straightforward operation that basically duplicated the Shader's functionality on the CPU and wrote the resulting texture to a file instead of rendering it to the screen. Everything appeared to be working great.
Well...looks like that wasn't working as well as I thought....
Here's the problem. A terrain chunk in the Editor doth not equal a terrain chunk in the Game. In the Editor, each chunk has two sets of UV coordinates. The first set is laid out as if the chunk were stretched onto a piece of paper. This is used to determine where you, the user, has clicked on the chunk when you paint a texture. The second set is laid out as if each 1-meter by 1-meter square were stacked on top of each other. This allows us to declare regular ol' textures in our terrain lists and have those textures tile on the chunks automatically.
Remember back when I said that the publishing process mimicked the behavior of the Shader but did it on the CPU and wrote to a file instead? Scroll up if you forget...I'll wait here. Good? Good. The CPU calculation wasn't taking the tiling of the texture into account. It was simply using the regular ol' texture from our list and blending from that. The end result is that the scale in the Game appears incorrect because the textures appear larger than they do in the Editor.
So how do we solve this? Read on!
Before we do all of our color blending to build the final texture for each chunk we need to recreate the tiling, but do so as a flat texture (instead of relying on the chunk's second UV set to do it for us). This is a two-step process. First, we need to scale down our base texture. Second, we need to take that smaller texture and repeat it over and over again on a new texture. We need to do this for each of the 8 terrain textures a map can use and then we can use those to build the final texture.
There is an old but great article and example code on re-sizing a texture with Bilinear Filtering here. The basic idea is to iterate the pixels in the source image and sample the surrounding color data in order to come up with your final pixel color. Luckily, this was the hardest part of the process. The Unity API already exposes some functionality for setting pixel data in "chunks" across a texture. (I feel like I overuse the word "chunk" but it's so good at describing so many things. I just can't help myself. Chunk, chunk, chunk.)
After applying this to the publishing pipeline I ended up with a much better result. Initially I was using a 512x512 texture resolution which resulted in the original textures being reduced to 32x32. The result was too blurry so I upped everything to 1024x1024. Here's what we have now.
There are still some things to be worked out. For one, the level of Anisotropic Filtering in the Game isn't high enough which is why you see the texture blurring towards the edge of the camera viewport. This shouldn't be too hard to fix and that camera angle probably won't stay the same as we move forward anyway. Secondly, because the texture data is encoded as a Base64 string and included in the map file, those files are becoming a bit large. (It was marginally acceptable when the textures were 512x512 but increasing the resolution has ballooned the footprint significantly.) I've got a few options to explore here. For one - we currently aren't using the Alpha channel of the texture for anything so I could switch the texture format from ARGB32 to RGB24 and see if that results in less data being written. Sadly, Unity doesn't allow you to dump texture data to a byte array if the texture is compressed to a DXT format. Another avenue, and I may end up doing this anyway, is to employ some kind of compression on the files. This type of solution could extend beyond map files to all data files we ship with the game so for now I'll hold off and explore this farther down the line. Certainly if we decided to go with a compression solution that isn't an open standard (and I doubt we will, but you never know) we'll make the tools available to decompress everything.
So with that I'm finally able to start making a game! We've got so many great ideas and I can't wait to start implementing them and showing all of you the things we've been planning on paper for months.
A new version of the Editor is available for download. This version includes map publishing. Please try it out and leave some feedback in our forum. Also, don't forget you can follow me on Twitter @ThisIsKelso.