vgl/material.js

var vgl = require('./vgl');
var inherit = require('../inherit');

/**
 * Create a new instance of class material.
 *
 * @class
 * @alias vgl.material
 * @returns {vgl.material}
 */
vgl.material = function () {
  'use strict';

  if (!(this instanceof vgl.material)) {
    return new vgl.material();
  }
  vgl.graphicsObject.call(this);

  var m_this = this,
      m_shaderProgram = new vgl.shaderProgram(),
      m_binNumber = 100,
      m_textureAttributes = {},
      m_attributes = {};

  /**
   * Return bin number for the material.
   *
   * @default 100
   * @returns {number}
   */
  this.binNumber = function () {
    return m_binNumber;
  };

  /**
   * Set bin number for the material.
   *
   * @param {number} binNo
   */
  this.setBinNumber = function (binNo) {
    m_binNumber = binNo;
    m_this.modified();
  };

  /**
   * Check if incoming attribute already exists in the material.
   *
   * @param {vgl.materialAttribute} attr
   * @returns {boolean}
   */
  this.exists = function (attr) {
    if (attr.type() === vgl.materialAttributeType.Texture) {
      return m_textureAttributes.hasOwnProperty(attr.textureUnit());
    }
    return m_attributes.hasOwnProperty(attr.type());
  };

  /**
   * Add a new attribute to the material.
   *
   * @param {vgl.materialAttribute} attr
   * @returns {boolean}
   */
  this.addAttribute = function (attr) {
    if (m_this.exists(attr)) {
      return false;
    }

    if (attr.type() === vgl.materialAttributeType.Texture) {
      // TODO Currently we don't check if we are replacing or not.
      // It would be nice to have a flag for it.
      m_textureAttributes[attr.textureUnit()] = attr;
      m_this.modified();
      return true;
    }

    // Shader is a very special attribute
    if (attr.type() === vgl.materialAttributeType.ShaderProgram) {
      m_shaderProgram = attr;
    }

    m_attributes[attr.type()] = attr;
    m_this.modified();
    return true;
  };

  /**
   * Return shader program used by the material.
   *
   * @returns {vgl.shaderProgram}
   */
  this.shaderProgram = function () {
    return m_shaderProgram;
  };

  /**
   * Remove any resources acquired before deletion.
   *
   * @param {vgl.renderState} renderState
   */
  this._cleanup = function (renderState) {
    for (var key in m_attributes) {
      if (m_attributes.hasOwnProperty(key)) {
        m_attributes[key]._cleanup(renderState);
      }
    }

    for (key in m_textureAttributes) {
      if (m_textureAttributes.hasOwnProperty(key)) {
        m_textureAttributes[key]._cleanup(renderState);
      }
    }
    m_shaderProgram._cleanup(renderState);
    m_this.modified();
  };

  /**
   * Bind and activate material states.
   *
   * @param {vgl.renderState} renderState
   */
  this.bind = function (renderState) {
    var key = null;

    m_shaderProgram.bind(renderState);

    for (key in m_attributes) {
      if (m_attributes.hasOwnProperty(key)) {
        if (m_attributes[key] !== m_shaderProgram) {
          m_attributes[key].bind(renderState);
        }
      }
    }

    for (key in m_textureAttributes) {
      if (m_textureAttributes.hasOwnProperty(key)) {
        m_textureAttributes[key].bind(renderState);
      }
    }
  };

  /**
   * Undo-bind and de-activate material states.
   *
   * @param {vgl.renderState} renderState
   */
  this.undoBind = function (renderState) {
    var key = null;
    for (key in m_attributes) {
      if (m_attributes.hasOwnProperty(key)) {
        m_attributes[key].undoBind(renderState);
      }
    }

    for (key in m_textureAttributes) {
      if (m_textureAttributes.hasOwnProperty(key)) {
        m_textureAttributes[key].undoBind(renderState);
      }
    }
  };

  /**
   * Bind vertex data.
   *
   * @param {vgl.renderState} renderState
   * @param {string} key
   */
  this.bindVertexData = function (renderState, key) {
    var i = null;

    for (i in m_attributes) {
      if (m_attributes.hasOwnProperty(i)) {
        m_attributes[i].bindVertexData(renderState, key);
      }
    }
  };

  /**
   * Undo bind vertex data.
   *
   * @param {vgl.renderState} renderState
   * @param {string} key
   */
  this.undoBindVertexData = function (renderState, key) {
    var i = null;

    for (i in m_attributes) {
      if (m_attributes.hasOwnProperty(i)) {
        m_attributes[i].undoBindVertexData(renderState, key);
      }
    }
  };

  return m_this;
};

vgl.material.RenderBin = {
  Base : 0,
  Default : 100,
  Opaque : 100,
  Transparent : 1000,
  Overlay : 10000
};

inherit(vgl.material, vgl.graphicsObject);