diff --git a/packages/engine/Source/Scene/GltfDefaultVertexBufferLoader.js b/packages/engine/Source/Scene/GltfDefaultVertexBufferLoader.js new file mode 100644 index 00000000000..391254c3d25 --- /dev/null +++ b/packages/engine/Source/Scene/GltfDefaultVertexBufferLoader.js @@ -0,0 +1,96 @@ +import Check from "../Core/Check.js"; +import defaultValue from "../Core/defaultValue.js"; +import defined from "../Core/defined.js"; +import GltfVertexBufferLoader from "./GltfVertexBufferLoader.js"; +import ResourceLoaderState from "./ResourceLoaderState.js"; + +/** + * Loads a vertex buffer from a glTF buffer view. + *
+ * Implements the {@link ResourceLoader} interface. + *
+ * + * @alias GltfDefaultVertexBufferLoader + * @constructor + * @augments GltfVertexBufferLoader + * + * @param {object} options Object with the properties that are required for {@link GltfVertexBufferLoader}, and the following properties: + * @param {number} [options.bufferViewId] The bufferView ID corresponding to the vertex buffer. + * + * @private + */ +function GltfDefaultVertexBufferLoader(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + GltfVertexBufferLoader.call(this, options); + + const bufferViewId = options.bufferViewId; + + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("options.bufferViewId", bufferViewId); + //>>includeEnd('debug'); +} + +if (defined(Object.create)) { + GltfDefaultVertexBufferLoader.prototype = Object.create( + GltfVertexBufferLoader.prototype, + ); + GltfDefaultVertexBufferLoader.prototype.constructor = + GltfDefaultVertexBufferLoader; +} + +GltfDefaultVertexBufferLoader.prototype.loadInternal = async function () { + this._promise = this.loadFromBufferView(); + return this._promise; +}; + +GltfDefaultVertexBufferLoader.prototype.loadFromBufferView = async function () { + this._state = ResourceLoaderState.LOADING; + const resourceCache = this._resourceCache; + try { + const bufferViewLoader = resourceCache.getBufferViewLoader({ + gltf: this._gltf, + bufferViewId: this._bufferViewId, + gltfResource: this._gltfResource, + baseResource: this._baseResource, + }); + this._bufferViewLoader = bufferViewLoader; + await bufferViewLoader.load(); + + if (this.isDestroyed()) { + return; + } + + this._typedArray = bufferViewLoader.typedArray; + this._state = ResourceLoaderState.PROCESSING; + return this; + } catch (error) { + if (this.isDestroyed()) { + return; + } + + handleError(this, error); + } +}; + +function handleError(vertexBufferLoader, error) { + vertexBufferLoader.unload(); + vertexBufferLoader._state = ResourceLoaderState.FAILED; + const errorMessage = "Failed to load vertex buffer"; + throw vertexBufferLoader.getError(errorMessage, error); +} + +GltfDefaultVertexBufferLoader.prototype.processInternal = function () {}; + +GltfVertexBufferLoader.prototype.unloadInternal = function () { + const resourceCache = this._resourceCache; + if ( + defined(this._bufferViewLoader) && + !this._bufferViewLoader.isDestroyed() + ) { + resourceCache.unload(this._bufferViewLoader); + } + + this._bufferViewLoader = undefined; +}; + +export default GltfDefaultVertexBufferLoader; diff --git a/packages/engine/Source/Scene/GltfDracoVertexBufferLoader.js b/packages/engine/Source/Scene/GltfDracoVertexBufferLoader.js new file mode 100644 index 00000000000..aeb5a80a408 --- /dev/null +++ b/packages/engine/Source/Scene/GltfDracoVertexBufferLoader.js @@ -0,0 +1,235 @@ +import Check from "../Core/Check.js"; +import defaultValue from "../Core/defaultValue.js"; +import defined from "../Core/defined.js"; +import AttributeType from "./AttributeType.js"; +import GltfVertexBufferLoader from "./GltfVertexBufferLoader.js"; +import ModelComponents from "./ModelComponents.js"; +import ResourceLoaderState from "./ResourceLoaderState.js"; + +/** + * Loads a vertex buffer from a glTF buffer view. + *+ * Implements the {@link ResourceLoader} interface. + *
+ * + * @alias GltfDracoVertexBufferLoader + * @constructor + * @augments GltfVertexBufferLoader + * + * @param {object} options Object with the properties that are required for {@link GltfVertexBufferLoader}, and the following properties: + * @param {object} [options.draco] The Draco extension object. + * @param {string} [options.attributeSemantic] The attribute semantic, e.g. POSITION or NORMAL. + * + * @private + */ +function GltfDracoVertexBufferLoader(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + GltfVertexBufferLoader.call(this, options); + + const draco = options.draco; + const attributeSemantic = options.attributeSemantic; + + //>>includeStart('debug', pragmas.debug); + Check.typeOf.object("options.draco", draco); + Check.typeOf.string("options.attributeSemantic", attributeSemantic); + //>>includeEnd('debug'); + + this._accessorId = draco.attributes[attributeSemantic]; + this._dracoLoader = undefined; + this._quantization = undefined; +} + +if (defined(Object.create)) { + GltfDracoVertexBufferLoader.prototype = Object.create( + GltfVertexBufferLoader.prototype, + ); + GltfDracoVertexBufferLoader.prototype.constructor = + GltfDracoVertexBufferLoader; +} + +Object.defineProperties(GltfDracoVertexBufferLoader.prototype, { + /** + * Information about the quantized vertex attribute after Draco decode. + * + * @memberof GltfVertexBufferLoader.prototype + * + * @type {ModelComponents.Quantization} + * @readonly + * @private + */ + quantization: { + get: function () { + return this._quantization; + }, + }, +}); + +GltfDracoVertexBufferLoader.prototype.loadInternal = async function () { + this._promise = loadFromDraco(this); + return this._promise; +}; + +async function loadFromDraco(vertexBufferLoader) { + vertexBufferLoader._state = ResourceLoaderState.LOADING; + const resourceCache = vertexBufferLoader._resourceCache; + try { + const dracoLoader = resourceCache.getDracoLoader({ + gltf: vertexBufferLoader._gltf, + draco: vertexBufferLoader._draco, + gltfResource: vertexBufferLoader._gltfResource, + baseResource: vertexBufferLoader._baseResource, + }); + vertexBufferLoader._dracoLoader = dracoLoader; + await dracoLoader.load(); + + if (vertexBufferLoader.isDestroyed()) { + return; + } + + // Now wait for process() to run to finish loading + vertexBufferLoader._state = ResourceLoaderState.LOADED; + return vertexBufferLoader; + } catch { + if (vertexBufferLoader.isDestroyed()) { + return; + } + + handleError(vertexBufferLoader); + } +} + +GltfDracoVertexBufferLoader.prototype.processDraco = function () { + this._state = ResourceLoaderState.PROCESSING; + const dracoLoader = this._dracoLoader; + + // Get the typed array and quantization information + const decodedVertexAttributes = dracoLoader.decodedData.vertexAttributes; + const attributeSemantic = this._attributeSemantic; + const dracoAttribute = decodedVertexAttributes[attributeSemantic]; + const accessorId = this._accessorId; + const accessor = this._gltf.accessors[accessorId]; + const type = accessor.type; + const typedArray = dracoAttribute.array; + const dracoQuantization = dracoAttribute.data.quantization; + if (defined(dracoQuantization)) { + this._quantization = getQuantizationInformation( + dracoQuantization, + dracoAttribute.data.componentDatatype, + dracoAttribute.data.componentsPerAttribute, + type, + ); + } + + this._typedArray = new Uint8Array( + typedArray.buffer, + typedArray.byteOffset, + typedArray.byteLength, + ); +}; + +function getQuantizationInformation( + dracoQuantization, + componentDatatype, + componentCount, + type, +) { + const quantizationBits = dracoQuantization.quantizationBits; + const normalizationRange = (1 << quantizationBits) - 1; + const normalizationDivisor = 1.0 / normalizationRange; + + const quantization = new ModelComponents.Quantization(); + quantization.componentDatatype = componentDatatype; + quantization.octEncoded = dracoQuantization.octEncoded; + quantization.octEncodedZXY = true; + quantization.type = type; + + if (quantization.octEncoded) { + quantization.type = AttributeType.VEC2; + quantization.normalizationRange = normalizationRange; + } else { + const MathType = AttributeType.getMathType(type); + if (MathType === Number) { + const dimensions = dracoQuantization.range; + quantization.quantizedVolumeOffset = dracoQuantization.minValues[0]; + quantization.quantizedVolumeDimensions = dimensions; + quantization.normalizationRange = normalizationRange; + quantization.quantizedVolumeStepSize = dimensions * normalizationDivisor; + } else { + quantization.quantizedVolumeOffset = MathType.unpack( + dracoQuantization.minValues, + ); + quantization.normalizationRange = MathType.unpack( + new Array(componentCount).fill(normalizationRange), + ); + const packedDimensions = new Array(componentCount).fill( + dracoQuantization.range, + ); + quantization.quantizedVolumeDimensions = + MathType.unpack(packedDimensions); + + // Computing the step size + const packedSteps = packedDimensions.map(function (dimension) { + return dimension * normalizationDivisor; + }); + quantization.quantizedVolumeStepSize = MathType.unpack(packedSteps); + } + } + + return quantization; +} + +function handleError(vertexBufferLoader, error) { + vertexBufferLoader.unload(); + vertexBufferLoader._state = ResourceLoaderState.FAILED; + const errorMessage = "Failed to load vertex buffer"; + throw vertexBufferLoader.getError(errorMessage, error); +} + +GltfDracoVertexBufferLoader.prototype.processInternal = function (frameState) { + try { + const ready = this._dracoLoader.process(frameState); + if (!ready) { + return; + } + } catch (error) { + handleError(this, error); + } + this.processDraco(); +}; + +GltfDracoVertexBufferLoader.prototype.processDraco = function () { + this._state = ResourceLoaderState.PROCESSING; + const dracoLoader = this._dracoLoader; + + // Get the typed array and quantization information + const decodedVertexAttributes = dracoLoader.decodedData.vertexAttributes; + const attributeSemantic = this._attributeSemantic; + const dracoAttribute = decodedVertexAttributes[attributeSemantic]; + const accessorId = this._accessorId; + const accessor = this._gltf.accessors[accessorId]; + const type = accessor.type; + const typedArray = dracoAttribute.array; + const dracoQuantization = dracoAttribute.data.quantization; + if (defined(dracoQuantization)) { + this._quantization = getQuantizationInformation( + dracoQuantization, + dracoAttribute.data.componentDatatype, + dracoAttribute.data.componentsPerAttribute, + type, + ); + } + + this._typedArray = new Uint8Array( + typedArray.buffer, + typedArray.byteOffset, + typedArray.byteLength, + ); +}; + +GltfDracoVertexBufferLoader.prototype.unloadInternal = function () { + const resourceCache = this._resourceCache; + resourceCache.unload(this._dracoLoader); + this._dracoLoader = undefined; +}; + +export default GltfDracoVertexBufferLoader; diff --git a/packages/engine/Source/Scene/GltfLoader.js b/packages/engine/Source/Scene/GltfLoader.js index 96d50daeacf..cb99d91ea77 100644 --- a/packages/engine/Source/Scene/GltfLoader.js +++ b/packages/engine/Source/Scene/GltfLoader.js @@ -1,3 +1,5 @@ +/* eslint-disable */ + import ArticulationStageType from "../Core/ArticulationStageType.js"; import Cartesian2 from "../Core/Cartesian2.js"; import Cartesian3 from "../Core/Cartesian3.js"; @@ -1117,18 +1119,9 @@ function finalizeAttribute( } } -function loadAttribute( - loader, - accessorId, - semanticInfo, - draco, - loadBuffer, - loadTypedArray, - frameState, -) { +function loadAttribute(loader, accessorId, semanticInfo) { const gltf = loader.gltfJson; const accessor = gltf.accessors[accessorId]; - const bufferViewId = accessor.bufferView; const gltfSemantic = semanticInfo.gltfSemantic; const renamedSemantic = semanticInfo.renamedSemantic; @@ -1147,47 +1140,6 @@ function loadAttribute( setIndex, ); - if (!defined(draco) && !defined(bufferViewId)) { - return attribute; - } - - const vertexBufferLoader = getVertexBufferLoader( - loader, - accessorId, - gltfSemantic, - draco, - loadBuffer, - loadTypedArray, - frameState, - ); - - const index = loader._geometryLoaders.length; - loader._geometryLoaders.push(vertexBufferLoader); - const promise = vertexBufferLoader.load(); - loader._loaderPromises.push(promise); - // This can only execute once vertexBufferLoader.process() has run and returns true - // Save this finish callback by the loader index so it can be called - // in process(). - loader._geometryCallbacks[index] = () => { - if (GltfUtil.hasDracoCompression(draco, gltfSemantic)) { - finalizeDracoAttribute( - attribute, - vertexBufferLoader, - loadBuffer, - loadTypedArray, - ); - } else { - finalizeAttribute( - gltf, - accessor, - attribute, - vertexBufferLoader, - loadBuffer, - loadTypedArray, - ); - } - }; - return attribute; } @@ -1247,6 +1199,45 @@ function loadVertexAttribute( frameState, ); + { + const vertexBufferLoader = getVertexBufferLoader( + loader, + accessorId, + gltfSemantic, + draco, + loadBuffer, + loadTypedArray, + frameState, + ); + + const index = loader._geometryLoaders.length; + loader._geometryLoaders.push(vertexBufferLoader); + const promise = vertexBufferLoader.load(); + loader._loaderPromises.push(promise); + // This can only execute once vertexBufferLoader.process() has run and returns true + // Save this finish callback by the loader index so it can be called + // in process(). + loader._geometryCallbacks[index] = () => { + if (GltfUtil.hasDracoCompression(draco, gltfSemantic)) { + finalizeDracoAttribute( + attribute, + vertexBufferLoader, + loadBuffer, + loadTypedArray, + ); + } else { + finalizeAttribute( + gltf, + accessor, + attribute, + vertexBufferLoader, + loadBuffer, + loadTypedArray, + ); + } + }; + } + const attributePlan = new PrimitiveLoadPlan.AttributeLoadPlan(attribute); attributePlan.loadBuffer = outputBuffer; attributePlan.loadTypedArray = outputTypedArray; @@ -1929,15 +1920,20 @@ function loadPrimitive(loader, gltfPrimitive, hasInstances, frameState) { hasFeatureIds = true; } - const attributePlan = loadVertexAttribute( - loader, - accessorId, - semanticInfo, - draco, - hasInstances, - needsPostProcessing, - frameState, - ); + let attributePlan; + if (defined(draco)) { + // TODO Handle draco + } else { + attributePlan = loadVertexAttribute( + loader, + accessorId, + semanticInfo, + draco, + hasInstances, + needsPostProcessing, + frameState, + ); + } primitivePlan.attributePlans.push(attributePlan); primitive.attributes.push(attributePlan.attribute); diff --git a/packages/engine/Source/Scene/GltfVertexBufferLoader.js b/packages/engine/Source/Scene/GltfVertexBufferLoader.js index f149fca1367..c6cde40cc0d 100644 --- a/packages/engine/Source/Scene/GltfVertexBufferLoader.js +++ b/packages/engine/Source/Scene/GltfVertexBufferLoader.js @@ -4,10 +4,7 @@ import defined from "../Core/defined.js"; import DeveloperError from "../Core/DeveloperError.js"; import Buffer from "../Renderer/Buffer.js"; import BufferUsage from "../Renderer/BufferUsage.js"; -import AttributeType from "./AttributeType.js"; -import GltfUtil from "./GltfUtil.js"; import JobType from "./JobType.js"; -import ModelComponents from "./ModelComponents.js"; import ResourceLoader from "./ResourceLoader.js"; import ResourceLoaderState from "./ResourceLoaderState.js"; @@ -26,19 +23,11 @@ import ResourceLoaderState from "./ResourceLoaderState.js"; * @param {object} options.gltf The glTF JSON. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to. - * @param {number} [options.bufferViewId] The bufferView ID corresponding to the vertex buffer. - * @param {object} [options.draco] The Draco extension object. - * @param {string} [options.attributeSemantic] The attribute semantic, e.g. POSITION or NORMAL. - * @param {number} [options.accessorId] The accessor id. * @param {string} [options.cacheKey] The cache key of the resource. * @param {boolean} [options.asynchronous=true] Determines if WebGL resource creation will be spread out over several frames or block until all WebGL resources are created. * @param {boolean} [options.loadBuffer=false] Load vertex buffer as a GPU vertex buffer. * @param {boolean} [options.loadTypedArray=false] Load vertex buffer as a typed array. * - * @exception {DeveloperError} One of options.bufferViewId and options.draco must be defined. - * @exception {DeveloperError} When options.draco is defined options.attributeSemantic must also be defined. - * @exception {DeveloperError} When options.draco is defined options.accessorId must also be defined. - * * @private */ function GltfVertexBufferLoader(options) { @@ -47,10 +36,6 @@ function GltfVertexBufferLoader(options) { const gltf = options.gltf; const gltfResource = options.gltfResource; const baseResource = options.baseResource; - const bufferViewId = options.bufferViewId; - const draco = options.draco; - const attributeSemantic = options.attributeSemantic; - const accessorId = options.accessorId; const cacheKey = options.cacheKey; const asynchronous = defaultValue(options.asynchronous, true); const loadBuffer = defaultValue(options.loadBuffer, false); @@ -66,54 +51,21 @@ function GltfVertexBufferLoader(options) { "At least one of loadBuffer and loadTypedArray must be true.", ); } - - const hasBufferViewId = defined(bufferViewId); - const hasDraco = GltfUtil.hasDracoCompression(draco, attributeSemantic); - const hasAttributeSemantic = defined(attributeSemantic); - const hasAccessorId = defined(accessorId); - - if (hasBufferViewId === hasDraco) { - throw new DeveloperError( - "One of options.bufferViewId and options.draco must be defined.", - ); - } - - if (hasDraco && !hasAttributeSemantic) { - throw new DeveloperError( - "When options.draco is defined options.attributeSemantic 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.draco", draco); - Check.typeOf.string("options.attributeSemantic", attributeSemantic); - Check.typeOf.number("options.accessorId", accessorId); - } //>>includeEnd('debug'); this._resourceCache = resourceCache; this._gltfResource = gltfResource; this._baseResource = baseResource; this._gltf = gltf; - this._bufferViewId = bufferViewId; - this._draco = draco; - 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; } @@ -166,20 +118,6 @@ Object.defineProperties(GltfVertexBufferLoader.prototype, { return this._typedArray; }, }, - /** - * Information about the quantized vertex attribute after Draco decode. - * - * @memberof GltfVertexBufferLoader.prototype - * - * @type {ModelComponents.Quantization} - * @readonly - * @private - */ - quantization: { - get: function () { - return this._quantization; - }, - }, }); /** @@ -192,159 +130,22 @@ GltfVertexBufferLoader.prototype.load = async function () { return this._promise; } - if (GltfUtil.hasDracoCompression(this._draco, this._attributeSemantic)) { - this._promise = loadFromDraco(this); - return this._promise; - } - - this._promise = loadFromBufferView(this); + this._promise = this.loadInternal(); return this._promise; }; -function getQuantizationInformation( - dracoQuantization, - componentDatatype, - componentCount, - type, -) { - const quantizationBits = dracoQuantization.quantizationBits; - const normalizationRange = (1 << quantizationBits) - 1; - const normalizationDivisor = 1.0 / normalizationRange; - - const quantization = new ModelComponents.Quantization(); - quantization.componentDatatype = componentDatatype; - quantization.octEncoded = dracoQuantization.octEncoded; - quantization.octEncodedZXY = true; - quantization.type = type; - - if (quantization.octEncoded) { - quantization.type = AttributeType.VEC2; - quantization.normalizationRange = normalizationRange; - } else { - const MathType = AttributeType.getMathType(type); - if (MathType === Number) { - const dimensions = dracoQuantization.range; - quantization.quantizedVolumeOffset = dracoQuantization.minValues[0]; - quantization.quantizedVolumeDimensions = dimensions; - quantization.normalizationRange = normalizationRange; - quantization.quantizedVolumeStepSize = dimensions * normalizationDivisor; - } else { - quantization.quantizedVolumeOffset = MathType.unpack( - dracoQuantization.minValues, - ); - quantization.normalizationRange = MathType.unpack( - new Array(componentCount).fill(normalizationRange), - ); - const packedDimensions = new Array(componentCount).fill( - dracoQuantization.range, - ); - quantization.quantizedVolumeDimensions = - MathType.unpack(packedDimensions); - - // Computing the step size - const packedSteps = packedDimensions.map(function (dimension) { - return dimension * normalizationDivisor; - }); - quantization.quantizedVolumeStepSize = MathType.unpack(packedSteps); - } - } - - return quantization; -} - -async function loadFromDraco(vertexBufferLoader) { - vertexBufferLoader._state = ResourceLoaderState.LOADING; - const resourceCache = vertexBufferLoader._resourceCache; - try { - const dracoLoader = resourceCache.getDracoLoader({ - gltf: vertexBufferLoader._gltf, - draco: vertexBufferLoader._draco, - gltfResource: vertexBufferLoader._gltfResource, - baseResource: vertexBufferLoader._baseResource, - }); - vertexBufferLoader._dracoLoader = dracoLoader; - await dracoLoader.load(); - - if (vertexBufferLoader.isDestroyed()) { - return; - } - - // Now wait for process() to run to finish loading - vertexBufferLoader._state = ResourceLoaderState.LOADED; - return vertexBufferLoader; - } catch { - if (vertexBufferLoader.isDestroyed()) { - return; - } - - handleError(vertexBufferLoader); - } -} - -function processDraco(vertexBufferLoader) { - vertexBufferLoader._state = ResourceLoaderState.PROCESSING; - const dracoLoader = vertexBufferLoader._dracoLoader; - - // Get the typed array and quantization information - const decodedVertexAttributes = dracoLoader.decodedData.vertexAttributes; - const attributeSemantic = vertexBufferLoader._attributeSemantic; - const dracoAttribute = decodedVertexAttributes[attributeSemantic]; - const accessorId = vertexBufferLoader._accessorId; - const accessor = vertexBufferLoader._gltf.accessors[accessorId]; - const type = accessor.type; - const typedArray = dracoAttribute.array; - const dracoQuantization = dracoAttribute.data.quantization; - if (defined(dracoQuantization)) { - vertexBufferLoader._quantization = getQuantizationInformation( - dracoQuantization, - dracoAttribute.data.componentDatatype, - dracoAttribute.data.componentsPerAttribute, - type, - ); - } - - vertexBufferLoader._typedArray = new Uint8Array( - typedArray.buffer, - typedArray.byteOffset, - typedArray.byteLength, - ); -} - -async function loadFromBufferView(vertexBufferLoader) { - vertexBufferLoader._state = ResourceLoaderState.LOADING; - const resourceCache = vertexBufferLoader._resourceCache; - try { - const bufferViewLoader = resourceCache.getBufferViewLoader({ - gltf: vertexBufferLoader._gltf, - bufferViewId: vertexBufferLoader._bufferViewId, - gltfResource: vertexBufferLoader._gltfResource, - baseResource: vertexBufferLoader._baseResource, - }); - vertexBufferLoader._bufferViewLoader = bufferViewLoader; - await bufferViewLoader.load(); - - if (vertexBufferLoader.isDestroyed()) { - return; - } +GltfVertexBufferLoader.prototype.loadInternal = async function () { + DeveloperError.throwInstantiationError(); +}; - vertexBufferLoader._typedArray = bufferViewLoader.typedArray; - vertexBufferLoader._state = ResourceLoaderState.PROCESSING; - return vertexBufferLoader; - } catch (error) { - if (vertexBufferLoader.isDestroyed()) { - return; - } +GltfVertexBufferLoader.prototype.processInternal = async function () { + DeveloperError.throwInstantiationError(); +}; - handleError(vertexBufferLoader, error); - } -} +GltfVertexBufferLoader.prototype.unloadInternal = async function () { + DeveloperError.throwInstantiationError(); +}; -function handleError(vertexBufferLoader, error) { - vertexBufferLoader.unload(); - vertexBufferLoader._state = ResourceLoaderState.FAILED; - const errorMessage = "Failed to load vertex buffer"; - throw vertexBufferLoader.getError(errorMessage, error); -} function CreateVertexBufferJob() { this.typedArray = undefined; this.context = undefined; @@ -394,17 +195,9 @@ GltfVertexBufferLoader.prototype.process = function (frameState) { 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 (this._state === ResourceLoader.LOADED) { + this.processInternal(frameState); + return true; } let buffer; @@ -441,21 +234,8 @@ GltfVertexBufferLoader.prototype.unload = function () { 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.unloadInternal(); - this._bufferViewLoader = undefined; - this._dracoLoader = undefined; this._typedArray = undefined; this._buffer = undefined; this._gltf = undefined; diff --git a/packages/engine/Source/Scene/ResourceCache.js b/packages/engine/Source/Scene/ResourceCache.js index 878dcbd7d52..92dcd50bf32 100644 --- a/packages/engine/Source/Scene/ResourceCache.js +++ b/packages/engine/Source/Scene/ResourceCache.js @@ -4,12 +4,13 @@ import defined from "../Core/defined.js"; import DeveloperError from "../Core/DeveloperError.js"; import BufferLoader from "./BufferLoader.js"; import GltfBufferViewLoader from "./GltfBufferViewLoader.js"; +import GltfDefaultVertexBufferLoader from "./GltfDefaultVertexBufferLoader.js"; import GltfDracoLoader from "./GltfDracoLoader.js"; +import GltfDracoVertexBufferLoader from "./GltfDracoVertexBufferLoader.js"; import GltfImageLoader from "./GltfImageLoader.js"; import GltfIndexBufferLoader from "./GltfIndexBufferLoader.js"; import GltfJsonLoader from "./GltfJsonLoader.js"; import GltfTextureLoader from "./GltfTextureLoader.js"; -import GltfUtil from "./GltfUtil.js"; import GltfVertexBufferLoader from "./GltfVertexBufferLoader.js"; import MetadataSchemaLoader from "./MetadataSchemaLoader.js"; import ResourceCacheKey from "./ResourceCacheKey.js"; @@ -396,10 +397,8 @@ ResourceCache.getDracoLoader = function (options) { * @param {Resource} options.gltfResource The {@link Resource} containing the glTF. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to. * @param {FrameState} options.frameState The frame state. - * @param {number} [options.bufferViewId] The bufferView ID corresponding to the vertex buffer. * @param {object} [options.draco] The Draco extension object. * @param {string} [options.attributeSemantic] The attribute semantic, e.g. POSITION or NORMAL. - * @param {number} [options.accessorId] The accessor ID. * @param {boolean} [options.asynchronous=true] Determines if WebGL resource creation will be spread out over several frames or block until all WebGL resources are created. * @param {boolean} [options.dequantize=false] Determines whether or not the vertex buffer will be dequantized on the CPU. * @param {boolean} [options.loadBuffer=false] Load vertex buffer as a GPU vertex buffer. @@ -411,17 +410,15 @@ ResourceCache.getDracoLoader = function (options) { * @returns {GltfVertexBufferLoader} The cached vertex buffer loader. * @private */ -ResourceCache.getVertexBufferLoader = function (options) { +ResourceCache.getDracoVertexBufferLoader = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); const { gltf, gltfResource, baseResource, frameState, - bufferViewId, draco, attributeSemantic, - accessorId, asynchronous = true, dequantize = false, loadBuffer = false, @@ -439,44 +436,93 @@ ResourceCache.getVertexBufferLoader = function (options) { ); } - const hasBufferViewId = defined(bufferViewId); - const hasDraco = GltfUtil.hasDracoCompression(draco, attributeSemantic); - const hasAttributeSemantic = defined(attributeSemantic); - const hasAccessorId = defined(accessorId); + Check.typeOf.object("options.draco", draco); + Check.typeOf.string("options.attributeSemantic", attributeSemantic); + //>>includeEnd('debug'); - if (hasBufferViewId === hasDraco) { - throw new DeveloperError( - "One of options.bufferViewId and options.draco must be defined.", - ); - } + const cacheKey = ResourceCacheKey.getDracoVertexBufferCacheKey({ + gltf: gltf, + gltfResource: gltfResource, + baseResource: baseResource, + frameState: frameState, + draco: draco, + attributeSemantic: attributeSemantic, + dequantize: dequantize, + loadBuffer: loadBuffer, + loadTypedArray: loadTypedArray, + }); - if (hasDraco && !hasAttributeSemantic) { - throw new DeveloperError( - "When options.draco is defined options.attributeSemantic must also be defined.", - ); + let vertexBufferLoader = ResourceCache.get(cacheKey); + if (defined(vertexBufferLoader)) { + return vertexBufferLoader; } - if (hasDraco && !hasAccessorId) { + vertexBufferLoader = new GltfDracoVertexBufferLoader({ + resourceCache: ResourceCache, + gltf: gltf, + gltfResource: gltfResource, + baseResource: baseResource, + draco: draco, + attributeSemantic: attributeSemantic, + cacheKey: cacheKey, + asynchronous: asynchronous, + dequantize: dequantize, + loadBuffer: loadBuffer, + loadTypedArray: loadTypedArray, + }); + + return ResourceCache.add(vertexBufferLoader); +}; + +/** + * Gets an existing glTF vertex buffer from the cache, or creates a new loader if one does not already exist. + * + * @param {object} options Object with the following properties: + * @param {object} options.gltf The glTF JSON. + * @param {Resource} options.gltfResource The {@link Resource} containing the glTF. + * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to. + * @param {FrameState} options.frameState The frame state. + * @param {number} [options.bufferViewId] The bufferView ID corresponding to the vertex buffer. + * @param {boolean} [options.asynchronous=true] Determines if WebGL resource creation will be spread out over several frames or block until all WebGL resources are created. + * @param {boolean} [options.dequantize=false] Determines whether or not the vertex buffer will be dequantized on the CPU. + * @param {boolean} [options.loadBuffer=false] Load vertex buffer as a GPU vertex buffer. + * @param {boolean} [options.loadTypedArray=false] Load vertex buffer as a typed array. + * + * @returns {GltfDefaultVertexBufferLoader} The cached vertex buffer loader. + * @private + */ +ResourceCache.getDefaultVertexBufferLoader = function (options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + const { + gltf, + gltfResource, + baseResource, + frameState, + bufferViewId, + asynchronous = true, + dequantize = false, + loadBuffer = false, + loadTypedArray = false, + } = 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.frameState", frameState); + if (!loadBuffer && !loadTypedArray) { throw new DeveloperError( - "When options.draco is defined options.haAccessorId must also be defined.", + "At least one of loadBuffer and loadTypedArray must be true.", ); } - - if (hasDraco) { - Check.typeOf.object("options.draco", draco); - Check.typeOf.string("options.attributeSemantic", attributeSemantic); - Check.typeOf.number("options.accessorId", accessorId); - } //>>includeEnd('debug'); - const cacheKey = ResourceCacheKey.getVertexBufferCacheKey({ + const cacheKey = ResourceCacheKey.getDefaultVertexBufferCacheKey({ gltf: gltf, gltfResource: gltfResource, baseResource: baseResource, frameState: frameState, bufferViewId: bufferViewId, - draco: draco, - attributeSemantic: attributeSemantic, dequantize: dequantize, loadBuffer: loadBuffer, loadTypedArray: loadTypedArray, @@ -487,15 +533,12 @@ ResourceCache.getVertexBufferLoader = function (options) { return vertexBufferLoader; } - vertexBufferLoader = new GltfVertexBufferLoader({ + vertexBufferLoader = new GltfDefaultVertexBufferLoader({ resourceCache: ResourceCache, gltf: gltf, gltfResource: gltfResource, baseResource: baseResource, bufferViewId: bufferViewId, - draco: draco, - attributeSemantic: attributeSemantic, - accessorId: accessorId, cacheKey: cacheKey, asynchronous: asynchronous, dequantize: dequantize, diff --git a/packages/engine/Source/Scene/ResourceCacheKey.js b/packages/engine/Source/Scene/ResourceCacheKey.js index 94931f827f5..ac6f14fd9f4 100644 --- a/packages/engine/Source/Scene/ResourceCacheKey.js +++ b/packages/engine/Source/Scene/ResourceCacheKey.js @@ -4,7 +4,6 @@ import defined from "../Core/defined.js"; import DeveloperError from "../Core/DeveloperError.js"; import getAbsoluteUri from "../Core/getAbsoluteUri.js"; import GltfLoaderUtil from "./GltfLoaderUtil.js"; -import GltfUtil from "./GltfUtil.js"; import hasExtension from "./hasExtension.js"; /** @@ -282,8 +281,6 @@ ResourceCacheKey.getDracoCacheKey = function (options) { * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to. * @param {FrameState} options.frameState The frame state. * @param {number} [options.bufferViewId] The bufferView ID corresponding to the vertex buffer. - * @param {object} [options.draco] The Draco extension object. - * @param {string} [options.attributeSemantic] The attribute semantic, e.g. POSITION or NORMAL. * @param {boolean} [options.dequantize=false] Determines whether or not the vertex buffer will be dequantized on the CPU. * @param {boolean} [options.loadBuffer=false] Load vertex buffer as a GPU vertex buffer. * @param {boolean} [options.loadTypedArray=false] Load vertex buffer as a typed array. @@ -293,7 +290,7 @@ ResourceCacheKey.getDracoCacheKey = function (options) { * @returns {string} The vertex buffer cache key. * @private */ -ResourceCacheKey.getVertexBufferCacheKey = function (options) { +ResourceCacheKey.getDefaultVertexBufferCacheKey = function (options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); const { gltf, @@ -301,8 +298,6 @@ ResourceCacheKey.getVertexBufferCacheKey = function (options) { baseResource, frameState, bufferViewId, - draco, - attributeSemantic, dequantize = false, loadBuffer = false, loadTypedArray = false, @@ -314,27 +309,6 @@ ResourceCacheKey.getVertexBufferCacheKey = function (options) { Check.typeOf.object("options.baseResource", baseResource); Check.typeOf.object("options.frameState", frameState); - const hasBufferViewId = defined(bufferViewId); - const hasDraco = GltfUtil.hasDracoCompression(draco, attributeSemantic); - const hasAttributeSemantic = defined(attributeSemantic); - - if (hasBufferViewId === hasDraco) { - throw new DeveloperError( - "One of options.bufferViewId and options.draco must be defined.", - ); - } - - if (hasDraco && !hasAttributeSemantic) { - throw new DeveloperError( - "When options.draco is defined options.attributeSemantic must also be defined.", - ); - } - - if (hasDraco) { - Check.typeOf.object("options.draco", draco); - Check.typeOf.string("options.attributeSemantic", attributeSemantic); - } - if (!loadBuffer && !loadTypedArray) { throw new DeveloperError( "At least one of loadBuffer and loadTypedArray must be true.", @@ -356,16 +330,6 @@ ResourceCacheKey.getVertexBufferCacheKey = function (options) { cacheKeySuffix += "-typed-array"; } - if (defined(draco)) { - const dracoCacheKey = getDracoCacheKey( - gltf, - draco, - gltfResource, - baseResource, - ); - return `vertex-buffer:${dracoCacheKey}-draco-${attributeSemantic}${cacheKeySuffix}`; - } - const bufferView = gltf.bufferViews[bufferViewId]; const bufferId = bufferView.buffer; const buffer = gltf.buffers[bufferId]; @@ -382,6 +346,77 @@ ResourceCacheKey.getVertexBufferCacheKey = function (options) { return `vertex-buffer:${bufferCacheKey}-range-${bufferViewCacheKey}${cacheKeySuffix}`; }; +/** + * Gets the draco vertex buffer cache key. + * + * @param {object} options Object with the following properties: + * @param {object} options.gltf The glTF JSON. + * @param {Resource} options.gltfResource The {@link Resource} containing the glTF. + * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to. + * @param {FrameState} options.frameState The frame state. + * @param {object} [options.draco] The Draco extension object. + * @param {string} [options.attributeSemantic] The attribute semantic, e.g. POSITION or NORMAL. + * @param {boolean} [options.dequantize=false] Determines whether or not the vertex buffer will be dequantized on the CPU. + * @param {boolean} [options.loadBuffer=false] Load vertex buffer as a GPU vertex buffer. + * @param {boolean} [options.loadTypedArray=false] Load vertex buffer as a typed array. + * @exception {DeveloperError} One of options.bufferViewId and options.draco must be defined. + * @exception {DeveloperError} When options.draco is defined options.attributeSemantic must also be defined. + * + * @returns {string} The vertex buffer cache key. + * @private + */ +ResourceCacheKey.getDracoVertexBufferCacheKey = function (options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + const { + gltf, + gltfResource, + baseResource, + frameState, + draco, + attributeSemantic, + dequantize = false, + loadBuffer = false, + loadTypedArray = false, + } = 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.frameState", frameState); + Check.typeOf.object("options.draco", draco); + Check.typeOf.string("options.attributeSemantic", attributeSemantic); + + if (!loadBuffer && !loadTypedArray) { + throw new DeveloperError( + "At least one of loadBuffer and loadTypedArray must be true.", + ); + } + //>>includeEnd('debug'); + + let cacheKeySuffix = ""; + if (dequantize) { + cacheKeySuffix += "-dequantize"; + } + + if (loadBuffer) { + cacheKeySuffix += "-buffer"; + cacheKeySuffix += `-context-${frameState.context.id}`; + } + + if (loadTypedArray) { + cacheKeySuffix += "-typed-array"; + } + + const dracoCacheKey = getDracoCacheKey( + gltf, + draco, + gltfResource, + baseResource, + ); + return `vertex-buffer:${dracoCacheKey}-draco-${attributeSemantic}${cacheKeySuffix}`; +}; + /** * Gets the index buffer cache key. *