diff --git a/src/core/sprites/SpriteBatch.js b/src/core/sprites/SpriteBatch.js index 360bd67..4238b4c 100644 --- a/src/core/sprites/SpriteBatch.js +++ b/src/core/sprites/SpriteBatch.js @@ -60,6 +60,9 @@ } renderer.setObjectRenderer( renderer.plugins.spriteBatch ); + + + renderer.plugins.spriteBatch.render( this ); }; diff --git a/src/core/sprites/SpriteBatch.js b/src/core/sprites/SpriteBatch.js index 360bd67..4238b4c 100644 --- a/src/core/sprites/SpriteBatch.js +++ b/src/core/sprites/SpriteBatch.js @@ -60,6 +60,9 @@ } renderer.setObjectRenderer( renderer.plugins.spriteBatch ); + + + renderer.plugins.spriteBatch.render( this ); }; diff --git a/src/core/sprites/webgl/SpriteBatchBuffers.js b/src/core/sprites/webgl/SpriteBatchBuffers.js new file mode 100644 index 0000000..e28daf5 --- /dev/null +++ b/src/core/sprites/webgl/SpriteBatchBuffers.js @@ -0,0 +1,317 @@ + +/** + * @author Mat Groves + * + * Big thanks to the very clever Matt DesLauriers https://github.com/mattdesl/ + * for creating the original pixi version! + * Also a thanks to https://github.com/bchevalier for tweaking the tint and alpha so that they now share 4 bytes on the vertex buffer + * + * Heavily inspired by LibGDX's SpriteBatchBuffers: + * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/SpriteBatchBuffers.java + */ + +/** + * + * @class + * @private + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this sprite batch works for. + */ +function SpriteBatchBuffers(gl, size ) +{ + this.gl = gl; + + /** + * + * + * @member {number} + */ + this.vertSize = 2; + + /** + * + * + * @member {number} + */ + this.vertByteSize = this.vertSize * 4; + + /** + * The number of images in the SpriteBatch before it flushes. + * + * @member {number} + */ + this.size = size; + + // the total number of bytes in our batch + var numVerts = this.size * 2 * this.vertByteSize; + + /** + * Holds the vertices + * + * @member {ArrayBuffer} + */ + this.vertices = new Float32Array(numVerts); + this.position = new Float32Array(numVerts); + this.rotation = new Float32Array(numVerts/2); + this.uvs = new Float32Array(numVerts); + this.alpha = new Float32Array(numVerts/2); + + this.initBuffers(); + + +} + +SpriteBatchBuffers.prototype.constructor = SpriteBatchBuffers; +module.exports = SpriteBatchBuffers; + +/** + * Sets up the renderer context and necessary buffers. + * + * @private + * @param gl {WebGLContext} the current WebGL drawing context + */ +SpriteBatchBuffers.prototype.initBuffers = function () +{ + var gl = this.gl; + + // create a couple of buffers + this.vertexBuffer = gl.createBuffer(); + this.positionBuffer = gl.createBuffer(); + this.rotationBuffer = gl.createBuffer(); + this.uvsBuffer = gl.createBuffer(); + this.alphaBuffer = gl.createBuffer(); + + // upload the buffers.. + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.position, gl.DYNAMIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.rotationBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvsBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.uvs, gl.DYNAMIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.alphaBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.alpha, gl.DYNAMIC_DRAW); +}; + + +SpriteBatchBuffers.prototype.refresh = function(children, startIndex, amount) +{ + this.uploadVerticies(children,startIndex, amount); + this.uploadRotation(children,startIndex, amount); + this.uploadUvs(children,startIndex, amount); + this.uploadAlpha(children,startIndex, amount); + +}; + +SpriteBatchBuffers.prototype.uploadVerticies = function (children,startIndex, amount) +{ + var vertices = this.vertices, + index = 0, + sprite, + texture, + trim, + sx, + sy, + w0, w1, h0, h1; + + for (var i = 0; i < amount; i++) { + + sprite = children[startIndex + i]; + texture = sprite._texture; + sx = sprite.scale.x; + sy = sprite.scale.y; + + if (texture.trim) + { + // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords.. + trim = texture.trim; + + w1 = trim.x - sprite.anchor.x * trim.width; + w0 = w1 + texture.crop.width; + + h1 = trim.y - sprite.anchor.y * trim.height; + h0 = h1 + texture.crop.height; + } + else + { + w0 = (texture._frame.width ) * (1-sprite.anchor.x); + w1 = (texture._frame.width ) * -sprite.anchor.x; + + h0 = texture._frame.height * (1-sprite.anchor.y); + h1 = texture._frame.height * -sprite.anchor.y; + } + + vertices[index++] = w1 * sx; + vertices[index++] = h1 * sy; + + vertices[index++] = w0 * sx; + vertices[index++] = h1 * sy; + + vertices[index++] = w0 * sx; + vertices[index++] = h0 * sy; + + vertices[index++] = w1 * sx; + vertices[index++] = h0 * sy; + } + + this.uploadToBuffer(this.vertexBuffer, this.vertices, 2 * 4, amount); +}; + +SpriteBatchBuffers.prototype.uploadPosition = function (children,startIndex, amount) +{ + var position = this.position, + index = 0, + spritePosition; + + for (var i = 0; i < amount; i++) { + + spritePosition = children[startIndex + i].position; + + position[index++] = spritePosition.x; + position[index++] = spritePosition.y; + + position[index++] = spritePosition.x; + position[index++] = spritePosition.y; + + position[index++] = spritePosition.x; + position[index++] = spritePosition.y; + + position[index++] = spritePosition.x; + position[index++] = spritePosition.y; + } + + + // upload the verts to the buffer + this.uploadToBuffer(this.positionBuffer, this.position, 2 * 4, amount); +}; + +SpriteBatchBuffers.prototype.uploadRotation = function (children,startIndex, amount) +{ + var rotation = this.rotation, + index = 0, + spriteRotation; + + for (var i = 0; i < amount; i++) { + + spriteRotation = children[startIndex + i].rotation; + + rotation[index++] = spriteRotation; + rotation[index++] = spriteRotation; + rotation[index++] = spriteRotation; + rotation[index++] = spriteRotation; + } + + this.uploadToBuffer(this.rotationBuffer, this.rotation, 1 * 4, amount); +}; + +SpriteBatchBuffers.prototype.uploadUvs = function (children,startIndex, amount) +{ + var uvs = this.uvs, + index = 0, + textureUvs; + + for (var i = 0; i < amount; i++) { + + textureUvs = children[startIndex + i]._texture._uvs; + + if(textureUvs) + { + uvs[index++] = textureUvs.x0; + uvs[index++] = textureUvs.y0; + + uvs[index++] = textureUvs.x1; + uvs[index++] = textureUvs.y1; + + uvs[index++] = textureUvs.x2; + uvs[index++] = textureUvs.y2; + + uvs[index++] = textureUvs.x3; + uvs[index++] = textureUvs.y3; + } + else + { + index += 8; + } + } + + this.uploadToBuffer(this.uvsBuffer, this.uvs, 2 * 4, amount); +}; + +SpriteBatchBuffers.prototype.uploadAlpha = function (children,startIndex, amount) +{ + var alpha = this.alpha, + index = 0, + spriteAlpha; + + for (var i = 0; i < amount; i++) { + + spriteAlpha = children[startIndex + i].alpha; + + alpha[index++] = spriteAlpha; + alpha[index++] = spriteAlpha; + alpha[index++] = spriteAlpha; + alpha[index++] = spriteAlpha; + } + + + this.uploadToBuffer(this.alphaBuffer, this.alpha, 1 * 4, amount); + +}; + +SpriteBatchBuffers.prototype.uploadToBuffer = function (buffer, data, dataSize, amount) +{ + var gl = this.gl; + + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + + if (this.currentBatchSize > ( this.size * 0.5 ) ) + { + gl.bufferSubData(gl.ARRAY_BUFFER, 0, data); + } + else + { + var view = data.subarray(0, amount * dataSize); + gl.bufferSubData(gl.ARRAY_BUFFER, 0, view); + } +}; + +/** + * Starts a new sprite batch. + * + */ +SpriteBatchBuffers.prototype.bind = function (shader) +{ + var gl = this.gl; + + // this is the same for each shader? + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.vertexAttribPointer(shader.attributes.aVertexPosition, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer); + gl.vertexAttribPointer(shader.attributes.aPositionCoord, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.rotationBuffer); + gl.vertexAttribPointer(shader.attributes.aRotation, 1, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvsBuffer); + gl.vertexAttribPointer(shader.attributes.aTextureCoord, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.alphaBuffer); + gl.vertexAttribPointer(shader.attributes.aColor, 1, gl.FLOAT, false, 0, 0); + +}; + +/** + * Destroys the SpriteBatch. + * + */ +SpriteBatchBuffers.prototype.destroy = function () +{ + //TODO implement this :) to busy making the fun bits.. +}; diff --git a/src/core/sprites/SpriteBatch.js b/src/core/sprites/SpriteBatch.js index 360bd67..4238b4c 100644 --- a/src/core/sprites/SpriteBatch.js +++ b/src/core/sprites/SpriteBatch.js @@ -60,6 +60,9 @@ } renderer.setObjectRenderer( renderer.plugins.spriteBatch ); + + + renderer.plugins.spriteBatch.render( this ); }; diff --git a/src/core/sprites/webgl/SpriteBatchBuffers.js b/src/core/sprites/webgl/SpriteBatchBuffers.js new file mode 100644 index 0000000..e28daf5 --- /dev/null +++ b/src/core/sprites/webgl/SpriteBatchBuffers.js @@ -0,0 +1,317 @@ + +/** + * @author Mat Groves + * + * Big thanks to the very clever Matt DesLauriers https://github.com/mattdesl/ + * for creating the original pixi version! + * Also a thanks to https://github.com/bchevalier for tweaking the tint and alpha so that they now share 4 bytes on the vertex buffer + * + * Heavily inspired by LibGDX's SpriteBatchBuffers: + * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/SpriteBatchBuffers.java + */ + +/** + * + * @class + * @private + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this sprite batch works for. + */ +function SpriteBatchBuffers(gl, size ) +{ + this.gl = gl; + + /** + * + * + * @member {number} + */ + this.vertSize = 2; + + /** + * + * + * @member {number} + */ + this.vertByteSize = this.vertSize * 4; + + /** + * The number of images in the SpriteBatch before it flushes. + * + * @member {number} + */ + this.size = size; + + // the total number of bytes in our batch + var numVerts = this.size * 2 * this.vertByteSize; + + /** + * Holds the vertices + * + * @member {ArrayBuffer} + */ + this.vertices = new Float32Array(numVerts); + this.position = new Float32Array(numVerts); + this.rotation = new Float32Array(numVerts/2); + this.uvs = new Float32Array(numVerts); + this.alpha = new Float32Array(numVerts/2); + + this.initBuffers(); + + +} + +SpriteBatchBuffers.prototype.constructor = SpriteBatchBuffers; +module.exports = SpriteBatchBuffers; + +/** + * Sets up the renderer context and necessary buffers. + * + * @private + * @param gl {WebGLContext} the current WebGL drawing context + */ +SpriteBatchBuffers.prototype.initBuffers = function () +{ + var gl = this.gl; + + // create a couple of buffers + this.vertexBuffer = gl.createBuffer(); + this.positionBuffer = gl.createBuffer(); + this.rotationBuffer = gl.createBuffer(); + this.uvsBuffer = gl.createBuffer(); + this.alphaBuffer = gl.createBuffer(); + + // upload the buffers.. + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.position, gl.DYNAMIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.rotationBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvsBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.uvs, gl.DYNAMIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.alphaBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.alpha, gl.DYNAMIC_DRAW); +}; + + +SpriteBatchBuffers.prototype.refresh = function(children, startIndex, amount) +{ + this.uploadVerticies(children,startIndex, amount); + this.uploadRotation(children,startIndex, amount); + this.uploadUvs(children,startIndex, amount); + this.uploadAlpha(children,startIndex, amount); + +}; + +SpriteBatchBuffers.prototype.uploadVerticies = function (children,startIndex, amount) +{ + var vertices = this.vertices, + index = 0, + sprite, + texture, + trim, + sx, + sy, + w0, w1, h0, h1; + + for (var i = 0; i < amount; i++) { + + sprite = children[startIndex + i]; + texture = sprite._texture; + sx = sprite.scale.x; + sy = sprite.scale.y; + + if (texture.trim) + { + // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords.. + trim = texture.trim; + + w1 = trim.x - sprite.anchor.x * trim.width; + w0 = w1 + texture.crop.width; + + h1 = trim.y - sprite.anchor.y * trim.height; + h0 = h1 + texture.crop.height; + } + else + { + w0 = (texture._frame.width ) * (1-sprite.anchor.x); + w1 = (texture._frame.width ) * -sprite.anchor.x; + + h0 = texture._frame.height * (1-sprite.anchor.y); + h1 = texture._frame.height * -sprite.anchor.y; + } + + vertices[index++] = w1 * sx; + vertices[index++] = h1 * sy; + + vertices[index++] = w0 * sx; + vertices[index++] = h1 * sy; + + vertices[index++] = w0 * sx; + vertices[index++] = h0 * sy; + + vertices[index++] = w1 * sx; + vertices[index++] = h0 * sy; + } + + this.uploadToBuffer(this.vertexBuffer, this.vertices, 2 * 4, amount); +}; + +SpriteBatchBuffers.prototype.uploadPosition = function (children,startIndex, amount) +{ + var position = this.position, + index = 0, + spritePosition; + + for (var i = 0; i < amount; i++) { + + spritePosition = children[startIndex + i].position; + + position[index++] = spritePosition.x; + position[index++] = spritePosition.y; + + position[index++] = spritePosition.x; + position[index++] = spritePosition.y; + + position[index++] = spritePosition.x; + position[index++] = spritePosition.y; + + position[index++] = spritePosition.x; + position[index++] = spritePosition.y; + } + + + // upload the verts to the buffer + this.uploadToBuffer(this.positionBuffer, this.position, 2 * 4, amount); +}; + +SpriteBatchBuffers.prototype.uploadRotation = function (children,startIndex, amount) +{ + var rotation = this.rotation, + index = 0, + spriteRotation; + + for (var i = 0; i < amount; i++) { + + spriteRotation = children[startIndex + i].rotation; + + rotation[index++] = spriteRotation; + rotation[index++] = spriteRotation; + rotation[index++] = spriteRotation; + rotation[index++] = spriteRotation; + } + + this.uploadToBuffer(this.rotationBuffer, this.rotation, 1 * 4, amount); +}; + +SpriteBatchBuffers.prototype.uploadUvs = function (children,startIndex, amount) +{ + var uvs = this.uvs, + index = 0, + textureUvs; + + for (var i = 0; i < amount; i++) { + + textureUvs = children[startIndex + i]._texture._uvs; + + if(textureUvs) + { + uvs[index++] = textureUvs.x0; + uvs[index++] = textureUvs.y0; + + uvs[index++] = textureUvs.x1; + uvs[index++] = textureUvs.y1; + + uvs[index++] = textureUvs.x2; + uvs[index++] = textureUvs.y2; + + uvs[index++] = textureUvs.x3; + uvs[index++] = textureUvs.y3; + } + else + { + index += 8; + } + } + + this.uploadToBuffer(this.uvsBuffer, this.uvs, 2 * 4, amount); +}; + +SpriteBatchBuffers.prototype.uploadAlpha = function (children,startIndex, amount) +{ + var alpha = this.alpha, + index = 0, + spriteAlpha; + + for (var i = 0; i < amount; i++) { + + spriteAlpha = children[startIndex + i].alpha; + + alpha[index++] = spriteAlpha; + alpha[index++] = spriteAlpha; + alpha[index++] = spriteAlpha; + alpha[index++] = spriteAlpha; + } + + + this.uploadToBuffer(this.alphaBuffer, this.alpha, 1 * 4, amount); + +}; + +SpriteBatchBuffers.prototype.uploadToBuffer = function (buffer, data, dataSize, amount) +{ + var gl = this.gl; + + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + + if (this.currentBatchSize > ( this.size * 0.5 ) ) + { + gl.bufferSubData(gl.ARRAY_BUFFER, 0, data); + } + else + { + var view = data.subarray(0, amount * dataSize); + gl.bufferSubData(gl.ARRAY_BUFFER, 0, view); + } +}; + +/** + * Starts a new sprite batch. + * + */ +SpriteBatchBuffers.prototype.bind = function (shader) +{ + var gl = this.gl; + + // this is the same for each shader? + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.vertexAttribPointer(shader.attributes.aVertexPosition, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer); + gl.vertexAttribPointer(shader.attributes.aPositionCoord, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.rotationBuffer); + gl.vertexAttribPointer(shader.attributes.aRotation, 1, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvsBuffer); + gl.vertexAttribPointer(shader.attributes.aTextureCoord, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.alphaBuffer); + gl.vertexAttribPointer(shader.attributes.aColor, 1, gl.FLOAT, false, 0, 0); + +}; + +/** + * Destroys the SpriteBatch. + * + */ +SpriteBatchBuffers.prototype.destroy = function () +{ + //TODO implement this :) to busy making the fun bits.. +}; diff --git a/src/core/sprites/webgl/SpriteBatchRenderer.js b/src/core/sprites/webgl/SpriteBatchRenderer.js index 7d04c11..e37b573 100644 --- a/src/core/sprites/webgl/SpriteBatchRenderer.js +++ b/src/core/sprites/webgl/SpriteBatchRenderer.js @@ -1,6 +1,7 @@ var ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), SpriteBatchShader = require('./SpriteBatchShader'), + SpriteBatchBuffers = require('./SpriteBatchBuffers'), math = require('../../math'); /** @@ -25,53 +26,24 @@ { ObjectRenderer.call(this, renderer); - /** - * - * - * @member {number} - */ - this.vertSize = 10; - - /** - * - * - * @member {number} - */ - this.vertByteSize = this.vertSize * 4; /** * The number of images in the SpriteBatch before it flushes. * * @member {number} */ - this.size = 2000;//CONST.SPRITE_BATCH_SIZE; // 2000 is a nice balance between mobile / desktop + this.size = 15000;//CONST.SPRITE_BATCH_SIZE; // 2000 is a nice balance between mobile / desktop + this.maxSprites = 200000; - // the total number of bytes in our batch - var numVerts = this.size * 4 * this.vertByteSize; - // the total number of indices in our batch var numIndices = this.size * 6; /** - * Holds the vertices - * - * @member {ArrayBuffer} - */ - this.vertices = new Float32Array(numVerts); - - /** * Holds the indices * * @member {Uint16Array} */ this.indices = new Uint16Array(numIndices); - /** - * - * - * @member {number} - */ - this.lastIndexCount = 0; - for (var i=0, j=0; i < numIndices; i += 6, j += 4) { this.indices[i + 0] = j + 0; @@ -85,13 +57,6 @@ /** * * - * @member {boolean} - */ - this.drawing = false; - - /** - * - * * @member {number} */ this.currentBatchSize = 0; @@ -99,37 +64,10 @@ /** * * - * @member {BaseTexture} - */ - this.currentBaseTexture = null; - - /** - * - * - * @member {Array} - */ - this.textures = []; - - /** - * - * - * @member {Array} - */ - this.blendModes = []; - - /** - * - * - * @member {Array} - */ - this.shaders = []; - - /** - * - * * @member {Array} */ this.sprites = []; + this.spriteDataCache = []; /** * The default shader that is used if a sprite doesn't have a more specific one. @@ -138,6 +76,8 @@ */ this.shader = null; + this.buffers = []; + this.tempMatrix = new math.Matrix(); } @@ -160,8 +100,6 @@ // setup default shader this.shader = new SpriteBatchShader(this.renderer.shaderManager); - // create a couple of buffers - this.vertexBuffer = gl.createBuffer(); this.indexBuffer = gl.createBuffer(); // 65535 is max index, so 65535 / 6 = 10922. @@ -170,12 +108,25 @@ 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; + this.setSize(this.maxSprites); }; +SpriteBatchRenderer.prototype.setSize = function ( totalSize ) +{ + var gl = this.renderer.gl; + + for (var i = 0; i < totalSize; i += this.size) + { + this.buffers.push( new SpriteBatchBuffers(gl, this.size) ); + } + + for (var i = 0; i < totalSize; i++) + { + this.spriteDataCache.push(new SpriteDataCache()); + }; +}; + + /** * Renders the sprite object. * @@ -194,152 +145,163 @@ gl.uniformMatrix3fv(this.shader.uniforms.projectionMatrix._location, false, m.toArray(true)); + this.currentBatchSize = 0; + + var rotationDirty = false; + var positionDirty = false; + var vertsDirty = false; + var uvsDirty = false; + var alphaDirty = false; + + this.sprites = children; + + this.vertDirty = 0; + for (var i=0,j= children.length; i= this.size) + this.uploadRotation(children); + this.uploadUvs(children); + this.uploadAlpha(children); +}; + +SpriteBatchRenderer.prototype.uploadVerticies = function (children) +{ + var j = 0; + for (var i = 0; i < children.length; i+=this.size) { - this.flush(); - this.currentBaseTexture = texture.baseTexture; + var amount = ( children.length - i ); + if(amount > this.size) + { + amount = this.size; + } + + this.buffers[j++].uploadVerticies(children, i, amount); } +}; - // get the uvs for the texture - var uvs = texture._uvs; - - // if the uvs have not updated then no point rendering just yet! - if (!uvs) +SpriteBatchRenderer.prototype.uploadPosition = function (children) +{ + var j = 0; + for (var i = 0; i < children.length; i+= this.size) { - return; + var amount = ( children.length - i ); + if(amount > this.size) + { + amount = this.size; + } + + this.buffers[j++].uploadPosition(children, i, amount); } +}; - var vertices = this.vertices, w0, w1, h0, h1, index; - - if (texture.trim) +SpriteBatchRenderer.prototype.uploadRotation = function (children) +{ + var j = 0; + for (var i = 0; i < children.length; i+=this.size) { - // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords.. - var trim = texture.trim; + var amount = ( children.length - i ); + if(amount > this.size) + { + amount = this.size; + } - w1 = trim.x - sprite.anchor.x * trim.width; - w0 = w1 + texture.crop.width; - - h1 = trim.y - sprite.anchor.y * trim.height; - h0 = h1 + texture.crop.height; + this.buffers[j++].uploadRotation(children, i, amount); } - else +}; + +SpriteBatchRenderer.prototype.uploadUvs = function (children) +{ + var j = 0; + for (var i = 0; i < children.length; i+=this.size) { - w0 = (texture._frame.width ) * (1-sprite.anchor.x); - w1 = (texture._frame.width ) * -sprite.anchor.x; + var amount = ( children.length - i ); + if(amount > this.size) + { + amount = this.size; + } - h0 = texture._frame.height * (1-sprite.anchor.y); - h1 = texture._frame.height * -sprite.anchor.y; + this.buffers[j++].uploadUvs(children, i, amount); } +}; +SpriteBatchRenderer.prototype.uploadAlpha = function (children) +{ + var j = 0; + for (var i = 0; i < children.length; i+=this.size) + { + var amount = ( children.length - i ); + if(amount > this.size) + { + amount = this.size; + } - index = this.currentBatchSize * this.vertByteSize; - - // lets upload! - vertices[index++] = w1; - vertices[index++] = h1; - - vertices[index++] = sprite.position.x; - vertices[index++] = sprite.position.y; - - //scale - vertices[index++] = sprite.scale.x; - vertices[index++] = sprite.scale.y; - - //rotation - vertices[index++] = sprite.rotation; - - // uv - vertices[index++] = uvs.x0; - vertices[index++] = uvs.y1; - // color - vertices[index++] = sprite.alpha; - - - // xy - vertices[index++] = w0; - vertices[index++] = h1; - - vertices[index++] = sprite.position.x; - vertices[index++] = sprite.position.y; - - //scale - vertices[index++] = sprite.scale.x; - vertices[index++] = sprite.scale.y; - - //rotation - vertices[index++] = sprite.rotation; - - // uv - vertices[index++] = uvs.x1; - vertices[index++] = uvs.y1; - // color - vertices[index++] = sprite.alpha; - - - // xy - vertices[index++] = w0; - vertices[index++] = h0; - - vertices[index++] = sprite.position.x; - vertices[index++] = sprite.position.y; - - //scale - vertices[index++] = sprite.scale.x; - vertices[index++] = sprite.scale.y; - - //rotation - vertices[index++] = sprite.rotation; - - // uv - vertices[index++] = uvs.x2; - vertices[index++] = uvs.y2; - // color - vertices[index++] = sprite.alpha; - - - // xy - vertices[index++] = w1; - vertices[index++] = h0; - - vertices[index++] = sprite.position.x; - vertices[index++] = sprite.position.y; - - //scale - vertices[index++] = sprite.scale.x; - vertices[index++] = sprite.scale.y; - - //rotation - vertices[index++] = sprite.rotation; - - // uv - vertices[index++] = uvs.x3; - vertices[index++] = uvs.y3; - // color - vertices[index++] = sprite.alpha; - - // color and alpha - - // increment the batchsize - this.sprites[this.currentBatchSize++] = sprite; + this.buffers[j++].uploadAlpha(children, i, amount); + } }; /** @@ -354,19 +316,6 @@ return; } - var gl = this.renderer.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 * this.vertByteSize); - gl.bufferSubData(gl.ARRAY_BUFFER, 0, view); - } - var nextTexture; var batchSize = 0; var start = 0; @@ -427,8 +376,23 @@ gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); } - // now draw those suckas! - gl.drawElements(gl.TRIANGLES, size * 6, gl.UNSIGNED_SHORT, startIndex * 6 * 2); + var j = 0; + for (var i = startIndex; i < size; i+=this.size) { + + this.buffers[j++].bind( this.shader ); + + + var amount = ( size - i) ; + if(amount > this.size) + { + amount = this.size; + } + + // now draw those suckas! + gl.drawElements(gl.TRIANGLES, this.size * 6, gl.UNSIGNED_SHORT, 0);//(startIndex % this.size) * 6 * 2); + } + + // increment the draw count this.renderer.drawCount++; @@ -446,24 +410,12 @@ gl.activeTexture(gl.TEXTURE0); // bind the buffers - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); var shader = this.shader; this.renderer.shaderManager.setShader(shader); - // this is the same for each shader? - var stride = this.vertByteSize; - - gl.vertexAttribPointer(shader.attributes.aVertexPosition, 2, gl.FLOAT, false, stride, 0); - gl.vertexAttribPointer(shader.attributes.aPositionCoord, 2, gl.FLOAT, false, stride, 2 * 4); - gl.vertexAttribPointer(shader.attributes.aScale, 2, gl.FLOAT, false, stride, 4 * 4); - - gl.vertexAttribPointer(shader.attributes.aRotation, 1, gl.FLOAT, false, stride, 6 * 4); - gl.vertexAttribPointer(shader.attributes.aTextureCoord, 2, gl.FLOAT, false, stride, 7 * 4); - gl.vertexAttribPointer(shader.attributes.aColor, 1, gl.FLOAT, false, stride, 9 * 4); - - // gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); }; /** @@ -485,8 +437,6 @@ this.vertexBuffer = null; this.indexBuffer = null; - this.currentBaseTexture = null; - this.drawing = false; this.textures = null; @@ -495,3 +445,15 @@ this.sprites = null; this.shader = null; }; + +var SpriteDataCache = function () +{ + this.rotation = 0; + this.pX = 0; + this.pY = 0; + this.texture = null; + this.sX = 0; + this.sY = 0; + this.alpha = 0; +} + diff --git a/src/core/sprites/SpriteBatch.js b/src/core/sprites/SpriteBatch.js index 360bd67..4238b4c 100644 --- a/src/core/sprites/SpriteBatch.js +++ b/src/core/sprites/SpriteBatch.js @@ -60,6 +60,9 @@ } renderer.setObjectRenderer( renderer.plugins.spriteBatch ); + + + renderer.plugins.spriteBatch.render( this ); }; diff --git a/src/core/sprites/webgl/SpriteBatchBuffers.js b/src/core/sprites/webgl/SpriteBatchBuffers.js new file mode 100644 index 0000000..e28daf5 --- /dev/null +++ b/src/core/sprites/webgl/SpriteBatchBuffers.js @@ -0,0 +1,317 @@ + +/** + * @author Mat Groves + * + * Big thanks to the very clever Matt DesLauriers https://github.com/mattdesl/ + * for creating the original pixi version! + * Also a thanks to https://github.com/bchevalier for tweaking the tint and alpha so that they now share 4 bytes on the vertex buffer + * + * Heavily inspired by LibGDX's SpriteBatchBuffers: + * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/SpriteBatchBuffers.java + */ + +/** + * + * @class + * @private + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this sprite batch works for. + */ +function SpriteBatchBuffers(gl, size ) +{ + this.gl = gl; + + /** + * + * + * @member {number} + */ + this.vertSize = 2; + + /** + * + * + * @member {number} + */ + this.vertByteSize = this.vertSize * 4; + + /** + * The number of images in the SpriteBatch before it flushes. + * + * @member {number} + */ + this.size = size; + + // the total number of bytes in our batch + var numVerts = this.size * 2 * this.vertByteSize; + + /** + * Holds the vertices + * + * @member {ArrayBuffer} + */ + this.vertices = new Float32Array(numVerts); + this.position = new Float32Array(numVerts); + this.rotation = new Float32Array(numVerts/2); + this.uvs = new Float32Array(numVerts); + this.alpha = new Float32Array(numVerts/2); + + this.initBuffers(); + + +} + +SpriteBatchBuffers.prototype.constructor = SpriteBatchBuffers; +module.exports = SpriteBatchBuffers; + +/** + * Sets up the renderer context and necessary buffers. + * + * @private + * @param gl {WebGLContext} the current WebGL drawing context + */ +SpriteBatchBuffers.prototype.initBuffers = function () +{ + var gl = this.gl; + + // create a couple of buffers + this.vertexBuffer = gl.createBuffer(); + this.positionBuffer = gl.createBuffer(); + this.rotationBuffer = gl.createBuffer(); + this.uvsBuffer = gl.createBuffer(); + this.alphaBuffer = gl.createBuffer(); + + // upload the buffers.. + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.position, gl.DYNAMIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.rotationBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvsBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.uvs, gl.DYNAMIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.alphaBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.alpha, gl.DYNAMIC_DRAW); +}; + + +SpriteBatchBuffers.prototype.refresh = function(children, startIndex, amount) +{ + this.uploadVerticies(children,startIndex, amount); + this.uploadRotation(children,startIndex, amount); + this.uploadUvs(children,startIndex, amount); + this.uploadAlpha(children,startIndex, amount); + +}; + +SpriteBatchBuffers.prototype.uploadVerticies = function (children,startIndex, amount) +{ + var vertices = this.vertices, + index = 0, + sprite, + texture, + trim, + sx, + sy, + w0, w1, h0, h1; + + for (var i = 0; i < amount; i++) { + + sprite = children[startIndex + i]; + texture = sprite._texture; + sx = sprite.scale.x; + sy = sprite.scale.y; + + if (texture.trim) + { + // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords.. + trim = texture.trim; + + w1 = trim.x - sprite.anchor.x * trim.width; + w0 = w1 + texture.crop.width; + + h1 = trim.y - sprite.anchor.y * trim.height; + h0 = h1 + texture.crop.height; + } + else + { + w0 = (texture._frame.width ) * (1-sprite.anchor.x); + w1 = (texture._frame.width ) * -sprite.anchor.x; + + h0 = texture._frame.height * (1-sprite.anchor.y); + h1 = texture._frame.height * -sprite.anchor.y; + } + + vertices[index++] = w1 * sx; + vertices[index++] = h1 * sy; + + vertices[index++] = w0 * sx; + vertices[index++] = h1 * sy; + + vertices[index++] = w0 * sx; + vertices[index++] = h0 * sy; + + vertices[index++] = w1 * sx; + vertices[index++] = h0 * sy; + } + + this.uploadToBuffer(this.vertexBuffer, this.vertices, 2 * 4, amount); +}; + +SpriteBatchBuffers.prototype.uploadPosition = function (children,startIndex, amount) +{ + var position = this.position, + index = 0, + spritePosition; + + for (var i = 0; i < amount; i++) { + + spritePosition = children[startIndex + i].position; + + position[index++] = spritePosition.x; + position[index++] = spritePosition.y; + + position[index++] = spritePosition.x; + position[index++] = spritePosition.y; + + position[index++] = spritePosition.x; + position[index++] = spritePosition.y; + + position[index++] = spritePosition.x; + position[index++] = spritePosition.y; + } + + + // upload the verts to the buffer + this.uploadToBuffer(this.positionBuffer, this.position, 2 * 4, amount); +}; + +SpriteBatchBuffers.prototype.uploadRotation = function (children,startIndex, amount) +{ + var rotation = this.rotation, + index = 0, + spriteRotation; + + for (var i = 0; i < amount; i++) { + + spriteRotation = children[startIndex + i].rotation; + + rotation[index++] = spriteRotation; + rotation[index++] = spriteRotation; + rotation[index++] = spriteRotation; + rotation[index++] = spriteRotation; + } + + this.uploadToBuffer(this.rotationBuffer, this.rotation, 1 * 4, amount); +}; + +SpriteBatchBuffers.prototype.uploadUvs = function (children,startIndex, amount) +{ + var uvs = this.uvs, + index = 0, + textureUvs; + + for (var i = 0; i < amount; i++) { + + textureUvs = children[startIndex + i]._texture._uvs; + + if(textureUvs) + { + uvs[index++] = textureUvs.x0; + uvs[index++] = textureUvs.y0; + + uvs[index++] = textureUvs.x1; + uvs[index++] = textureUvs.y1; + + uvs[index++] = textureUvs.x2; + uvs[index++] = textureUvs.y2; + + uvs[index++] = textureUvs.x3; + uvs[index++] = textureUvs.y3; + } + else + { + index += 8; + } + } + + this.uploadToBuffer(this.uvsBuffer, this.uvs, 2 * 4, amount); +}; + +SpriteBatchBuffers.prototype.uploadAlpha = function (children,startIndex, amount) +{ + var alpha = this.alpha, + index = 0, + spriteAlpha; + + for (var i = 0; i < amount; i++) { + + spriteAlpha = children[startIndex + i].alpha; + + alpha[index++] = spriteAlpha; + alpha[index++] = spriteAlpha; + alpha[index++] = spriteAlpha; + alpha[index++] = spriteAlpha; + } + + + this.uploadToBuffer(this.alphaBuffer, this.alpha, 1 * 4, amount); + +}; + +SpriteBatchBuffers.prototype.uploadToBuffer = function (buffer, data, dataSize, amount) +{ + var gl = this.gl; + + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + + if (this.currentBatchSize > ( this.size * 0.5 ) ) + { + gl.bufferSubData(gl.ARRAY_BUFFER, 0, data); + } + else + { + var view = data.subarray(0, amount * dataSize); + gl.bufferSubData(gl.ARRAY_BUFFER, 0, view); + } +}; + +/** + * Starts a new sprite batch. + * + */ +SpriteBatchBuffers.prototype.bind = function (shader) +{ + var gl = this.gl; + + // this is the same for each shader? + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.vertexAttribPointer(shader.attributes.aVertexPosition, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer); + gl.vertexAttribPointer(shader.attributes.aPositionCoord, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.rotationBuffer); + gl.vertexAttribPointer(shader.attributes.aRotation, 1, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvsBuffer); + gl.vertexAttribPointer(shader.attributes.aTextureCoord, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.alphaBuffer); + gl.vertexAttribPointer(shader.attributes.aColor, 1, gl.FLOAT, false, 0, 0); + +}; + +/** + * Destroys the SpriteBatch. + * + */ +SpriteBatchBuffers.prototype.destroy = function () +{ + //TODO implement this :) to busy making the fun bits.. +}; diff --git a/src/core/sprites/webgl/SpriteBatchRenderer.js b/src/core/sprites/webgl/SpriteBatchRenderer.js index 7d04c11..e37b573 100644 --- a/src/core/sprites/webgl/SpriteBatchRenderer.js +++ b/src/core/sprites/webgl/SpriteBatchRenderer.js @@ -1,6 +1,7 @@ var ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), SpriteBatchShader = require('./SpriteBatchShader'), + SpriteBatchBuffers = require('./SpriteBatchBuffers'), math = require('../../math'); /** @@ -25,53 +26,24 @@ { ObjectRenderer.call(this, renderer); - /** - * - * - * @member {number} - */ - this.vertSize = 10; - - /** - * - * - * @member {number} - */ - this.vertByteSize = this.vertSize * 4; /** * The number of images in the SpriteBatch before it flushes. * * @member {number} */ - this.size = 2000;//CONST.SPRITE_BATCH_SIZE; // 2000 is a nice balance between mobile / desktop + this.size = 15000;//CONST.SPRITE_BATCH_SIZE; // 2000 is a nice balance between mobile / desktop + this.maxSprites = 200000; - // the total number of bytes in our batch - var numVerts = this.size * 4 * this.vertByteSize; - // the total number of indices in our batch var numIndices = this.size * 6; /** - * Holds the vertices - * - * @member {ArrayBuffer} - */ - this.vertices = new Float32Array(numVerts); - - /** * Holds the indices * * @member {Uint16Array} */ this.indices = new Uint16Array(numIndices); - /** - * - * - * @member {number} - */ - this.lastIndexCount = 0; - for (var i=0, j=0; i < numIndices; i += 6, j += 4) { this.indices[i + 0] = j + 0; @@ -85,13 +57,6 @@ /** * * - * @member {boolean} - */ - this.drawing = false; - - /** - * - * * @member {number} */ this.currentBatchSize = 0; @@ -99,37 +64,10 @@ /** * * - * @member {BaseTexture} - */ - this.currentBaseTexture = null; - - /** - * - * - * @member {Array} - */ - this.textures = []; - - /** - * - * - * @member {Array} - */ - this.blendModes = []; - - /** - * - * - * @member {Array} - */ - this.shaders = []; - - /** - * - * * @member {Array} */ this.sprites = []; + this.spriteDataCache = []; /** * The default shader that is used if a sprite doesn't have a more specific one. @@ -138,6 +76,8 @@ */ this.shader = null; + this.buffers = []; + this.tempMatrix = new math.Matrix(); } @@ -160,8 +100,6 @@ // setup default shader this.shader = new SpriteBatchShader(this.renderer.shaderManager); - // create a couple of buffers - this.vertexBuffer = gl.createBuffer(); this.indexBuffer = gl.createBuffer(); // 65535 is max index, so 65535 / 6 = 10922. @@ -170,12 +108,25 @@ 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; + this.setSize(this.maxSprites); }; +SpriteBatchRenderer.prototype.setSize = function ( totalSize ) +{ + var gl = this.renderer.gl; + + for (var i = 0; i < totalSize; i += this.size) + { + this.buffers.push( new SpriteBatchBuffers(gl, this.size) ); + } + + for (var i = 0; i < totalSize; i++) + { + this.spriteDataCache.push(new SpriteDataCache()); + }; +}; + + /** * Renders the sprite object. * @@ -194,152 +145,163 @@ gl.uniformMatrix3fv(this.shader.uniforms.projectionMatrix._location, false, m.toArray(true)); + this.currentBatchSize = 0; + + var rotationDirty = false; + var positionDirty = false; + var vertsDirty = false; + var uvsDirty = false; + var alphaDirty = false; + + this.sprites = children; + + this.vertDirty = 0; + for (var i=0,j= children.length; i= this.size) + this.uploadRotation(children); + this.uploadUvs(children); + this.uploadAlpha(children); +}; + +SpriteBatchRenderer.prototype.uploadVerticies = function (children) +{ + var j = 0; + for (var i = 0; i < children.length; i+=this.size) { - this.flush(); - this.currentBaseTexture = texture.baseTexture; + var amount = ( children.length - i ); + if(amount > this.size) + { + amount = this.size; + } + + this.buffers[j++].uploadVerticies(children, i, amount); } +}; - // get the uvs for the texture - var uvs = texture._uvs; - - // if the uvs have not updated then no point rendering just yet! - if (!uvs) +SpriteBatchRenderer.prototype.uploadPosition = function (children) +{ + var j = 0; + for (var i = 0; i < children.length; i+= this.size) { - return; + var amount = ( children.length - i ); + if(amount > this.size) + { + amount = this.size; + } + + this.buffers[j++].uploadPosition(children, i, amount); } +}; - var vertices = this.vertices, w0, w1, h0, h1, index; - - if (texture.trim) +SpriteBatchRenderer.prototype.uploadRotation = function (children) +{ + var j = 0; + for (var i = 0; i < children.length; i+=this.size) { - // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords.. - var trim = texture.trim; + var amount = ( children.length - i ); + if(amount > this.size) + { + amount = this.size; + } - w1 = trim.x - sprite.anchor.x * trim.width; - w0 = w1 + texture.crop.width; - - h1 = trim.y - sprite.anchor.y * trim.height; - h0 = h1 + texture.crop.height; + this.buffers[j++].uploadRotation(children, i, amount); } - else +}; + +SpriteBatchRenderer.prototype.uploadUvs = function (children) +{ + var j = 0; + for (var i = 0; i < children.length; i+=this.size) { - w0 = (texture._frame.width ) * (1-sprite.anchor.x); - w1 = (texture._frame.width ) * -sprite.anchor.x; + var amount = ( children.length - i ); + if(amount > this.size) + { + amount = this.size; + } - h0 = texture._frame.height * (1-sprite.anchor.y); - h1 = texture._frame.height * -sprite.anchor.y; + this.buffers[j++].uploadUvs(children, i, amount); } +}; +SpriteBatchRenderer.prototype.uploadAlpha = function (children) +{ + var j = 0; + for (var i = 0; i < children.length; i+=this.size) + { + var amount = ( children.length - i ); + if(amount > this.size) + { + amount = this.size; + } - index = this.currentBatchSize * this.vertByteSize; - - // lets upload! - vertices[index++] = w1; - vertices[index++] = h1; - - vertices[index++] = sprite.position.x; - vertices[index++] = sprite.position.y; - - //scale - vertices[index++] = sprite.scale.x; - vertices[index++] = sprite.scale.y; - - //rotation - vertices[index++] = sprite.rotation; - - // uv - vertices[index++] = uvs.x0; - vertices[index++] = uvs.y1; - // color - vertices[index++] = sprite.alpha; - - - // xy - vertices[index++] = w0; - vertices[index++] = h1; - - vertices[index++] = sprite.position.x; - vertices[index++] = sprite.position.y; - - //scale - vertices[index++] = sprite.scale.x; - vertices[index++] = sprite.scale.y; - - //rotation - vertices[index++] = sprite.rotation; - - // uv - vertices[index++] = uvs.x1; - vertices[index++] = uvs.y1; - // color - vertices[index++] = sprite.alpha; - - - // xy - vertices[index++] = w0; - vertices[index++] = h0; - - vertices[index++] = sprite.position.x; - vertices[index++] = sprite.position.y; - - //scale - vertices[index++] = sprite.scale.x; - vertices[index++] = sprite.scale.y; - - //rotation - vertices[index++] = sprite.rotation; - - // uv - vertices[index++] = uvs.x2; - vertices[index++] = uvs.y2; - // color - vertices[index++] = sprite.alpha; - - - // xy - vertices[index++] = w1; - vertices[index++] = h0; - - vertices[index++] = sprite.position.x; - vertices[index++] = sprite.position.y; - - //scale - vertices[index++] = sprite.scale.x; - vertices[index++] = sprite.scale.y; - - //rotation - vertices[index++] = sprite.rotation; - - // uv - vertices[index++] = uvs.x3; - vertices[index++] = uvs.y3; - // color - vertices[index++] = sprite.alpha; - - // color and alpha - - // increment the batchsize - this.sprites[this.currentBatchSize++] = sprite; + this.buffers[j++].uploadAlpha(children, i, amount); + } }; /** @@ -354,19 +316,6 @@ return; } - var gl = this.renderer.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 * this.vertByteSize); - gl.bufferSubData(gl.ARRAY_BUFFER, 0, view); - } - var nextTexture; var batchSize = 0; var start = 0; @@ -427,8 +376,23 @@ gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); } - // now draw those suckas! - gl.drawElements(gl.TRIANGLES, size * 6, gl.UNSIGNED_SHORT, startIndex * 6 * 2); + var j = 0; + for (var i = startIndex; i < size; i+=this.size) { + + this.buffers[j++].bind( this.shader ); + + + var amount = ( size - i) ; + if(amount > this.size) + { + amount = this.size; + } + + // now draw those suckas! + gl.drawElements(gl.TRIANGLES, this.size * 6, gl.UNSIGNED_SHORT, 0);//(startIndex % this.size) * 6 * 2); + } + + // increment the draw count this.renderer.drawCount++; @@ -446,24 +410,12 @@ gl.activeTexture(gl.TEXTURE0); // bind the buffers - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); var shader = this.shader; this.renderer.shaderManager.setShader(shader); - // this is the same for each shader? - var stride = this.vertByteSize; - - gl.vertexAttribPointer(shader.attributes.aVertexPosition, 2, gl.FLOAT, false, stride, 0); - gl.vertexAttribPointer(shader.attributes.aPositionCoord, 2, gl.FLOAT, false, stride, 2 * 4); - gl.vertexAttribPointer(shader.attributes.aScale, 2, gl.FLOAT, false, stride, 4 * 4); - - gl.vertexAttribPointer(shader.attributes.aRotation, 1, gl.FLOAT, false, stride, 6 * 4); - gl.vertexAttribPointer(shader.attributes.aTextureCoord, 2, gl.FLOAT, false, stride, 7 * 4); - gl.vertexAttribPointer(shader.attributes.aColor, 1, gl.FLOAT, false, stride, 9 * 4); - - // gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); }; /** @@ -485,8 +437,6 @@ this.vertexBuffer = null; this.indexBuffer = null; - this.currentBaseTexture = null; - this.drawing = false; this.textures = null; @@ -495,3 +445,15 @@ this.sprites = null; this.shader = null; }; + +var SpriteDataCache = function () +{ + this.rotation = 0; + this.pX = 0; + this.pY = 0; + this.texture = null; + this.sX = 0; + this.sY = 0; + this.alpha = 0; +} + diff --git a/src/core/sprites/webgl/SpriteBatchShader.js b/src/core/sprites/webgl/SpriteBatchShader.js index 41f3309..81d9a6f 100644 --- a/src/core/sprites/webgl/SpriteBatchShader.js +++ b/src/core/sprites/webgl/SpriteBatchShader.js @@ -22,22 +22,20 @@ 'uniform mat3 projectionMatrix;', - // 'uniform mat3 uMatrix;', - 'varying vec2 vTextureCoord;', 'varying float vColor;', 'void main(void){', - ' vec2 v;', - ' vec2 sv = aVertexPosition * aScale;', - ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', - ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', - ' v = v + aPositionCoord ;', + ' vec2 v = aVertexPosition;', + + ' v.x = (aVertexPosition.x) * cos(aRotation) - (aVertexPosition.y) * sin(aRotation);', + ' v.y = (aVertexPosition.x) * sin(aRotation) + (aVertexPosition.y) * cos(aRotation);', + ' v = v + aPositionCoord;', ' gl_Position = vec4((projectionMatrix * vec3(v, 1.0)).xy, 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', - ' vColor = 1.0;', + ' vColor = aColor;', '}' ].join('\n'), // hello @@ -54,13 +52,11 @@ '}' ].join('\n'), // custom uniforms - { - // uMatrix: { type: 'mat3', value: new Float32Array(9) } - }, + null, // custom attributes { aPositionCoord: 0, - aScale: 0, + // aScale: 0, aRotation: 0 } ); @@ -71,4 +67,5 @@ SpriteBatchShader.prototype = Object.create(TextureShader.prototype); SpriteBatchShader.prototype.constructor = SpriteBatchShader; + module.exports = SpriteBatchShader;