mirror of
https://github.com/photonstorm/phaser
synced 2024-12-18 00:53:42 +00:00
BlitterBatch pipeline working
This commit is contained in:
parent
15c45bda0e
commit
54274b782a
22 changed files with 238 additions and 435 deletions
|
@ -1,82 +1,13 @@
|
||||||
var GameObject = require('../GameObject');
|
var GameObject = require('../GameObject');
|
||||||
|
|
||||||
var BlitterWebGLRenderer = function (renderer, src, interpolationPercentage, camera)
|
var BlitterWebGLRenderer = function (renderer, gameObject, interpolationPercentage, camera)
|
||||||
{
|
{
|
||||||
if (GameObject.RENDER_MASK !== src.renderFlags || (src.cameraFilter > 0 && (src.cameraFilter & camera._id)))
|
if (GameObject.RENDER_MASK !== gameObject.renderFlags || (gameObject.cameraFilter > 0 && (gameObject.cameraFilter & camera._id)))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var list = src.getRenderList();
|
renderer.blitterBatch.drawBlitter(gameObject, camera)
|
||||||
var blitterBatch = renderer.blitterBatch;
|
|
||||||
var cameraMatrix = camera.matrix.matrix;
|
|
||||||
var a = cameraMatrix[0];
|
|
||||||
var b = cameraMatrix[1];
|
|
||||||
var c = cameraMatrix[2];
|
|
||||||
var d = cameraMatrix[3];
|
|
||||||
var e = cameraMatrix[4];
|
|
||||||
var f = cameraMatrix[5];
|
|
||||||
var cameraScrollX = camera.scrollX * src.scrollFactorX;
|
|
||||||
var cameraScrollY = camera.scrollY * src.scrollFactorY;
|
|
||||||
var renderTarget = src.renderTarget;
|
|
||||||
|
|
||||||
// Render bobs
|
|
||||||
|
|
||||||
for (var i = 0, l = list.length; i < l; i++)
|
|
||||||
{
|
|
||||||
var bob = list[i];
|
|
||||||
var frame = bob.frame;
|
|
||||||
var alpha = bob.alpha;
|
|
||||||
var vertexDataBuffer = blitterBatch.vertexDataBuffer;
|
|
||||||
var vertexBuffer = vertexDataBuffer.floatView;
|
|
||||||
var vertexOffset = 0;
|
|
||||||
var uvs = frame.uvs;
|
|
||||||
var width = frame.width * (bob.flipX ? -1 : 1);
|
|
||||||
var height = frame.height * (bob.flipY ? -1 : 1);
|
|
||||||
var x = bob.x + frame.x - cameraScrollX + ((frame.width) * (bob.flipX ? 1 : 0.0));
|
|
||||||
var y = bob.y + frame.y - cameraScrollY + ((frame.height) * (bob.flipY ? 1 : 0.0));
|
|
||||||
var xw = x + width;
|
|
||||||
var yh = y + height;
|
|
||||||
var tx = x * a + y * c + e;
|
|
||||||
var ty = x * b + y * d + f;
|
|
||||||
var txw = xw * a + yh * c + e;
|
|
||||||
var tyh = xw * b + yh * d + f;
|
|
||||||
|
|
||||||
if (blitterBatch.elementCount >= blitterBatch.maxParticles)
|
|
||||||
{
|
|
||||||
blitterBatch.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer.setRenderer(blitterBatch, frame.texture.source[frame.sourceIndex].glTexture, camera, renderTarget);
|
|
||||||
vertexOffset = vertexDataBuffer.allocate(20);
|
|
||||||
blitterBatch.elementCount += 6;
|
|
||||||
x += frame.x;
|
|
||||||
y += frame.y;
|
|
||||||
|
|
||||||
vertexBuffer[vertexOffset++] = tx;
|
|
||||||
vertexBuffer[vertexOffset++] = ty;
|
|
||||||
vertexBuffer[vertexOffset++] = uvs.x0;
|
|
||||||
vertexBuffer[vertexOffset++] = uvs.y0;
|
|
||||||
vertexBuffer[vertexOffset++] = alpha;
|
|
||||||
|
|
||||||
vertexBuffer[vertexOffset++] = tx;
|
|
||||||
vertexBuffer[vertexOffset++] = tyh;
|
|
||||||
vertexBuffer[vertexOffset++] = uvs.x1;
|
|
||||||
vertexBuffer[vertexOffset++] = uvs.y1;
|
|
||||||
vertexBuffer[vertexOffset++] = alpha;
|
|
||||||
|
|
||||||
vertexBuffer[vertexOffset++] = txw;
|
|
||||||
vertexBuffer[vertexOffset++] = tyh;
|
|
||||||
vertexBuffer[vertexOffset++] = uvs.x2;
|
|
||||||
vertexBuffer[vertexOffset++] = uvs.y2;
|
|
||||||
vertexBuffer[vertexOffset++] = alpha;
|
|
||||||
|
|
||||||
vertexBuffer[vertexOffset++] = txw;
|
|
||||||
vertexBuffer[vertexOffset++] = ty;
|
|
||||||
vertexBuffer[vertexOffset++] = uvs.x3;
|
|
||||||
vertexBuffer[vertexOffset++] = uvs.y3;
|
|
||||||
vertexBuffer[vertexOffset++] = alpha;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = BlitterWebGLRenderer;
|
module.exports = BlitterWebGLRenderer;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
var Class = require('../../../utils/Class');
|
var Class = require('../../../utils/Class');
|
||||||
var GameObject = require('../../GameObject');
|
var GameObject = require('../../GameObject');
|
||||||
var Components = require('../../components');
|
var Components = require('../../components');
|
||||||
var CONST = require('../../../renderer/webgl/renderers/tilemaprenderer/const');
|
var CONST = require('../../../renderer/webgl/pipelines/tilemaprenderer/const');
|
||||||
var StaticTilemapLayerRender = require('./StaticTilemapLayerRender');
|
var StaticTilemapLayerRender = require('./StaticTilemapLayerRender');
|
||||||
var TilemapComponents = require('../components');
|
var TilemapComponents = require('../components');
|
||||||
|
|
||||||
|
|
|
@ -1,124 +1,163 @@
|
||||||
var Class = require('../../../utils/Class');
|
var Class = require('../../utils/Class');
|
||||||
|
|
||||||
var Pipeline = new Class({
|
var Pipeline = new Class({
|
||||||
|
|
||||||
initialize:
|
initialize:
|
||||||
|
|
||||||
function Pipeline(config)
|
function Pipeline(config)
|
||||||
{
|
{
|
||||||
|
this.name = config.name;
|
||||||
|
this.game = config.game;
|
||||||
|
this.view = config.game.canvas;
|
||||||
|
this.resolution = config.game.config.resolution;
|
||||||
|
this.width = config.game.config.width * this.resolution;
|
||||||
|
this.height = config.game.config.height * this.resolution;
|
||||||
|
this.gl = config.gl;
|
||||||
|
this.vertexCount = 0;
|
||||||
|
this.vertexCapacity = config.vertexCapacity;
|
||||||
|
this.manager = config.manager;
|
||||||
|
this.resources = config.manager.resourceManager;
|
||||||
|
this.vertexData = new ArrayBuffer(config.vertexCapacity * config.vertexSize);
|
||||||
|
this.vertexBuffer = null;
|
||||||
|
this.program = null;
|
||||||
|
this.vertexLayout = config.vertexLayout;
|
||||||
|
this.vertexSize = config.vertexSize;
|
||||||
|
this.currentRenderTarget = null;
|
||||||
|
this.currentProgram = null;
|
||||||
|
this.topology = config.topology;
|
||||||
|
this.onBeginPassTarget = config.onBeginPassTarget || null;
|
||||||
|
this.onEndPassTarget = config.onBeginPassTarget || null;
|
||||||
|
this.onFlushTarget = config.onFlushTarget || null;
|
||||||
|
this.onBindTarget = config.onBindTarget || null;
|
||||||
|
this.onResizeTarget = config.onResizeTarget || null;
|
||||||
|
this.onBeginPass = config.onBeginPass || function onBeginPassStub(pipeline) {};
|
||||||
|
this.onEndPass = config.onEndPass || function onEndPassStub(pipeline) {};
|
||||||
|
this.onFlush = config.onFlush || function onFlushStub(pipeline) {};
|
||||||
|
this.onBind = config.onBind || function onBindStub(pipeline) {};
|
||||||
|
this.onResize = config.onResize || function onResize(width, height, resolution) {};
|
||||||
|
|
||||||
|
// Initialize Shaders and Buffers
|
||||||
|
{
|
||||||
|
var gl = this.gl;
|
||||||
|
var resources = this.resources;
|
||||||
|
var vertexSize = this.vertexSize;
|
||||||
|
var vertexLayout = this.vertexLayout;
|
||||||
|
var program = resources.createShader(this.name, config.shader);
|
||||||
|
var vertexBuffer = resources.createBuffer(gl.ARRAY_BUFFER, this.vertexData.byteLength, gl.STREAM_DRAW);
|
||||||
|
|
||||||
this.name = config.name;
|
for (var key in vertexLayout)
|
||||||
this.game = config.game;
|
{
|
||||||
this.view = config.game.canvas;
|
var element = vertexLayout[key];
|
||||||
this.resolution = config.game.config.resolution;
|
|
||||||
this.width = config.game.config.width * this.resolution;
|
|
||||||
this.height = config.game.config.height * this.resolution;
|
|
||||||
this.glContext = config.gl;
|
|
||||||
this.vertexCount = 0;
|
|
||||||
this.vertexCapacity = config.vertexCapacity;
|
|
||||||
this.manager = config.manager;
|
|
||||||
this.resources = config.manager.resourceManager;
|
|
||||||
this.vertexData = new ArrayBuffer(config.vertexCapacity * config.vertexSize);
|
|
||||||
this.vertexBuffer = null;
|
|
||||||
this.program = null;
|
|
||||||
this.vertexLayout = config.vertexLayout;
|
|
||||||
this.vertexSize = config.vertexSize;
|
|
||||||
this.currentRenderTarget = null;
|
|
||||||
this.currentProgram = null;
|
|
||||||
this.topology = config.topology;
|
|
||||||
|
|
||||||
// Initialize Shaders and Buffers
|
|
||||||
{
|
|
||||||
var gl = this.glContext;
|
|
||||||
var resources = this.resources;
|
|
||||||
var vertexSize = this.vertexSize;
|
|
||||||
var vertexLayout = this.vertexLayout;
|
|
||||||
var program = resources.createShader(this.name, config.shader);
|
|
||||||
var vertexBuffer = resources.createBuffer(gl.ARRAY_BUFFER, this.vertexCapacity, gl.STREAM_DRAW);
|
|
||||||
|
|
||||||
for (var key in vertexLayout)
|
vertexBuffer.addAttribute(
|
||||||
{
|
program.getAttribLocation(key),
|
||||||
var element = vertexLayout[key];
|
element.size,
|
||||||
|
element.type,
|
||||||
|
element.normalize,
|
||||||
|
vertexSize,
|
||||||
|
element.offset
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
vertexBuffer.addAttribute(
|
this.vertexBuffer = vertexBuffer;
|
||||||
program.getAttribLocation(key),
|
this.program = program;
|
||||||
element.size,
|
}
|
||||||
element.type,
|
},
|
||||||
element.normalize,
|
|
||||||
vertexSize,
|
|
||||||
element.offset
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.vertexBuffer = vertexBuffer;
|
shouldFlush: function ()
|
||||||
this.program = program;
|
{
|
||||||
}
|
return this.vertexCount >= this.vertexCapacity;
|
||||||
},
|
},
|
||||||
|
|
||||||
beginDraw: function (renderTarget, program)
|
bind: function ()
|
||||||
{
|
{
|
||||||
if (this.currentRenderTarget !== null ||
|
this.onBind.call(this.onBindTarget, this);
|
||||||
this.currentProgram !== null)
|
return this;
|
||||||
{
|
},
|
||||||
this.draw();
|
|
||||||
this.endDraw();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.currentRenderTarget = (renderTarget || null);
|
resize: function (width, height, resolution)
|
||||||
this.currentProgram = (program || this.program);
|
{
|
||||||
this.currentProgram.bind();
|
this.onResize.call(this.onResizeTarget, width, height, resolution);
|
||||||
this.vertexBuffer.bind();
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
gl.bindFramebuffer(gl.FRAMEBUFFER, this.currentRenderTarget.framebufferObject);
|
beginPass: function (renderTarget, program)
|
||||||
|
{
|
||||||
|
if (this.currentRenderTarget !== null ||
|
||||||
|
this.currentProgram !== null)
|
||||||
|
{
|
||||||
|
this.flush();
|
||||||
|
this.endPass();
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
this.currentRenderTarget = (renderTarget || null);
|
||||||
},
|
this.currentProgram = (program || this.program);
|
||||||
|
this.currentProgram.bind();
|
||||||
|
this.vertexBuffer.bind();
|
||||||
|
|
||||||
draw: function ()
|
if (this.currentRenderTarget !== null)
|
||||||
{
|
{
|
||||||
var gl = this.glContext;
|
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.currentRenderTarget.framebufferObject);
|
||||||
var vertexCount = this.vertexCount;
|
}
|
||||||
var vertexBuffer = this.vertexBuffer;
|
else
|
||||||
var vertexData = this.vertexData;
|
{
|
||||||
var topology = this.topology;
|
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);
|
||||||
|
}
|
||||||
|
|
||||||
if (vertexCount === 0) return;
|
this.onBeginPass.call(this.onBeginPassTarget, this);
|
||||||
|
|
||||||
vertexBuffer.updateResource(vertexData, 0);
|
return this;
|
||||||
gl.drawArrays(topology, 0, vertexCount);
|
},
|
||||||
|
|
||||||
this.vertexCount = 0;
|
flush: function ()
|
||||||
|
{
|
||||||
|
var gl = this.gl;
|
||||||
|
var vertexCount = this.vertexCount;
|
||||||
|
var vertexBuffer = this.vertexBuffer;
|
||||||
|
var vertexData = this.vertexData;
|
||||||
|
var topology = this.topology;
|
||||||
|
|
||||||
return this;
|
if (vertexCount === 0) return;
|
||||||
},
|
|
||||||
|
|
||||||
endDraw: function ()
|
vertexBuffer.updateResource(vertexData, 0);
|
||||||
{
|
gl.drawArrays(topology, 0, vertexCount);
|
||||||
var renderTarget = this.currentRenderTarget;
|
|
||||||
var program = this.currentProgram;
|
|
||||||
|
|
||||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
this.vertexCount = 0;
|
||||||
|
|
||||||
this.currentRenderTarget = null;
|
this.onFlush.call(this.onFlushTarget, this);
|
||||||
this.program = null;
|
|
||||||
this.vertexCount = 0;
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
destroy: function ()
|
endPass: function ()
|
||||||
{
|
{
|
||||||
var resources = this.resources;
|
var renderTarget = this.currentRenderTarget;
|
||||||
|
var program = this.currentProgram;
|
||||||
|
|
||||||
|
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);
|
||||||
|
|
||||||
|
this.currentRenderTarget = null;
|
||||||
|
this.currentProgram = null;
|
||||||
|
this.vertexCount = 0;
|
||||||
|
|
||||||
|
this.onEndPass.call(this.onEndPassTarget, this);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function ()
|
||||||
|
{
|
||||||
|
var resources = this.resources;
|
||||||
|
|
||||||
resources.deleteShader(this.program);
|
resources.deleteShader(this.program);
|
||||||
resources.deleteBuffer(this.vertexBuffer);
|
resources.deleteBuffer(this.vertexBuffer);
|
||||||
|
|
||||||
this.program = null;
|
this.program = null;
|
||||||
this.vertexBuffer = null;
|
this.vertexBuffer = null;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = BaseRenderer;
|
module.exports = Pipeline;
|
||||||
|
|
|
@ -6,28 +6,28 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var BlendModes = require('../BlendModes');
|
var BlendModes = require('../BlendModes');
|
||||||
var BlitterBatch = require('./renderers/blitterbatch/BlitterBatch');
|
var BlitterBatch = require('./pipelines/blitterbatch/BlitterBatch');
|
||||||
var Class = require('../../utils/Class');
|
var Class = require('../../utils/Class');
|
||||||
var CONST = require('../../const');
|
var CONST = require('../../const');
|
||||||
var EffectRenderer = require('./renderers/effectrenderer/EffectRenderer');
|
var EffectRenderer = require('./pipelines/effectrenderer/EffectRenderer');
|
||||||
var IsSizePowerOfTwo = require('../../math/pow2/IsSizePowerOfTwo');
|
var IsSizePowerOfTwo = require('../../math/pow2/IsSizePowerOfTwo');
|
||||||
var MaskRenderer = require('./renderers/maskrenderer/MaskRenderer');
|
var MaskRenderer = require('./pipelines/maskrenderer/MaskRenderer');
|
||||||
var QuadBatch = require('./renderers/quadbatch/QuadBatch');
|
var QuadBatch = require('./pipelines/quadbatch/QuadBatch');
|
||||||
var ParticleRenderer = require('./renderers/particlerenderer/ParticleRenderer');
|
var ParticleRenderer = require('./pipelines/particlerenderer/ParticleRenderer');
|
||||||
var ResourceManager = require('./ResourceManager');
|
var ResourceManager = require('./ResourceManager');
|
||||||
var Resources = require('./resources');
|
var Resources = require('./resources');
|
||||||
var ScaleModes = require('../ScaleModes');
|
var ScaleModes = require('../ScaleModes');
|
||||||
var ShapeBatch = require('./renderers/shapebatch/ShapeBatch');
|
var ShapeBatch = require('./pipelines/shapebatch/ShapeBatch');
|
||||||
var SpriteBatch = require('./renderers/spritebatch/SpriteBatch');
|
var SpriteBatch = require('./pipelines/spritebatch/SpriteBatch');
|
||||||
var TileBatch = require('./renderers/tilebatch/TileBatch');
|
var TileBatch = require('./pipelines/tilebatch/TileBatch');
|
||||||
var TilemapRenderer = require('./renderers/tilemaprenderer/TilemapRenderer');
|
var TilemapRenderer = require('./pipelines/tilemaprenderer/TilemapRenderer');
|
||||||
var WebGLSnapshot = require('../snapshot/WebGLSnapshot');
|
var WebGLSnapshot = require('../snapshot/WebGLSnapshot');
|
||||||
|
|
||||||
var WebGLRenderer = new Class({
|
var WebGLpipeline = new Class({
|
||||||
|
|
||||||
initialize:
|
initialize:
|
||||||
|
|
||||||
function WebGLRenderer (game)
|
function WebGLpipeline (game)
|
||||||
{
|
{
|
||||||
var _this = this;
|
var _this = this;
|
||||||
this.game = game;
|
this.game = game;
|
||||||
|
@ -40,10 +40,10 @@ var WebGLRenderer = new Class({
|
||||||
this.view = game.canvas;
|
this.view = game.canvas;
|
||||||
this.view.addEventListener('webglcontextlost', function (evt) {
|
this.view.addEventListener('webglcontextlost', function (evt) {
|
||||||
var callbacks = _this.onContextLostCallbacks;
|
var callbacks = _this.onContextLostCallbacks;
|
||||||
var renderers = _this.rendererArray;
|
var pipelines = _this.pipelines;
|
||||||
for (var index = 0; index < renderers.length; ++index)
|
for (var index = 0; index < pipelines.length; ++index)
|
||||||
{
|
{
|
||||||
renderers[index].destroy();
|
pipelines[index].destroy();
|
||||||
}
|
}
|
||||||
_this.contextLost = true;
|
_this.contextLost = true;
|
||||||
for (var index = 0; index < callbacks.length; ++index)
|
for (var index = 0; index < callbacks.length; ++index)
|
||||||
|
@ -55,7 +55,7 @@ var WebGLRenderer = new Class({
|
||||||
|
|
||||||
this.view.addEventListener('webglcontextrestored', function (evt) {
|
this.view.addEventListener('webglcontextrestored', function (evt) {
|
||||||
var callbacks = _this.onContextRestoredCallbacks;
|
var callbacks = _this.onContextRestoredCallbacks;
|
||||||
_this.rendererArray.length = 0;
|
_this.pipelines.length = 0;
|
||||||
_this.resourceManager.shaderCache = {};
|
_this.resourceManager.shaderCache = {};
|
||||||
_this.resourceManager.shaderCount = 0;
|
_this.resourceManager.shaderCount = 0;
|
||||||
_this.contextLost = false;
|
_this.contextLost = false;
|
||||||
|
@ -95,14 +95,14 @@ var WebGLRenderer = new Class({
|
||||||
this.gl = null;
|
this.gl = null;
|
||||||
this.extensions = null;
|
this.extensions = null;
|
||||||
this.extensionList = {};
|
this.extensionList = {};
|
||||||
this.rendererArray = [];
|
this.pipelines = [];
|
||||||
this.blitterBatch = null;
|
this.blitterBatch = null;
|
||||||
this.aaQuadBatch = null;
|
this.aaQuadBatch = null;
|
||||||
this.spriteBatch = null;
|
this.spriteBatch = null;
|
||||||
this.shapeBatch = null;
|
this.shapeBatch = null;
|
||||||
this.effectRenderer = null;
|
this.EffectRenderer = null;
|
||||||
this.maskRenderer = null;
|
this.MaskRenderer = null;
|
||||||
this.currentRenderer = null;
|
this.currentPipeline = null;
|
||||||
this.currentTexture = [];
|
this.currentTexture = [];
|
||||||
this.shaderCache = {};
|
this.shaderCache = {};
|
||||||
this.currentShader = null;
|
this.currentShader = null;
|
||||||
|
@ -130,7 +130,7 @@ var WebGLRenderer = new Class({
|
||||||
if (!this.gl)
|
if (!this.gl)
|
||||||
{
|
{
|
||||||
this.contextLost = true;
|
this.contextLost = true;
|
||||||
throw new Error('This browser does not support WebGL. Try using the Canvas renderer.');
|
throw new Error('This browser does not support WebGL. Try using the Canvas pipeline.');
|
||||||
}
|
}
|
||||||
var gl = this.gl;
|
var gl = this.gl;
|
||||||
|
|
||||||
|
@ -164,16 +164,16 @@ var WebGLRenderer = new Class({
|
||||||
|
|
||||||
this.blendMode = -1;
|
this.blendMode = -1;
|
||||||
this.extensions = gl.getSupportedExtensions();
|
this.extensions = gl.getSupportedExtensions();
|
||||||
this.blitterBatch = this.addRenderer(new BlitterBatch(this.game, gl, this));
|
this.blitterBatch = this.addPipeline(new BlitterBatch(this.game, gl, this));
|
||||||
this.quadBatch = this.addRenderer(new QuadBatch(this.game, gl, this));
|
//this.quadBatch = this.addPipeline(new QuadBatch(this.game, gl, this));
|
||||||
this.spriteBatch = this.addRenderer(new SpriteBatch(this.game, gl, this));
|
//this.spriteBatch = this.addPipeline(new SpriteBatch(this.game, gl, this));
|
||||||
this.shapeBatch = this.addRenderer(new ShapeBatch(this.game, gl, this));
|
//this.shapeBatch = this.addPipeline(new ShapeBatch(this.game, gl, this));
|
||||||
this.effectRenderer = this.addRenderer(new EffectRenderer(this.game, gl, this));
|
//this.EffectRenderer = this.addPipeline(new EffectRenderer(this.game, gl, this));
|
||||||
this.tileBatch = this.addRenderer(new TileBatch(this.game, gl, this));
|
//this.tileBatch = this.addPipeline(new TileBatch(this.game, gl, this));
|
||||||
this.tilemapRenderer = this.addRenderer(new TilemapRenderer(this.game, gl, this));
|
//this.TilemapRenderer = this.addPipeline(new TilemapRenderer(this.game, gl, this));
|
||||||
this.particleRenderer = this.addRenderer(new ParticleRenderer(this.game, gl, this));
|
//this.ParticleRenderer = this.addPipeline(new ParticleRenderer(this.game, gl, this));
|
||||||
this.maskRenderer = this.addRenderer(new MaskRenderer(this.game, gl, this));
|
//this.MaskRenderer = this.addPipeline(new MaskRenderer(this.game, gl, this));
|
||||||
this.currentRenderer = this.spriteBatch;
|
this.currentPipeline = this.blitterBatch;
|
||||||
this.currentVertexBuffer = null;
|
this.currentVertexBuffer = null;
|
||||||
this.setBlendMode(0);
|
this.setBlendMode(0);
|
||||||
this.resize(this.width, this.height);
|
this.resize(this.width, this.height);
|
||||||
|
@ -298,7 +298,8 @@ var WebGLRenderer = new Class({
|
||||||
{
|
{
|
||||||
var gl = this.gl;
|
var gl = this.gl;
|
||||||
|
|
||||||
this.currentRenderer.flush();
|
this.currentPipeline.flush();
|
||||||
|
this.currentPipeline.endPass();
|
||||||
|
|
||||||
gl.activeTexture(gl.TEXTURE0 + unit);
|
gl.activeTexture(gl.TEXTURE0 + unit);
|
||||||
|
|
||||||
|
@ -315,15 +316,14 @@ var WebGLRenderer = new Class({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setRenderer: function (renderer, texture, renderTarget)
|
setPipeline: function (pipeline)
|
||||||
{
|
{
|
||||||
this.setTexture(texture);
|
if (this.currentPipeline !== pipeline ||
|
||||||
this.setRenderTarget(renderTarget);
|
this.currentPipeline.shouldFlush())
|
||||||
|
|
||||||
if (this.currentRenderer !== renderer || this.currentRenderer.shouldFlush())
|
|
||||||
{
|
{
|
||||||
this.currentRenderer.flush();
|
this.currentPipeline.flush();
|
||||||
this.currentRenderer = renderer;
|
this.currentPipeline.endPass();
|
||||||
|
this.currentPipeline = pipeline;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -333,7 +333,7 @@ var WebGLRenderer = new Class({
|
||||||
|
|
||||||
if (this.currentRenderTarget !== renderTarget)
|
if (this.currentRenderTarget !== renderTarget)
|
||||||
{
|
{
|
||||||
this.currentRenderer.flush();
|
this.currentPipeline.flush();
|
||||||
|
|
||||||
if (renderTarget !== null)
|
if (renderTarget !== null)
|
||||||
{
|
{
|
||||||
|
@ -374,13 +374,13 @@ var WebGLRenderer = new Class({
|
||||||
|
|
||||||
this.gl.viewport(0, 0, this.width, this.height);
|
this.gl.viewport(0, 0, this.width, this.height);
|
||||||
|
|
||||||
for (var i = 0, l = this.rendererArray.length; i < l; ++i)
|
for (var i = 0, l = this.pipelines.length; i < l; ++i)
|
||||||
{
|
{
|
||||||
this.rendererArray[i].bind();
|
//this.pipelines[i].bind();
|
||||||
this.rendererArray[i].resize(width, height, resolution);
|
//this.pipelines[i].resize(width, height, resolution);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.currentRenderer.bind();
|
//this.currentPipeline.bind();
|
||||||
},
|
},
|
||||||
|
|
||||||
// Call at the start of the render loop
|
// Call at the start of the render loop
|
||||||
|
@ -442,21 +442,18 @@ var WebGLRenderer = new Class({
|
||||||
{
|
{
|
||||||
var color = camera.backgroundColor;
|
var color = camera.backgroundColor;
|
||||||
|
|
||||||
quadBatch.bind();
|
//quadBatch.bind();
|
||||||
|
//quadBatch.add(
|
||||||
quadBatch.add(
|
// camera.x, camera.y, camera.width, camera.height,
|
||||||
camera.x, camera.y, camera.width, camera.height,
|
// color.redGL, color.greenGL, color.blueGL, color.alphaGL
|
||||||
color.redGL, color.greenGL, color.blueGL, color.alphaGL
|
//);
|
||||||
);
|
//quadBatch.flush();
|
||||||
|
//this.currentPipeline.bind();
|
||||||
quadBatch.flush();
|
|
||||||
|
|
||||||
this.currentRenderer.bind();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var list = children.list;
|
var list = children.list;
|
||||||
var length = list.length;
|
var length = list.length;
|
||||||
var renderer;
|
var pipeline;
|
||||||
|
|
||||||
for (var index = 0; index < length; ++index)
|
for (var index = 0; index < length; ++index)
|
||||||
{
|
{
|
||||||
|
@ -485,42 +482,39 @@ var WebGLRenderer = new Class({
|
||||||
child.mask.postRenderWebGL(this, child);
|
child.mask.postRenderWebGL(this, child);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer = this.currentRenderer;
|
pipeline = this.currentPipeline;
|
||||||
|
|
||||||
if (renderer.isFull() || renderer.shouldFlush())
|
if (pipeline.shouldFlush())
|
||||||
{
|
{
|
||||||
renderer.flush();
|
pipeline.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.currentRenderer.flush();
|
this.currentPipeline.flush();
|
||||||
|
|
||||||
if (camera._fadeAlpha > 0 || camera._flashAlpha > 0)
|
if (camera._fadeAlpha > 0 || camera._flashAlpha > 0)
|
||||||
{
|
{
|
||||||
this.setRenderTarget(null);
|
this.setRenderTarget(null);
|
||||||
this.setBlendMode(BlendModes.NORMAL);
|
this.setBlendMode(BlendModes.NORMAL);
|
||||||
|
|
||||||
// fade rendering
|
// // fade rendering
|
||||||
quadBatch.add(
|
// quadBatch.add(
|
||||||
camera.x, camera.y, camera.width, camera.height,
|
// camera.x, camera.y, camera.width, camera.height,
|
||||||
camera._fadeRed,
|
// camera._fadeRed,
|
||||||
camera._fadeGreen,
|
// camera._fadeGreen,
|
||||||
camera._fadeBlue,
|
// camera._fadeBlue,
|
||||||
camera._fadeAlpha
|
// camera._fadeAlpha
|
||||||
);
|
// );
|
||||||
|
// // flash rendering
|
||||||
// flash rendering
|
// quadBatch.add(
|
||||||
quadBatch.add(
|
// camera.x, camera.y, camera.width, camera.height,
|
||||||
camera.x, camera.y, camera.width, camera.height,
|
// camera._flashRed,
|
||||||
camera._flashRed,
|
// camera._flashGreen,
|
||||||
camera._flashGreen,
|
// camera._flashBlue,
|
||||||
camera._flashBlue,
|
// camera._flashAlpha
|
||||||
camera._flashAlpha
|
// );
|
||||||
);
|
// quadBatch.flush();
|
||||||
|
// this.currentPipeline.bind();
|
||||||
quadBatch.flush();
|
|
||||||
|
|
||||||
this.currentRenderer.bind();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.scissor.enabled)
|
if (this.scissor.enabled)
|
||||||
|
@ -534,7 +528,7 @@ var WebGLRenderer = new Class({
|
||||||
{
|
{
|
||||||
if (this.contextLost) return;
|
if (this.contextLost) return;
|
||||||
|
|
||||||
this.currentRenderer.flush();
|
this.currentPipeline.flush();
|
||||||
|
|
||||||
if (this.snapshotCallback)
|
if (this.snapshotCallback)
|
||||||
{
|
{
|
||||||
|
@ -565,13 +559,13 @@ var WebGLRenderer = new Class({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var renderer = this.currentRenderer;
|
var pipeline = this.currentPipeline;
|
||||||
|
|
||||||
if (this.blendMode !== newBlendMode)
|
if (this.blendMode !== newBlendMode)
|
||||||
{
|
{
|
||||||
if (renderer)
|
if (pipeline)
|
||||||
{
|
{
|
||||||
renderer.flush();
|
pipeline.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
var blend = this.blendModes[newBlendMode].func;
|
var blend = this.blendModes[newBlendMode].func;
|
||||||
|
@ -592,14 +586,14 @@ var WebGLRenderer = new Class({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
addRenderer: function (rendererInstance)
|
addPipeline: function (pipelineInstance)
|
||||||
{
|
{
|
||||||
var index = this.rendererArray.indexOf(rendererInstance);
|
var index = this.pipelines.indexOf(pipelineInstance);
|
||||||
|
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
{
|
{
|
||||||
this.rendererArray.push(rendererInstance);
|
this.pipelines.push(pipelineInstance);
|
||||||
return rendererInstance;
|
return pipelineInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -640,7 +634,7 @@ var WebGLRenderer = new Class({
|
||||||
|
|
||||||
if (dstTexture != this.currentTexture[0])
|
if (dstTexture != this.currentTexture[0])
|
||||||
{
|
{
|
||||||
this.currentRenderer.flush();
|
this.currentPipeline.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.activeTexture(gl.TEXTURE0);
|
gl.activeTexture(gl.TEXTURE0);
|
||||||
|
@ -684,4 +678,4 @@ var WebGLRenderer = new Class({
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = WebGLRenderer;
|
module.exports = WebGLpipeline;
|
||||||
|
|
|
@ -11,7 +11,7 @@ var BlitterBatch = new Class({
|
||||||
|
|
||||||
function BlitterBatch(game, gl, manager)
|
function BlitterBatch(game, gl, manager)
|
||||||
{
|
{
|
||||||
Pipeline.call(this, game, {
|
Pipeline.call(this, {
|
||||||
name: 'BlitterBatch',
|
name: 'BlitterBatch',
|
||||||
game: game,
|
game: game,
|
||||||
gl: gl,
|
gl: gl,
|
||||||
|
@ -19,7 +19,7 @@ var BlitterBatch = new Class({
|
||||||
topology: gl.TRIANGLES,
|
topology: gl.TRIANGLES,
|
||||||
shader: ShaderSource,
|
shader: ShaderSource,
|
||||||
vertexCapacity: 12000,
|
vertexCapacity: 12000,
|
||||||
|
|
||||||
vertexSize:
|
vertexSize:
|
||||||
Float32Array.BYTES_PER_ELEMENT * 2 +
|
Float32Array.BYTES_PER_ELEMENT * 2 +
|
||||||
Float32Array.BYTES_PER_ELEMENT * 2 +
|
Float32Array.BYTES_PER_ELEMENT * 2 +
|
||||||
|
@ -37,7 +37,7 @@ var BlitterBatch = new Class({
|
||||||
type: gl.FLOAT,
|
type: gl.FLOAT,
|
||||||
normalize: false,
|
normalize: false,
|
||||||
offset: Float32Array.BYTES_PER_ELEMENT * 2
|
offset: Float32Array.BYTES_PER_ELEMENT * 2
|
||||||
}
|
},
|
||||||
'inTint': {
|
'inTint': {
|
||||||
size: 4,
|
size: 4,
|
||||||
type: gl.UNSIGNED_BYTE,
|
type: gl.UNSIGNED_BYTE,
|
||||||
|
@ -75,11 +75,15 @@ var BlitterBatch = new Class({
|
||||||
|
|
||||||
drawBlitter: function (blitter, camera)
|
drawBlitter: function (blitter, camera)
|
||||||
{
|
{
|
||||||
this.beginDraw(blitter.shader, blitter.renderTarget);
|
this.manager.setPipeline(this);
|
||||||
|
this.beginPass(blitter.shader, blitter.renderTarget);
|
||||||
|
|
||||||
var vertexViewF32 = this.vertexViewF32;
|
var vertexViewF32 = this.vertexViewF32;
|
||||||
var vertexViewU32 = this.vertexViewU32;
|
var vertexViewU32 = this.vertexViewU32;
|
||||||
|
var orthoViewMatrix = this.orthoViewMatrix;
|
||||||
var manager = this.manager;
|
var manager = this.manager;
|
||||||
|
var gl = this.gl;
|
||||||
|
var shader = this.currentProgram;
|
||||||
var list = blitter.getRenderList();
|
var list = blitter.getRenderList();
|
||||||
var length = list.length;
|
var length = list.length;
|
||||||
var cameraMatrix = camera.matrix.matrix;
|
var cameraMatrix = camera.matrix.matrix;
|
||||||
|
@ -94,6 +98,11 @@ var BlitterBatch = new Class({
|
||||||
var batchCount = Math.ceil(length / 2000);
|
var batchCount = Math.ceil(length / 2000);
|
||||||
var batchOffset = 0;
|
var batchOffset = 0;
|
||||||
|
|
||||||
|
orthoViewMatrix[0] = +2.0 / this.width;
|
||||||
|
orthoViewMatrix[5] = -2.0 / this.height;
|
||||||
|
|
||||||
|
shader.setConstantMatrix4x4(shader.getUniformLocation('uOrthoMatrix'), orthoViewMatrix);
|
||||||
|
|
||||||
for (var batchIndex = 0; batchIndex < batchCount; ++batchIndex)
|
for (var batchIndex = 0; batchIndex < batchCount; ++batchIndex)
|
||||||
{
|
{
|
||||||
var batchSize = Math.min(length, 2000);
|
var batchSize = Math.min(length, 2000);
|
||||||
|
@ -122,8 +131,7 @@ var BlitterBatch = new Class({
|
||||||
// Bind Texture if texture wasn't bound.
|
// Bind Texture if texture wasn't bound.
|
||||||
// This needs to be here because of multiple
|
// This needs to be here because of multiple
|
||||||
// texture atlas.
|
// texture atlas.
|
||||||
|
manager.setTexture(frame.texture.source[frame.sourceIndex].glTexture, 0);
|
||||||
// manager.setTexture(frame.texture.source[frame.sourceIndex].glTexture, 0);
|
|
||||||
|
|
||||||
vertexViewF32[vertexOffset + 0] = tx;
|
vertexViewF32[vertexOffset + 0] = tx;
|
||||||
vertexViewF32[vertexOffset + 1] = ty;
|
vertexViewF32[vertexOffset + 1] = ty;
|
||||||
|
@ -168,10 +176,10 @@ var BlitterBatch = new Class({
|
||||||
length -= batchSize;
|
length -= batchSize;
|
||||||
|
|
||||||
this.vertexCount = (batchSize * 6);
|
this.vertexCount = (batchSize * 6);
|
||||||
this.draw();
|
this.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.endDraw();
|
this.endPass();
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
|
@ -1,169 +0,0 @@
|
||||||
var Class = require('../../../../utils/Class');
|
|
||||||
var CONST = require('./const');
|
|
||||||
var DataBuffer16 = require('../../utils/DataBuffer16');
|
|
||||||
var DataBuffer32 = require('../../utils/DataBuffer32');
|
|
||||||
var PHASER_CONST = require('../../../../const');
|
|
||||||
var TexturedAndAlphaShader = require('../../shaders/TexturedAndAlphaShader');
|
|
||||||
|
|
||||||
var BlitterBatch = new Class({
|
|
||||||
|
|
||||||
initialize:
|
|
||||||
|
|
||||||
function BlitterBatch (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.maxParticles = null;
|
|
||||||
this.shader = null;
|
|
||||||
this.vertexBufferObject = 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);
|
|
||||||
},
|
|
||||||
|
|
||||||
init: function (gl)
|
|
||||||
{
|
|
||||||
var vertexDataBuffer = new DataBuffer32(CONST.VERTEX_SIZE * CONST.PARTICLE_VERTEX_COUNT * CONST.MAX_PARTICLES);
|
|
||||||
var indexDataBuffer = new DataBuffer16(CONST.INDEX_SIZE * CONST.PARTICLE_INDEX_COUNT * CONST.MAX_PARTICLES);
|
|
||||||
var shader = this.manager.resourceManager.createShader('TexturedAndAlphaShader', TexturedAndAlphaShader);
|
|
||||||
var indexBufferObject = this.manager.resourceManager.createBuffer(gl.ELEMENT_ARRAY_BUFFER, indexDataBuffer.getByteCapacity(), gl.STATIC_DRAW);
|
|
||||||
var vertexBufferObject = this.manager.resourceManager.createBuffer(gl.ARRAY_BUFFER, vertexDataBuffer.getByteCapacity(), gl.STREAM_DRAW);
|
|
||||||
var viewMatrixLocation = shader.getUniformLocation('u_view_matrix');
|
|
||||||
var indexBuffer = indexDataBuffer.uintView;
|
|
||||||
var max = CONST.MAX_PARTICLES * CONST.PARTICLE_INDEX_COUNT;
|
|
||||||
|
|
||||||
vertexBufferObject.addAttribute(shader.getAttribLocation('a_position'), 2, gl.FLOAT, false, CONST.VERTEX_SIZE, 0);
|
|
||||||
vertexBufferObject.addAttribute(shader.getAttribLocation('a_tex_coord'), 2, gl.FLOAT, false, CONST.VERTEX_SIZE, 8);
|
|
||||||
vertexBufferObject.addAttribute(shader.getAttribLocation('a_alpha'), 1, gl.FLOAT, false, CONST.VERTEX_SIZE, 16);
|
|
||||||
|
|
||||||
this.vertexDataBuffer = vertexDataBuffer;
|
|
||||||
this.indexDataBuffer = indexDataBuffer;
|
|
||||||
this.shader = shader;
|
|
||||||
this.indexBufferObject = indexBufferObject;
|
|
||||||
this.vertexBufferObject = vertexBufferObject;
|
|
||||||
this.viewMatrixLocation = viewMatrixLocation;
|
|
||||||
this.maxParticles = max;
|
|
||||||
|
|
||||||
// Populate the index buffer only once
|
|
||||||
for (var indexA = 0, indexB = 0; indexA < max; indexA += CONST.PARTICLE_INDEX_COUNT, indexB += CONST.PARTICLE_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
indexBufferObject.updateResource(indexBuffer, 0);
|
|
||||||
|
|
||||||
this.resize(this.width, this.height, this.game.config.resolution);
|
|
||||||
},
|
|
||||||
|
|
||||||
shouldFlush: function ()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
isFull: function ()
|
|
||||||
{
|
|
||||||
return (this.vertexDataBuffer.getByteLength() >= this.vertexDataBuffer.getByteCapacity());
|
|
||||||
},
|
|
||||||
|
|
||||||
bind: function (shader)
|
|
||||||
{
|
|
||||||
if (!shader)
|
|
||||||
{
|
|
||||||
this.shader.bind();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
shader.bind();
|
|
||||||
this.resize(this.width, this.height, this.game.config.resolution, shader);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.indexBufferObject.bind();
|
|
||||||
this.vertexBufferObject.bind();
|
|
||||||
},
|
|
||||||
|
|
||||||
flush: function (shader)
|
|
||||||
{
|
|
||||||
var gl = this.glContext;
|
|
||||||
var vertexDataBuffer = this.vertexDataBuffer;
|
|
||||||
|
|
||||||
if (this.elementCount === 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.bind(shader);
|
|
||||||
this.vertexBufferObject.updateResource(vertexDataBuffer.getUsedBufferAsFloat(), 0);
|
|
||||||
|
|
||||||
gl.drawElements(gl.TRIANGLES, this.elementCount, gl.UNSIGNED_SHORT, 0);
|
|
||||||
|
|
||||||
vertexDataBuffer.clear();
|
|
||||||
|
|
||||||
this.elementCount = 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
resize: function (width, height, resolution, shader)
|
|
||||||
{
|
|
||||||
var activeShader = shader ? shader : this.shader;
|
|
||||||
|
|
||||||
this.width = width * resolution;
|
|
||||||
this.height = height * resolution;
|
|
||||||
|
|
||||||
activeShader.setConstantMatrix4x4(
|
|
||||||
this.viewMatrixLocation,
|
|
||||||
new Float32Array([
|
|
||||||
2 / this.width, 0, 0, 0,
|
|
||||||
0, -2 / this.height, 0, 0,
|
|
||||||
0, 0, 1, 1,
|
|
||||||
-1, 1, 0, 0
|
|
||||||
])
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
destroy: function ()
|
|
||||||
{
|
|
||||||
this.manager.resourceManager.deleteShader(this.shader);
|
|
||||||
this.manager.resourceManager.deleteBuffer(this.indexBufferObject);
|
|
||||||
this.manager.resourceManager.deleteBuffer(this.vertexBufferObject);
|
|
||||||
|
|
||||||
this.shader = null;
|
|
||||||
this.indexBufferObject = null;
|
|
||||||
this.vertexBufferObject = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = BlitterBatch;
|
|
Loading…
Reference in a new issue