/** * @author Mat Groves * * Big thanks to the very clever Matt DesLauriers <mattdesl> 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 ParticleBuffer: * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/ParticleBuffer.java */ /** * * @class * @private * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this sprite batch works for. */ function ParticleBuffer(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(); } ParticleBuffer.prototype.constructor = ParticleBuffer; module.exports = ParticleBuffer; /** * Sets up the renderer context and necessary buffers. * * @private * @param gl {WebGLContext} the current WebGL drawing context */ ParticleBuffer.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); }; ParticleBuffer.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); }; ParticleBuffer.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); }; ParticleBuffer.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); }; ParticleBuffer.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); }; ParticleBuffer.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); }; ParticleBuffer.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); }; ParticleBuffer.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. * */ ParticleBuffer.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. * */ ParticleBuffer.prototype.destroy = function () { //TODO implement this :) to busy making the fun bits.. };