#

API Docs for: 1.6.0
Show: Inherited Protected Private Deprecated

# File: src/pixi/renderers/webgl/utils/WebGLSpriteBatch.js

    /**
     * @author Mat Groves
     *
     * Big thanks to the very clever Matt DesLauriers <mattdesl> https://github.com/mattdesl/
     * for creating the original pixi version!
     *
     * Heavily inspired by LibGDX's WebGLSpriteBatch:
     * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/WebGLSpriteBatch.java
     */

     /**
     *
     * @class WebGLSpriteBatch
     * @private
     * @constructor
     * @param gl {WebGLContext} the current WebGL drawing context
     *
     */
    PIXI.WebGLSpriteBatch = function(gl)
    {

        /**
         *
         *
         * @property vertSize
         * @type Number
         */
        this.vertSize = 6;

        /**
         * The number of images in the SpriteBatch before it flushes
         * @property size
         * @type Number
         */
        this.size = 2000;//Math.pow(2, 16) /  this.vertSize;

        //the total number of floats in our batch
        var numVerts = this.size * 4 *  this.vertSize;
        //the total number of indices in our batch
        var numIndices = this.size * 6;

        //vertex data

        /**
        * Holds the vertices
        *
        * @property vertices
        * @type Float32Array
        */
        this.vertices = new Float32Array(numVerts);

        //index data
        /**
         * Holds the indices
         *
         * @property indices
         * @type Uint16Array
         */
        this.indices = new Uint16Array(numIndices);

        this.lastIndexCount = 0;

        for (var i=0, j=0; i < numIndices; i += 6, j += 4)
        {
            this.indices[i + 0] = j + 0;
            this.indices[i + 1] = j + 1;
            this.indices[i + 2] = j + 2;
            this.indices[i + 3] = j + 0;
            this.indices[i + 4] = j + 2;
            this.indices[i + 5] = j + 3;
        }

        this.drawing = false;
        this.currentBatchSize = 0;
        this.currentBaseTexture = null;

        this.setContext(gl);

        this.dirty = true;
    };

    /**
    *
    * @method setContext
    *
    * @param gl {WebGLContext} the current WebGL drawing context
    */
    PIXI.WebGLSpriteBatch.prototype.setContext = function(gl)
    {
        this.gl = gl;

        // create a couple of buffers
        this.vertexBuffer = gl.createBuffer();
        this.indexBuffer = gl.createBuffer();

        // 65535 is max index, so 65535 / 6 = 10922.

        //upload the index data
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW);

        this.currentBlendMode = 99999;
    };

    /**
    *
    * @method begin
    *
    * @param renderSession {RenderSession} the RenderSession
    */
    PIXI.WebGLSpriteBatch.prototype.begin = function(renderSession)
    {
        this.renderSession = renderSession;
        this.shader = this.renderSession.shaderManager.defaultShader;

        this.start();
    };

    /**
    *
    * @method end
    *
    */
    PIXI.WebGLSpriteBatch.prototype.end = function()
    {
        this.flush();
    };

    /**
    *
    * @method render
    *
    * @param sprite {Sprite} the sprite to render when using this spritebatch
    */
    PIXI.WebGLSpriteBatch.prototype.render = function(sprite)
    {
        var texture = sprite.texture;

        var blendChange = this.renderSession.blendModeManager.currentBlendMode !== sprite.blendMode;

        // check texture..
        if(texture.baseTexture !== this.currentBaseTexture || this.currentBatchSize >= this.size || blendChange)
        {
            this.flush();
            this.currentBaseTexture = texture.baseTexture;
            this.renderSession.blendModeManager.setBlendMode(sprite.blendMode);
        }

        // get the uvs for the texture
        var uvs = texture._uvs;
        // if the uvs have not updated then no point rendering just yet!
        if(!uvs)return;

        // get the sprites current alpha
        var alpha = sprite.worldAlpha;
        var tint = sprite.tint;

        var verticies = this.vertices;

        // TODO trim??
        var aX = sprite.anchor.x;
        var aY = sprite.anchor.y;

        var w0, w1, h0, h1;

        if (texture.trim)
        {
            // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords..
            var trim = texture.trim;

            w1 = trim.x - aX * trim.width;
            w0 = w1 + texture.crop.width;

            h1 = trim.y - aY * trim.height;
            h0 = h1 + texture.crop.height;

        }
        else
        {
            w0 = (texture.frame.width ) * (1-aX);
            w1 = (texture.frame.width ) * -aX;

            h0 = texture.frame.height * (1-aY);
            h1 = texture.frame.height * -aY;
        }

        var index = this.currentBatchSize * 4 * this.vertSize;

        var worldTransform = sprite.worldTransform;//.toArray();

        var a = worldTransform.a;//[0];
        var b = worldTransform.c;//[3];
        var c = worldTransform.b;//[1];
        var d = worldTransform.d;//[4];
        var tx = worldTransform.tx;//[2];
        var ty = worldTransform.ty;///[5];

        // xy
        verticies[index++] = a * w1 + c * h1 + tx;
        verticies[index++] = d * h1 + b * w1 + ty;
        // uv
        verticies[index++] = uvs.x0;
        verticies[index++] = uvs.y0;
        // color
        verticies[index++] = alpha;
        verticies[index++] = tint;

        // xy
        verticies[index++] = a * w0 + c * h1 + tx;
        verticies[index++] = d * h1 + b * w0 + ty;
        // uv
        verticies[index++] = uvs.x1;
        verticies[index++] = uvs.y1;
        // color
        verticies[index++] = alpha;
        verticies[index++] = tint;

        // xy
        verticies[index++] = a * w0 + c * h0 + tx;
        verticies[index++] = d * h0 + b * w0 + ty;
        // uv
        verticies[index++] = uvs.x2;
        verticies[index++] = uvs.y2;
        // color
        verticies[index++] = alpha;
        verticies[index++] = tint;

        // xy
        verticies[index++] = a * w1 + c * h0 + tx;
        verticies[index++] = d * h0 + b * w1 + ty;
        // uv
        verticies[index++] = uvs.x3;
        verticies[index++] = uvs.y3;
        // color
        verticies[index++] = alpha;
        verticies[index++] = tint;

        // increment the batchsize
        this.currentBatchSize++;

    };

    /**
    * Renders a tilingSprite using the spriteBatch
    * @method renderTilingSprite
    *
    * @param sprite {TilingSprite} the tilingSprite to render
    */
    PIXI.WebGLSpriteBatch.prototype.renderTilingSprite = function(tilingSprite)
    {
        var texture = tilingSprite.tilingTexture;

        var blendChange = this.renderSession.blendModeManager.currentBlendMode !== tilingSprite.blendMode;

        // check texture..
        if(texture.baseTexture !== this.currentBaseTexture || this.currentBatchSize >= this.size || blendChange)
        {
            this.flush();
            this.currentBaseTexture = texture.baseTexture;
            this.renderSession.blendModeManager.setBlendMode(tilingSprite.blendMode);
        }

         // set the textures uvs temporarily
        // TODO create a separate texture so that we can tile part of a texture

        if(!tilingSprite._uvs)tilingSprite._uvs = new PIXI.TextureUvs();

        var uvs = tilingSprite._uvs;

        tilingSprite.tilePosition.x %= texture.baseTexture.width * tilingSprite.tileScaleOffset.x;
        tilingSprite.tilePosition.y %= texture.baseTexture.height * tilingSprite.tileScaleOffset.y;

        var offsetX =  tilingSprite.tilePosition.x/(texture.baseTexture.width*tilingSprite.tileScaleOffset.x);
        var offsetY =  tilingSprite.tilePosition.y/(texture.baseTexture.height*tilingSprite.tileScaleOffset.y);

        var scaleX =  (tilingSprite.width / texture.baseTexture.width)  / (tilingSprite.tileScale.x * tilingSprite.tileScaleOffset.x);
        var scaleY =  (tilingSprite.height / texture.baseTexture.height) / (tilingSprite.tileScale.y * tilingSprite.tileScaleOffset.y);

        uvs.x0 = 0 - offsetX;
        uvs.y0 = 0 - offsetY;

        uvs.x1 = (1 * scaleX) - offsetX;
        uvs.y1 = 0 - offsetY;

        uvs.x2 = (1 * scaleX) - offsetX;
        uvs.y2 = (1 * scaleY) - offsetY;

        uvs.x3 = 0 - offsetX;
        uvs.y3 = (1 *scaleY) - offsetY;

        // get the tilingSprites current alpha
        var alpha = tilingSprite.worldAlpha;
        var tint = tilingSprite.tint;

        var  verticies = this.vertices;

        var width = tilingSprite.width;
        var height = tilingSprite.height;

        // TODO trim??
        var aX = tilingSprite.anchor.x; // - tilingSprite.texture.trim.x
        var aY = tilingSprite.anchor.y; //- tilingSprite.texture.trim.y
        var w0 = width * (1-aX);
        var w1 = width * -aX;

        var h0 = height * (1-aY);
        var h1 = height * -aY;

        var index = this.currentBatchSize * 4 * this.vertSize;

        var worldTransform = tilingSprite.worldTransform;

        var a = worldTransform.a;//[0];
        var b = worldTransform.c;//[3];
        var c = worldTransform.b;//[1];
        var d = worldTransform.d;//[4];
        var tx = worldTransform.tx;//[2];
        var ty = worldTransform.ty;///[5];

        // xy
        verticies[index++] = a * w1 + c * h1 + tx;
        verticies[index++] = d * h1 + b * w1 + ty;
        // uv
        verticies[index++] = uvs.x0;
        verticies[index++] = uvs.y0;
        // color
        verticies[index++] = alpha;
        verticies[index++] = tint;

        // xy
        verticies[index++] = a * w0 + c * h1 + tx;
        verticies[index++] = d * h1 + b * w0 + ty;
        // uv
        verticies[index++] = uvs.x1;
        verticies[index++] = uvs.y1;
        // color
        verticies[index++] = alpha;
        verticies[index++] = tint;

        // xy
        verticies[index++] = a * w0 + c * h0 + tx;
        verticies[index++] = d * h0 + b * w0 + ty;
        // uv
        verticies[index++] = uvs.x2;
        verticies[index++] = uvs.y2;
        // color
        verticies[index++] = alpha;
        verticies[index++] = tint;

        // xy
        verticies[index++] = a * w1 + c * h0 + tx;
        verticies[index++] = d * h0 + b * w1 + ty;
        // uv
        verticies[index++] = uvs.x3;
        verticies[index++] = uvs.y3;
        // color
        verticies[index++] = alpha;
        verticies[index++] = tint;

        // increment the batchs
        this.currentBatchSize++;
    };

    /**
    * Renders the content and empties the current batch
    *
    * @method flush
    *
    */
    PIXI.WebGLSpriteBatch.prototype.flush = function()
    {
        // If the batch is length 0 then return as there is nothing to draw
        if (this.currentBatchSize===0)return;

        var gl = this.gl;

        this.renderSession.shaderManager.setShader(this.renderSession.shaderManager.defaultShader);

        //TODO - im usre this can be done better - will look to tweak this for 1.7..
        if(this.dirty)
        {
            this.dirty = false;
            // bind the main texture
            gl.activeTexture(gl.TEXTURE0);

            // bind the buffers
            gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);

            // set the projection
            var projection = this.renderSession.projection;
            gl.uniform2f(this.shader.projectionVector, projection.x, projection.y);

            // set the pointers
            var stride =  this.vertSize * 4;
            gl.vertexAttribPointer(this.shader.aVertexPosition, 2, gl.FLOAT, false, stride, 0);
            gl.vertexAttribPointer(this.shader.aTextureCoord, 2, gl.FLOAT, false, stride, 2 * 4);
            gl.vertexAttribPointer(this.shader.colorAttribute, 2, gl.FLOAT, false, stride, 4 * 4);

        }

        // bind the current texture
        gl.bindTexture(gl.TEXTURE_2D, this.currentBaseTexture._glTextures[gl.id] || PIXI.createWebGLTexture(this.currentBaseTexture, gl));

        // check if a texture is dirty..
        if(this.currentBaseTexture._dirty[gl.id])
        {
            PIXI.updateWebGLTexture(this.currentBaseTexture, gl);
        }

        // upload the verts to the buffer
        if(this.currentBatchSize > ( this.size * 0.5 ) )
        {
            gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices);
        }
        else
        {
            var view = this.vertices.subarray(0, this.currentBatchSize * 4 * this.vertSize);

            gl.bufferSubData(gl.ARRAY_BUFFER, 0, view);
        }

       // var view = this.vertices.subarray(0, this.currentBatchSize * 4 * this.vertSize);
        //gl.bufferSubData(gl.ARRAY_BUFFER, 0, view);

        // now draw those suckas!
        gl.drawElements(gl.TRIANGLES, this.currentBatchSize * 6, gl.UNSIGNED_SHORT, 0);

        // then reset the batch!
        this.currentBatchSize = 0;

        // increment the draw count
        this.renderSession.drawCount++;
    };

    /**
    *
    * @method stop
    *
    */
    PIXI.WebGLSpriteBatch.prototype.stop = function()
    {
        this.flush();
    };

    /**
    *
    * @method start
    *
    */
    PIXI.WebGLSpriteBatch.prototype.start = function()
    {
        this.dirty = true;
    };

    /**
    * Destroys the SpriteBatch
    * @method destroy
    */
    PIXI.WebGLSpriteBatch.prototype.destroy = function()
    {

        this.vertices = null;
        this.indices = null;

        this.gl.deleteBuffer( this.vertexBuffer );
        this.gl.deleteBuffer( this.indexBuffer );

        this.currentBaseTexture = null;

        this.gl = null;
    };