UNPKG

gl-sprite-batch

Version:
233 lines (190 loc) 5.96 kB
var colorToFloat = require('./pack-rgba-float') var mixes = require('mixes') var premult = require('premultiplied-rgba') var WhiteTex = require('gl-white-texture') var vertNumFloats = require('./common').floatsPerVertex //Temporary arrays to avoid GC thrashing var position = [0, 0], shape = [0, 0], texcoord = [0, 0, 0, 0], color = [0, 0, 0, 0] var tmp4 = [0, 0, 0, 0], rotOrigin = [0, 0], tmp2 = [0, 0] function SpriteBatch(gl, opt) { if (!(this instanceof SpriteBatch)) return new SpriteBatch(gl, opt) if (!gl) throw new Error("must specify gl context") this.gl = gl opt = opt || {} this._bound = false this.idx = 0 //no transform means identity this.transform = null //white texture is akin to "no texture" (without switching shaders) this._defaultTexture = opt.defaultTexture || WhiteTex(gl) this._ownsDefault = !opt.defaultTexture this._lastTexture = this._defaultTexture this._texture = this._defaultTexture this.texture = null this.mode = typeof opt.mode === 'number' ? opt.mode : gl.TRIANGLES this.premultiplied = opt.premultiplied || false this._dirty = true this.create(opt) //set default attributes this.defaults() } //mix in create() and ensureCapacity() functions mixes(SpriteBatch, require('./common').mixins) mixes(SpriteBatch, { capacity: { get: function() { return this._capacity } }, texture: { get: function() { return this._texture }, set: function(tex) { this._texture = tex || this._defaultTexture } }, dispose: function() { if (this.vertexBuffer) this.vertexBuffer.dispose() if (this.indexBuffer) this.indexBuffer.dispose() if (this.vao) this.vao.dispose() if (this._ownsDefault) this._defaultTexture.dispose() }, clear: function() { this.idx = 0 return this }, bind: function(shader) { shader.bind() this.vao.bind(shader) this._bound = true }, unbind: function() { this.vao.unbind() this._bound = false }, defaults: function() { this.position = copy2(position, 0, 0) this.texcoord = copy4(texcoord, 0, 0, 1, 1) this.color = copy4(color, 1, 1, 1, 1) this.shape = copy2(shape, 0, 0) return this }, push: function(sprite) { //if we are defining attributes on the fly if (sprite) { this.texture = sprite.texture this.position = sprite.position || copy2(position, 0, 0) this.texcoord = sprite.texcoord || copy4(texcoord, 0, 0, 1, 1) this.color = sprite.color || copy4(color, 1, 1, 1, 1) this.shape = sprite.shape || copy2(shape, 0, 0) } if (this.texture !== this._lastTexture) { //new texture, flush previous data if (this._bound) this.flush() this._lastTexture = this.texture } else if (this.idx === this.vertices.length) { //if we AREN'T bound, we need to stop pushing vertex data! if (!this._bound) return this //if we ARE bound, we can flush the batch and continue drawing this.flush() } this._dirty = true //get RGBA components and pack into a single float var colorRGBA = this.premultiplied ? premult(this.color, tmp4) : this.color var c = colorToFloat(colorRGBA) var u1 = this.texcoord[0], v1 = this.texcoord[1], u2 = this.texcoord[2], v2 = this.texcoord[3] var x = this.position[0], y = this.position[1], width = this.shape[0], height = this.shape[1] this._vert(x, y, u1, v1, c) this._vert(x+width, y, u2, v1, c) this._vert(x+width, y+height, u2, v2, c) this._vert(x, y+height, u1, v2, c) return this }, _vert: function(x1, y1, u1, v1, c) { var idx = this.idx, verts = this.vertices, transform = this.transform if (transform) { var x = x1, y = y1 x1 = transform[0] * x + transform[4] * y + transform[12] y1 = transform[1] * x + transform[5] * y + transform[13] } //xy verts[idx++] = x1 verts[idx++] = y1 //uv verts[idx++] = u1 verts[idx++] = v1 //color verts[idx++] = c this.idx = idx }, flush: function() { this.draw() return this.clear() }, draw: function() { //If we've reached a new texture or capacity //while not bound, then we will just clear the batch //to zero and draw nothing if (this.idx === 0 || !this._bound) return this var gl = this.gl if (this._dirty) { var view = this.vertices.subarray(0, this.idx) this.vertexBuffer.update(view, 0) this._dirty = false } if (this._lastTexture) this._lastTexture.bind() this._lastTexture = this.texture var sprites = (this.idx / (vertNumFloats * 4)) if (sprites > 0) this.vao.draw(this.mode, sprites * 6, 0) return this }, }) module.exports = SpriteBatch //TODO: will use modular gl-matrix for these... function copy2(out, x, y) { out[0] = x out[1] = y return out } function copy4(out, x, y, z, w) { out[0] = x out[1] = y out[2] = z out[3] = w return out } function copyVec2(out, vec) { return copy2(out, vec[0], vec[1]) } function transformMat4(out, a, m) { var x = a[0], y = a[1] out[0] = m[0] * x + m[4] * y + m[12] out[1] = m[1] * x + m[5] * y + m[13] return out }