loaders.gl
Version:
Framework-independent loaders for 3D graphics formats
280 lines (218 loc) • 9.99 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _loaderUtils = require("../common/loader-utils");
var _glbAccessorUtils = require("./glb-accessor-utils");
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
var MAGIC_glTF = 0x676c5446; // glTF in Big-Endian ASCII
var LE = true; // Binary GLTF is little endian.
var BE = false; // Magic needs to be written as BE
var GLB_FILE_HEADER_SIZE = 12;
var GLB_CHUNK_HEADER_SIZE = 8;
var GLBBuilder =
/*#__PURE__*/
function () {
function GLBBuilder(rootPath) {
_classCallCheck(this, GLBBuilder);
// Lets us keep track of how large the body will be, as well as the offset for each of the
// original buffers.
this.rootPath = rootPath;
this.byteLength = 0;
this.json = {
buffers: [{
// Just the single BIN chunk buffer
byteLength: 0 // Updated at end of conversion
}],
bufferViews: [],
accessors: [],
images: []
}; // list of binary buffers to be written to the BIN chunk
// (Each call to addBuffer, addImage etc adds an entry here)
this.sourceBuffers = [];
}
_createClass(GLBBuilder, [{
key: "getByteLength",
value: function getByteLength() {
return this.byteLength;
}
}, {
key: "copyToArrayBuffer",
value: function copyToArrayBuffer(arrayBuffer, byteOffset) {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = this.parts[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var _step$value = _step.value,
offset = _step$value.offset,
contents = _step$value.contents;
contents.copy(arrayBuffer, byteOffset + offset);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return != null) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
} // Add a binary buffer. Builds glTF "JSON metadata" and saves buffer reference
// Buffer will be copied into BIN chunk during "pack"
}, {
key: "addBuffer",
value: function addBuffer(sourceBuffer) {
var accessor = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
size: 3
};
var bufferViewIndex = this._addBufferView(sourceBuffer); // Add an accessor pointing to the new buffer view
var glTFAccessor = {
bufferView: bufferViewIndex,
type: (0, _glbAccessorUtils.getAccessorType)(accessor.size),
componentType: (0, _glbAccessorUtils.getAccessorComponentType)(sourceBuffer),
count: Math.round(sourceBuffer.length / accessor.size)
};
this.json.accessors.push(glTFAccessor);
return this.json.accessors.length - 1;
} // Checks if a binary buffer is a recognized image format (PNG, JPG, GIF, ...)
}, {
key: "isImage",
value: function isImage(imageData) {
try {
(0, _loaderUtils.getImageSize)(imageData);
return true;
} catch (error) {
return false;
}
} // Adds a binary image. Builds glTF "JSON metadata" and saves buffer reference
// Buffer will be copied into BIN chunk during "pack"
}, {
key: "addImage",
value: function addImage(imageData) {
var bufferViewIndex = this._addBufferView(imageData);
var glTFImage = {
bufferView: bufferViewIndex
}; // Get the properties of the image to add as metadata.
var sizeAndType = (0, _loaderUtils.getImageSize)(imageData);
if (sizeAndType) {
var mimeType = sizeAndType.mimeType,
width = sizeAndType.width,
height = sizeAndType.height;
Object.assign(glTFImage, {
mimeType: mimeType,
width: width,
height: height
});
}
this.json.images.push(glTFImage);
return this.json.images.length - 1;
}
}, {
key: "pack",
value: function pack() {
this._packBinaryChunk();
return {
arrayBuffer: this.arrayBuffer,
json: this.json
};
}
}, {
key: "encode",
value: function encode(json) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
return this._createGlbBuffer(json, options);
} // PRIVATE
// Add one source buffer, create a matchibng glTF `bufferView`, and return its index
}, {
key: "_addBufferView",
value: function _addBufferView(buffer) {
var byteLength = buffer.byteLength || buffer.length; // Add a bufferView indicating start and length of this binary sub-chunk
this.json.bufferViews.push({
buffer: 0,
// Write offset from the start of the binary body
byteOffset: this.byteLength,
byteLength: byteLength
}); // We've now written the contents to the body, so update the total length
// Every sub-chunk needs to be 4-byte aligned
this.byteLength += (0, _loaderUtils.padTo4Bytes)(byteLength); // Add this buffer to the list of buffers to be written to the body.
this.sourceBuffers.push(buffer); // Return the index to the just created bufferView
return this.json.bufferViews.length - 1;
} // Pack the binary chunk
}, {
key: "_packBinaryChunk",
value: function _packBinaryChunk() {
// Already packed
if (this.arrayBuffer) {
return;
} // Allocate total array
var totalByteLength = this.byteLength;
var arrayBuffer = new ArrayBuffer(totalByteLength);
var targetArray = new Uint8Array(arrayBuffer); // Copy each array into
var byteOffset = 0;
for (var i = 0; i < this.sourceBuffers.length; i++) {
var sourceBuffer = this.sourceBuffers[i];
var byteLength = sourceBuffer.byteLength; // Pack buffer onto the big target array
var sourceArray = new Uint8Array(sourceBuffer.buffer);
targetArray.set(sourceArray, byteOffset);
byteOffset += (0, _loaderUtils.padTo4Bytes)(byteLength);
} // Update the glTF BIN CHUNK byte length
this.json.buffers[0].byteLength = totalByteLength; // Save generated arrayBuffer
this.arrayBuffer = arrayBuffer; // Clear out sourceBuffers
this.sourceBuffers = [];
} // Encode the full GLB buffer with header etc
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#
// glb-file-format-specification
}, {
key: "_createGlbBuffer",
value: function _createGlbBuffer(appJson) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
// TODO - avoid double array buffer creation
this._packBinaryChunk();
this.json.json = appJson;
var binChunk = this.arrayBuffer;
if (options.magic) {
console.warn('Custom glTF magic number no longer supported'); // eslint-disable-line
}
var jsonChunkOffset = GLB_FILE_HEADER_SIZE + GLB_CHUNK_HEADER_SIZE; // First headers: 20 bytes
var jsonChunk = convertObjectToJsonChunk(this.json); // As body is 4-byte aligned, the scene length must be padded to have a multiple of 4.
var jsonChunkLength = (0, _loaderUtils.padTo4Bytes)(jsonChunk.byteLength);
var binChunkOffset = jsonChunkLength + jsonChunkOffset;
var fileLength = binChunkOffset + GLB_CHUNK_HEADER_SIZE + (0, _loaderUtils.padTo4Bytes)(binChunk.byteLength); // Length is know, we can create the GLB memory buffer!
var glbArrayBuffer = new ArrayBuffer(fileLength);
var dataView = new DataView(glbArrayBuffer); // GLB Header
dataView.setUint32(0, MAGIC_glTF, BE); // Magic number (the ASCII string 'glTF').
dataView.setUint32(4, 2, LE); // Version 2 of binary glTF container format uint32
dataView.setUint32(8, fileLength, LE); // Total byte length of generated file (uint32)
// Write the JSON chunk
dataView.setUint32(12, jsonChunk.byteLength, LE); // Byte length of json chunk (uint32)
dataView.setUint32(16, 0, LE); // Chunk format as uint32 (JSON is 0)
(0, _loaderUtils.copyArrayBuffer)(glbArrayBuffer, jsonChunk, jsonChunkOffset); // TODO - Add spaces as padding to ensure scene is a multiple of 4 bytes.
// for (let i = jsonChunkLength + 20; i < binChunkOffset; ++i) {
// glbFileArray[i] = 0x20;
// }
// Write the BIN chunk
var binChunkLengthPadded = (0, _loaderUtils.padTo4Bytes)(binChunk.byteLength);
dataView.setUint32(binChunkOffset + 0, binChunkLengthPadded, LE); // Byte length BIN (uint32)
dataView.setUint32(binChunkOffset + 4, 1, LE); // Chunk format as uint32 (BIN is 1)
(0, _loaderUtils.copyArrayBuffer)(glbArrayBuffer, binChunk, binChunkOffset + GLB_CHUNK_HEADER_SIZE);
return glbArrayBuffer;
}
}]);
return GLBBuilder;
}();
exports.default = GLBBuilder;
function convertObjectToJsonChunk(json) {
var jsonChunkString = JSON.stringify(json);
var textEncoder = new _loaderUtils.TextEncoder('utf8');
return textEncoder.encode(jsonChunkString);
}
//# sourceMappingURL=glb-builder.js.map