diff --git a/packages/engine/Source/Scene/BufferLoader.js b/packages/engine/Source/Scene/BufferLoader.js index 7dad54d0d9f..d7d8869e117 100644 --- a/packages/engine/Source/Scene/BufferLoader.js +++ b/packages/engine/Source/Scene/BufferLoader.js @@ -23,33 +23,30 @@ import ResourceLoaderState from "./ResourceLoaderState.js"; * * @private */ -function BufferLoader(options) { - options = options ?? Frozen.EMPTY_OBJECT; - const typedArray = options.typedArray; - const resource = options.resource; - const cacheKey = options.cacheKey; +class BufferLoader extends ResourceLoader { + constructor(options) { + super(); - //>>includeStart('debug', pragmas.debug); - if (defined(typedArray) === defined(resource)) { - throw new DeveloperError( - "One of options.typedArray and options.resource must be defined.", - ); - } - //>>includeEnd('debug'); + options = options ?? Frozen.EMPTY_OBJECT; + const typedArray = options.typedArray; + const resource = options.resource; + const cacheKey = options.cacheKey; - this._typedArray = typedArray; - this._resource = resource; - this._cacheKey = cacheKey; - this._state = ResourceLoaderState.UNLOADED; - this._promise = undefined; -} + //>>includeStart('debug', pragmas.debug); + if (defined(typedArray) === defined(resource)) { + throw new DeveloperError( + "One of options.typedArray and options.resource must be defined.", + ); + } + //>>includeEnd('debug'); -if (defined(Object.create)) { - BufferLoader.prototype = Object.create(ResourceLoader.prototype); - BufferLoader.prototype.constructor = BufferLoader; -} + this._typedArray = typedArray; + this._resource = resource; + this._cacheKey = cacheKey; + this._state = ResourceLoaderState.UNLOADED; + this._promise = undefined; + } -Object.defineProperties(BufferLoader.prototype, { /** * The cache key of the resource. * @@ -59,11 +56,10 @@ Object.defineProperties(BufferLoader.prototype, { * @readonly * @private */ - cacheKey: { - get: function () { - return this._cacheKey; - }, - }, + get cacheKey() { + return this._cacheKey; + } + /** * The typed array containing the embedded buffer contents. * @@ -73,31 +69,45 @@ Object.defineProperties(BufferLoader.prototype, { * @readonly * @private */ - typedArray: { - get: function () { - return this._typedArray; - }, - }, -}); + get typedArray() { + return this._typedArray; + } -/** - * Loads the resource. - * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. - * @private - */ -BufferLoader.prototype.load = async function () { - if (defined(this._promise)) { + /** + * Loads the resource. + * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. + * @private + */ + async load() { + if (defined(this._promise)) { + return this._promise; + } + + if (defined(this._typedArray)) { + this._promise = Promise.resolve(this); + return this._promise; + } + + this._promise = loadExternalBuffer(this); return this._promise; } - if (defined(this._typedArray)) { - this._promise = Promise.resolve(this); - return this._promise; + /** + * Exposed for testing + * @private + */ + static _fetchArrayBuffer(resource) { + return resource.fetchArrayBuffer(); } - this._promise = loadExternalBuffer(this); - return this._promise; -}; + /** + * Unloads the resource. + * @private + */ + unload() { + this._typedArray = undefined; + } +} async function loadExternalBuffer(bufferLoader) { const resource = bufferLoader._resource; @@ -122,20 +132,4 @@ async function loadExternalBuffer(bufferLoader) { } } -/** - * Exposed for testing - * @private - */ -BufferLoader._fetchArrayBuffer = function (resource) { - return resource.fetchArrayBuffer(); -}; - -/** - * Unloads the resource. - * @private - */ -BufferLoader.prototype.unload = function () { - this._typedArray = undefined; -}; - export default BufferLoader; diff --git a/packages/engine/Source/Scene/DracoLoader.js b/packages/engine/Source/Scene/DracoLoader.js index 74a51750499..c7c25a2d12b 100644 --- a/packages/engine/Source/Scene/DracoLoader.js +++ b/packages/engine/Source/Scene/DracoLoader.js @@ -6,94 +6,95 @@ import TaskProcessor from "../Core/TaskProcessor.js"; /** * @private */ -function DracoLoader() {} +class DracoLoader { + static _getDecoderTaskProcessor() { + if (!defined(DracoLoader._decoderTaskProcessor)) { + const processor = new TaskProcessor( + "decodeDraco", + DracoLoader._maxDecodingConcurrency, + ); + processor + .initWebAssemblyModule({ + wasmBinaryFile: "ThirdParty/draco_decoder.wasm", + }) + .then(function (result) { + if (result) { + DracoLoader._taskProcessorReady = true; + } else { + DracoLoader._error = new RuntimeError( + "Draco decoder could not be initialized.", + ); + } + }) + .catch((error) => { + DracoLoader._error = error; + }); + DracoLoader._decoderTaskProcessor = processor; + } -// Maximum concurrency to use when decoding draco models -DracoLoader._maxDecodingConcurrency = Math.max( - FeatureDetection.hardwareConcurrency - 1, - 1, -); - -// Exposed for testing purposes -DracoLoader._decoderTaskProcessor = undefined; -DracoLoader._taskProcessorReady = false; -DracoLoader._error = undefined; -DracoLoader._getDecoderTaskProcessor = function () { - if (!defined(DracoLoader._decoderTaskProcessor)) { - const processor = new TaskProcessor( - "decodeDraco", - DracoLoader._maxDecodingConcurrency, - ); - processor - .initWebAssemblyModule({ - wasmBinaryFile: "ThirdParty/draco_decoder.wasm", - }) - .then(function (result) { - if (result) { - DracoLoader._taskProcessorReady = true; - } else { - DracoLoader._error = new RuntimeError( - "Draco decoder could not be initialized.", - ); - } - }) - .catch((error) => { - DracoLoader._error = error; - }); - DracoLoader._decoderTaskProcessor = processor; + return DracoLoader._decoderTaskProcessor; } - return DracoLoader._decoderTaskProcessor; -}; + /** + * Decodes a compressed point cloud. Returns undefined if the task cannot be scheduled. + * @private + * + * @exception {RuntimeError} Draco decoder could not be initialized. + */ + static decodePointCloud(parameters) { + const decoderTaskProcessor = DracoLoader._getDecoderTaskProcessor(); + if (defined(DracoLoader._error)) { + throw DracoLoader._error; + } -/** - * Decodes a compressed point cloud. Returns undefined if the task cannot be scheduled. - * @private - * - * @exception {RuntimeError} Draco decoder could not be initialized. - */ -DracoLoader.decodePointCloud = function (parameters) { - const decoderTaskProcessor = DracoLoader._getDecoderTaskProcessor(); - if (defined(DracoLoader._error)) { - throw DracoLoader._error; + if (!DracoLoader._taskProcessorReady) { + // The task processor is not ready to schedule tasks + return; + } + return decoderTaskProcessor.scheduleTask(parameters, [ + parameters.buffer.buffer, + ]); } - if (!DracoLoader._taskProcessorReady) { - // The task processor is not ready to schedule tasks - return; - } - return decoderTaskProcessor.scheduleTask(parameters, [ - parameters.buffer.buffer, - ]); -}; + /** + * Decodes a buffer view. Returns undefined if the task cannot be scheduled. + * + * @param {object} options Object with the following properties: + * @param {Uint8Array} options.array The typed array containing the buffer view data. + * @param {object} options.bufferView The glTF buffer view object. + * @param {Object} options.compressedAttributes The compressed attributes. + * @param {boolean} options.dequantizeInShader Whether POSITION and NORMAL attributes should be dequantized on the GPU. + * + * @returns {Promise} A promise that resolves to the decoded indices and attributes. + * @private + * + * @exception {RuntimeError} Draco decoder could not be initialized. + */ + static decodeBufferView(options) { + const decoderTaskProcessor = DracoLoader._getDecoderTaskProcessor(); -/** - * Decodes a buffer view. Returns undefined if the task cannot be scheduled. - * - * @param {object} options Object with the following properties: - * @param {Uint8Array} options.array The typed array containing the buffer view data. - * @param {object} options.bufferView The glTF buffer view object. - * @param {Object} options.compressedAttributes The compressed attributes. - * @param {boolean} options.dequantizeInShader Whether POSITION and NORMAL attributes should be dequantized on the GPU. - * - * @returns {Promise} A promise that resolves to the decoded indices and attributes. - * @private - * - * @exception {RuntimeError} Draco decoder could not be initialized. - */ -DracoLoader.decodeBufferView = function (options) { - const decoderTaskProcessor = DracoLoader._getDecoderTaskProcessor(); + if (defined(DracoLoader._error)) { + throw DracoLoader._error; + } - if (defined(DracoLoader._error)) { - throw DracoLoader._error; - } + if (!DracoLoader._taskProcessorReady) { + // The task processor is not ready to schedule tasks + return; + } - if (!DracoLoader._taskProcessorReady) { - // The task processor is not ready to schedule tasks - return; + return decoderTaskProcessor.scheduleTask(options, [options.array.buffer]); } +} + +// Maximum concurrency to use when decoding draco models +DracoLoader._maxDecodingConcurrency = Math.max( + FeatureDetection.hardwareConcurrency - 1, + 1, +); - return decoderTaskProcessor.scheduleTask(options, [options.array.buffer]); -}; +// Exposed for testing purposes +DracoLoader._decoderTaskProcessor = undefined; +DracoLoader._taskProcessorReady = false; +DracoLoader._error = undefined; export default DracoLoader; diff --git a/packages/engine/Source/Scene/GltfBufferViewLoader.js b/packages/engine/Source/Scene/GltfBufferViewLoader.js index 5cdbf16418e..d8eb6d0f03c 100644 --- a/packages/engine/Source/Scene/GltfBufferViewLoader.js +++ b/packages/engine/Source/Scene/GltfBufferViewLoader.js @@ -26,75 +26,72 @@ import ResourceLoaderState from "./ResourceLoaderState.js"; * * @private */ -function GltfBufferViewLoader(options) { - options = options ?? Frozen.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 = meshopt.byteOffset ?? 0; - byteLength = meshopt.byteLength; - - hasMeshopt = true; - meshoptByteStride = meshopt.byteStride; - meshoptCount = meshopt.count; - meshoptMode = meshopt.mode; - meshoptFilter = 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; -} +class GltfBufferViewLoader extends ResourceLoader { + constructor(options) { + super(); + + options = options ?? Frozen.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 = meshopt.byteOffset ?? 0; + byteLength = meshopt.byteLength; + + hasMeshopt = true; + meshoptByteStride = meshopt.byteStride; + meshoptCount = meshopt.count; + meshoptMode = meshopt.mode; + meshoptFilter = meshopt.filter ?? "NONE"; + } -if (defined(Object.create)) { - GltfBufferViewLoader.prototype = Object.create(ResourceLoader.prototype); - GltfBufferViewLoader.prototype.constructor = GltfBufferViewLoader; -} + 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; + } -Object.defineProperties(GltfBufferViewLoader.prototype, { /** * The cache key of the resource. * @@ -104,11 +101,10 @@ Object.defineProperties(GltfBufferViewLoader.prototype, { * @readonly * @private */ - cacheKey: { - get: function () { - return this._cacheKey; - }, - }, + get cacheKey() { + return this._cacheKey; + } + /** * The typed array containing buffer view data. * @@ -118,12 +114,38 @@ Object.defineProperties(GltfBufferViewLoader.prototype, { * @readonly * @private */ - typedArray: { - get: function () { - return this._typedArray; - }, - }, -}); + get typedArray() { + return this._typedArray; + } + + /** + * Loads the resource. + * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. + * @private + */ + async load() { + if (defined(this._promise)) { + return this._promise; + } + + this._state = ResourceLoaderState.LOADING; + this._promise = loadResources(this); + return this._promise; + } + + /** + * Unloads the resource. + * @private + */ + unload() { + if (defined(this._bufferLoader) && !this._bufferLoader.isDestroyed()) { + this._resourceCache.unload(this._bufferLoader); + } + + this._bufferLoader = undefined; + this._typedArray = undefined; + } +} /** * Load the resources associated with the loader. @@ -181,21 +203,6 @@ async function loadResources(loader) { } } -/** - * 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; -}; - /** * Get the buffer loader for the specified buffer view loader. * Attempts to retrieve from the resource cache first. If a buffer loader is @@ -226,17 +233,4 @@ function getBufferLoader(bufferViewLoader) { }); } -/** - * 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; diff --git a/packages/engine/Source/Scene/GltfDracoLoader.js b/packages/engine/Source/Scene/GltfDracoLoader.js index 9e604516730..f875d09e2ac 100644 --- a/packages/engine/Source/Scene/GltfDracoLoader.js +++ b/packages/engine/Source/Scene/GltfDracoLoader.js @@ -28,47 +28,44 @@ import VertexAttributeSemantic from "./VertexAttributeSemantic.js"; * * @private */ -function GltfDracoLoader(options) { - options = options ?? Frozen.EMPTY_OBJECT; - const resourceCache = options.resourceCache; - const gltf = options.gltf; - const primitive = options.primitive; - const draco = options.draco; - 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.object("options.primitive", primitive); - Check.typeOf.object("options.draco", draco); - Check.typeOf.object("options.gltfResource", gltfResource); - Check.typeOf.object("options.baseResource", baseResource); - //>>includeEnd('debug'); - - this._resourceCache = resourceCache; - this._gltfResource = gltfResource; - this._baseResource = baseResource; - this._gltf = gltf; - this._primitive = primitive; - this._draco = draco; - this._cacheKey = cacheKey; - this._bufferViewLoader = undefined; - this._bufferViewTypedArray = undefined; - this._decodePromise = undefined; - this._decodedData = undefined; - this._state = ResourceLoaderState.UNLOADED; - this._promise = undefined; - this._dracoError = undefined; -} - -if (defined(Object.create)) { - GltfDracoLoader.prototype = Object.create(ResourceLoader.prototype); - GltfDracoLoader.prototype.constructor = GltfDracoLoader; -} +class GltfDracoLoader extends ResourceLoader { + constructor(options) { + super(); + + options = options ?? Frozen.EMPTY_OBJECT; + const resourceCache = options.resourceCache; + const gltf = options.gltf; + const primitive = options.primitive; + const draco = options.draco; + 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.object("options.primitive", primitive); + Check.typeOf.object("options.draco", draco); + Check.typeOf.object("options.gltfResource", gltfResource); + Check.typeOf.object("options.baseResource", baseResource); + //>>includeEnd('debug'); + + this._resourceCache = resourceCache; + this._gltfResource = gltfResource; + this._baseResource = baseResource; + this._gltf = gltf; + this._primitive = primitive; + this._draco = draco; + this._cacheKey = cacheKey; + this._bufferViewLoader = undefined; + this._bufferViewTypedArray = undefined; + this._decodePromise = undefined; + this._decodedData = undefined; + this._state = ResourceLoaderState.UNLOADED; + this._promise = undefined; + this._dracoError = undefined; + } -Object.defineProperties(GltfDracoLoader.prototype, { /** * The cache key of the resource. * @@ -78,11 +75,10 @@ Object.defineProperties(GltfDracoLoader.prototype, { * @readonly * @private */ - cacheKey: { - get: function () { - return this._cacheKey; - }, - }, + get cacheKey() { + return this._cacheKey; + } + /** * The decoded data. * @@ -92,12 +88,122 @@ Object.defineProperties(GltfDracoLoader.prototype, { * @readonly * @private */ - decodedData: { - get: function () { - return this._decodedData; - }, - }, -}); + get decodedData() { + return this._decodedData; + } + + /** + * Loads the resource. + * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. + * @private + */ + async load() { + if (defined(this._promise)) { + return this._promise; + } + + this._state = ResourceLoaderState.LOADING; + this._promise = loadResources(this); + return this._promise; + } + + /** + * Processes the resource until it becomes ready. + * + * @param {FrameState} frameState The frame state. + * @private + */ + process(frameState) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("frameState", frameState); + //>>includeEnd('debug'); + + if (this._state === ResourceLoaderState.READY) { + return true; + } + + if (this._state !== ResourceLoaderState.PROCESSING) { + return false; + } + + if (defined(this._dracoError)) { + handleError(this, this._dracoError); + } + + if (!defined(this._bufferViewTypedArray)) { + // Not ready to decode the Draco buffer + return false; + } + + if (defined(this._decodePromise)) { + // Currently decoding + return false; + } + + const draco = this._draco; + const primitive = this._primitive; + const gltf = this._gltf; + const bufferViews = gltf.bufferViews; + const bufferViewId = draco.bufferView; + const bufferView = bufferViews[bufferViewId]; + const compressedAttributes = draco.attributes; + + // Skip de-quantization transform if present for floating point attributes. + // They will stay quantized in memory and be dequantized in the shader. + const attributesToSkipTransform = []; + + for (const attribute in primitive.attributes) { + if (primitive.attributes.hasOwnProperty(attribute)) { + const dracoAttributeType = getDracoAttributeType(attribute); + if (defined(dracoAttributeType)) { + const accessor = gltf.accessors[primitive.attributes[attribute]]; + if (accessor.componentType === ComponentDatatype.FLOAT) { + if (!attributesToSkipTransform.includes(dracoAttributeType)) { + attributesToSkipTransform.push(dracoAttributeType); + } + } + } + } + } + + const decodeOptions = { + // Need to make a copy of the typed array otherwise the underlying + // ArrayBuffer may be accessed on both the worker and the main thread. This + // leads to errors such as "ArrayBuffer at index 0 is already detached". + // PERFORMANCE_IDEA: Look into SharedArrayBuffer to get around this. + array: new Uint8Array(this._bufferViewTypedArray), + bufferView: bufferView, + compressedAttributes: compressedAttributes, + dequantizeInShader: true, + attributesToSkipTransform: attributesToSkipTransform, + }; + + const decodePromise = DracoLoader.decodeBufferView(decodeOptions); + + if (!defined(decodePromise)) { + // Cannot schedule task this frame + return false; + } + + this._decodePromise = processDecode(this, decodePromise); + } + + /** + * Unloads the resource. + * @private + */ + unload() { + if (defined(this._bufferViewLoader)) { + this._resourceCache.unload(this._bufferViewLoader); + } + + this._bufferViewLoader = undefined; + this._bufferViewTypedArray = undefined; + this._decodedData = undefined; + this._gltf = undefined; + this._primitive = undefined; + } +} async function loadResources(loader) { const resourceCache = loader._resourceCache; @@ -127,21 +233,6 @@ async function loadResources(loader) { } } -/** - * Loads the resource. - * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. - * @private - */ -GltfDracoLoader.prototype.load = async function () { - if (defined(this._promise)) { - return this._promise; - } - - this._state = ResourceLoaderState.LOADING; - this._promise = loadResources(this); - return this._promise; -}; - function handleError(dracoLoader, error) { dracoLoader.unload(); dracoLoader._state = ResourceLoaderState.FAILED; @@ -193,101 +284,4 @@ function getDracoAttributeType(attribute) { return undefined; } -/** - * Processes the resource until it becomes ready. - * - * @param {FrameState} frameState The frame state. - * @private - */ -GltfDracoLoader.prototype.process = function (frameState) { - //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("frameState", frameState); - //>>includeEnd('debug'); - - if (this._state === ResourceLoaderState.READY) { - return true; - } - - if (this._state !== ResourceLoaderState.PROCESSING) { - return false; - } - - if (defined(this._dracoError)) { - handleError(this, this._dracoError); - } - - if (!defined(this._bufferViewTypedArray)) { - // Not ready to decode the Draco buffer - return false; - } - - if (defined(this._decodePromise)) { - // Currently decoding - return false; - } - - const draco = this._draco; - const primitive = this._primitive; - const gltf = this._gltf; - const bufferViews = gltf.bufferViews; - const bufferViewId = draco.bufferView; - const bufferView = bufferViews[bufferViewId]; - const compressedAttributes = draco.attributes; - - // Skip de-quantization transform if present for floating point attributes. - // They will stay quantized in memory and be dequantized in the shader. - const attributesToSkipTransform = []; - - for (const attribute in primitive.attributes) { - if (primitive.attributes.hasOwnProperty(attribute)) { - const dracoAttributeType = getDracoAttributeType(attribute); - if (defined(dracoAttributeType)) { - const accessor = gltf.accessors[primitive.attributes[attribute]]; - if (accessor.componentType === ComponentDatatype.FLOAT) { - if (!attributesToSkipTransform.includes(dracoAttributeType)) { - attributesToSkipTransform.push(dracoAttributeType); - } - } - } - } - } - - const decodeOptions = { - // Need to make a copy of the typed array otherwise the underlying - // ArrayBuffer may be accessed on both the worker and the main thread. This - // leads to errors such as "ArrayBuffer at index 0 is already detached". - // PERFORMANCE_IDEA: Look into SharedArrayBuffer to get around this. - array: new Uint8Array(this._bufferViewTypedArray), - bufferView: bufferView, - compressedAttributes: compressedAttributes, - dequantizeInShader: true, - attributesToSkipTransform: attributesToSkipTransform, - }; - - const decodePromise = DracoLoader.decodeBufferView(decodeOptions); - - if (!defined(decodePromise)) { - // Cannot schedule task this frame - return false; - } - - this._decodePromise = processDecode(this, decodePromise); -}; - -/** - * Unloads the resource. - * @private - */ -GltfDracoLoader.prototype.unload = function () { - if (defined(this._bufferViewLoader)) { - this._resourceCache.unload(this._bufferViewLoader); - } - - this._bufferViewLoader = undefined; - this._bufferViewTypedArray = undefined; - this._decodedData = undefined; - this._gltf = undefined; - this._primitive = undefined; -}; - export default GltfDracoLoader; diff --git a/packages/engine/Source/Scene/GltfImageLoader.js b/packages/engine/Source/Scene/GltfImageLoader.js index 34cb4f58ac1..116c1abc35b 100644 --- a/packages/engine/Source/Scene/GltfImageLoader.js +++ b/packages/engine/Source/Scene/GltfImageLoader.js @@ -27,47 +27,44 @@ import ResourceLoaderState from "./ResourceLoaderState.js"; * * @private */ -function GltfImageLoader(options) { - options = options ?? Frozen.EMPTY_OBJECT; - const resourceCache = options.resourceCache; - const gltf = options.gltf; - const imageId = options.imageId; - 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.imageId", imageId); - Check.typeOf.object("options.gltfResource", gltfResource); - Check.typeOf.object("options.baseResource", baseResource); - //>>includeEnd('debug'); - - const image = gltf.images[imageId]; - const bufferViewId = image.bufferView; - const uri = image.uri; - - this._resourceCache = resourceCache; - this._gltfResource = gltfResource; - this._baseResource = baseResource; - this._gltf = gltf; - this._bufferViewId = bufferViewId; - this._uri = uri; - this._cacheKey = cacheKey; - this._bufferViewLoader = undefined; - this._image = undefined; - this._mipLevels = undefined; - this._state = ResourceLoaderState.UNLOADED; - this._promise = undefined; -} - -if (defined(Object.create)) { - GltfImageLoader.prototype = Object.create(ResourceLoader.prototype); - GltfImageLoader.prototype.constructor = GltfImageLoader; -} +class GltfImageLoader extends ResourceLoader { + constructor(options) { + super(); + + options = options ?? Frozen.EMPTY_OBJECT; + const resourceCache = options.resourceCache; + const gltf = options.gltf; + const imageId = options.imageId; + 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.imageId", imageId); + Check.typeOf.object("options.gltfResource", gltfResource); + Check.typeOf.object("options.baseResource", baseResource); + //>>includeEnd('debug'); + + const image = gltf.images[imageId]; + const bufferViewId = image.bufferView; + const uri = image.uri; + + this._resourceCache = resourceCache; + this._gltfResource = gltfResource; + this._baseResource = baseResource; + this._gltf = gltf; + this._bufferViewId = bufferViewId; + this._uri = uri; + this._cacheKey = cacheKey; + this._bufferViewLoader = undefined; + this._image = undefined; + this._mipLevels = undefined; + this._state = ResourceLoaderState.UNLOADED; + this._promise = undefined; + } -Object.defineProperties(GltfImageLoader.prototype, { /** * The cache key of the resource. * @@ -77,11 +74,10 @@ Object.defineProperties(GltfImageLoader.prototype, { * @readonly * @private */ - cacheKey: { - get: function () { - return this._cacheKey; - }, - }, + get cacheKey() { + return this._cacheKey; + } + /** * The image. * @@ -91,11 +87,10 @@ Object.defineProperties(GltfImageLoader.prototype, { * @readonly * @private */ - image: { - get: function () { - return this._image; - }, - }, + get image() { + return this._image; + } + /** * The mip levels. Only defined for KTX2 files containing mip levels. * @@ -105,31 +100,48 @@ Object.defineProperties(GltfImageLoader.prototype, { * @readonly * @private */ - mipLevels: { - get: function () { - return this._mipLevels; - }, - }, -}); - -/** - * Loads the resource. - * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. - * @private - */ -GltfImageLoader.prototype.load = function () { - if (defined(this._promise)) { - return this._promise; + get mipLevels() { + return this._mipLevels; } - if (defined(this._bufferViewId)) { - this._promise = loadFromBufferView(this); + /** + * Loads the resource. + * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. + * @private + */ + load() { + if (defined(this._promise)) { + return this._promise; + } + + if (defined(this._bufferViewId)) { + this._promise = loadFromBufferView(this); + return this._promise; + } + + this._promise = loadFromUri(this); return this._promise; } - this._promise = loadFromUri(this); - return this._promise; -}; + /** + * Unloads the resource. + * @private + */ + unload() { + if ( + defined(this._bufferViewLoader) && + !this._bufferViewLoader.isDestroyed() + ) { + this._resourceCache.unload(this._bufferViewLoader); + } + + this._bufferViewLoader = undefined; + this._uri = undefined; // Free in case the uri is a data uri + this._image = undefined; + this._mipLevels = undefined; + this._gltf = undefined; + } +} function getImageAndMipLevels(image) { // Images transcoded from KTX2 can contain multiple mip levels: @@ -295,25 +307,6 @@ function loadImageFromUri(resource) { }); } -/** - * Unloads the resource. - * @private - */ -GltfImageLoader.prototype.unload = function () { - if ( - defined(this._bufferViewLoader) && - !this._bufferViewLoader.isDestroyed() - ) { - this._resourceCache.unload(this._bufferViewLoader); - } - - this._bufferViewLoader = undefined; - this._uri = undefined; // Free in case the uri is a data uri - this._image = undefined; - this._mipLevels = undefined; - this._gltf = undefined; -}; - // Exposed for testing GltfImageLoader._loadImageFromTypedArray = loadImageFromTypedArray; diff --git a/packages/engine/Source/Scene/GltfIndexBufferLoader.js b/packages/engine/Source/Scene/GltfIndexBufferLoader.js index eb04334bc77..08ef55e7820 100644 --- a/packages/engine/Source/Scene/GltfIndexBufferLoader.js +++ b/packages/engine/Source/Scene/GltfIndexBufferLoader.js @@ -35,61 +35,58 @@ import ResourceLoaderState from "./ResourceLoaderState.js"; * @param {boolean} [options.loadTypedArray=false] Load the index buffer as a typed array. * @private */ -function GltfIndexBufferLoader(options) { - options = options ?? Frozen.EMPTY_OBJECT; - const resourceCache = options.resourceCache; - const gltf = options.gltf; - const accessorId = options.accessorId; - const gltfResource = options.gltfResource; - const baseResource = options.baseResource; - const primitive = options.primitive; - const draco = options.draco; - const cacheKey = options.cacheKey; - const asynchronous = options.asynchronous ?? true; - const loadBuffer = options.loadBuffer ?? false; - const loadTypedArray = options.loadTypedArray ?? false; - - //>>includeStart('debug', pragmas.debug); - Check.typeOf.func("options.resourceCache", resourceCache); - Check.typeOf.object("options.gltf", gltf); - Check.typeOf.number("options.accessorId", accessorId); - Check.typeOf.object("options.gltfResource", gltfResource); - Check.typeOf.object("options.baseResource", baseResource); - if (!loadBuffer && !loadTypedArray) { - throw new DeveloperError( - "At least one of loadBuffer and loadTypedArray must be true.", - ); +class GltfIndexBufferLoader extends ResourceLoader { + constructor(options) { + super(); + + options = options ?? Frozen.EMPTY_OBJECT; + const resourceCache = options.resourceCache; + const gltf = options.gltf; + const accessorId = options.accessorId; + const gltfResource = options.gltfResource; + const baseResource = options.baseResource; + const primitive = options.primitive; + const draco = options.draco; + const cacheKey = options.cacheKey; + const asynchronous = options.asynchronous ?? true; + const loadBuffer = options.loadBuffer ?? false; + const loadTypedArray = options.loadTypedArray ?? false; + + //>>includeStart('debug', pragmas.debug); + Check.typeOf.func("options.resourceCache", resourceCache); + Check.typeOf.object("options.gltf", gltf); + Check.typeOf.number("options.accessorId", accessorId); + Check.typeOf.object("options.gltfResource", gltfResource); + Check.typeOf.object("options.baseResource", baseResource); + if (!loadBuffer && !loadTypedArray) { + throw new DeveloperError( + "At least one of loadBuffer and loadTypedArray must be true.", + ); + } + //>>includeEnd('debug'); + + const indexDatatype = gltf.accessors[accessorId].componentType; + + this._resourceCache = resourceCache; + this._gltfResource = gltfResource; + this._baseResource = baseResource; + this._gltf = gltf; + this._accessorId = accessorId; + this._indexDatatype = indexDatatype; + this._primitive = primitive; + this._draco = draco; + this._cacheKey = cacheKey; + this._asynchronous = asynchronous; + this._loadBuffer = loadBuffer; + this._loadTypedArray = loadTypedArray; + this._bufferViewLoader = undefined; + this._dracoLoader = undefined; + this._typedArray = undefined; + this._buffer = undefined; + this._state = ResourceLoaderState.UNLOADED; + this._promise = undefined; } - //>>includeEnd('debug'); - - const indexDatatype = gltf.accessors[accessorId].componentType; - - this._resourceCache = resourceCache; - this._gltfResource = gltfResource; - this._baseResource = baseResource; - this._gltf = gltf; - this._accessorId = accessorId; - this._indexDatatype = indexDatatype; - this._primitive = primitive; - this._draco = draco; - this._cacheKey = cacheKey; - this._asynchronous = asynchronous; - this._loadBuffer = loadBuffer; - this._loadTypedArray = loadTypedArray; - this._bufferViewLoader = undefined; - this._dracoLoader = undefined; - this._typedArray = undefined; - this._buffer = undefined; - this._state = ResourceLoaderState.UNLOADED; - this._promise = undefined; -} - -if (defined(Object.create)) { - GltfIndexBufferLoader.prototype = Object.create(ResourceLoader.prototype); - GltfIndexBufferLoader.prototype.constructor = GltfIndexBufferLoader; -} -Object.defineProperties(GltfIndexBufferLoader.prototype, { /** * The cache key of the resource. * @@ -99,11 +96,10 @@ Object.defineProperties(GltfIndexBufferLoader.prototype, { * @readonly * @private */ - cacheKey: { - get: function () { - return this._cacheKey; - }, - }, + get cacheKey() { + return this._cacheKey; + } + /** * The index buffer. This is only defined when loadBuffer is true. * @@ -113,11 +109,10 @@ Object.defineProperties(GltfIndexBufferLoader.prototype, { * @readonly * @private */ - buffer: { - get: function () { - return this._buffer; - }, - }, + get buffer() { + return this._buffer; + } + /** * The typed array containing indices. This is only defined when loadTypedArray is true. * @@ -127,11 +122,9 @@ Object.defineProperties(GltfIndexBufferLoader.prototype, { * @readonly * @private */ - typedArray: { - get: function () { - return this._typedArray; - }, - }, + get typedArray() { + return this._typedArray; + } /** * The index datatype after decode. @@ -142,33 +135,166 @@ Object.defineProperties(GltfIndexBufferLoader.prototype, { * @readonly * @private */ - indexDatatype: { - get: function () { - return this._indexDatatype; - }, - }, -}); + get indexDatatype() { + return this._indexDatatype; + } -const scratchIndexBufferJob = new CreateIndexBufferJob(); + /** + * Loads the resource. + * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. + * @private + */ + async load() { + if (defined(this._promise)) { + return this._promise; + } -/** - * Loads the resource. - * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. - * @private - */ -GltfIndexBufferLoader.prototype.load = async function () { - if (defined(this._promise)) { + if (defined(this._draco)) { + this._promise = loadFromDraco(this); + return this._promise; + } + + this._promise = loadFromBufferView(this); return this._promise; } - if (defined(this._draco)) { - this._promise = loadFromDraco(this); - return this._promise; + /** + * Processes the resource until it becomes ready. + * + * @param {FrameState} frameState The frame state. + * @private + */ + process(frameState) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("frameState", frameState); + //>>includeEnd('debug'); + + if (this._state === ResourceLoaderState.READY) { + return true; + } + + if ( + this._state !== ResourceLoaderState.LOADED && + this._state !== ResourceLoaderState.PROCESSING + ) { + return false; + } + + let typedArray = this._typedArray; + let indexDatatype = this._indexDatatype; + + if (defined(this._dracoLoader)) { + try { + const ready = this._dracoLoader.process(frameState); + if (ready) { + const dracoLoader = this._dracoLoader; + typedArray = dracoLoader.decodedData.indices.typedArray; + this._typedArray = typedArray; + // The index datatype may be a smaller datatype after draco decode + indexDatatype = ComponentDatatype.fromTypedArray(typedArray); + this._indexDatatype = indexDatatype; + } + } catch (error) { + handleError(this, error); + } + } + + if (!defined(typedArray)) { + // Buffer view hasn't been loaded yet + return false; + } + + let buffer; + if (this._loadBuffer && this._asynchronous) { + const indexBufferJob = scratchIndexBufferJob; + indexBufferJob.set(typedArray, indexDatatype, frameState.context); + const jobScheduler = frameState.jobScheduler; + if (!jobScheduler.execute(indexBufferJob, JobType.BUFFER)) { + // Job scheduler is full. Try again next frame. + return false; + } + buffer = indexBufferJob.buffer; + } else if (this._loadBuffer) { + buffer = createIndexBuffer(typedArray, indexDatatype, frameState.context); + } + + // Unload everything except the index buffer and/or typed array. + this.unload(); + + this._buffer = buffer; + this._typedArray = this._loadTypedArray ? typedArray : undefined; + this._state = ResourceLoaderState.READY; + + this._resourceCache.statistics.addGeometryLoader(this); + return true; } - this._promise = loadFromBufferView(this); - return this._promise; -}; + /** + * Unloads the resource. + * @private + */ + unload() { + if (defined(this._buffer)) { + this._buffer.destroy(); + } + + const resourceCache = this._resourceCache; + + if ( + defined(this._bufferViewLoader) && + !this._bufferViewLoader.isDestroyed() + ) { + resourceCache.unload(this._bufferViewLoader); + } + + if (defined(this._dracoLoader)) { + resourceCache.unload(this._dracoLoader); + } + + this._bufferViewLoader = undefined; + this._dracoLoader = undefined; + this._typedArray = undefined; + this._buffer = undefined; + this._gltf = undefined; + this._primitive = undefined; + } +} + +class CreateIndexBufferJob { + constructor() { + this.typedArray = undefined; + this.indexDatatype = undefined; + this.context = undefined; + this.buffer = undefined; + } + + set(typedArray, indexDatatype, context) { + this.typedArray = typedArray; + this.indexDatatype = indexDatatype; + this.context = context; + } + + execute() { + this.buffer = createIndexBuffer( + this.typedArray, + this.indexDatatype, + this.context, + ); + } +} + +function createIndexBuffer(typedArray, indexDatatype, context) { + const buffer = Buffer.createIndexBuffer({ + typedArray: typedArray, + context: context, + usage: BufferUsage.STATIC_DRAW, + indexDatatype: indexDatatype, + }); + buffer.vertexArrayDestroyable = false; + return buffer; +} + +const scratchIndexBufferJob = new CreateIndexBufferJob(); async function loadFromDraco(indexBufferLoader) { indexBufferLoader._state = ResourceLoaderState.LOADING; @@ -281,141 +407,4 @@ function handleError(indexBufferLoader, error) { throw indexBufferLoader.getError(errorMessage, error); } -function CreateIndexBufferJob() { - this.typedArray = undefined; - this.indexDatatype = undefined; - this.context = undefined; - this.buffer = undefined; -} - -CreateIndexBufferJob.prototype.set = function ( - typedArray, - indexDatatype, - context, -) { - this.typedArray = typedArray; - this.indexDatatype = indexDatatype; - this.context = context; -}; - -CreateIndexBufferJob.prototype.execute = function () { - this.buffer = createIndexBuffer( - this.typedArray, - this.indexDatatype, - this.context, - ); -}; - -function createIndexBuffer(typedArray, indexDatatype, context) { - const buffer = Buffer.createIndexBuffer({ - typedArray: typedArray, - context: context, - usage: BufferUsage.STATIC_DRAW, - indexDatatype: indexDatatype, - }); - buffer.vertexArrayDestroyable = false; - return buffer; -} - -/** - * Processes the resource until it becomes ready. - * - * @param {FrameState} frameState The frame state. - * @private - */ -GltfIndexBufferLoader.prototype.process = function (frameState) { - //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("frameState", frameState); - //>>includeEnd('debug'); - - if (this._state === ResourceLoaderState.READY) { - return true; - } - - if ( - this._state !== ResourceLoaderState.LOADED && - this._state !== ResourceLoaderState.PROCESSING - ) { - return false; - } - - let typedArray = this._typedArray; - let indexDatatype = this._indexDatatype; - - if (defined(this._dracoLoader)) { - try { - const ready = this._dracoLoader.process(frameState); - if (ready) { - const dracoLoader = this._dracoLoader; - typedArray = dracoLoader.decodedData.indices.typedArray; - this._typedArray = typedArray; - // The index datatype may be a smaller datatype after draco decode - indexDatatype = ComponentDatatype.fromTypedArray(typedArray); - this._indexDatatype = indexDatatype; - } - } catch (error) { - handleError(this, error); - } - } - - if (!defined(typedArray)) { - // Buffer view hasn't been loaded yet - return false; - } - - let buffer; - if (this._loadBuffer && this._asynchronous) { - const indexBufferJob = scratchIndexBufferJob; - indexBufferJob.set(typedArray, indexDatatype, frameState.context); - const jobScheduler = frameState.jobScheduler; - if (!jobScheduler.execute(indexBufferJob, JobType.BUFFER)) { - // Job scheduler is full. Try again next frame. - return false; - } - buffer = indexBufferJob.buffer; - } else if (this._loadBuffer) { - buffer = createIndexBuffer(typedArray, indexDatatype, frameState.context); - } - - // Unload everything except the index buffer and/or typed array. - this.unload(); - - this._buffer = buffer; - this._typedArray = this._loadTypedArray ? typedArray : undefined; - this._state = ResourceLoaderState.READY; - - this._resourceCache.statistics.addGeometryLoader(this); - return true; -}; - -/** - * Unloads the resource. - * @private - */ -GltfIndexBufferLoader.prototype.unload = function () { - if (defined(this._buffer)) { - this._buffer.destroy(); - } - - const resourceCache = this._resourceCache; - - if ( - defined(this._bufferViewLoader) && - !this._bufferViewLoader.isDestroyed() - ) { - resourceCache.unload(this._bufferViewLoader); - } - - if (defined(this._dracoLoader)) { - resourceCache.unload(this._dracoLoader); - } - - this._bufferViewLoader = undefined; - this._dracoLoader = undefined; - this._typedArray = undefined; - this._buffer = undefined; - this._gltf = undefined; - this._primitive = undefined; -}; - export default GltfIndexBufferLoader; diff --git a/packages/engine/Source/Scene/GltfJsonLoader.js b/packages/engine/Source/Scene/GltfJsonLoader.js index 53035ef1637..aba3664c0b1 100644 --- a/packages/engine/Source/Scene/GltfJsonLoader.js +++ b/packages/engine/Source/Scene/GltfJsonLoader.js @@ -37,39 +37,36 @@ import ModelUtility from "./Model/ModelUtility.js"; * * @private */ -function GltfJsonLoader(options) { - options = options ?? Frozen.EMPTY_OBJECT; - const resourceCache = options.resourceCache; - const gltfResource = options.gltfResource; - const baseResource = options.baseResource; - const typedArray = options.typedArray; - const gltfJson = options.gltfJson; - const cacheKey = options.cacheKey; - - //>>includeStart('debug', pragmas.debug); - Check.typeOf.func("options.resourceCache", resourceCache); - Check.typeOf.object("options.gltfResource", gltfResource); - Check.typeOf.object("options.baseResource", baseResource); - //>>includeEnd('debug'); - - this._resourceCache = resourceCache; - this._gltfResource = gltfResource; - this._baseResource = baseResource; - this._typedArray = typedArray; - this._gltfJson = gltfJson; - this._cacheKey = cacheKey; - this._gltf = undefined; - this._bufferLoaders = []; - this._state = ResourceLoaderState.UNLOADED; - this._promise = undefined; -} - -if (defined(Object.create)) { - GltfJsonLoader.prototype = Object.create(ResourceLoader.prototype); - GltfJsonLoader.prototype.constructor = GltfJsonLoader; -} +class GltfJsonLoader extends ResourceLoader { + constructor(options) { + super(); + + options = options ?? Frozen.EMPTY_OBJECT; + const resourceCache = options.resourceCache; + const gltfResource = options.gltfResource; + const baseResource = options.baseResource; + const typedArray = options.typedArray; + const gltfJson = options.gltfJson; + const cacheKey = options.cacheKey; + + //>>includeStart('debug', pragmas.debug); + Check.typeOf.func("options.resourceCache", resourceCache); + Check.typeOf.object("options.gltfResource", gltfResource); + Check.typeOf.object("options.baseResource", baseResource); + //>>includeEnd('debug'); + + this._resourceCache = resourceCache; + this._gltfResource = gltfResource; + this._baseResource = baseResource; + this._typedArray = typedArray; + this._gltfJson = gltfJson; + this._cacheKey = cacheKey; + this._gltf = undefined; + this._bufferLoaders = []; + this._state = ResourceLoaderState.UNLOADED; + this._promise = undefined; + } -Object.defineProperties(GltfJsonLoader.prototype, { /** * The cache key of the resource. * @@ -79,11 +76,10 @@ Object.defineProperties(GltfJsonLoader.prototype, { * @readonly * @private */ - cacheKey: { - get: function () { - return this._cacheKey; - }, - }, + get cacheKey() { + return this._cacheKey; + } + /** * The glTF JSON. * @@ -93,38 +89,62 @@ Object.defineProperties(GltfJsonLoader.prototype, { * @readonly * @private */ - gltf: { - get: function () { - return this._gltf; - }, - }, -}); - -/** - * Loads the resource. - * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. - * @private - */ -GltfJsonLoader.prototype.load = async function () { - if (defined(this._promise)) { - return this._promise; + get gltf() { + return this._gltf; } - this._state = ResourceLoaderState.LOADING; + /** + * Loads the resource. + * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. + * @private + */ + async load() { + if (defined(this._promise)) { + return this._promise; + } + + this._state = ResourceLoaderState.LOADING; - if (defined(this._gltfJson)) { - this._promise = processGltfJson(this, this._gltfJson); + if (defined(this._gltfJson)) { + this._promise = processGltfJson(this, this._gltfJson); + return this._promise; + } + + if (defined(this._typedArray)) { + this._promise = processGltfTypedArray(this, this._typedArray); + return this._promise; + } + + this._promise = loadFromUri(this); return this._promise; } - if (defined(this._typedArray)) { - this._promise = processGltfTypedArray(this, this._typedArray); - return this._promise; + /** + * Unloads the resource. + * @private + */ + unload() { + const bufferLoaders = this._bufferLoaders; + const bufferLoadersLength = bufferLoaders.length; + for (let i = 0; i < bufferLoadersLength; ++i) { + bufferLoaders[i] = + !bufferLoaders[i].isDestroyed() && + this._resourceCache.unload(bufferLoaders[i]); + } + this._bufferLoaders.length = 0; + + this._gltf = undefined; } - this._promise = loadFromUri(this); - return this._promise; -}; + /** + * Exposed for testing + * + * @private + */ + _fetchGltf() { + return this._gltfResource.fetchArrayBuffer(); + } +} async function loadFromUri(gltfJsonLoader) { let typedArray; @@ -285,30 +305,4 @@ async function processGltfTypedArray(gltfJsonLoader, typedArray) { return processGltfJson(gltfJsonLoader, gltf); } -/** - * Unloads the resource. - * @private - */ -GltfJsonLoader.prototype.unload = function () { - const bufferLoaders = this._bufferLoaders; - const bufferLoadersLength = bufferLoaders.length; - for (let i = 0; i < bufferLoadersLength; ++i) { - bufferLoaders[i] = - !bufferLoaders[i].isDestroyed() && - this._resourceCache.unload(bufferLoaders[i]); - } - this._bufferLoaders.length = 0; - - this._gltf = undefined; -}; - -/** - * Exposed for testing - * - * @private - */ -GltfJsonLoader.prototype._fetchGltf = function () { - return this._gltfResource.fetchArrayBuffer(); -}; - export default GltfJsonLoader; diff --git a/packages/engine/Source/Scene/GltfLoader.js b/packages/engine/Source/Scene/GltfLoader.js index 478a59a38eb..7ba54347740 100644 --- a/packages/engine/Source/Scene/GltfLoader.js +++ b/packages/engine/Source/Scene/GltfLoader.js @@ -196,97 +196,94 @@ const GltfLoaderState = { * @param {boolean} [options.renameBatchIdSemantic=false] If true, rename _BATCHID or BATCHID to _FEATURE_ID_0. This is used for .b3dm models * @private */ -function GltfLoader(options) { - options = options ?? Frozen.EMPTY_OBJECT; - const { - gltfResource, - typedArray, - releaseGltfJson = false, - asynchronous = true, - incrementallyLoadTextures = true, - upAxis = Axis.Y, - forwardAxis = Axis.Z, - loadAttributesAsTypedArray = false, - loadAttributesFor2D = false, - enablePick = false, - loadIndicesForWireframe = false, - loadPrimitiveOutline = true, - loadForClassification = false, - renameBatchIdSemantic = false, - } = options; - - //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("options.gltfResource", gltfResource); - //>>includeEnd('debug'); - - const { baseResource = gltfResource.clone() } = options; - - this._gltfJson = options.gltfJson; - this._gltfResource = gltfResource; - this._baseResource = baseResource; - this._typedArray = typedArray; - this._releaseGltfJson = releaseGltfJson; - this._asynchronous = asynchronous; - this._incrementallyLoadTextures = incrementallyLoadTextures; - this._upAxis = upAxis; - this._forwardAxis = forwardAxis; - this._loadAttributesAsTypedArray = loadAttributesAsTypedArray; - this._loadAttributesFor2D = loadAttributesFor2D; - this._enablePick = enablePick; - this._loadIndicesForWireframe = loadIndicesForWireframe; - this._loadPrimitiveOutline = loadPrimitiveOutline; - this._loadForClassification = loadForClassification; - this._renameBatchIdSemantic = renameBatchIdSemantic; - - // When loading EXT_feature_metadata, the feature tables and textures - // are now stored as arrays like the newer EXT_structural_metadata extension. - // This requires sorting the dictionary keys for a consistent ordering. - this._sortedPropertyTableIds = undefined; - this._sortedFeatureTextureIds = undefined; - - this._gltfJsonLoader = undefined; - this._state = GltfLoaderState.NOT_LOADED; - this._textureState = GltfLoaderState.NOT_LOADED; - this._promise = undefined; - this._processError = undefined; - this._textureErrors = []; - - // Information about whether to load primitives as typed arrays or buffers, - // and whether post-processing is needed after loading (e.g. for - // generating outlines) - this._primitiveLoadPlans = []; - - // Loaders that need to be processed before the glTF becomes ready - this._loaderPromises = []; - this._textureLoaders = []; - this._texturesPromises = []; - this._textureCallbacks = []; - this._bufferViewLoaders = []; - this._geometryLoaders = []; - this._geometryCallbacks = []; - this._structuralMetadataLoader = undefined; - this._meshPrimitiveGpmLoader = undefined; - this._loadResourcesPromise = undefined; - this._resourcesLoaded = false; - this._texturesLoaded = false; - - this._supportedImageFormats = undefined; - - // In some cases where geometry post-processing is needed (like generating - // outlines) new attributes are added that may have GPU resources attached. - // The GltfLoader will own the resources and store them here. - this._postProcessBuffers = []; - - // Loaded results - this._components = undefined; -} - -if (defined(Object.create)) { - GltfLoader.prototype = Object.create(ResourceLoader.prototype); - GltfLoader.prototype.constructor = GltfLoader; -} - -Object.defineProperties(GltfLoader.prototype, { +class GltfLoader extends ResourceLoader { + constructor(options) { + super(); + + options = options ?? Frozen.EMPTY_OBJECT; + const { + gltfResource, + typedArray, + releaseGltfJson = false, + asynchronous = true, + incrementallyLoadTextures = true, + upAxis = Axis.Y, + forwardAxis = Axis.Z, + loadAttributesAsTypedArray = false, + loadAttributesFor2D = false, + enablePick = false, + loadIndicesForWireframe = false, + loadPrimitiveOutline = true, + loadForClassification = false, + renameBatchIdSemantic = false, + } = options; + + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("options.gltfResource", gltfResource); + //>>includeEnd('debug'); + + const { baseResource = gltfResource.clone() } = options; + + this._gltfJson = options.gltfJson; + this._gltfResource = gltfResource; + this._baseResource = baseResource; + this._typedArray = typedArray; + this._releaseGltfJson = releaseGltfJson; + this._asynchronous = asynchronous; + this._incrementallyLoadTextures = incrementallyLoadTextures; + this._upAxis = upAxis; + this._forwardAxis = forwardAxis; + this._loadAttributesAsTypedArray = loadAttributesAsTypedArray; + this._loadAttributesFor2D = loadAttributesFor2D; + this._enablePick = enablePick; + this._loadIndicesForWireframe = loadIndicesForWireframe; + this._loadPrimitiveOutline = loadPrimitiveOutline; + this._loadForClassification = loadForClassification; + this._renameBatchIdSemantic = renameBatchIdSemantic; + + // When loading EXT_feature_metadata, the feature tables and textures + // are now stored as arrays like the newer EXT_structural_metadata extension. + // This requires sorting the dictionary keys for a consistent ordering. + this._sortedPropertyTableIds = undefined; + this._sortedFeatureTextureIds = undefined; + + this._gltfJsonLoader = undefined; + this._state = GltfLoaderState.NOT_LOADED; + this._textureState = GltfLoaderState.NOT_LOADED; + this._promise = undefined; + this._processError = undefined; + this._textureErrors = []; + + // Information about whether to load primitives as typed arrays or buffers, + // and whether post-processing is needed after loading (e.g. for + // generating outlines) + this._primitiveLoadPlans = []; + + // Loaders that need to be processed before the glTF becomes ready + this._loaderPromises = []; + this._textureLoaders = []; + this._texturesPromises = []; + this._textureCallbacks = []; + this._bufferViewLoaders = []; + this._geometryLoaders = []; + this._geometryCallbacks = []; + this._structuralMetadataLoader = undefined; + this._meshPrimitiveGpmLoader = undefined; + this._loadResourcesPromise = undefined; + this._resourcesLoaded = false; + this._texturesLoaded = false; + + this._supportedImageFormats = undefined; + + // In some cases where geometry post-processing is needed (like generating + // outlines) new attributes are added that may have GPU resources attached. + // The GltfLoader will own the resources and store them here. + this._postProcessBuffers = []; + + // Loaded results + this._components = undefined; + } + /** * The cache key of the resource. * @@ -296,11 +293,10 @@ Object.defineProperties(GltfLoader.prototype, { * @readonly * @private */ - cacheKey: { - get: function () { - return undefined; - }, - }, + get cacheKey() { + return undefined; + } + /** * The loaded components. * @@ -310,11 +306,10 @@ Object.defineProperties(GltfLoader.prototype, { * @readonly * @private */ - components: { - get: function () { - return this._components; - }, - }, + get components() { + return this._components; + } + /** * The loaded glTF json. * @@ -324,14 +319,13 @@ Object.defineProperties(GltfLoader.prototype, { * @readonly * @private */ - gltfJson: { - get: function () { - if (defined(this._gltfJsonLoader)) { - return this._gltfJsonLoader.gltf; - } - return this._gltfJson; - }, - }, + get gltfJson() { + if (defined(this._gltfJsonLoader)) { + return this._gltfJsonLoader.gltf; + } + return this._gltfJson; + } + /** * Returns true if textures are loaded separately from the other glTF resources. * @@ -341,11 +335,10 @@ Object.defineProperties(GltfLoader.prototype, { * @readonly * @private */ - incrementallyLoadTextures: { - get: function () { - return this._incrementallyLoadTextures; - }, - }, + get incrementallyLoadTextures() { + return this._incrementallyLoadTextures; + } + /** * true if textures are loaded, useful when incrementallyLoadTextures is true * @@ -355,12 +348,193 @@ Object.defineProperties(GltfLoader.prototype, { * @readonly * @private */ - texturesLoaded: { - get: function () { - return this._texturesLoaded; - }, - }, -}); + get texturesLoaded() { + return this._texturesLoaded; + } + + /** + * Loads the resource. + * @returns {Promise.} A promise which resolves to the loader when the resource loading is completed. + * @exception {RuntimeError} Unsupported glTF version + * @exception {RuntimeError} Unsupported glTF Extension + * @private + */ + async load() { + if (defined(this._promise)) { + return this._promise; + } + + this._promise = loadGltfJson(this); + return this._promise; + } + + /** + * Process loaders other than textures + * @private + */ + _process(frameState) { + if (this._state === GltfLoaderState.READY) { + return true; + } + + if (this._state === GltfLoaderState.PROCESSING) { + processLoaders(this, frameState); + } + + if ( + this._resourcesLoaded && + this._state === GltfLoaderState.POST_PROCESSING + ) { + postProcessGeometry(this, frameState.context); + this._state = GltfLoaderState.PROCESSED; + } + + if (this._resourcesLoaded && this._state === GltfLoaderState.PROCESSED) { + // The buffer views can be unloaded once the data is copied. + unloadBufferViewLoaders(this); + + // Similarly, if the glTF was loaded from a typed array, release the memory + this._typedArray = undefined; + + this._state = GltfLoaderState.READY; + return true; + } + + return false; + } + + /** + * Process textures other than textures + * @private + */ + _processTextures(frameState) { + if (this._textureState === GltfLoaderState.READY) { + return true; + } + + if (this._textureState !== GltfLoaderState.PROCESSING) { + return false; + } + + let ready = true; + const textureLoaders = this._textureLoaders; + for (let i = 0; i < textureLoaders.length; ++i) { + const textureReady = textureLoaders[i].process(frameState); + if (textureReady && defined(this._textureCallbacks[i])) { + this._textureCallbacks[i](); + this._textureCallbacks[i] = undefined; + } + + ready = ready && textureReady; + } + + if (!ready) { + return false; + } + + this._textureState = GltfLoaderState.READY; + this._texturesLoaded = true; + return true; + } + + /** + * Processes the resource until it becomes ready. + * + * @param {FrameState} frameState The frame state. + * @private + */ + process(frameState) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("frameState", frameState); + //>>includeEnd('debug'); + + if ( + this._state === GltfLoaderState.LOADED && + !defined(this._loadResourcesPromise) + ) { + this._loadResourcesPromise = loadResources(this, frameState) + .then(() => { + this._resourcesLoaded = true; + }) + .catch((error) => { + this._processError = error; + }); + } + + if (defined(this._processError)) { + this._state = GltfLoaderState.FAILED; + const error = this._processError; + this._processError = undefined; + handleError(this, error); + } + + // Pop the next error of the list in case there are multiple + const textureError = this._textureErrors.pop(); + if (defined(textureError)) { + // There shouldn't be the need to completely unload in this case. Just throw the error. + const error = this.getError("Failed to load glTF texture", textureError); + error.name = "TextureError"; + throw error; + } + + if (this._state === GltfLoaderState.FAILED) { + return false; + } + + let ready = false; + try { + ready = this._process(frameState); + } catch (error) { + this._state = GltfLoaderState.FAILED; + handleError(this, error); + } + + // Since textures can be loaded independently and are handled through a separate promise, they are processed in their own function + let texturesReady = false; + try { + texturesReady = this._processTextures(frameState); + } catch (error) { + this._textureState = GltfLoaderState.FAILED; + handleError(this, error); + } + + if (this._incrementallyLoadTextures) { + return ready; + } + + return ready && texturesReady; + } + + /** + * Returns whether the resource has been unloaded. + * @private + */ + isUnloaded() { + return this._state === GltfLoaderState.UNLOADED; + } + + /** + * Unloads the resource. + * @private + */ + unload() { + if (defined(this._gltfJsonLoader) && !this._gltfJsonLoader.isDestroyed()) { + ResourceCache.unload(this._gltfJsonLoader); + } + this._gltfJsonLoader = undefined; + + unloadTextures(this); + unloadBufferViewLoaders(this); + unloadGeometry(this); + unloadGeneratedAttributes(this); + unloadStructuralMetadata(this); + unloadMeshPrimitiveGpm(this); + + this._components = undefined; + this._typedArray = undefined; + this._state = GltfLoaderState.UNLOADED; + } +} /** * Loads the gltf object @@ -433,22 +607,6 @@ async function loadResources(loader, frameState) { return promise; } -/** - * Loads the resource. - * @returns {Promise.} A promise which resolves to the loader when the resource loading is completed. - * @exception {RuntimeError} Unsupported glTF version - * @exception {RuntimeError} Unsupported glTF Extension - * @private - */ -GltfLoader.prototype.load = async function () { - if (defined(this._promise)) { - return this._promise; - } - - this._promise = loadGltfJson(this); - return this._promise; -}; - function handleError(gltfLoader, error) { gltfLoader.unload(); const errorMessage = "Failed to load glTF"; @@ -545,143 +703,6 @@ function gatherPostProcessBuffers(loader, primitiveLoadPlan) { } } -/** - * Process loaders other than textures - * @private - */ -GltfLoader.prototype._process = function (frameState) { - if (this._state === GltfLoaderState.READY) { - return true; - } - - if (this._state === GltfLoaderState.PROCESSING) { - processLoaders(this, frameState); - } - - if ( - this._resourcesLoaded && - this._state === GltfLoaderState.POST_PROCESSING - ) { - postProcessGeometry(this, frameState.context); - this._state = GltfLoaderState.PROCESSED; - } - - if (this._resourcesLoaded && this._state === GltfLoaderState.PROCESSED) { - // The buffer views can be unloaded once the data is copied. - unloadBufferViewLoaders(this); - - // Similarly, if the glTF was loaded from a typed array, release the memory - this._typedArray = undefined; - - this._state = GltfLoaderState.READY; - return true; - } - - return false; -}; - -/** - * Process textures other than textures - * @private - */ -GltfLoader.prototype._processTextures = function (frameState) { - if (this._textureState === GltfLoaderState.READY) { - return true; - } - - if (this._textureState !== GltfLoaderState.PROCESSING) { - return false; - } - - let ready = true; - const textureLoaders = this._textureLoaders; - for (let i = 0; i < textureLoaders.length; ++i) { - const textureReady = textureLoaders[i].process(frameState); - if (textureReady && defined(this._textureCallbacks[i])) { - this._textureCallbacks[i](); - this._textureCallbacks[i] = undefined; - } - - ready = ready && textureReady; - } - - if (!ready) { - return false; - } - - this._textureState = GltfLoaderState.READY; - this._texturesLoaded = true; - return true; -}; - -/** - * Processes the resource until it becomes ready. - * - * @param {FrameState} frameState The frame state. - * @private - */ -GltfLoader.prototype.process = function (frameState) { - //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("frameState", frameState); - //>>includeEnd('debug'); - - if ( - this._state === GltfLoaderState.LOADED && - !defined(this._loadResourcesPromise) - ) { - this._loadResourcesPromise = loadResources(this, frameState) - .then(() => { - this._resourcesLoaded = true; - }) - .catch((error) => { - this._processError = error; - }); - } - - if (defined(this._processError)) { - this._state = GltfLoaderState.FAILED; - const error = this._processError; - this._processError = undefined; - handleError(this, error); - } - - // Pop the next error of the list in case there are multiple - const textureError = this._textureErrors.pop(); - if (defined(textureError)) { - // There shouldn't be the need to completely unload in this case. Just throw the error. - const error = this.getError("Failed to load glTF texture", textureError); - error.name = "TextureError"; - throw error; - } - - if (this._state === GltfLoaderState.FAILED) { - return false; - } - - let ready = false; - try { - ready = this._process(frameState); - } catch (error) { - this._state = GltfLoaderState.FAILED; - handleError(this, error); - } - - // Since textures can be loaded independently and are handled through a separate promise, they are processed in their own function - let texturesReady = false; - try { - texturesReady = this._processTextures(frameState); - } catch (error) { - this._textureState = GltfLoaderState.FAILED; - handleError(this, error); - } - - if (this._incrementallyLoadTextures) { - return ready; - } - - return ready && texturesReady; -}; - function getVertexBufferLoader( loader, accessorId, @@ -2966,34 +2987,4 @@ function unloadMeshPrimitiveGpm(loader) { } } -/** - * Returns whether the resource has been unloaded. - * @private - */ -GltfLoader.prototype.isUnloaded = function () { - return this._state === GltfLoaderState.UNLOADED; -}; - -/** - * Unloads the resource. - * @private - */ -GltfLoader.prototype.unload = function () { - if (defined(this._gltfJsonLoader) && !this._gltfJsonLoader.isDestroyed()) { - ResourceCache.unload(this._gltfJsonLoader); - } - this._gltfJsonLoader = undefined; - - unloadTextures(this); - unloadBufferViewLoaders(this); - unloadGeometry(this); - unloadGeneratedAttributes(this); - unloadStructuralMetadata(this); - unloadMeshPrimitiveGpm(this); - - this._components = undefined; - this._typedArray = undefined; - this._state = GltfLoaderState.UNLOADED; -}; - export default GltfLoader; diff --git a/packages/engine/Source/Scene/GltfSpzLoader.js b/packages/engine/Source/Scene/GltfSpzLoader.js index d00b2eab392..bfa6f9f4cc2 100644 --- a/packages/engine/Source/Scene/GltfSpzLoader.js +++ b/packages/engine/Source/Scene/GltfSpzLoader.js @@ -24,47 +24,44 @@ import { loadSpz } from "@spz-loader/core"; * * @private */ -function GltfSpzLoader(options) { - options = options ?? Frozen.EMPTY_OBJECT; - const resourceCache = options.resourceCache; - const gltf = options.gltf; - const primitive = options.primitive; - const spz = options.spz; - 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.object("options.primitive", primitive); - Check.typeOf.object("options.spz", spz); - Check.typeOf.object("options.gltfResource", gltfResource); - Check.typeOf.object("options.baseResource", baseResource); - //>>includeEnd('debug'); - - this._resourceCache = resourceCache; - this._gltfResource = gltfResource; - this._baseResource = baseResource; - this._gltf = gltf; - this._primitive = primitive; - this._spz = spz; - this._cacheKey = cacheKey; - this._bufferViewLoader = undefined; - this._bufferViewTypedArray = undefined; - this._decodePromise = undefined; - this._decodedData = undefined; - this._state = ResourceLoaderState.UNLOADED; - this._promise = undefined; - this._spzError = undefined; -} - -if (defined(Object.create)) { - GltfSpzLoader.prototype = Object.create(ResourceLoader.prototype); - GltfSpzLoader.prototype.constructor = GltfSpzLoader; -} +class GltfSpzLoader extends ResourceLoader { + constructor(options) { + super(); + + options = options ?? Frozen.EMPTY_OBJECT; + const resourceCache = options.resourceCache; + const gltf = options.gltf; + const primitive = options.primitive; + const spz = options.spz; + 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.object("options.primitive", primitive); + Check.typeOf.object("options.spz", spz); + Check.typeOf.object("options.gltfResource", gltfResource); + Check.typeOf.object("options.baseResource", baseResource); + //>>includeEnd('debug'); + + this._resourceCache = resourceCache; + this._gltfResource = gltfResource; + this._baseResource = baseResource; + this._gltf = gltf; + this._primitive = primitive; + this._spz = spz; + this._cacheKey = cacheKey; + this._bufferViewLoader = undefined; + this._bufferViewTypedArray = undefined; + this._decodePromise = undefined; + this._decodedData = undefined; + this._state = ResourceLoaderState.UNLOADED; + this._promise = undefined; + this._spzError = undefined; + } -Object.defineProperties(GltfSpzLoader.prototype, { /** * The cache key of the resource. * @memberof GltfSpzLoader.prototype @@ -72,11 +69,10 @@ Object.defineProperties(GltfSpzLoader.prototype, { * @readonly * @private */ - cacheKey: { - get: function () { - return this._cacheKey; - }, - }, + get cacheKey() { + return this._cacheKey; + } + /** * The decoded SPZ data. * @memberof GltfSpzLoader.prototype @@ -84,12 +80,82 @@ Object.defineProperties(GltfSpzLoader.prototype, { * @readonly * @private */ - decodedData: { - get: function () { - return this._decodedData; - }, - }, -}); + get decodedData() { + return this._decodedData; + } + + /** + * Loads the SPZ resource. + * @returns {Promise} A promise that resolves to the resource when the SPZ is loaded. + * @private + */ + async load() { + if (defined(this._promise)) { + return this._promise; + } + + this._state = ResourceLoaderState.LOADING; + this._promise = loadResources(this); + return this._promise; + } + + /** + * Processes the SPZ resource. + * @param {FrameState} frameState The frame state. + * @private + */ + process(frameState) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("frameState", frameState); + //>>includeEnd('debug'); + + if (this._state === ResourceLoaderState.READY) { + return true; + } + + if (this._state !== ResourceLoaderState.PROCESSING) { + return false; + } + + if (defined(this._spzError)) { + handleError(this, this._spzError); + } + + if (!defined(this._bufferViewTypedArray)) { + return false; + } + + if (defined(this._decodePromise)) { + return false; + } + + const decodePromise = loadSpz(this._bufferViewTypedArray, { + unpackOptions: { coordinateSystem: "UNSPECIFIED" }, + }); + + if (!defined(decodePromise)) { + return false; + } + + this._decodePromise = processDecode(this, decodePromise); + } + + /** + * Unloads the SPZ resource and frees associated resources. + * @private + */ + unload() { + if (defined(this._bufferViewLoader)) { + this._resourceCache.unload(this._bufferViewLoader); + } + + this._bufferViewLoader = undefined; + this._bufferViewTypedArray = undefined; + this._decodedData = undefined; + this._gltf = undefined; + this._primitive = undefined; + } +} async function loadResources(loader) { const resourceCache = loader._resourceCache; @@ -119,21 +185,6 @@ async function loadResources(loader) { } } -/** - * Loads the SPZ resource. - * @returns {Promise} A promise that resolves to the resource when the SPZ is loaded. - * @private - */ -GltfSpzLoader.prototype.load = async function () { - if (defined(this._promise)) { - return this._promise; - } - - this._state = ResourceLoaderState.LOADING; - this._promise = loadResources(this); - return this._promise; -}; - function handleError(spzLoader, error) { spzLoader.unload(); spzLoader._state = ResourceLoaderState.FAILED; @@ -164,61 +215,4 @@ async function processDecode(loader, decodePromise) { } } -/** - * Processes the SPZ resource. - * @param {FrameState} frameState The frame state. - * @private - */ -GltfSpzLoader.prototype.process = function (frameState) { - //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("frameState", frameState); - //>>includeEnd('debug'); - - if (this._state === ResourceLoaderState.READY) { - return true; - } - - if (this._state !== ResourceLoaderState.PROCESSING) { - return false; - } - - if (defined(this._spzError)) { - handleError(this, this._spzError); - } - - if (!defined(this._bufferViewTypedArray)) { - return false; - } - - if (defined(this._decodePromise)) { - return false; - } - - const decodePromise = loadSpz(this._bufferViewTypedArray, { - unpackOptions: { coordinateSystem: "UNSPECIFIED" }, - }); - - if (!defined(decodePromise)) { - return false; - } - - this._decodePromise = processDecode(this, decodePromise); -}; - -/** - * Unloads the SPZ resource and frees associated resources. - * @private - */ -GltfSpzLoader.prototype.unload = function () { - if (defined(this._bufferViewLoader)) { - this._resourceCache.unload(this._bufferViewLoader); - } - - this._bufferViewLoader = undefined; - this._bufferViewTypedArray = undefined; - this._decodedData = undefined; - this._gltf = undefined; - this._primitive = undefined; -}; - export default GltfSpzLoader; diff --git a/packages/engine/Source/Scene/GltfStructuralMetadataLoader.js b/packages/engine/Source/Scene/GltfStructuralMetadataLoader.js index 01b90cf1d61..4cebd697bfe 100644 --- a/packages/engine/Source/Scene/GltfStructuralMetadataLoader.js +++ b/packages/engine/Source/Scene/GltfStructuralMetadataLoader.js @@ -32,62 +32,56 @@ import ResourceLoaderState from "./ResourceLoaderState.js"; * @private * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy. */ -function GltfStructuralMetadataLoader(options) { - options = options ?? Frozen.EMPTY_OBJECT; - const { - gltf, - extension, - extensionLegacy, - gltfResource, - baseResource, - supportedImageFormats, - frameState, - cacheKey, - asynchronous = true, - } = options; - - //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("options.gltf", gltf); - Check.typeOf.object("options.gltfResource", gltfResource); - Check.typeOf.object("options.baseResource", baseResource); - Check.typeOf.object("options.supportedImageFormats", supportedImageFormats); - Check.typeOf.object("options.frameState", frameState); - - if (!defined(options.extension) && !defined(options.extensionLegacy)) { - throw new DeveloperError( - "One of options.extension or options.extensionLegacy must be specified", - ); +class GltfStructuralMetadataLoader extends ResourceLoader { + constructor(options) { + super(); + + options = options ?? Frozen.EMPTY_OBJECT; + const { + gltf, + extension, + extensionLegacy, + gltfResource, + baseResource, + supportedImageFormats, + frameState, + cacheKey, + asynchronous = true, + } = options; + + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("options.gltf", gltf); + Check.typeOf.object("options.gltfResource", gltfResource); + Check.typeOf.object("options.baseResource", baseResource); + Check.typeOf.object("options.supportedImageFormats", supportedImageFormats); + Check.typeOf.object("options.frameState", frameState); + + if (!defined(options.extension) && !defined(options.extensionLegacy)) { + throw new DeveloperError( + "One of options.extension or options.extensionLegacy must be specified", + ); + } + //>>includeEnd('debug'); + + this._gltfResource = gltfResource; + this._baseResource = baseResource; + this._gltf = gltf; + this._extension = extension; + this._extensionLegacy = extensionLegacy; + this._supportedImageFormats = supportedImageFormats; + this._frameState = frameState; + this._cacheKey = cacheKey; + this._asynchronous = asynchronous; + this._bufferViewLoaders = []; + this._bufferViewIds = []; + this._textureLoaders = []; + this._textureIds = []; + this._schemaLoader = undefined; + this._structuralMetadata = undefined; + this._state = ResourceLoaderState.UNLOADED; + this._promise = undefined; } - //>>includeEnd('debug'); - - this._gltfResource = gltfResource; - this._baseResource = baseResource; - this._gltf = gltf; - this._extension = extension; - this._extensionLegacy = extensionLegacy; - this._supportedImageFormats = supportedImageFormats; - this._frameState = frameState; - this._cacheKey = cacheKey; - this._asynchronous = asynchronous; - this._bufferViewLoaders = []; - this._bufferViewIds = []; - this._textureLoaders = []; - this._textureIds = []; - this._schemaLoader = undefined; - this._structuralMetadata = undefined; - this._state = ResourceLoaderState.UNLOADED; - this._promise = undefined; -} - -if (defined(Object.create)) { - GltfStructuralMetadataLoader.prototype = Object.create( - ResourceLoader.prototype, - ); - GltfStructuralMetadataLoader.prototype.constructor = - GltfStructuralMetadataLoader; -} -Object.defineProperties(GltfStructuralMetadataLoader.prototype, { /** * The cache key of the resource. * @@ -97,11 +91,10 @@ Object.defineProperties(GltfStructuralMetadataLoader.prototype, { * @readonly * @private */ - cacheKey: { - get: function () { - return this._cacheKey; - }, - }, + get cacheKey() { + return this._cacheKey; + } + /** * The parsed structural metadata * @@ -111,12 +104,118 @@ Object.defineProperties(GltfStructuralMetadataLoader.prototype, { * @readonly * @private */ - structuralMetadata: { - get: function () { - return this._structuralMetadata; - }, - }, -}); + get structuralMetadata() { + return this._structuralMetadata; + } + + /** + * Loads the resource. + * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. + * @private + */ + load() { + if (defined(this._promise)) { + return this._promise; + } + + this._state = ResourceLoaderState.LOADING; + this._promise = loadResources(this); + return this._promise; + } + + /** + * Processes the resource until it becomes ready. + * + * @param {FrameState} frameState The frame state. + * @private + */ + process(frameState) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("frameState", frameState); + //>>includeEnd('debug'); + + if (this._state === ResourceLoaderState.READY) { + return true; + } + + if (this._state !== ResourceLoaderState.LOADED) { + return false; + } + + const textureLoaders = this._textureLoaders; + const textureLoadersLength = textureLoaders.length; + let ready = true; + for (let i = 0; i < textureLoadersLength; ++i) { + const textureLoader = textureLoaders[i]; + const textureReady = textureLoader.process(frameState); + ready = ready && textureReady; + } + + if (!ready) { + return false; + } + + const schema = this._schemaLoader.schema; + const bufferViews = {}; + for (let i = 0; i < this._bufferViewIds.length; ++i) { + const bufferViewId = this._bufferViewIds[i]; + const bufferViewLoader = this._bufferViewLoaders[i]; + if (!bufferViewLoader.isDestroyed()) { + // Copy the typed array and let the underlying ArrayBuffer be freed + const bufferViewTypedArray = new Uint8Array( + bufferViewLoader.typedArray, + ); + bufferViews[bufferViewId] = bufferViewTypedArray; + } + } + + const textures = {}; + for (let i = 0; i < this._textureIds.length; ++i) { + const textureId = this._textureIds[i]; + const textureLoader = textureLoaders[i]; + if (!textureLoader.isDestroyed()) { + textures[textureId] = textureLoader.texture; + } + } + if (defined(this._extension)) { + this._structuralMetadata = parseStructuralMetadata({ + extension: this._extension, + schema: schema, + bufferViews: bufferViews, + textures: textures, + }); + } else { + this._structuralMetadata = parseFeatureMetadataLegacy({ + extension: this._extensionLegacy, + schema: schema, + bufferViews: bufferViews, + textures: textures, + }); + } + + // Buffer views can be unloaded after the data has been copied + unloadBufferViews(this); + + this._state = ResourceLoaderState.READY; + return true; + } + + /** + * Unloads the resource. + * @private + */ + unload() { + unloadBufferViews(this); + unloadTextures(this); + + if (defined(this._schemaLoader)) { + ResourceCache.unload(this._schemaLoader); + } + this._schemaLoader = undefined; + + this._structuralMetadata = undefined; + } +} async function loadResources(loader) { try { @@ -146,21 +245,6 @@ async function loadResources(loader) { } } -/** - * Loads the resource. - * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. - * @private - */ -GltfStructuralMetadataLoader.prototype.load = function () { - if (defined(this._promise)) { - return this._promise; - } - - this._state = ResourceLoaderState.LOADING; - this._promise = loadResources(this); - return this._promise; -}; - function gatherBufferViewIdsFromProperties(properties, bufferViewIdSet) { for (const propertyId in properties) { if (properties.hasOwnProperty(propertyId)) { @@ -391,81 +475,6 @@ async function loadSchema(structuralMetadataLoader) { } } -/** - * Processes the resource until it becomes ready. - * - * @param {FrameState} frameState The frame state. - * @private - */ -GltfStructuralMetadataLoader.prototype.process = function (frameState) { - //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("frameState", frameState); - //>>includeEnd('debug'); - - if (this._state === ResourceLoaderState.READY) { - return true; - } - - if (this._state !== ResourceLoaderState.LOADED) { - return false; - } - - const textureLoaders = this._textureLoaders; - const textureLoadersLength = textureLoaders.length; - let ready = true; - for (let i = 0; i < textureLoadersLength; ++i) { - const textureLoader = textureLoaders[i]; - const textureReady = textureLoader.process(frameState); - ready = ready && textureReady; - } - - if (!ready) { - return false; - } - - const schema = this._schemaLoader.schema; - const bufferViews = {}; - for (let i = 0; i < this._bufferViewIds.length; ++i) { - const bufferViewId = this._bufferViewIds[i]; - const bufferViewLoader = this._bufferViewLoaders[i]; - if (!bufferViewLoader.isDestroyed()) { - // Copy the typed array and let the underlying ArrayBuffer be freed - const bufferViewTypedArray = new Uint8Array(bufferViewLoader.typedArray); - bufferViews[bufferViewId] = bufferViewTypedArray; - } - } - - const textures = {}; - for (let i = 0; i < this._textureIds.length; ++i) { - const textureId = this._textureIds[i]; - const textureLoader = textureLoaders[i]; - if (!textureLoader.isDestroyed()) { - textures[textureId] = textureLoader.texture; - } - } - if (defined(this._extension)) { - this._structuralMetadata = parseStructuralMetadata({ - extension: this._extension, - schema: schema, - bufferViews: bufferViews, - textures: textures, - }); - } else { - this._structuralMetadata = parseFeatureMetadataLegacy({ - extension: this._extensionLegacy, - schema: schema, - bufferViews: bufferViews, - textures: textures, - }); - } - - // Buffer views can be unloaded after the data has been copied - unloadBufferViews(this); - - this._state = ResourceLoaderState.READY; - return true; -}; - function unloadBufferViews(structuralMetadataLoader) { const bufferViewLoaders = structuralMetadataLoader._bufferViewLoaders; const bufferViewLoadersLength = bufferViewLoaders.length; @@ -486,20 +495,4 @@ function unloadTextures(structuralMetadataLoader) { structuralMetadataLoader._textureIds.length = 0; } -/** - * Unloads the resource. - * @private - */ -GltfStructuralMetadataLoader.prototype.unload = function () { - unloadBufferViews(this); - unloadTextures(this); - - if (defined(this._schemaLoader)) { - ResourceCache.unload(this._schemaLoader); - } - this._schemaLoader = undefined; - - this._structuralMetadata = undefined; -}; - export default GltfStructuralMetadataLoader; diff --git a/packages/engine/Source/Scene/GltfTextureLoader.js b/packages/engine/Source/Scene/GltfTextureLoader.js index 36c3349ace6..2e05d029946 100644 --- a/packages/engine/Source/Scene/GltfTextureLoader.js +++ b/packages/engine/Source/Scene/GltfTextureLoader.js @@ -34,58 +34,55 @@ import resizeImageToNextPowerOfTwo from "../Core/resizeImageToNextPowerOfTwo.js" * * @private */ -function GltfTextureLoader(options) { - options = options ?? Frozen.EMPTY_OBJECT; - const resourceCache = options.resourceCache; - const gltf = options.gltf; - const textureInfo = options.textureInfo; - const gltfResource = options.gltfResource; - const baseResource = options.baseResource; - const supportedImageFormats = options.supportedImageFormats; - const cacheKey = options.cacheKey; - const asynchronous = options.asynchronous ?? true; - - //>>includeStart('debug', pragmas.debug); - Check.typeOf.func("options.resourceCache", resourceCache); - Check.typeOf.object("options.gltf", gltf); - Check.typeOf.object("options.textureInfo", textureInfo); - Check.typeOf.object("options.gltfResource", gltfResource); - Check.typeOf.object("options.baseResource", baseResource); - Check.typeOf.object("options.supportedImageFormats", supportedImageFormats); - //>>includeEnd('debug'); - - const textureId = textureInfo.index; - - // imageId is guaranteed to be defined otherwise the GltfTextureLoader - // wouldn't have been created - const imageId = GltfLoaderUtil.getImageIdFromTexture({ - gltf: gltf, - textureId: textureId, - supportedImageFormats: supportedImageFormats, - }); - - this._resourceCache = resourceCache; - this._gltf = gltf; - this._textureInfo = textureInfo; - this._imageId = imageId; - this._gltfResource = gltfResource; - this._baseResource = baseResource; - this._cacheKey = cacheKey; - this._asynchronous = asynchronous; - this._imageLoader = undefined; - this._image = undefined; - this._mipLevels = undefined; - this._texture = undefined; - this._state = ResourceLoaderState.UNLOADED; - this._promise = undefined; -} +class GltfTextureLoader extends ResourceLoader { + constructor(options) { + super(); + + options = options ?? Frozen.EMPTY_OBJECT; + const resourceCache = options.resourceCache; + const gltf = options.gltf; + const textureInfo = options.textureInfo; + const gltfResource = options.gltfResource; + const baseResource = options.baseResource; + const supportedImageFormats = options.supportedImageFormats; + const cacheKey = options.cacheKey; + const asynchronous = options.asynchronous ?? true; + + //>>includeStart('debug', pragmas.debug); + Check.typeOf.func("options.resourceCache", resourceCache); + Check.typeOf.object("options.gltf", gltf); + Check.typeOf.object("options.textureInfo", textureInfo); + Check.typeOf.object("options.gltfResource", gltfResource); + Check.typeOf.object("options.baseResource", baseResource); + Check.typeOf.object("options.supportedImageFormats", supportedImageFormats); + //>>includeEnd('debug'); + + const textureId = textureInfo.index; + + // imageId is guaranteed to be defined otherwise the GltfTextureLoader + // wouldn't have been created + const imageId = GltfLoaderUtil.getImageIdFromTexture({ + gltf: gltf, + textureId: textureId, + supportedImageFormats: supportedImageFormats, + }); -if (defined(Object.create)) { - GltfTextureLoader.prototype = Object.create(ResourceLoader.prototype); - GltfTextureLoader.prototype.constructor = GltfTextureLoader; -} + this._resourceCache = resourceCache; + this._gltf = gltf; + this._textureInfo = textureInfo; + this._imageId = imageId; + this._gltfResource = gltfResource; + this._baseResource = baseResource; + this._cacheKey = cacheKey; + this._asynchronous = asynchronous; + this._imageLoader = undefined; + this._image = undefined; + this._mipLevels = undefined; + this._texture = undefined; + this._state = ResourceLoaderState.UNLOADED; + this._promise = undefined; + } -Object.defineProperties(GltfTextureLoader.prototype, { /** * The cache key of the resource. * @@ -95,11 +92,10 @@ Object.defineProperties(GltfTextureLoader.prototype, { * @readonly * @private */ - cacheKey: { - get: function () { - return this._cacheKey; - }, - }, + get cacheKey() { + return this._cacheKey; + } + /** * The texture. * @@ -109,99 +105,148 @@ Object.defineProperties(GltfTextureLoader.prototype, { * @readonly * @private */ - texture: { - get: function () { - return this._texture; - }, - }, -}); + get texture() { + return this._texture; + } -const scratchTextureJob = new CreateTextureJob(); + /** + * Loads the resource. + * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. + * @private + */ + async load() { + if (defined(this._promise)) { + return this._promise; + } -async function loadResources(loader) { - const resourceCache = loader._resourceCache; - try { - const imageLoader = resourceCache.getImageLoader({ - gltf: loader._gltf, - imageId: loader._imageId, - gltfResource: loader._gltfResource, - baseResource: loader._baseResource, - }); - loader._imageLoader = imageLoader; - await imageLoader.load(); + this._state = ResourceLoaderState.LOADING; + this._promise = loadResources(this); + return this._promise; + } - if (loader.isDestroyed()) { - return; + /** + * Processes the resource until it becomes ready. + * + * @param {FrameState} frameState The frame state. + * @returns {boolean} true once all resourced are ready. + * @private + */ + process(frameState) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("frameState", frameState); + //>>includeEnd('debug'); + + if (this._state === ResourceLoaderState.READY) { + return true; } - // Now wait for process() to run to finish loading - loader._image = imageLoader.image; - loader._mipLevels = imageLoader.mipLevels; - loader._state = ResourceLoaderState.LOADED; + if ( + this._state !== ResourceLoaderState.LOADED && + this._state !== ResourceLoaderState.PROCESSING + ) { + return false; + } - return loader; - } catch (error) { - if (loader.isDestroyed()) { - return; + if (defined(this._texture)) { + // Already created texture + return false; } - loader.unload(); - loader._state = ResourceLoaderState.FAILED; - const errorMessage = "Failed to load texture"; - throw loader.getError(errorMessage, error); + if (!defined(this._image)) { + // Not ready to create texture + return false; + } + + this._state = ResourceLoaderState.PROCESSING; + + let texture; + if (this._asynchronous) { + const textureJob = scratchTextureJob; + textureJob.set( + this._gltf, + this._textureInfo, + this._cacheKey, + this._image, + this._mipLevels, + frameState.context, + ); + const jobScheduler = frameState.jobScheduler; + if (!jobScheduler.execute(textureJob, JobType.TEXTURE)) { + // Job scheduler is full. Try again next frame. + return; + } + texture = textureJob.texture; + } else { + texture = createTexture( + this._gltf, + this._textureInfo, + this._cacheKey, + this._image, + this._mipLevels, + frameState.context, + ); + } + + // Unload everything except the texture + this.unload(); + + this._texture = texture; + this._state = ResourceLoaderState.READY; + this._resourceCache.statistics.addTextureLoader(this); + return true; + } + + /** + * Unloads the resource. + * @private + */ + unload() { + if (defined(this._texture)) { + this._texture.destroy(); + } + + if (defined(this._imageLoader) && !this._imageLoader.isDestroyed()) { + this._resourceCache.unload(this._imageLoader); + } + + this._imageLoader = undefined; + this._image = undefined; + this._mipLevels = undefined; + this._texture = undefined; + this._gltf = undefined; } } -/** - * Loads the resource. - * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. - * @private - */ -GltfTextureLoader.prototype.load = async function () { - if (defined(this._promise)) { - return this._promise; +class CreateTextureJob { + constructor() { + this.gltf = undefined; + this.textureInfo = undefined; + this.textureId = undefined; + this.image = undefined; + this.context = undefined; + this.texture = undefined; } - this._state = ResourceLoaderState.LOADING; - this._promise = loadResources(this); - return this._promise; -}; - -function CreateTextureJob() { - this.gltf = undefined; - this.textureInfo = undefined; - this.textureId = undefined; - this.image = undefined; - this.context = undefined; - this.texture = undefined; -} + set(gltf, textureInfo, textureId, image, mipLevels, context) { + this.gltf = gltf; + this.textureInfo = textureInfo; + this.textureId = textureId; + this.image = image; + this.mipLevels = mipLevels; + this.context = context; + } -CreateTextureJob.prototype.set = function ( - gltf, - textureInfo, - textureId, - image, - mipLevels, - context, -) { - this.gltf = gltf; - this.textureInfo = textureInfo; - this.textureId = textureId; - this.image = image; - this.mipLevels = mipLevels; - this.context = context; -}; - -CreateTextureJob.prototype.execute = function () { - this.texture = createTexture( - this.gltf, - this.textureInfo, - this.textureId, - this.image, - this.mipLevels, - this.context, - ); -}; + execute() { + this.texture = createTexture( + this.gltf, + this.textureInfo, + this.textureId, + this.image, + this.mipLevels, + this.context, + ); + } +} function createTexture( gltf, @@ -304,96 +349,40 @@ function createTexture( return texture; } -/** - * Processes the resource until it becomes ready. - * - * @param {FrameState} frameState The frame state. - * @returns {boolean} true once all resourced are ready. - * @private - */ -GltfTextureLoader.prototype.process = function (frameState) { - //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("frameState", frameState); - //>>includeEnd('debug'); - - if (this._state === ResourceLoaderState.READY) { - return true; - } - - if ( - this._state !== ResourceLoaderState.LOADED && - this._state !== ResourceLoaderState.PROCESSING - ) { - return false; - } - - if (defined(this._texture)) { - // Already created texture - return false; - } - - if (!defined(this._image)) { - // Not ready to create texture - return false; - } +const scratchTextureJob = new CreateTextureJob(); - this._state = ResourceLoaderState.PROCESSING; +async function loadResources(loader) { + const resourceCache = loader._resourceCache; + try { + const imageLoader = resourceCache.getImageLoader({ + gltf: loader._gltf, + imageId: loader._imageId, + gltfResource: loader._gltfResource, + baseResource: loader._baseResource, + }); + loader._imageLoader = imageLoader; + await imageLoader.load(); - let texture; - if (this._asynchronous) { - const textureJob = scratchTextureJob; - textureJob.set( - this._gltf, - this._textureInfo, - this._cacheKey, - this._image, - this._mipLevels, - frameState.context, - ); - const jobScheduler = frameState.jobScheduler; - if (!jobScheduler.execute(textureJob, JobType.TEXTURE)) { - // Job scheduler is full. Try again next frame. + if (loader.isDestroyed()) { return; } - texture = textureJob.texture; - } else { - texture = createTexture( - this._gltf, - this._textureInfo, - this._cacheKey, - this._image, - this._mipLevels, - frameState.context, - ); - } - // Unload everything except the texture - this.unload(); - - this._texture = texture; - this._state = ResourceLoaderState.READY; - this._resourceCache.statistics.addTextureLoader(this); - return true; -}; + // Now wait for process() to run to finish loading + loader._image = imageLoader.image; + loader._mipLevels = imageLoader.mipLevels; + loader._state = ResourceLoaderState.LOADED; -/** - * Unloads the resource. - * @private - */ -GltfTextureLoader.prototype.unload = function () { - if (defined(this._texture)) { - this._texture.destroy(); - } + return loader; + } catch (error) { + if (loader.isDestroyed()) { + return; + } - if (defined(this._imageLoader) && !this._imageLoader.isDestroyed()) { - this._resourceCache.unload(this._imageLoader); + loader.unload(); + loader._state = ResourceLoaderState.FAILED; + const errorMessage = "Failed to load texture"; + throw loader.getError(errorMessage, error); } - - this._imageLoader = undefined; - this._image = undefined; - this._mipLevels = undefined; - this._texture = undefined; - this._gltf = undefined; -}; +} export default GltfTextureLoader; diff --git a/packages/engine/Source/Scene/GltfVertexBufferLoader.js b/packages/engine/Source/Scene/GltfVertexBufferLoader.js index 9495538fad3..4210e47ab8f 100644 --- a/packages/engine/Source/Scene/GltfVertexBufferLoader.js +++ b/packages/engine/Source/Scene/GltfVertexBufferLoader.js @@ -42,102 +42,99 @@ import CesiumMath from "../Core/Math.js"; * * @private */ -function GltfVertexBufferLoader(options) { - options = options ?? Frozen.EMPTY_OBJECT; - const resourceCache = options.resourceCache; - const gltf = options.gltf; - const gltfResource = options.gltfResource; - const baseResource = options.baseResource; - const bufferViewId = options.bufferViewId; - const primitive = options.primitive; - const draco = options.draco; - const attributeSemantic = options.attributeSemantic; - const accessorId = options.accessorId; - const cacheKey = options.cacheKey; - const spz = options.spz; - const asynchronous = options.asynchronous ?? true; - const loadBuffer = options.loadBuffer ?? false; - const loadTypedArray = options.loadTypedArray ?? false; - - //>>includeStart('debug', pragmas.debug); - Check.typeOf.func("options.resourceCache", resourceCache); - Check.typeOf.object("options.gltf", gltf); - Check.typeOf.object("options.gltfResource", gltfResource); - Check.typeOf.object("options.baseResource", baseResource); - if (!loadBuffer && !loadTypedArray) { - throw new DeveloperError( - "At least one of loadBuffer and loadTypedArray must be true.", - ); - } - - const hasBufferViewId = defined(bufferViewId); - const hasPrimitive = defined(primitive); - const hasDraco = hasDracoCompression(draco, attributeSemantic); - const hasAttributeSemantic = defined(attributeSemantic); - const hasAccessorId = defined(accessorId); - const hasSpz = defined(spz); - if (hasBufferViewId === (hasDraco !== hasSpz)) { - throw new DeveloperError( - "One of options.bufferViewId, options.draco, or options.spz must be defined.", - ); - } +class GltfVertexBufferLoader extends ResourceLoader { + constructor(options) { + super(); + + options = options ?? Frozen.EMPTY_OBJECT; + const resourceCache = options.resourceCache; + const gltf = options.gltf; + const gltfResource = options.gltfResource; + const baseResource = options.baseResource; + const bufferViewId = options.bufferViewId; + const primitive = options.primitive; + const draco = options.draco; + const attributeSemantic = options.attributeSemantic; + const accessorId = options.accessorId; + const cacheKey = options.cacheKey; + const spz = options.spz; + const asynchronous = options.asynchronous ?? true; + const loadBuffer = options.loadBuffer ?? false; + const loadTypedArray = options.loadTypedArray ?? false; + + //>>includeStart('debug', pragmas.debug); + Check.typeOf.func("options.resourceCache", resourceCache); + Check.typeOf.object("options.gltf", gltf); + Check.typeOf.object("options.gltfResource", gltfResource); + Check.typeOf.object("options.baseResource", baseResource); + if (!loadBuffer && !loadTypedArray) { + throw new DeveloperError( + "At least one of loadBuffer and loadTypedArray must be true.", + ); + } - if (hasDraco && !hasAttributeSemantic) { - throw new DeveloperError( - "When options.draco is defined options.attributeSemantic must also be defined.", - ); - } + const hasBufferViewId = defined(bufferViewId); + const hasPrimitive = defined(primitive); + const hasDraco = hasDracoCompression(draco, attributeSemantic); + const hasAttributeSemantic = defined(attributeSemantic); + const hasAccessorId = defined(accessorId); + const hasSpz = defined(spz); + if (hasBufferViewId === (hasDraco !== hasSpz)) { + throw new DeveloperError( + "One of options.bufferViewId, options.draco, or options.spz must be defined.", + ); + } - if (hasDraco && !hasAccessorId) { - throw new DeveloperError( - "When options.draco is defined options.accessorId must also be defined.", - ); - } + if (hasDraco && !hasAttributeSemantic) { + throw new DeveloperError( + "When options.draco is defined options.attributeSemantic must also be defined.", + ); + } - if (hasDraco && !hasPrimitive) { - throw new DeveloperError( - "When options.draco is defined options.primitive must also be defined.", - ); - } + if (hasDraco && !hasAccessorId) { + throw new DeveloperError( + "When options.draco is defined options.accessorId must also be defined.", + ); + } - if (hasDraco) { - Check.typeOf.object("options.primitive", primitive); - Check.typeOf.object("options.draco", draco); - Check.typeOf.string("options.attributeSemantic", attributeSemantic); - Check.typeOf.number("options.accessorId", accessorId); - } + if (hasDraco && !hasPrimitive) { + throw new DeveloperError( + "When options.draco is defined options.primitive must also be defined.", + ); + } - //>>includeEnd('debug'); - - this._resourceCache = resourceCache; - this._gltfResource = gltfResource; - this._baseResource = baseResource; - this._gltf = gltf; - this._bufferViewId = bufferViewId; - this._primitive = primitive; - this._draco = draco; - this._spz = spz; - this._attributeSemantic = attributeSemantic; - this._accessorId = accessorId; - this._cacheKey = cacheKey; - this._asynchronous = asynchronous; - this._loadBuffer = loadBuffer; - this._loadTypedArray = loadTypedArray; - this._bufferViewLoader = undefined; - this._dracoLoader = undefined; - this._quantization = undefined; - this._typedArray = undefined; - this._buffer = undefined; - this._state = ResourceLoaderState.UNLOADED; - this._promise = undefined; -} + if (hasDraco) { + Check.typeOf.object("options.primitive", primitive); + Check.typeOf.object("options.draco", draco); + Check.typeOf.string("options.attributeSemantic", attributeSemantic); + Check.typeOf.number("options.accessorId", accessorId); + } -if (defined(Object.create)) { - GltfVertexBufferLoader.prototype = Object.create(ResourceLoader.prototype); - GltfVertexBufferLoader.prototype.constructor = GltfVertexBufferLoader; -} + //>>includeEnd('debug'); + + this._resourceCache = resourceCache; + this._gltfResource = gltfResource; + this._baseResource = baseResource; + this._gltf = gltf; + this._bufferViewId = bufferViewId; + this._primitive = primitive; + this._draco = draco; + this._spz = spz; + this._attributeSemantic = attributeSemantic; + this._accessorId = accessorId; + this._cacheKey = cacheKey; + this._asynchronous = asynchronous; + this._loadBuffer = loadBuffer; + this._loadTypedArray = loadTypedArray; + this._bufferViewLoader = undefined; + this._dracoLoader = undefined; + this._quantization = undefined; + this._typedArray = undefined; + this._buffer = undefined; + this._state = ResourceLoaderState.UNLOADED; + this._promise = undefined; + } -Object.defineProperties(GltfVertexBufferLoader.prototype, { /** * The cache key of the resource. * @@ -147,11 +144,10 @@ Object.defineProperties(GltfVertexBufferLoader.prototype, { * @readonly * @private */ - cacheKey: { - get: function () { - return this._cacheKey; - }, - }, + get cacheKey() { + return this._cacheKey; + } + /** * The vertex buffer. This is only defined when loadAsTypedArray is false. * @@ -161,11 +157,10 @@ Object.defineProperties(GltfVertexBufferLoader.prototype, { * @readonly * @private */ - buffer: { - get: function () { - return this._buffer; - }, - }, + get buffer() { + return this._buffer; + } + /** * The typed array containing vertex buffer data. This is only defined when loadAsTypedArray is true. * @@ -175,11 +170,10 @@ Object.defineProperties(GltfVertexBufferLoader.prototype, { * @readonly * @private */ - typedArray: { - get: function () { - return this._typedArray; - }, - }, + get typedArray() { + return this._typedArray; + } + /** * Information about the quantized vertex attribute after Draco decode. * @@ -189,44 +183,150 @@ Object.defineProperties(GltfVertexBufferLoader.prototype, { * @readonly * @private */ - quantization: { - get: function () { - return this._quantization; - }, - }, -}); + get quantization() { + return this._quantization; + } -function hasDracoCompression(draco, semantic) { - return ( - defined(draco) && - defined(draco.attributes) && - defined(draco.attributes[semantic]) - ); -} + /** + * Loads the resource. + * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. + * @private + */ + async load() { + if (defined(this._promise)) { + return this._promise; + } -/** - * Loads the resource. - * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. - * @private - */ -GltfVertexBufferLoader.prototype.load = async function () { - if (defined(this._promise)) { + if (defined(this._spz)) { + this._promise = loadFromSpz(this); + return this._promise; + } + + if (hasDracoCompression(this._draco, this._attributeSemantic)) { + this._promise = loadFromDraco(this); + return this._promise; + } + + this._promise = loadFromBufferView(this); return this._promise; } - if (defined(this._spz)) { - this._promise = loadFromSpz(this); - return this._promise; + /** + * Processes the resource until it becomes ready. + * + * @param {FrameState} frameState The frame state. + * @private + */ + process(frameState) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("frameState", frameState); + //>>includeEnd('debug'); + + if (this._state === ResourceLoaderState.READY) { + return true; + } + + if ( + this._state !== ResourceLoaderState.LOADED && + this._state !== ResourceLoaderState.PROCESSING + ) { + return false; + } + + if (defined(this._dracoLoader)) { + try { + const ready = this._dracoLoader.process(frameState); + if (!ready) { + return false; + } + } catch (error) { + handleError(this, error); + } + + processDraco(this); + } + + if (defined(this._spzLoader)) { + try { + const ready = this._spzLoader.process(frameState); + if (!ready) { + return false; + } + } catch (error) { + handleError(this, error); + } + + processSpz(this); + } + + let buffer; + const typedArray = this._typedArray; + if (this._loadBuffer && this._asynchronous) { + const vertexBufferJob = scratchVertexBufferJob; + vertexBufferJob.set(typedArray, frameState.context); + const jobScheduler = frameState.jobScheduler; + if (!jobScheduler.execute(vertexBufferJob, JobType.BUFFER)) { + // Job scheduler is full. Try again next frame. + return false; + } + buffer = vertexBufferJob.buffer; + } else if (this._loadBuffer) { + buffer = createVertexBuffer(typedArray, frameState.context); + } + + // Unload everything except the vertex buffer + this.unload(); + + this._buffer = buffer; + this._typedArray = this._loadTypedArray ? typedArray : undefined; + this._state = ResourceLoaderState.READY; + this._resourceCache.statistics.addGeometryLoader(this); + return true; } - if (hasDracoCompression(this._draco, this._attributeSemantic)) { - this._promise = loadFromDraco(this); - return this._promise; + /** + * Unloads the resource. + * @private + */ + unload() { + if (defined(this._buffer)) { + this._buffer.destroy(); + } + + const resourceCache = this._resourceCache; + + if ( + defined(this._bufferViewLoader) && + !this._bufferViewLoader.isDestroyed() + ) { + resourceCache.unload(this._bufferViewLoader); + } + + if (defined(this._dracoLoader)) { + resourceCache.unload(this._dracoLoader); + } + + if (defined(this._spzLoader)) { + resourceCache.unload(this._spzLoader); + } + + this._bufferViewLoader = undefined; + this._dracoLoader = undefined; + this._spzLoader = undefined; + this._typedArray = undefined; + this._buffer = undefined; + this._gltf = undefined; + this._primitive = undefined; } +} - this._promise = loadFromBufferView(this); - return this._promise; -}; +function hasDracoCompression(draco, semantic) { + return ( + defined(draco) && + defined(draco.attributes) && + defined(draco.attributes[semantic]) + ); +} function getQuantizationInformation( dracoQuantization, @@ -496,20 +596,22 @@ function handleError(vertexBufferLoader, error) { throw vertexBufferLoader.getError(errorMessage, error); } -function CreateVertexBufferJob() { - this.typedArray = undefined; - this.context = undefined; - this.buffer = undefined; -} +class CreateVertexBufferJob { + constructor() { + this.typedArray = undefined; + this.context = undefined; + this.buffer = undefined; + } -CreateVertexBufferJob.prototype.set = function (typedArray, context) { - this.typedArray = typedArray; - this.context = context; -}; + set(typedArray, context) { + this.typedArray = typedArray; + this.context = context; + } -CreateVertexBufferJob.prototype.execute = function () { - this.buffer = createVertexBuffer(this.typedArray, this.context); -}; + execute() { + this.buffer = createVertexBuffer(this.typedArray, this.context); + } +} function createVertexBuffer(typedArray, context) { const buffer = Buffer.createVertexBuffer({ @@ -523,112 +625,4 @@ function createVertexBuffer(typedArray, context) { const scratchVertexBufferJob = new CreateVertexBufferJob(); -/** - * Processes the resource until it becomes ready. - * - * @param {FrameState} frameState The frame state. - * @private - */ -GltfVertexBufferLoader.prototype.process = function (frameState) { - //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("frameState", frameState); - //>>includeEnd('debug'); - - if (this._state === ResourceLoaderState.READY) { - return true; - } - - if ( - this._state !== ResourceLoaderState.LOADED && - this._state !== ResourceLoaderState.PROCESSING - ) { - return false; - } - - if (defined(this._dracoLoader)) { - try { - const ready = this._dracoLoader.process(frameState); - if (!ready) { - return false; - } - } catch (error) { - handleError(this, error); - } - - processDraco(this); - } - - if (defined(this._spzLoader)) { - try { - const ready = this._spzLoader.process(frameState); - if (!ready) { - return false; - } - } catch (error) { - handleError(this, error); - } - - processSpz(this); - } - - let buffer; - const typedArray = this._typedArray; - if (this._loadBuffer && this._asynchronous) { - const vertexBufferJob = scratchVertexBufferJob; - vertexBufferJob.set(typedArray, frameState.context); - const jobScheduler = frameState.jobScheduler; - if (!jobScheduler.execute(vertexBufferJob, JobType.BUFFER)) { - // Job scheduler is full. Try again next frame. - return false; - } - buffer = vertexBufferJob.buffer; - } else if (this._loadBuffer) { - buffer = createVertexBuffer(typedArray, frameState.context); - } - - // Unload everything except the vertex buffer - this.unload(); - - this._buffer = buffer; - this._typedArray = this._loadTypedArray ? typedArray : undefined; - this._state = ResourceLoaderState.READY; - this._resourceCache.statistics.addGeometryLoader(this); - return true; -}; - -/** - * Unloads the resource. - * @private - */ -GltfVertexBufferLoader.prototype.unload = function () { - if (defined(this._buffer)) { - this._buffer.destroy(); - } - - const resourceCache = this._resourceCache; - - if ( - defined(this._bufferViewLoader) && - !this._bufferViewLoader.isDestroyed() - ) { - resourceCache.unload(this._bufferViewLoader); - } - - if (defined(this._dracoLoader)) { - resourceCache.unload(this._dracoLoader); - } - - if (defined(this._spzLoader)) { - resourceCache.unload(this._spzLoader); - } - - this._bufferViewLoader = undefined; - this._dracoLoader = undefined; - this._spzLoader = undefined; - this._typedArray = undefined; - this._buffer = undefined; - this._gltf = undefined; - this._primitive = undefined; -}; - export default GltfVertexBufferLoader; diff --git a/packages/engine/Source/Scene/MetadataSchemaLoader.js b/packages/engine/Source/Scene/MetadataSchemaLoader.js index 565a42ab48d..33ac644dc48 100644 --- a/packages/engine/Source/Scene/MetadataSchemaLoader.js +++ b/packages/engine/Source/Scene/MetadataSchemaLoader.js @@ -25,33 +25,32 @@ import ResourceLoaderState from "./ResourceLoaderState.js"; * @private * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy. */ -function MetadataSchemaLoader(options) { - options = options ?? Frozen.EMPTY_OBJECT; - const schema = options.schema; - const resource = options.resource; - const cacheKey = options.cacheKey; +class MetadataSchemaLoader extends ResourceLoader { + constructor(options) { + super(); - //>>includeStart('debug', pragmas.debug); - if (defined(schema) === defined(resource)) { - throw new DeveloperError( - "One of options.schema and options.resource must be defined.", - ); - } - //>>includeEnd('debug'); + options = options ?? Frozen.EMPTY_OBJECT; + const schema = options.schema; + const resource = options.resource; + const cacheKey = options.cacheKey; - this._schema = defined(schema) ? MetadataSchema.fromJson(schema) : undefined; - this._resource = resource; - this._cacheKey = cacheKey; - this._state = ResourceLoaderState.UNLOADED; - this._promise = undefined; -} + //>>includeStart('debug', pragmas.debug); + if (defined(schema) === defined(resource)) { + throw new DeveloperError( + "One of options.schema and options.resource must be defined.", + ); + } + //>>includeEnd('debug'); -if (defined(Object.create)) { - MetadataSchemaLoader.prototype = Object.create(ResourceLoader.prototype); - MetadataSchemaLoader.prototype.constructor = MetadataSchemaLoader; -} + this._schema = defined(schema) + ? MetadataSchema.fromJson(schema) + : undefined; + this._resource = resource; + this._cacheKey = cacheKey; + this._state = ResourceLoaderState.UNLOADED; + this._promise = undefined; + } -Object.defineProperties(MetadataSchemaLoader.prototype, { /** * The cache key of the resource. * @@ -61,11 +60,10 @@ Object.defineProperties(MetadataSchemaLoader.prototype, { * @readonly * @private */ - cacheKey: { - get: function () { - return this._cacheKey; - }, - }, + get cacheKey() { + return this._cacheKey; + } + /** * The metadata schema object. * @@ -75,31 +73,37 @@ Object.defineProperties(MetadataSchemaLoader.prototype, { * @readonly * @private */ - schema: { - get: function () { - return this._schema; - }, - }, -}); - -/** - * Loads the resource. - * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. - * @private - */ -MetadataSchemaLoader.prototype.load = async function () { - if (defined(this._promise)) { - return this._promise; + get schema() { + return this._schema; } - if (defined(this._schema)) { - this._promise = Promise.resolve(this); + /** + * Loads the resource. + * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. + * @private + */ + async load() { + if (defined(this._promise)) { + return this._promise; + } + + if (defined(this._schema)) { + this._promise = Promise.resolve(this); + return this._promise; + } + + this._promise = loadExternalSchema(this); return this._promise; } - this._promise = loadExternalSchema(this); - return this._promise; -}; + /** + * Unloads the resource. + * @private + */ + unload() { + this._schema = undefined; + } +} async function loadExternalSchema(schemaLoader) { const resource = schemaLoader._resource; @@ -124,12 +128,4 @@ async function loadExternalSchema(schemaLoader) { } } -/** - * Unloads the resource. - * @private - */ -MetadataSchemaLoader.prototype.unload = function () { - this._schema = undefined; -}; - export default MetadataSchemaLoader; diff --git a/packages/engine/Source/Scene/Model/B3dmLoader.js b/packages/engine/Source/Scene/Model/B3dmLoader.js index 8687f4cb901..accd1f2775d 100644 --- a/packages/engine/Source/Scene/Model/B3dmLoader.js +++ b/packages/engine/Source/Scene/Model/B3dmLoader.js @@ -55,71 +55,68 @@ const FeatureIdAttribute = ModelComponents.FeatureIdAttribute; * @param {boolean} [options.loadPrimitiveOutline=true] If true, load outlines from the {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/CESIUM_primitive_outline|CESIUM_primitive_outline} extension. This can be set false to avoid post-processing geometry at load time. * @param {boolean} [options.loadForClassification=false] If true and if the model has feature IDs, load the feature IDs and indices as typed arrays. This is useful for batching features for classification. * */ -function B3dmLoader(options) { - options = options ?? Frozen.EMPTY_OBJECT; - - const b3dmResource = options.b3dmResource; - let baseResource = options.baseResource; - const arrayBuffer = options.arrayBuffer; - const byteOffset = options.byteOffset ?? 0; - const releaseGltfJson = options.releaseGltfJson ?? false; - const asynchronous = options.asynchronous ?? true; - const incrementallyLoadTextures = options.incrementallyLoadTextures ?? true; - const upAxis = options.upAxis ?? Axis.Y; - const forwardAxis = options.forwardAxis ?? Axis.X; - const loadAttributesAsTypedArray = - options.loadAttributesAsTypedArray ?? false; - const loadAttributesFor2D = options.loadAttributesFor2D ?? false; - const enablePick = options.enablePick ?? false; - const loadIndicesForWireframe = options.loadIndicesForWireframe ?? false; - const loadPrimitiveOutline = options.loadPrimitiveOutline ?? true; - const loadForClassification = options.loadForClassification ?? false; - - //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("options.b3dmResource", b3dmResource); - Check.typeOf.object("options.arrayBuffer", arrayBuffer); - //>>includeEnd('debug'); - - baseResource = defined(baseResource) ? baseResource : b3dmResource.clone(); - - this._b3dmResource = b3dmResource; - this._baseResource = baseResource; - this._arrayBuffer = arrayBuffer; - this._byteOffset = byteOffset; - this._releaseGltfJson = releaseGltfJson; - this._asynchronous = asynchronous; - this._incrementallyLoadTextures = incrementallyLoadTextures; - this._upAxis = upAxis; - this._forwardAxis = forwardAxis; - this._loadAttributesAsTypedArray = loadAttributesAsTypedArray; - this._loadAttributesFor2D = loadAttributesFor2D; - this._enablePick = enablePick; - this._loadIndicesForWireframe = loadIndicesForWireframe; - this._loadPrimitiveOutline = loadPrimitiveOutline; - this._loadForClassification = loadForClassification; - - this._state = B3dmLoaderState.UNLOADED; - - this._promise = undefined; - - this._gltfLoader = undefined; - - // Loaded results. - this._batchLength = 0; - this._propertyTable = undefined; - - // The batch table object contains a json and a binary component access using keys of the same name. - this._batchTable = undefined; - this._components = undefined; - this._transform = Matrix4.IDENTITY; -} - -if (defined(Object.create)) { - B3dmLoader.prototype = Object.create(ResourceLoader.prototype); - B3dmLoader.prototype.constructor = B3dmLoader; -} +class B3dmLoader extends ResourceLoader { + constructor(options) { + super(); + + options = options ?? Frozen.EMPTY_OBJECT; + + const b3dmResource = options.b3dmResource; + let baseResource = options.baseResource; + const arrayBuffer = options.arrayBuffer; + const byteOffset = options.byteOffset ?? 0; + const releaseGltfJson = options.releaseGltfJson ?? false; + const asynchronous = options.asynchronous ?? true; + const incrementallyLoadTextures = options.incrementallyLoadTextures ?? true; + const upAxis = options.upAxis ?? Axis.Y; + const forwardAxis = options.forwardAxis ?? Axis.X; + const loadAttributesAsTypedArray = + options.loadAttributesAsTypedArray ?? false; + const loadAttributesFor2D = options.loadAttributesFor2D ?? false; + const enablePick = options.enablePick ?? false; + const loadIndicesForWireframe = options.loadIndicesForWireframe ?? false; + const loadPrimitiveOutline = options.loadPrimitiveOutline ?? true; + const loadForClassification = options.loadForClassification ?? false; + + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("options.b3dmResource", b3dmResource); + Check.typeOf.object("options.arrayBuffer", arrayBuffer); + //>>includeEnd('debug'); + + baseResource = defined(baseResource) ? baseResource : b3dmResource.clone(); + + this._b3dmResource = b3dmResource; + this._baseResource = baseResource; + this._arrayBuffer = arrayBuffer; + this._byteOffset = byteOffset; + this._releaseGltfJson = releaseGltfJson; + this._asynchronous = asynchronous; + this._incrementallyLoadTextures = incrementallyLoadTextures; + this._upAxis = upAxis; + this._forwardAxis = forwardAxis; + this._loadAttributesAsTypedArray = loadAttributesAsTypedArray; + this._loadAttributesFor2D = loadAttributesFor2D; + this._enablePick = enablePick; + this._loadIndicesForWireframe = loadIndicesForWireframe; + this._loadPrimitiveOutline = loadPrimitiveOutline; + this._loadForClassification = loadForClassification; + + this._state = B3dmLoaderState.UNLOADED; + + this._promise = undefined; + + this._gltfLoader = undefined; + + // Loaded results. + this._batchLength = 0; + this._propertyTable = undefined; + + // The batch table object contains a json and a binary component access using keys of the same name. + this._batchTable = undefined; + this._components = undefined; + this._transform = Matrix4.IDENTITY; + } -Object.defineProperties(B3dmLoader.prototype, { /** * true if textures are loaded, useful when incrementallyLoadTextures is true * @@ -129,11 +126,10 @@ Object.defineProperties(B3dmLoader.prototype, { * @readonly * @private */ - texturesLoaded: { - get: function () { - return this._gltfLoader?.texturesLoaded; - }, - }, + get texturesLoaded() { + return this._gltfLoader?.texturesLoaded; + } + /** * The cache key of the resource * @@ -143,11 +139,9 @@ Object.defineProperties(B3dmLoader.prototype, { * @readonly * @private */ - cacheKey: { - get: function () { - return undefined; - }, - }, + get cacheKey() { + return undefined; + } /** * The loaded components. @@ -158,141 +152,150 @@ Object.defineProperties(B3dmLoader.prototype, { * @readonly * @private */ - components: { - get: function () { - return this._components; - }, - }, -}); + get components() { + return this._components; + } + + /** + * Loads the resource. + * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. + * @private + */ + load() { + if (defined(this._promise)) { + return this._promise; + } + + const b3dm = B3dmParser.parse(this._arrayBuffer, this._byteOffset); + + let batchLength = b3dm.batchLength; + const featureTableJson = b3dm.featureTableJson; + const featureTableBinary = b3dm.featureTableBinary; + const batchTableJson = b3dm.batchTableJson; + const batchTableBinary = b3dm.batchTableBinary; + + const featureTable = new Cesium3DTileFeatureTable( + featureTableJson, + featureTableBinary, + ); + batchLength = featureTable.getGlobalProperty("BATCH_LENGTH"); + // Set batch length. + this._batchLength = batchLength; + // Set the RTC Center transform, if present. + const rtcCenter = featureTable.getGlobalProperty( + "RTC_CENTER", + ComponentDatatype.FLOAT, + 3, + ); + if (defined(rtcCenter)) { + this._transform = Matrix4.fromTranslation( + Cartesian3.fromArray(rtcCenter), + ); + } + + this._batchTable = { + json: batchTableJson, + binary: batchTableBinary, + }; + + const gltfLoader = new GltfLoader({ + typedArray: b3dm.gltf, + upAxis: this._upAxis, + forwardAxis: this._forwardAxis, + gltfResource: this._b3dmResource, + baseResource: this._baseResource, + releaseGltfJson: this._releaseGltfJson, + incrementallyLoadTextures: this._incrementallyLoadTextures, + loadAttributesAsTypedArray: this._loadAttributesAsTypedArray, + loadAttributesFor2D: this._loadAttributesFor2D, + enablePick: this._enablePick, + loadIndicesForWireframe: this._loadIndicesForWireframe, + loadPrimitiveOutline: this._loadPrimitiveOutline, + loadForClassification: this._loadForClassification, + renameBatchIdSemantic: true, + }); + + this._gltfLoader = gltfLoader; + this._state = B3dmLoaderState.LOADING; + + const that = this; + this._promise = gltfLoader + .load() + .then(function () { + if (that.isDestroyed()) { + return; + } + + that._state = B3dmLoaderState.PROCESSING; + return that; + }) + .catch(function (error) { + if (that.isDestroyed()) { + return; + } + + return handleError(that, error); + }); -/** - * Loads the resource. - * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. - * @private - */ -B3dmLoader.prototype.load = function () { - if (defined(this._promise)) { return this._promise; } - const b3dm = B3dmParser.parse(this._arrayBuffer, this._byteOffset); - - let batchLength = b3dm.batchLength; - const featureTableJson = b3dm.featureTableJson; - const featureTableBinary = b3dm.featureTableBinary; - const batchTableJson = b3dm.batchTableJson; - const batchTableBinary = b3dm.batchTableBinary; - - const featureTable = new Cesium3DTileFeatureTable( - featureTableJson, - featureTableBinary, - ); - batchLength = featureTable.getGlobalProperty("BATCH_LENGTH"); - // Set batch length. - this._batchLength = batchLength; - // Set the RTC Center transform, if present. - const rtcCenter = featureTable.getGlobalProperty( - "RTC_CENTER", - ComponentDatatype.FLOAT, - 3, - ); - if (defined(rtcCenter)) { - this._transform = Matrix4.fromTranslation(Cartesian3.fromArray(rtcCenter)); - } + process(frameState) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("frameState", frameState); + //>>includeEnd('debug'); - this._batchTable = { - json: batchTableJson, - binary: batchTableBinary, - }; - - const gltfLoader = new GltfLoader({ - typedArray: b3dm.gltf, - upAxis: this._upAxis, - forwardAxis: this._forwardAxis, - gltfResource: this._b3dmResource, - baseResource: this._baseResource, - releaseGltfJson: this._releaseGltfJson, - incrementallyLoadTextures: this._incrementallyLoadTextures, - loadAttributesAsTypedArray: this._loadAttributesAsTypedArray, - loadAttributesFor2D: this._loadAttributesFor2D, - enablePick: this._enablePick, - loadIndicesForWireframe: this._loadIndicesForWireframe, - loadPrimitiveOutline: this._loadPrimitiveOutline, - loadForClassification: this._loadForClassification, - renameBatchIdSemantic: true, - }); - - this._gltfLoader = gltfLoader; - this._state = B3dmLoaderState.LOADING; - - const that = this; - this._promise = gltfLoader - .load() - .then(function () { - if (that.isDestroyed()) { - return; - } - - that._state = B3dmLoaderState.PROCESSING; - return that; - }) - .catch(function (error) { - if (that.isDestroyed()) { - return; - } - - return handleError(that, error); - }); + if (this._state === B3dmLoaderState.READY) { + return true; + } - return this._promise; -}; + if (this._state !== B3dmLoaderState.PROCESSING) { + return false; + } -function handleError(b3dmLoader, error) { - b3dmLoader.unload(); - b3dmLoader._state = B3dmLoaderState.FAILED; - const errorMessage = "Failed to load b3dm"; - error = b3dmLoader.getError(errorMessage, error); - return Promise.reject(error); -} + const ready = this._gltfLoader.process(frameState); + if (!ready) { + return false; + } -B3dmLoader.prototype.process = function (frameState) { - //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("frameState", frameState); - //>>includeEnd('debug'); + const components = this._gltfLoader.components; - if (this._state === B3dmLoaderState.READY) { + // Combine the RTC_CENTER transform from the b3dm and the CESIUM_RTC + // transform from the glTF. In practice usually only one or the + // other is supplied. If they don't exist the transforms will + // be identity matrices. + components.transform = Matrix4.multiplyTransformation( + this._transform, + components.transform, + components.transform, + ); + createStructuralMetadata(this, components); + this._components = components; + + // Now that we have the parsed components, we can release the array buffer + this._arrayBuffer = undefined; + + this._state = B3dmLoaderState.READY; return true; } - if (this._state !== B3dmLoaderState.PROCESSING) { - return false; - } + unload() { + if (defined(this._gltfLoader) && !this._gltfLoader.isDestroyed()) { + this._gltfLoader.unload(); + } - const ready = this._gltfLoader.process(frameState); - if (!ready) { - return false; + this._components = undefined; + this._arrayBuffer = undefined; } +} - const components = this._gltfLoader.components; - - // Combine the RTC_CENTER transform from the b3dm and the CESIUM_RTC - // transform from the glTF. In practice usually only one or the - // other is supplied. If they don't exist the transforms will - // be identity matrices. - components.transform = Matrix4.multiplyTransformation( - this._transform, - components.transform, - components.transform, - ); - createStructuralMetadata(this, components); - this._components = components; - - // Now that we have the parsed components, we can release the array buffer - this._arrayBuffer = undefined; - - this._state = B3dmLoaderState.READY; - return true; -}; +function handleError(b3dmLoader, error) { + b3dmLoader.unload(); + b3dmLoader._state = B3dmLoaderState.FAILED; + const errorMessage = "Failed to load b3dm"; + error = b3dmLoader.getError(errorMessage, error); + return Promise.reject(error); +} function createStructuralMetadata(loader, components) { const batchTable = loader._batchTable; @@ -356,13 +359,4 @@ function processNode(node) { } } -B3dmLoader.prototype.unload = function () { - if (defined(this._gltfLoader) && !this._gltfLoader.isDestroyed()) { - this._gltfLoader.unload(); - } - - this._components = undefined; - this._arrayBuffer = undefined; -}; - export default B3dmLoader; diff --git a/packages/engine/Source/Scene/Model/GeoJsonLoader.js b/packages/engine/Source/Scene/Model/GeoJsonLoader.js index 28bc942b372..385a85760ef 100644 --- a/packages/engine/Source/Scene/Model/GeoJsonLoader.js +++ b/packages/engine/Source/Scene/Model/GeoJsonLoader.js @@ -41,23 +41,20 @@ import addAllToArray from "../../Core/addAllToArray.js"; * @param {object} options Object with the following properties: * @param {object} options.geoJson The GeoJson object. */ -function GeoJsonLoader(options) { - options = options ?? Frozen.EMPTY_OBJECT; +class GeoJsonLoader extends ResourceLoader { + constructor(options) { + super(); - //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("options.geoJson", options.geoJson); - //>>includeEnd('debug'); + options = options ?? Frozen.EMPTY_OBJECT; - this._geoJson = options.geoJson; - this._components = undefined; -} + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("options.geoJson", options.geoJson); + //>>includeEnd('debug'); -if (defined(Object.create)) { - GeoJsonLoader.prototype = Object.create(ResourceLoader.prototype); - GeoJsonLoader.prototype.constructor = GeoJsonLoader; -} + this._geoJson = options.geoJson; + this._components = undefined; + } -Object.defineProperties(GeoJsonLoader.prototype, { /** * The cache key of the resource. * @@ -67,11 +64,10 @@ Object.defineProperties(GeoJsonLoader.prototype, { * @readonly * @private */ - cacheKey: { - get: function () { - return undefined; - }, - }, + get cacheKey() { + return undefined; + } + /** * The loaded components. * @@ -81,41 +77,47 @@ Object.defineProperties(GeoJsonLoader.prototype, { * @readonly * @private */ - components: { - get: function () { - return this._components; - }, - }, -}); + get components() { + return this._components; + } -/** - * Loads the resource. - * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. - * @private - */ -GeoJsonLoader.prototype.load = function () { - return Promise.resolve(this); -}; + /** + * Loads the resource. + * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. + * @private + */ + load() { + return Promise.resolve(this); + } -/** - * Processes the resource until it becomes ready. - * - * @param {FrameState} frameState The frame state. - * @private - */ -GeoJsonLoader.prototype.process = function (frameState) { - //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("frameState", frameState); - //>>includeEnd('debug'); + /** + * Processes the resource until it becomes ready. + * + * @param {FrameState} frameState The frame state. + * @private + */ + process(frameState) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("frameState", frameState); + //>>includeEnd('debug'); - if (defined(this._components)) { + if (defined(this._components)) { + return true; + } + + this._components = parse(this._geoJson, frameState); + this._geoJson = undefined; return true; } - this._components = parse(this._geoJson, frameState); - this._geoJson = undefined; - return true; -}; + /** + * Unloads the resource. + * @private + */ + unload() { + this._components = undefined; + } +} function ParsedFeature() { this.lines = undefined; @@ -686,12 +688,4 @@ function parse(geoJson, frameState) { return components; } -/** - * Unloads the resource. - * @private - */ -GeoJsonLoader.prototype.unload = function () { - this._components = undefined; -}; - export default GeoJsonLoader; diff --git a/packages/engine/Source/Scene/Model/I3dmLoader.js b/packages/engine/Source/Scene/Model/I3dmLoader.js index 6817383b7d8..251ce1d6e83 100644 --- a/packages/engine/Source/Scene/Model/I3dmLoader.js +++ b/packages/engine/Source/Scene/Model/I3dmLoader.js @@ -68,69 +68,66 @@ const Instances = ModelComponents.Instances; * @param {boolean} [options.loadIndicesForWireframe=false] Load the index buffer as a typed array so wireframe indices can be created for WebGL1. * @param {boolean} [options.loadPrimitiveOutline=true] If true, load outlines from the {@link https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/CESIUM_primitive_outline|CESIUM_primitive_outline} extension. This can be set false to avoid post-processing geometry at load time. */ -function I3dmLoader(options) { - options = options ?? Frozen.EMPTY_OBJECT; - - const i3dmResource = options.i3dmResource; - const arrayBuffer = options.arrayBuffer; - let baseResource = options.baseResource; - const byteOffset = options.byteOffset ?? 0; - const releaseGltfJson = options.releaseGltfJson ?? false; - const asynchronous = options.asynchronous ?? true; - const incrementallyLoadTextures = options.incrementallyLoadTextures ?? true; - const upAxis = options.upAxis ?? Axis.Y; - const forwardAxis = options.forwardAxis ?? Axis.X; - const loadAttributesAsTypedArray = - options.loadAttributesAsTypedArray ?? false; - const loadIndicesForWireframe = options.loadIndicesForWireframe ?? false; - const loadPrimitiveOutline = options.loadPrimitiveOutline ?? true; - const enablePick = options.enablePick ?? false; - - //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("options.i3dmResource", i3dmResource); - Check.typeOf.object("options.arrayBuffer", arrayBuffer); - //>>includeEnd('debug'); - - baseResource = defined(baseResource) ? baseResource : i3dmResource.clone(); - - this._i3dmResource = i3dmResource; - this._baseResource = baseResource; - this._arrayBuffer = arrayBuffer; - this._byteOffset = byteOffset; - this._releaseGltfJson = releaseGltfJson; - this._asynchronous = asynchronous; - this._incrementallyLoadTextures = incrementallyLoadTextures; - this._upAxis = upAxis; - this._forwardAxis = forwardAxis; - this._loadAttributesAsTypedArray = loadAttributesAsTypedArray; - this._loadIndicesForWireframe = loadIndicesForWireframe; - this._loadPrimitiveOutline = loadPrimitiveOutline; - this._enablePick = enablePick; - - this._state = I3dmLoaderState.NOT_LOADED; - this._promise = undefined; - - this._gltfLoader = undefined; - - // Instanced attributes are initially parsed as typed arrays, but if they - // do not need to be further processed (e.g. turned into transform matrices), - // it is more efficient to turn them into buffers. The I3dmLoader will own the - // resources and store them here. - this._buffers = []; - this._components = undefined; - - this._transform = Matrix4.IDENTITY; - this._batchTable = undefined; - this._featureTable = undefined; - this._instancesLength = 0; -} - -if (defined(Object.create)) { - I3dmLoader.prototype = Object.create(ResourceLoader.prototype); - I3dmLoader.prototype.constructor = I3dmLoader; -} +class I3dmLoader extends ResourceLoader { + constructor(options) { + super(); + + options = options ?? Frozen.EMPTY_OBJECT; + + const i3dmResource = options.i3dmResource; + const arrayBuffer = options.arrayBuffer; + let baseResource = options.baseResource; + const byteOffset = options.byteOffset ?? 0; + const releaseGltfJson = options.releaseGltfJson ?? false; + const asynchronous = options.asynchronous ?? true; + const incrementallyLoadTextures = options.incrementallyLoadTextures ?? true; + const upAxis = options.upAxis ?? Axis.Y; + const forwardAxis = options.forwardAxis ?? Axis.X; + const loadAttributesAsTypedArray = + options.loadAttributesAsTypedArray ?? false; + const loadIndicesForWireframe = options.loadIndicesForWireframe ?? false; + const loadPrimitiveOutline = options.loadPrimitiveOutline ?? true; + const enablePick = options.enablePick ?? false; + + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("options.i3dmResource", i3dmResource); + Check.typeOf.object("options.arrayBuffer", arrayBuffer); + //>>includeEnd('debug'); + + baseResource = defined(baseResource) ? baseResource : i3dmResource.clone(); + + this._i3dmResource = i3dmResource; + this._baseResource = baseResource; + this._arrayBuffer = arrayBuffer; + this._byteOffset = byteOffset; + this._releaseGltfJson = releaseGltfJson; + this._asynchronous = asynchronous; + this._incrementallyLoadTextures = incrementallyLoadTextures; + this._upAxis = upAxis; + this._forwardAxis = forwardAxis; + this._loadAttributesAsTypedArray = loadAttributesAsTypedArray; + this._loadIndicesForWireframe = loadIndicesForWireframe; + this._loadPrimitiveOutline = loadPrimitiveOutline; + this._enablePick = enablePick; + + this._state = I3dmLoaderState.NOT_LOADED; + this._promise = undefined; + + this._gltfLoader = undefined; + + // Instanced attributes are initially parsed as typed arrays, but if they + // do not need to be further processed (e.g. turned into transform matrices), + // it is more efficient to turn them into buffers. The I3dmLoader will own the + // resources and store them here. + this._buffers = []; + this._components = undefined; + + this._transform = Matrix4.IDENTITY; + this._batchTable = undefined; + this._featureTable = undefined; + this._instancesLength = 0; + } -Object.defineProperties(I3dmLoader.prototype, { /** * true if textures are loaded, useful when incrementallyLoadTextures is true * @@ -140,11 +137,10 @@ Object.defineProperties(I3dmLoader.prototype, { * @readonly * @private */ - texturesLoaded: { - get: function () { - return this._gltfLoader?.texturesLoaded; - }, - }, + get texturesLoaded() { + return this._gltfLoader?.texturesLoaded; + } + /** * The cache key of the resource * @@ -154,11 +150,9 @@ Object.defineProperties(I3dmLoader.prototype, { * @readonly * @private */ - cacheKey: { - get: function () { - return undefined; - }, - }, + get cacheKey() { + return undefined; + } /** * The loaded components. @@ -170,165 +164,181 @@ Object.defineProperties(I3dmLoader.prototype, { * @readonly * @private */ - components: { - get: function () { - return this._components; - }, - }, -}); - -/** - * Loads the resource. - * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. - * @private - */ -I3dmLoader.prototype.load = function () { - if (defined(this._promise)) { - return this._promise; + get components() { + return this._components; } - // Parse the i3dm into its various sections. - const i3dm = I3dmParser.parse(this._arrayBuffer, this._byteOffset); + /** + * Loads the resource. + * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. + * @private + */ + load() { + if (defined(this._promise)) { + return this._promise; + } - const featureTableJson = i3dm.featureTableJson; - const featureTableBinary = i3dm.featureTableBinary; - const batchTableJson = i3dm.batchTableJson; - const batchTableBinary = i3dm.batchTableBinary; - const gltfFormat = i3dm.gltfFormat; + // Parse the i3dm into its various sections. + const i3dm = I3dmParser.parse(this._arrayBuffer, this._byteOffset); - // Generate the feature table. - const featureTable = new Cesium3DTileFeatureTable( - featureTableJson, - featureTableBinary, - ); - this._featureTable = featureTable; + const featureTableJson = i3dm.featureTableJson; + const featureTableBinary = i3dm.featureTableBinary; + const batchTableJson = i3dm.batchTableJson; + const batchTableBinary = i3dm.batchTableBinary; + const gltfFormat = i3dm.gltfFormat; - // Get the number of instances in the i3dm. - const instancesLength = featureTable.getGlobalProperty("INSTANCES_LENGTH"); - featureTable.featuresLength = instancesLength; - if (!defined(instancesLength)) { - throw new RuntimeError( - "Feature table global property: INSTANCES_LENGTH must be defined", + // Generate the feature table. + const featureTable = new Cesium3DTileFeatureTable( + featureTableJson, + featureTableBinary, ); - } - this._instancesLength = instancesLength; + this._featureTable = featureTable; - // Get the RTC center, if available, and set the loader's transform. - const rtcCenter = featureTable.getGlobalProperty( - "RTC_CENTER", - ComponentDatatype.FLOAT, - 3, - ); - if (defined(rtcCenter)) { - this._transform = Matrix4.fromTranslation(Cartesian3.fromArray(rtcCenter)); - } + // Get the number of instances in the i3dm. + const instancesLength = featureTable.getGlobalProperty("INSTANCES_LENGTH"); + featureTable.featuresLength = instancesLength; + if (!defined(instancesLength)) { + throw new RuntimeError( + "Feature table global property: INSTANCES_LENGTH must be defined", + ); + } + this._instancesLength = instancesLength; - // Save the batch table section to use for StructuralMetadata generation. - this._batchTable = { - json: batchTableJson, - binary: batchTableBinary, - }; - - const loaderOptions = { - upAxis: this._upAxis, - forwardAxis: this._forwardAxis, - releaseGltfJson: this._releaseGltfJson, - incrementallyLoadTextures: this._incrementallyLoadTextures, - loadAttributesAsTypedArray: this._loadAttributesAsTypedArray, - enablePick: this._enablePick, - loadIndicesForWireframe: this._loadIndicesForWireframe, - loadPrimitiveOutline: this._loadPrimitiveOutline, - }; - - if (gltfFormat === 0) { - let gltfUrl = getStringFromTypedArray(i3dm.gltf); - - // We need to remove padding from the end of the model URL in case this tile was part of a composite tile. - // This removes all white space and null characters from the end of the string. - gltfUrl = gltfUrl.replace(/[\s\0]+$/, ""); - const gltfResource = this._baseResource.getDerivedResource({ - url: gltfUrl, - }); - loaderOptions.gltfResource = gltfResource; - loaderOptions.baseResource = gltfResource; - } else { - loaderOptions.gltfResource = this._i3dmResource; - loaderOptions.typedArray = i3dm.gltf; + // Get the RTC center, if available, and set the loader's transform. + const rtcCenter = featureTable.getGlobalProperty( + "RTC_CENTER", + ComponentDatatype.FLOAT, + 3, + ); + if (defined(rtcCenter)) { + this._transform = Matrix4.fromTranslation( + Cartesian3.fromArray(rtcCenter), + ); + } + + // Save the batch table section to use for StructuralMetadata generation. + this._batchTable = { + json: batchTableJson, + binary: batchTableBinary, + }; + + const loaderOptions = { + upAxis: this._upAxis, + forwardAxis: this._forwardAxis, + releaseGltfJson: this._releaseGltfJson, + incrementallyLoadTextures: this._incrementallyLoadTextures, + loadAttributesAsTypedArray: this._loadAttributesAsTypedArray, + enablePick: this._enablePick, + loadIndicesForWireframe: this._loadIndicesForWireframe, + loadPrimitiveOutline: this._loadPrimitiveOutline, + }; + + if (gltfFormat === 0) { + let gltfUrl = getStringFromTypedArray(i3dm.gltf); + + // We need to remove padding from the end of the model URL in case this tile was part of a composite tile. + // This removes all white space and null characters from the end of the string. + gltfUrl = gltfUrl.replace(/[\s\0]+$/, ""); + const gltfResource = this._baseResource.getDerivedResource({ + url: gltfUrl, + }); + loaderOptions.gltfResource = gltfResource; + loaderOptions.baseResource = gltfResource; + } else { + loaderOptions.gltfResource = this._i3dmResource; + loaderOptions.typedArray = i3dm.gltf; + } + + // Create the GltfLoader, update the state and load the glTF. + const gltfLoader = new GltfLoader(loaderOptions); + + this._gltfLoader = gltfLoader; + this._state = I3dmLoaderState.LOADING; + + this._promise = gltfLoader + .load() + .then(() => { + if (this.isDestroyed()) { + return; + } + + this._state = I3dmLoaderState.PROCESSING; + return this; + }) + .catch((error) => { + if (this.isDestroyed()) { + return; + } + throw handleError(this, error); + }); + + return this._promise; } - // Create the GltfLoader, update the state and load the glTF. - const gltfLoader = new GltfLoader(loaderOptions); + process(frameState) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("frameState", frameState); + //>>includeEnd('debug'); - this._gltfLoader = gltfLoader; - this._state = I3dmLoaderState.LOADING; + if (this._state === I3dmLoaderState.READY) { + return true; + } - this._promise = gltfLoader - .load() - .then(() => { - if (this.isDestroyed()) { - return; - } + const gltfLoader = this._gltfLoader; + let ready = false; + if (this._state === I3dmLoaderState.PROCESSING) { + ready = gltfLoader.process(frameState); + } - this._state = I3dmLoaderState.PROCESSING; - return this; - }) - .catch((error) => { - if (this.isDestroyed()) { - return; - } - throw handleError(this, error); - }); + if (!ready) { + return false; + } - return this._promise; -}; + const components = gltfLoader.components; -function handleError(i3dmLoader, error) { - i3dmLoader.unload(); - i3dmLoader._state = I3dmLoaderState.FAILED; - const errorMessage = "Failed to load i3dm"; - return i3dmLoader.getError(errorMessage, error); -} + // Combine the RTC_CENTER transform from the i3dm and the CESIUM_RTC + // transform from the glTF. In practice CESIUM_RTC is not set for + // instanced models but multiply the transforms just in case. + components.transform = Matrix4.multiplyTransformation( + this._transform, + components.transform, + components.transform, + ); -I3dmLoader.prototype.process = function (frameState) { - //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("frameState", frameState); - //>>includeEnd('debug'); + createInstances(this, components, frameState); + createStructuralMetadata(this, components); + this._components = components; - if (this._state === I3dmLoaderState.READY) { - return true; - } + // Now that we have the parsed components, we can release the array buffer + this._arrayBuffer = undefined; - const gltfLoader = this._gltfLoader; - let ready = false; - if (this._state === I3dmLoaderState.PROCESSING) { - ready = gltfLoader.process(frameState); + this._state = I3dmLoaderState.READY; + return true; } - if (!ready) { - return false; + isUnloaded() { + return this._state === I3dmLoaderState.UNLOADED; } - const components = gltfLoader.components; - - // Combine the RTC_CENTER transform from the i3dm and the CESIUM_RTC - // transform from the glTF. In practice CESIUM_RTC is not set for - // instanced models but multiply the transforms just in case. - components.transform = Matrix4.multiplyTransformation( - this._transform, - components.transform, - components.transform, - ); + unload() { + if (defined(this._gltfLoader) && !this._gltfLoader.isDestroyed()) { + this._gltfLoader.unload(); + } - createInstances(this, components, frameState); - createStructuralMetadata(this, components); - this._components = components; + unloadBuffers(this); - // Now that we have the parsed components, we can release the array buffer - this._arrayBuffer = undefined; + this._components = undefined; + this._arrayBuffer = undefined; + this._state = I3dmLoaderState.UNLOADED; + } +} - this._state = I3dmLoaderState.READY; - return true; -}; +function handleError(i3dmLoader, error) { + i3dmLoader.unload(); + i3dmLoader._state = I3dmLoaderState.FAILED; + const errorMessage = "Failed to load i3dm"; + return i3dmLoader.getError(errorMessage, error); +} function createStructuralMetadata(loader, components) { const batchTable = loader._batchTable; @@ -864,20 +874,4 @@ function unloadBuffers(loader) { buffers.length = 0; } -I3dmLoader.prototype.isUnloaded = function () { - return this._state === I3dmLoaderState.UNLOADED; -}; - -I3dmLoader.prototype.unload = function () { - if (defined(this._gltfLoader) && !this._gltfLoader.isDestroyed()) { - this._gltfLoader.unload(); - } - - unloadBuffers(this); - - this._components = undefined; - this._arrayBuffer = undefined; - this._state = I3dmLoaderState.UNLOADED; -}; - export default I3dmLoader; diff --git a/packages/engine/Source/Scene/Model/PntsLoader.js b/packages/engine/Source/Scene/Model/PntsLoader.js index 1461678210e..f408e1f0718 100644 --- a/packages/engine/Source/Scene/Model/PntsLoader.js +++ b/packages/engine/Source/Scene/Model/PntsLoader.js @@ -47,40 +47,37 @@ const MetallicRoughness = ModelComponents.MetallicRoughness; * @param {number} [options.byteOffset] The byte offset to the beginning of the pnts contents in the array buffer * @param {boolean} [options.loadAttributesFor2D=false] If true, load the positions buffer as a typed array for accurately projecting models to 2D. */ -function PntsLoader(options) { - options = options ?? Frozen.EMPTY_OBJECT; +class PntsLoader extends ResourceLoader { + constructor(options) { + super(); - const arrayBuffer = options.arrayBuffer; - const byteOffset = options.byteOffset ?? 0; + options = options ?? Frozen.EMPTY_OBJECT; - //>>includeStart('debug', pragmas.debug); - Check.typeOf.object("options.arrayBuffer", arrayBuffer); - //>>includeEnd('debug'); + const arrayBuffer = options.arrayBuffer; + const byteOffset = options.byteOffset ?? 0; - this._arrayBuffer = arrayBuffer; - this._byteOffset = byteOffset; - this._loadAttributesFor2D = options.loadAttributesFor2D ?? false; + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("options.arrayBuffer", arrayBuffer); + //>>includeEnd('debug'); - this._parsedContent = undefined; - this._decodePromise = undefined; - this._decodedAttributes = undefined; + this._arrayBuffer = arrayBuffer; + this._byteOffset = byteOffset; + this._loadAttributesFor2D = options.loadAttributesFor2D ?? false; - this._promise = undefined; - this._error = undefined; - this._state = ResourceLoaderState.UNLOADED; - this._buffers = []; + this._parsedContent = undefined; + this._decodePromise = undefined; + this._decodedAttributes = undefined; - // The batch table object contains a json and a binary component access using keys of the same name. - this._components = undefined; - this._transform = Matrix4.IDENTITY; -} + this._promise = undefined; + this._error = undefined; + this._state = ResourceLoaderState.UNLOADED; + this._buffers = []; -if (defined(Object.create)) { - PntsLoader.prototype = Object.create(ResourceLoader.prototype); - PntsLoader.prototype.constructor = PntsLoader; -} + // The batch table object contains a json and a binary component access using keys of the same name. + this._components = undefined; + this._transform = Matrix4.IDENTITY; + } -Object.defineProperties(PntsLoader.prototype, { /** * The cache key of the resource * @@ -90,11 +87,9 @@ Object.defineProperties(PntsLoader.prototype, { * @readonly * @private */ - cacheKey: { - get: function () { - return undefined; - }, - }, + get cacheKey() { + return undefined; + } /** * The loaded components. @@ -105,11 +100,9 @@ Object.defineProperties(PntsLoader.prototype, { * @readonly * @private */ - components: { - get: function () { - return this._components; - }, - }, + get components() { + return this._components; + } /** * A world-space transform to apply to the primitives. @@ -121,50 +114,60 @@ Object.defineProperties(PntsLoader.prototype, { * @readonly * @private */ - transform: { - get: function () { - return this._transform; - }, - }, -}); - -/** - * Loads the resource. - * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. - * @private - */ -PntsLoader.prototype.load = function () { - if (defined(this._promise)) { - return this._promise; + get transform() { + return this._transform; } - this._parsedContent = PntsParser.parse(this._arrayBuffer, this._byteOffset); - this._state = ResourceLoaderState.PROCESSING; + /** + * Loads the resource. + * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. + * @private + */ + load() { + if (defined(this._promise)) { + return this._promise; + } - this._promise = Promise.resolve(this); -}; + this._parsedContent = PntsParser.parse(this._arrayBuffer, this._byteOffset); + this._state = ResourceLoaderState.PROCESSING; -PntsLoader.prototype.process = function (frameState) { - if (defined(this._error)) { - const error = this._error; - this._error = undefined; - throw error; + this._promise = Promise.resolve(this); } - if (this._state === ResourceLoaderState.READY) { - return true; - } + process(frameState) { + if (defined(this._error)) { + const error = this._error; + this._error = undefined; + throw error; + } + + if (this._state === ResourceLoaderState.READY) { + return true; + } + + if (this._state === ResourceLoaderState.PROCESSING) { + if (defined(this._decodePromise)) { + return false; + } - if (this._state === ResourceLoaderState.PROCESSING) { - if (defined(this._decodePromise)) { - return false; + this._decodePromise = decodeDraco(this, frameState.context); } - this._decodePromise = decodeDraco(this, frameState.context); + return false; } - return false; -}; + unload() { + const buffers = this._buffers; + for (let i = 0; i < buffers.length; i++) { + buffers[i].destroy(); + } + buffers.length = 0; + + this._components = undefined; + this._parsedContent = undefined; + this._arrayBuffer = undefined; + } +} function decodeDraco(loader, context) { const parsedContent = loader._parsedContent; @@ -701,16 +704,4 @@ function addPropertyAttributesToPrimitive( primitive.propertyAttributeIds = [0]; } -PntsLoader.prototype.unload = function () { - const buffers = this._buffers; - for (let i = 0; i < buffers.length; i++) { - buffers[i].destroy(); - } - buffers.length = 0; - - this._components = undefined; - this._parsedContent = undefined; - this._arrayBuffer = undefined; -}; - export default PntsLoader; diff --git a/packages/engine/Source/Scene/ResourceLoader.js b/packages/engine/Source/Scene/ResourceLoader.js index d4ca1e4f8e1..fddacfc18f0 100644 --- a/packages/engine/Source/Scene/ResourceLoader.js +++ b/packages/engine/Source/Scene/ResourceLoader.js @@ -17,9 +17,7 @@ import RuntimeError from "../Core/RuntimeError.js"; * * @private */ -function ResourceLoader() {} - -Object.defineProperties(ResourceLoader.prototype, { +class ResourceLoader { /** * The cache key of the resource. * @@ -29,99 +27,97 @@ Object.defineProperties(ResourceLoader.prototype, { * @readonly * @private */ - cacheKey: { - // eslint-disable-next-line getter-return - get: function () { - DeveloperError.throwInstantiationError(); - }, - }, -}); + // eslint-disable-next-line getter-return + get cacheKey() { + DeveloperError.throwInstantiationError(); + } -/** - * Loads the resource. - * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. - * @private - */ -ResourceLoader.prototype.load = function () { - DeveloperError.throwInstantiationError(); -}; + /** + * Loads the resource. + * @returns {Promise} A promise which resolves to the loader when the resource loading is completed. + * @private + */ + load() { + DeveloperError.throwInstantiationError(); + } -/** - * Unloads the resource. - * @private - */ -ResourceLoader.prototype.unload = function () {}; + /** + * Unloads the resource. + * @private + */ + unload() {} -/** - * Processes the resource until it becomes ready. - * - * @param {FrameState} frameState The frame state. - * @returns {boolean} true once all resourced are ready. - * @private - */ -ResourceLoader.prototype.process = function (frameState) { - return false; -}; + /** + * Processes the resource until it becomes ready. + * + * @param {FrameState} frameState The frame state. + * @returns {boolean} true once all resourced are ready. + * @private + */ + process(frameState) { + return false; + } -/** - * Constructs a {@link RuntimeError} from an errorMessage and an error. - * - * @param {string} errorMessage The error message. - * @param {Error} [error] The error. - * - * @returns {RuntimeError} The runtime error. - * @private - */ -ResourceLoader.prototype.getError = function (errorMessage, error) { - //>>includeStart('debug', pragmas.debug); - Check.typeOf.string("errorMessage", errorMessage); - //>>includeEnd('debug'); + /** + * Constructs a {@link RuntimeError} from an errorMessage and an error. + * + * @param {string} errorMessage The error message. + * @param {Error} [error] The error. + * + * @returns {RuntimeError} The runtime error. + * @private + */ + getError(errorMessage, error) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.string("errorMessage", errorMessage); + //>>includeEnd('debug'); - if (defined(error) && defined(error.message)) { - errorMessage += `\n${error.message}`; - } + if (defined(error) && defined(error.message)) { + errorMessage += `\n${error.message}`; + } - const runtimeError = new RuntimeError(errorMessage); - if (defined(error)) { - runtimeError.stack = `Original stack:\n${error.stack}\nHandler stack:\n${runtimeError.stack}`; - } + const runtimeError = new RuntimeError(errorMessage); + if (defined(error)) { + runtimeError.stack = `Original stack:\n${error.stack}\nHandler stack:\n${runtimeError.stack}`; + } - return runtimeError; -}; + return runtimeError; + } -/** - * Returns true if this object was destroyed; otherwise, false. - *

- * If this object was destroyed, it should not be used; calling any function other than - * isDestroyed will result in a {@link DeveloperError} exception. - * - * @returns {boolean} true if this object was destroyed; otherwise, false. - * - * @see ResourceLoader#destroy - * @private - */ -ResourceLoader.prototype.isDestroyed = function () { - return false; -}; + /** + * Returns true if this object was destroyed; otherwise, false. + *

+ * If this object was destroyed, it should not be used; calling any function other than + * isDestroyed will result in a {@link DeveloperError} exception. + * + * @returns {boolean} true if this object was destroyed; otherwise, false. + * + * @see ResourceLoader#destroy + * @private + */ + isDestroyed() { + return false; + } -/** - * Destroys the loaded resource. - *

- * Once an object is destroyed, it should not be used; calling any function other than - * isDestroyed will result in a {@link DeveloperError} exception. Therefore, - * assign the return value (undefined) to the object as done in the example. - * - * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. - * - * @example - * resourceLoader = resourceLoader && resourceLoader.destroy(); - * - * @see ResourceLoader#isDestroyed - * @private - */ -ResourceLoader.prototype.destroy = function () { - this.unload(); - return destroyObject(this); -}; + /** + * Destroys the loaded resource. + *

+ * Once an object is destroyed, it should not be used; calling any function other than + * isDestroyed will result in a {@link DeveloperError} exception. Therefore, + * assign the return value (undefined) to the object as done in the example. + * + * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called. + * + * @example + * resourceLoader = resourceLoader && resourceLoader.destroy(); + * + * @see ResourceLoader#isDestroyed + * @private + */ + destroy() { + this.unload(); + return destroyObject(this); + } +} export default ResourceLoader;