@loaders.gl/gltf
Advanced tools
Comparing version 0.4.8 to 0.5.0
@@ -55,4 +55,5 @@ "use strict"; | ||
this.sourceBuffers = []; | ||
} | ||
} // ACCESSORS | ||
_createClass(GLBBuilder, [{ | ||
@@ -62,2 +63,17 @@ key: "getByteLength", | ||
return this.byteLength; | ||
} // Checks if a binary buffer is a recognized image format (PNG, JPG, GIF, ...) | ||
}, { | ||
key: "isImage", | ||
value: function isImage(imageData) { | ||
return (0, _core.isImage)(imageData); | ||
} // MODIFERS | ||
// Encode the full glTF file as a binary GLB file | ||
// Returns an ArrayBuffer that represents the complete GLB image that can be saved to file | ||
}, { | ||
key: "encodeAsGLB", | ||
value: function encodeAsGLB() { | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
return this._createGLBBuffer(options); | ||
} // Add an extra application-defined key to the top-level data structure | ||
@@ -73,23 +89,22 @@ // By default packs JSON by extracting binary data and replacing it with JSON pointers | ||
return this; | ||
} // Encode the full glTF file as a binary GLB file | ||
// Returns an ArrayBuffer that represents the complete GLB image that can be saved to file | ||
} // Add one untyped source buffer, create a matching glTF `bufferView`, and return its index | ||
}, { | ||
key: "encodeAsGLB", | ||
value: function encodeAsGLB() { | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
return this._createGlbBuffer(options); | ||
} // Returns an arrayBuffer together with JSON etc data. | ||
key: "addBufferView", | ||
value: function addBufferView(buffer) { | ||
var byteLength = buffer.byteLength || buffer.length; // Add a bufferView indicating start and length of this binary sub-chunk | ||
}, { | ||
key: "encodeAsGLBWithMetadata", | ||
value: function encodeAsGLBWithMetadata() { | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
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 | ||
var arrayBuffer = this._createGlbBuffer(options); | ||
this.byteLength += (0, _core.padTo4Bytes)(byteLength); // Add this buffer to the list of buffers to be written to the body. | ||
return { | ||
arrayBuffer: arrayBuffer, | ||
json: this.json | ||
}; | ||
this.sourceBuffers.push(buffer); // Return the index to the just created bufferView | ||
return this.json.bufferViews.length - 1; | ||
} // Add a binary buffer. Builds glTF "JSON metadata" and saves buffer reference | ||
@@ -104,6 +119,4 @@ // Buffer will be copied into BIN chunk during "pack" | ||
}; | ||
var bufferViewIndex = this.addBufferView(sourceBuffer); // Add an accessor pointing to the new buffer view | ||
var bufferViewIndex = this._addBufferView(sourceBuffer); // Add an accessor pointing to the new buffer view | ||
var glTFAccessor = { | ||
@@ -117,8 +130,2 @@ bufferView: bufferViewIndex, | ||
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) { | ||
return (0, _core.isImage)(imageData); | ||
} // Adds a binary image. Builds glTF "JSON metadata" and saves buffer reference | ||
@@ -130,4 +137,3 @@ // Buffer will be copied into BIN chunk during "pack" | ||
value: function addImage(imageData) { | ||
var bufferViewIndex = this._addBufferView(imageData); | ||
var bufferViewIndex = this.addBufferView(imageData); | ||
var glTFImage = { | ||
@@ -152,3 +158,4 @@ bufferView: bufferViewIndex | ||
return this.json.images.length - 1; | ||
} // For testing | ||
} // PRIVATE | ||
// For testing | ||
@@ -164,23 +171,2 @@ }, { | ||
}; | ||
} // 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, _core.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 | ||
@@ -231,4 +217,4 @@ | ||
}, { | ||
key: "_createGlbBuffer", | ||
value: function _createGlbBuffer() { | ||
key: "_createGLBBuffer", | ||
value: function _createGLBBuffer() { | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
@@ -235,0 +221,0 @@ |
@@ -55,12 +55,8 @@ "use strict"; | ||
function GLBParser(glbArrayBuffer) { | ||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
function GLBParser() { | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
_classCallCheck(this, GLBParser); | ||
// Soft dependency on Draco, needs to be imported and supplied by app | ||
this.DracoDecoder = options.DracoDecoder; // Input | ||
this.glbArrayBuffer = glbArrayBuffer; // Result | ||
// Result | ||
this.binaryByteOffset = null; | ||
@@ -74,6 +70,7 @@ this.packedJson = null; | ||
key: "parse", | ||
value: function parse() { | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
value: function parse(glbArrayBuffer) { | ||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
// Input | ||
this.glbArrayBuffer = glbArrayBuffer; // Only parse once | ||
// Only parse once | ||
if (this.json === null && this.binaryByteOffset === null) { | ||
@@ -108,2 +105,41 @@ this.result = this._parse(options); | ||
return this.binaryByteOffset; | ||
} // Unpacks a bufferview into a new Uint8Array that is a view into the binary chunk | ||
}, { | ||
key: "getBufferView", | ||
value: function getBufferView(glTFBufferView) { | ||
var byteOffset = glTFBufferView.byteOffset + this.binaryByteOffset; | ||
return new Uint8Array(byteOffset, glTFBufferView.byteLength); | ||
} // Unpacks a glTF accessor into a new typed array that is a view into the binary chunk | ||
}, { | ||
key: "getBuffer", | ||
value: function getBuffer(glTFAccessor) { | ||
// Decode the glTF accessor format | ||
var ArrayType = _gltfTypeUtils.ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY[glTFAccessor.componentType]; | ||
var components = _gltfTypeUtils.ATTRIBUTE_TYPE_TO_COMPONENTS[glTFAccessor.type]; | ||
var bytesPerComponent = _gltfTypeUtils.ATTRIBUTE_COMPONENT_TYPE_TO_BYTE_SIZE[glTFAccessor.componentType]; | ||
var length = glTFAccessor.count * components; | ||
var byteLength = glTFAccessor.count * components * bytesPerComponent; // Get the boundaries of the binary sub-chunk for this bufferView | ||
var glTFBufferView = this.json.bufferViews[glTFAccessor.bufferView]; | ||
(0, _core.assert)(byteLength >= 0 && byteLength <= glTFBufferView.byteLength); | ||
var byteOffset = glTFBufferView.byteOffset + this.binaryByteOffset; | ||
return new ArrayType(this.arrayBuffer, byteOffset, length); | ||
} // Unpacks an image into an HTML image | ||
}, { | ||
key: "getImage", | ||
value: function getImage(glTFImage) { | ||
/* global window, Blob, Image */ | ||
var arrayBufferView = this.unpackBufferView(glTFImage.bufferView); | ||
var mimeType = glTFImage.mimeType || 'image/jpeg'; | ||
var blob = new Blob([arrayBufferView], { | ||
type: mimeType | ||
}); | ||
var urlCreator = window.URL || window.webkitURL; | ||
var imageUrl = urlCreator.createObjectURL(blob); | ||
var img = new Image(); | ||
img.src = imageUrl; | ||
return img; | ||
} // PRIVATE | ||
@@ -172,184 +208,2 @@ | ||
} | ||
}, { | ||
key: "unpackBinaryObjects", | ||
value: function unpackBinaryObjects() { | ||
var unpackedBinaryObjects = { | ||
images: [], | ||
accessors: [], | ||
meshes: [] | ||
}; | ||
var images = this.json.images || []; | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
try { | ||
for (var _iterator = images[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var glTFImage = _step.value; | ||
unpackedBinaryObjects.images.push(this._unpackImage(glTFImage)); | ||
} | ||
} catch (err) { | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion && _iterator.return != null) { | ||
_iterator.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
} | ||
} | ||
} | ||
var accessors = this.json.accessors || []; | ||
var _iteratorNormalCompletion2 = true; | ||
var _didIteratorError2 = false; | ||
var _iteratorError2 = undefined; | ||
try { | ||
for (var _iterator2 = accessors[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
var glTFAccessor = _step2.value; | ||
unpackedBinaryObjects.accessors.push(this._unpackAccessor(glTFAccessor)); | ||
} | ||
} catch (err) { | ||
_didIteratorError2 = true; | ||
_iteratorError2 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion2 && _iterator2.return != null) { | ||
_iterator2.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError2) { | ||
throw _iteratorError2; | ||
} | ||
} | ||
} | ||
var meshes = this.json.meshes || []; | ||
var _iteratorNormalCompletion3 = true; | ||
var _didIteratorError3 = false; | ||
var _iteratorError3 = undefined; | ||
try { | ||
for (var _iterator3 = meshes[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { | ||
var glTFMesh = _step3.value; | ||
unpackedBinaryObjects.meshes.push(this._unpackMesh(glTFMesh)); | ||
} | ||
} catch (err) { | ||
_didIteratorError3 = true; | ||
_iteratorError3 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion3 && _iterator3.return != null) { | ||
_iterator3.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError3) { | ||
throw _iteratorError3; | ||
} | ||
} | ||
} | ||
return unpackedBinaryObjects; | ||
} | ||
}, { | ||
key: "_unpackImage", | ||
value: function _unpackImage(glTFImage) { | ||
/* global window, Blob, Image */ | ||
var arrayBufferView = this.unpackBufferView(glTFImage.bufferView); | ||
var mimeType = glTFImage.mimeType || 'image/jpeg'; | ||
var blob = new Blob([arrayBufferView], { | ||
type: mimeType | ||
}); | ||
var urlCreator = window.URL || window.webkitURL; | ||
var imageUrl = urlCreator.createObjectURL(blob); | ||
var img = new Image(); | ||
img.src = imageUrl; | ||
return img; | ||
} | ||
}, { | ||
key: "_unpackAccessor", | ||
value: function _unpackAccessor(glTFAccessor) { | ||
// Decode the glTF accessor format | ||
var ArrayType = _gltfTypeUtils.ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY[glTFAccessor.componentType]; | ||
var components = _gltfTypeUtils.ATTRIBUTE_TYPE_TO_COMPONENTS[glTFAccessor.type]; | ||
var bytesPerComponent = _gltfTypeUtils.ATTRIBUTE_COMPONENT_TYPE_TO_BYTE_SIZE[glTFAccessor.componentType]; | ||
var length = glTFAccessor.count * components; | ||
var byteLength = glTFAccessor.count * components * bytesPerComponent; // Get the boundaries of the binary sub-chunk for this bufferView | ||
var glTFBufferView = this.json.bufferViews[glTFAccessor.bufferView]; | ||
(0, _core.assert)(byteLength >= 0 && byteLength <= glTFBufferView.byteLength); | ||
var byteOffset = glTFBufferView.byteOffset + this.binaryByteOffset; | ||
return new ArrayType(this.arrayBuffer, byteOffset, length); | ||
} // Create a new typed array as a view into the binary chunk | ||
}, { | ||
key: "_unpackBufferView", | ||
value: function _unpackBufferView(glTFBufferView) { | ||
var byteOffset = glTFBufferView.byteOffset + this.binaryByteOffset; | ||
return new Uint8Array(byteOffset, glTFBufferView.byteLength); | ||
} | ||
}, { | ||
key: "_unpackMesh", | ||
value: function _unpackMesh(mesh) { | ||
var unpackedPrimitives = []; | ||
var _iteratorNormalCompletion4 = true; | ||
var _didIteratorError4 = false; | ||
var _iteratorError4 = undefined; | ||
try { | ||
for (var _iterator4 = mesh.primitives[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { | ||
var primitive = _step4.value; | ||
var compressedMesh = primitive.extensions && primitive.extensions.UBER_draco_mesh_compression; | ||
var compressedPointCloud = primitive.extensions && primitive.extensions.UBER_draco_point_cloud_compression; | ||
var unpackedPrimitive = { | ||
mode: primitive.mode, | ||
material: primitive.material | ||
}; | ||
if (compressedMesh) { | ||
var dracoDecoder = new this.DracoDecoder(); | ||
var decodedData = dracoDecoder.decodeMesh(compressedMesh); | ||
dracoDecoder.destroy(); | ||
Object.assign(unpackedPrimitive, { | ||
indices: decodedData.indices, | ||
attributes: decodedData.attributes | ||
}); | ||
} else if (compressedPointCloud) { | ||
var _dracoDecoder = new this.DracoDecoder(); | ||
var _decodedData = _dracoDecoder.decodePointCloud(compressedPointCloud); | ||
_dracoDecoder.destroy(); | ||
Object.assign(unpackedPrimitive, { | ||
mode: 0, | ||
attributes: _decodedData.attributes | ||
}); | ||
} else {// No compression - just a glTF mesh primitive | ||
// TODO - Resolve all accessors | ||
} | ||
unpackedPrimitives.push(unpackedPrimitive); | ||
} | ||
} catch (err) { | ||
_didIteratorError4 = true; | ||
_iteratorError4 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion4 && _iterator4.return != null) { | ||
_iterator4.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError4) { | ||
throw _iteratorError4; | ||
} | ||
} | ||
} | ||
return unpackedPrimitives.length === 1 ? unpackedPrimitives[0] : unpackedPrimitives; | ||
} | ||
}]); | ||
@@ -356,0 +210,0 @@ |
@@ -60,3 +60,4 @@ "use strict"; | ||
return _this; | ||
} // Encode as a textual JSON file with binary data in base64 data URLs. | ||
} // TODO - support encoding to non-GLB versions of glTF format | ||
// Encode as a textual JSON file with binary data in base64 data URLs. | ||
// encodeAsDataURLs(options) { | ||
@@ -63,0 +64,0 @@ // throw new Error('Not yet implemented'); |
@@ -23,14 +23,13 @@ "use strict"; | ||
function () { | ||
function GLTFParser(gltf) { | ||
function GLTFParser() { | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
_classCallCheck(this, GLTFParser); | ||
if (gltf instanceof ArrayBuffer) { | ||
gltf = new _glbParser.default(gltf).parse().json; | ||
} | ||
this.gltf = gltf; | ||
this.json = gltf; | ||
// TODO - move parsing to parse | ||
this.log = console; // eslint-disable-line | ||
this.out = {}; | ||
this.out = {}; // Soft dependency on Draco, needs to be imported and supplied by app | ||
this.DracoDecoder = options.DracoDecoder || null; | ||
} | ||
@@ -40,20 +39,21 @@ | ||
key: "parse", | ||
value: function parse() { | ||
var _this = this; | ||
value: function parse(gltf) { | ||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
this.glbParser = new _glbParser.default(); // GLTF can be JSON or binary (GLB) | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
// Load all images | ||
this.out.images = (this.gltf.images || []).map(function (image) { | ||
return _this.parseImage(image); | ||
}).filter(Boolean); // Parse all scenes | ||
if (gltf instanceof ArrayBuffer) { | ||
this.gltf = this.glbParser.parse(gltf).json; | ||
this.json = this.gltf; | ||
} else { | ||
this.gltf = gltf; | ||
this.json = gltf; | ||
} | ||
this.out.scenes = (this.gltf.scenes || []).map(function (scene) { | ||
return _this.parseImage(scene); | ||
}).filter(Boolean); | ||
this._loadLinkedAssets(options); // TODO - not implemented | ||
// this._postProcessGLTF(options); TODO - remove done differently now | ||
if (this.gltf.scene) { | ||
this.out.scene = this.gltf.scenes[this.gltf.scene]; | ||
} | ||
return this; | ||
this._resolveToTree(options); | ||
return this.gltf; | ||
} // Accessors | ||
@@ -90,4 +90,46 @@ | ||
return this.json.extensionsUsed; | ||
} | ||
} // DATA UNPACKING | ||
// Unpacks all the primitives in a mesh | ||
}, { | ||
key: "unpackMesh", | ||
value: function unpackMesh(mesh) { | ||
return mesh.primitives.map(this.unpackPrimitive.bind(this)); | ||
} // Unpacks one mesh primitive | ||
}, { | ||
key: "unpackPrimitive", | ||
value: function unpackPrimitive(primitive) { | ||
var compressedMesh = primitive.extensions && primitive.extensions.UBER_draco_mesh_compression; | ||
var compressedPointCloud = primitive.extensions && primitive.extensions.UBER_draco_point_cloud_compression; | ||
var unpackedPrimitive = { | ||
mode: primitive.mode, | ||
material: primitive.material | ||
}; | ||
if (compressedMesh) { | ||
var dracoDecoder = new this.DracoDecoder(); | ||
var decodedData = dracoDecoder.decodeMesh(compressedMesh); | ||
dracoDecoder.destroy(); | ||
Object.assign(unpackedPrimitive, { | ||
indices: decodedData.indices, | ||
attributes: decodedData.attributes | ||
}); | ||
} else if (compressedPointCloud) { | ||
var _dracoDecoder = new this.DracoDecoder(); | ||
var _decodedData = _dracoDecoder.decodePointCloud(compressedPointCloud); | ||
_dracoDecoder.destroy(); | ||
Object.assign(unpackedPrimitive, { | ||
mode: 0, | ||
attributes: _decodedData.attributes | ||
}); | ||
} else {// No compression - just a glTF mesh primitive | ||
// TODO - Resolve all accessors | ||
} | ||
} // PRIVATE | ||
}, { | ||
key: "getScene", | ||
@@ -146,4 +188,3 @@ value: function getScene(index) { | ||
return this._get('buffers', index); | ||
} // PRIVATE | ||
} | ||
}, { | ||
@@ -155,3 +196,3 @@ key: "_get", | ||
if (!object) { | ||
console.warn("glTF file error: Could not resolve ".concat(array, "[").concat(index, "]")); // eslint-disable-line | ||
console.warn("glTF file error: Could not find ".concat(array, "[").concat(index, "]")); // eslint-disable-line | ||
} | ||
@@ -161,43 +202,38 @@ | ||
} // PARSING HELPERS | ||
// Start loading linked assets | ||
}, { | ||
key: "parseScene", | ||
value: function parseScene() {} | ||
}, { | ||
key: "parseImage", | ||
value: function parseImage(image) { | ||
return this.config.createImage(image); | ||
key: "_loadLinkedAssets", | ||
value: function _loadLinkedAssets(options) {// TODO: Not implemented | ||
// TODO: Return a promise? | ||
} | ||
}, { | ||
key: "parseMesh", | ||
value: function parseMesh(mesh) { | ||
var _this2 = this; | ||
key: "_postProcessGLTF", | ||
value: function _postProcessGLTF() { | ||
var _this = this; | ||
// Each primitive is intended to correspond to a draw call | ||
var primitives = (mesh.primitives || []).map(function (primitive) { | ||
return _this2.parseMeshPrimitive(primitive); | ||
}); | ||
return primitives.length === 1 ? primitives[0] : this.config.createGroup(primitives); | ||
} | ||
}, { | ||
key: "parseMeshPrimitive", | ||
value: function parseMeshPrimitive(primitive) { | ||
// if (!primitive.attributes) | ||
// this.log.warn(primitive without attributes`) | ||
var attributes = primitive.attributes || {}; | ||
attributes = this.config.mapAttributes(attributes); | ||
return attributes; | ||
} | ||
}, { | ||
key: "parseAccessor", | ||
value: function parseAccessor(accessor) { | ||
return this.config.createBuffer(accessor); | ||
} // PREPARATION STEP: CROSS-LINK INDEX RESOLUTION, ENUM LOOKUP, CONVENIENCE CALCULATIONS | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
// Create all images (if requested) | ||
this.out.images = (this.gltf.images || []).map(function (image) { | ||
return _this.parseImage(image, options); | ||
}).filter(Boolean); // Normalize all scenes | ||
this.out.scenes = (this.gltf.scenes || []).map(function (scene) { | ||
return _this.parseScene(scene, options); | ||
}).filter(Boolean); | ||
if (this.gltf.scene) { | ||
this.out.scene = this.gltf.scenes[this.gltf.scene]; | ||
} | ||
return this; | ||
} // Convert indexed glTF structure into tree structure | ||
// PREPARATION STEP: CROSS-LINK INDEX RESOLUTION, ENUM LOOKUP, CONVENIENCE CALCULATIONS | ||
/* eslint-disable complexity */ | ||
}, { | ||
key: "resolve", | ||
value: function resolve() { | ||
var _this3 = this; | ||
key: "_resolveToTree", | ||
value: function _resolveToTree() { | ||
var _this2 = this; | ||
@@ -207,30 +243,30 @@ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
(gltf.bufferViews || []).forEach(function (bufView, i) { | ||
return _this3.resolveBufferView(bufView, i); | ||
return _this2._resolveBufferView(bufView, i); | ||
}); | ||
(gltf.images || []).forEach(function (image, i) { | ||
return _this3.resolveImage(image, i); | ||
return _this2._resolveImage(image, i, options); | ||
}); | ||
(gltf.samplers || []).forEach(function (sampler, i) { | ||
return _this3.resolveSampler(sampler, i); | ||
return _this2._resolveSampler(sampler, i); | ||
}); | ||
(gltf.textures || []).forEach(function (texture, i) { | ||
return _this3.resolveTexture(texture, i); | ||
return _this2._resolveTexture(texture, i); | ||
}); | ||
(gltf.accessors || []).forEach(function (accessor, i) { | ||
return _this3.resolveAccessor(accessor, i); | ||
return _this2._resolveAccessor(accessor, i); | ||
}); | ||
(gltf.materials || []).forEach(function (material, i) { | ||
return _this3.resolveMaterial(material, i); | ||
return _this2._resolveMaterial(material, i); | ||
}); | ||
(gltf.meshes || []).forEach(function (mesh, i) { | ||
return _this3.resolveMesh(mesh, i); | ||
return _this2._resolveMesh(mesh, i); | ||
}); | ||
(gltf.nodes || []).forEach(function (node, i) { | ||
return _this3.resolveNode(node, i); | ||
return _this2._resolveNode(node, i); | ||
}); | ||
(gltf.skins || []).forEach(function (skin, i) { | ||
return _this3.resolveSkin(skin, i); | ||
return _this2._resolveSkin(skin, i); | ||
}); | ||
(gltf.scenes || []).forEach(function (scene, i) { | ||
return _this3.resolveScene(scene, i); | ||
return _this2._resolveScene(scene, i); | ||
}); | ||
@@ -247,19 +283,19 @@ | ||
}, { | ||
key: "resolveScene", | ||
value: function resolveScene(scene, index) { | ||
var _this4 = this; | ||
key: "_resolveScene", | ||
value: function _resolveScene(scene, index) { | ||
var _this3 = this; | ||
scene.id = "scene-".concat(index); | ||
scene.nodes = (scene.nodes || []).map(function (node) { | ||
return _this4.getNode(node); | ||
return _this3.getNode(node); | ||
}); | ||
} | ||
}, { | ||
key: "resolveNode", | ||
value: function resolveNode(node, index) { | ||
var _this5 = this; | ||
key: "_resolveNode", | ||
value: function _resolveNode(node, index) { | ||
var _this4 = this; | ||
node.id = "node-".concat(index); | ||
node.children = (node.children || []).map(function (child) { | ||
return _this5.getNode(child); | ||
return _this4.getNode(child); | ||
}); | ||
@@ -280,4 +316,4 @@ | ||
}, { | ||
key: "resolveSkin", | ||
value: function resolveSkin(skin, index) { | ||
key: "_resolveSkin", | ||
value: function _resolveSkin(skin, index) { | ||
skin.id = "skin-".concat(index); | ||
@@ -287,4 +323,4 @@ skin.inverseBindMatrices = this.getAccessor(skin.inverseBindMatrices); | ||
}, { | ||
key: "resolveMesh", | ||
value: function resolveMesh(mesh, index) { | ||
key: "_resolveMesh", | ||
value: function _resolveMesh(mesh, index) { | ||
mesh.id = "mesh-".concat(index); | ||
@@ -327,4 +363,4 @@ var _iteratorNormalCompletion = true; | ||
}, { | ||
key: "resolveMaterial", | ||
value: function resolveMaterial(material, index) { | ||
key: "_resolveMaterial", | ||
value: function _resolveMaterial(material, index) { | ||
material.id = "material-".concat(index); | ||
@@ -357,4 +393,4 @@ | ||
}, { | ||
key: "resolveAccessor", | ||
value: function resolveAccessor(accessor, index) { | ||
key: "_resolveAccessor", | ||
value: function _resolveAccessor(accessor, index) { | ||
accessor.id = "accessor-".concat(index); | ||
@@ -368,4 +404,4 @@ accessor.bufferView = this.getBufferView(accessor.bufferView); // Look up enums | ||
}, { | ||
key: "resolveTexture", | ||
value: function resolveTexture(texture, index) { | ||
key: "_resolveTexture", | ||
value: function _resolveTexture(texture, index) { | ||
texture.id = "texture-".concat(index); | ||
@@ -376,4 +412,4 @@ texture.sampler = this.getSampler(texture.sampler); | ||
}, { | ||
key: "resolveSampler", | ||
value: function resolveSampler(sampler, index) { | ||
key: "_resolveSampler", | ||
value: function _resolveSampler(sampler, index) { | ||
sampler.id = "sampler-".concat(index); // Map textual parameters to GL parameter values | ||
@@ -389,4 +425,4 @@ | ||
}, { | ||
key: "resolveImage", | ||
value: function resolveImage(image, index) { | ||
key: "_resolveImage", | ||
value: function _resolveImage(image, index, options) { | ||
image.id = "image-".concat(index); | ||
@@ -396,8 +432,16 @@ | ||
image.bufferView = this.getBufferView(image.bufferView); | ||
} // TODO - Handle URIs etc | ||
} // TODO - Handle non-binary-chunk images, data URIs, URLs etc | ||
// TODO - Image creation could be done on getImage instead of during load | ||
var _options$createImages = options.createImages, | ||
createImages = _options$createImages === void 0 ? true : _options$createImages; | ||
if (createImages) { | ||
image.image = this.glbParser.unpackImage(image); | ||
} | ||
} | ||
}, { | ||
key: "resolveBufferView", | ||
value: function resolveBufferView(bufferView, index) { | ||
key: "_resolveBufferView", | ||
value: function _resolveBufferView(bufferView, index) { | ||
bufferView.id = "bufferView-".concat(index); | ||
@@ -408,11 +452,9 @@ bufferView.buffer = this.getBuffer(bufferView.buffer); | ||
}, { | ||
key: "resolveCamera", | ||
value: function resolveCamera(camera) { | ||
// TODO - resolve step should not create | ||
if (camera.perspective) { | ||
camera.matrix = this.config.createPerspectiveMatrix(camera.perspective); | ||
key: "_resolveCamera", | ||
value: function _resolveCamera(camera) { | ||
// TODO - create 4x4 matrices | ||
if (camera.perspective) {// camera.matrix = createPerspectiveMatrix(camera.perspective); | ||
} | ||
if (camera.orthographic) { | ||
camera.matrix = this.config.createOrthographicMatrix(camera.orthographic); | ||
if (camera.orthographic) {// camera.matrix = createOrthographicMatrix(camera.orthographic); | ||
} | ||
@@ -419,0 +461,0 @@ } |
@@ -33,6 +33,20 @@ /* eslint-disable camelcase, max-statements */ | ||
this.sourceBuffers = []; | ||
} | ||
} // ACCESSORS | ||
getByteLength() { | ||
return this.byteLength; | ||
} // Checks if a binary buffer is a recognized image format (PNG, JPG, GIF, ...) | ||
isImage(imageData) { | ||
return isImage(imageData); | ||
} // MODIFERS | ||
// Encode the full glTF file as a binary GLB file | ||
// Returns an ArrayBuffer that represents the complete GLB image that can be saved to file | ||
encodeAsGLB() { | ||
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
return this._createGLBBuffer(options); | ||
} // Add an extra application-defined key to the top-level data structure | ||
@@ -47,21 +61,21 @@ // By default packs JSON by extracting binary data and replacing it with JSON pointers | ||
return this; | ||
} // Encode the full glTF file as a binary GLB file | ||
// Returns an ArrayBuffer that represents the complete GLB image that can be saved to file | ||
} // Add one untyped source buffer, create a matching glTF `bufferView`, and return its index | ||
encodeAsGLB() { | ||
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
return this._createGlbBuffer(options); | ||
} // Returns an arrayBuffer together with JSON etc data. | ||
addBufferView(buffer) { | ||
const 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 | ||
}); // We've now written the contents to the body, so update the total length | ||
// Every sub-chunk needs to be 4-byte aligned | ||
encodeAsGLBWithMetadata() { | ||
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
this.byteLength += padTo4Bytes(byteLength); // Add this buffer to the list of buffers to be written to the body. | ||
const arrayBuffer = this._createGlbBuffer(options); | ||
this.sourceBuffers.push(buffer); // Return the index to the just created bufferView | ||
return { | ||
arrayBuffer, | ||
json: this.json | ||
}; | ||
return this.json.bufferViews.length - 1; | ||
} // Add a binary buffer. Builds glTF "JSON metadata" and saves buffer reference | ||
@@ -75,6 +89,4 @@ // Buffer will be copied into BIN chunk during "pack" | ||
}; | ||
const bufferViewIndex = this.addBufferView(sourceBuffer); // Add an accessor pointing to the new buffer view | ||
const bufferViewIndex = this._addBufferView(sourceBuffer); // Add an accessor pointing to the new buffer view | ||
const glTFAccessor = { | ||
@@ -88,7 +100,2 @@ bufferView: bufferViewIndex, | ||
return this.json.accessors.length - 1; | ||
} // Checks if a binary buffer is a recognized image format (PNG, JPG, GIF, ...) | ||
isImage(imageData) { | ||
return isImage(imageData); | ||
} // Adds a binary image. Builds glTF "JSON metadata" and saves buffer reference | ||
@@ -99,4 +106,3 @@ // Buffer will be copied into BIN chunk during "pack" | ||
addImage(imageData) { | ||
const bufferViewIndex = this._addBufferView(imageData); | ||
const bufferViewIndex = this.addBufferView(imageData); | ||
const glTFImage = { | ||
@@ -121,3 +127,4 @@ bufferView: bufferViewIndex | ||
return this.json.images.length - 1; | ||
} // For testing | ||
} // PRIVATE | ||
// For testing | ||
@@ -132,22 +139,2 @@ | ||
}; | ||
} // PRIVATE | ||
// Add one source buffer, create a matchibng glTF `bufferView`, and return its index | ||
_addBufferView(buffer) { | ||
const 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 | ||
}); // 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 += 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 | ||
@@ -197,3 +184,3 @@ | ||
_createGlbBuffer() { | ||
_createGLBBuffer() { | ||
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
@@ -200,0 +187,0 @@ |
@@ -36,9 +36,5 @@ /* eslint-disable camelcase, max-statements */ | ||
constructor(glbArrayBuffer) { | ||
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
// Soft dependency on Draco, needs to be imported and supplied by app | ||
this.DracoDecoder = options.DracoDecoder; // Input | ||
this.glbArrayBuffer = glbArrayBuffer; // Result | ||
constructor() { | ||
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
// Result | ||
this.binaryByteOffset = null; | ||
@@ -50,6 +46,7 @@ this.packedJson = null; | ||
parse() { | ||
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
parse(glbArrayBuffer) { | ||
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
// Input | ||
this.glbArrayBuffer = glbArrayBuffer; // Only parse once | ||
// Only parse once | ||
if (this.json === null && this.binaryByteOffset === null) { | ||
@@ -80,2 +77,38 @@ this.result = this._parse(options); | ||
return this.binaryByteOffset; | ||
} // Unpacks a bufferview into a new Uint8Array that is a view into the binary chunk | ||
getBufferView(glTFBufferView) { | ||
const byteOffset = glTFBufferView.byteOffset + this.binaryByteOffset; | ||
return new Uint8Array(byteOffset, glTFBufferView.byteLength); | ||
} // Unpacks a glTF accessor into a new typed array that is a view into the binary chunk | ||
getBuffer(glTFAccessor) { | ||
// Decode the glTF accessor format | ||
const ArrayType = ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY[glTFAccessor.componentType]; | ||
const components = ATTRIBUTE_TYPE_TO_COMPONENTS[glTFAccessor.type]; | ||
const bytesPerComponent = ATTRIBUTE_COMPONENT_TYPE_TO_BYTE_SIZE[glTFAccessor.componentType]; | ||
const length = glTFAccessor.count * components; | ||
const byteLength = glTFAccessor.count * components * bytesPerComponent; // Get the boundaries of the binary sub-chunk for this bufferView | ||
const glTFBufferView = this.json.bufferViews[glTFAccessor.bufferView]; | ||
assert(byteLength >= 0 && byteLength <= glTFBufferView.byteLength); | ||
const byteOffset = glTFBufferView.byteOffset + this.binaryByteOffset; | ||
return new ArrayType(this.arrayBuffer, byteOffset, length); | ||
} // Unpacks an image into an HTML image | ||
getImage(glTFImage) { | ||
/* global window, Blob, Image */ | ||
const arrayBufferView = this.unpackBufferView(glTFImage.bufferView); | ||
const mimeType = glTFImage.mimeType || 'image/jpeg'; | ||
const blob = new Blob([arrayBufferView], { | ||
type: mimeType | ||
}); | ||
const urlCreator = window.URL || window.webkitURL; | ||
const imageUrl = urlCreator.createObjectURL(blob); | ||
const img = new Image(); | ||
img.src = imageUrl; | ||
return img; | ||
} // PRIVATE | ||
@@ -143,101 +176,3 @@ | ||
unpackBinaryObjects() { | ||
const unpackedBinaryObjects = { | ||
images: [], | ||
accessors: [], | ||
meshes: [] | ||
}; | ||
const images = this.json.images || []; | ||
for (const glTFImage of images) { | ||
unpackedBinaryObjects.images.push(this._unpackImage(glTFImage)); | ||
} | ||
const accessors = this.json.accessors || []; | ||
for (const glTFAccessor of accessors) { | ||
unpackedBinaryObjects.accessors.push(this._unpackAccessor(glTFAccessor)); | ||
} | ||
const meshes = this.json.meshes || []; | ||
for (const glTFMesh of meshes) { | ||
unpackedBinaryObjects.meshes.push(this._unpackMesh(glTFMesh)); | ||
} | ||
return unpackedBinaryObjects; | ||
} | ||
_unpackImage(glTFImage) { | ||
/* global window, Blob, Image */ | ||
const arrayBufferView = this.unpackBufferView(glTFImage.bufferView); | ||
const mimeType = glTFImage.mimeType || 'image/jpeg'; | ||
const blob = new Blob([arrayBufferView], { | ||
type: mimeType | ||
}); | ||
const urlCreator = window.URL || window.webkitURL; | ||
const imageUrl = urlCreator.createObjectURL(blob); | ||
const img = new Image(); | ||
img.src = imageUrl; | ||
return img; | ||
} | ||
_unpackAccessor(glTFAccessor) { | ||
// Decode the glTF accessor format | ||
const ArrayType = ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY[glTFAccessor.componentType]; | ||
const components = ATTRIBUTE_TYPE_TO_COMPONENTS[glTFAccessor.type]; | ||
const bytesPerComponent = ATTRIBUTE_COMPONENT_TYPE_TO_BYTE_SIZE[glTFAccessor.componentType]; | ||
const length = glTFAccessor.count * components; | ||
const byteLength = glTFAccessor.count * components * bytesPerComponent; // Get the boundaries of the binary sub-chunk for this bufferView | ||
const glTFBufferView = this.json.bufferViews[glTFAccessor.bufferView]; | ||
assert(byteLength >= 0 && byteLength <= glTFBufferView.byteLength); | ||
const byteOffset = glTFBufferView.byteOffset + this.binaryByteOffset; | ||
return new ArrayType(this.arrayBuffer, byteOffset, length); | ||
} // Create a new typed array as a view into the binary chunk | ||
_unpackBufferView(glTFBufferView) { | ||
const byteOffset = glTFBufferView.byteOffset + this.binaryByteOffset; | ||
return new Uint8Array(byteOffset, glTFBufferView.byteLength); | ||
} | ||
_unpackMesh(mesh) { | ||
const unpackedPrimitives = []; | ||
for (const primitive of mesh.primitives) { | ||
const compressedMesh = primitive.extensions && primitive.extensions.UBER_draco_mesh_compression; | ||
const compressedPointCloud = primitive.extensions && primitive.extensions.UBER_draco_point_cloud_compression; | ||
const unpackedPrimitive = { | ||
mode: primitive.mode, | ||
material: primitive.material | ||
}; | ||
if (compressedMesh) { | ||
const dracoDecoder = new this.DracoDecoder(); | ||
const decodedData = dracoDecoder.decodeMesh(compressedMesh); | ||
dracoDecoder.destroy(); | ||
Object.assign(unpackedPrimitive, { | ||
indices: decodedData.indices, | ||
attributes: decodedData.attributes | ||
}); | ||
} else if (compressedPointCloud) { | ||
const dracoDecoder = new this.DracoDecoder(); | ||
const decodedData = dracoDecoder.decodePointCloud(compressedPointCloud); | ||
dracoDecoder.destroy(); | ||
Object.assign(unpackedPrimitive, { | ||
mode: 0, | ||
attributes: decodedData.attributes | ||
}); | ||
} else {// No compression - just a glTF mesh primitive | ||
// TODO - Resolve all accessors | ||
} | ||
unpackedPrimitives.push(unpackedPrimitive); | ||
} | ||
return unpackedPrimitives.length === 1 ? unpackedPrimitives[0] : unpackedPrimitives; | ||
} | ||
} | ||
//# sourceMappingURL=glb-parser.js.map |
@@ -18,3 +18,4 @@ /* eslint-disable camelcase, max-statements */ | ||
}); | ||
} // Encode as a textual JSON file with binary data in base64 data URLs. | ||
} // TODO - support encoding to non-GLB versions of glTF format | ||
// Encode as a textual JSON file with binary data in base64 data URLs. | ||
// encodeAsDataURLs(options) { | ||
@@ -21,0 +22,0 @@ // throw new Error('Not yet implemented'); |
import { getBytesFromComponentType, getSizeFromAccessorType } from '../utils/gltf-type-utils'; | ||
import GLBParser from '../glb/glb-parser'; | ||
export default class GLTFParser { | ||
constructor(gltf) { | ||
if (gltf instanceof ArrayBuffer) { | ||
gltf = new GLBParser(gltf).parse().json; | ||
} | ||
this.gltf = gltf; | ||
this.json = gltf; | ||
constructor() { | ||
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
// TODO - move parsing to parse | ||
this.log = console; // eslint-disable-line | ||
this.out = {}; | ||
this.out = {}; // Soft dependency on Draco, needs to be imported and supplied by app | ||
this.DracoDecoder = options.DracoDecoder || null; | ||
} | ||
parse() { | ||
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
// Load all images | ||
this.out.images = (this.gltf.images || []).map(image => this.parseImage(image)).filter(Boolean); // Parse all scenes | ||
parse(gltf) { | ||
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
this.glbParser = new GLBParser(); // GLTF can be JSON or binary (GLB) | ||
this.out.scenes = (this.gltf.scenes || []).map(scene => this.parseImage(scene)).filter(Boolean); | ||
if (this.gltf.scene) { | ||
this.out.scene = this.gltf.scenes[this.gltf.scene]; | ||
if (gltf instanceof ArrayBuffer) { | ||
this.gltf = this.glbParser.parse(gltf).json; | ||
this.json = this.gltf; | ||
} else { | ||
this.gltf = gltf; | ||
this.json = gltf; | ||
} | ||
return this; | ||
this._loadLinkedAssets(options); // TODO - not implemented | ||
// this._postProcessGLTF(options); TODO - remove done differently now | ||
this._resolveToTree(options); | ||
return this.gltf; | ||
} // Accessors | ||
@@ -54,4 +59,41 @@ | ||
return this.json.extensionsUsed; | ||
} | ||
} // DATA UNPACKING | ||
// Unpacks all the primitives in a mesh | ||
unpackMesh(mesh) { | ||
return mesh.primitives.map(this.unpackPrimitive.bind(this)); | ||
} // Unpacks one mesh primitive | ||
unpackPrimitive(primitive) { | ||
const compressedMesh = primitive.extensions && primitive.extensions.UBER_draco_mesh_compression; | ||
const compressedPointCloud = primitive.extensions && primitive.extensions.UBER_draco_point_cloud_compression; | ||
const unpackedPrimitive = { | ||
mode: primitive.mode, | ||
material: primitive.material | ||
}; | ||
if (compressedMesh) { | ||
const dracoDecoder = new this.DracoDecoder(); | ||
const decodedData = dracoDecoder.decodeMesh(compressedMesh); | ||
dracoDecoder.destroy(); | ||
Object.assign(unpackedPrimitive, { | ||
indices: decodedData.indices, | ||
attributes: decodedData.attributes | ||
}); | ||
} else if (compressedPointCloud) { | ||
const dracoDecoder = new this.DracoDecoder(); | ||
const decodedData = dracoDecoder.decodePointCloud(compressedPointCloud); | ||
dracoDecoder.destroy(); | ||
Object.assign(unpackedPrimitive, { | ||
mode: 0, | ||
attributes: decodedData.attributes | ||
}); | ||
} else {// No compression - just a glTF mesh primitive | ||
// TODO - Resolve all accessors | ||
} | ||
} // PRIVATE | ||
getScene(index) { | ||
@@ -99,5 +141,4 @@ return this._get('scenes', index); | ||
return this._get('buffers', index); | ||
} // PRIVATE | ||
} | ||
_get(array, index) { | ||
@@ -107,3 +148,3 @@ const object = this.gltf[array] && this.gltf[array][index]; | ||
if (!object) { | ||
console.warn(`glTF file error: Could not resolve ${array}[${index}]`); // eslint-disable-line | ||
console.warn(`glTF file error: Could not find ${array}[${index}]`); // eslint-disable-line | ||
} | ||
@@ -113,44 +154,40 @@ | ||
} // PARSING HELPERS | ||
// Start loading linked assets | ||
parseScene() {} | ||
parseImage(image) { | ||
return this.config.createImage(image); | ||
_loadLinkedAssets(options) {// TODO: Not implemented | ||
// TODO: Return a promise? | ||
} | ||
parseMesh(mesh) { | ||
// Each primitive is intended to correspond to a draw call | ||
const primitives = (mesh.primitives || []).map(primitive => this.parseMeshPrimitive(primitive)); | ||
return primitives.length === 1 ? primitives[0] : this.config.createGroup(primitives); | ||
} | ||
_postProcessGLTF() { | ||
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
// Create all images (if requested) | ||
this.out.images = (this.gltf.images || []).map(image => this.parseImage(image, options)).filter(Boolean); // Normalize all scenes | ||
parseMeshPrimitive(primitive) { | ||
// if (!primitive.attributes) | ||
// this.log.warn(primitive without attributes`) | ||
let attributes = primitive.attributes || {}; | ||
attributes = this.config.mapAttributes(attributes); | ||
return attributes; | ||
} | ||
this.out.scenes = (this.gltf.scenes || []).map(scene => this.parseScene(scene, options)).filter(Boolean); | ||
parseAccessor(accessor) { | ||
return this.config.createBuffer(accessor); | ||
} // PREPARATION STEP: CROSS-LINK INDEX RESOLUTION, ENUM LOOKUP, CONVENIENCE CALCULATIONS | ||
if (this.gltf.scene) { | ||
this.out.scene = this.gltf.scenes[this.gltf.scene]; | ||
} | ||
return this; | ||
} // Convert indexed glTF structure into tree structure | ||
// PREPARATION STEP: CROSS-LINK INDEX RESOLUTION, ENUM LOOKUP, CONVENIENCE CALCULATIONS | ||
/* eslint-disable complexity */ | ||
resolve() { | ||
_resolveToTree() { | ||
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
const gltf = this.gltf; | ||
(gltf.bufferViews || []).forEach((bufView, i) => this.resolveBufferView(bufView, i)); | ||
(gltf.images || []).forEach((image, i) => this.resolveImage(image, i)); | ||
(gltf.samplers || []).forEach((sampler, i) => this.resolveSampler(sampler, i)); | ||
(gltf.textures || []).forEach((texture, i) => this.resolveTexture(texture, i)); | ||
(gltf.accessors || []).forEach((accessor, i) => this.resolveAccessor(accessor, i)); | ||
(gltf.materials || []).forEach((material, i) => this.resolveMaterial(material, i)); | ||
(gltf.meshes || []).forEach((mesh, i) => this.resolveMesh(mesh, i)); | ||
(gltf.nodes || []).forEach((node, i) => this.resolveNode(node, i)); | ||
(gltf.skins || []).forEach((skin, i) => this.resolveSkin(skin, i)); | ||
(gltf.scenes || []).forEach((scene, i) => this.resolveScene(scene, i)); | ||
(gltf.bufferViews || []).forEach((bufView, i) => this._resolveBufferView(bufView, i)); | ||
(gltf.images || []).forEach((image, i) => this._resolveImage(image, i, options)); | ||
(gltf.samplers || []).forEach((sampler, i) => this._resolveSampler(sampler, i)); | ||
(gltf.textures || []).forEach((texture, i) => this._resolveTexture(texture, i)); | ||
(gltf.accessors || []).forEach((accessor, i) => this._resolveAccessor(accessor, i)); | ||
(gltf.materials || []).forEach((material, i) => this._resolveMaterial(material, i)); | ||
(gltf.meshes || []).forEach((mesh, i) => this._resolveMesh(mesh, i)); | ||
(gltf.nodes || []).forEach((node, i) => this._resolveNode(node, i)); | ||
(gltf.skins || []).forEach((skin, i) => this._resolveSkin(skin, i)); | ||
(gltf.scenes || []).forEach((scene, i) => this._resolveScene(scene, i)); | ||
@@ -166,3 +203,3 @@ if (gltf.scene) { | ||
resolveScene(scene, index) { | ||
_resolveScene(scene, index) { | ||
scene.id = `scene-${index}`; | ||
@@ -172,3 +209,3 @@ scene.nodes = (scene.nodes || []).map(node => this.getNode(node)); | ||
resolveNode(node, index) { | ||
_resolveNode(node, index) { | ||
node.id = `node-${index}`; | ||
@@ -190,3 +227,3 @@ node.children = (node.children || []).map(child => this.getNode(child)); | ||
resolveSkin(skin, index) { | ||
_resolveSkin(skin, index) { | ||
skin.id = `skin-${index}`; | ||
@@ -196,3 +233,3 @@ skin.inverseBindMatrices = this.getAccessor(skin.inverseBindMatrices); | ||
resolveMesh(mesh, index) { | ||
_resolveMesh(mesh, index) { | ||
mesh.id = `mesh-${index}`; | ||
@@ -215,3 +252,3 @@ | ||
resolveMaterial(material, index) { | ||
_resolveMaterial(material, index) { | ||
material.id = `material-${index}`; | ||
@@ -244,3 +281,3 @@ | ||
resolveAccessor(accessor, index) { | ||
_resolveAccessor(accessor, index) { | ||
accessor.id = `accessor-${index}`; | ||
@@ -254,3 +291,3 @@ accessor.bufferView = this.getBufferView(accessor.bufferView); // Look up enums | ||
resolveTexture(texture, index) { | ||
_resolveTexture(texture, index) { | ||
texture.id = `texture-${index}`; | ||
@@ -261,3 +298,3 @@ texture.sampler = this.getSampler(texture.sampler); | ||
resolveSampler(sampler, index) { | ||
_resolveSampler(sampler, index) { | ||
sampler.id = `sampler-${index}`; // Map textual parameters to GL parameter values | ||
@@ -273,3 +310,3 @@ | ||
resolveImage(image, index) { | ||
_resolveImage(image, index, options) { | ||
image.id = `image-${index}`; | ||
@@ -279,7 +316,15 @@ | ||
image.bufferView = this.getBufferView(image.bufferView); | ||
} // TODO - Handle URIs etc | ||
} // TODO - Handle non-binary-chunk images, data URIs, URLs etc | ||
// TODO - Image creation could be done on getImage instead of during load | ||
const _options$createImages = options.createImages, | ||
createImages = _options$createImages === void 0 ? true : _options$createImages; | ||
if (createImages) { | ||
image.image = this.glbParser.unpackImage(image); | ||
} | ||
} | ||
resolveBufferView(bufferView, index) { | ||
_resolveBufferView(bufferView, index) { | ||
bufferView.id = `bufferView-${index}`; | ||
@@ -290,10 +335,8 @@ bufferView.buffer = this.getBuffer(bufferView.buffer); | ||
resolveCamera(camera) { | ||
// TODO - resolve step should not create | ||
if (camera.perspective) { | ||
camera.matrix = this.config.createPerspectiveMatrix(camera.perspective); | ||
_resolveCamera(camera) { | ||
// TODO - create 4x4 matrices | ||
if (camera.perspective) {// camera.matrix = createPerspectiveMatrix(camera.perspective); | ||
} | ||
if (camera.orthographic) { | ||
camera.matrix = this.config.createOrthographicMatrix(camera.orthographic); | ||
if (camera.orthographic) {// camera.matrix = createOrthographicMatrix(camera.orthographic); | ||
} | ||
@@ -300,0 +343,0 @@ } |
@@ -45,4 +45,5 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
this.sourceBuffers = []; | ||
} | ||
} // ACCESSORS | ||
_createClass(GLBBuilder, [{ | ||
@@ -52,2 +53,17 @@ key: "getByteLength", | ||
return this.byteLength; | ||
} // Checks if a binary buffer is a recognized image format (PNG, JPG, GIF, ...) | ||
}, { | ||
key: "isImage", | ||
value: function isImage(imageData) { | ||
return _isImage(imageData); | ||
} // MODIFERS | ||
// Encode the full glTF file as a binary GLB file | ||
// Returns an ArrayBuffer that represents the complete GLB image that can be saved to file | ||
}, { | ||
key: "encodeAsGLB", | ||
value: function encodeAsGLB() { | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
return this._createGLBBuffer(options); | ||
} // Add an extra application-defined key to the top-level data structure | ||
@@ -63,23 +79,22 @@ // By default packs JSON by extracting binary data and replacing it with JSON pointers | ||
return this; | ||
} // Encode the full glTF file as a binary GLB file | ||
// Returns an ArrayBuffer that represents the complete GLB image that can be saved to file | ||
} // Add one untyped source buffer, create a matching glTF `bufferView`, and return its index | ||
}, { | ||
key: "encodeAsGLB", | ||
value: function encodeAsGLB() { | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
return this._createGlbBuffer(options); | ||
} // Returns an arrayBuffer together with JSON etc data. | ||
key: "addBufferView", | ||
value: function addBufferView(buffer) { | ||
var byteLength = buffer.byteLength || buffer.length; // Add a bufferView indicating start and length of this binary sub-chunk | ||
}, { | ||
key: "encodeAsGLBWithMetadata", | ||
value: function encodeAsGLBWithMetadata() { | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
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 | ||
var arrayBuffer = this._createGlbBuffer(options); | ||
this.byteLength += padTo4Bytes(byteLength); // Add this buffer to the list of buffers to be written to the body. | ||
return { | ||
arrayBuffer: arrayBuffer, | ||
json: this.json | ||
}; | ||
this.sourceBuffers.push(buffer); // Return the index to the just created bufferView | ||
return this.json.bufferViews.length - 1; | ||
} // Add a binary buffer. Builds glTF "JSON metadata" and saves buffer reference | ||
@@ -94,6 +109,4 @@ // Buffer will be copied into BIN chunk during "pack" | ||
}; | ||
var bufferViewIndex = this.addBufferView(sourceBuffer); // Add an accessor pointing to the new buffer view | ||
var bufferViewIndex = this._addBufferView(sourceBuffer); // Add an accessor pointing to the new buffer view | ||
var glTFAccessor = { | ||
@@ -107,8 +120,2 @@ bufferView: bufferViewIndex, | ||
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) { | ||
return _isImage(imageData); | ||
} // Adds a binary image. Builds glTF "JSON metadata" and saves buffer reference | ||
@@ -120,4 +127,3 @@ // Buffer will be copied into BIN chunk during "pack" | ||
value: function addImage(imageData) { | ||
var bufferViewIndex = this._addBufferView(imageData); | ||
var bufferViewIndex = this.addBufferView(imageData); | ||
var glTFImage = { | ||
@@ -142,3 +148,4 @@ bufferView: bufferViewIndex | ||
return this.json.images.length - 1; | ||
} // For testing | ||
} // PRIVATE | ||
// For testing | ||
@@ -154,23 +161,2 @@ }, { | ||
}; | ||
} // 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 += 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 | ||
@@ -221,4 +207,4 @@ | ||
}, { | ||
key: "_createGlbBuffer", | ||
value: function _createGlbBuffer() { | ||
key: "_createGLBBuffer", | ||
value: function _createGLBBuffer() { | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
@@ -225,0 +211,0 @@ |
@@ -43,12 +43,8 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function GLBParser(glbArrayBuffer) { | ||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
function GLBParser() { | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
_classCallCheck(this, GLBParser); | ||
// Soft dependency on Draco, needs to be imported and supplied by app | ||
this.DracoDecoder = options.DracoDecoder; // Input | ||
this.glbArrayBuffer = glbArrayBuffer; // Result | ||
// Result | ||
this.binaryByteOffset = null; | ||
@@ -62,6 +58,7 @@ this.packedJson = null; | ||
key: "parse", | ||
value: function parse() { | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
value: function parse(glbArrayBuffer) { | ||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
// Input | ||
this.glbArrayBuffer = glbArrayBuffer; // Only parse once | ||
// Only parse once | ||
if (this.json === null && this.binaryByteOffset === null) { | ||
@@ -96,2 +93,41 @@ this.result = this._parse(options); | ||
return this.binaryByteOffset; | ||
} // Unpacks a bufferview into a new Uint8Array that is a view into the binary chunk | ||
}, { | ||
key: "getBufferView", | ||
value: function getBufferView(glTFBufferView) { | ||
var byteOffset = glTFBufferView.byteOffset + this.binaryByteOffset; | ||
return new Uint8Array(byteOffset, glTFBufferView.byteLength); | ||
} // Unpacks a glTF accessor into a new typed array that is a view into the binary chunk | ||
}, { | ||
key: "getBuffer", | ||
value: function getBuffer(glTFAccessor) { | ||
// Decode the glTF accessor format | ||
var ArrayType = ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY[glTFAccessor.componentType]; | ||
var components = ATTRIBUTE_TYPE_TO_COMPONENTS[glTFAccessor.type]; | ||
var bytesPerComponent = ATTRIBUTE_COMPONENT_TYPE_TO_BYTE_SIZE[glTFAccessor.componentType]; | ||
var length = glTFAccessor.count * components; | ||
var byteLength = glTFAccessor.count * components * bytesPerComponent; // Get the boundaries of the binary sub-chunk for this bufferView | ||
var glTFBufferView = this.json.bufferViews[glTFAccessor.bufferView]; | ||
assert(byteLength >= 0 && byteLength <= glTFBufferView.byteLength); | ||
var byteOffset = glTFBufferView.byteOffset + this.binaryByteOffset; | ||
return new ArrayType(this.arrayBuffer, byteOffset, length); | ||
} // Unpacks an image into an HTML image | ||
}, { | ||
key: "getImage", | ||
value: function getImage(glTFImage) { | ||
/* global window, Blob, Image */ | ||
var arrayBufferView = this.unpackBufferView(glTFImage.bufferView); | ||
var mimeType = glTFImage.mimeType || 'image/jpeg'; | ||
var blob = new Blob([arrayBufferView], { | ||
type: mimeType | ||
}); | ||
var urlCreator = window.URL || window.webkitURL; | ||
var imageUrl = urlCreator.createObjectURL(blob); | ||
var img = new Image(); | ||
img.src = imageUrl; | ||
return img; | ||
} // PRIVATE | ||
@@ -160,184 +196,2 @@ | ||
} | ||
}, { | ||
key: "unpackBinaryObjects", | ||
value: function unpackBinaryObjects() { | ||
var unpackedBinaryObjects = { | ||
images: [], | ||
accessors: [], | ||
meshes: [] | ||
}; | ||
var images = this.json.images || []; | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
try { | ||
for (var _iterator = images[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var glTFImage = _step.value; | ||
unpackedBinaryObjects.images.push(this._unpackImage(glTFImage)); | ||
} | ||
} catch (err) { | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion && _iterator.return != null) { | ||
_iterator.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
} | ||
} | ||
} | ||
var accessors = this.json.accessors || []; | ||
var _iteratorNormalCompletion2 = true; | ||
var _didIteratorError2 = false; | ||
var _iteratorError2 = undefined; | ||
try { | ||
for (var _iterator2 = accessors[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
var glTFAccessor = _step2.value; | ||
unpackedBinaryObjects.accessors.push(this._unpackAccessor(glTFAccessor)); | ||
} | ||
} catch (err) { | ||
_didIteratorError2 = true; | ||
_iteratorError2 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion2 && _iterator2.return != null) { | ||
_iterator2.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError2) { | ||
throw _iteratorError2; | ||
} | ||
} | ||
} | ||
var meshes = this.json.meshes || []; | ||
var _iteratorNormalCompletion3 = true; | ||
var _didIteratorError3 = false; | ||
var _iteratorError3 = undefined; | ||
try { | ||
for (var _iterator3 = meshes[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { | ||
var glTFMesh = _step3.value; | ||
unpackedBinaryObjects.meshes.push(this._unpackMesh(glTFMesh)); | ||
} | ||
} catch (err) { | ||
_didIteratorError3 = true; | ||
_iteratorError3 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion3 && _iterator3.return != null) { | ||
_iterator3.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError3) { | ||
throw _iteratorError3; | ||
} | ||
} | ||
} | ||
return unpackedBinaryObjects; | ||
} | ||
}, { | ||
key: "_unpackImage", | ||
value: function _unpackImage(glTFImage) { | ||
/* global window, Blob, Image */ | ||
var arrayBufferView = this.unpackBufferView(glTFImage.bufferView); | ||
var mimeType = glTFImage.mimeType || 'image/jpeg'; | ||
var blob = new Blob([arrayBufferView], { | ||
type: mimeType | ||
}); | ||
var urlCreator = window.URL || window.webkitURL; | ||
var imageUrl = urlCreator.createObjectURL(blob); | ||
var img = new Image(); | ||
img.src = imageUrl; | ||
return img; | ||
} | ||
}, { | ||
key: "_unpackAccessor", | ||
value: function _unpackAccessor(glTFAccessor) { | ||
// Decode the glTF accessor format | ||
var ArrayType = ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY[glTFAccessor.componentType]; | ||
var components = ATTRIBUTE_TYPE_TO_COMPONENTS[glTFAccessor.type]; | ||
var bytesPerComponent = ATTRIBUTE_COMPONENT_TYPE_TO_BYTE_SIZE[glTFAccessor.componentType]; | ||
var length = glTFAccessor.count * components; | ||
var byteLength = glTFAccessor.count * components * bytesPerComponent; // Get the boundaries of the binary sub-chunk for this bufferView | ||
var glTFBufferView = this.json.bufferViews[glTFAccessor.bufferView]; | ||
assert(byteLength >= 0 && byteLength <= glTFBufferView.byteLength); | ||
var byteOffset = glTFBufferView.byteOffset + this.binaryByteOffset; | ||
return new ArrayType(this.arrayBuffer, byteOffset, length); | ||
} // Create a new typed array as a view into the binary chunk | ||
}, { | ||
key: "_unpackBufferView", | ||
value: function _unpackBufferView(glTFBufferView) { | ||
var byteOffset = glTFBufferView.byteOffset + this.binaryByteOffset; | ||
return new Uint8Array(byteOffset, glTFBufferView.byteLength); | ||
} | ||
}, { | ||
key: "_unpackMesh", | ||
value: function _unpackMesh(mesh) { | ||
var unpackedPrimitives = []; | ||
var _iteratorNormalCompletion4 = true; | ||
var _didIteratorError4 = false; | ||
var _iteratorError4 = undefined; | ||
try { | ||
for (var _iterator4 = mesh.primitives[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { | ||
var primitive = _step4.value; | ||
var compressedMesh = primitive.extensions && primitive.extensions.UBER_draco_mesh_compression; | ||
var compressedPointCloud = primitive.extensions && primitive.extensions.UBER_draco_point_cloud_compression; | ||
var unpackedPrimitive = { | ||
mode: primitive.mode, | ||
material: primitive.material | ||
}; | ||
if (compressedMesh) { | ||
var dracoDecoder = new this.DracoDecoder(); | ||
var decodedData = dracoDecoder.decodeMesh(compressedMesh); | ||
dracoDecoder.destroy(); | ||
Object.assign(unpackedPrimitive, { | ||
indices: decodedData.indices, | ||
attributes: decodedData.attributes | ||
}); | ||
} else if (compressedPointCloud) { | ||
var _dracoDecoder = new this.DracoDecoder(); | ||
var _decodedData = _dracoDecoder.decodePointCloud(compressedPointCloud); | ||
_dracoDecoder.destroy(); | ||
Object.assign(unpackedPrimitive, { | ||
mode: 0, | ||
attributes: _decodedData.attributes | ||
}); | ||
} else {// No compression - just a glTF mesh primitive | ||
// TODO - Resolve all accessors | ||
} | ||
unpackedPrimitives.push(unpackedPrimitive); | ||
} | ||
} catch (err) { | ||
_didIteratorError4 = true; | ||
_iteratorError4 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion4 && _iterator4.return != null) { | ||
_iterator4.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError4) { | ||
throw _iteratorError4; | ||
} | ||
} | ||
} | ||
return unpackedPrimitives.length === 1 ? unpackedPrimitives[0] : unpackedPrimitives; | ||
} | ||
}]); | ||
@@ -344,0 +198,0 @@ |
@@ -49,3 +49,4 @@ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | ||
return _this; | ||
} // Encode as a textual JSON file with binary data in base64 data URLs. | ||
} // TODO - support encoding to non-GLB versions of glTF format | ||
// Encode as a textual JSON file with binary data in base64 data URLs. | ||
// encodeAsDataURLs(options) { | ||
@@ -52,0 +53,0 @@ // throw new Error('Not yet implemented'); |
@@ -13,14 +13,13 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function () { | ||
function GLTFParser(gltf) { | ||
function GLTFParser() { | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
_classCallCheck(this, GLTFParser); | ||
if (gltf instanceof ArrayBuffer) { | ||
gltf = new GLBParser(gltf).parse().json; | ||
} | ||
this.gltf = gltf; | ||
this.json = gltf; | ||
// TODO - move parsing to parse | ||
this.log = console; // eslint-disable-line | ||
this.out = {}; | ||
this.out = {}; // Soft dependency on Draco, needs to be imported and supplied by app | ||
this.DracoDecoder = options.DracoDecoder || null; | ||
} | ||
@@ -30,20 +29,21 @@ | ||
key: "parse", | ||
value: function parse() { | ||
var _this = this; | ||
value: function parse(gltf) { | ||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
this.glbParser = new GLBParser(); // GLTF can be JSON or binary (GLB) | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
// Load all images | ||
this.out.images = (this.gltf.images || []).map(function (image) { | ||
return _this.parseImage(image); | ||
}).filter(Boolean); // Parse all scenes | ||
if (gltf instanceof ArrayBuffer) { | ||
this.gltf = this.glbParser.parse(gltf).json; | ||
this.json = this.gltf; | ||
} else { | ||
this.gltf = gltf; | ||
this.json = gltf; | ||
} | ||
this.out.scenes = (this.gltf.scenes || []).map(function (scene) { | ||
return _this.parseImage(scene); | ||
}).filter(Boolean); | ||
this._loadLinkedAssets(options); // TODO - not implemented | ||
// this._postProcessGLTF(options); TODO - remove done differently now | ||
if (this.gltf.scene) { | ||
this.out.scene = this.gltf.scenes[this.gltf.scene]; | ||
} | ||
return this; | ||
this._resolveToTree(options); | ||
return this.gltf; | ||
} // Accessors | ||
@@ -80,4 +80,46 @@ | ||
return this.json.extensionsUsed; | ||
} | ||
} // DATA UNPACKING | ||
// Unpacks all the primitives in a mesh | ||
}, { | ||
key: "unpackMesh", | ||
value: function unpackMesh(mesh) { | ||
return mesh.primitives.map(this.unpackPrimitive.bind(this)); | ||
} // Unpacks one mesh primitive | ||
}, { | ||
key: "unpackPrimitive", | ||
value: function unpackPrimitive(primitive) { | ||
var compressedMesh = primitive.extensions && primitive.extensions.UBER_draco_mesh_compression; | ||
var compressedPointCloud = primitive.extensions && primitive.extensions.UBER_draco_point_cloud_compression; | ||
var unpackedPrimitive = { | ||
mode: primitive.mode, | ||
material: primitive.material | ||
}; | ||
if (compressedMesh) { | ||
var dracoDecoder = new this.DracoDecoder(); | ||
var decodedData = dracoDecoder.decodeMesh(compressedMesh); | ||
dracoDecoder.destroy(); | ||
Object.assign(unpackedPrimitive, { | ||
indices: decodedData.indices, | ||
attributes: decodedData.attributes | ||
}); | ||
} else if (compressedPointCloud) { | ||
var _dracoDecoder = new this.DracoDecoder(); | ||
var _decodedData = _dracoDecoder.decodePointCloud(compressedPointCloud); | ||
_dracoDecoder.destroy(); | ||
Object.assign(unpackedPrimitive, { | ||
mode: 0, | ||
attributes: _decodedData.attributes | ||
}); | ||
} else {// No compression - just a glTF mesh primitive | ||
// TODO - Resolve all accessors | ||
} | ||
} // PRIVATE | ||
}, { | ||
key: "getScene", | ||
@@ -136,4 +178,3 @@ value: function getScene(index) { | ||
return this._get('buffers', index); | ||
} // PRIVATE | ||
} | ||
}, { | ||
@@ -145,3 +186,3 @@ key: "_get", | ||
if (!object) { | ||
console.warn("glTF file error: Could not resolve ".concat(array, "[").concat(index, "]")); // eslint-disable-line | ||
console.warn("glTF file error: Could not find ".concat(array, "[").concat(index, "]")); // eslint-disable-line | ||
} | ||
@@ -151,43 +192,38 @@ | ||
} // PARSING HELPERS | ||
// Start loading linked assets | ||
}, { | ||
key: "parseScene", | ||
value: function parseScene() {} | ||
}, { | ||
key: "parseImage", | ||
value: function parseImage(image) { | ||
return this.config.createImage(image); | ||
key: "_loadLinkedAssets", | ||
value: function _loadLinkedAssets(options) {// TODO: Not implemented | ||
// TODO: Return a promise? | ||
} | ||
}, { | ||
key: "parseMesh", | ||
value: function parseMesh(mesh) { | ||
var _this2 = this; | ||
key: "_postProcessGLTF", | ||
value: function _postProcessGLTF() { | ||
var _this = this; | ||
// Each primitive is intended to correspond to a draw call | ||
var primitives = (mesh.primitives || []).map(function (primitive) { | ||
return _this2.parseMeshPrimitive(primitive); | ||
}); | ||
return primitives.length === 1 ? primitives[0] : this.config.createGroup(primitives); | ||
} | ||
}, { | ||
key: "parseMeshPrimitive", | ||
value: function parseMeshPrimitive(primitive) { | ||
// if (!primitive.attributes) | ||
// this.log.warn(primitive without attributes`) | ||
var attributes = primitive.attributes || {}; | ||
attributes = this.config.mapAttributes(attributes); | ||
return attributes; | ||
} | ||
}, { | ||
key: "parseAccessor", | ||
value: function parseAccessor(accessor) { | ||
return this.config.createBuffer(accessor); | ||
} // PREPARATION STEP: CROSS-LINK INDEX RESOLUTION, ENUM LOOKUP, CONVENIENCE CALCULATIONS | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
// Create all images (if requested) | ||
this.out.images = (this.gltf.images || []).map(function (image) { | ||
return _this.parseImage(image, options); | ||
}).filter(Boolean); // Normalize all scenes | ||
this.out.scenes = (this.gltf.scenes || []).map(function (scene) { | ||
return _this.parseScene(scene, options); | ||
}).filter(Boolean); | ||
if (this.gltf.scene) { | ||
this.out.scene = this.gltf.scenes[this.gltf.scene]; | ||
} | ||
return this; | ||
} // Convert indexed glTF structure into tree structure | ||
// PREPARATION STEP: CROSS-LINK INDEX RESOLUTION, ENUM LOOKUP, CONVENIENCE CALCULATIONS | ||
/* eslint-disable complexity */ | ||
}, { | ||
key: "resolve", | ||
value: function resolve() { | ||
var _this3 = this; | ||
key: "_resolveToTree", | ||
value: function _resolveToTree() { | ||
var _this2 = this; | ||
@@ -197,30 +233,30 @@ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
(gltf.bufferViews || []).forEach(function (bufView, i) { | ||
return _this3.resolveBufferView(bufView, i); | ||
return _this2._resolveBufferView(bufView, i); | ||
}); | ||
(gltf.images || []).forEach(function (image, i) { | ||
return _this3.resolveImage(image, i); | ||
return _this2._resolveImage(image, i, options); | ||
}); | ||
(gltf.samplers || []).forEach(function (sampler, i) { | ||
return _this3.resolveSampler(sampler, i); | ||
return _this2._resolveSampler(sampler, i); | ||
}); | ||
(gltf.textures || []).forEach(function (texture, i) { | ||
return _this3.resolveTexture(texture, i); | ||
return _this2._resolveTexture(texture, i); | ||
}); | ||
(gltf.accessors || []).forEach(function (accessor, i) { | ||
return _this3.resolveAccessor(accessor, i); | ||
return _this2._resolveAccessor(accessor, i); | ||
}); | ||
(gltf.materials || []).forEach(function (material, i) { | ||
return _this3.resolveMaterial(material, i); | ||
return _this2._resolveMaterial(material, i); | ||
}); | ||
(gltf.meshes || []).forEach(function (mesh, i) { | ||
return _this3.resolveMesh(mesh, i); | ||
return _this2._resolveMesh(mesh, i); | ||
}); | ||
(gltf.nodes || []).forEach(function (node, i) { | ||
return _this3.resolveNode(node, i); | ||
return _this2._resolveNode(node, i); | ||
}); | ||
(gltf.skins || []).forEach(function (skin, i) { | ||
return _this3.resolveSkin(skin, i); | ||
return _this2._resolveSkin(skin, i); | ||
}); | ||
(gltf.scenes || []).forEach(function (scene, i) { | ||
return _this3.resolveScene(scene, i); | ||
return _this2._resolveScene(scene, i); | ||
}); | ||
@@ -237,19 +273,19 @@ | ||
}, { | ||
key: "resolveScene", | ||
value: function resolveScene(scene, index) { | ||
var _this4 = this; | ||
key: "_resolveScene", | ||
value: function _resolveScene(scene, index) { | ||
var _this3 = this; | ||
scene.id = "scene-".concat(index); | ||
scene.nodes = (scene.nodes || []).map(function (node) { | ||
return _this4.getNode(node); | ||
return _this3.getNode(node); | ||
}); | ||
} | ||
}, { | ||
key: "resolveNode", | ||
value: function resolveNode(node, index) { | ||
var _this5 = this; | ||
key: "_resolveNode", | ||
value: function _resolveNode(node, index) { | ||
var _this4 = this; | ||
node.id = "node-".concat(index); | ||
node.children = (node.children || []).map(function (child) { | ||
return _this5.getNode(child); | ||
return _this4.getNode(child); | ||
}); | ||
@@ -270,4 +306,4 @@ | ||
}, { | ||
key: "resolveSkin", | ||
value: function resolveSkin(skin, index) { | ||
key: "_resolveSkin", | ||
value: function _resolveSkin(skin, index) { | ||
skin.id = "skin-".concat(index); | ||
@@ -277,4 +313,4 @@ skin.inverseBindMatrices = this.getAccessor(skin.inverseBindMatrices); | ||
}, { | ||
key: "resolveMesh", | ||
value: function resolveMesh(mesh, index) { | ||
key: "_resolveMesh", | ||
value: function _resolveMesh(mesh, index) { | ||
mesh.id = "mesh-".concat(index); | ||
@@ -317,4 +353,4 @@ var _iteratorNormalCompletion = true; | ||
}, { | ||
key: "resolveMaterial", | ||
value: function resolveMaterial(material, index) { | ||
key: "_resolveMaterial", | ||
value: function _resolveMaterial(material, index) { | ||
material.id = "material-".concat(index); | ||
@@ -347,4 +383,4 @@ | ||
}, { | ||
key: "resolveAccessor", | ||
value: function resolveAccessor(accessor, index) { | ||
key: "_resolveAccessor", | ||
value: function _resolveAccessor(accessor, index) { | ||
accessor.id = "accessor-".concat(index); | ||
@@ -358,4 +394,4 @@ accessor.bufferView = this.getBufferView(accessor.bufferView); // Look up enums | ||
}, { | ||
key: "resolveTexture", | ||
value: function resolveTexture(texture, index) { | ||
key: "_resolveTexture", | ||
value: function _resolveTexture(texture, index) { | ||
texture.id = "texture-".concat(index); | ||
@@ -366,4 +402,4 @@ texture.sampler = this.getSampler(texture.sampler); | ||
}, { | ||
key: "resolveSampler", | ||
value: function resolveSampler(sampler, index) { | ||
key: "_resolveSampler", | ||
value: function _resolveSampler(sampler, index) { | ||
sampler.id = "sampler-".concat(index); // Map textual parameters to GL parameter values | ||
@@ -379,4 +415,4 @@ | ||
}, { | ||
key: "resolveImage", | ||
value: function resolveImage(image, index) { | ||
key: "_resolveImage", | ||
value: function _resolveImage(image, index, options) { | ||
image.id = "image-".concat(index); | ||
@@ -386,8 +422,16 @@ | ||
image.bufferView = this.getBufferView(image.bufferView); | ||
} // TODO - Handle URIs etc | ||
} // TODO - Handle non-binary-chunk images, data URIs, URLs etc | ||
// TODO - Image creation could be done on getImage instead of during load | ||
var _options$createImages = options.createImages, | ||
createImages = _options$createImages === void 0 ? true : _options$createImages; | ||
if (createImages) { | ||
image.image = this.glbParser.unpackImage(image); | ||
} | ||
} | ||
}, { | ||
key: "resolveBufferView", | ||
value: function resolveBufferView(bufferView, index) { | ||
key: "_resolveBufferView", | ||
value: function _resolveBufferView(bufferView, index) { | ||
bufferView.id = "bufferView-".concat(index); | ||
@@ -398,11 +442,9 @@ bufferView.buffer = this.getBuffer(bufferView.buffer); | ||
}, { | ||
key: "resolveCamera", | ||
value: function resolveCamera(camera) { | ||
// TODO - resolve step should not create | ||
if (camera.perspective) { | ||
camera.matrix = this.config.createPerspectiveMatrix(camera.perspective); | ||
key: "_resolveCamera", | ||
value: function _resolveCamera(camera) { | ||
// TODO - create 4x4 matrices | ||
if (camera.perspective) {// camera.matrix = createPerspectiveMatrix(camera.perspective); | ||
} | ||
if (camera.orthographic) { | ||
camera.matrix = this.config.createOrthographicMatrix(camera.orthographic); | ||
if (camera.orthographic) {// camera.matrix = createOrthographicMatrix(camera.orthographic); | ||
} | ||
@@ -409,0 +451,0 @@ } |
{ | ||
"name": "@loaders.gl/gltf", | ||
"version": "0.4.8", | ||
"version": "0.5.0", | ||
"description": "Framework-independent loader for the glTF format", | ||
@@ -28,19 +28,19 @@ "license": "MIT", | ||
"dist", | ||
".bin", | ||
"README.md" | ||
], | ||
"bin": { | ||
"glbdump": "./.bin/glbdump" | ||
"glbdump": "./bin/glbdump.js" | ||
}, | ||
"scripts": { | ||
"clean": "rm -fr dist && mkdir -p dist", | ||
"build": "npm run clean && npm run build-es6 && npm run build-esm && npm run build-es5", | ||
"build": "npm run clean && npm run build-es6 && npm run build-esm && npm run build-es5 && npm run build-bin", | ||
"build-es6": "BABEL_ENV=es6 babel src --config-file ../../babel.config.js --out-dir dist/es6 --source-maps --ignore 'node_modules/'", | ||
"build-esm": "BABEL_ENV=esm babel src --config-file ../../babel.config.js --out-dir dist/esm --source-maps --ignore 'node_modules/'", | ||
"build-es5": "BABEL_ENV=es5 babel src --config-file ../../babel.config.js --out-dir dist/es5 --source-maps --ignore 'node_modules/'" | ||
"build-es5": "BABEL_ENV=es5 babel src --config-file ../../babel.config.js --out-dir dist/es5 --source-maps --ignore 'node_modules/'", | ||
"build-bin": "BABEL_ENV=es5 babel scripts --config-file ../../babel.config.js --out-dir dist/scripts --source-maps --ignore 'node_modules/'" | ||
}, | ||
"sideEffects": false, | ||
"dependencies": { | ||
"@loaders.gl/core": "^0.4.6" | ||
"@loaders.gl/core": "^0.5.0" | ||
} | ||
} |
@@ -48,2 +48,4 @@ /* eslint-disable camelcase, max-statements */ | ||
// ACCESSORS | ||
getByteLength() { | ||
@@ -53,2 +55,15 @@ return this.byteLength; | ||
// Checks if a binary buffer is a recognized image format (PNG, JPG, GIF, ...) | ||
isImage(imageData) { | ||
return isImage(imageData); | ||
} | ||
// MODIFERS | ||
// Encode the full glTF file as a binary GLB file | ||
// Returns an ArrayBuffer that represents the complete GLB image that can be saved to file | ||
encodeAsGLB(options = {}) { | ||
return this._createGLBBuffer(options); | ||
} | ||
// Add an extra application-defined key to the top-level data structure | ||
@@ -62,12 +77,23 @@ // By default packs JSON by extracting binary data and replacing it with JSON pointers | ||
// Encode the full glTF file as a binary GLB file | ||
// Returns an ArrayBuffer that represents the complete GLB image that can be saved to file | ||
encodeAsGLB(options = {}) { | ||
return this._createGlbBuffer(options); | ||
} | ||
// Add one untyped source buffer, create a matching glTF `bufferView`, and return its index | ||
addBufferView(buffer) { | ||
const byteLength = buffer.byteLength || buffer.length; | ||
// Returns an arrayBuffer together with JSON etc data. | ||
encodeAsGLBWithMetadata(options = {}) { | ||
const arrayBuffer = this._createGlbBuffer(options); | ||
return {arrayBuffer, json: this.json}; | ||
// 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 | ||
}); | ||
// 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 += 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; | ||
} | ||
@@ -78,3 +104,3 @@ | ||
addBuffer(sourceBuffer, accessor = {size: 3}) { | ||
const bufferViewIndex = this._addBufferView(sourceBuffer); | ||
const bufferViewIndex = this.addBufferView(sourceBuffer); | ||
@@ -94,11 +120,6 @@ // Add an accessor pointing to the new buffer view | ||
// Checks if a binary buffer is a recognized image format (PNG, JPG, GIF, ...) | ||
isImage(imageData) { | ||
return isImage(imageData); | ||
} | ||
// Adds a binary image. Builds glTF "JSON metadata" and saves buffer reference | ||
// Buffer will be copied into BIN chunk during "pack" | ||
addImage(imageData) { | ||
const bufferViewIndex = this._addBufferView(imageData); | ||
const bufferViewIndex = this.addBufferView(imageData); | ||
@@ -121,4 +142,5 @@ const glTFImage = { | ||
// PRIVATE | ||
// For testing | ||
_pack() { | ||
@@ -129,27 +151,2 @@ this._packBinaryChunk(); | ||
// PRIVATE | ||
// Add one source buffer, create a matchibng glTF `bufferView`, and return its index | ||
_addBufferView(buffer) { | ||
const 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 | ||
}); | ||
// 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 += 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 | ||
@@ -202,3 +199,3 @@ _packBinaryChunk() { | ||
// glb-file-format-specification | ||
_createGlbBuffer(options = {}) { | ||
_createGLBBuffer(options = {}) { | ||
// TODO - avoid double array buffer creation | ||
@@ -205,0 +202,0 @@ this._packBinaryChunk(); |
@@ -43,9 +43,3 @@ /* eslint-disable camelcase, max-statements */ | ||
constructor(glbArrayBuffer, options = {}) { | ||
// Soft dependency on Draco, needs to be imported and supplied by app | ||
this.DracoDecoder = options.DracoDecoder; | ||
// Input | ||
this.glbArrayBuffer = glbArrayBuffer; | ||
constructor(options = {}) { | ||
// Result | ||
@@ -58,3 +52,6 @@ this.binaryByteOffset = null; | ||
// Return the gltf JSON and the original arrayBuffer | ||
parse(options = {}) { | ||
parse(glbArrayBuffer, options = {}) { | ||
// Input | ||
this.glbArrayBuffer = glbArrayBuffer; | ||
// Only parse once | ||
@@ -87,2 +84,38 @@ if (this.json === null && this.binaryByteOffset === null) { | ||
// Unpacks a bufferview into a new Uint8Array that is a view into the binary chunk | ||
getBufferView(glTFBufferView) { | ||
const byteOffset = glTFBufferView.byteOffset + this.binaryByteOffset; | ||
return new Uint8Array(byteOffset, glTFBufferView.byteLength); | ||
} | ||
// Unpacks a glTF accessor into a new typed array that is a view into the binary chunk | ||
getBuffer(glTFAccessor) { | ||
// Decode the glTF accessor format | ||
const ArrayType = ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY[glTFAccessor.componentType]; | ||
const components = ATTRIBUTE_TYPE_TO_COMPONENTS[glTFAccessor.type]; | ||
const bytesPerComponent = ATTRIBUTE_COMPONENT_TYPE_TO_BYTE_SIZE[glTFAccessor.componentType]; | ||
const length = glTFAccessor.count * components; | ||
const byteLength = glTFAccessor.count * components * bytesPerComponent; | ||
// Get the boundaries of the binary sub-chunk for this bufferView | ||
const glTFBufferView = this.json.bufferViews[glTFAccessor.bufferView]; | ||
assert(byteLength >= 0 && byteLength <= glTFBufferView.byteLength); | ||
const byteOffset = glTFBufferView.byteOffset + this.binaryByteOffset; | ||
return new ArrayType(this.arrayBuffer, byteOffset, length); | ||
} | ||
// Unpacks an image into an HTML image | ||
getImage(glTFImage) { | ||
/* global window, Blob, Image */ | ||
const arrayBufferView = this.unpackBufferView(glTFImage.bufferView); | ||
const mimeType = glTFImage.mimeType || 'image/jpeg'; | ||
const blob = new Blob([arrayBufferView], {type: mimeType}); | ||
const urlCreator = window.URL || window.webkitURL; | ||
const imageUrl = urlCreator.createObjectURL(blob); | ||
const img = new Image(); | ||
img.src = imageUrl; | ||
return img; | ||
} | ||
// PRIVATE | ||
@@ -147,106 +180,2 @@ | ||
} | ||
unpackBinaryObjects() { | ||
const unpackedBinaryObjects = { | ||
images: [], | ||
accessors: [], | ||
meshes: [] | ||
}; | ||
const images = this.json.images || []; | ||
for (const glTFImage of images) { | ||
unpackedBinaryObjects.images.push(this._unpackImage(glTFImage)); | ||
} | ||
const accessors = this.json.accessors || []; | ||
for (const glTFAccessor of accessors) { | ||
unpackedBinaryObjects.accessors.push(this._unpackAccessor(glTFAccessor)); | ||
} | ||
const meshes = this.json.meshes || []; | ||
for (const glTFMesh of meshes) { | ||
unpackedBinaryObjects.meshes.push(this._unpackMesh(glTFMesh)); | ||
} | ||
return unpackedBinaryObjects; | ||
} | ||
_unpackImage(glTFImage) { | ||
/* global window, Blob, Image */ | ||
const arrayBufferView = this.unpackBufferView(glTFImage.bufferView); | ||
const mimeType = glTFImage.mimeType || 'image/jpeg'; | ||
const blob = new Blob([arrayBufferView], {type: mimeType}); | ||
const urlCreator = window.URL || window.webkitURL; | ||
const imageUrl = urlCreator.createObjectURL(blob); | ||
const img = new Image(); | ||
img.src = imageUrl; | ||
return img; | ||
} | ||
_unpackAccessor(glTFAccessor) { | ||
// Decode the glTF accessor format | ||
const ArrayType = ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY[glTFAccessor.componentType]; | ||
const components = ATTRIBUTE_TYPE_TO_COMPONENTS[glTFAccessor.type]; | ||
const bytesPerComponent = ATTRIBUTE_COMPONENT_TYPE_TO_BYTE_SIZE[glTFAccessor.componentType]; | ||
const length = glTFAccessor.count * components; | ||
const byteLength = glTFAccessor.count * components * bytesPerComponent; | ||
// Get the boundaries of the binary sub-chunk for this bufferView | ||
const glTFBufferView = this.json.bufferViews[glTFAccessor.bufferView]; | ||
assert(byteLength >= 0 && byteLength <= glTFBufferView.byteLength); | ||
const byteOffset = glTFBufferView.byteOffset + this.binaryByteOffset; | ||
return new ArrayType(this.arrayBuffer, byteOffset, length); | ||
} | ||
// Create a new typed array as a view into the binary chunk | ||
_unpackBufferView(glTFBufferView) { | ||
const byteOffset = glTFBufferView.byteOffset + this.binaryByteOffset; | ||
return new Uint8Array(byteOffset, glTFBufferView.byteLength); | ||
} | ||
_unpackMesh(mesh) { | ||
const unpackedPrimitives = []; | ||
for (const primitive of mesh.primitives) { | ||
const compressedMesh = | ||
primitive.extensions && primitive.extensions.UBER_draco_mesh_compression; | ||
const compressedPointCloud = | ||
primitive.extensions && primitive.extensions.UBER_draco_point_cloud_compression; | ||
const unpackedPrimitive = { | ||
mode: primitive.mode, | ||
material: primitive.material | ||
}; | ||
if (compressedMesh) { | ||
const dracoDecoder = new this.DracoDecoder(); | ||
const decodedData = dracoDecoder.decodeMesh(compressedMesh); | ||
dracoDecoder.destroy(); | ||
Object.assign(unpackedPrimitive, { | ||
indices: decodedData.indices, | ||
attributes: decodedData.attributes | ||
}); | ||
} else if (compressedPointCloud) { | ||
const dracoDecoder = new this.DracoDecoder(); | ||
const decodedData = dracoDecoder.decodePointCloud(compressedPointCloud); | ||
dracoDecoder.destroy(); | ||
Object.assign(unpackedPrimitive, { | ||
mode: 0, | ||
attributes: decodedData.attributes | ||
}); | ||
} else { | ||
// No compression - just a glTF mesh primitive | ||
// TODO - Resolve all accessors | ||
} | ||
unpackedPrimitives.push(unpackedPrimitive); | ||
} | ||
return unpackedPrimitives.length === 1 ? unpackedPrimitives[0] : unpackedPrimitives; | ||
} | ||
} |
@@ -23,2 +23,4 @@ /* eslint-disable camelcase, max-statements */ | ||
// TODO - support encoding to non-GLB versions of glTF format | ||
// Encode as a textual JSON file with binary data in base64 data URLs. | ||
@@ -25,0 +27,0 @@ // encodeAsDataURLs(options) { |
@@ -5,28 +5,28 @@ import {getBytesFromComponentType, getSizeFromAccessorType} from '../utils/gltf-type-utils'; | ||
export default class GLTFParser { | ||
constructor(gltf) { | ||
if (gltf instanceof ArrayBuffer) { | ||
gltf = new GLBParser(gltf).parse().json; | ||
} | ||
this.gltf = gltf; | ||
this.json = gltf; | ||
constructor(options = {}) { | ||
// TODO - move parsing to parse | ||
this.log = console; // eslint-disable-line | ||
this.out = {}; | ||
// Soft dependency on Draco, needs to be imported and supplied by app | ||
this.DracoDecoder = options.DracoDecoder || null; | ||
} | ||
parse(options = {}) { | ||
// Load all images | ||
this.out.images = (this.gltf.images || []) | ||
.map(image => this.parseImage(image)) | ||
.filter(Boolean); | ||
parse(gltf, options = {}) { | ||
this.glbParser = new GLBParser(); | ||
// Parse all scenes | ||
this.out.scenes = (this.gltf.scenes || []) | ||
.map(scene => this.parseImage(scene)) | ||
.filter(Boolean); | ||
if (this.gltf.scene) { | ||
this.out.scene = this.gltf.scenes[this.gltf.scene]; | ||
// GLTF can be JSON or binary (GLB) | ||
if (gltf instanceof ArrayBuffer) { | ||
this.gltf = this.glbParser.parse(gltf).json; | ||
this.json = this.gltf; | ||
} else { | ||
this.gltf = gltf; | ||
this.json = gltf; | ||
} | ||
return this; | ||
this._loadLinkedAssets(options); // TODO - not implemented | ||
// this._postProcessGLTF(options); TODO - remove done differently now | ||
this._resolveToTree(options); | ||
return this.gltf; | ||
} | ||
@@ -61,2 +61,48 @@ | ||
// DATA UNPACKING | ||
// Unpacks all the primitives in a mesh | ||
unpackMesh(mesh) { | ||
return mesh.primitives.map(this.unpackPrimitive.bind(this)); | ||
} | ||
// Unpacks one mesh primitive | ||
unpackPrimitive(primitive) { | ||
const compressedMesh = | ||
primitive.extensions && primitive.extensions.UBER_draco_mesh_compression; | ||
const compressedPointCloud = | ||
primitive.extensions && primitive.extensions.UBER_draco_point_cloud_compression; | ||
const unpackedPrimitive = { | ||
mode: primitive.mode, | ||
material: primitive.material | ||
}; | ||
if (compressedMesh) { | ||
const dracoDecoder = new this.DracoDecoder(); | ||
const decodedData = dracoDecoder.decodeMesh(compressedMesh); | ||
dracoDecoder.destroy(); | ||
Object.assign(unpackedPrimitive, { | ||
indices: decodedData.indices, | ||
attributes: decodedData.attributes | ||
}); | ||
} else if (compressedPointCloud) { | ||
const dracoDecoder = new this.DracoDecoder(); | ||
const decodedData = dracoDecoder.decodePointCloud(compressedPointCloud); | ||
dracoDecoder.destroy(); | ||
Object.assign(unpackedPrimitive, { | ||
mode: 0, | ||
attributes: decodedData.attributes | ||
}); | ||
} else { | ||
// No compression - just a glTF mesh primitive | ||
// TODO - Resolve all accessors | ||
} | ||
} | ||
// PRIVATE | ||
getScene(index) { | ||
@@ -106,8 +152,6 @@ return this._get('scenes', index); | ||
// PRIVATE | ||
_get(array, index) { | ||
const object = this.gltf[array] && this.gltf[array][index]; | ||
if (!object) { | ||
console.warn(`glTF file error: Could not resolve ${array}[${index}]`); // eslint-disable-line | ||
console.warn(`glTF file error: Could not find ${array}[${index}]`); // eslint-disable-line | ||
} | ||
@@ -119,50 +163,47 @@ return object; | ||
parseScene() { | ||
// Start loading linked assets | ||
_loadLinkedAssets(options) { | ||
// TODO: Not implemented | ||
// TODO: Return a promise? | ||
} | ||
parseImage(image) { | ||
return this.config.createImage(image); | ||
} | ||
_postProcessGLTF(options = {}) { | ||
// Create all images (if requested) | ||
this.out.images = (this.gltf.images || []) | ||
.map(image => this.parseImage(image, options)) | ||
.filter(Boolean); | ||
parseMesh(mesh) { | ||
// Each primitive is intended to correspond to a draw call | ||
const primitives = (mesh.primitives || []).map(primitive => this.parseMeshPrimitive(primitive)); | ||
// Normalize all scenes | ||
this.out.scenes = (this.gltf.scenes || []) | ||
.map(scene => this.parseScene(scene, options)) | ||
.filter(Boolean); | ||
return primitives.length === 1 ? primitives[0] : this.config.createGroup(primitives); | ||
} | ||
if (this.gltf.scene) { | ||
this.out.scene = this.gltf.scenes[this.gltf.scene]; | ||
} | ||
parseMeshPrimitive(primitive) { | ||
// if (!primitive.attributes) | ||
// this.log.warn(primitive without attributes`) | ||
let attributes = primitive.attributes || {}; | ||
attributes = this.config.mapAttributes(attributes); | ||
return attributes; | ||
return this; | ||
} | ||
parseAccessor(accessor) { | ||
return this.config.createBuffer(accessor); | ||
} | ||
// Convert indexed glTF structure into tree structure | ||
// PREPARATION STEP: CROSS-LINK INDEX RESOLUTION, ENUM LOOKUP, CONVENIENCE CALCULATIONS | ||
/* eslint-disable complexity */ | ||
resolve(options = {}) { | ||
_resolveToTree(options = {}) { | ||
const {gltf} = this; | ||
(gltf.bufferViews || []).forEach((bufView, i) => this.resolveBufferView(bufView, i)); | ||
(gltf.bufferViews || []).forEach((bufView, i) => this._resolveBufferView(bufView, i)); | ||
(gltf.images || []).forEach((image, i) => this.resolveImage(image, i)); | ||
(gltf.samplers || []).forEach((sampler, i) => this.resolveSampler(sampler, i)); | ||
(gltf.textures || []).forEach((texture, i) => this.resolveTexture(texture, i)); | ||
(gltf.images || []).forEach((image, i) => this._resolveImage(image, i, options)); | ||
(gltf.samplers || []).forEach((sampler, i) => this._resolveSampler(sampler, i)); | ||
(gltf.textures || []).forEach((texture, i) => this._resolveTexture(texture, i)); | ||
(gltf.accessors || []).forEach((accessor, i) => this.resolveAccessor(accessor, i)); | ||
(gltf.materials || []).forEach((material, i) => this.resolveMaterial(material, i)); | ||
(gltf.meshes || []).forEach((mesh, i) => this.resolveMesh(mesh, i)); | ||
(gltf.accessors || []).forEach((accessor, i) => this._resolveAccessor(accessor, i)); | ||
(gltf.materials || []).forEach((material, i) => this._resolveMaterial(material, i)); | ||
(gltf.meshes || []).forEach((mesh, i) => this._resolveMesh(mesh, i)); | ||
(gltf.nodes || []).forEach((node, i) => this.resolveNode(node, i)); | ||
(gltf.nodes || []).forEach((node, i) => this._resolveNode(node, i)); | ||
(gltf.skins || []).forEach((skin, i) => this.resolveSkin(skin, i)); | ||
(gltf.skins || []).forEach((skin, i) => this._resolveSkin(skin, i)); | ||
(gltf.scenes || []).forEach((scene, i) => this.resolveScene(scene, i)); | ||
(gltf.scenes || []).forEach((scene, i) => this._resolveScene(scene, i)); | ||
@@ -177,3 +218,3 @@ if (gltf.scene) { | ||
resolveScene(scene, index) { | ||
_resolveScene(scene, index) { | ||
scene.id = `scene-${index}`; | ||
@@ -183,3 +224,3 @@ scene.nodes = (scene.nodes || []).map(node => this.getNode(node)); | ||
resolveNode(node, index) { | ||
_resolveNode(node, index) { | ||
node.id = `node-${index}`; | ||
@@ -198,3 +239,3 @@ node.children = (node.children || []).map(child => this.getNode(child)); | ||
resolveSkin(skin, index) { | ||
_resolveSkin(skin, index) { | ||
skin.id = `skin-${index}`; | ||
@@ -204,3 +245,3 @@ skin.inverseBindMatrices = this.getAccessor(skin.inverseBindMatrices); | ||
resolveMesh(mesh, index) { | ||
_resolveMesh(mesh, index) { | ||
mesh.id = `mesh-${index}`; | ||
@@ -220,3 +261,3 @@ for (const primitive of mesh.primitives) { | ||
resolveMaterial(material, index) { | ||
_resolveMaterial(material, index) { | ||
material.id = `material-${index}`; | ||
@@ -244,6 +285,5 @@ if (material.normalTexture) { | ||
resolveAccessor(accessor, index) { | ||
_resolveAccessor(accessor, index) { | ||
accessor.id = `accessor-${index}`; | ||
accessor.bufferView = this.getBufferView(accessor.bufferView); | ||
// Look up enums | ||
@@ -255,3 +295,3 @@ accessor.bytesPerComponent = getBytesFromComponentType(accessor); | ||
resolveTexture(texture, index) { | ||
_resolveTexture(texture, index) { | ||
texture.id = `texture-${index}`; | ||
@@ -262,3 +302,3 @@ texture.sampler = this.getSampler(texture.sampler); | ||
resolveSampler(sampler, index) { | ||
_resolveSampler(sampler, index) { | ||
sampler.id = `sampler-${index}`; | ||
@@ -273,3 +313,3 @@ // Map textual parameters to GL parameter values | ||
resolveImage(image, index) { | ||
_resolveImage(image, index, options) { | ||
image.id = `image-${index}`; | ||
@@ -279,6 +319,12 @@ if (image.bufferView) { | ||
} | ||
// TODO - Handle URIs etc | ||
// TODO - Handle non-binary-chunk images, data URIs, URLs etc | ||
// TODO - Image creation could be done on getImage instead of during load | ||
const {createImages = true} = options; | ||
if (createImages) { | ||
image.image = this.glbParser.unpackImage(image); | ||
} | ||
} | ||
resolveBufferView(bufferView, index) { | ||
_resolveBufferView(bufferView, index) { | ||
bufferView.id = `bufferView-${index}`; | ||
@@ -290,11 +336,12 @@ bufferView.buffer = this.getBuffer(bufferView.buffer); | ||
resolveCamera(camera) { | ||
// TODO - resolve step should not create | ||
_resolveCamera(camera) { | ||
// TODO - create 4x4 matrices | ||
if (camera.perspective) { | ||
camera.matrix = this.config.createPerspectiveMatrix(camera.perspective); | ||
// camera.matrix = createPerspectiveMatrix(camera.perspective); | ||
} | ||
if (camera.orthographic) { | ||
camera.matrix = this.config.createOrthographicMatrix(camera.orthographic); | ||
// camera.matrix = createOrthographicMatrix(camera.orthographic); | ||
} | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
81
466401
4917
1
1
+ Added@loaders.gl/core@0.5.4(transitive)
- Removed@loaders.gl/core@0.4.6(transitive)
Updated@loaders.gl/core@^0.5.0