Rendering Terrain Part 29 – Loading Fewer Files

Last post I offhandedly remarked that thinking I’d be done refactoring for today likely meant I wouldn’t be. Perhaps unsurprisingly, that turned out to be true. To be fair, when I wrote that post, I had forgotten that I had plans for last night and wouldn’t be getting any work done. If I had remembered that, I probably wouldn’t have been at all optimistic about finishing for today. Still, I’m a bit disappointed with how little work I got done and really how little work there is left to do. If I just sat down and put a full 8 hour day into it at some point this week, I’d be done and on to the next project by now. I’m pretty much done writing each of the new classes I noted in the last diagram from last post: Frame, Device, ResourceManager, Material. I just need to rewrite the old classes to work with the new. That’s really not that much work, I just need to focus for a bit. I shouldn’t have any issue completing it over the weekend, which means the last posts on this project should happen next week.

As for today’s post, I wanted to talk about how I’ve been loading textures. When I started on the project, I wanted a simple solution to load a height map file that I was storing as a PNG file. LodePNG was a pretty simple solution to that problem. All I had to do was include lodepng.h and lodepng.cpp, then the code to load a file was nice and simple.

unsigned char* data;
unsigned int h, w;
// load the black and white heightmap png file. Data is RGBA unsigned char.
unsigned error = lodepng_decode32_file(&data, &w, &h, fn);
if (error) {
	std::string msg = "ResourceManager::LoadFile: Error loading file " + std::string(fn);
	throw GFX_Exception(msg.c_str());

As far as this project has been concerned, that’s really probably good enough. The problem with this, however, began to rear its ugly head when I started wanting to load more data from files. I played aroud with the idea of loading normals for the height map from file, although I never really got that to work well. Then I wanted to use a displacement map, which was just another height map, but then using a normal map for that actually worked well for. So I was now loading three files and then I actually combined the height and normal maps for the displacement map into one texture.
Then I wanted to add bump mapping and diffuse texture mapping. Well, that’s two more files to load. Then I said, ‘let’s have four different terrain textures to choose from, based on height and slope.’ So that’s eight files. Then I decided to use depth maps to solve the problem of transitioning between the different textures. That was four more textures.
Worse still, to save on texture sampling, I wanted to use the alpha channel of an existing texture for the depth map. But because I was doing the bump mapping separately from the diffuse mapping, I need the depth map in both the normal maps and the diffuse maps. And the way my file loading was set up, this meant loading the depth maps from file twice!
Obviously I could get around the duplicate file loading with a new function that loaded the depth map, then loaded the normal or diffuse map and combined them with the depth map, but that’s yet another function with a pretty specific purpose. I prefer to be a bit more generic in my coding so I can try and reuse whatever I can.

What I really wanted was to combine the depth map with the normal and diffuse maps in the PNG files themselves. This way, I’d need far fewer file loads and far less custom work to combine these things at run time.
I wound up installing GIMP and found some instructions on how to take a grey scale image and copy it into the alpha channel of another image. I was then able to export to PNG and simplify my file loading quite a bit.
Of the methods from that StackExchange answer, I found method 1 worked the best. For both methods 2 and 3, I got a weird issue where part of the RGB channels were zeroed out. This seemed to correspond with where the depth map value was also zero, so when you looked at the texture in an image editor with the transparency on, it looked fine, but if you turned off the transparency, you’d see something weird:
Again, method 1 didn’t give me this problem. That may simply mean I was using the other methods incorrectly, but I only needed one method to work anyway.

So, before messing around and combining my depth maps with my other maps, I was loading a total of 19 files, including duplicates. After adding the depth map to each normal map and diffuse map, as well as combining the displacement map’s height and normal maps, I am now down to 10 texture files to load.

It turns out that when I am ready to replace lodepng with a more robust file loader, I should be able to reduce my file loading quite a bit further. I’ve seen a number of references to DDS as the industry standard in games these days. Microsoft has made a library for loading DDS files available on GitHub. Apparently the DDS file format actually handles texture arrays, so I could actually combine all of my terrain texture data into one file and load it directly. Or perhaps two files, one for normals and one for diffuse maps. That would make much more sense from the perspective of having a generic Material class. Currently, I have made mine a custom TerrainMaterial class to load all of the files and build the texture array. It would make a lot more sense if my Material class just took a normal texture and a diffuse texture, and was capable of handling texture arrays for each. In terms of the code to support that, it would just be a matter of changing the array size in your texture description and the type of your Shader Resource View (SRV).

I don’t know when I’ll integrate that. Maybe I’ll make it a sub goal for my next project since I know I’ll be reusing at least some of this code.

That’s all I’ve got for today. Next post should almost certainly be discussing the code from my rewrite. I haven’t uploaded the changes mentioned here yet, but you can still find the code at GitHub.