function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }

function _instanceof(left, right) { if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) { return right[Symbol.hasInstance](left); } else { return left instanceof right; } }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

function _classCallCheck(instance, Constructor) { if (!_instanceof(instance, Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

import GL from '../constants';
import Attribute from './attribute';
import Object3D from './object-3d';
import { getDrawMode } from '../geometry/geometry';
import { Buffer, Query, Program, TransformFeedback, VertexArray, clear as _clear } from '../webgl';
import { isWebGL } from '../webgl-utils';
import { getUniformsTable } from '../webgl/uniforms';
import { MODULAR_SHADERS } from '../shadertools/src/shaders';
import { assembleShaders } from '../shadertools/src';
import { addModel, removeModel, logModel, getOverrides } from '../debug/seer-integration';
import { log, isObjectEmpty } from '../utils';
import assert from '../utils/assert';
var ERR_MODEL_PARAMS = 'Model needs drawMode and vertexCount';
var LOG_DRAW_PRIORITY = 2;
var LOG_DRAW_TIMEOUT = 10000; // These old picking uniforms should be avoided and we should use picking module
// and set uniforms using Model class 'updateModuleSettings()'
// TODO - move to shader modules

var DEPRECATED_PICKING_UNIFORMS = ['renderPickingBuffer', 'pickingEnabled']; // Model abstract O3D Class

var Model =
/*#__PURE__*/
function (_Object3D) {
  _inherits(Model, _Object3D);

  function Model(gl) {
    var _this;

    var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

    _classCallCheck(this, Model);

    _this = _possibleConstructorReturn(this, (Model.__proto__ || Object.getPrototypeOf(Model)).call(this, props));
    assert(isWebGL(gl));
    _this.gl = gl;
    _this.lastLogTime = 0; // TODO - move to probe.gl

    _this.initialize(props); // intended to be subclassed, do not seal


    return _this;
  }

  _createClass(Model, [{
    key: "initialize",

    /* eslint-disable max-statements  */

    /* eslint-disable complexity  */
    value: function initialize() {
      var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      this.props = {};
      this.program = this._createProgram(props); // Create a vertex array configured after this program

      this.vertexArray = new VertexArray(this.gl, {
        program: this.program
      }); // Initialize state

      this.userData = {};
      this.needsRedraw = true; // Model manages auto Buffer creation from typed arrays

      this._attributes = {}; // All attributes

      this.attributes = {}; // User defined attributes
      // Model manages uniform animation

      this.animatedUniforms = {};
      this.animated = false;
      this.animationLoop = null; // if set, used as source for animationProps

      this.timerQueryEnabled = false;
      this.timeElapsedQuery = undefined;
      this.lastQueryReturned = true;
      this.stats = {
        accumulatedFrameTime: 0,
        averageFrameTime: 0,
        profileFrameCount: 0
      }; // picking options

      this.pickable = true; // this.pick = pick || (() => false);

      this.setProps(props); // Make sure we have some reasonable default uniforms in place

      this.setUniforms(Object.assign({}, this.getModuleUniforms(), // Get all default uniforms
      this.getModuleUniforms(props.moduleSettings) // Get unforms for supplied parameters
      )); // Attributes and buffers
      // geometry might have set drawMode and vertexCount

      this.isInstanced = props.isInstanced || props.instanced;

      this.onBeforeRender = props.onBeforeRender || function () {};

      this.onAfterRender = props.onAfterRender || function () {}; // assert(program || program instanceof Program);


      assert(this.drawMode !== undefined && Number.isFinite(this.vertexCount), ERR_MODEL_PARAMS);
    }
    /* eslint-enable max-statements */

  }, {
    key: "setProps",
    value: function setProps(props) {
      Object.assign(this.props, props); // params
      // if ('drawMode' in props) {
      //   this.drawMode = getDrawMode(props.drawMode);
      // }
      // if ('vertexCount' in props) {
      //   this.vertexCount = props.vertexCount;
      // }

      if ('instanceCount' in props) {
        this.instanceCount = props.instanceCount;
      }

      if ('geometry' in props) {
        this.setGeometry(props.geometry);
      } // webgl settings


      if ('attributes' in props) {
        this.setAttributes(props.attributes);
      }

      if ('uniforms' in props) {
        this.setUniforms(props.uniforms, props.samplers);
      }

      if ('pickable' in props) {
        this.pickable = props.pickable;
      } // Experimental props


      if ('timerQueryEnabled' in props) {
        this.timerQueryEnabled = props.timerQueryEnabled && Query.isSupported(this.gl, ['timers']);

        if (props.timerQueryEnabled && !this.timerQueryEnabled) {
          log.warn('GPU timer not supported')();
        }
      }

      if ('_feedbackBuffers' in props) {
        this._setFeedbackBuffers(props._feedbackBuffers);
      }

      if ('_animationProps' in props) {
        this._setAnimationProps(props._animationProps);
      }

      if ('_animationLoop' in props) {
        this.animationLoop = props._animationLoop;
      }
    }
  }, {
    key: "delete",
    value: function _delete() {
      // delete all attributes created by this model
      // TODO - should buffer deletes be handled by vertex array?
      for (var key in this._attributes) {
        if (this._attributes[key] !== this.attributes[key]) {
          this._attributes[key].delete();
        }
      }

      this.program.delete();
      this.vertexArray.delete();
      removeModel(this.id);
    }
  }, {
    key: "destroy",
    value: function destroy() {
      this.delete();
    } // GETTERS

  }, {
    key: "getNeedsRedraw",
    value: function getNeedsRedraw() {
      var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
          _ref$clearRedrawFlags = _ref.clearRedrawFlags,
          clearRedrawFlags = _ref$clearRedrawFlags === void 0 ? false : _ref$clearRedrawFlags;

      var redraw = false;
      redraw = redraw || this.needsRedraw;
      this.needsRedraw = this.needsRedraw && !clearRedrawFlags;

      if (this.geometry) {
        redraw = redraw || this.geometry.getNeedsRedraw({
          clearRedrawFlags: clearRedrawFlags
        });
      }

      return redraw;
    }
  }, {
    key: "getDrawMode",
    value: function getDrawMode() {
      return this.drawMode;
    }
  }, {
    key: "getVertexCount",
    value: function getVertexCount() {
      return this.vertexCount;
    }
  }, {
    key: "getInstanceCount",
    value: function getInstanceCount() {
      return this.instanceCount;
    }
  }, {
    key: "getProgram",
    value: function getProgram() {
      return this.program;
    }
  }, {
    key: "getAttributes",
    value: function getAttributes() {
      return this.attributes;
    }
  }, {
    key: "getUniforms",
    value: function getUniforms() {
      return this.program.getUniforms;
    } // SETTERS

  }, {
    key: "setNeedsRedraw",
    value: function setNeedsRedraw() {
      var redraw = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
      this.needsRedraw = redraw;
      return this;
    }
  }, {
    key: "setDrawMode",
    value: function setDrawMode(drawMode) {
      this.props.drawMode = getDrawMode(drawMode);
      return this;
    }
  }, {
    key: "setVertexCount",
    value: function setVertexCount(vertexCount) {
      assert(Number.isFinite(vertexCount));
      this.props.vertexCount = vertexCount;
      return this;
    }
  }, {
    key: "setInstanceCount",
    value: function setInstanceCount(instanceCount) {
      assert(Number.isFinite(instanceCount));
      this.instanceCount = instanceCount;
      return this;
    } // TODO - just set attributes, don't hold on to geometry

  }, {
    key: "setGeometry",
    value: function setGeometry(geometry) {
      this.geometry = geometry;

      var buffers = this._createBuffersFromAttributeDescriptors(this.geometry.getAttributes());

      this.vertexArray.setAttributes(buffers);
      this.setNeedsRedraw();
      return this;
    }
  }, {
    key: "setAttributes",
    value: function setAttributes() {
      var attributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

      // Avoid setting needsRedraw if no attributes
      if (isObjectEmpty(attributes)) {
        return this;
      }

      Object.assign(this.attributes, attributes);

      var buffers = this._createBuffersFromAttributeDescriptors(attributes); // Object.assign(this.attributes, buffers);


      this.vertexArray.setAttributes(buffers);
      this.setNeedsRedraw();
      return this;
    } // TODO - should actually set the uniforms

  }, {
    key: "setUniforms",
    value: function setUniforms() {
      var _this2 = this;

      var uniforms = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var samplers = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      // Let Seer override edited uniforms
      uniforms = Object.assign({}, uniforms);
      getOverrides(this.id, uniforms); // Resolve any animated uniforms so that we have an initial value

      uniforms = this._extractAnimatedUniforms(uniforms);
      this.program.setUniforms(uniforms, samplers, function () {
        // if something changed
        _this2._checkForDeprecatedUniforms(uniforms);

        _this2.setNeedsRedraw();
      });
    }
  }, {
    key: "updateModuleSettings",
    value: function updateModuleSettings(opts) {
      var uniforms = this.getModuleUniforms(opts || {});
      return this.setUniforms(uniforms);
    } // DRAW CALLS

  }, {
    key: "clear",
    value: function clear(opts) {
      _clear(this.program.gl, opts);

      return this;
    }
    /* eslint-disable max-statements  */

  }, {
    key: "draw",
    value: function draw() {
      var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var _opts$moduleSettings = opts.moduleSettings,
          moduleSettings = _opts$moduleSettings === void 0 ? null : _opts$moduleSettings,
          framebuffer = opts.framebuffer,
          _opts$uniforms = opts.uniforms,
          uniforms = _opts$uniforms === void 0 ? {} : _opts$uniforms,
          _opts$attributes = opts.attributes,
          attributes = _opts$attributes === void 0 ? {} : _opts$attributes,
          _opts$samplers = opts.samplers,
          samplers = _opts$samplers === void 0 ? {} : _opts$samplers,
          _opts$transformFeedba = opts.transformFeedback,
          transformFeedback = _opts$transformFeedba === void 0 ? this.transformFeedback : _opts$transformFeedba,
          _opts$parameters = opts.parameters,
          parameters = _opts$parameters === void 0 ? {} : _opts$parameters,
          _opts$vertexArray = opts.vertexArray,
          vertexArray = _opts$vertexArray === void 0 ? this.vertexArray : _opts$vertexArray,
          animationProps = opts.animationProps; // Update module settings

      addModel(this); // Update model with any just provided attributes, settings or uniforms

      this.setAttributes(attributes);
      this.updateModuleSettings(moduleSettings);
      this.setUniforms(uniforms, samplers); // Animate any function valued uniforms

      this._refreshAnimationProps(animationProps);

      var logPriority = this._logDrawCallStart(2);

      var drawParams = this.vertexArray.drawParams;

      if (drawParams.isInstanced && !this.isInstanced) {
        log.warn('Found instanced attributes on non-instanced model', this.id)();
      }

      var isIndexed = drawParams.isIndexed,
          indexType = drawParams.indexType;
      var isInstanced = this.isInstanced,
          instanceCount = this.instanceCount;
      this.onBeforeRender();

      this._timerQueryStart();

      this.program.draw(Object.assign(opts, {
        logPriority: logPriority,
        framebuffer: framebuffer,
        parameters: parameters,
        drawMode: this.getDrawMode(),
        vertexCount: this.getVertexCount(),
        vertexArray: vertexArray,
        transformFeedback: transformFeedback,
        isIndexed: isIndexed,
        indexType: indexType,
        isInstanced: isInstanced,
        instanceCount: instanceCount
      }));

      this._timerQueryEnd();

      this.onAfterRender();
      this.setNeedsRedraw(false);

      this._logDrawCallEnd(logPriority, vertexArray, framebuffer);

      return this;
    }
    /* eslint-enable max-statements  */
    // Draw call for transform feedback

  }, {
    key: "transform",
    value: function transform() {
      var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var _opts$discard = opts.discard,
          discard = _opts$discard === void 0 ? true : _opts$discard,
          feedbackBuffers = opts.feedbackBuffers,
          _opts$unbindModels = opts.unbindModels,
          unbindModels = _opts$unbindModels === void 0 ? [] : _opts$unbindModels;
      var parameters = opts.parameters;

      if (feedbackBuffers) {
        this._setFeedbackBuffers(feedbackBuffers);
      }

      if (discard) {
        parameters = Object.assign({}, parameters, _defineProperty({}, GL.RASTERIZER_DISCARD, discard));
      }

      unbindModels.forEach(function (model) {
        return model.vertexArray.unbindBuffers();
      });

      try {
        this.draw(Object.assign({}, opts, {
          parameters: parameters
        }));
      } finally {
        unbindModels.forEach(function (model) {
          return model.vertexArray.bindBuffers();
        });
      }

      return this;
    } // DEPRECATED METHODS

  }, {
    key: "render",
    value: function render() {
      var uniforms = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      assert(arguments.length <= 1); // log.deprecated('Model.render()', 'Model.draw()')();

      return this.draw({
        uniforms: uniforms
      });
    } // PRIVATE METHODS

  }, {
    key: "_createProgram",
    value: function _createProgram(_ref2) {
      var _ref2$vs = _ref2.vs,
          vs = _ref2$vs === void 0 ? null : _ref2$vs,
          _ref2$fs = _ref2.fs,
          fs = _ref2$fs === void 0 ? null : _ref2$fs,
          _ref2$modules = _ref2.modules,
          modules = _ref2$modules === void 0 ? null : _ref2$modules,
          _ref2$defines = _ref2.defines,
          defines = _ref2$defines === void 0 ? {} : _ref2$defines,
          _ref2$inject = _ref2.inject,
          inject = _ref2$inject === void 0 ? {} : _ref2$inject,
          _ref2$shaderCache = _ref2.shaderCache,
          shaderCache = _ref2$shaderCache === void 0 ? null : _ref2$shaderCache,
          _ref2$varyings = _ref2.varyings,
          varyings = _ref2$varyings === void 0 ? null : _ref2$varyings,
          _ref2$bufferMode = _ref2.bufferMode,
          bufferMode = _ref2$bufferMode === void 0 ? GL.SEPARATE_ATTRIBS : _ref2$bufferMode,
          _ref2$program = _ref2.program,
          program = _ref2$program === void 0 ? null : _ref2$program;

      this.getModuleUniforms = function (x) {};

      if (!program) {
        // Assign default shaders if none are provided
        vs = vs || MODULAR_SHADERS.vs;
        fs = fs || MODULAR_SHADERS.fs;
        var assembleResult = assembleShaders(this.gl, {
          vs: vs,
          fs: fs,
          modules: modules,
          inject: inject,
          defines: defines,
          log: log
        });
        vs = assembleResult.vs;
        fs = assembleResult.fs;

        if (shaderCache) {
          program = shaderCache.getProgram(this.gl, {
            vs: vs,
            fs: fs,
            id: this.id
          });
        } else {
          program = new Program(this.gl, {
            vs: vs,
            fs: fs,
            varyings: varyings,
            bufferMode: bufferMode
          });
        }

        this.getModuleUniforms = assembleResult.getUniforms || function (x) {};
      }

      assert(_instanceof(program, Program), 'Model needs a program');
      return program;
    }
    /* eslint-enable complexity */
    // Uniforms

  }, {
    key: "_checkForDeprecatedUniforms",
    value: function _checkForDeprecatedUniforms(uniforms) {
      // deprecated picking uniforms
      DEPRECATED_PICKING_UNIFORMS.forEach(function (uniform) {
        if (uniform in uniforms) {
          log.deprecated(uniform, 'use picking shader module and Model class updateModuleSettings()')();
        }
      });
    } // Refreshes animated uniforms, attempting to get animated props from animationLoop if registered

  }, {
    key: "_refreshAnimationProps",
    value: function _refreshAnimationProps(animationProps) {
      // Try to read animationProps
      animationProps = animationProps || this.animationLoop && this.animationLoop.animationProps;

      this._setAnimationProps(animationProps);
    } // Generates and sets uniform values based on new animationProps

  }, {
    key: "_setAnimationProps",
    value: function _setAnimationProps(animationProps) {
      var _this3 = this;

      if (this.animated) {
        assert(animationProps, 'Model.draw(): animated uniforms but no animationProps');

        var animatedUniforms = this._evaluateAnimateUniforms(animationProps);

        this.program.setUniforms(animatedUniforms, {}, function () {
          // if something changed
          _this3._checkForDeprecatedUniforms(animatedUniforms);

          _this3.setNeedsRedraw();
        });
      }
    } // Calculate new values for any function uniforms based on supplied animationProps

  }, {
    key: "_evaluateAnimateUniforms",
    value: function _evaluateAnimateUniforms(animationProps) {
      if (!this.animated) {
        return {};
      }

      var animatedUniforms = {};

      for (var uniformName in this.animatedUniforms) {
        var valueFunction = this.animatedUniforms[uniformName];
        animatedUniforms[uniformName] = valueFunction(animationProps);
      }

      return animatedUniforms;
    } // Extracts a list of function valued uniforms, so we can update them before each draw call
    // Also removes such uniforms from the returned list

  }, {
    key: "_extractAnimatedUniforms",
    value: function _extractAnimatedUniforms(uniforms) {
      var foundAnimated = false; // Keep our animatedUniforms map up-to-date

      for (var uniformName in uniforms) {
        var newValue = uniforms[uniformName];

        if (typeof newValue === 'function') {
          this.animatedUniforms[uniformName] = newValue;
          foundAnimated = true;
        } else {
          delete this.animatedUniforms[uniformName];
        }
      } // Update animated flag: `Model` is animated if any uniforms are animated (i.e. functions)


      this.animated = !isObjectEmpty(this.animatedUniforms);

      if (!foundAnimated) {
        return uniforms;
      } // If animated uniforms were found, remove them from ordinary uniform list
      // `Program` class can't (and shouldn't) handle function valued uniforms


      var staticUniforms = {};

      for (var _uniformName in uniforms) {
        if (!this.animatedUniforms[_uniformName]) {
          staticUniforms[_uniformName] = uniforms[_uniformName];
        }
      }

      return staticUniforms;
    } // Transform Feedback

  }, {
    key: "_setFeedbackBuffers",
    value: function _setFeedbackBuffers() {
      var feedbackBuffers = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

      // Avoid setting needsRedraw if no feedbackBuffers
      if (isObjectEmpty(feedbackBuffers)) {
        return this;
      }

      var gl = this.program.gl;
      this.transformFeedback = this.transformFeedback || new TransformFeedback(gl, {
        program: this.program
      });
      this.transformFeedback.setBuffers(feedbackBuffers);
      this.setNeedsRedraw();
      return this;
    } // Timer Queries

  }, {
    key: "_timerQueryStart",
    value: function _timerQueryStart() {
      if (this.timerQueryEnabled === true) {
        if (!this.timeElapsedQuery) {
          this.timeElapsedQuery = new Query(this.gl);
        }

        if (this.lastQueryReturned) {
          this.lastQueryReturned = false;
          this.timeElapsedQuery.beginTimeElapsedQuery();
        }
      }
    }
  }, {
    key: "_timerQueryEnd",
    value: function _timerQueryEnd() {
      if (this.timerQueryEnabled === true) {
        this.timeElapsedQuery.end(); // TODO: Skip results if 'gl.getParameter(this.ext.GPU_DISJOINT_EXT)' returns false
        // should this be incorporated into Query object?

        if (this.timeElapsedQuery.isResultAvailable()) {
          this.lastQueryReturned = true;
          var elapsedTime = this.timeElapsedQuery.getResult(); // Update stats (e.g. for seer)

          this.stats.lastFrameTime = elapsedTime;
          this.stats.accumulatedFrameTime += elapsedTime;
          this.stats.profileFrameCount++;
          this.stats.averageFrameTime = this.stats.accumulatedFrameTime / this.stats.profileFrameCount; // Log stats

          log.log(LOG_DRAW_PRIORITY, "GPU time ".concat(this.program.id, ": ").concat(this.stats.lastFrameTime, "ms average ").concat(this.stats.averageFrameTime, "ms accumulated: ").concat(this.stats.accumulatedFrameTime, "ms count: ").concat(this.stats.profileFrameCount))();
        }
      }
    } // Makes sure buffers are created for all attributes
    // and that the program is updated with those buffers
    // TODO - do we need the separation between "attributes" and "buffers"
    // couldn't apps just create buffers directly?

  }, {
    key: "_createBuffersFromAttributeDescriptors",
    value: function _createBuffersFromAttributeDescriptors(attributes) {
      var gl = this.program.gl; // const attributes = {};

      var buffers = {};

      for (var attributeName in attributes) {
        var descriptor = attributes[attributeName];
        var attribute = this._attributes[attributeName];

        if (_instanceof(descriptor, Attribute)) {
          attribute = descriptor;
        } else if (_instanceof(descriptor, Buffer)) {
          attribute = attribute || new Attribute(gl, Object.assign({}, descriptor.layout, {
            id: attributeName
          }));
          attribute.update({
            buffer: descriptor
          });
        } else if (attribute) {
          attribute.update(descriptor);
        } else {
          attribute = new Attribute(gl, descriptor);
        }

        this._attributes[attributeName] = attribute;
        buffers[attributeName] = attribute.getValue();
      }

      return buffers;
    }
  }, {
    key: "_logDrawCallStart",
    value: function _logDrawCallStart(priority) {
      var logDrawTimeout = priority > 3 ? 0 : LOG_DRAW_TIMEOUT;

      if (log.priority < priority || Date.now() - this.lastLogTime < logDrawTimeout) {
        return undefined;
      }

      this.lastLogTime = Date.now();
      log.group(LOG_DRAW_PRIORITY, ">>> DRAWING MODEL ".concat(this.id), {
        collapsed: log.priority <= 2
      })();
      return priority;
    }
  }, {
    key: "_logDrawCallEnd",
    value: function _logDrawCallEnd(priority, vertexArray, uniforms, framebuffer) {
      if (priority === undefined) {
        return;
      }

      var attributeTable = vertexArray._getDebugTable({
        header: "".concat(this.id, " attributes"),
        attributes: this._attributes
      });

      var _getUniformsTable = getUniformsTable({
        header: "".concat(this.id, " uniforms"),
        program: this.program,
        uniforms: Object.assign({}, this.program.uniforms, uniforms)
      }),
          uniformTable = _getUniformsTable.table,
          unusedTable = _getUniformsTable.unusedTable,
          unusedCount = _getUniformsTable.unusedCount; // log missing uniforms


      var _getUniformsTable2 = getUniformsTable({
        header: "".concat(this.id, " uniforms"),
        program: this.program,
        uniforms: Object.assign({}, this.program.uniforms, uniforms),
        undefinedOnly: true
      }),
          missingTable = _getUniformsTable2.table,
          missingCount = _getUniformsTable2.count;

      if (missingCount > 0) {
        log.log('MISSING UNIFORMS', Object.keys(missingTable))(); // log.table(priority, missingTable)();
      }

      if (unusedCount > 0) {
        log.log('UNUSED UNIFORMS', Object.keys(unusedTable))(); // log.log(priority, 'Unused uniforms ', unusedTable)();
      }

      log.table(priority, attributeTable)();
      log.table(priority, uniformTable)();
      logModel(this, uniforms);

      if (framebuffer) {
        framebuffer.log({
          priority: LOG_DRAW_PRIORITY,
          message: "Rendered to ".concat(framebuffer.id)
        });
      }

      log.groupEnd(LOG_DRAW_PRIORITY, ">>> DRAWING MODEL ".concat(this.id))();
    }
  }, {
    key: "vertexCount",
    get: function get() {
      if (Number.isFinite(this.props.vertexCount)) {
        return this.props.vertexCount;
      }

      return this.geometry && this.geometry.getVertexCount();
    }
  }, {
    key: "drawMode",
    get: function get() {
      if (Number.isFinite(this.props.drawMode)) {
        return this.props.drawMode;
      }

      return this.geometry && this.geometry.drawMode;
    }
  }]);

  return Model;
}(Object3D);

export { Model as default };
//# sourceMappingURL=model.js.map