chromium/chrome/test/data/third_party/spaceport/js/sprites/renderers/webGLBatchDraw.js

define([ 'util/ensureCallback', 'sprites/canvas', 'sprites/webGL' ], function (ensureCallback, canvas, webGL) {
    var FLOATS_PER_VERT = 4;
    var VERTS_PER_SPRITE = 6;
    var FLOATS_PER_SPRITE = VERTS_PER_SPRITE * FLOATS_PER_VERT;

    function RenderContext(sourceData, frameData) {
        this.sourceData = sourceData;
        this.frameData = frameData;

        this.canvas = canvas();
        var gl = webGL.getContext(this.canvas);
        this.context = gl;

        this.program = webGL.shaders.batchSprite(gl);

        // Buffer data is interleaved:
        //
        // struct bufferUnit {
        //   vec2 aCoord;
        //   vec2 aTexCoord;  -- Constant
        // };
        //
        // sizeof(bufferUnit) == sizeof(float) * 4 == 16
        //
        // There are six of these structs per sprite
        // (one for each corner of the each triangle,
        // and two triangles per square).

        var maxSprites = Math.max.apply(Math, frameData.map(function (arr) {
            return arr.length;
        }));

        var bufferData = new Float32Array(maxSprites * FLOATS_PER_SPRITE);
        for (var i = 0, j = 0; i < maxSprites; ++i, j += FLOATS_PER_SPRITE) {
            // p0
            bufferData[j + 0*4 + 2] = 0;
            bufferData[j + 0*4 + 3] = 0;

            // p1
            bufferData[j + 1*4 + 2] = 1;
            bufferData[j + 1*4 + 3] = 0;

            // p2
            bufferData[j + 2*4 + 2] = 0;
            bufferData[j + 2*4 + 3] = 1;

            // p3
            bufferData[j + 3*4 + 2] = 1;
            bufferData[j + 3*4 + 3] = 0;

            // p4
            bufferData[j + 4*4 + 2] = 0;
            bufferData[j + 4*4 + 3] = 1;

            // p5
            bufferData[j + 5*4 + 2] = 1;
            bufferData[j + 5*4 + 3] = 1;
        }

        this.bufferData = bufferData;

        var buffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
        gl.bufferData(gl.ARRAY_BUFFER, bufferData, gl.DYNAMIC_DRAW);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        this.buffer = buffer;

        this.texture = webGL.makeTexture(gl, this.sourceData.img);

        gl.enable(gl.BLEND);
        gl.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD);
        gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.SRC_ALPHA, gl.ONE);
    }

    RenderContext.prototype.load = function load(callback) {
        callback = ensureCallback(callback);

        document.body.appendChild(this.canvas);

        this.clear();

        callback(null);
    };

    RenderContext.prototype.unload = function unload() {
        if (this.canvas.parentNode) {
            this.canvas.parentNode.removeChild(this.canvas);
        }
        
        var gl = this.context;
        gl.deleteTexture(this.texture);
        this.texture = null;
        
        gl.deleteProgram(this.program);
        this.program = null;
        
        gl.deleteBuffer(this.buffer);
        this.buffer = null;
        this.bufferData = null;
    };

    RenderContext.prototype.clear = function clear() {
        var gl = this.context;
        gl.viewport(0, 0, this.canvas.width, this.canvas.height);
        gl.clearColor(255, 255, 255, 255);
        gl.clear(gl.COLOR_BUFFER_BIT);
    };

    RenderContext.prototype.renderFrame = function renderFrame(frameIndex) {
        var gl = this.context;
        var sourceData = this.sourceData;

        var img = sourceData.img;
        var imgWidth = img.width;
        var imgHeight = img.height;

        var transforms = this.frameData[frameIndex];
        var count = transforms.length;

        var bufferData = this.bufferData;

        var i, j;
        for (i = 0, j = 0; i < count; ++i, j += FLOATS_PER_SPRITE) {
            var t = transforms[i];
            t.transformPointInto(0,        0,         bufferData, j + 0);
            t.transformPointInto(imgWidth, 0,         bufferData, j + 4);
            t.transformPointInto(0,        imgHeight, bufferData, j + 8);

            bufferData[j + 12] = bufferData[j + 4];
            bufferData[j + 13] = bufferData[j + 5];

            bufferData[j + 16] = bufferData[j + 8];
            bufferData[j + 17] = bufferData[j + 9];

            t.transformPointInto(imgWidth, imgHeight, bufferData, j + 20);
        }

        this.clear();

        var program = this.program;
        gl.useProgram(program);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
        gl.bufferData(gl.ARRAY_BUFFER, bufferData, gl.DYNAMIC_DRAW);

        gl.vertexAttribPointer(program.attr.coord, 2, gl.FLOAT, false, 16, 0);
        gl.vertexAttribPointer(program.attr.texCoord, 2, gl.FLOAT, false, 16, 8);
        gl.enableVertexAttribArray(program.attr.coord);
        gl.enableVertexAttribArray(program.attr.texCoord);

        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, this.texture);
        gl.uniform1i(program.uni.sampler, 0);

        gl.drawArrays(gl.TRIANGLES, 0, count * VERTS_PER_SPRITE);

        // Cleanup
        gl.disableVertexAttribArray(program.attr.coord);
        gl.disableVertexAttribArray(program.attr.texCoord);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        gl.useProgram(null);
    };

    return function (element, frameData) {
        return new RenderContext(element, frameData);
    };
});