#ifndef VOX_LOADER_H
#define VOX_LOADER_H
#ifndef VOX_MALLOC
#define VOX_MALLOC(sz) malloc(sz)
#endif
#ifndef VOX_CALLOC
#define VOX_CALLOC(n,sz) calloc(n,sz)
#endif
#ifndef VOX_REALLOC
#define VOX_REALLOC(n,sz) realloc(n,sz)
#endif
#ifndef VOX_FREE
#define VOX_FREE(p) free(p)
#endif
#define VOX_SUCCESS (0)
#define VOX_ERROR_FILE_NOT_FOUND (-1)
#define VOX_ERROR_INVALID_FORMAT (-2)
#define VOX_ERROR_FILE_VERSION_NOT_MATCH (-3)
typedef struct {
unsigned char r, g, b, a;
} VoxColor;
typedef struct {
float x, y, z;
} VoxVector3;
typedef struct {
VoxVector3* array;
int used, size;
} ArrayVector3;
typedef struct {
VoxColor* array;
int used, size;
} ArrayColor;
typedef struct {
unsigned short* array;
int used, size;
} ArrayUShort;
typedef struct {
unsigned char* m_array; int arraySize; } CubeChunk3D;
typedef struct {
int sizeX;
int sizeY;
int sizeZ;
int chunksSizeX;
int chunksSizeY;
int chunksSizeZ;
CubeChunk3D* m_arrayChunks;
int arrayChunksSize;
int ChunkFlattenOffset;
int chunksAllocated;
int chunksTotal;
ArrayVector3 vertices;
ArrayVector3 normals;
ArrayUShort indices;
ArrayColor colors;
VoxColor palette[256];
} VoxArray3D;
#if defined(__cplusplus)
extern "C" { #endif
int Vox_LoadFromMemory(unsigned char* pvoxData, unsigned int voxDataSize, VoxArray3D* pvoxarray);
void Vox_FreeArrays(VoxArray3D* voxarray);
#ifdef __cplusplus
}
#endif
#endif
#ifdef VOX_LOADER_IMPLEMENTATION
#include <string.h>
#include <stdlib.h>
static void initArrayUShort(ArrayUShort* a, int initialSize)
{
a->array = VOX_MALLOC(initialSize * sizeof(unsigned short));
a->used = 0;
a->size = initialSize;
}
static void insertArrayUShort(ArrayUShort* a, unsigned short element)
{
if (a->used == a->size)
{
a->size *= 2;
a->array = VOX_REALLOC(a->array, a->size * sizeof(unsigned short));
}
a->array[a->used++] = element;
}
static void freeArrayUShort(ArrayUShort* a)
{
VOX_FREE(a->array);
a->array = NULL;
a->used = a->size = 0;
}
static void initArrayVector3(ArrayVector3* a, int initialSize)
{
a->array = VOX_MALLOC(initialSize * sizeof(VoxVector3));
a->used = 0;
a->size = initialSize;
}
static void insertArrayVector3(ArrayVector3* a, VoxVector3 element)
{
if (a->used == a->size)
{
a->size *= 2;
a->array = VOX_REALLOC(a->array, a->size * sizeof(VoxVector3));
}
a->array[a->used++] = element;
}
static void freeArrayVector3(ArrayVector3* a)
{
VOX_FREE(a->array);
a->array = NULL;
a->used = a->size = 0;
}
static void initArrayColor(ArrayColor* a, int initialSize)
{
a->array = VOX_MALLOC(initialSize * sizeof(VoxColor));
a->used = 0;
a->size = initialSize;
}
static void insertArrayColor(ArrayColor* a, VoxColor element)
{
if (a->used == a->size)
{
a->size *= 2;
a->array = VOX_REALLOC(a->array, a->size * sizeof(VoxColor));
}
a->array[a->used++] = element;
}
static void freeArrayColor(ArrayColor* a)
{
VOX_FREE(a->array);
a->array = NULL;
a->used = a->size = 0;
}
#define CHUNKSIZE 16
#define CHUNKSIZE_OPSHIFT 4
#define CHUNK_FLATTENOFFSET_OPSHIFT 8
const int fv[6][4] = {
{0, 2, 6, 4 }, {5, 7, 3, 1 }, {0, 4, 5, 1 }, {6, 2, 3, 7 }, {1, 3, 2, 0 }, {4, 6, 7, 5 } };
const VoxVector3 SolidVertex[] = {
{0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {1, 1, 0}, {0, 0, 1}, {1, 0, 1}, {0, 1, 1}, {1, 1, 1} };
const VoxVector3 FacesPerSideNormal[] = {
{ -1, 0, 0 }, {1, 0, 0 }, {0,-1, 0}, {0, 1, 0}, {0, 0, -1}, {0, 0, 1}, };
static void Vox_AllocArray(VoxArray3D* pvoxarray, int _sx, int _sy, int _sz)
{
int sx = _sx + ((CHUNKSIZE - (_sx % CHUNKSIZE)) % CHUNKSIZE);
int sy = _sy + ((CHUNKSIZE - (_sy % CHUNKSIZE)) % CHUNKSIZE);
int sz = _sz + ((CHUNKSIZE - (_sz % CHUNKSIZE)) % CHUNKSIZE);
int chx = sx >> CHUNKSIZE_OPSHIFT; int chy = sy >> CHUNKSIZE_OPSHIFT; int chz = sz >> CHUNKSIZE_OPSHIFT;
pvoxarray->sizeX = sx;
pvoxarray->sizeY = sy;
pvoxarray->sizeZ = sz;
pvoxarray->chunksSizeX = chx;
pvoxarray->chunksSizeY = chy;
pvoxarray->chunksSizeZ = chz;
pvoxarray->ChunkFlattenOffset = (chy * chz);
int size = sizeof(CubeChunk3D) * chx * chy * chz;
pvoxarray->m_arrayChunks = VOX_MALLOC(size);
pvoxarray->arrayChunksSize = size;
size = chx * chy * chz;
pvoxarray->chunksTotal = size;
pvoxarray->chunksAllocated = 0;
for (int i = 0; i < size; i++)
{
pvoxarray->m_arrayChunks[i].m_array = 0;
pvoxarray->m_arrayChunks[i].arraySize = 0;
}
}
static void Vox_SetVoxel(VoxArray3D* pvoxarray, int x, int y, int z, unsigned char id)
{
int chX = x >> CHUNKSIZE_OPSHIFT; int chY = y >> CHUNKSIZE_OPSHIFT; int chZ = z >> CHUNKSIZE_OPSHIFT; int offset = (chX * pvoxarray->ChunkFlattenOffset) + (chZ * pvoxarray->chunksSizeY) + chY;
CubeChunk3D* chunk = &pvoxarray->m_arrayChunks[offset];
chX = x - (chX << CHUNKSIZE_OPSHIFT); chY = y - (chY << CHUNKSIZE_OPSHIFT); chZ = z - (chZ << CHUNKSIZE_OPSHIFT);
if (chunk->m_array == 0)
{
int size = CHUNKSIZE * CHUNKSIZE * CHUNKSIZE;
chunk->m_array = VOX_MALLOC(size);
chunk->arraySize = size;
memset(chunk->m_array, 0, size);
pvoxarray->chunksAllocated++;
}
offset = (chX << CHUNK_FLATTENOFFSET_OPSHIFT) + (chZ << CHUNKSIZE_OPSHIFT) + chY;
chunk->m_array[offset] = id;
}
static unsigned char Vox_GetVoxel(VoxArray3D* pvoxarray, int x, int y, int z)
{
if (x < 0 || y < 0 || z < 0) return 0;
if (x >= pvoxarray->sizeX || y >= pvoxarray->sizeY || z >= pvoxarray->sizeZ) return 0;
int chX = x >> CHUNKSIZE_OPSHIFT; int chY = y >> CHUNKSIZE_OPSHIFT; int chZ = z >> CHUNKSIZE_OPSHIFT; int offset = (chX * pvoxarray->ChunkFlattenOffset) + (chZ * pvoxarray->chunksSizeY) + chY;
CubeChunk3D* chunk = &pvoxarray->m_arrayChunks[offset];
chX = x - (chX << CHUNKSIZE_OPSHIFT); chY = y - (chY << CHUNKSIZE_OPSHIFT); chZ = z - (chZ << CHUNKSIZE_OPSHIFT);
if (chunk->m_array == 0)
{
return 0;
}
offset = (chX << CHUNK_FLATTENOFFSET_OPSHIFT) + (chZ << CHUNKSIZE_OPSHIFT) + chY;
return chunk->m_array[offset];
}
static unsigned char Vox_CalcFacesVisible(VoxArray3D* pvoxArray, int cx, int cy, int cz)
{
unsigned char idXm1 = Vox_GetVoxel(pvoxArray, cx - 1, cy, cz);
unsigned char idXp1 = Vox_GetVoxel(pvoxArray, cx + 1, cy, cz);
unsigned char idYm1 = Vox_GetVoxel(pvoxArray, cx, cy - 1, cz);
unsigned char idYp1 = Vox_GetVoxel(pvoxArray, cx, cy + 1, cz);
unsigned char idZm1 = Vox_GetVoxel(pvoxArray, cx, cy, cz - 1);
unsigned char idZp1 = Vox_GetVoxel(pvoxArray, cx, cy, cz + 1);
unsigned char byVFMask = 0;
if (idXm1 == 0) byVFMask |= (1 << 0);
if (idXp1 == 0) byVFMask |= (1 << 1);
if (idYm1 == 0) byVFMask |= (1 << 2);
if (idYp1 == 0) byVFMask |= (1 << 3);
if (idZm1 == 0) byVFMask |= (1 << 4);
if (idZp1 == 0) byVFMask |= (1 << 5);
return byVFMask;
}
static VoxVector3 Vox_GetVertexPosition(int _wcx, int _wcy, int _wcz, int _nNumVertex)
{
float scale = 0.25;
VoxVector3 vtx = SolidVertex[_nNumVertex];
vtx.x = (vtx.x + _wcx) * scale;
vtx.y = (vtx.y + _wcy) * scale;
vtx.z = (vtx.z + _wcz) * scale;
return vtx;
}
static void Vox_Build_Voxel(VoxArray3D* pvoxArray, int x, int y, int z, int matID)
{
unsigned char byVFMask = Vox_CalcFacesVisible(pvoxArray, x, y, z);
if (byVFMask == 0) return;
int i, j;
VoxVector3 vertComputed[8];
int bVertexComputed[8];
memset(vertComputed, 0, sizeof(vertComputed));
memset(bVertexComputed, 0, sizeof(bVertexComputed));
for (i = 0; i < 6; i++) {
if ((byVFMask & (1 << i)) != 0) {
for (j = 0; j < 4; j++) {
int nNumVertex = fv[i][j]; if (bVertexComputed[nNumVertex] == 0) {
bVertexComputed[nNumVertex] = 1;
vertComputed[nNumVertex] = Vox_GetVertexPosition(x, y, z, nNumVertex);
}
}
}
}
for (i = 0; i < 6; i++) {
if ((byVFMask & (1 << i)) == 0)
continue;
int v0 = fv[i][0]; int v1 = fv[i][1]; int v2 = fv[i][2]; int v3 = fv[i][3];
int idx = pvoxArray->vertices.used;
insertArrayVector3(&pvoxArray->vertices, vertComputed[v0]);
insertArrayVector3(&pvoxArray->vertices, vertComputed[v1]);
insertArrayVector3(&pvoxArray->vertices, vertComputed[v2]);
insertArrayVector3(&pvoxArray->vertices, vertComputed[v3]);
insertArrayVector3(&pvoxArray->normals, FacesPerSideNormal[i]);
insertArrayVector3(&pvoxArray->normals, FacesPerSideNormal[i]);
insertArrayVector3(&pvoxArray->normals, FacesPerSideNormal[i]);
insertArrayVector3(&pvoxArray->normals, FacesPerSideNormal[i]);
VoxColor col = pvoxArray->palette[matID];
insertArrayColor(&pvoxArray->colors, col);
insertArrayColor(&pvoxArray->colors, col);
insertArrayColor(&pvoxArray->colors, col);
insertArrayColor(&pvoxArray->colors, col);
insertArrayUShort(&pvoxArray->indices, idx + 0);
insertArrayUShort(&pvoxArray->indices, idx + 2);
insertArrayUShort(&pvoxArray->indices, idx + 1);
insertArrayUShort(&pvoxArray->indices, idx + 0);
insertArrayUShort(&pvoxArray->indices, idx + 3);
insertArrayUShort(&pvoxArray->indices, idx + 2);
}
}
int Vox_LoadFromMemory(unsigned char* pvoxData, unsigned int voxDataSize, VoxArray3D* pvoxarray)
{
unsigned char* fileData = pvoxData;
unsigned char* fileDataPtr = fileData;
unsigned char* endfileDataPtr = fileData + voxDataSize;
if (strncmp((char*)fileDataPtr, "VOX ", 4) != 0)
{
return VOX_ERROR_INVALID_FORMAT; }
fileDataPtr += 4;
unsigned int version = 0;
version = ((unsigned int*)fileDataPtr)[0];
fileDataPtr += 4;
if (version != 150 && version != 200)
{
return VOX_ERROR_FILE_VERSION_NOT_MATCH; }
unsigned int sizeX, sizeY, sizeZ;
sizeX = sizeY = sizeZ = 0;
unsigned int numVoxels = 0;
while (fileDataPtr < endfileDataPtr)
{
char szChunkName[5];
memcpy(szChunkName, fileDataPtr, 4);
szChunkName[4] = 0;
fileDataPtr += 4;
unsigned int chunkSize = *((unsigned int*)fileDataPtr);
fileDataPtr += sizeof(unsigned int);
fileDataPtr += sizeof(unsigned int);
if (strcmp(szChunkName, "SIZE") == 0)
{
sizeX = *((unsigned int*)fileDataPtr);
fileDataPtr += sizeof(unsigned int);
sizeY = *((unsigned int*)fileDataPtr);
fileDataPtr += sizeof(unsigned int);
sizeZ = *((unsigned int*)fileDataPtr);
fileDataPtr += sizeof(unsigned int);
Vox_AllocArray(pvoxarray, sizeX, sizeZ, sizeY); }
else if (strcmp(szChunkName, "XYZI") == 0)
{
unsigned char vx, vy, vz, vi;
numVoxels = *((unsigned int*)fileDataPtr);
fileDataPtr += sizeof(unsigned int);
while (numVoxels > 0)
{
vx = *((unsigned char*)fileDataPtr++);
vy = *((unsigned char*)fileDataPtr++);
vz = *((unsigned char*)fileDataPtr++);
vi = *((unsigned char*)fileDataPtr++);
Vox_SetVoxel(pvoxarray, vx, vz, pvoxarray->sizeZ-vy-1, vi);
numVoxels--;
}
}
else if (strcmp(szChunkName, "RGBA") == 0)
{
VoxColor col;
for (int i = 0; i < 256 - 1; i++)
{
col.r = *((unsigned char*)fileDataPtr++);
col.g = *((unsigned char*)fileDataPtr++);
col.b = *((unsigned char*)fileDataPtr++);
col.a = *((unsigned char*)fileDataPtr++);
pvoxarray->palette[i + 1] = col;
}
}
else
{
fileDataPtr += chunkSize;
}
}
initArrayVector3(&pvoxarray->vertices, 3 * 1024);
initArrayVector3(&pvoxarray->normals, 3 * 1024);
initArrayUShort(&pvoxarray->indices, 3 * 1024);
initArrayColor(&pvoxarray->colors, 3 * 1024);
int x, y, z;
for (x = 0; x <= pvoxarray->sizeX; x++)
{
for (z = 0; z <= pvoxarray->sizeZ; z++)
{
for (y = 0; y <= pvoxarray->sizeY; y++)
{
unsigned char matID = Vox_GetVoxel(pvoxarray, x, y, z);
if (matID != 0)
Vox_Build_Voxel(pvoxarray, x, y, z, matID);
}
}
}
return VOX_SUCCESS;
}
void Vox_FreeArrays(VoxArray3D* voxarray)
{
if (voxarray->m_arrayChunks != 0)
{
for (int i = 0; i < voxarray->chunksTotal; i++)
{
CubeChunk3D* chunk = &voxarray->m_arrayChunks[i];
if (chunk->m_array != 0)
{
chunk->arraySize = 0;
VOX_FREE(chunk->m_array);
}
}
VOX_FREE(voxarray->m_arrayChunks);
voxarray->m_arrayChunks = 0;
voxarray->arrayChunksSize = 0;
voxarray->chunksSizeX = voxarray->chunksSizeY = voxarray->chunksSizeZ = 0;
voxarray->chunksTotal = 0;
voxarray->chunksAllocated = 0;
voxarray->ChunkFlattenOffset = 0;
voxarray->sizeX = voxarray->sizeY = voxarray->sizeZ = 0;
}
freeArrayVector3(&voxarray->vertices);
freeArrayUShort(&voxarray->indices);
freeArrayColor(&voxarray->colors);
}
#endif