1 define([ 'util/ensureCallback', 'sprites/canvas', 'sprites/webGL' ], function (ensureCallback, canvas, webGL) {
2 var FLOATS_PER_VERT = 4;
3 var VERTS_PER_SPRITE = 6;
4 var FLOATS_PER_SPRITE = VERTS_PER_SPRITE * FLOATS_PER_VERT;
6 function RenderContext(sourceData, frameData) {
7 this.sourceData = sourceData;
8 this.frameData = frameData;
10 this.canvas = canvas();
11 var gl = webGL.getContext(this.canvas);
14 this.program = webGL.shaders.batchSprite(gl);
16 // Buffer data is interleaved:
18 // struct bufferUnit {
20 // vec2 aTexCoord; -- Constant
23 // sizeof(bufferUnit) == sizeof(float) * 4 == 16
25 // There are six of these structs per sprite
26 // (one for each corner of the each triangle,
27 // and two triangles per square).
29 var maxSprites = Math.max.apply(Math, frameData.map(function (arr) {
33 var bufferData = new Float32Array(maxSprites * FLOATS_PER_SPRITE);
34 for (var i = 0, j = 0; i < maxSprites; ++i, j += FLOATS_PER_SPRITE) {
36 bufferData[j + 0*4 + 2] = 0;
37 bufferData[j + 0*4 + 3] = 0;
40 bufferData[j + 1*4 + 2] = 1;
41 bufferData[j + 1*4 + 3] = 0;
44 bufferData[j + 2*4 + 2] = 0;
45 bufferData[j + 2*4 + 3] = 1;
48 bufferData[j + 3*4 + 2] = 1;
49 bufferData[j + 3*4 + 3] = 0;
52 bufferData[j + 4*4 + 2] = 0;
53 bufferData[j + 4*4 + 3] = 1;
56 bufferData[j + 5*4 + 2] = 1;
57 bufferData[j + 5*4 + 3] = 1;
60 this.bufferData = bufferData;
62 var buffer = gl.createBuffer();
63 gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
64 gl.bufferData(gl.ARRAY_BUFFER, bufferData, gl.DYNAMIC_DRAW);
65 gl.bindBuffer(gl.ARRAY_BUFFER, null);
69 this.texture = webGL.makeTexture(gl, this.sourceData.img);
72 gl.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD);
73 gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.SRC_ALPHA, gl.ONE);
76 RenderContext.prototype.load = function load(callback) {
77 callback = ensureCallback(callback);
79 document.body.appendChild(this.canvas);
86 RenderContext.prototype.unload = function unload() {
87 if (this.canvas.parentNode) {
88 this.canvas.parentNode.removeChild(this.canvas);
91 var gl = this.context;
92 gl.deleteTexture(this.texture);
95 gl.deleteProgram(this.program);
98 gl.deleteBuffer(this.buffer);
100 this.bufferData = null;
103 RenderContext.prototype.clear = function clear() {
104 var gl = this.context;
105 gl.viewport(0, 0, this.canvas.width, this.canvas.height);
106 gl.clearColor(255, 255, 255, 255);
107 gl.clear(gl.COLOR_BUFFER_BIT);
110 RenderContext.prototype.renderFrame = function renderFrame(frameIndex) {
111 var gl = this.context;
112 var sourceData = this.sourceData;
114 var img = sourceData.img;
115 var imgWidth = img.width;
116 var imgHeight = img.height;
118 var transforms = this.frameData[frameIndex];
119 var count = transforms.length;
121 var bufferData = this.bufferData;
124 for (i = 0, j = 0; i < count; ++i, j += FLOATS_PER_SPRITE) {
125 var t = transforms[i];
126 t.transformPointInto(0, 0, bufferData, j + 0);
127 t.transformPointInto(imgWidth, 0, bufferData, j + 4);
128 t.transformPointInto(0, imgHeight, bufferData, j + 8);
130 bufferData[j + 12] = bufferData[j + 4];
131 bufferData[j + 13] = bufferData[j + 5];
133 bufferData[j + 16] = bufferData[j + 8];
134 bufferData[j + 17] = bufferData[j + 9];
136 t.transformPointInto(imgWidth, imgHeight, bufferData, j + 20);
141 var program = this.program;
142 gl.useProgram(program);
144 gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
145 gl.bufferData(gl.ARRAY_BUFFER, bufferData, gl.DYNAMIC_DRAW);
147 gl.vertexAttribPointer(program.attr.coord, 2, gl.FLOAT, false, 16, 0);
148 gl.vertexAttribPointer(program.attr.texCoord, 2, gl.FLOAT, false, 16, 8);
149 gl.enableVertexAttribArray(program.attr.coord);
150 gl.enableVertexAttribArray(program.attr.texCoord);
152 gl.activeTexture(gl.TEXTURE0);
153 gl.bindTexture(gl.TEXTURE_2D, this.texture);
154 gl.uniform1i(program.uni.sampler, 0);
156 gl.drawArrays(gl.TRIANGLES, 0, count * VERTS_PER_SPRITE);
159 gl.disableVertexAttribArray(program.attr.coord);
160 gl.disableVertexAttribArray(program.attr.texCoord);
161 gl.bindBuffer(gl.ARRAY_BUFFER, null);
166 return function (element, frameData) {
167 return new RenderContext(element, frameData);