| 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 modelsDracoLoader._maxDecodingConcurrency = Math.max(  FeatureDetection.hardwareConcurrency - 1,  1);// Exposed for testing purposesDracoLoader._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;
 |