*excerpt from slab6.txt in the SLAB6 download found at http://advsys.net/ken/download.htm#slab6 *
Both SLABSPRI&SLAB6(D) support a simpler, uncompressed voxel format using the VOX file extension. Here's some C pseudocode that describes the format:
long xsiz, ysiz, zsiz; //Variable declarations
char voxel[xsiz][ysiz][zsiz];
char palette[256][3];
fil = open("?.vox",...);
read(fil,&xsiz,4); //Dimensions of 3-D array of voxels
read(fil,&ysiz,4);
read(fil,&zsiz,4);
read(fil,voxel,xsiz*ysiz*zsiz); //The 3-D array itself!
read(fil,palette,768); //VGA palette (values range from 0-63)
close(fil);
VOX files use color 255 to define empty space (air). This is a limitation of the VOX format. Fortunately, KVX doesn't have this problem. For interior voxels (ones you can never see), do not use color 255, because it will prevent SLABSPRI/SLAB6(D) from being able to take advantage of back-face culling.
I'm always interested in adding more sample voxels to my collection. Because I'm such a nice guy, I am describing my KVX voxel format here so you can use them in your own programs. The KVX file format was designed to be compact, yet also renderable directly from its format. Storing a byte for every voxel is not efficient for large models, so I use a form of run-length encoding and store only the voxels that are visible - just the surface voxels. The "runs" are stored in the ceiling to floor direction because that is the best axis to use for fast rendering in the Build Engine.
Each KVX file uses this structure for each of its mip-map levels:
long xsiz, ysiz, zsiz, xpivot, ypivot, zpivot;
long xoffset[xsiz+1];
short xyoffset[xsiz][ysiz+1];
char rawslabdata[?];
The file can be loaded like this:
if ((fil = open("?.kvx",O_BINARY|O_RDWR,S_IREAD)) == -1) return(0);
nummipmaplevels = 1; //nummipmaplevels = 5 for unstripped KVX files
for(i=0;i<nummipmaplevels;i++)
{
read(fil,&numbytes,4);
read(fil,&xsiz,4);
read(fil,&ysiz,4);
read(fil,&zsiz,4);
read(fil,&xpivot,4);
read(fil,&ypivot,4);
read(fil,&zpivot,4);
read(fil,xoffset,(xsiz+1)*4);
read(fil,xyoffset,xsiz*(ysiz+1)*2);
read(fil,voxdata,numbytes-24-(xsiz+1)*4-xsiz*(ysiz+1)*2);
}
read(fil,palette,768);
numbytes: Total # of bytes (not including numbytes) in each mip-map level
xsiz, ysiz, zsiz: Dimensions of voxel. (zsiz is height)
xpivot, ypivot, zpivot: Centroid of voxel. For extra precision, this location has been shifted up by 8 bits.
xoffset, xyoffset: For compression purposes, I store the column pointers in a way that offers quick access to the data, but with slightly more overhead in calculating the positions. See example of usage in voxdata. NOTE: xoffset[0] = (xsiz+1)4 + xsiz(ysiz+1)*2 (ALWAYS)
voxdata: stored in sequential format. Here's how you can get pointers to the start and end of any (x, y) column:
//pointer to start of slabs on column (x, y):
startptr = &voxdata[xoffset[x] + xyoffset[x][y]];
//pointer to end of slabs on column (x, y):
endptr = &voxdata[xoffset[x] + xyoffset[x][y+1]];
Note: endptr is actually the first piece of data in the next column
Once you get these pointers, you can run through all of the "slabs" in the column. Each slab has 3 bytes of header, then an array of colors. Here's the format:
char slabztop; //Starting z coordinate of top of slab
char slabzleng; //# of bytes in the color array - slab height
char slabbackfacecullinfo; //Low 6 bits tell which of 6 faces are exposed
char col[slabzleng]; //The array of colors from top to bottom
palette: The last 768 bytes of the KVX file is a standard 256-color VGA palette. The palette is in (Red:0, Green:1, Blue:2) order and intensities range from 0-63.
Note: To keep this ZIP size small, I have stripped out the lower mip-map levels. KVX files from Shadow Warrior or Blood include this data. To get the palette data, I recommend seeking 768 bytes before the end of the KVX file.
//C pseudocode for loader:
typedef struct { long col; unsigned short z; char vis, dir; } kv6voxtype;
long xsiz, ysiz, zsiz;
float xpiv, ypiv, zpiv;
unsigned long xlen[xsiz];
unsigned short ylen[xsiz][ysiz];
long numvoxs;
FILE *fil = fopen("?.KV6",rb");
fread(&fileid,4,1,fil); //'Kvxl' (= 0x6c78764b in Little Endian)
//Voxel grid dimensions
fread(&xsiz,4,1,fil); fread(&ysiz,4,1,fil); fread(&zsiz,4,1,fil);
//Pivot point. Floating point format. Voxel units.
fread(&xpiv,4,1,fil); fread(&ypiv,4,1,fil); fread(&zpiv,4,1,fil);
fread(&numvoxs,4,1,fil); //Total number of surface voxels
for(i=0;i<numvoxs;i++) //8 bytes per surface voxel, Z's must be sorted
{
red [i] = fgetc(fil); //Range: 0..255
green[i] = fgetc(fil); //"
blue [i] = fgetc(fil); //"
dummy = fgetc(fil); //Always 128. Ignore.
height_low = fgetc(fil); //Z coordinate of this surface voxel
height_high = fgetc(fil); //"
visibility = fgetc(fil); //Low 6 bits say if neighbor is solid or air
normalindex = fgetc(fil); //Uses 256-entry lookup table
}
//Number of surface voxels present in plane x (extra information)
for(x=0;x<xsiz;x++) fread(&xlen[x],4,1,fil);
//Number of surface voxels present in column (x,y)
for(x=0;x<xsiz;x++) for(y=0;y<ysiz;y++) fread(&ylen[x][y],2,1,fil);
//Added 06/30/2007: suggested palette (optional)
fread(&suggpalid,4,1,fil); //'SPal' (= 0x6C615053 in Little Endian)
fread(suggestedpalette,768,1,fil); //R,G,B,R,G,.. Value range: 0-63
fclose(fil);