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