This page is about an insight into how one can interpret a grid of data values in order to produce graphics. I use this (and variations) all the time in my various game projects (so does, I suspect, Blizzard Entertainment...). I recently found Sean Howard's excellent page, and this writeup on related stuff inspired me to share some of my own thoughts on this.
All of this stuff relates to drawing 2d (and even 3d) tiled graphics in aesthetically pleasing ways, specifically how to deal with the boundaries between tiled "materials".
|for example something like this...||based on data like this...|
The term "16 cases" refers to the fact that there are 16 cases to deal with when drawing something like this from tiles / pieces.
Up until a few years back I was under the impression that there were two main different and incompatible ways of interpreting the underlying grid data that led to either 16 cases or 256 cases. This was significant because coding 16 cases is manageable, while coding 256 cases is quite a pain in the neck. This sort of relates, in 3d, to Marching Tetrahedrons (16 cases) vs Marching Cubes (256 cases).
Of course there are lots of different ways one can use all of this, and surely more ways of interpreting the data, but I want to share a key insight that has helped me out quite a bit, namely that the 16 cases and the 256 cases are simply interpretations of the same thing.
Staying, for the moment, with the assumption that one can interpret the data in two ways, the 16 case version is based on interpreting the values in the data grid as "corners", and hence a corner can be either ON or OFF, giving us 4^2 = 16 cases:
If, in your specific application, you can get away with rotating or mirroring the various images, then you can get away with only needing these pieces:
The other way of doing this is instead to interpret the values in the data grid as "areas" or "tiles". In order to deal with boundary cases one would then need to take all 8 neighbouring data values into account (the "edges" and "corners"), yielding 2^8 = 256 cases:
This is really bad news, as it requires lots more code to deal with all the cases, as well as more source art. Sean Howard points out that you don't necessarily need 256 pieces, but it is still "more than 16".
I will take some time to say why I think the differentiation really matters at all, namely how you logically (in your Model) view the data in your grid. Is it more logical to view it as corners, or more logical to view it as areas / solids? This greatly depends on your application domain.
In some (actually most) games that I have made where the logical Model is grid based, I like to think of the data in the grid representing areas or volumes of space, like this:
Having to deal with the 256 cases in the visualization of this logical model as a "penalty" for wanting to have the data represent areas / solids seems like a terrible price to pay. Luckily this is not the case.
The good news is that it turns out that both things are really the same. What this means is that you never have to deal with more than the 16 cases version no matter how you choose to logically interpret the data.
Observe the following:
|these are "corners"...||these are "areas"...|
|corners shifted half a tile...|
The trick is simply to realize that you can, for visualization, interpret the grid values as "corners", and then simply shift the visualization by half a tile. This gives you the best of both worlds, namely the ability to logically use the data as areas / volumes but still be able to get away with the reduced cases for visualization.
Free Pixel Maze
Inspired by Sean Howard's Free Pixel Project I did this (the art is all copyright Sean Howard, with the tileset being a derivation of pixels from here):
My mockup maze (coded). Note that the result isn't exactly like the original inspiration, as Sean's version has "deeper walls".
Here are the tiles I used, at 16x16. Note that I have only used 13 of the 16 cases here; this is due to the nature of the maze I have created, in which certain cases simply don't occur (opposing corners and empty floor). I also just realized that the "top of walls" tile isn't required either, the one in the center of the 3x3 chunk of wall, so really 12 of 16 cases here...
Here is an example of multiple layers / materials.
The teal is the background, the greenish layer is the bottom one, and the tan layer is on top.
The whole data grid is still two dimensional, i.e. there are no multiple layers of data.
The colored dots on the grid intersections correspond directly to the data values in the grid.
The data values are however interpreted as being logically "stacked" (i.e. 2 is on top of 1, 1 is on top of 0...) and this corresponds to the order you draw the materials, from bottom to top. This approach creates a certain amount of overdraw, in all cases where the layer on top doesn't completely obscure the underlying one.
A bit of overdraw (the pixels that differ in color from the previous image). When the top layer overlaps but does not completely obscure the bottom one, the bottom one is still drawn (the completely filled tile case) and then the top layer is drawn as well.
There are of course many ways to do this, but in this example I decided that walls going "down" from any given floor / material belong to that material, and the floors are then "unspecified", i.e. alphamasked out. This way you can put materials on top of other materials.
|The graphics for the bottom layer...||The graphics for the top layer...|
|"That pink" is used as the color key / alpha mask.|
Here's an update with stippled shadows.
16 cases in 3d
Here is a somewhat glitchy / unfinished project that uses these techniques in three dimensions.
Using the 16 cases principle three times: once for the upper walls, once for the floors, and once for the lower walls / pits.
Another visualization of the data (from the level editor) that drives the above 3d shot.
The pieces used to construct the 3d visualization.
I hope this stuff has given you some insight into how to visualize your gridded datasets. Please feel free to contact me with any questions or comments: johno(at)johno(dot)se