Program.js

import {
  VERTEX_SHADER,
  FRAGMENT_SHADER,
  LINK_STATUS,
  COMPILE_STATUS,
  ACTIVE_UNIFORMS
} from '../const/webglConst';
import webglNumber from '../const/webglNumber';
import Debug from '../utils/Debug';
const uniformMap = {
 INT: 'uniform1i',
 FLOAT: 'uniform1f',
 FLOAT_VEC2: 'uniform2f',
 FLOAT_VEC3: 'uniform3f',
 SAMPLER_2D: 'uniform1i',
 SAMPLER_CUBE: 'uniform1i',
 FLOAT_MAT3: 'uniformMatrix3fv',
 FLOAT_MAT4: 'uniformMatrix4fv'
 }

 const attributesMapSize = {
  FLOAT: 1,
  INT: 1,
  FLOAT_VEC2: 2,
  FLOAT_VEC3: 3,
  FLOAT_VEC4: 4,
}


 String.prototype.capitalizeFirstLetter = function() {
     return this.charAt(0).toUpperCase() + this.slice(1);
 }
 class Program {
  /**
    * Constructs a new Program
    * @param {object} [params={}]  options
    * @param {object} params.context  context webgl
    * @param {string} params.vertexShader  vertexShader source
    * @param {string} params.fragmentShader  fragmentShader source
    * @param {string} params.name  name
    */
  constructor({
    context,
    vertexShader,
    fragmentShader,
    name = '',
  } = {}) {
    this.gl = context;
    this.vs = this.createShader(vertexShader, VERTEX_SHADER);
    this.fs = this.createShader(fragmentShader, FRAGMENT_SHADER);
    this.program = this.createProgram(this.vs, this.fs);
    this.uniforms = this.u = {};
    this.attributes = this.a = {};

    Debug.group(`Program: ${name}`);
    this.buildAttributeGetter();
    this.buildUniformsGetterSetter();
    Debug.groupEnd()


  }
  /**
  * @func  createProgram
  * @description  create a shader
  * @param {object} vertexShader  vertexShader source
  * @param {object} fragmentShader  fragmentShader source
  * @memberof Program.prototype
  */
  createProgram(vertexShader, fragmentShader) {
    const program = this.gl.createProgram();
    this.gl.attachShader(program, vertexShader);
    this.gl.attachShader(program, fragmentShader);
    this.gl.linkProgram(program);
    const success = this.gl.getProgramParameter(program, LINK_STATUS);
    if (success) return program;

    console.log(this.gl.getProgramInfoLog(program));
    this.gl.deleteProgram(program);
    return null;
  }
  /**
  * @func  createShader
  * @description  create a shader
  * @param {string} source  shader source
  * @param {string} type  VERTEX_SHADER|FRAGMENT_SHADER
  * @memberof Program.prototype
  */
  createShader(source, type) {
    const shader = this.gl.createShader(type);
    this.gl.shaderSource(shader, source);
    this.gl.compileShader(shader);
    const success = this.gl.getShaderParameter(shader, COMPILE_STATUS);
    if (success) return shader;

    console.log(this.gl.getShaderInfoLog(shader));

    this.gl.deleteShader(shader);
    return null;
  }
  /**
  * @func  buildAttributeGetter
  * @description  Auto build attribute getter
  * @memberof Program.prototype
  */
  buildAttributeGetter() {
    var numAttributes = this.gl.getProgramParameter(this.program, this.gl.ACTIVE_ATTRIBUTES);
    Debug.log(`Number of active attributes: ${numAttributes}`);


    for (var i = 0; i < numAttributes; ++i) {
      var attribute = this.gl.getActiveAttrib(this.program, i);
      var aLocation = this.gl.getAttribLocation(this.program, attribute.name);
      attribute.location = aLocation;
      const Afn = `get${attribute.name.capitalizeFirstLetter()}Location`;
      attribute._size = attributesMapSize[webglNumber[attribute.type]];
      this.attributes[attribute.name] = attribute;
      Debug.log(`Attribute generated: ${attribute.name}`)

      // console.log(attributesMapSize[webglNumber[attribute.type]]);
    }

  }
  /**
  * @func  buildUniformsGetterSetter
  * @description  Auto build uniforms getter and setter
  * @memberof Program.prototype
  */
  buildUniformsGetterSetter() {

    const nbActiveUniforms = this.gl.getProgramParameter(this.program, ACTIVE_UNIFORMS);
    Debug.log(`Number of active uniforms: ${nbActiveUniforms}`);

    for (let i = 0; i < nbActiveUniforms; ++i)  {
      let uniform = this.gl.getActiveUniform(this.program, i);
      let uLocation = this.gl.getUniformLocation( this.program, uniform.name );
      let uFunction = uniformMap[webglNumber[uniform.type]];
      Debug.log(`Uniform generated: ${uniform.name} | ${webglNumber[uniform.type]}`)
      this.uniforms[uniform.name] = null;

      Object.defineProperty(this.uniforms, uniform.name, {
        get: () => {
           return uniform.value;
         },
         set: (value) => {
            uniform.value = value;

            if (uFunction.indexOf('Matrix') === -1) {

              if(!value.length) {
                if(uFunction === 'uniform1i') {
                  if(webglNumber[uniform.type] === 'INT') {
                    this.gl[uFunction](uLocation, value);
                  } else {
                    this.gl[uFunction](uLocation, value._bindIndex);
                  }
                } else {

                  this.gl[uFunction](uLocation, value);

                }
              }
              else {
                this.gl[uFunction].apply( this.gl, Array.prototype.concat.apply( uLocation, value) );
              }

            } else {
              this.gl[uFunction](uLocation, this.gl.FALSE, value);
            }
         }
       });

    }

  }
  /**
  * @func  bind
  * @description  Bind the program
  * @memberof Program.prototype
  */
  bind() {
    this.gl.useProgram(this.program);
  }
  /**
  * @func  destroy
  * @description  Destroy the program
  * @memberof Program.prototype
  */
  destroy() {
    this.gl.deleteProgram(this.program);
    this.gl.deleteShader(this.vs);
    this.gl.deleteShader(this.fs);
  }
}
export default Program;