mirror of
https://github.com/photonstorm/phaser
synced 2024-11-23 21:24:09 +00:00
Sprite Batch implementation
This commit is contained in:
parent
9965ab674d
commit
f342fac027
19 changed files with 368 additions and 24 deletions
|
@ -1,4 +1,4 @@
|
|||
var CHECKSUM = {
|
||||
build: 'bc213d90-de6f-11e6-bd32-974da4fb460f'
|
||||
build: '98f0c510-de98-11e6-aa23-b3f509f20724'
|
||||
};
|
||||
module.exports = CHECKSUM;
|
|
@ -7,7 +7,8 @@
|
|||
|
||||
var CONST = require('../../const');
|
||||
var CreateEmptyTexture = require('./utils/CreateEmptyTexture');
|
||||
var BlitterBatch = require('./batches/blitter/BlitterBatch');
|
||||
var BlitterBatch = require('./batches/blitter/BlitterBatch');
|
||||
var SpriteBatch = require('./batches/sprite/SpriteBatch');
|
||||
|
||||
var WebGLRenderer = function (game)
|
||||
{
|
||||
|
@ -46,6 +47,7 @@ var WebGLRenderer = function (game)
|
|||
|
||||
this.init();
|
||||
this.blitterBatch = new BlitterBatch(game, this.gl, this);
|
||||
this.spriteBatch = new SpriteBatch(game, this.gl, this);
|
||||
this.batch = null;
|
||||
this.currentTexture2D = null;
|
||||
};
|
||||
|
@ -131,12 +133,26 @@ WebGLRenderer.prototype = {
|
|||
];
|
||||
},
|
||||
|
||||
setTexture2D: function(texture2D)
|
||||
setTexture2D: function (texture2D)
|
||||
{
|
||||
this.currentTexture = texture2D;
|
||||
this.batch.dirty = true;
|
||||
},
|
||||
|
||||
setBatch: function (batch)
|
||||
{
|
||||
if (this.batch != batch)
|
||||
{
|
||||
if (this.batch)
|
||||
{
|
||||
this.batch.flush();
|
||||
}
|
||||
batch.bind();
|
||||
batch.setTexture2D(this.currentTexture2D, true);
|
||||
this.batch = batch;
|
||||
}
|
||||
},
|
||||
|
||||
resize: function (width, height)
|
||||
{
|
||||
var res = this.game.config.resolution;
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
// Could you move these into sub-folders please, i.e. 'vao', 'shader' etc?
|
||||
|
||||
var BindVertexArray = require('../../utils/BindVertexArray');
|
||||
var CreateProgram = require('../../utils/CreateProgram');
|
||||
var CreateShader = require('../../utils/CreateShader');
|
||||
var CreateBuffer = require('../../utils/CreateBuffer');
|
||||
var CreateAttribDesc = require('../../utils/CreateAttribDesc');
|
||||
var VertexBuffer = require('../../utils/VertexBuffer');
|
||||
var IndexBuffer = require('../../utils/IndexBuffer');
|
||||
var VertexArray = require('../../utils/VertexArray');
|
||||
var BindVertexArray = require('../../utils/vao/BindVertexArray');
|
||||
var CreateProgram = require('../../utils/shader/CreateProgram');
|
||||
var CreateShader = require('../../utils/shader/CreateShader');
|
||||
var CreateBuffer = require('../../utils/buffer/CreateBuffer');
|
||||
var CreateAttribDesc = require('../../utils/vao/CreateAttribDesc');
|
||||
var VertexBuffer = require('../../utils/buffer/VertexBuffer');
|
||||
var IndexBuffer = require('../../utils/buffer/IndexBuffer');
|
||||
var VertexArray = require('../../utils/vao/VertexArray');
|
||||
|
||||
var PHASER_CONST = require('../../../../const');
|
||||
var CONST = require('./const');
|
||||
|
@ -133,16 +133,7 @@ BlitterBatch.prototype = {
|
|||
|
||||
add: function (x, y, width, height, umin, vmin, umax, vmax)
|
||||
{
|
||||
var manager = this.manager;
|
||||
if (manager.batch !== this || this.dirty)
|
||||
{
|
||||
if (manager.batch)
|
||||
manager.batch.flush();
|
||||
|
||||
this.bind();
|
||||
this.setTexture2D(manager.currentTexture2D, true);
|
||||
manager.batch = this;
|
||||
}
|
||||
this.manager.setBatch(this);
|
||||
|
||||
// The user must check if the buffers are full before flushing
|
||||
// this is to give freedom of when should the renderer flush. var vertexDataBuffer = this.vertexDataBuffer;
|
||||
|
|
8
v3/src/renderer/webgl/batches/sprite/FragmentShader.js
Normal file
8
v3/src/renderer/webgl/batches/sprite/FragmentShader.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
module.exports = [
|
||||
'precision lowp float;',
|
||||
'uniform sampler2D u_sampler2D;',
|
||||
'varying vec2 v_tex_coord;',
|
||||
'void main() {',
|
||||
' gl_FragColor = texture2D(u_sampler2D, v_tex_coord);',
|
||||
'}'
|
||||
].join('\n');
|
|
@ -1,4 +1,292 @@
|
|||
var SpriteBatch = function ()
|
||||
{};
|
||||
var BindVertexArray = require('../../utils/vao/BindVertexArray');
|
||||
var CreateProgram = require('../../utils/shader/CreateProgram');
|
||||
var CreateShader = require('../../utils/shader/CreateShader');
|
||||
var CreateBuffer = require('../../utils/buffer/CreateBuffer');
|
||||
var CreateAttribDesc = require('../../utils/vao/CreateAttribDesc');
|
||||
var VertexBuffer = require('../../utils/buffer/VertexBuffer');
|
||||
var IndexBuffer = require('../../utils/buffer/IndexBuffer');
|
||||
var VertexArray = require('../../utils/vao/VertexArray');
|
||||
|
||||
module.exports = SpriteBatch;
|
||||
var PHASER_CONST = require('../../../../const');
|
||||
var CONST = require('./const');
|
||||
|
||||
var SpriteBatch = function (game, gl, manager)
|
||||
{
|
||||
this.game = game;
|
||||
this.type = PHASER_CONST.WEBGL;
|
||||
|
||||
this.view = game.canvas;
|
||||
this.resolution = game.config.resolution;
|
||||
this.width = game.config.width * game.config.resolution;
|
||||
this.height = game.config.height * game.config.resolution;
|
||||
|
||||
this.glContext = gl;
|
||||
|
||||
this.maxSprites = null;
|
||||
|
||||
this.vertShader = null;
|
||||
this.fragShader = null;
|
||||
|
||||
this.program = null;
|
||||
|
||||
this.vertexArray = null;
|
||||
this.indexBufferObject = null;
|
||||
this.vertexDataBuffer = null;
|
||||
this.indexDataBuffer = null;
|
||||
|
||||
this.elementCount = 0;
|
||||
|
||||
this.currentTexture2D = null;
|
||||
this.viewMatrixLocation = null;
|
||||
|
||||
// All of these settings will be able to be controlled via the Game Config
|
||||
this.config = {
|
||||
clearBeforeRender: true,
|
||||
transparent: false,
|
||||
autoResize: false,
|
||||
preserveDrawingBuffer: false,
|
||||
|
||||
WebGLContextOptions: {
|
||||
alpha: true,
|
||||
antialias: true,
|
||||
premultipliedAlpha: true,
|
||||
stencil: true,
|
||||
preserveDrawingBuffer: false
|
||||
}
|
||||
};
|
||||
|
||||
this.manager = manager;
|
||||
this.dirty = false;
|
||||
|
||||
this.init(this.glContext);
|
||||
};
|
||||
|
||||
SpriteBatch.prototype.constructor = SpriteBatch;
|
||||
|
||||
SpriteBatch.prototype = {
|
||||
|
||||
init: function (gl)
|
||||
{
|
||||
|
||||
var vertexDataBuffer = new VertexBuffer(CONST.VERTEX_SIZE * CONST.SPRITE_VERTEX_COUNT * CONST.MAX_SPRITES);
|
||||
|
||||
var indexDataBuffer = new IndexBuffer(CONST.INDEX_SIZE * CONST.SPRITE_INDEX_COUNT * CONST.MAX_SPRITES);
|
||||
|
||||
var vertShader = CreateShader(gl, CONST.VERTEX_SHADER_SOURCE, gl.VERTEX_SHADER);
|
||||
var fragShader = CreateShader(gl, CONST.FRAGMENT_SHADER_SOURCE, gl.FRAGMENT_SHADER);
|
||||
var program = CreateProgram(gl, vertShader, fragShader);
|
||||
|
||||
var indexBufferObject = CreateBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, gl.STATIC_DRAW, null, indexDataBuffer.getByteCapacity());
|
||||
|
||||
var attribArray = [
|
||||
CreateAttribDesc(gl, program, 'a_position', 2, gl.FLOAT, false, CONST.VERTEX_SIZE, 0),
|
||||
CreateAttribDesc(gl, program, 'a_tex_coord', 2, gl.FLOAT, false, CONST.VERTEX_SIZE, 8),
|
||||
CreateAttribDesc(gl, program, 'a_translate', 2, gl.FLOAT, false, CONST.VERTEX_SIZE, 16),
|
||||
CreateAttribDesc(gl, program, 'a_scale', 2, gl.FLOAT, false, CONST.VERTEX_SIZE, 24),
|
||||
CreateAttribDesc(gl, program, 'a_rotation', 1, gl.FLOAT, false, CONST.VERTEX_SIZE, 32)
|
||||
];
|
||||
|
||||
var vertexArray = new VertexArray(CreateBuffer(gl, gl.ARRAY_BUFFER, gl.STREAM_DRAW, null, vertexDataBuffer.getByteCapacity()), attribArray);
|
||||
|
||||
var viewMatrixLocation = gl.getUniformLocation(program, 'u_view_matrix');
|
||||
|
||||
this.vertexDataBuffer = vertexDataBuffer;
|
||||
this.indexDataBuffer = indexDataBuffer;
|
||||
|
||||
this.vertShader = vertShader;
|
||||
this.fragShader = fragShader;
|
||||
this.program = program;
|
||||
|
||||
this.indexBufferObject = indexBufferObject;
|
||||
this.vertexArray = vertexArray;
|
||||
|
||||
|
||||
this.viewMatrixLocation = viewMatrixLocation;
|
||||
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBufferObject);
|
||||
|
||||
var indexBuffer = indexDataBuffer.wordView;
|
||||
var max = CONST.MAX_SPRITES * CONST.SPRITE_INDEX_COUNT;
|
||||
|
||||
// Populate the index buffer only once
|
||||
for (var indexA = 0, indexB = 0; indexA < max; indexA += CONST.SPRITE_INDEX_COUNT, indexB += CONST.SPRITE_VERTEX_COUNT)
|
||||
{
|
||||
indexBuffer[indexA + 0] = indexB + 0;
|
||||
indexBuffer[indexA + 1] = indexB + 1;
|
||||
indexBuffer[indexA + 2] = indexB + 2;
|
||||
indexBuffer[indexA + 3] = indexB + 0;
|
||||
indexBuffer[indexA + 4] = indexB + 2;
|
||||
indexBuffer[indexA + 5] = indexB + 3;
|
||||
}
|
||||
gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, indexDataBuffer.getUsedBufferAsWord());
|
||||
|
||||
this.bind();
|
||||
|
||||
this.resize(this.width, this.height);
|
||||
|
||||
this.unbind();
|
||||
},
|
||||
|
||||
isFull: function ()
|
||||
{
|
||||
return (this.vertexDataBuffer.getByteLength() >= this.vertexDataBuffer.getByteCapacity());
|
||||
},
|
||||
|
||||
add: function (x, y, width, height, umin, vmin, umax, vmax, translateX, translateY, scaleX, scaleY, rotation)
|
||||
{
|
||||
this.manager.setBatch(this);
|
||||
|
||||
// The user must check if the buffers are full before flushing
|
||||
// this is to give freedom of when should the renderer flush. var vertexDataBuffer = this.vertexDataBuffer;
|
||||
var vertexDataBuffer = this.vertexDataBuffer;
|
||||
var vertexBuffer = vertexDataBuffer.floatView;
|
||||
var vertexOffset = vertexDataBuffer.allocate(CONST.SPRITE_VERTEX_COMPONENT_COUNT * CONST.SPRITE_VERTEX_COUNT);
|
||||
|
||||
vertexBuffer[vertexOffset++] = x;
|
||||
vertexBuffer[vertexOffset++] = y;
|
||||
vertexBuffer[vertexOffset++] = umin;
|
||||
vertexBuffer[vertexOffset++] = vmin;
|
||||
vertexBuffer[vertexOffset++] = translateX;
|
||||
vertexBuffer[vertexOffset++] = translateY;
|
||||
vertexBuffer[vertexOffset++] = scaleX;
|
||||
vertexBuffer[vertexOffset++] = scaleY;
|
||||
vertexBuffer[vertexOffset++] = rotation;
|
||||
|
||||
vertexBuffer[vertexOffset++] = x;
|
||||
vertexBuffer[vertexOffset++] = y + height;
|
||||
vertexBuffer[vertexOffset++] = umin;
|
||||
vertexBuffer[vertexOffset++] = vmax;
|
||||
vertexBuffer[vertexOffset++] = translateX;
|
||||
vertexBuffer[vertexOffset++] = translateY;
|
||||
vertexBuffer[vertexOffset++] = scaleX;
|
||||
vertexBuffer[vertexOffset++] = scaleY;
|
||||
vertexBuffer[vertexOffset++] = rotation;
|
||||
|
||||
vertexBuffer[vertexOffset++] = x + width;
|
||||
vertexBuffer[vertexOffset++] = y + height;
|
||||
vertexBuffer[vertexOffset++] = umax;
|
||||
vertexBuffer[vertexOffset++] = vmax;
|
||||
vertexBuffer[vertexOffset++] = translateX;
|
||||
vertexBuffer[vertexOffset++] = translateY;
|
||||
vertexBuffer[vertexOffset++] = scaleX;
|
||||
vertexBuffer[vertexOffset++] = scaleY;
|
||||
vertexBuffer[vertexOffset++] = rotation;
|
||||
|
||||
vertexBuffer[vertexOffset++] = x + width;
|
||||
vertexBuffer[vertexOffset++] = y;
|
||||
vertexBuffer[vertexOffset++] = umax;
|
||||
vertexBuffer[vertexOffset++] = vmin;
|
||||
vertexBuffer[vertexOffset++] = translateX;
|
||||
vertexBuffer[vertexOffset++] = translateY;
|
||||
vertexBuffer[vertexOffset++] = scaleX;
|
||||
vertexBuffer[vertexOffset++] = scaleY;
|
||||
vertexBuffer[vertexOffset++] = rotation;
|
||||
|
||||
this.elementCount += CONST.SPRITE_INDEX_COUNT;
|
||||
},
|
||||
|
||||
setTexture2D: function (texture2D, force)
|
||||
{
|
||||
var gl = this.glContext;
|
||||
|
||||
if (this.currentTexture2D !== texture2D || force)
|
||||
{
|
||||
this.flush();
|
||||
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture2D);
|
||||
|
||||
this.currentTexture2D = texture2D;
|
||||
}
|
||||
},
|
||||
|
||||
bind: function ()
|
||||
{
|
||||
var gl = this.glContext;
|
||||
|
||||
gl.useProgram(this.program);
|
||||
|
||||
gl.clearColor(0, 0, 0, 1);
|
||||
|
||||
gl.enable(gl.BLEND);
|
||||
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBufferObject);
|
||||
|
||||
BindVertexArray(gl, this.vertexArray);
|
||||
},
|
||||
|
||||
unbind: function ()
|
||||
{
|
||||
var gl = this.glContext;
|
||||
|
||||
gl.useProgram(null);
|
||||
|
||||
gl.disable(gl.BLEND);
|
||||
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
||||
},
|
||||
|
||||
flush: function ()
|
||||
{
|
||||
var gl = this.glContext;
|
||||
var vertexDataBuffer = this.vertexDataBuffer;
|
||||
|
||||
gl.bufferSubData(gl.ARRAY_BUFFER, 0, vertexDataBuffer.getUsedBufferAsFloat());
|
||||
|
||||
gl.drawElements(gl.TRIANGLES, this.elementCount, gl.UNSIGNED_SHORT, 0);
|
||||
|
||||
vertexDataBuffer.clear();
|
||||
|
||||
this.elementCount = 0;
|
||||
},
|
||||
|
||||
resize: function (width, height)
|
||||
{
|
||||
var gl = this.glContext;
|
||||
var res = this.game.config.resolution;
|
||||
|
||||
this.width = width * res;
|
||||
this.height = height * res;
|
||||
|
||||
this.view.width = this.width;
|
||||
this.view.height = this.height;
|
||||
|
||||
if (this.autoResize)
|
||||
{
|
||||
this.view.style.width = (this.width / res) + 'px';
|
||||
this.view.style.height = (this.height / res) + 'px';
|
||||
}
|
||||
|
||||
gl.viewport(0, 0, this.width, this.height);
|
||||
|
||||
gl.uniformMatrix4fv(
|
||||
this.viewMatrixLocation,
|
||||
false,
|
||||
new Float32Array([
|
||||
2 / this.view.width, 0, 0, 0,
|
||||
0, -2 / this.view.height, 0, 0,
|
||||
0, 0, 1, 1,
|
||||
-1, 1, 0, 0
|
||||
])
|
||||
);
|
||||
},
|
||||
|
||||
destroy: function ()
|
||||
{
|
||||
var gl = this.glContext;
|
||||
|
||||
if (gl)
|
||||
{
|
||||
gl.deleteShader(this.vertShader);
|
||||
gl.deleteShader(this.fragShader);
|
||||
gl.deleteProgram(this.program);
|
||||
gl.deleteBuffer(this.indexBufferObject);
|
||||
gl.deleteBuffer(this.vertexArray.buffer);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = SpriteBatch;
|
||||
|
|
18
v3/src/renderer/webgl/batches/sprite/VertexShader.js
Normal file
18
v3/src/renderer/webgl/batches/sprite/VertexShader.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
module.exports = [
|
||||
'uniform mat4 u_view_matrix;',
|
||||
'attribute vec2 a_position;',
|
||||
'attribute vec2 a_tex_coord;',
|
||||
'attribute vec2 a_translate;',
|
||||
'attribute vec2 a_scale;',
|
||||
'attribute float a_rotation;',
|
||||
'varying vec2 v_tex_coord;',
|
||||
'void main () {',
|
||||
' float t_cos = cos(a_rotation);',
|
||||
' float t_sin = sin(a_rotation);',
|
||||
' vec2 t_position = a_position * a_scale;',
|
||||
' t_position = vec2(t_position.x * t_cos - t_position.y * t_sin, t_position.x * t_sin + t_position.y * t_cos);',
|
||||
' t_position += a_translate;',
|
||||
' gl_Position = u_view_matrix * vec4(t_position, 1.0, 1.0);',
|
||||
' v_tex_coord = a_tex_coord;',
|
||||
'}'
|
||||
].join('\n');
|
23
v3/src/renderer/webgl/batches/sprite/const.js
Normal file
23
v3/src/renderer/webgl/batches/sprite/const.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
var FragmentShader = require('./FragmentShader');
|
||||
var VertexShader = require('./VertexShader');
|
||||
|
||||
var CONST = {
|
||||
|
||||
// VERTEX_SIZE = (sizeof(vec2) * 4) + (sizeof(float))
|
||||
VERTEX_SIZE: 36,
|
||||
INDEX_SIZE: 2,
|
||||
SPRITE_VERTEX_COUNT: 4,
|
||||
SPRITE_INDEX_COUNT: 6,
|
||||
|
||||
// How many 32-bit components does the vertex have.
|
||||
SPRITE_VERTEX_COMPONENT_COUNT: 9,
|
||||
|
||||
// Can't be bigger since index are 16-bit
|
||||
MAX_SPRITES: 10000,
|
||||
|
||||
VERTEX_SHADER_SOURCE: VertexShader,
|
||||
FRAGMENT_SHADER_SOURCE: FragmentShader
|
||||
|
||||
};
|
||||
|
||||
module.exports = CONST;
|
Loading…
Reference in a new issue