Part 1/ Data Structure
First of all, before displaying anything, the cubes in the game need to be stored somehow, and depending on what you want to do with them, they will be stored in different ways.
For example, if your game only uses a few cubes (let's say a few thousands cubes) that can be physically animated, their positions, speeds and rotations can be stored individually, so they can be easily updated.
Example of Rigid Physics Simulation from Blender |
However, in our game, we are not interested in the tracking of individual voxels (except for the physics engine of the game, but we'll discuss this another day...). Our voxels don't live on their own, they are always part of larger group that describes an object in the game (for example a character or a tree), and they are positioned regularly on a three-dimensional grid.
Thus, it makes sense to store them on a 3D matrix.
A character in its 3D matrix. |
Okay, one could argue that nowadays computers are packed with memory anyway, so there is no point in trying to save memory. Indeed the above character is stored on a 16x16x16 grid, which represents 4 kilobytes of memory if each voxel is encoded on a byte. This is very small when compared to the several gigabytes of RAM computers have nowadays!
But what if the totality of the game-word is saved this way? If the player is 16 voxels large, we want the world to be at least 100 times bigger, i.e., 1600 voxels large. Cubed, this correspond to 4096000000 voxels, i.e., 4 gigabytes of memory, which fills up most of the available RAM, if not all of it. Of course, we probably don't need the world to be a thousand of cubes high, so this could be reduced, but what if we want a larger world that can extend as far as the horizon?
One solution could be to use an octree structure, which uses encapsulated hierarchical grids to store voxels only where they are.
Octree Structure of a Stanford bunny! |
Another solution is to regroup the voxels in chunks that can be streamed in and out of the RAM to the hard-disk, where a lot more space is available (several hundreds of gigabytes).
A chunk of world in Minecraft. |
In our game, we chose an alternative method for storage after noticing that:
- The ground is going to be mostly flat, so no need to store it as voxels, just use a big square and apply a texture on top of it. And no need to store what's underground either, we are never going to see it!
- Objects in the world are always on the ground and they can only move on the ground (basically, the movements are in 2D!). Each object can be composed of a various number of voxels, but it's usually small (like 4096!).
An object has a position which states where it should be placed in the world. And this position does not have have to be an integer, so the object can move smoothly in between "world-voxels" which wouldn't be possible otherwise.
Also an object can have a rotation, and the voxel can be rotated with it. This wouldn't be possible in standard chunk storage either.
And while we are it, why not allow for different object sizes (corresponding to different voxels sizes). This way a forest can be made out of a single tree model, without too much visual repetition (see one of the previous video below)
In the end, the voxels are stored on a regular 3D grid, that can be positioned, rotated and resized in any way. Each voxel is encoded on a byte (8 bits) of memory, so there can be 256 different types of voxel (255 if you exclude the empty type). At the minute, only 40 of these states are used, which means there are only 40 different colors, but these colors can be redefined per object. Meaning an object can use a complete different set of colors to an other object.
Everything is voxels! |
I can believe how long this post already is!
Okay I'll stop here, in part 2 we'll discuss how to display all these voxels.
Nick.
No comments:
Post a Comment