Link Search Menu Expand Document

Qubicle Binary File Specifications

Qubicle Binary is a widely used vanilla exchange format that stores voxel data in binary form.


Table of contents

  1. Data Structure
    1. Header
    2. Voxel Data
  2. Parsing
  3. Visibility Mask

Data Structure

Qubicle Binary (QB) supports the storage of multiple separate voxel matrices in a single file, as well as optional compression (RLE).

The first 24 bytes are used for the header which includes following information:

  1. Version (4 bytes): stores the version of the Qubicle Binary file format as major, minor, release, build. Current version is 1.1.0.0
  2. Color format (4): Can be either 0 or 1. If 0 voxel data is encoded as RGBA, if 1 as BGRA
  3. Z-Axis orientation (4): Can either be 0 = left handed or 1 = right handed
  4. Compression (4): If set to 1 data is compressed using run length encoding (RLE). If set to 0 data is uncompressed.
  5. Visibility-Mask encoding (4): If set to 0 the A value of RGBA or BGRA is either 0 (invisble voxel) or 255 (visible voxel). If set to 1 the visibility mask of each voxel is encoded into the A value telling your software which sides of the voxel are visible. You can save a lot of render time using this option. More info about this in the section visibility-mask encoding
  6. Matrix count (4): How many matrices are stored in this file.

Voxel Data

All following data is voxel data. For each matrix the size and position is stored and afterwards the voxel data. How that data looks like depends on the settings in the file header.


Parsing

The following source code is written in pseudo code and hopefully understandable by any coder no matter the programming language he uses. How you store the data found in the file is up to you. In this example the raw data is stored in a simple one dimensional array. Be careful with the variable types. All of them are unsigned 32bit long integers, except for the position vars.

function loadQubicleBinary(filename:string) 
{
	var version:uint32;
	var colorFormat:uint32;
	var zAxisOrientation:uint32;
	var compressed:uint32;
	var visibilityMaskEncoded:uint32;
	var numMatrices:uint32;
	var f:file;
	var i:uint32;
	var j:uint32;
	var x:uint32;
	var y:uint32;
	var z:uint32;
	var sizeX:uint32;
	var sizeY:uint32;
	var sizeZ:uint32;
	var posX:int32;
	var posY:int32;
	var posZ:int32;
	var matrix:array;
	var matrixList:array;
	var index:uint32;
	var data:uint32;
	var count:uint32;
	const CODEFLAG = 2;
	const NEXTSLICEFLAG = 6;

	f = fopen(filename);
	version = fread_uint32(f);
	colorFormat = fread_uint32(f);
	zAxisOrientation = fread_uint32(f);
	compressed = fread_uint32(f);
	visibilityMaskEncoded = fread_uint32(f);
	numMatrices = fread_uint32(f);
	matrixList = new array();
 
	for (var i:unit32 = 0; i < numMatrices; i++) // for each matrix stored in file
	{
		// read matrix name
		nameLength = fread_byte(file);
		name = fread_string(file,nameLength);

		// read matrix size 
		sizeX = fread_uint32(file);
		sizeY = fread_uint32(file);
		sizeZ = fread_uint32(file);

		// read matrix position (in this example the position is irrelevant)
		posX = fread_int32(file);
		posY = fread_int32(file);
		posZ = fread_int32(file);

		// create matrix and add to matrix list
		matrix = new array(sizeX*sizeY*sizeZ);
		matrixList.push(matrix);

		if (compressed == 0) // if uncompressd
		{
			for (z = 0; z < sizeZ; z++)
				for (y = 0; y < sizeY; y++)
					for (x = 0; x < sizeX; x++)
						matrix[x + y*sizeX + z*sizeX*sizeY] = fread_uint32(file);
		}
		else // if compressed
		{ 
			z = 0;

			while (z < sizeZ) 
			{
				z++;
				index = 0;

				while (true) 
				{
					data = fread_uint32(file);

					if (data == NEXTSLICEFLAG)
						break;
					else if (data == CODEFLAG) 
					{
						count = fread_uint32(file);
						data = fread_uint32(file);

						for (j = 0; j < count; j++) 
						{
							x = index mod sizeX + 1; // mod = modulo e.g. 12 mod 8 = 4
							y = index div sizeX + 1; // div = integer division e.g. 12 div 8 = 1
							index++;
							matrix[x + y*sizeX + z*sizeX*sizeY] = data;
						}
					}
					else 
					{
						x = index mod sizex + 1;
						y = index div sizex + 1;
						index++;
						matrix[x + y*sizeX + z*sizeX*sizeY] = data;
					}
				}
			}
		}
	}
}

Visibility Mask

To increase render speed Qubicle internally uses a visibility mask telling the engine which of the voxel’s sides to render. The mask is optionally encoded in the A value of the RGBA/BGRA voxel data and uses a single byte. To test whether a side should be rendered use this pseudo code:

if (mask == 0) // voxel invisble
if (mask && 2 == 2) // left side visible
if (mask && 4 == 4) // right side visible
if (mask && 8 == 8) // top side visible
if (mask && 16 == 16) // bottom side visible
if (mask && 32 == 32) // front side visible
if (mask && 64 == 64) // back side visible