/** * @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 WebGLPrimitiveBatch: * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/WebGLPrimitiveBatch.java */ /** * * @class WebGLPrimitiveBatch * @private * @constructor * @param gl {WebGLContext} the current WebGL drawing context * */ PIXI.WebGLPrimitiveBatch = 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); }; /** * * @method setContext * * @param gl {WebGLContext} the current WebGL drawing context */ PIXI.WebGLPrimitiveBatch.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.WebGLPrimitiveBatch.prototype.begin = function(renderSession) { this.renderSession = renderSession; this.shader = this.renderSession.shaderManager.defaultShader; this.start(); }; /** * * @method end * */ PIXI.WebGLPrimitiveBatch.prototype.end = function() { this.flush(); }; PIXI.WebGLPrimitiveBatch.prototype.render = function(sprite) { var texture = sprite.texture; // check texture.. if(texture.baseTexture !== this.currentBaseTexture || this.currentBatchSize >= this.size) { this.flush(); this.currentBaseTexture = texture.baseTexture; } // check blend mode if(sprite.blendMode !== this.currentBlendMode) { this.setBlendMode(sprite.blendMode); } // get the uvs for the texture var uvs = sprite._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.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 (sprite.texture.trim) { // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords.. var trim = sprite.texture.trim; w1 = trim.x - aX * trim.width; w0 = w1 + texture.frame.width; h1 = trim.y - aY * trim.height; h0 = h1 + texture.frame.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 the content and empties the current batch * * @method flush * */ PIXI.WebGLPrimitiveBatch.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; // bind the current texture gl.bindTexture(gl.TEXTURE_2D, this.currentBaseTexture._glTextures[gl.id] || PIXI.createWebGLTexture(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.WebGLPrimitiveBatch.prototype.stop = function() { this.flush(); }; /** * * @method start * */ PIXI.WebGLPrimitiveBatch.prototype.start = function() { var gl = this.gl; // 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); // set the blend mode.. if(this.currentBlendMode !== PIXI.blendModes.NORMAL) { this.setBlendMode(PIXI.blendModes.NORMAL); } }; /** * Sets-up the given blendMode from WebGL's point of view * @method setBlendMode * * @param blendMode {Number} the blendMode, should be a Pixi const, such as PIXI.BlendModes.ADD */ PIXI.WebGLPrimitiveBatch.prototype.setBlendMode = function(blendMode) { this.flush(); this.currentBlendMode = blendMode; var blendModeWebGL = PIXI.blendModesWebGL[this.currentBlendMode]; this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); }; /** * Destroys the SpriteBatch * @method destroy */ PIXI.WebGLPrimitiveBatch.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; };