vox2scene = (buffer) => {
const { children } = parseChunks(buffer);
const chunks = children.reduce((chunks, chunk) => {
const { type } = chunk;
(chunks[type] = chunks[type] || []).push(chunk);
return chunks;
}, {});
const { palette } = chunks.RGBA[0];
const globalColorMap = new Map();
const globalColors = new Map();
const idMap = new Map();
let colorId = 0;
const rgb2hex = (r, g, b) =>
((1 << 24) | (r << 16) | (g << 8) | b).toString(16).slice(1);
palette.slice(1).forEach(({ r, g, b, a }, index) => {
const hexColor = rgb2hex(r, g, b);
if (!globalColors.has(hexColor)) {
globalColors.set(hexColor, [r, g, b]);
globalColorMap.set(++colorId, hexColor);
}
idMap.set(index, colorId);
});
const colors = new Map();
const model = (voxVoxels) => {
const voxels = [];
const colorMap = new Map();
voxVoxels.forEach(({ x, y, z, color }) => {
const colorId = idMap.get(color);
const colorHex = globalColorMap.get(colorId);
colorMap.set(colorId, colorHex);
colors.set(colorHex, globalColors.get(colorHex));
voxels.push([x, y, z, colorId]);
});
return { voxels, colorMap };
};
const models = new Map(
chunks.XYZI.map(({ voxels }, i) => [i, model(voxels)])
);
const scene = (root) => ({ models, colors, root });
if (!chunks.nTRN) return scene({ translation: [0, 0, 0], model: 0 });
const chunkMap = (chunks) =>
new Map(chunks.map((chunk) => [chunk.id, chunk]));
const transfos = chunkMap(chunks.nTRN);
const groups = chunkMap(chunks.nGRP);
const shapes = chunkMap(chunks.nSHP);
const splitV3 = (str) => str.split(" ").map((value) => +value);
const recTree = ({ child: childId, transfo }) => {
const child = groups.get(childId) || shapes.get(childId);
const node = { translation: splitV3(transfo.get("_t") || "0 0 0") };
if (child.model) node.model = child.model.id;
else node.children = child.ids.map((id) => recTree(transfos.get(id)));
return node;
};
return scene(recTree(transfos.get(0)));
}