I've started a new article (or series, most likely) about random level generation from a practical perspective. Instead of taking some random topic and elaborating on it at great, windy length, I'm starting with a basic level description instead, and working from there toward a finished script. Along the way it'll discuss the techniques involved, and hopefully the context of a practical real-world level might demonstrate the techniques better.
At any rate, I came up with a level description that would hopefully demonstrate lots of techniques. I wanted mountains and flatlands, and drawing upon my memories of the Rims in Billings, Montana, I wanted a long, high cliff bisecting the entire map. And I wanted a road winding up the cliff to a fortress at the top. So I sat down with some noise generators and the height-map editor, to figure out what I had to do, and discovered that a long cliff isn't as easy as I thought given the functionality of the engine at that point in time.
So, I built a very quick, very simple little module extension to libnoise. I call it Fault. Basically, it implements a simple bisecting plane that acts as a threshold, so that the module returns either high or low (specified by user, default to 0 and 1 respectively; should probably default it to -1 and 1 to better fit with the rest of the library). On a 2D mapping of a module, I simply set the bisecting plane so that it appears to be a bisecting line, and I'm in business. I didn't like the sharp-edge, so I added a fall-off parameter that smooths it out. However, there be weirdness...
A straight cliff is boring, so in order to spice it up a bit I need to chain it with a turbulence modifier. However, turbulence plays hell with the fall-off, since I am perturbing the input coordinates of the fault module. I can't really see any way to really get a perturbed cliff with a smooth gradient change, other than performing a 2 pass--perturb a sharp fault into a buffer, then pass a blur across it to dull the edges. Which sucks ass, because it breaks the elegant chaining built into libnoise.
To sort of mitigate the chaining breakage, I implemented another libnoise module extension that maps the input into a user-specified buffer. It's a bit of a hack, since it maps a 2D buffer of my CFloatArray2D variety (the same data-type used by the scripting interface for buffer and heightmap manipulation), however for my strictly 2D purposes it works fine. It allows the user to specify a float buffer, and a range to map the buffer to (repeating), then the module can be used as part of a libnoise module chain just like any other.
So I can generate my perturbed, blurred fault map in one step, assign it to a Buffer module, then use that module as a source in further heightmap generation. Which works, but I hate having to split it into steps.
In the process of trying to figure that problem out, I came up with a third libnoise extension, the TurbulenceMasked module. Basically, it is identical to the basic Turbulence module, but it accepts a control module to be used as a power modifier for the turbulence. This control buffer typically should map to the range [0,1], and the value at the given (x,y,z) is multiplied by the turbulence module's own power modifier. This allows me to mask out or phase out turbulence in areas of a map where the control module maps to 0, and to apply full turbulence at areas where control maps to 1. And, of course, gradients in between.
The purpose of this was that I would generate a straight fault cliff, then map an externally loaded turbulence mask to a buffer module to mask out a portion of the cliff in the center of the map. This would cause turbulence to be applied to the cliff, but the turbulence would get less and less as we neared the location in the center where I was going to put the fortress and the road. These portions would later be blended into the final heightmap from pre-generated pieces edited in the heightmap editor, and the phased-out turbulence would make the edges match up.
Some time tonight, I reckon I'll post the code to the three modules in case anyone can make use of them.
At any rate, the next articles are well underway, so hopefully in the next few weeks you'll start seeing them appear.