Skip to content

Voxel and Voxel Data Encoding โ€‹

This page describes how voxels and voxel grid data are encoded in Foxel.

Voxel Encoding โ€‹

In Foxel, a voxel is stored as a 32-bit unsigned integer.

The individual bits store shape, rotation, color, material, and selection state.

Voxel Encoding

BitsPropertyValid valuesDescription
๐ŸŸจSelection bit[0..1]Indicates whether the voxel is selected.
๐ŸŸฅMaterial ID[0..127]Index of the material in the material map.
๐ŸŸซโ€”โ€”Currently unused and reserved.
๐ŸŸฉColor ID[0..255]Index of the color in the Color Palette.
๐ŸŸฆShape ID[0..5]Identifies the voxel shape.
๐ŸŸชRotation ID[1..24]Identifies the voxel rotation using one of the 24 box rotations.

INFO

Rotation ID starts at 1 and cannot be 0. This ensures that solid voxels are always non-zero.

Shape ID cannot be 7, because that value is reserved for run-length encoding.

Voxel Data Encoding โ€‹

A voxel grid is stored internally as a one-dimensional array of unsigned integers.

To preserve voxel positions, voxels are written in a fixed order. To reduce memory usage, Foxel stores voxel data as run-length encoded columns.

Run-length encoding uses a special flag value to indicate that the next voxel value is repeated multiple times:

c
uint rleflag = (runlength << 8u) | 250u;

Sample Code โ€‹

The following examples show the basic principles of decoding voxels and encoding or decoding voxel grid data.

Decoding Voxels โ€‹

c
uint voxel;
vec4 materials[128];
vec3 colors[256];

// ...
// initialize voxel, materials, and colors
// ...

bool isSolid = (voxel != 0u);
bool isAir = (voxel == 0u);

uint rotationID = voxel & 31u;
uint shapeID = (voxel >> 5u) & 7u;
uint colorID = (voxel >> 8u) & 255u;
uint materialID = (voxel >> 24u) & 127u;
bool selected = ((voxel & 2147483648u) != 0u);

bool isValid = (voxel != 0u && rotationID > 0u && rotationID < 25u && shapeID < 7u);

vec3 color = colors[colorID];

vec4 material = materials[materialID];
float metallic = material.x;
float roughness = material.y;
float emissive = material.z;
float alpha = material.w;

bool isRLE = ((voxel & 250u) == 250u);
uint runlength = (voxel >> 8u) & 16777215u;

Encoding Voxel Data โ€‹

The following example encodes a 3D voxel grid into a one-dimensional data array.

c
uint grid[][][];

// ...
// initialize grid
// ...

uint data[];
uint voxel;
uint rleflag;
int sizex = sizeof(grid);
int sizey = sizeof(grid[0]);
int sizez = sizeof(grid[0][0]);
int index = 0;
int runlength;

for (int x = 0; x < sizex; x++)
{
	for (int z = 0; z < sizez; z++)
	{
		runlength = 1;

		for (int y = 0; y < sizey; y++)
		{
			voxel = grid[x][y][z];

			if (y < sizey - 1 && voxel == grid[x][y + 1][z])
			{
				runlength++;
			}
			else
			{
				if (runlength == 1)
				{
					data[index] = voxel;
					index++;
				}
				else
				{
					rleflag = (runlength << 8u) | 250u;
					data[index] = rleflag;
					data[index + 1] = voxel;
					index += 2;
					runlength = 1;
				}
			}
		}
	}
}

Decoding Voxel Data โ€‹

The following example decodes a one-dimensional voxel data array back into a 3D voxel grid.

c
uint sizex;
uint sizey;
uint sizez;
uint data[];

// ...
// initialize sizex, sizey, sizez, and data
// ...

uint datasize = sizeof(data);
uint grid[][][];
int index = 0;
int runlength;
uint value;
int x = 0;
int y = 0;
int z = 0;

while (index < datasize)
{
	value = data[index];

	if ((value & 250u) == 250u)
	{
		runlength = (value >> 8u) & 16777215u;
		value = data[index + 1];

		for (int i = 0; i < runlength; i++)
		{
			grid[x][y + i][z] = value;
		}

		index += 2;
		y += runlength;
	}
	else
	{
		grid[x][y][z] = value;
		index++;
		y++;
	}

	if (y == sizey)
	{
		y = 0;
		z++;

		if (z == sizez)
		{
			z = 0;
			x++;
		}
	}
}