Source: Renderer/createUniformArray.js

/*global define*/
define([
        '../Core/Cartesian2',
        '../Core/Cartesian3',
        '../Core/Cartesian4',
        '../Core/Color',
        '../Core/defined',
        '../Core/defineProperties',
        '../Core/DeveloperError',
        '../Core/Matrix2',
        '../Core/Matrix3',
        '../Core/Matrix4',
        '../Core/RuntimeError'
    ], function(
        Cartesian2,
        Cartesian3,
        Cartesian4,
        Color,
        defined,
        defineProperties,
        DeveloperError,
        Matrix2,
        Matrix3,
        Matrix4,
        RuntimeError) {
    'use strict';

    /**
     * @private
     */
    function createUniformArray(gl, activeUniform, uniformName, locations) {
        switch (activeUniform.type) {
            case gl.FLOAT:
                return new UniformArrayFloat(gl, activeUniform, uniformName, locations);
            case gl.FLOAT_VEC2:
                return new UniformArrayFloatVec2(gl, activeUniform, uniformName, locations);
            case gl.FLOAT_VEC3:
                return new UniformArrayFloatVec3(gl, activeUniform, uniformName, locations);
            case gl.FLOAT_VEC4:
                return new UniformArrayFloatVec4(gl, activeUniform, uniformName, locations);
            case gl.SAMPLER_2D:
            case gl.SAMPLER_CUBE:
                return new UniformArraySampler(gl, activeUniform, uniformName, locations);
            case gl.INT:
            case gl.BOOL:
                return new UniformArrayInt(gl, activeUniform, uniformName, locations);
            case gl.INT_VEC2:
            case gl.BOOL_VEC2:
                return new UniformArrayIntVec2(gl, activeUniform, uniformName, locations);
            case gl.INT_VEC3:
            case gl.BOOL_VEC3:
                return new UniformArrayIntVec3(gl, activeUniform, uniformName, locations);
            case gl.INT_VEC4:
            case gl.BOOL_VEC4:
                return new UniformArrayIntVec4(gl, activeUniform, uniformName, locations);
            case gl.FLOAT_MAT2:
                return new UniformArrayMat2(gl, activeUniform, uniformName, locations);
            case gl.FLOAT_MAT3:
                return new UniformArrayMat3(gl, activeUniform, uniformName, locations);
            case gl.FLOAT_MAT4:
                return new UniformArrayMat4(gl, activeUniform, uniformName, locations);
            default:
                throw new RuntimeError('Unrecognized uniform type: ' + activeUniform.type + ' for uniform "' + uniformName + '".');
        }
    }

    function UniformArrayFloat(gl, activeUniform, uniformName, locations) {
        var length = locations.length;

        /**
         * @readonly
         */
        this.name = uniformName;

        this.value = new Array(length);
        this._value = new Float32Array(length);

        this._gl = gl;
        this._location = locations[0];
    }

    UniformArrayFloat.prototype.set = function() {
        var value = this.value;
        var length = value.length;
        var arraybuffer = this._value;
        var changed = false;

        for (var i = 0; i < length; ++i) {
            var v = value[i];

            if (v !== arraybuffer[i]) {
                arraybuffer[i] = v;
                changed = true;
            }
        }

        if (changed) {
            this._gl.uniform1fv(this._location, arraybuffer);
        }
    };

    ///////////////////////////////////////////////////////////////////////////

    function UniformArrayFloatVec2(gl, activeUniform, uniformName, locations) {
        var length = locations.length;

        /**
         * @readonly
         */
        this.name = uniformName;

        this.value = new Array(length);
        this._value = new Float32Array(length * 2);

        this._gl = gl;
        this._location = locations[0];
    }

    UniformArrayFloatVec2.prototype.set = function() {
        var value = this.value;
        var length = value.length;
        var arraybuffer = this._value;
        var changed = false;
        var j = 0;

        for (var i = 0; i < length; ++i) {
            var v = value[i];

            if (!Cartesian2.equalsArray(v, arraybuffer, j)) {
                Cartesian2.pack(v, arraybuffer, j);
                changed = true;
            }
            j += 2;
        }

        if (changed) {
            this._gl.uniform2fv(this._location, arraybuffer);
        }
    };

    ///////////////////////////////////////////////////////////////////////////

    function UniformArrayFloatVec3(gl, activeUniform, uniformName, locations) {
        var length = locations.length;

        /**
         * @readonly
         */
        this.name = uniformName;

        this.value = new Array(length);
        this._value = new Float32Array(length * 3);

        this._gl = gl;
        this._location = locations[0];
    }

    UniformArrayFloatVec3.prototype.set = function() {
        var value = this.value;
        var length = value.length;
        var arraybuffer = this._value;
        var changed = false;
        var j = 0;

        for (var i = 0; i < length; ++i) {
            var v = value[i];

            if (defined(v.red)) {
                if ((v.red !== arraybuffer[j]) ||
                    (v.green !== arraybuffer[j + 1]) ||
                    (v.blue !== arraybuffer[j + 2])) {

                    arraybuffer[j] = v.red;
                    arraybuffer[j + 1] = v.green;
                    arraybuffer[j + 2] = v.blue;
                    changed = true;
                }
            } else if (defined(v.x)) {
                if (!Cartesian3.equalsArray(v, arraybuffer, j)) {
                    Cartesian3.pack(v, arraybuffer, j);
                    changed = true;
                }
            } else {
                //>>includeStart('debug', pragmas.debug);
                throw new DeveloperError('Invalid vec3 value.');
                //>>includeEnd('debug');
            }

            j += 3;
        }

        if (changed) {
            this._gl.uniform3fv(this._location, arraybuffer);
        }
    };

    ///////////////////////////////////////////////////////////////////////////

    function UniformArrayFloatVec4(gl, activeUniform, uniformName, locations) {
        var length = locations.length;

        /**
         * @readonly
         */
        this.name = uniformName;

        this.value = new Array(length);
        this._value = new Float32Array(length * 4);

        this._gl = gl;
        this._location = locations[0];
    }

    UniformArrayFloatVec4.prototype.set = function() {
        // PERFORMANCE_IDEA: if it is a common case that only a few elements
        // in a uniform array change, we could use heuristics to determine
        // when it is better to call uniform4f for each element that changed
        // vs. call uniform4fv once to set the entire array.  This applies
        // to all uniform array types, not just vec4.  We might not care
        // once we have uniform buffers since that will be the fast path.

        // PERFORMANCE_IDEA: Micro-optimization (I bet it works though):
        // As soon as changed is true, break into a separate loop that
        // does the copy without the equals check.

        var value = this.value;
        var length = value.length;
        var arraybuffer = this._value;
        var changed = false;
        var j = 0;

        for (var i = 0; i < length; ++i) {
            var v = value[i];

            if (defined(v.red)) {
                if (!Color.equalsArray(v, arraybuffer, j)) {
                    Color.pack(v, arraybuffer, j);
                    changed = true;
                }
            } else if (defined(v.x)) {
                if (!Cartesian4.equalsArray(v, arraybuffer, j)) {
                    Cartesian4.pack(v, arraybuffer, j);
                    changed = true;
                }
            } else {
                //>>includeStart('debug', pragmas.debug);
                throw new DeveloperError('Invalid vec4 value.');
                //>>includeEnd('debug');
            }

            j += 4;
        }

        if (changed) {
            this._gl.uniform4fv(this._location, arraybuffer);
        }
    };

    ///////////////////////////////////////////////////////////////////////////

    function UniformArraySampler(gl, activeUniform, uniformName, locations) {
        var length = locations.length;

        /**
         * @readonly
         */
        this.name = uniformName;

        this.value = new Array(length);
        this._value = new Float32Array(length);

        this._gl = gl;
        this._locations = locations;

        this.textureUnitIndex = undefined;
    }

    UniformArraySampler.prototype.set = function() {
        var gl = this._gl;
        var textureUnitIndex = gl.TEXTURE0 + this.textureUnitIndex;

        var value = this.value;
        var length = value.length;
        for (var i = 0; i < length; ++i) {
            var v = value[i];
            gl.activeTexture(textureUnitIndex + i);
            gl.bindTexture(v._target, v._texture);
        }
    };

    UniformArraySampler.prototype._setSampler = function(textureUnitIndex) {
        this.textureUnitIndex = textureUnitIndex;

        var locations = this._locations;
        var length = locations.length;
        for (var i = 0; i < length; ++i) {
            var index = textureUnitIndex + i;
            this._gl.uniform1i(locations[i], index);
        }

        return textureUnitIndex + length;
    };

    ///////////////////////////////////////////////////////////////////////////

    function UniformArrayInt(gl, activeUniform, uniformName, locations) {
        var length = locations.length;

        /**
         * @readonly
         */
        this.name = uniformName;

        this.value = new Array(length);
        this._value = new Int32Array(length);

        this._gl = gl;
        this._location = locations[0];
    }

    UniformArrayInt.prototype.set = function() {
        var value = this.value;
        var length = value.length;
        var arraybuffer = this._value;
        var changed = false;

        for (var i = 0; i < length; ++i) {
            var v = value[i];

            if (v !== arraybuffer[i]) {
                arraybuffer[i] = v;
                changed = true;
            }
        }

        if (changed) {
            this._gl.uniform1iv(this._location, arraybuffer);
        }
    };

    ///////////////////////////////////////////////////////////////////////////

    function UniformArrayIntVec2(gl, activeUniform, uniformName, locations) {
        var length = locations.length;

        /**
         * @readonly
         */
        this.name = uniformName;

        this.value = new Array(length);
        this._value = new Int32Array(length * 2);

        this._gl = gl;
        this._location = locations[0];
    }

    UniformArrayIntVec2.prototype.set = function() {
        var value = this.value;
        var length = value.length;
        var arraybuffer = this._value;
        var changed = false;
        var j = 0;

        for (var i = 0; i < length; ++i) {
            var v = value[i];

            if (!Cartesian2.equalsArray(v, arraybuffer, j)) {
                Cartesian2.pack(v, arraybuffer, j);
                changed = true;
            }
            j += 2;
        }

        if (changed) {
            this._gl.uniform2iv(this._location, arraybuffer);
        }
    };

    ///////////////////////////////////////////////////////////////////////////

    function UniformArrayIntVec3(gl, activeUniform, uniformName, locations) {
        var length = locations.length;

        /**
         * @readonly
         */
        this.name = uniformName;

        this.value = new Array(length);
        this._value = new Int32Array(length * 3);

        this._gl = gl;
        this._location = locations[0];
    }

    UniformArrayIntVec3.prototype.set = function() {
        var value = this.value;
        var length = value.length;
        var arraybuffer = this._value;
        var changed = false;
        var j = 0;

        for (var i = 0; i < length; ++i) {
            var v = value[i];

            if (!Cartesian3.equalsArray(v, arraybuffer, j)) {
                Cartesian3.pack(v, arraybuffer, j);
                changed = true;
            }
            j += 3;
        }

        if (changed) {
            this._gl.uniform3iv(this._location, arraybuffer);
        }
    };

    ///////////////////////////////////////////////////////////////////////////

    function UniformArrayIntVec4(gl, activeUniform, uniformName, locations) {
        var length = locations.length;

        /**
         * @readonly
         */
        this.name = uniformName;

        this.value = new Array(length);
        this._value = new Int32Array(length * 4);

        this._gl = gl;
        this._location = locations[0];
    }

    UniformArrayIntVec4.prototype.set = function() {
        var value = this.value;
        var length = value.length;
        var arraybuffer = this._value;
        var changed = false;
        var j = 0;

        for (var i = 0; i < length; ++i) {
            var v = value[i];

            if (!Cartesian4.equalsArray(v, arraybuffer, j)) {
                Cartesian4.pack(v, arraybuffer, j);
                changed = true;
            }
            j += 4;
        }

        if (changed) {
            this._gl.uniform4iv(this._location, arraybuffer);
        }
    };

    ///////////////////////////////////////////////////////////////////////////

    function UniformArrayMat2(gl, activeUniform, uniformName, locations) {
        var length = locations.length;

        /**
         * @readonly
         */
        this.name = uniformName;

        this.value = new Array(length);
        this._value = new Float32Array(length * 4);

        this._gl = gl;
        this._location = locations[0];
    }

    UniformArrayMat2.prototype.set = function() {
        var value = this.value;
        var length = value.length;
        var arraybuffer = this._value;
        var changed = false;
        var j = 0;

        for (var i = 0; i < length; ++i) {
            var v = value[i];

            if (!Matrix2.equalsArray(v, arraybuffer, j)) {
                Matrix2.pack(v, arraybuffer, j);
                changed = true;
            }
            j += 4;
        }

        if (changed) {
            this._gl.uniformMatrix2fv(this._location, false, arraybuffer);
        }
    };

    ///////////////////////////////////////////////////////////////////////////

    function UniformArrayMat3(gl, activeUniform, uniformName, locations) {
        var length = locations.length;

        /**
         * @readonly
         */
        this.name = uniformName;

        this.value = new Array(length);
        this._value = new Float32Array(length * 9);

        this._gl = gl;
        this._location = locations[0];
    }

    UniformArrayMat3.prototype.set = function() {
        var value = this.value;
        var length = value.length;
        var arraybuffer = this._value;
        var changed = false;
        var j = 0;

        for (var i = 0; i < length; ++i) {
            var v = value[i];

            if (!Matrix3.equalsArray(v, arraybuffer, j)) {
                Matrix3.pack(v, arraybuffer, j);
                changed = true;
            }
            j += 9;
        }

        if (changed) {
            this._gl.uniformMatrix3fv(this._location, false, arraybuffer);
        }
    };

    ///////////////////////////////////////////////////////////////////////////

    function UniformArrayMat4(gl, activeUniform, uniformName, locations) {
        var length = locations.length;

        /**
         * @readonly
         */
        this.name = uniformName;

        this.value = new Array(length);
        this._value = new Float32Array(length * 16);

        this._gl = gl;
        this._location = locations[0];
    }

    UniformArrayMat4.prototype.set = function() {
        var value = this.value;
        var length = value.length;
        var arraybuffer = this._value;
        var changed = false;
        var j = 0;

        for (var i = 0; i < length; ++i) {
            var v = value[i];

            if (!Matrix4.equalsArray(v, arraybuffer, j)) {
                Matrix4.pack(v, arraybuffer, j);
                changed = true;
            }
            j += 16;
        }

        if (changed) {
            this._gl.uniformMatrix4fv(this._location, false, arraybuffer);
        }
    };

    return createUniformArray;
});