Skip to content

Commit

Permalink
initial commit 🎉
Browse files Browse the repository at this point in the history
  • Loading branch information
Florian Fechner authored and Florian Fechner committed Jun 6, 2018
1 parent c1b797b commit ff71781
Show file tree
Hide file tree
Showing 13 changed files with 254 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
bower_components
bin/*
dist
package-lock.json
14 changes: 14 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

const fs = require('fs');
const util = require('util')

const readVoxFile = require('./src/readVox');

fs.readFile('test.vox', (error, buffer) =>
{
if (error) throw error;

const voxData = readVoxFile(buffer);

console.log(util.inspect(voxData, { showHidden: false, depth: null }))
});
Binary file added material-test.vox
Binary file not shown.
12 changes: 12 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "vox-reader",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"author": "",
"license": "MIT",
"dependencies": {}
}
48 changes: 48 additions & 0 deletions src/parseVoxChunk/parseVoxChunk.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@

const read4ByteInteger = require('../shared/read4ByteInteger/read4ByteInteger');
const read4ByteFloat = require('../shared/read4ByteFloat/read4ByteFloat');
const groupArray = require('../shared/groupArray/groupArray');

const BLOCK_SIZE = 4;


const parseVoxChunk = (id, contentData) =>
{
const tokens = groupArray(contentData, BLOCK_SIZE);

if(id === 'PACK') return {
numModels: read4ByteInteger(tokens[0])
};

if(id === 'SIZE') return {
x: read4ByteInteger(tokens[0]),
y: read4ByteInteger(tokens[1]),
z: read4ByteInteger(tokens[2])
}

if(id === 'XYZI') return {
numVoxels: read4ByteInteger(tokens[0]),
values: tokens
.slice(1)
.map((c) => ({ x: c[0], y: c[1], z: c[2], i: c[3] }))
}

if(id === 'RGBA') return {
values: tokens
.map((c) => ({ r: c[0], g: c[1], b: c[2], a: c[3] }))
}

if(id === 'MATT') return {
id: read4ByteInteger(tokens[0]),
materialType: read4ByteInteger(tokens[1]),
materialWeight: read4ByteFloat(tokens[2]),
propertyBits: read4ByteInteger(tokens[3]),
normalizedPropertyValues: tokens
.slice(4)
.map((token) => read4ByteFloat(token))
}

return {};
}

module.exports = parseVoxChunk;
46 changes: 46 additions & 0 deletions src/readRiffFile/readChunks/readChunks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

const read4ByteString = require('../../shared/read4ByteString/read4ByteString');
const read4ByteInteger = require('../../shared/read4ByteInteger/read4ByteInteger');
const groupArray = require('../../shared/groupArray/groupArray');

const BLOCK_SIZE = 4;
const HEADER_SIZE = 12;


const readChunks = (data, parser) =>
{
let chunks = [];

while(data.length != 0)
{
const headerData = data.slice(0, HEADER_SIZE);
const header = groupArray(headerData, BLOCK_SIZE);

const chunkId = read4ByteString(header[0]);
const contentBytes = read4ByteInteger(header[1]);
const childrenBytes = read4ByteInteger(header[2]);

chunks.push(createChunk(data, chunkId, contentBytes, childrenBytes, parser));

data = data.slice(HEADER_SIZE + contentBytes);
}

return chunks;
}

const createChunk = (data, id, contentBytes, childrenBytes, parser) =>
{
const contentDataEndIndex = HEADER_SIZE + contentBytes;
const childrenDataEndIndex = contentDataEndIndex + childrenBytes;

const contentData = data.slice(HEADER_SIZE, contentDataEndIndex);
const childrenData = data.slice(contentDataEndIndex, childrenDataEndIndex);

return {
id: id,
data: parser(id, contentData),
children: readChunks(childrenData, parser)
}
}

module.exports = readChunks;
12 changes: 12 additions & 0 deletions src/readRiffFile/readRiffFile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

const readChunks = require('./readChunks/readChunks');

const readRiffFile = (buffer, offset, parser) =>
{
const dataWithoutOffset = buffer.slice(offset);
const chunks = readChunks(dataWithoutOffset, parser);

return chunks[0];
}

module.exports = readRiffFile;
29 changes: 29 additions & 0 deletions src/readVox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

const read4ByteString = require('./shared/read4ByteString/read4ByteString');
const read4ByteInteger = require('./shared/read4ByteInteger/read4ByteInteger');
const groupArray = require('./shared/groupArray/groupArray');

const readRiffFile = require('./readRiffFile/readRiffFile');
const parseVoxChunk = require('./parseVoxChunk/parseVoxChunk');


const readVox = (buffer) =>
{
const BLOCK_SIZE = 4;
const OFFSET = 8; // VOX <space> 150 0 0 0

const data = [...buffer]; // convert buffer to array
const tokens = groupArray(data, BLOCK_SIZE);

const id = read4ByteString(tokens[0]);
const version = read4ByteInteger(tokens[1]);

if(id != 'VOX ') throw Error(`Id of .vox-file should be "VOX ", found "${id}".`);
if(version != 150) throw Error(`Version of .vox-file structure should be 150, found "${version}".`);

const result = readRiffFile(data, OFFSET, parseVoxChunk);

return result;
}

module.exports = readVox;
17 changes: 17 additions & 0 deletions src/shared/groupArray/groupArray.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

const groupArray = (array, groupSize) =>
{
return array.reduce((result, item, index) =>
{
const i1 = parseInt(index / groupSize);
const i2 = index % groupSize;

if(i2 == 0) result.push([]);

result[i1].push(item);

return result;
}, []);
}

module.exports = groupArray;
60 changes: 60 additions & 0 deletions src/shared/read4ByteFloat/read4ByteFloat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@

// see https://en.wikipedia.org/wiki/IEEE_754

const BYTE_SIZE = 8;

const flatten = (array) =>
[].concat.apply([], array);

const binarifyByte = (byte) =>
{
let bits = [];

for(let i=1; i <= BYTE_SIZE; i++)
{
const divident = 2 ** (BYTE_SIZE - i);
const bit = parseInt(byte / divident);

bits.push(bit);

byte = byte % divident;
}

return bits.reverse();
}

const getExponentValueOfBits = (bits) =>
bits.reduce((sum, bit, index) =>
(sum + ((bit) ? 2 ** (BYTE_SIZE - index - 1) : 0))
, -127);

const getMantissaValueOfBits = (bits, denormalized) =>
bits.reduce((sum, bit, index) =>
{
return sum + ((bit) ? 2 ** ((-1 * index) - 1) : 0)
}, (denormalized ? 0 : 1));


const read4ByteFloat = (data) =>
{
data = data.reverse();

const deepBits = data.map(byte => binarifyByte(byte).reverse());
const bits = flatten(deepBits);

const sign = bits[0];

let exponent = getExponentValueOfBits(bits.slice(1, 9));
const denormalized = (exponent === -127);
exponent += denormalized ? 1 : 0;

const mantissa = getMantissaValueOfBits(bits.slice(9), denormalized);

// potentes in JavaScript causes wrong result
if(mantissa === 0) return 0;

return ((sign) ? -1 : 1) * mantissa ** exponent;
}


module.exports = read4ByteFloat;
5 changes: 5 additions & 0 deletions src/shared/read4ByteInteger/read4ByteInteger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const read4ByteInteger = (data) =>
data.map((num, index) => (num * 2 ** (8 * index)) )
.reduce((sum, summand) => sum + summand, 0);

module.exports = read4ByteInteger;
6 changes: 6 additions & 0 deletions src/shared/read4ByteString/read4ByteString.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

const read4ByteString = (data) =>
data.map(charCode => String.fromCharCode(charCode))
.join('');

module.exports = read4ByteString;
Binary file added test.vox
Binary file not shown.

0 comments on commit ff71781

Please sign in to comment.