gltf-pipeline
Version:
Content pipeline tools for optimizing glTF assets.
119 lines (103 loc) • 3.8 kB
JavaScript
'use strict';
const Cesium = require('cesium');
const addPipelineExtras = require('./addPipelineExtras');
const removeExtensionsUsed = require('./removeExtensionsUsed');
const defaultValue = Cesium.defaultValue;
const defined = Cesium.defined;
const getMagic = Cesium.getMagic;
const getStringFromTypedArray = Cesium.getStringFromTypedArray;
const RuntimeError = Cesium.RuntimeError;
const sizeOfUint32 = 4;
module.exports = parseGlb;
/**
* Convert a binary glTF to glTF.
*
* The returned glTF has pipeline extras included. The embedded binary data is stored in gltf.buffers[0].extras._pipeline.source.
*
* @param {Buffer} glb The glb data to parse.
* @returns {Object} A javascript object containing a glTF asset with pipeline extras included.
*
* @private
*/
function parseGlb(glb) {
// Check that the magic string is present
const magic = getMagic(glb);
if (magic !== 'glTF') {
throw new RuntimeError('File is not valid binary glTF');
}
const header = readHeader(glb, 0, 5);
const version = header[1];
if (version !== 1 && version !== 2) {
throw new RuntimeError('Binary glTF version is not 1 or 2');
}
if (version === 1) {
return parseGlbVersion1(glb, header);
}
return parseGlbVersion2(glb, header);
}
function readHeader(glb, byteOffset, count) {
const dataView = new DataView(glb.buffer);
const header = new Array(count);
for (let i = 0; i < count; ++i) {
header[i] = dataView.getUint32(glb.byteOffset + byteOffset + i * sizeOfUint32, true);
}
return header;
}
function parseGlbVersion1(glb, header) {
const length = header[2];
const contentLength = header[3];
const contentFormat = header[4];
// Check that the content format is 0, indicating that it is JSON
if (contentFormat !== 0) {
throw new RuntimeError('Binary glTF scene format is not JSON');
}
const jsonStart = 20;
const binaryStart = jsonStart + contentLength;
const contentString = getStringFromTypedArray(glb, jsonStart, contentLength);
const gltf = JSON.parse(contentString);
addPipelineExtras(gltf);
const binaryBuffer = glb.subarray(binaryStart, length);
const buffers = gltf.buffers;
if (defined(buffers) && Object.keys(buffers).length > 0) {
// In some older models, the binary glTF buffer is named KHR_binary_glTF
const binaryGltfBuffer = defaultValue(buffers.binary_glTF, buffers.KHR_binary_glTF);
if (defined(binaryGltfBuffer)) {
binaryGltfBuffer.extras._pipeline.source = binaryBuffer;
}
}
// Remove the KHR_binary_glTF extension
removeExtensionsUsed(gltf, 'KHR_binary_glTF');
return gltf;
}
function parseGlbVersion2(glb, header) {
const length = header[2];
let byteOffset = 12;
let gltf;
let binaryBuffer;
while (byteOffset < length) {
const chunkHeader = readHeader(glb, byteOffset, 2);
const chunkLength = chunkHeader[0];
const chunkType = chunkHeader[1];
byteOffset += 8;
const chunkBuffer = glb.subarray(byteOffset, byteOffset + chunkLength);
byteOffset += chunkLength;
// Load JSON chunk
if (chunkType === 0x4E4F534A) {
const jsonString = getStringFromTypedArray(chunkBuffer);
gltf = JSON.parse(jsonString);
addPipelineExtras(gltf);
}
// Load Binary chunk
else if (chunkType === 0x004E4942) {
binaryBuffer = chunkBuffer;
}
}
if (defined(gltf) && defined(binaryBuffer)) {
const buffers = gltf.buffers;
if (defined(buffers) && buffers.length > 0) {
const buffer = buffers[0];
buffer.extras._pipeline.source = binaryBuffer;
}
}
return gltf;
}