Source: stage/buffer/ShaderGenerator.js

/**
 * Generates shader from given values
 * @class
 * @constructor
 */
Ayce.ShaderGenerator = function () {

    this.lightsCount = 4;
    this.texturesCount = 1;
    this.useNormals = false;
    this.useVertexLighting = false;
    this.useFragmentLighting = false;
    this.useSpecularLighting = false;
    this.useTexture = false;
    this.useColor = false;
    this.useSpecularMap = false;
    this.useNormalMap = false;
    this.isParticleSystem = false;

//    var useLightingArray = true;//Boolean(this.lightsCount > 1);
    var useTextureArray = false;

    this.vertexShader = "";
    this.fragmentShader = "";

    /**
     * Assembles shader according to given variable values
     */
    this.init = function(){
        if (!this.useTexture) {
            this.useSpecularMap = false;
            this.useNormalMap = false;
        }
        useTextureArray = Boolean(this.texturesCount > 1);

        this.vertexShader +=
            "attribute vec3 aVertexPosition;";

        if(this.useColor){
            this.vertexShader +=
                "attribute vec4 aVertexColor;";
        }

        if(this.useTexture){
            this.vertexShader +=
                "attribute vec2 aTextureCoord;";
        }

        if (this.useNormals) {
            this.vertexShader +=
                "attribute vec3 aVertexNormal;";
        }

        if(this.isParticleSystem) {
            this.vertexShader +=
                "attribute vec3 aGeometryPosition;" +
                "attribute vec3 aVertexVelocity;" +
                "attribute vec3 aVertexRotation;" +
                "attribute float aLifetime;" +
                "attribute float aGravity;" +
                "attribute float aGravityExponent;";
        }

        if (this.useTexture && useTextureArray) {
            this.vertexShader +=
                "attribute float aTextureIndex;";
        }

        this.vertexShader +=
            "uniform mat4 uMVMatrix;" +
            "uniform mat4 uPMatrix;";

        if(this.isParticleSystem) {
            this.vertexShader +=
                "uniform float uTime;";

        }

        if (this.useNormals) {
            this.vertexShader +=
                "uniform mat3 uNMatrix;";
        }
        if (this.useVertexLighting) {
            this.vertexShader +=
                "uniform vec3 uAmbientColor;" +
                "uniform vec3 uPointLightingLocations[" + this.lightsCount + "];" +
                "uniform vec3 uPointLightingColors[" + this.lightsCount + "];" +
                "uniform float uLightIndex;";

            if(this.useSpecularLighting){
                this.vertexShader +=
                "uniform float uShininess;";
            }

            if(this.useSpecularLighting){
                this.vertexShader +=
                "uniform vec3 uSpecularColors[" + this.lightsCount + "];";
            }

        }

        if(this.useColor){
            this.vertexShader +=
                "varying vec4 vColor;";
        }

        if(this.useTexture) {
            this.vertexShader +=
                "varying vec2 vTextureCoord;";
        }

        if (this.useVertexLighting) {
            this.vertexShader +=
                "varying vec3 vLightWeighting;";
        } else if (this.useFragmentLighting) {
            this.vertexShader +=
                "varying vec3 vTransformedNormal;" +
                "varying vec4 vPosition;";
        }

        if (this.useTexture) {
            if (useTextureArray) {
                this.vertexShader +=
                    "varying float vTextureIndex;";
            }
        }

        this.vertexShader +=
            "void main(void) {";
        if(this.isParticleSystem){
            this.vertexShader +=
                "float factor;" +
                "if(aLifetime==0.0){" +
                "factor = uTime;" +
                "}else{" +
                "factor = mod(uTime, aLifetime);" +
                "}" +
                "vec3 distance = aGeometryPosition - aVertexPosition;" +
                "float xAngle = aVertexRotation.x*factor;" +
                "float yAngle = aVertexRotation.y*factor;" +
                "float zAngle = aVertexRotation.z*factor;" +
                "mat4 xRotation = mat4(" +
                "1.0,	 		 0.0,			 0.0,			 0.0," +
                "0.0,			 cos(xAngle),	 sin(xAngle),	 0.0," +
                "0.0,			-sin(xAngle),	 cos(xAngle),	 0.0," +
                "0.0,	 		 0.0,			 0.0,			 1.0" +
                ");" +
                "mat4 yRotation = mat4(" +
                "cos(yAngle),	 0.0,			-sin(yAngle),	 0.0," +
                "0.0,			 1.0,	 		 0.0,			 0.0," +
                "sin(yAngle),	 0.0,	 		 cos(yAngle),	 0.0," +
                "0.0,			 0.0,	 		 0.0,			 1.0" +
                ");" +
                "mat4 zRotation = mat4(" +
                "cos(zAngle),	 sin(zAngle),	 0.0,			 0.0," +
                "-sin(zAngle),	 cos(zAngle),	 0.0,			 0.0," +
                "0.0,			 0.0,			 1.0,			 0.0," +
                "0.0,			 0.0,			 0.0,			 1.0" +
                ");" +
                "vec4 position = vec4(aGeometryPosition, 1.0)*xRotation*yRotation*zRotation;" +
                "position = position-vec4(distance, 0.0);" +
                "vec3 velocity = aVertexVelocity+vec3(0.0, aGravity*pow(factor, aGravityExponent),0.0);" +
                "position = uMVMatrix * vec4(position.xyz+velocity*factor, 1.0);";

        }else {
            if (!this.useFragmentLighting) { // only fragment lighting requires different code here
                this.vertexShader +=
                    "vec4 position = uMVMatrix * vec4(aVertexPosition, 1.0);";
            } else if (this.useFragmentLighting) {
                this.vertexShader +=
                    "vPosition = uMVMatrix * vec4(aVertexPosition, 1.0);" +
                    "vec4 position = vPosition;";
            }
        }

        this.vertexShader +=
            "gl_Position = uPMatrix * position;";

        if (this.useTexture && useTextureArray) {
            this.vertexShader +=
                "vTextureIndex = aTextureIndex;";
        }

        if (this.useTexture) {
            this.vertexShader +=
                "vTextureCoord = aTextureCoord;";
        }

        if (this.useColor) {
            this.vertexShader +=
                "vColor = aVertexColor;";
        }

        if (this.useVertexLighting) {
            this.vertexShader +=
                "vLightWeighting = uAmbientColor;";
            if(this.isParticleSystem) {
                this.vertexShader +=
                    "vec3 transformedNormal = uNMatrix * (vec4(aVertexNormal, 1.0) * xRotation * yRotation * zRotation).xyz;";
            }else{
                this.vertexShader +=
                    "vec3 transformedNormal = uNMatrix * aVertexNormal;";
            }
            this.vertexShader +=
                "vec3 normal = normalize(transformedNormal);" +
                "vec3 eyeDirection = normalize(-position.xyz);" +
                this.getVertexLightingWeightCalcArrayVertex(this.lightsCount, this.useSpecularLighting);
        } else if (this.useFragmentLighting) {
            if(this.isParticleSystem){
                this.vertexShader +=
                    "vTransformedNormal = uNMatrix * (vec4(aVertexNormal, 1.0) * xRotation * yRotation * zRotation).xyz;";
            }else {
                this.vertexShader +=
                    "vTransformedNormal = uNMatrix * aVertexNormal;";
            }
        }

        this.vertexShader +=
            "}";
///////////////////////////////////////////////////////////////////////////////////////

        this.fragmentShader +=
            "precision mediump float;";

        if (this.useVertexLighting) {
            this.fragmentShader +=
                "varying vec3 vLightWeighting;";
        } else if (this.useFragmentLighting) {
            this.fragmentShader +=
                "varying vec3 vTransformedNormal;" +
                "varying vec4 vPosition;" +
                "uniform vec3 uAmbientColor;" +
                "uniform vec3 uPointLightingLocations[" + this.lightsCount + "];" +
                "uniform vec3 uPointLightingColors[" + this.lightsCount + "];" +
                "uniform float uLightIndex;";

            if(this.useSpecularLighting){
                this.fragmentShader +=
                    "uniform float uShininess;";
            }

            if(this.useSpecularLighting){
                this.fragmentShader +=
                    "uniform vec3 uSpecularColors[" + this.lightsCount + "];";
            }
        }

        if (this.useColor) {
            this.fragmentShader +=
                "varying vec4 vColor;";
        }

        if (this.useTexture) {
            this.fragmentShader +=
                "varying vec2 vTextureCoord;";
            if (useTextureArray) {
                this.fragmentShader +=
                "varying float vTextureIndex;" +
                "uniform sampler2D uSampler[" + this.texturesCount + "];";
            }
            else {
                this.fragmentShader +=
                "uniform sampler2D uSampler;";
            }
        }
        if (this.useSpecularMap) {
            this.fragmentShader +=
                "uniform sampler2D uSpecularMapSampler;";
        }

        if (this.useNormalMap) {
            this.fragmentShader +=
                "uniform sampler2D uNormalMapSampler;";
        }

        this.fragmentShader +=
            "void main(void) {" +
            "vec4 fragmentColor;";

        if (this.useFragmentLighting) {
            this.fragmentShader +=
                "vec3 lightWeighting = uAmbientColor;" +
                "vec3 normal = normalize(vTransformedNormal);" +
                "vec3 eyeDirection = normalize(-vPosition.xyz);" +
                this.getFragmentLightingWeightCalcArrayVertex(this.lightsCount, this.useSpecularMap, this.useNormalMap, this.useSpecularLighting);
        }

        if (this.useTexture && useTextureArray) {
            this.fragmentShader +=
                "if (vTextureCoord.x > -0.5 && vTextureCoord.y > -0.5) {" +
                "vec4 tex = vec4(0.0);" +
                "float i = vTextureIndex;" +
                this.getTextureCalcArray(this.texturesCount) +
                "fragmentColor = tex;" +
                "} else {";
            if (this.useColor) {
                this.fragmentShader +=
                    "fragmentColor = vColor;";
            }
            else {
                this.fragmentShader +=
                    "fragmentColor = vec4(0.5, 0.5, 0.5, 1.0);";
            }
            this.fragmentShader +=
                "}";
        }
        else if (this.useTexture) {
            this.fragmentShader +=
                "fragmentColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));";
        }
        else if (this.useColor) {
            this.fragmentShader +=
                "fragmentColor = vColor;";
        }
        else {
            this.fragmentShader +=
                "fragmentColor = vec4(0.5, 0.5, 0.5, 1.0);";
        }

        if (this.useVertexLighting) {
            this.fragmentShader +=
                "gl_FragColor = vec4(fragmentColor.rgb * vLightWeighting, fragmentColor.a);";
        } else if (this.useFragmentLighting) {
            this.fragmentShader +=
                "gl_FragColor = vec4(fragmentColor.rgb * lightWeighting, fragmentColor.a);";
        } else {
            this.fragmentShader +=
                "gl_FragColor = fragmentColor;";
        }
        this.fragmentShader += "}";
    };
};

Ayce.ShaderGenerator.prototype = {
    /**
     * Returns shader code for lighting calculation per vertex
     * @param {Number} lightsCount
     * @return {String} string
     */
    getVertexLightingWeightCalcArrayVertex: function(lightsCount, useSpecularLighting){
        var string = "";
        for(var i=0; i < lightsCount; i++){
            string +=
                "if(uLightIndex-"+(i+1)+".0>-0.01){" +
                "vec3 lightDirection = normalize(uPointLightingLocations["+i+"] - position.xyz);" +
                "vec3 reflectionDirection = reflect(-lightDirection, normal);" +
                "float diffuseLightWeighting = max(dot(normalize(transformedNormal), lightDirection), 0.0);";

            if(useSpecularLighting) {
                string +=
                    "float specularLightWeighting = pow(max(dot(reflectionDirection, eyeDirection), 0.0), uShininess);" +
                    "vLightWeighting += uPointLightingColors[" + i + "] * diffuseLightWeighting + uSpecularColors[" + i + "] * specularLightWeighting;";
            }else{
                string +=
                    "vLightWeighting += uPointLightingColors[" + i + "] * diffuseLightWeighting;";
            }
            string+=
                "}";
        }
        return string;
    },
    /**
     * Returns shader code for lighting calculation per fragment
     * @param {Number} lightsCount
     * @param {Boolean} useSpecularMap
     * @param {Boolean} useNormalMap
     * @param {Boolean} useSpecularLighting
     * @return {String} string
     */
    getFragmentLightingWeightCalcArrayVertex: function(lightsCount, useSpecularMap, useNormalMap, useSpecularLighting){
        var string = "";
        for(var i=0; i < lightsCount; i++){
            string += "" +
                "if(uLightIndex-"+(i+1)+".0>-0.01){" +
                "vec3 lightDirection = normalize(uPointLightingLocations["+i+"] - vPosition.xyz);" +
                "vec3 reflectionDirection = reflect(-lightDirection, normal);";
            if(useSpecularMap){
                string +=
                    "float shininess = texture2D(uSpecularMapSampler, 255.0-vec2(vTextureCoord.s, vTextureCoord.t)).r * 255.0 * uShininess;" +
                    "float specularLightWeighting = pow(max(dot(reflectionDirection, eyeDirection), 0.0), shininess);";
            }else {
                if(useSpecularLighting) {
                    string +=
                        "float specularLightWeighting = pow(max(dot(reflectionDirection, eyeDirection), 0.0), uShininess);";
                }
            }

            if(useNormalMap){
                string+=
                    "vec3 normalMap = texture2D(uNormalMapSampler, vec2(vTextureCoord.s, vTextureCoord.t)).rgb*2.0-1.0;" +
                    "float diffuseLightWeighting = max(dot(normalize(normalMap), lightDirection), 0.0);"
            }else{
                string+=
                    "float diffuseLightWeighting = max(dot(normalize(vTransformedNormal), lightDirection), 0.0);"
            }

            if(useSpecularLighting) {
                string +=
                    "lightWeighting += uPointLightingColors[" + i + "] * diffuseLightWeighting + uSpecularColors[" + i + "] * specularLightWeighting;";
            }else{
                string +=
                    "lightWeighting += uPointLightingColors[" + i + "] * diffuseLightWeighting;";
            }
            string+=
                "}";
        }
        return string;
    },
    /**
     * Returns shader code for handling multiple shaders
     * @param {Number} textureCount
     * @return {String} string
     */
    getTextureCalcArray: function(textureCount){
        var string = "";
        for(var i=0; i<textureCount; i++){
            if(i !== 0) string += "else ";
            string +=
                "if(i-"+i+".0 < 0.01){" +
                "tex = texture2D(uSampler["+i+"], vec2(vTextureCoord.s, vTextureCoord.t));" +
                "}";
        }
        return string;
    }

};