import addPipelineExtras from "./addPipelineExtras.js";
|
import removeExtensionsUsed from "./removeExtensionsUsed.js";
|
import defaultValue from "../../Core/defaultValue.js";
|
import defined from "../../Core/defined.js";
|
import getMagic from "../../Core/getMagic.js";
|
import getStringFromTypedArray from "../../Core/getStringFromTypedArray.js";
|
import RuntimeError from "../../Core/RuntimeError.js";
|
|
const sizeOfUint32 = 4;
|
|
/**
|
* 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;
|
delete binaryGltfBuffer.uri;
|
}
|
}
|
// 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;
|
}
|
|
export default parseGlb;
|