/**
* @author Matt DesLauriers <mattdesl> https://github.com/mattdesl/
*
* Heavily inspired by LibGDX's WebGLSpriteBatch:
* https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/WebGLSpriteBatch.java
*/
PIXI.WebGLSpriteBatch = function(gl)
{
this.gl = gl;
// create a couple of buffers
this.vertexBuffer = gl.createBuffer();
this.indexBuffer = gl.createBuffer();
this.size = 2000;
// 65535 is max index, so 65535 / 6 = 10922.
//the total number of floats in our batch
var numVerts = this.size * 4 * 5;
//the total number of indices in our batch
var numIndices = this.size * 6;
//TODO: use properties here
//current blend mode.. changing it flushes the batch
this.blendMode = PIXI.blendModes.NORMAL;
//vertex data
this.vertices = new Float32Array(numVerts);
//index data
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;
};
//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.drawing = false;
this.currentBatchSize = 0;
this.currentBaseTexture;
}
PIXI.WebGLSpriteBatch.prototype.begin = function(renderSession)
{
this.renderSession = renderSession;
var gl = this.gl;
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
var stride = 5 * 4;
var projection = renderSession.projection;
gl.uniform2f(PIXI.defaultShader.projectionVector, projection.x, projection.y);
gl.vertexAttribPointer(PIXI.defaultShader.aVertexPosition, 2, gl.FLOAT, false, stride, 0);
gl.vertexAttribPointer(PIXI.defaultShader.aTextureCoord, 2, gl.FLOAT, false, stride, 2 * 4);
gl.vertexAttribPointer(PIXI.defaultShader.colorAttribute, 1, gl.FLOAT, false, stride, 4 * 4);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
}
PIXI.WebGLSpriteBatch.prototype.end = function()
{
this.flush();
}
PIXI.WebGLSpriteBatch.prototype.render = function(sprite)
{
if(sprite.texture.baseTexture !== this.currentBaseTexture || this.currentBatchSize >= this.size)
{
this.flush();
this.currentBaseTexture = sprite.texture.baseTexture;
}
// get the uvs for the texture
var uvs = sprite.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.alpha;
var verticies = this.vertices;
width = sprite.texture.frame.width;
height = sprite.texture.frame.height;
// TODO trim??
var aX = sprite.anchor.x; // - sprite.texture.trim.x
var aY = sprite.anchor.y; //- sprite.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 * 5;
worldTransform = sprite.worldTransform;
var a = worldTransform[0];
var b = worldTransform[3];
var c = worldTransform[1];
var d = worldTransform[4];
var tx = worldTransform[2];
var ty = worldTransform[5];
// xy
verticies[index++] = a * w1 + c * h1 + tx;
verticies[index++] = d * h1 + b * w1 + ty;
// uv
verticies[index++] = uvs[0];
verticies[index++] = uvs[1];
// color
verticies[index++] = alpha;
// xy
verticies[index++] = a * w0 + c * h1 + tx;
verticies[index++] = d * h1 + b * w0 + ty;
// uv
verticies[index++] = uvs[2];
verticies[index++] = uvs[3];
// color
verticies[index++] = alpha;
// xy
verticies[index++] = a * w0 + c * h0 + tx;
verticies[index++] = d * h0 + b * w0 + ty;
// uv
verticies[index++] = uvs[4];
verticies[index++] = uvs[5];
// color
verticies[index++] = alpha;
// xy
verticies[index++] = a * w1 + c * h0 + tx;
verticies[index++] = d * h0 + b * w1 + ty;
// uv
verticies[index++] = uvs[6];
verticies[index++] = uvs[7];
// color
verticies[index++] = alpha;
// increment the batchs
this.currentBatchSize++;
}
PIXI.WebGLSpriteBatch.prototype.renderTilingSprite = function(tilingSprite)
{
var texture = tilingSprite.texture;
// set the textures uvs temporarily
// TODO create a seperate texture so that we can tile part of a texture
var tempUvs = texture._uvs;
if(!tilingSprite._uvs)tilingSprite._uvs = new Float32Array(8);
var uvs = tilingSprite._uvs;
var offsetX = tilingSprite.tilePosition.x/texture.baseTexture.width;
var offsetY = tilingSprite.tilePosition.y/texture.baseTexture.height;
var scaleX = (tilingSprite.width / texture.baseTexture.width) / tilingSprite.tileScale.x;
var scaleY = (tilingSprite.height / texture.baseTexture.height) / tilingSprite.tileScale.y;
uvs[0] = 0 - offsetX;
uvs[1] = 0 - offsetY;
uvs[2] = (1 * scaleX) - offsetX;
uvs[3] = 0 - offsetY;
uvs[4] = (1 * scaleX) - offsetX;
uvs[5] = (1 * scaleY) - offsetY;
uvs[6] = 0 - offsetX;
uvs[7] = (1 *scaleY) - offsetY;
texture._uvs = uvs;
this.render(tilingSprite);
tilingSprite.texture._uvs = tempUvs;
}
PIXI.WebGLSpriteBatch.prototype.flush = function()
{
// first draw
if (this.currentBatchSize===0)return;
var gl = this.gl;
//setup our vertex attributes & binds textures
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this.currentBaseTexture._glTexture);
// bind the index
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
//bind our vertex buffer
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
// Only update a region of the buffer. On my computer
// this is faster (especially if you are not filling the entire batch)
// but it could do with more testing. In theory it SHOULD be faster
// since bufferData allocates memory, whereas this should not.
var view = this.vertices.subarray(0, this.currentBatchSize * 4 * 5);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, view);
gl.drawElements(gl.TRIANGLES, this.currentBatchSize * 6, gl.UNSIGNED_SHORT, 0);
// then reset!
this.currentBatchSize = 0;
this.renderSession.drawCount++;
}
PIXI.WebGLSpriteBatch.prototype.stop = function()
{
this.flush();
}
PIXI.WebGLSpriteBatch.prototype.start = function()
{
var gl = this.gl;
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
var stride = 5 * 4;
var projection = this.renderSession.projection;
gl.uniform2f(PIXI.defaultShader.projectionVector, projection.x, projection.y);
gl.vertexAttribPointer(PIXI.defaultShader.aVertexPosition, 2, gl.FLOAT, false, stride, 0);
gl.vertexAttribPointer(PIXI.defaultShader.aTextureCoord, 2, gl.FLOAT, false, stride, 2 * 4);
gl.vertexAttribPointer(PIXI.defaultShader.colorAttribute, 1, gl.FLOAT, false, stride, 4 * 4);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
}