123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342 |
- import arraySlice from "../Core/arraySlice.js";
- import ComponentDatatype from "../Core/ComponentDatatype.js";
- import defined from "../Core/defined.js";
- import FeatureDetection from "../Core/FeatureDetection.js";
- import TaskProcessor from "../Core/TaskProcessor.js";
- import ForEach from "./GltfPipeline/ForEach.js";
- /**
- * @private
- */
- function DracoLoader() {}
- // 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._getDecoderTaskProcessor = function () {
- if (!defined(DracoLoader._decoderTaskProcessor)) {
- const processor = new TaskProcessor(
- "decodeDraco",
- DracoLoader._maxDecodingConcurrency
- );
- processor
- .initWebAssemblyModule({
- modulePath: "ThirdParty/Workers/draco_decoder_nodejs.js",
- wasmBinaryFile: "ThirdParty/draco_decoder.wasm",
- })
- .then(function () {
- DracoLoader._taskProcessorReady = true;
- });
- DracoLoader._decoderTaskProcessor = processor;
- }
- return DracoLoader._decoderTaskProcessor;
- };
- /**
- * Returns true if the model uses or requires KHR_draco_mesh_compression.
- *
- * @private
- */
- DracoLoader.hasExtension = function (model) {
- return (
- defined(model.extensionsRequired.KHR_draco_mesh_compression) ||
- defined(model.extensionsUsed.KHR_draco_mesh_compression)
- );
- };
- function addBufferToLoadResources(loadResources, typedArray) {
- // Create a new id to differentiate from original glTF bufferViews
- const bufferViewId = `runtime.${
- Object.keys(loadResources.createdBufferViews).length
- }`;
- const loadResourceBuffers = loadResources.buffers;
- const id = Object.keys(loadResourceBuffers).length;
- loadResourceBuffers[id] = typedArray;
- loadResources.createdBufferViews[bufferViewId] = {
- buffer: id,
- byteOffset: 0,
- byteLength: typedArray.byteLength,
- };
- return bufferViewId;
- }
- function addNewVertexBuffer(typedArray, model, context) {
- const loadResources = model._loadResources;
- const id = addBufferToLoadResources(loadResources, typedArray);
- loadResources.vertexBuffersToCreate.enqueue(id);
- return id;
- }
- function addNewIndexBuffer(indexArray, model, context) {
- const typedArray = indexArray.typedArray;
- const loadResources = model._loadResources;
- const id = addBufferToLoadResources(loadResources, typedArray);
- loadResources.indexBuffersToCreate.enqueue({
- id: id,
- componentType: ComponentDatatype.fromTypedArray(typedArray),
- });
- return {
- bufferViewId: id,
- numberOfIndices: indexArray.numberOfIndices,
- };
- }
- function scheduleDecodingTask(
- decoderTaskProcessor,
- model,
- loadResources,
- context
- ) {
- if (!DracoLoader._taskProcessorReady) {
- // The task processor is not ready to schedule tasks
- return;
- }
- const taskData = loadResources.primitivesToDecode.peek();
- if (!defined(taskData)) {
- // All primitives are processing
- return;
- }
- const promise = decoderTaskProcessor.scheduleTask(taskData, [
- taskData.array.buffer,
- ]);
- if (!defined(promise)) {
- // Cannot schedule another task this frame
- return;
- }
- loadResources.activeDecodingTasks++;
- loadResources.primitivesToDecode.dequeue();
- return promise.then(function (result) {
- loadResources.activeDecodingTasks--;
- const decodedIndexBuffer = addNewIndexBuffer(
- result.indexArray,
- model,
- context
- );
- const attributes = {};
- const decodedAttributeData = result.attributeData;
- for (const attributeName in decodedAttributeData) {
- if (decodedAttributeData.hasOwnProperty(attributeName)) {
- const attribute = decodedAttributeData[attributeName];
- const vertexArray = attribute.array;
- const vertexBufferView = addNewVertexBuffer(
- vertexArray,
- model,
- context
- );
- const data = attribute.data;
- data.bufferView = vertexBufferView;
- attributes[attributeName] = data;
- }
- }
- model._decodedData[`${taskData.mesh}.primitive.${taskData.primitive}`] = {
- bufferView: decodedIndexBuffer.bufferViewId,
- numberOfIndices: decodedIndexBuffer.numberOfIndices,
- attributes: attributes,
- };
- });
- }
- DracoLoader._decodedModelResourceCache = undefined;
- /**
- * Parses draco extension on model primitives and
- * adds the decoding data to the model's load resources.
- *
- * @private
- */
- DracoLoader.parse = function (model, context) {
- if (!DracoLoader.hasExtension(model)) {
- return;
- }
- const loadResources = model._loadResources;
- const cacheKey = model.cacheKey;
- if (defined(cacheKey)) {
- if (!defined(DracoLoader._decodedModelResourceCache)) {
- if (!defined(context.cache.modelDecodingCache)) {
- context.cache.modelDecodingCache = {};
- }
- DracoLoader._decodedModelResourceCache = context.cache.modelDecodingCache;
- }
- // Decoded data for model will be loaded from cache
- const cachedData = DracoLoader._decodedModelResourceCache[cacheKey];
- if (defined(cachedData)) {
- cachedData.count++;
- loadResources.pendingDecodingCache = true;
- return;
- }
- }
- const dequantizeInShader = model._dequantizeInShader;
- const gltf = model.gltf;
- ForEach.mesh(gltf, function (mesh, meshId) {
- ForEach.meshPrimitive(mesh, function (primitive, primitiveId) {
- if (!defined(primitive.extensions)) {
- return;
- }
- const compressionData = primitive.extensions.KHR_draco_mesh_compression;
- if (!defined(compressionData)) {
- return;
- }
- const bufferView = gltf.bufferViews[compressionData.bufferView];
- const typedArray = arraySlice(
- gltf.buffers[bufferView.buffer].extras._pipeline.source,
- bufferView.byteOffset,
- bufferView.byteOffset + bufferView.byteLength
- );
- loadResources.primitivesToDecode.enqueue({
- mesh: meshId,
- primitive: primitiveId,
- array: typedArray,
- bufferView: bufferView,
- compressedAttributes: compressionData.attributes,
- dequantizeInShader: dequantizeInShader,
- });
- });
- });
- };
- /**
- * Schedules decoding tasks available this frame.
- * @private
- */
- DracoLoader.decodeModel = function (model, context) {
- if (!DracoLoader.hasExtension(model)) {
- return Promise.resolve();
- }
- const loadResources = model._loadResources;
- const cacheKey = model.cacheKey;
- if (defined(cacheKey) && defined(DracoLoader._decodedModelResourceCache)) {
- const cachedData = DracoLoader._decodedModelResourceCache[cacheKey];
- // Load decoded data for model when cache is ready
- if (defined(cachedData) && loadResources.pendingDecodingCache) {
- return Promise.resolve(cachedData.ready).then(function () {
- model._decodedData = cachedData.data;
- loadResources.pendingDecodingCache = false;
- });
- }
- // Decoded data for model should be cached when ready
- DracoLoader._decodedModelResourceCache[cacheKey] = {
- ready: false,
- count: 1,
- data: undefined,
- };
- }
- if (loadResources.primitivesToDecode.length === 0) {
- // No more tasks to schedule
- return Promise.resolve();
- }
- const decoderTaskProcessor = DracoLoader._getDecoderTaskProcessor();
- const decodingPromises = [];
- let promise = scheduleDecodingTask(
- decoderTaskProcessor,
- model,
- loadResources,
- context
- );
- while (defined(promise)) {
- decodingPromises.push(promise);
- promise = scheduleDecodingTask(
- decoderTaskProcessor,
- model,
- loadResources,
- context
- );
- }
- return Promise.all(decodingPromises);
- };
- /**
- * Decodes a compressed point cloud. Returns undefined if the task cannot be scheduled.
- * @private
- */
- DracoLoader.decodePointCloud = function (parameters) {
- const decoderTaskProcessor = DracoLoader._getDecoderTaskProcessor();
- 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.<String, Number>} 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
- */
- DracoLoader.decodeBufferView = function (options) {
- const decoderTaskProcessor = DracoLoader._getDecoderTaskProcessor();
- if (!DracoLoader._taskProcessorReady) {
- // The task processor is not ready to schedule tasks
- return;
- }
- return decoderTaskProcessor.scheduleTask(options, [options.array.buffer]);
- };
- /**
- * Caches a models decoded data so it doesn't need to decode more than once.
- * @private
- */
- DracoLoader.cacheDataForModel = function (model) {
- const cacheKey = model.cacheKey;
- if (defined(cacheKey) && defined(DracoLoader._decodedModelResourceCache)) {
- const cachedData = DracoLoader._decodedModelResourceCache[cacheKey];
- if (defined(cachedData)) {
- cachedData.ready = true;
- cachedData.data = model._decodedData;
- }
- }
- };
- /**
- * Destroys the cached data that this model references if it is no longer in use.
- * @private
- */
- DracoLoader.destroyCachedDataForModel = function (model) {
- const cacheKey = model.cacheKey;
- if (defined(cacheKey) && defined(DracoLoader._decodedModelResourceCache)) {
- const cachedData = DracoLoader._decodedModelResourceCache[cacheKey];
- if (defined(cachedData) && --cachedData.count === 0) {
- delete DracoLoader._decodedModelResourceCache[cacheKey];
- }
- }
- };
- export default DracoLoader;
|