text: /**
   * @author Don McCurdy / https://www.donmccurdy.com
   */
  
  import {
      BufferAttribute,
      BufferGeometry,
      FileLoader,
      Loader
  } from "../../../build/v3d.module.js";
  
  var DRACOLoader = function(manager) {
  
      Loader.call(this, manager);
  
      this.decoderPath = '';
      this.decoderConfig = {};
      this.decoderBinary = null;
      this.decoderPending = null;
  
      this.workerLimit = 4;
      this.workerPool = [];
      this.workerNextTaskID = 1;
      this.workerSourceURL = '';
  
      this.defaultAttributeIDs = {
          position: 'POSITION',
          normal: 'NORMAL',
          color: 'COLOR',
          uv: 'TEX_COORD'
      };
      this.defaultAttributeTypes = {
          position: 'Float32Array',
          normal: 'Float32Array',
          color: 'Float32Array',
          uv: 'Float32Array'
      };
  
  };
  
  DRACOLoader.prototype = Object.assign(Object.create(Loader.prototype), {
  
      constructor: DRACOLoader,
  
      setDecoderPath: function(path) {
  
          this.decoderPath = path;
  
          return this;
  
      },
  
      setDecoderConfig: function(config) {
  
          this.decoderConfig = config;
  
          return this;
  
      },
  
      setWorkerLimit: function(workerLimit) {
  
          this.workerLimit = workerLimit;
  
          return this;
  
      },
  
      /** @deprecated */
      setVerbosity: function() {
  
          console.warn('v3d.DRACOLoader: The .setVerbosity() method has been removed.');
  
      },
  
      /** @deprecated */
      setDrawMode: function() {
  
          console.warn('v3d.DRACOLoader: The .setDrawMode() method has been removed.');
  
      },
  
      /** @deprecated */
      setSkipDequantization: function() {
  
          console.warn('v3d.DRACOLoader: The .setSkipDequantization() method has been removed.');
  
      },
  
      load: function(url, onLoad, onProgress, onError) {
  
          var loader = new FileLoader(this.manager);
  
          loader.setPath(this.path);
          loader.setResponseType('arraybuffer');
  
          if (this.crossOrigin === 'use-credentials') {
  
              loader.setWithCredentials(true);
  
          }
  
          loader.load(url, (buffer) => {
  
              var taskConfig = {
                  attributeIDs: this.defaultAttributeIDs,
                  attributeTypes: this.defaultAttributeTypes,
                  useUniqueIDs: false
              };
  
              this.decodeGeometry(buffer, taskConfig)
                  .then(onLoad)
                  .catch(onError);
  
          }, onProgress, onError);
  
      },
  
      /** @deprecated Kept for backward-compatibility with previous DRACOLoader versions. */
      decodeDracoFile: function(buffer, callback, attributeIDs, attributeTypes) {
  
          var taskConfig = {
              attributeIDs: attributeIDs || this.defaultAttributeIDs,
              attributeTypes: attributeTypes || this.defaultAttributeTypes,
              useUniqueIDs: !! attributeIDs
          };
  
          this.decodeGeometry(buffer, taskConfig).then(callback);
  
      },
  
      decodeGeometry: function(buffer, taskConfig) {
  
          // TODO: For backward-compatibility, support 'attributeTypes' objects containing
          // references (rather than names) to typed array constructors. These must be
          // serialized before sending them to the worker.
          for (var attribute in taskConfig.attributeTypes) {
  
              var type = taskConfig.attributeTypes[attribute];
  
              if (type.BYTES_PER_ELEMENT !== undefined) {
  
                  taskConfig.attributeTypes[attribute] = type.name;
  
              }
  
          }
  
          //
  
          var taskKey = JSON.stringify(taskConfig);
  
          // Check for an existing task using this buffer. A transferred buffer cannot be transferred
          // again from this thread.
          if (DRACOLoader.taskCache.has(buffer)) {
  
              var cachedTask = DRACOLoader.taskCache.get(buffer);
  
              if (cachedTask.key === taskKey) {
  
                  return cachedTask.promise;
  
              } else if (buffer.byteLength === 0) {
  
                  // Technically, it would be possible to wait for the previous task to complete,
                  // transfer the buffer back, and decode again with the second configuration. That
                  // is complex, and I don't know of any reason to decode a Draco buffer twice in
                  // different ways, so this is left unimplemented.
                  throw new Error(
  
                      'v3d.DRACOLoader: Unable to re-decode a buffer with different ' +
                      'settings. Buffer has already been transferred.'
  
                  );
  
              }
  
          }
  
          //
  
          var worker;
          var taskID = this.workerNextTaskID ++;
          var taskCost = buffer.byteLength;
  
          // Obtain a worker and assign a task, and construct a geometry instance
          // when the task completes.
          var geometryPending = this._getWorker(taskID, taskCost)
              .then((_worker) => {
  
                  worker = _worker;
  
                  return new Promise((resolve, reject) => {
  
                      worker._callbacks[taskID] = { resolve, reject };
  
                      worker.postMessage({ type: 'decode', id: taskID, taskConfig, buffer }, [buffer]);
  
                      // this.debug();
  
                  });
  
              })
              .then((message) => this._createGeometry(message.geometry));
  
          // Remove task from the task list.
          geometryPending
              .finally(() => {
  
                  if (worker && taskID) {
  
                      this._releaseTask(worker, taskID);
  
                      // this.debug();
  
                  }
  
              });
  
          // Cache the task result.
          DRACOLoader.taskCache.set(buffer, {
  
              key: taskKey,
              promise: geometryPending
  
          });
  
          return geometryPending;
  
      },
  
      _createGeometry: function(geometryData) {
  
          var geometry = new BufferGeometry();
  
          if (geometryData.index) {
  
              geometry.setIndex(new BufferAttribute(geometryData.index.array, 1));
  
          }
  
          for (var i = 0; i < geometryData.attributes.length; i++) {
  
              var attribute = geometryData.attributes[i];
              var name = attribute.name;
              var array = attribute.array;
              var itemSize = attribute.itemSize;
  
              geometry.setAttribute(name, new BufferAttribute(array, itemSize));
  
          }
  
          return geometry;
  
      },
  
      _loadLibrary: function(url, responseType) {
  
          var loader = new FileLoader(this.manager);
          loader.setPath(this.decoderPath);
          loader.setResponseType(responseType);
  
          return new Promise((resolve, reject) => {
  
              loader.load(url, resolve, undefined, reject);
  
          });
  
      },
  
      preload: function() {
  
          this._initDecoder();
  
          return this;
  
      },
  
      _initDecoder: function() {
  
          if (this.decoderPending) return this.decoderPending;
  
          var useJS = typeof WebAssembly !== 'object' || this.decoderConfig.type === 'js';
          var librariesPending = [];
  
          if (useJS) {
  
              librariesPending.push(this._loadLibrary('draco_decoder.js', 'text'));
  
          } else {
  
              librariesPending.push(this._loadLibrary('draco_wasm_wrapper.js', 'text'));
              librariesPending.push(this._loadLibrary('draco_decoder.wasm', 'arraybuffer'));
  
          }
  
          this.decoderPending = Promise.all(librariesPending)
              .then((libraries) => {
  
                  var jsContent = libraries[0];
  
                  if (!useJS) {
  
                      this.decoderConfig.wasmBinary = libraries[1];
  
                  }
  
                  var fn = DRACOLoader.DRACOWorker.toString();
  
                  var body = [
                      '/* draco decoder */',
                      jsContent,
                      '',
                      '/* worker */',
                      fn.substring(fn.indexOf('{') + 1, fn.lastIndexOf('}'))
                  ].join('\n');
  
                  this.workerSourceURL = URL.createObjectURL(new Blob([body]));
  
              });
  
          return this.decoderPending;
  
      },
  
      _getWorker: function(taskID, taskCost) {
  
          return this._initDecoder().then(() => {
  
              if (this.workerPool.length < this.workerLimit) {
  
                  var worker = new Worker(this.workerSourceURL);
  
                  worker._callbacks = {};
                  worker._taskCosts = {};
                  worker._taskLoad = 0;
  
                  worker.postMessage({ type: 'init', decoderConfig: this.decoderConfig });
  
                  worker.onmessage = function(e) {
  
                      var message = e.data;
  
                      switch (message.type) {
  
                          case 'decode':
                              worker._callbacks[message.id].resolve(message);
                              break;
  
                          case 'error':
                              worker._callbacks[message.id].reject(message);
                              break;
  
                          default:
                              console.error('v3d.DRACOLoader: Unexpected message, "' + message.type + '"');
  
                      }
  
                  };
  
                  this.workerPool.push(worker);
  
              } else {
  
                  this.workerPool.sort(function(a, b) {
  
                      return a._taskLoad > b._taskLoad ? - 1 : 1;
  
                  });
  
              }
  
              var worker = this.workerPool[this.workerPool.length - 1];
              worker._taskCosts[taskID] = taskCost;
              worker._taskLoad += taskCost;
              return worker;
  
          });
  
      },
  
      _releaseTask: function(worker, taskID) {
  
          worker._taskLoad -= worker._taskCosts[taskID];
          delete worker._callbacks[taskID];
          delete worker._taskCosts[taskID];
  
      },
  
      debug: function() {
  
          console.log('Task load: ', this.workerPool.map((worker) => worker._taskLoad));
  
      },
  
      dispose: function() {
  
          for (var i = 0; i < this.workerPool.length; ++ i) {
  
              this.workerPool[i].terminate();
  
          }
  
          this.workerPool.length = 0;
  
          return this;
  
      }
  
  });
  
  /* WEB WORKER */
  
  DRACOLoader.DRACOWorker = function() {
  
      var decoderConfig;
      var decoderPending;
  
      onmessage = function(e) {
  
          var message = e.data;
  
          switch (message.type) {
  
              case 'init':
                  decoderConfig = message.decoderConfig;
                  decoderPending = new Promise(function(resolve/*, reject*/) {
  
                      decoderConfig.onModuleLoaded = function(draco) {
  
                          // Module is Promise-like. Wrap before resolving to avoid loop.
                          resolve({ draco: draco });
  
                      };
  
                      DracoDecoderModule(decoderConfig);
  
                  });
                  break;
  
              case 'decode':
                  var buffer = message.buffer;
                  var taskConfig = message.taskConfig;
                  decoderPending.then((module) => {
  
                      var draco = module.draco;
                      var decoder = new draco.Decoder();
                      var decoderBuffer = new draco.DecoderBuffer();
                      decoderBuffer.Init(new Int8Array(buffer), buffer.byteLength);
  
                      try {
  
                          var geometry = decodeGeometry(draco, decoder, decoderBuffer, taskConfig);
  
                          var buffers = geometry.attributes.map((attr) => attr.array.buffer);
  
                          if (geometry.index) buffers.push(geometry.index.array.buffer);
  
                          self.postMessage({ type: 'decode', id: message.id, geometry }, buffers);
  
                      } catch (error) {
  
                          console.error(error);
  
                          self.postMessage({ type: 'error', id: message.id, error: error.message });
  
                      } finally {
  
                          draco.destroy(decoderBuffer);
                          draco.destroy(decoder);
  
                      }
  
                  });
                  break;
  
          }
  
      };
  
      function decodeGeometry(draco, decoder, decoderBuffer, taskConfig) {
  
          var attributeIDs = taskConfig.attributeIDs;
          var attributeTypes = taskConfig.attributeTypes;
  
          var dracoGeometry;
          var decodingStatus;
  
          var geometryType = decoder.GetEncodedGeometryType(decoderBuffer);
  
          if (geometryType === draco.TRIANGULAR_MESH) {
  
              dracoGeometry = new draco.Mesh();
              decodingStatus = decoder.DecodeBufferToMesh(decoderBuffer, dracoGeometry);
  
          } else if (geometryType === draco.POINT_CLOUD) {
  
              dracoGeometry = new draco.PointCloud();
              decodingStatus = decoder.DecodeBufferToPointCloud(decoderBuffer, dracoGeometry);
  
          } else {
  
              throw new Error('v3d.DRACOLoader: Unexpected geometry type.');
  
          }
  
          if (!decodingStatus.ok() || dracoGeometry.ptr === 0) {
  
              throw new Error('v3d.DRACOLoader: Decoding failed: ' + decodingStatus.error_msg());
  
          }
  
          var geometry = { index: null, attributes: [] };
  
          // Gather all vertex attributes.
          for (var attributeName in attributeIDs) {
  
              var attributeType = self[attributeTypes[attributeName]];
  
              var attribute;
              var attributeID;
  
              // A Draco file may be created with default vertex attributes, whose attribute IDs
              // are mapped 1:1 from their semantic name (POSITION, NORMAL, ...). Alternatively,
              // a Draco file may contain a custom set of attributes, identified by known unique
              // IDs. glTF files always do the latter, and `.drc` files typically do the former.
              if (taskConfig.useUniqueIDs) {
  
                  attributeID = attributeIDs[attributeName];
                  attribute = decoder.GetAttributeByUniqueId(dracoGeometry, attributeID);
  
              } else {
  
                  attributeID = decoder.GetAttributeId(dracoGeometry, draco[attributeIDs[attributeName]]);
  
                  if (attributeID === - 1) continue;
  
                  attribute = decoder.GetAttribute(dracoGeometry, attributeID);
  
              }
  
              geometry.attributes.push(decodeAttribute(draco, decoder, dracoGeometry, attributeName, attributeType, attribute));
  
          }
  
          // Add index.
          if (geometryType === draco.TRIANGULAR_MESH) {
  
              // Generate mesh faces.
              var numFaces = dracoGeometry.num_faces();
              var numIndices = numFaces * 3;
              var index = new Uint32Array(numIndices);
              var indexArray = new draco.DracoInt32Array();
  
              for (var i = 0; i < numFaces; ++ i) {
  
                  decoder.GetFaceFromMesh(dracoGeometry, i, indexArray);
  
                  for (var j = 0; j < 3; ++ j) {
  
                      index[i * 3 + j] = indexArray.GetValue(j);
  
                  }
  
              }
  
              geometry.index = { array: index, itemSize: 1 };
  
              draco.destroy(indexArray);
  
          }
  
          draco.destroy(dracoGeometry);
  
          return geometry;
  
      }
  
      function decodeAttribute(draco, decoder, dracoGeometry, attributeName, attributeType, attribute) {
  
          var numComponents = attribute.num_components();
          var numPoints = dracoGeometry.num_points();
          var numValues = numPoints * numComponents;
          var dracoArray;
  
          var array;
  
          switch (attributeType) {
  
              case Float32Array:
                  dracoArray = new draco.DracoFloat32Array();
                  decoder.GetAttributeFloatForAllPoints(dracoGeometry, attribute, dracoArray);
                  array = new Float32Array(numValues);
                  break;
  
              case Int8Array:
                  dracoArray = new draco.DracoInt8Array();
                  decoder.GetAttributeInt8ForAllPoints(dracoGeometry, attribute, dracoArray);
                  array = new Int8Array(numValues);
                  break;
  
              case Int16Array:
                  dracoArray = new draco.DracoInt16Array();
                  decoder.GetAttributeInt16ForAllPoints(dracoGeometry, attribute, dracoArray);
                  array = new Int16Array(numValues);
                  break;
  
              case Int32Array:
                  dracoArray = new draco.DracoInt32Array();
                  decoder.GetAttributeInt32ForAllPoints(dracoGeometry, attribute, dracoArray);
                  array = new Int32Array(numValues);
                  break;
  
              case Uint8Array:
                  dracoArray = new draco.DracoUInt8Array();
                  decoder.GetAttributeUInt8ForAllPoints(dracoGeometry, attribute, dracoArray);
                  array = new Uint8Array(numValues);
                  break;
  
              case Uint16Array:
                  dracoArray = new draco.DracoUInt16Array();
                  decoder.GetAttributeUInt16ForAllPoints(dracoGeometry, attribute, dracoArray);
                  array = new Uint16Array(numValues);
                  break;
  
              case Uint32Array:
                  dracoArray = new draco.DracoUInt32Array();
                  decoder.GetAttributeUInt32ForAllPoints(dracoGeometry, attribute, dracoArray);
                  array = new Uint32Array(numValues);
                  break;
  
              default:
                  throw new Error('v3d.DRACOLoader: Unexpected attribute type.');
  
          }
  
          for (var i = 0; i < numValues; i++) {
  
              array[i] = dracoArray.GetValue(i);
  
          }
  
          draco.destroy(dracoArray);
  
          return {
              name: attributeName,
              array: array,
              itemSize: numComponents
          };
  
      }
  
  };
  
  DRACOLoader.taskCache = new WeakMap();
  
  /** Deprecated static methods */
  
  /** @deprecated */
  DRACOLoader.setDecoderPath = function() {
  
      console.warn('v3d.DRACOLoader: The .setDecoderPath() method has been removed. Use instance methods.');
  
  };
  
  /** @deprecated */
  DRACOLoader.setDecoderConfig = function() {
  
      console.warn('v3d.DRACOLoader: The .setDecoderConfig() method has been removed. Use instance methods.');
  
  };
  
  /** @deprecated */
  DRACOLoader.releaseDecoderModule = function() {
  
      console.warn('v3d.DRACOLoader: The .releaseDecoderModule() method has been removed. Use instance methods.');
  
  };
  
  /** @deprecated */
  DRACOLoader.getDecoderModule = function() {
  
      console.warn('v3d.DRACOLoader: The .getDecoderModule() method has been removed. Use instance methods.');
  
  };
  
  export { DRACOLoader };
  