"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.DRACOLoader = void 0;
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
var _three = require("three");
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function () { var Super = (0, _getPrototypeOf2["default"])(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2["default"])(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2["default"])(this, result); }; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
var _taskCache = new WeakMap();
var DRACOLoader = /*#__PURE__*/function (_Loader) {
  (0, _inherits2["default"])(DRACOLoader, _Loader);
  var _super = _createSuper(DRACOLoader);
  function DRACOLoader(manager) {
    var _this;
    (0, _classCallCheck2["default"])(this, DRACOLoader);
    _this = _super.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'
    };
    return _this;
  }
  (0, _createClass2["default"])(DRACOLoader, [{
    key: "setDecoderPath",
    value: function setDecoderPath(path) {
      this.decoderPath = path;
      return this;
    }
  }, {
    key: "setDecoderConfig",
    value: function setDecoderConfig(config) {
      this.decoderConfig = config;
      return this;
    }
  }, {
    key: "setWorkerLimit",
    value: function setWorkerLimit(workerLimit) {
      this.workerLimit = workerLimit;
      return this;
    }
  }, {
    key: "load",
    value: function load(url, onLoad, onProgress, onError) {
      var _this2 = this;
      var loader = new _three.FileLoader(this.manager);
      loader.setPath(this.path);
      loader.setResponseType('arraybuffer');
      loader.setRequestHeader(this.requestHeader);
      loader.setWithCredentials(this.withCredentials);
      loader.load(url, function (buffer) {
        _this2.parse(buffer, onLoad, onError);
      }, onProgress, onError);
    }
  }, {
    key: "parse",
    value: function parse(buffer, onLoad, onError) {
      this.decodeDracoFile(buffer, onLoad, null, null, _three.SRGBColorSpace)["catch"](onError);
    }
  }, {
    key: "decodeDracoFile",
    value: function decodeDracoFile(buffer, callback, attributeIDs, attributeTypes) {
      var vertexColorSpace = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : _three.LinearSRGBColorSpace;
      var taskConfig = {
        attributeIDs: attributeIDs || this.defaultAttributeIDs,
        attributeTypes: attributeTypes || this.defaultAttributeTypes,
        useUniqueIDs: !!attributeIDs,
        vertexColorSpace: vertexColorSpace
      };
      return this.decodeGeometry(buffer, taskConfig).then(callback);
    }
  }, {
    key: "decodeGeometry",
    value: function decodeGeometry(buffer, taskConfig) {
      var _this3 = this;
      var taskKey = JSON.stringify(taskConfig);

      // Check for an existing task using this buffer. A transferred buffer cannot be transferred
      // again from this thread.
      if (_taskCache.has(buffer)) {
        var cachedTask = _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('THREE.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(function (_worker) {
        worker = _worker;
        return new Promise(function (resolve, reject) {
          worker._callbacks[taskID] = {
            resolve: resolve,
            reject: reject
          };
          worker.postMessage({
            type: 'decode',
            id: taskID,
            taskConfig: taskConfig,
            buffer: buffer
          }, [buffer]);

          // this.debug();
        });
      }).then(function (message) {
        return _this3._createGeometry(message.geometry);
      });

      // Remove task from the task list.
      // Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416)
      geometryPending["catch"](function () {
        return true;
      }).then(function () {
        if (worker && taskID) {
          _this3._releaseTask(worker, taskID);

          // this.debug();
        }
      });

      // Cache the task result.
      _taskCache.set(buffer, {
        key: taskKey,
        promise: geometryPending
      });
      return geometryPending;
    }
  }, {
    key: "_createGeometry",
    value: function _createGeometry(geometryData) {
      var geometry = new _three.BufferGeometry();
      if (geometryData.index) {
        geometry.setIndex(new _three.BufferAttribute(geometryData.index.array, 1));
      }
      for (var i = 0; i < geometryData.attributes.length; i++) {
        var result = geometryData.attributes[i];
        var name = result.name;
        var array = result.array;
        var itemSize = result.itemSize;
        var attribute = new _three.BufferAttribute(array, itemSize);
        if (name === 'color') {
          this._assignVertexColorSpace(attribute, result.vertexColorSpace);
          attribute.normalized = array instanceof Float32Array === false;
        }
        geometry.setAttribute(name, attribute);
      }
      return geometry;
    }
  }, {
    key: "_assignVertexColorSpace",
    value: function _assignVertexColorSpace(attribute, inputColorSpace) {
      // While .drc files do not specify colorspace, the only 'official' tooling
      // is PLY and OBJ converters, which use sRGB. We'll assume sRGB when a .drc
      // file is passed into .load() or .parse(). GLTFLoader uses internal APIs
      // to decode geometry, and vertex colors are already Linear-sRGB in there.

      if (inputColorSpace !== _three.SRGBColorSpace) return;
      var _color = new _three.Color();
      for (var i = 0, il = attribute.count; i < il; i++) {
        _color.fromBufferAttribute(attribute, i).convertSRGBToLinear();
        attribute.setXYZ(i, _color.r, _color.g, _color.b);
      }
    }
  }, {
    key: "_loadLibrary",
    value: function _loadLibrary(url, responseType) {
      var loader = new _three.FileLoader(this.manager);
      loader.setPath(this.decoderPath);
      loader.setResponseType(responseType);
      loader.setWithCredentials(this.withCredentials);
      return new Promise(function (resolve, reject) {
        loader.load(url, resolve, undefined, reject);
      });
    }
  }, {
    key: "preload",
    value: function preload() {
      this._initDecoder();
      return this;
    }
  }, {
    key: "_initDecoder",
    value: function _initDecoder() {
      var _this4 = this;
      if (this.decoderPending) return this.decoderPending;
      var useJS = (typeof WebAssembly === "undefined" ? "undefined" : (0, _typeof2["default"])(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(function (libraries) {
        var jsContent = libraries[0];
        if (!useJS) {
          _this4.decoderConfig.wasmBinary = libraries[1];
        }
        var fn = DRACOWorker.toString();
        var body = ['/* draco decoder */', jsContent, '', '/* worker */', fn.substring(fn.indexOf('{') + 1, fn.lastIndexOf('}'))].join('\n');
        _this4.workerSourceURL = URL.createObjectURL(new Blob([body]));
      });
      return this.decoderPending;
    }
  }, {
    key: "_getWorker",
    value: function _getWorker(taskID, taskCost) {
      var _this5 = this;
      return this._initDecoder().then(function () {
        if (_this5.workerPool.length < _this5.workerLimit) {
          var _worker2 = new Worker(_this5.workerSourceURL);
          _worker2._callbacks = {};
          _worker2._taskCosts = {};
          _worker2._taskLoad = 0;
          _worker2.postMessage({
            type: 'init',
            decoderConfig: _this5.decoderConfig
          });
          _worker2.onmessage = function (e) {
            var message = e.data;
            switch (message.type) {
              case 'decode':
                _worker2._callbacks[message.id].resolve(message);
                break;
              case 'error':
                _worker2._callbacks[message.id].reject(message);
                break;
              default:
                console.error('THREE.DRACOLoader: Unexpected message, "' + message.type + '"');
            }
          };
          _this5.workerPool.push(_worker2);
        } else {
          _this5.workerPool.sort(function (a, b) {
            return a._taskLoad > b._taskLoad ? -1 : 1;
          });
        }
        var worker = _this5.workerPool[_this5.workerPool.length - 1];
        worker._taskCosts[taskID] = taskCost;
        worker._taskLoad += taskCost;
        return worker;
      });
    }
  }, {
    key: "_releaseTask",
    value: function _releaseTask(worker, taskID) {
      worker._taskLoad -= worker._taskCosts[taskID];
      delete worker._callbacks[taskID];
      delete worker._taskCosts[taskID];
    }
  }, {
    key: "debug",
    value: function debug() {
      console.log('Task load: ', this.workerPool.map(function (worker) {
        return worker._taskLoad;
      }));
    }
  }, {
    key: "dispose",
    value: function dispose() {
      for (var i = 0; i < this.workerPool.length; ++i) {
        this.workerPool[i].terminate();
      }
      this.workerPool.length = 0;
      if (this.workerSourceURL !== '') {
        URL.revokeObjectURL(this.workerSourceURL);
      }
      return this;
    }
  }]);
  return DRACOLoader;
}(_three.Loader);
/* WEB WORKER */
exports.DRACOLoader = DRACOLoader;
function DRACOWorker() {
  var decoderConfig;
  var decoderPending;
  onmessage = function onmessage(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); // eslint-disable-line no-undef
        });

        break;
      case 'decode':
        var buffer = message.buffer;
        var taskConfig = message.taskConfig;
        decoderPending.then(function (module) {
          var draco = module.draco;
          var decoder = new draco.Decoder();
          try {
            var geometry = decodeGeometry(draco, decoder, new Int8Array(buffer), taskConfig);
            var buffers = geometry.attributes.map(function (attr) {
              return attr.array.buffer;
            });
            if (geometry.index) buffers.push(geometry.index.array.buffer);
            self.postMessage({
              type: 'decode',
              id: message.id,
              geometry: geometry
            }, buffers);
          } catch (error) {
            console.error(error);
            self.postMessage({
              type: 'error',
              id: message.id,
              error: error.message
            });
          } finally {
            draco.destroy(decoder);
          }
        });
        break;
    }
  };
  function decodeGeometry(draco, decoder, array, taskConfig) {
    var attributeIDs = taskConfig.attributeIDs;
    var attributeTypes = taskConfig.attributeTypes;
    var dracoGeometry;
    var decodingStatus;
    var geometryType = decoder.GetEncodedGeometryType(array);
    if (geometryType === draco.TRIANGULAR_MESH) {
      dracoGeometry = new draco.Mesh();
      decodingStatus = decoder.DecodeArrayToMesh(array, array.byteLength, dracoGeometry);
    } else if (geometryType === draco.POINT_CLOUD) {
      dracoGeometry = new draco.PointCloud();
      decodingStatus = decoder.DecodeArrayToPointCloud(array, array.byteLength, dracoGeometry);
    } else {
      throw new Error('THREE.DRACOLoader: Unexpected geometry type.');
    }
    if (!decodingStatus.ok() || dracoGeometry.ptr === 0) {
      throw new Error('THREE.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 = void 0;
      var attributeID = void 0;

      // 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);
      }
      var attributeResult = decodeAttribute(draco, decoder, dracoGeometry, attributeName, attributeType, attribute);
      if (attributeName === 'color') {
        attributeResult.vertexColorSpace = taskConfig.vertexColorSpace;
      }
      geometry.attributes.push(attributeResult);
    }

    // Add index.
    if (geometryType === draco.TRIANGULAR_MESH) {
      geometry.index = decodeIndex(draco, decoder, dracoGeometry);
    }
    draco.destroy(dracoGeometry);
    return geometry;
  }
  function decodeIndex(draco, decoder, dracoGeometry) {
    var numFaces = dracoGeometry.num_faces();
    var numIndices = numFaces * 3;
    var byteLength = numIndices * 4;
    var ptr = draco._malloc(byteLength);
    decoder.GetTrianglesUInt32Array(dracoGeometry, byteLength, ptr);
    var index = new Uint32Array(draco.HEAPF32.buffer, ptr, numIndices).slice();
    draco._free(ptr);
    return {
      array: index,
      itemSize: 1
    };
  }
  function decodeAttribute(draco, decoder, dracoGeometry, attributeName, attributeType, attribute) {
    var numComponents = attribute.num_components();
    var numPoints = dracoGeometry.num_points();
    var numValues = numPoints * numComponents;
    var byteLength = numValues * attributeType.BYTES_PER_ELEMENT;
    var dataType = getDracoDataType(draco, attributeType);
    var ptr = draco._malloc(byteLength);
    decoder.GetAttributeDataArrayForAllPoints(dracoGeometry, attribute, dataType, byteLength, ptr);
    var array = new attributeType(draco.HEAPF32.buffer, ptr, numValues).slice();
    draco._free(ptr);
    return {
      name: attributeName,
      array: array,
      itemSize: numComponents
    };
  }
  function getDracoDataType(draco, attributeType) {
    switch (attributeType) {
      case Float32Array:
        return draco.DT_FLOAT32;
      case Int8Array:
        return draco.DT_INT8;
      case Int16Array:
        return draco.DT_INT16;
      case Int32Array:
        return draco.DT_INT32;
      case Uint8Array:
        return draco.DT_UINT8;
      case Uint16Array:
        return draco.DT_UINT16;
      case Uint32Array:
        return draco.DT_UINT32;
    }
  }
}