import Check from "../Core/Check.js"; import defaultValue from "../Core/defaultValue.js"; import defined from "../Core/defined.js"; import hasExtension from "./hasExtension.js"; import { MeshoptDecoder } from "meshoptimizer"; import ResourceLoader from "./ResourceLoader.js"; import ResourceLoaderState from "./ResourceLoaderState.js"; /** * Loads a glTF buffer view. *

* Implements the {@link ResourceLoader} interface. *

* * @alias GltfBufferViewLoader * @constructor * @augments ResourceLoader * * @param {object} options Object with the following properties: * @param {ResourceCache} options.resourceCache The {@link ResourceCache} (to avoid circular dependencies). * @param {object} options.gltf The glTF JSON. * @param {number} options.bufferViewId The buffer view ID. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to. * @param {string} [options.cacheKey] The cache key of the resource. * * @private */ function GltfBufferViewLoader(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); const resourceCache = options.resourceCache; const gltf = options.gltf; const bufferViewId = options.bufferViewId; const gltfResource = options.gltfResource; const baseResource = options.baseResource; const cacheKey = options.cacheKey; //>>includeStart('debug', pragmas.debug); Check.typeOf.func("options.resourceCache", resourceCache); Check.typeOf.object("options.gltf", gltf); Check.typeOf.number("options.bufferViewId", bufferViewId); Check.typeOf.object("options.gltfResource", gltfResource); Check.typeOf.object("options.baseResource", baseResource); //>>includeEnd('debug'); const bufferView = gltf.bufferViews[bufferViewId]; let bufferId = bufferView.buffer; let byteOffset = bufferView.byteOffset; let byteLength = bufferView.byteLength; let hasMeshopt = false; let meshoptByteStride; let meshoptCount; let meshoptMode; let meshoptFilter; if (hasExtension(bufferView, "EXT_meshopt_compression")) { const meshopt = bufferView.extensions.EXT_meshopt_compression; bufferId = meshopt.buffer; byteOffset = defaultValue(meshopt.byteOffset, 0); byteLength = meshopt.byteLength; hasMeshopt = true; meshoptByteStride = meshopt.byteStride; meshoptCount = meshopt.count; meshoptMode = meshopt.mode; meshoptFilter = defaultValue(meshopt.filter, "NONE"); } const buffer = gltf.buffers[bufferId]; this._hasMeshopt = hasMeshopt; this._meshoptByteStride = meshoptByteStride; this._meshoptCount = meshoptCount; this._meshoptMode = meshoptMode; this._meshoptFilter = meshoptFilter; this._resourceCache = resourceCache; this._gltfResource = gltfResource; this._baseResource = baseResource; this._buffer = buffer; this._bufferId = bufferId; this._byteOffset = byteOffset; this._byteLength = byteLength; this._cacheKey = cacheKey; this._bufferLoader = undefined; this._typedArray = undefined; this._state = ResourceLoaderState.UNLOADED; this._promise = undefined; } if (defined(Object.create)) { GltfBufferViewLoader.prototype = Object.create(ResourceLoader.prototype); GltfBufferViewLoader.prototype.constructor = GltfBufferViewLoader; } Object.defineProperties(GltfBufferViewLoader.prototype, { /** * The cache key of the resource. * * @memberof GltfBufferViewLoader.prototype * * @type {string} * @readonly * @private */ cacheKey: { get: function () { return this._cacheKey; }, }, /** * The typed array containing buffer view data. * * @memberof GltfBufferViewLoader.prototype * * @type {Uint8Array} * @readonly * @private */ typedArray: { get: function () { return this._typedArray; }, }, }); async function loadResources(loader) { try { const bufferLoader = getBufferLoader(loader); loader._bufferLoader = bufferLoader; await bufferLoader.load(); if (loader.isDestroyed()) { return; } const bufferTypedArray = bufferLoader.typedArray; const bufferViewTypedArray = new Uint8Array( bufferTypedArray.buffer, bufferTypedArray.byteOffset + loader._byteOffset, loader._byteLength ); // Unload the buffer loader.unload(); loader._typedArray = bufferViewTypedArray; if (loader._hasMeshopt) { const count = loader._meshoptCount; const byteStride = loader._meshoptByteStride; const result = new Uint8Array(count * byteStride); MeshoptDecoder.decodeGltfBuffer( result, count, byteStride, loader._typedArray, loader._meshoptMode, loader._meshoptFilter ); loader._typedArray = result; } loader._state = ResourceLoaderState.READY; return loader; } catch (error) { if (loader.isDestroyed()) { return; } loader.unload(); loader._state = ResourceLoaderState.FAILED; const errorMessage = "Failed to load buffer view"; throw loader.getError(errorMessage, error); } } /** * Loads the resource. * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. * @private */ GltfBufferViewLoader.prototype.load = async function () { if (defined(this._promise)) { return this._promise; } this._state = ResourceLoaderState.LOADING; this._promise = loadResources(this); return this._promise; }; function getBufferLoader(bufferViewLoader) { const resourceCache = bufferViewLoader._resourceCache; const buffer = bufferViewLoader._buffer; if (defined(buffer.uri)) { const baseResource = bufferViewLoader._baseResource; const resource = baseResource.getDerivedResource({ url: buffer.uri, }); return resourceCache.getExternalBufferLoader({ resource: resource, }); } return resourceCache.getEmbeddedBufferLoader({ parentResource: bufferViewLoader._gltfResource, bufferId: bufferViewLoader._bufferId, }); } /** * Unloads the resource. * @private */ GltfBufferViewLoader.prototype.unload = function () { if (defined(this._bufferLoader) && !this._bufferLoader.isDestroyed()) { this._resourceCache.unload(this._bufferLoader); } this._bufferLoader = undefined; this._typedArray = undefined; }; export default GltfBufferViewLoader;