Now that I've fully recovered from the weekend celebrations, I spent the morning wrapping up the terrain generator for Tech Arena. It's a topic that's been discussed in great detail so I won't get into the basics but I am going to cover what I've been doing with the core generator architecture. After revising the terrain generator a few times and realizing it still wasn't near what I wanted, I decided to create a more flexible, modular solution that can be easily modified and extended if and whenever necessary.
Originally, I planned on using simple noise generation while allowing for geometric modifiers to define areas in which certain effects could be applied to the output. This is just miserable. Noise generation only gives you something uninteresting to start off with and geometric modifiers look, well, geometric. I still like the idea of being able to create a geometric "concept" for a map or even being able to generate geometric patterns such as Voronoi Diagrams. Even noise has its place, but just doesn't cut it alone. Here's an example, under the new system, of a diagram being processed into something hopefully useful:
This is a 64x64 height map and what I tend to use for terrain "chunks" that are streamed in as the player moves around the map. There are (currently) three levels of detail that are loaded in, each 8X the resolution of the last. The first defines areas of 4km, 512m for the second and 64m for the third. Chunk edges are expected to match up somewhat but blending is applied to seem together any breaks. So, we can be a little more liberal in what types of filters can be used and not worry (too much) about breaking seems.
Each of these detail levels can use a seperate generator, each containing sets of channels, filters and modifiers. Channels are simply just raw data that filters may read and write to. A filter type is defined with its process (if it generates anything) along with it's source and destination iterators. These are generic iterators that simply define how to find the first element/node pointer and how to get the next element/node pointer. So the channel may be of any data type or data structure. It can even be a result set generated by another module or system. The filter, once instantiated, could then have modifiers added to it that tell it how to combine the source data with the generated data (if any) before writing to the destination. Here's a quick overview of the generator used to create the above images:
First, we start with the diagram. This is a whole other system in itself and is used for precalculating and quickly querying diagrams of arbitrary polygons. Such a system comes in handy for things like AI, rendering and, for what it will be used for here, Voronoi diagrams and terrain editing/generation. When querying an area, the diagram returns a generic iterator which enumerates the contained shapes. This iterator is passed to a "simple" filter which does nothing but read a source and write to a destination. Since enumerated values (as seen on the far left) aren't very interesting, an ElevateByID modifier is applied which gives a value in the range 0.0 to 1.0 based on the current shape enumeration and the coordinates of the point being iterated. A curve modifier (similar to Photoshop curves) is applied to "bubble" out the terrain, giving it a rounded shape. A MultByID modifier generates a list of numbers on initialization (for the number of shapes in the diagram) and multiplies each "site" by each corresponding number. This gives us psuedorandom elevations for each shape. Some wider passageways would be nice, so the entire map is subtracted by 20 and a Floor modifier clamps the values to zero, forming spaces between the geometries as seen in the "Filtered" channel, second from left.
Some noise is generated (midpoint displacement) and multiplied by the desired elevation to be combined with the diagram output. The combine filter may take raw data from a channel and use that as "generated" output which may then be combined (by modifiers, Add in this case) with the source. This is similar to how the displacement filter works as well (only the data being combined is displaced and interpolated).
A second noise filter is used to generate a displacement map which is used twice to displace the map, once diagonally from top-right (Disp Out) and once from top-left (Output). Curve is used again to sharpen the output (really, just makes for a cooler looking heightmap for use in the journal) and finally Clamp is used to keep the values within an easily renderable range (you can see where values fall out of range in the other channels).
There are a number of other modifiers such as Blend, Overlay and BinMultByID which multiplies an enumerated site by a percentage controlled mix of 0's or 1's. This will be used in conjuction with Voronoi diagrams to control the sparsity of sites. Also, since there's some margin of error allowed in the seeming of the maps, I'll be implementing parameter maps that contain ranges of filter and modifier parameters. The parameters for an area are generated and blended with neighboring areas so that maps won't be monotonous over large distances.
I'll be implementing a terrain editor in-game that will allow arrangement of channels, filter and modifiers as well some editing of diagrams. I may also put together some diagram generation routines. I thought it would be cool to have a maze generation algorithm create diagrams which could then be filtered into maze-like systems of mountains. A lot of ideas stem from this and not all seem to add to the overall experience. I will likely release the filter and modifier API so that other programmers can build their own components. TechOS already has an extensions directory where DLLs can be added to the application so it wouldn't require much, if any, extra work.
I'll be spending the rest of the week incorporating this into the app so stay tuned for a screenshots. ;-)