Cleaning up WebGLRenderer

This commit is contained in:
Felipe Alfonso 2018-01-17 18:25:43 -03:00
parent a41681c21a
commit cfe07706e6
58 changed files with 1133 additions and 1453 deletions

View file

@ -1,284 +0,0 @@
var Class = require('../../utils/Class');
var GameObject = require('../GameObject');
var Components = require('../components');
var Render = require('./EffectLayerRender');
var TexturedAndNormalizedTintedShader = require('../../renderer/webgl/shaders/TexturedAndNormalizedTintedShader');
// EffectLayer renders all elements on the layer to an offscreen render target
// and then when rendering the color buffer of that render target to the main screen
// it applies the effect layer shader.
var EffectLayer = new Class({
Extends: GameObject,
Mixins: [
Components.Alpha,
Components.BlendMode,
Components.Flip,
Components.GetBounds,
Components.Origin,
Components.RenderTarget,
Components.ScaleMode,
Components.Size,
Components.Transform,
Components.Visible,
Components.ScrollFactor,
Render
],
initialize:
function EffectLayer (scene, x, y, width, height, effectName, fragmentShader)
{
GameObject.call(this, scene, 'EffectLayer');
var pot = ((width & (width - 1)) == 0 && (height & (height - 1)) == 0);
var resourceManager = scene.sys.game.renderer.resourceManager;
var wrap;
var gl;
this.dstRenderTarget = null;
this.renderTexture = null;
this.dstShader = null;
this.uniforms = {};
if (resourceManager !== undefined)
{
gl = scene.sys.game.renderer.gl;
wrap = pot ? gl.REPEAT : gl.CLAMP_TO_EDGE;
this.dstShader = resourceManager.createShader(effectName, {
vert: TexturedAndNormalizedTintedShader.vert,
frag: fragmentShader
});
this.renderTexture = resourceManager.createTexture(
0,
gl.LINEAR, gl.LINEAR,
wrap, wrap,
gl.RGBA,
null, width, height
);
this.dstRenderTarget = resourceManager.createRenderTarget(width, height, this.renderTexture, null);
scene.sys.game.renderer.currentTexture[0] = null; // force rebinding of prev texture
}
this.flipY = true;
this.setPosition(x, y);
this.setSize(width, height);
this.setOrigin(0, 0);
var _this = this;
scene.sys.game.renderer.addContextRestoredCallback(function (renderer) {
var resourceManager = renderer.resourceManager;
var gl = renderer.gl;
var wrap = pot ? gl.REPEAT : gl.CLAMP_TO_EDGE;
_this.dstShader = resourceManager.createShader(effectName, {
vert: TexturedAndNormalizedTintedShader.vert,
frag: fragmentShader
});
_this.renderTexture = resourceManager.createTexture(
0,
gl.LINEAR, gl.LINEAR,
wrap, wrap,
gl.RGBA,
null, _this.width, _this.height
);
_this.dstRenderTarget = resourceManager.createRenderTarget(_this.width, _this.height, _this.renderTexture, null);
_this.uniforms = {};
scene.sys.game.renderer.currentTexture[0] = null; // force rebinding of prev texture
});
},
setClearAlpha: function (alpha)
{
if (this.dstRenderTarget)
{
this.dstRenderTarget.clearAlpha = alpha;
}
},
renderOffScreen: function ()
{
this.renderTarget = this.dstRenderTarget;
},
renderOnScreen: function ()
{
this.renderTarget = null;
},
add: function (gameObject)
{
if (gameObject.renderTarget !== undefined)
{
gameObject.renderTarget = this.dstRenderTarget;
}
},
remove: function (gameObject)
{
if (gameObject.renderTarget !== undefined)
{
gameObject.renderTarget = null;
}
},
getUniformLocation: function (uniformName)
{
var dstShader = this.dstShader;
var uniforms = this.uniforms;
var location;
if (uniformName in uniforms)
{
location = uniforms[uniformName];
}
else
{
location = dstShader.getUniformLocation(uniformName);
uniforms[uniformName] = location;
}
return location;
},
setFloat: function (uniformName, x)
{
var dstShader = this.dstShader;
if (dstShader === null)
{
return;
}
dstShader.setConstantFloat1(this.getUniformLocation(uniformName), x);
},
setFloat2: function (uniformName, x, y)
{
var dstShader = this.dstShader;
if (dstShader === null)
{
return;
}
dstShader.setConstantFloat2(this.getUniformLocation(uniformName), x, y);
},
setFloat3: function (uniformName, x, y, z)
{
var dstShader = this.dstShader;
if (dstShader === null)
{
return;
}
dstShader.setConstantFloat3(this.getUniformLocation(uniformName), x, y, z);
},
setFloat4: function (uniformName, x, y, z, w)
{
var dstShader = this.dstShader;
if (dstShader === null)
{
return;
}
dstShader.setConstantFloat4(this.getUniformLocation(uniformName), x, y, z, w);
},
setInt: function (uniformName, x)
{
var dstShader = this.dstShader;
if (dstShader === null)
{
return;
}
dstShader.setConstantInt1(this.getUniformLocation(uniformName), x);
},
setInt2: function (uniformName, x, y)
{
var dstShader = this.dstShader;
if (dstShader === null)
{
return;
}
dstShader.setConstantInt2(this.getUniformLocation(uniformName), x, y);
},
setInt3: function (uniformName, x, y, z)
{
var dstShader = this.dstShader;
if (dstShader === null)
{
return;
}
dstShader.setConstantInt3(this.getUniformLocation(uniformName), x, y, z);
},
setInt4: function (uniformName, x, y, z, w)
{
var dstShader = this.dstShader;
if (dstShader === null)
{
return;
}
dstShader.setConstantInt4(this.getUniformLocation(uniformName), x, y, z, w);
},
setMatrix2x2: function (uniformName, matrix)
{
var dstShader = this.dstShader;
if (dstShader === null)
{
return;
}
dstShader.setConstantMatrix2x2(this.getUniformLocation(uniformName), matrix);
},
setMatrix3x3: function (uniformName, matrix)
{
var dstShader = this.dstShader;
if (dstShader === null)
{
return;
}
dstShader.setConstantMatrix3x3(this.getUniformLocation(uniformName), matrix);
},
setMatrix4x4: function (uniformName, matrix)
{
var dstShader = this.dstShader;
if (dstShader === null)
{
return;
}
dstShader.setConstantMatrix4x4(this.getUniformLocation(uniformName), matrix);
}
});
module.exports = EffectLayer;

View file

@ -1,7 +0,0 @@
var EffectLayerCanvasRenderer = function (renderer, src, interpolationPercentage, camera)
{
};
module.exports = EffectLayerCanvasRenderer;

View file

@ -1,22 +0,0 @@
var BuildGameObject = require('../BuildGameObject');
var EffectLayer = require('./EffectLayer');
var GameObjectCreator = require('../../scene/plugins/GameObjectCreator');
var GetAdvancedValue = require('../../utils/object/GetAdvancedValue');
// When registering a factory function 'this' refers to the GameObjectCreator context.
GameObjectCreator.register('effectLayer', function (config)
{
var x = GetAdvancedValue(config, 'x', 0);
var y = GetAdvancedValue(config, 'y', 0);
var width = GetAdvancedValue(config, 'width', 512);
var height = GetAdvancedValue(config, 'height', 512);
var key = GetAdvancedValue(config, 'key', null);
var fragmentShader = GetAdvancedValue(config, 'fragmentShader', '');
var layer = new EffectLayer(this.scene, x, y, width, height, effectName, fragmentShader);
BuildGameObject(this.scene, layer, config);
return layer;
});

View file

@ -1,18 +0,0 @@
var EffectLayer = require('./EffectLayer');
var GameObjectFactory = require('../../scene/plugins/GameObjectFactory');
// When registering a factory function 'this' refers to the GameObjectFactory context.
//
// There are several properties available to use:
//
// this.scene - a reference to the Scene that owns the GameObjectFactory
// this.displayList - a reference to the Display List the Scene owns
// this.updateList - a reference to the Update List the Scene owns
if (WEBGL_RENDERER)
{
GameObjectFactory.register('effectLayer', function (x, y, width, height, effectName, fragmentShader)
{
return this.displayList.add(new EffectLayer(this.scene, x, y, width, height, effectName, fragmentShader));
});
}

View file

@ -1,19 +0,0 @@
var renderWebGL = require('../../utils/NOOP');
var renderCanvas = require('../../utils/NOOP');
if (WEBGL_RENDERER)
{
renderWebGL = require('./EffectLayerWebGLRenderer');
}
if (CANVAS_RENDERER)
{
renderCanvas = require('./EffectLayerCanvasRenderer');
}
module.exports = {
renderWebGL: renderWebGL,
renderCanvas: renderCanvas
};

View file

@ -1,13 +0,0 @@
var GameObject = require('../GameObject');
var EffectLayerWebGLRenderer = function (renderer, src, interpolationPercentage, camera)
{
if (GameObject.RENDER_MASK !== src.renderFlags || (src.cameraFilter > 0 && (src.cameraFilter & camera._id)))
{
return;
}
renderer.effectRenderer.renderEffect(src, camera, src.renderTexture, src.width, src.height);
};
module.exports = EffectLayerWebGLRenderer;

View file

@ -1,354 +0,0 @@
// RenderPass Will only work with Sprite and Image GameObjects.
var Class = require('../../utils/Class');
var GameObject = require('../GameObject');
var Components = require('../components');
var Render = require('./RenderPassRender');
var TexturedAndNormalizedTintedShader = require('../../renderer/webgl/shaders/TexturedAndNormalizedTintedShader');
var UntexturedAndNormalizedTintedShader = require('../../renderer/webgl/shaders/UntexturedAndNormalizedTintedShader');
// RenderPass - the user has a higher control on the rendering since you explicitly
// indicate what is rendered. RenderPass also has a render target but the difference
// is that when explicitly rendering an object to the render pass the shader from that
// render pass is applied. This is useful for additive passes and specific object effects.
var RenderPass = new Class({
Extends: GameObject,
Mixins: [
Components.Alpha,
Components.BlendMode,
Components.Flip,
Components.GetBounds,
Components.Origin,
Components.RenderTarget,
Components.ScaleMode,
Components.ScrollFactor,
Components.Size,
Components.Tint,
Components.Transform,
Components.Visible,
Render
],
initialize:
function RenderPass (scene, x, y, width, height, shaderName, fragmentShader, untextured)
{
GameObject.call(this, scene, 'RenderPass');
var _this = this;
var resourceManager = scene.sys.game.renderer.resourceManager;
var pot = ((width & (width - 1)) == 0 && (height & (height - 1)) == 0);
var gl;
var wrap;
this.renderer = scene.sys.game.renderer;
this.passRenderTarget = null;
this.renderTexture = null;
this.passShader = null;
this.uniforms = {};
this.textures = {};
this.setFlipY(true);
this.setPosition(x, y);
this.setSize(width, height);
this.setOrigin(0, 0);
if (resourceManager !== undefined)
{
gl = scene.sys.game.renderer.gl;
wrap = pot ? gl.REPEAT : gl.CLAMP_TO_EDGE;
if (!untextured)
{
this.passShader = resourceManager.createShader(shaderName, {vert: TexturedAndNormalizedTintedShader.vert, frag: fragmentShader});
}
else
{
this.passShader = resourceManager.createShader(shaderName, {vert: UntexturedAndNormalizedTintedShader.vert, frag: fragmentShader});
}
this.renderTexture = resourceManager.createTexture(0, gl.LINEAR, gl.LINEAR, wrap, wrap, gl.RGBA, null, width, height);
this.passRenderTarget = resourceManager.createRenderTarget(width, height, this.renderTexture, null);
scene.sys.game.renderer.currentTexture[0] = null; // force rebinding of prev texture
this.passShader.bind();
this.passShader.setConstantFloat2(this.passShader.getUniformLocation('u_resolution'), width, height);
}
scene.sys.game.renderer.addContextRestoredCallback(function (renderer) {
var gl = renderer.gl;
var wrap = pot ? gl.REPEAT : gl.CLAMP_TO_EDGE;
_this.passShader = resourceManager.createShader(shaderName, {vert: TexturedAndNormalizedTintedShader.vert, frag: fragmentShader});
_this.renderTexture = resourceManager.createTexture(0, gl.LINEAR, gl.LINEAR, wrap, wrap, gl.RGBA, null, _this.width, _this.height);
_this.passRenderTarget = resourceManager.createRenderTarget(_this.width, _this.height, _this.renderTexture, null);
_this.uniforms = {};
_this.textures = {};
_this.passShader.bind();
_this.passShader.setConstantFloat2(_this.passShader.getUniformLocation('u_resolution'), _this.width, _this.height);
scene.sys.game.renderer.currentTexture[0] = null; // force rebinding of prev texture
});
},
clearColorBuffer: function (r, g, b, a)
{
var gl = this.renderer.gl;
if (gl)
{
gl.bindFramebuffer(gl.FRAMEBUFFER, this.passRenderTarget.framebufferObject);
gl.clearColor(r, g, b, a);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}
},
clearDepthStencilBuffers: function (depth, stencil)
{
var gl = this.renderer.gl;
if (gl)
{
gl.bindFramebuffer(gl.FRAMEBUFFER, this.passRenderTarget.framebufferObject);
gl.clearDepth(depth);
gl.clearStencil(stencil);
gl.clear(gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}
},
clearAllBuffers: function (r, g, b, a, depth, stencil)
{
var gl = this.renderer.gl;
if (gl)
{
gl.bindFramebuffer(gl.FRAMEBUFFER, this.passRenderTarget.framebufferObject);
gl.clearColor(r, g, b, a);
gl.clearDepth(depth);
gl.clearStencil(stencil);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}
},
render: function (gameObject, camera)
{
var renderer = this.renderer;
var gl = renderer.gl;
if (gl && !renderer.contextLost)
{
//gameObject.setShader(this.passShader);
gameObject.setRenderTarget(this.passRenderTarget);
gameObject.renderWebGL(renderer, gameObject, 0.0, camera);
for (var key in this.textures)
{
var textureData = this.textures[key];
this.setInt(key, textureData.unit);
gl.activeTexture(gl.TEXTURE0 + textureData.unit);
gl.bindTexture(gl.TEXTURE_2D, textureData.texture);
gl.activeTexture(gl.TEXTURE0);
}
renderer.currentRenderer.flush(this.passShader, this.passRenderTarget);
gameObject.setRenderTarget(null);
//gameObject.setShader(null);
}
},
renderRect: function (x, y, width, height, camera)
{
var renderer = this.renderer;
var gl = renderer.gl;
if (gl && !renderer.contextLost)
{
renderer.setRenderer(this.renderer.spriteBatch, null, null);
renderer.spriteBatch.addTileTextureRect(
null, x, y, width, height, 1.0, 0xFFFFFFFF, this.scrollFactorX, this.scrollFactorY,
this.width, this.height, 0, 0, width, height, camera, null, false, true
);
for (var key in this.textures)
{
var textureData = this.textures[key];
this.setInt(key, textureData.unit);
gl.activeTexture(gl.TEXTURE0 + textureData.unit);
gl.bindTexture(gl.TEXTURE_2D, textureData.texture);
gl.activeTexture(gl.TEXTURE0);
}
//renderer.spriteBatch.addRenderPassRect(x, y, width, height, this.scrollFactorX, this.scrollFactorY, camera, this.passShader, this.passRenderTarget);
renderer.spriteBatch.flush(this.passShader, this.passRenderTarget);
}
},
setRenderTextureAt: function (renderTexture, samplerName, unit)
{
var renderer = this.renderer;
var gl = renderer.gl;
if (gl && !renderer.contextLost)
{
/* Texture 1 is reserved for Phasers Main Renderer */
unit = (unit > 0) ? unit : 1;
this.textures[samplerName] = { texture: renderTexture.texture, unit: unit };
//this.setInt(samplerName, unit);
//gl.activeTexture(gl.TEXTURE0 + unit);
//gl.bindTexture(gl.TEXTURE_2D, renderTexture.texture);
//gl.activeTexture(gl.TEXTURE0);
}
},
getUniformLocation: function (uniformName)
{
var passShader = this.passShader;
var uniforms = this.uniforms;
var location;
if (uniformName in uniforms)
{
location = uniforms[uniformName];
}
else
{
location = passShader.getUniformLocation(uniformName);
uniforms[uniformName] = location;
}
return location;
},
setFloat: function (uniformName, x)
{
var passShader = this.passShader;
if (passShader === null)
{
return;
}
passShader.setConstantFloat1(this.getUniformLocation(uniformName), x);
},
setFloat2: function (uniformName, x, y)
{
var passShader = this.passShader;
if (passShader === null)
{
return;
}
passShader.setConstantFloat2(this.getUniformLocation(uniformName), x, y);
},
setFloat3: function (uniformName, x, y, z)
{
var passShader = this.passShader;
if (passShader === null)
{
return;
}
passShader.setConstantFloat3(this.getUniformLocation(uniformName), x, y, z);
},
setFloat4: function (uniformName, x, y, z, w)
{
var passShader = this.passShader;
if (passShader === null)
{
return;
}
passShader.setConstantFloat4(this.getUniformLocation(uniformName), x, y, z, w);
},
setInt: function (uniformName, x)
{
var passShader = this.passShader;
if (passShader === null)
{
return;
}
passShader.setConstantInt1(this.getUniformLocation(uniformName), x);
},
setInt2: function (uniformName, x, y)
{
var passShader = this.passShader;
if (passShader === null)
{
return;
}
passShader.setConstantInt2(this.getUniformLocation(uniformName), x, y);
},
setInt3: function (uniformName, x, y, z)
{
var passShader = this.passShader;
if (passShader === null)
{
return;
}
passShader.setConstantInt3(this.getUniformLocation(uniformName), x, y, z);
},
setInt4: function (uniformName, x, y, z, w)
{
var passShader = this.passShader;
if (passShader === null)
{
return;
}
passShader.setConstantInt4(this.getUniformLocation(uniformName), x, y, z, w);
},
setMatrix2x2: function (uniformName, matrix)
{
var passShader = this.passShader;
if (passShader === null)
{
return;
}
passShader.setConstantMatrix2x2(this.getUniformLocation(uniformName), matrix);
},
setMatrix3x3: function (uniformName, matrix)
{
var passShader = this.passShader;
if (passShader === null)
{
return;
}
passShader.setConstantMatrix3x3(this.getUniformLocation(uniformName), matrix);
},
setMatrix4x4: function (uniformName, matrix)
{
var passShader = this.passShader;
if (passShader === null)
{
return;
}
passShader.setConstantMatrix4x4(this.getUniformLocation(uniformName), matrix);
}
});
module.exports = RenderPass;

View file

@ -1,7 +0,0 @@
var RenderPassCanvasRenderer = function (renderer, src, interpolationPercentage, camera)
{
};
module.exports = RenderPassCanvasRenderer;

View file

@ -1,27 +0,0 @@
var BuildGameObject = require('../BuildGameObject');
var GameObjectCreator = require('../../scene/plugins/GameObjectCreator');
var GetAdvancedValue = require('../../utils/object/GetAdvancedValue');
var RenderPass = require('./RenderPass');
// When registering a factory function 'this' refers to the GameObjectCreator context.
GameObjectCreator.register('renderPass', function (config)
{
var x = GetAdvancedValue(config, 'x', 0);
var y = GetAdvancedValue(config, 'y', 0);
var width = GetAdvancedValue(config, 'width', 512);
var height = GetAdvancedValue(config, 'height', 512);
var shaderName = GetAdvancedValue(config, 'shaderName', '');
var fragmentShader = GetAdvancedValue(config, 'fragmentShader', '');
var untextured = GetAdvancedValue(config, 'untextured', false);
var pass = new RenderPass(this.scene, x, y, width, height, shaderName, fragmentShader, untextured);
BuildGameObject(this.scene, pass, config);
pass.setPosition(x, y);
pass.setSize(width, height);
pass.setOrigin(0, 0);
return pass;
});

View file

@ -1,18 +0,0 @@
var RenderPass = require('./RenderPass');
var GameObjectFactory = require('../../scene/plugins/GameObjectFactory');
// When registering a factory function 'this' refers to the GameObjectFactory context.
//
// There are several properties available to use:
//
// this.scene - a reference to the Scene that owns the GameObjectFactory
// this.displayList - a reference to the Display List the Scene owns
// this.updateList - a reference to the Update List the Scene owns
if (WEBGL_RENDERER)
{
GameObjectFactory.register('renderPass', function (x, y, width, height, shaderName, fragmentShader, untextured)
{
return this.displayList.add(new RenderPass(this.scene, x, y, width, height, shaderName, fragmentShader, untextured));
});
}

View file

@ -1,19 +0,0 @@
var renderWebGL = require('../../utils/NOOP');
var renderCanvas = require('../../utils/NOOP');
if (WEBGL_RENDERER)
{
renderWebGL = require('./RenderPassWebGLRenderer');
}
if (CANVAS_RENDERER)
{
renderCanvas = require('./RenderPassCanvasRenderer');
}
module.exports = {
renderWebGL: renderWebGL,
renderCanvas: renderCanvas
};

View file

@ -1,13 +0,0 @@
var GameObject = require('../GameObject');
var RenderPassWebGLRenderer = function (renderer, src, interpolationPercentage, camera)
{
if (GameObject.RENDER_MASK !== src.renderFlags || (src.cameraFilter > 0 && (src.cameraFilter & camera._id)))
{
return;
}
renderer.spriteBatch.addSpriteTexture(src, camera, src.renderTexture, src.width, src.height);
};
module.exports = RenderPassWebGLRenderer;

View file

@ -0,0 +1,178 @@
var Class = require('../../utils/Class');
var WebGLPipeline = new Class({
initialize:
function WebGLPipeline(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.topology = config.topology;
// 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);
for (var key in vertexLayout)
{
var element = vertexLayout[key];
vertexBuffer.addAttribute(
program.getAttribLocation(key),
element.size,
element.type,
element.normalize,
vertexSize,
element.offset
);
}
this.vertexBuffer = vertexBuffer;
this.program = program;
}
},
shouldFlush: function ()
{
return this.vertexCount >= this.vertexCapacity;
},
resize: function (width, height, resolution)
{
this.width = width * resolution;
this.height = height * resolution;
return this;
},
bind: function (overrideProgram)
{
// Check if we're using a custom program or
// the default one from the pipeline.
if (!overrideProgram) this.program.bind();
else overrideProgram.bind();
this.vertexBuffer.bind();
return this;
},
flush: function ()
{
var gl = this.gl;
var vertexCount = this.vertexCount;
var vertexBuffer = this.vertexBuffer;
var vertexData = this.vertexData;
var topology = this.topology;
if (vertexCount === 0) return;
vertexBuffer.updateResource(vertexData, 0);
gl.drawArrays(topology, 0, vertexCount);
this.vertexCount = 0;
return this;
},
destroy: function ()
{
var resources = this.resources;
resources.deleteShader(this.program);
resources.deleteBuffer(this.vertexBuffer);
this.program = null;
this.vertexBuffer = null;
return this;
},
setFloat1: function (name, x)
{
this.gl.uniform1f(this.gl.getUniformLocation(this.program.program, name), x);
return this;
},
setFloat2: function (name, x, y)
{
this.gl.uniform2f(this.gl.getUniformLocation(this.program.program, name), x, y);
return this;
},
setFloat3: function (name, x, y, z)
{
this.gl.uniform3f(this.gl.getUniformLocation(this.program.program, name), x, y, z);
return this;
},
setFloat4: function (name, x, y, z, w)
{
this.gl.uniform4f(this.gl.getUniformLocation(this.program.program, name), x, y, z, w);
return this;
},
setInt1: function (name, x)
{
this.gl.uniform1i(this.gl.getUniformLocation(this.program.program, name), x);
return this;
},
setInt2: function (name, x, y)
{
this.gl.uniform2i(this.gl.getUniformLocation(this.program.program, name), x, y);
return this;
},
setInt3: function (name, x, y, z)
{
this.gl.uniform3i(this.gl.getUniformLocation(this.program.program, name), x, y, z);
return this;
},
setInt4: function (name, x, y, z, w)
{
this.gl.uniform4i(this.gl.getUniformLocation(this.program.program, name), x, y, z, w);
return this;
},
setMatrix2: function (name, transpose, matrix)
{
this.gl.uniformMatrix2fv(this.gl.getUniformLocation(this.program.program, name), transpose, matrix);
return this;
},
setMatrix3: function (name, transpose, matrix)
{
this.gl.uniformMatrix2fv(this.gl.getUniformLocation(this.program.program, name), transpose, matrix);
return this;
},
setMatrix4: function (name, transpose, matrix)
{
this.gl.uniformMatrix2fv(this.gl.getUniformLocation(this.program.program, name), transpose, matrix);
return this;
}
});
module.exports = WebGLPipeline;

View file

@ -1,680 +1,288 @@
/**
* @author Richard Davey (@photonstorm)
* @author Felipe Alfonso (@bitnenfer)
* @copyright 2017 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
var BlendModes = require('../BlendModes');
var BlitterBatch = require('./pipelines/blitterbatch/BlitterBatch');
var Class = require('../../utils/Class');
var CONST = require('../../const');
var EffectRenderer = require('./pipelines/effectrenderer/EffectRenderer');
var IsSizePowerOfTwo = require('../../math/pow2/IsSizePowerOfTwo');
var MaskRenderer = require('./pipelines/maskrenderer/MaskRenderer');
var QuadBatch = require('./pipelines/quadbatch/QuadBatch');
var ParticleRenderer = require('./pipelines/particlerenderer/ParticleRenderer');
var ResourceManager = require('./ResourceManager');
var Resources = require('./resources');
var ScaleModes = require('../ScaleModes');
var ShapeBatch = require('./pipelines/shapebatch/ShapeBatch');
var SpriteRenderer = require('./pipelines/spriterenderer/SpriteRenderer');
var TileBatch = require('./pipelines/tilebatch/TileBatch');
var TilemapRenderer = require('./pipelines/tilemaprenderer/TilemapRenderer');
var WebGLSnapshot = require('../snapshot/WebGLSnapshot');
var WebGLpipeline = new Class({
var WebGLRenderer = new Class({
initialize:
function WebGLpipeline (game)
function WebGLRenderer (game)
{
var _this = this;
this.game = game;
this.onContextLostCallbacks = [];
this.onContextRestoredCallbacks = [];
this.type = CONST.WEBGL;
this.width = game.config.width * game.config.resolution;
this.height = game.config.height * game.config.resolution;
this.resolution = game.config.resolution;
this.view = game.canvas;
this.view.addEventListener('webglcontextlost', function (evt) {
var callbacks = _this.onContextLostCallbacks;
var pipelines = _this.pipelines;
for (var index = 0; index < pipelines.length; ++index)
{
pipelines[index].destroy();
}
_this.contextLost = true;
for (var index = 0; index < callbacks.length; ++index)
{
callbacks[index](_this);
}
evt.preventDefault();
}, false);
this.view.addEventListener('webglcontextrestored', function (evt) {
var callbacks = _this.onContextRestoredCallbacks;
_this.pipelines.length = 0;
_this.resourceManager.shaderCache = {};
_this.resourceManager.shaderCount = 0;
_this.contextLost = false;
_this.init();
_this.game.textures.each(function (texture) {
for (var i = 0; i < texture.source.length; ++i)
{
texture.source[i].init(_this.game);
}
}, null);
for (var index = 0; index < callbacks.length; ++index)
{
callbacks[index](_this);
}
}, false);
// 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,
var renderer = this;
var config = {
backgroundColor: game.config.backgroundColor,
contextCreation: {
alpha: false,
depth: false, // enable when 3D is added in the future
antialias: true,
premultipliedAlpha: true,
stencil: true,
preserveDrawingBuffer: false
preserveDrawingBuffer: false,
failIfMajorPerformanceCaveat: false
}
};
this.contextLost = false;
this.maxTextures = 1;
this.multiTexture = false;
this.game = game;
this.type = CONST.WEBGL;
this.width = game.config.width * game.config.resolution;
this.height = game.config.height * game.config.resolution;
this.canvas = game.canvas;
this.blendModes = [];
this.gl = null;
this.extensions = null;
this.extensionList = {};
this.pipelines = [];
this.blitterBatch = null;
this.aaQuadBatch = null;
this.spriteBatch = null;
this.shapeBatch = null;
this.EffectRenderer = null;
this.MaskRenderer = null;
this.contextLost = false;
this.autoResize = false;
this.pipelines = null;
for (var i = 0; i <= 16; i++)
{
this.blendModes.push({ func: [ WebGLRenderingContext.ONE, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA ], equation: WebGLRenderingContext.FUNC_ADD });
}
this.blendModes[1].func = [ WebGLRenderingContext.ONE, WebGLRenderingContext.DST_ALPHA ];
this.blendModes[2].func = [ WebGLRenderingContext.DST_COLOR, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA ];
this.blendModes[3].func = [ WebGLRenderingContext.ONE, WebGLRenderingContext.ONE_MINUS_SRC_COLOR ];
// Intenal Renderer State (Textures, Framebuffers, Pipelines, Buffers, etc)
this.currentTextures = new Array(16);
this.currentFramebuffer = null;
this.currentPipeline = null;
this.currentTexture = [];
this.shaderCache = {};
this.currentShader = null;
this.resourceManager = null;
this.currentRenderTarget = null;
this.snapshotCallback = null;
this.snapshotType = null;
this.snapshotEncoder = null;
this.currentVertexBuffer = null;
this.currentIndexBuffer = null;
this.currentBlendMode = CONST.BlendModes.NORMAL;
this.currentScissorState = { enabled: false, x: 0, y: 0, w: 0, h: 0 };
this.scissor = {
enabled: false,
x: 0,
y: 0,
width: 0,
height: 0
};
// Setup context lost and restore event listeners
this.canvas.addEventListener('webglcontextlost', function (event) {
renderer.contextLost = true;
event.preventDefault();
}, false);
this.init();
this.canvas.addEventListener('webglcontextrestored', function (event) {
renderer.contextLost = false;
renderer.init(config);
}, false);
// This are initialized post context creation
this.gl = null;
this.supportedExtensions = null;
this.extensions = {};
this.init(config);
},
init: function ()
init: function (config)
{
this.gl = this.view.getContext('webgl', this.config.WebGLContextOptions) || this.view.getContext('experimental-webgl', this.config.WebGLContextOptions);
var canvas = this.canvas;
var clearColor = config.backgroundColor;
var gl = canvas.getContext('webgl', config.contextCreation) || canvas.getContext('experimental-webgl', config.contextCreation);
if (!this.gl)
if (!gl)
{
this.contextLost = true;
throw new Error('This browser does not support WebGL. Try using the Canvas pipeline.');
}
var gl = this.gl;
this.lostContext = this.getExtension('WEBGL_lose_context');
var color = this.game.config.backgroundColor;
this.resourceManager = new ResourceManager(gl);
// Load supported extensions
this.supportedExtensions = gl.getSupportedExtensions();
// Setup initial WebGL state
gl.disable(gl.DEPTH_TEST);
gl.disable(gl.CULL_FACE);
gl.disable(gl.SCISSOR_TEST);
gl.enable(gl.BLEND);
gl.clearColor(color.redGL, color.greenGL, color.blueGL, color.alphaGL);
gl.clearColor(clearColor.redGL, clearColor.greenGL, clearColor.blueGL, 1.0);
// Map Blend Modes
this.blendModes = [];
for (var i = 0; i <= 16; i++)
// Initialize all textures to null
for (var index = 0; index < this.currentTextures.length; ++index)
{
this.blendModes.push({ func: [ gl.ONE, gl.ONE_MINUS_SRC_ALPHA ], equation: gl.FUNC_ADD });
this.currentTextures[index] = null;
}
// Add
this.blendModes[1].func = [ gl.ONE, gl.DST_ALPHA ];
// Clear previous pipelines and reload default ones
this.pipelines = {};
// Multiply
this.blendModes[2].func = [ gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA ];
// Screen
this.blendModes[3].func = [ gl.ONE, gl.ONE_MINUS_SRC_COLOR ];
this.blendMode = -1;
this.extensions = gl.getSupportedExtensions();
this.blitterBatch = this.addPipeline(new BlitterBatch(this.game, gl, this));
//this.quadBatch = this.addPipeline(new QuadBatch(this.game, gl, this));
this.spriteRenderer = this.addPipeline(new SpriteRenderer(this.game, gl, this));
//this.shapeBatch = this.addPipeline(new ShapeBatch(this.game, gl, this));
//this.EffectRenderer = this.addPipeline(new EffectRenderer(this.game, gl, this));
//this.tileBatch = this.addPipeline(new TileBatch(this.game, gl, this));
//this.TilemapRenderer = this.addPipeline(new TilemapRenderer(this.game, gl, this));
//this.ParticleRenderer = this.addPipeline(new ParticleRenderer(this.game, gl, this));
//this.MaskRenderer = this.addPipeline(new MaskRenderer(this.game, gl, this));
this.currentPipeline = this.spriteRenderer;
this.currentVertexBuffer = null;
this.setBlendMode(0);
this.resize(this.width, this.height);
},
// https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/blendFunc
// https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/blendFuncSeparate
// https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/blendEquation
// https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/blendEquationSeparate
// https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/blendColor
addBlendMode: function (func, equation)
{
var index = this.blendModes.push({ func: func, equation: equation });
return index - 1;
},
updateBlendMode: function (index, func, equation)
{
if (this.blendModes[index])
{
this.blendModes[index].func = func;
if (equation)
{
this.blendModes[index].equation = equation;
}
}
this.setBlendMode(CONST.BlendModes.NORMAL);
this.resize(this.width, this.height, this.game.config.resolution);
return this;
},
removeBlendMode: function (index)
{
if (index > 16 && this.blendModes[index])
{
this.blendModes.splice(index, 1);
}
return this;
},
getExtension: function (name)
{
if (!(name in this.extensionList))
{
this.extensionList[name] = this.gl.getExtension(name);
}
return this.extensionList[name];
},
addContextLostCallback: function (callback)
{
if (this.onContextLostCallbacks.indexOf(callback) === -1)
{
this.onContextLostCallbacks.push(callback);
}
},
addContextRestoredCallback: function (callback)
{
if (this.onContextRestoredCallbacks.indexOf(callback) === -1)
{
this.onContextRestoredCallbacks.push(callback);
}
},
createTexture: function (source, width, height)
{
width = source ? source.width : width;
height = source ? source.height : height;
var gl = this.gl;
var filter = gl.NEAREST;
var wrap = IsSizePowerOfTwo(width, height) ? gl.REPEAT : gl.CLAMP_TO_EDGE;
if (!source.glTexture)
{
if (source.scaleMode === ScaleModes.LINEAR)
{
filter = gl.LINEAR;
}
else if (source.scaleMode === ScaleModes.NEAREST || this.game.config.pixelArt)
{
filter = gl.NEAREST;
}
if (!source && typeof width === 'number' && typeof height === 'number')
{
source.glTexture = this.resourceManager.createTexture(
0,
filter,
filter,
wrap,
wrap,
gl.RGBA,
null,
width, height
);
}
else
{
source.glTexture = this.resourceManager.createTexture(
0,
filter,
filter,
wrap,
wrap,
gl.RGBA,
source.image
);
}
}
this.currentTexture[0] = null;
},
setTexture: function (texture, unit)
{
unit = unit || 0;
if (this.currentTexture[unit] !== texture)
resize: function (width, height, resolution)
{
var gl = this.gl;
this.currentPipeline.flush();
gl.activeTexture(gl.TEXTURE0 + unit);
if (texture !== null)
{
gl.bindTexture(gl.TEXTURE_2D, texture.texture);
}
else
{
gl.bindTexture(gl.TEXTURE_2D, null);
}
this.currentTexture[unit] = texture;
}
},
setPipeline: function (pipeline)
{
if (this.currentPipeline !== pipeline ||
this.currentPipeline.shouldFlush())
{
this.currentPipeline.flush();
this.currentPipeline.endPass();
this.currentPipeline = pipeline;
}
},
setRenderTarget: function (renderTarget)
{
var gl = this.gl;
if (this.currentRenderTarget !== renderTarget)
{
this.currentPipeline.flush();
if (renderTarget !== null)
{
gl.bindFramebuffer(gl.FRAMEBUFFER, renderTarget.framebufferObject);
if (renderTarget.shouldClear)
{
gl.clearColor(0, 0, 0, renderTarget.clearAlpha);
gl.clear(gl.COLOR_BUFFER_BIT);
renderTarget.shouldClear = false;
}
}
else
{
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, this.width, this.height);
}
this.currentRenderTarget = renderTarget;
}
},
resize: function (width, height)
{
var resolution = this.game.config.resolution;
this.width = width * resolution;
this.height = height * resolution;
this.view.width = this.width;
this.view.height = this.height;
this.canvas.width = this.width;
this.canvas.height = this.height;
if (this.autoResize)
{
this.view.style.width = (this.width / resolution) + 'px';
this.view.style.height = (this.height / resolution) + 'px';
this.canvas.style.width = (this.width / resolution) + 'px';
this.canvas.style.height = (this.height / resolution) + 'px';
}
this.gl.viewport(0, 0, this.width, this.height);
gl.viewport(0, 0, this.width, this.height);
for (var i = 0, l = this.pipelines.length; i < l; ++i)
{
//this.pipelines[i].bind();
//this.pipelines[i].resize(width, height, resolution);
}
// Update all registered pipelines
//this.currentPipeline.bind();
return this;
},
// Call at the start of the render loop
preRender: function ()
hasExtension: function (extensionName)
{
// No point rendering if our context has been blown up!
if (this.contextLost)
{
return;
}
this.setRenderTarget(null);
// Add Pre-render hook
var gl = this.gl;
var color = this.game.config.backgroundColor;
gl.clearColor(color.redGL, color.greenGL, color.blueGL, color.alphaGL);
// Some drivers require to call glClear
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
this.setBlendMode(BlendModes.NORMAL);
this.gl.viewport(0, 0, this.width, this.height);
return this.supportedExtensions ? this.supportedExtensions.indexOf(extensionName) : false;
},
/**
* Renders a single Scene.
*
* @method render
* @param {Phaser.Scene} scene - The Scene to be rendered.
* @param {number} interpolationPercentage - The cumulative amount of time that hasn't been simulated yet, divided
* by the amount of time that will be simulated the next time update()
* runs. Useful for interpolating frames.
*/
render: function (scene, children, interpolationPercentage, camera)
getExtension: function (extensionName)
{
if (this.contextLost) return;
var gl = this.gl;
var quadBatch = this.quadBatch;
if (!this.hasExtension(extensionName)) return null;
this.scissor.enabled = (camera.x !== 0 || camera.y !== 0 || camera.width !== gl.canvas.width || camera.height !== gl.canvas.height);
this.setRenderTarget(null);
if (this.scissor.enabled)
if (!(extensionName in this.extensions))
{
gl.enable(gl.SCISSOR_TEST);
this.scissor.x = camera.x;
this.scissor.y = gl.drawingBufferHeight - camera.y - camera.height;
this.scissor.width = camera.width;
this.scissor.height = camera.height;
gl.scissor(this.scissor.x, this.scissor.y, this.scissor.width, this.scissor.height);
this.extensions[extensionName] = this.gl.getExtension(extensionName);
}
if (camera.backgroundColor.alphaGL > 0)
{
var color = camera.backgroundColor;
//quadBatch.bind();
//quadBatch.add(
// camera.x, camera.y, camera.width, camera.height,
// color.redGL, color.greenGL, color.blueGL, color.alphaGL
//);
//quadBatch.flush();
//this.currentPipeline.bind();
}
var list = children.list;
var length = list.length;
var pipeline;
for (var index = 0; index < length; ++index)
{
var child = list[index];
if (!child.willRender())
{
continue;
}
if (child.blendMode !== this.blendMode)
{
this.setBlendMode(child.blendMode);
}
if (child.mask)
{
child.mask.preRenderWebGL(this, child, camera);
}
// drawing child
child.renderWebGL(this, child, interpolationPercentage, camera);
if (child.mask)
{
child.mask.postRenderWebGL(this, child);
}
pipeline = this.currentPipeline;
if (pipeline.shouldFlush())
{
pipeline.flush();
}
}
this.currentPipeline.flush();
if (camera._fadeAlpha > 0 || camera._flashAlpha > 0)
{
this.setRenderTarget(null);
this.setBlendMode(BlendModes.NORMAL);
// // fade rendering
// quadBatch.add(
// camera.x, camera.y, camera.width, camera.height,
// camera._fadeRed,
// camera._fadeGreen,
// camera._fadeBlue,
// camera._fadeAlpha
// );
// // flash rendering
// quadBatch.add(
// camera.x, camera.y, camera.width, camera.height,
// camera._flashRed,
// camera._flashGreen,
// camera._flashBlue,
// camera._flashAlpha
// );
// quadBatch.flush();
// this.currentPipeline.bind();
}
if (this.scissor.enabled)
{
gl.disable(gl.SCISSOR_TEST);
}
return this.extensions[extensionName];
},
// Called at the end of the render loop (tidy things up, etc)
postRender: function ()
/* Renderer State Manipulation Functions */
hasPipeline: function (pipelineName)
{
if (this.contextLost) return;
this.currentPipeline.flush();
if (this.snapshotCallback)
{
this.snapshotCallback(WebGLSnapshot(this.view, this.snapshotType, this.snapshotEncoder));
this.snapshotCallback = null;
}
// Add Post-render hook
// console.log('%c render end ', 'color: #ffffff; background: #ff0000;');
return (pipelineName in this.pipelines);
},
snapshot: function (callback, type, encoderOptions)
getPipeline: function (pipelineName)
{
this.snapshotCallback = callback;
this.snapshotType = type;
this.snapshotEncoder = encoderOptions;
},
createFBO: function () {},
setBlendMode: function (newBlendMode)
{
var gl = this.gl;
if (newBlendMode === BlendModes.SKIP_CHECK)
{
return;
}
var pipeline = this.currentPipeline;
if (this.blendMode !== newBlendMode)
{
if (pipeline)
{
pipeline.flush();
}
var blend = this.blendModes[newBlendMode].func;
gl.enable(gl.BLEND);
gl.blendEquation(this.blendModes[newBlendMode].equation);
if (blend.length > 2)
{
gl.blendFuncSeparate(blend[0], blend[1], blend[2], blend[3]);
}
else
{
gl.blendFunc(blend[0], blend[1]);
}
this.blendMode = newBlendMode;
}
},
addPipeline: function (pipelineInstance)
{
var index = this.pipelines.indexOf(pipelineInstance);
if (index < 0)
{
this.pipelines.push(pipelineInstance);
return pipelineInstance;
}
if (this.hasPipeline(pipelineName)) return this.pipelines[pipelineName];
return null;
},
setTextureFilterMode: function (texture, filterMode)
removePipeline: function (pipelineName)
{
delete this.pipelines[pipelineName];
return this;
},
addPipeline: function (pipelineName, pipelineInstance)
{
if (!this.hasPipeline(pipelineName)) this.pipelines[pipelineName] = pipelineInstance;
else console.warn('Pipeline', pipelineName, ' already exists.');
this.pipelines[pipelineName].resize(this.width, this.height, this.game.config.resolution);
return this;
},
setBlendMode: function (blendModeId)
{
var gl = this.gl;
var glFilter = [ gl.LINEAR, gl.NEAREST ][filterMode];
var pipeline = this.currentPipeline;
var blendMode = this.blendModes[blendModeId];
gl.bindTexture(gl.TEXTURE_2D, texture.texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, glFilter);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, glFilter);
if (blendModeId === CONST.BlendModes.SKIP_CHECK || !pipeline)
return;
if (this.currentTexture[0] !== null)
if (this.currentBlendMode !== blendModeId)
{
gl.bindTexture(gl.TEXTURE_2D, this.currentTexture[0].texture);
pipeline.flush();
gl.enable(gl.BLEND);
gl.blendEquation(blendMode.equation);
if (blendMode.func.length > 2)
{
gl.blendFuncSeparate(blendMode.func[0], blendMode.func[1], blendMode.func[2], blendMode.func[3]);
}
else
{
gl.bindTexture(gl.TEXTURE_2D, null);
gl.blendFunc(blendMode.func[0], blendMode.func[1]);
}
return texture;
this.currentBlendMode = blendModeId;
}
return this;
},
uploadCanvasToGPU: function (srcCanvas, dstTexture, shouldReallocate)
setTexture2D: function (texture, textureUnit)
{
var gl = this.gl;
if (!dstTexture)
if (texture !== this.currentTextures[textureUnit])
{
dstTexture = new Resources.Texture(null, 0, 0);
gl.activeTexture(gl.TEXTURE0 + textureUnit);
gl.bindTexture(gl.TEXTURE_2D, texture);
// Only call this once
dstTexture.texture = gl.createTexture();
this.currentTextures[textureUnit] = texture;
}
if (dstTexture != this.currentTexture[0])
{
this.currentPipeline.flush();
}
gl.activeTexture(gl.TEXTURE0);
if (!shouldReallocate)
{
// Update resource
gl.bindTexture(gl.TEXTURE_2D, dstTexture.texture);
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, srcCanvas);
}
else
{
// Allocate or Reallocate resource
gl.bindTexture(gl.TEXTURE_2D, dstTexture.texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, srcCanvas);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
dstTexture.width = srcCanvas.width;
dstTexture.height = srcCanvas.height;
}
// We must rebind old texture
if (this.currentTexture.length > 0 && dstTexture != this.currentTexture[0] && this.currentTexture[0] !== null)
{
gl.bindTexture(gl.TEXTURE_2D, this.currentTexture[0].texture);
}
return dstTexture;
return this;
},
destroy: function ()
setFramebuffer: function (framebuffer)
{
if (this.lostContext)
var gl = this.gl;
if (framebuffer !== this.currentFramebuffer)
{
this.lostContext.loseContext();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
}
this.gl = null;
return this;
},
setVertexBuffer: function (vertexBuffer)
{
var gl = this.gl;
if (vertexBuffer !== this.currentVertexBuffer)
{
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
}
return this;
},
setIndexBuffer: function (indexBuffer)
{
var gl = this.gl;
if (indexBuffer !== this.currentIndexBuffer)
{
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
}
return this;
},
/* Renderer Resource Creation Functions */
createTexture2D: function ()
{
},
createFramebuffer: function ()
{
},
createProgram: function ()
{
},
createVertexBuffer: function ()
{
},
createIndexBuffer: function ()
{
}
});
module.exports = WebGLpipeline;
module.exports = WebGLRenderer;

View file

@ -22,19 +22,7 @@ var Pipeline = new Class({
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
{
@ -69,43 +57,22 @@ var Pipeline = new Class({
return this.vertexCount >= this.vertexCapacity;
},
bind: function ()
{
this.onBind.call(this.onBindTarget, this);
return this;
},
resize: function (width, height, resolution)
{
this.onResize.call(this.onResizeTarget, width, height, resolution);
this.width = width * resolution;
this.height = height * resolution;
return this;
},
beginPass: function (renderTarget, program)
bind: function (overrideProgram)
{
if (this.currentRenderTarget !== null ||
this.currentProgram !== null)
{
this.flush();
this.endPass();
}
// Check if we're using a custom program or
// the default one from the pipeline.
if (!overrideProgram) this.program.bind();
else overrideProgram.bind();
this.currentRenderTarget = (renderTarget || null);
this.currentProgram = (program || this.program);
this.currentProgram.bind();
this.vertexBuffer.bind();
if (this.currentRenderTarget !== null)
{
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.currentRenderTarget.framebufferObject);
}
else
{
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);
}
this.onBeginPass.call(this.onBeginPassTarget, this);
return this;
},
@ -124,24 +91,6 @@ var Pipeline = new Class({
this.vertexCount = 0;
this.onFlush.call(this.onFlushTarget, this);
return this;
},
endPass: function ()
{
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;
},
@ -155,6 +104,72 @@ var Pipeline = new Class({
this.program = null;
this.vertexBuffer = null;
return this;
},
setFloat1: function (name, x)
{
this.gl.uniform1f(this.gl.getUniformLocation(this.program.program, name), x);
return this;
},
setFloat2: function (name, x, y)
{
this.gl.uniform2f(this.gl.getUniformLocation(this.program.program, name), x, y);
return this;
},
setFloat3: function (name, x, y, z)
{
this.gl.uniform3f(this.gl.getUniformLocation(this.program.program, name), x, y, z);
return this;
},
setFloat4: function (name, x, y, z, w)
{
this.gl.uniform4f(this.gl.getUniformLocation(this.program.program, name), x, y, z, w);
return this;
},
setInt1: function (name, x)
{
this.gl.uniform1i(this.gl.getUniformLocation(this.program.program, name), x);
return this;
},
setInt2: function (name, x, y)
{
this.gl.uniform2i(this.gl.getUniformLocation(this.program.program, name), x, y);
return this;
},
setInt3: function (name, x, y, z)
{
this.gl.uniform3i(this.gl.getUniformLocation(this.program.program, name), x, y, z);
return this;
},
setInt4: function (name, x, y, z, w)
{
this.gl.uniform4i(this.gl.getUniformLocation(this.program.program, name), x, y, z, w);
return this;
},
setMatrix2: function (name, transpose, matrix)
{
this.gl.uniformMatrix2fv(this.gl.getUniformLocation(this.program.program, name), transpose, matrix);
return this;
},
setMatrix3: function (name, transpose, matrix)
{
this.gl.uniformMatrix2fv(this.gl.getUniformLocation(this.program.program, name), transpose, matrix);
return this;
},
setMatrix4: function (name, transpose, matrix)
{
this.gl.uniformMatrix2fv(this.gl.getUniformLocation(this.program.program, name), transpose, matrix);
return this;
}

View file

@ -0,0 +1,680 @@
/**
* @author Richard Davey (@photonstorm)
* @author Felipe Alfonso (@bitnenfer)
* @copyright 2017 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
var BlendModes = require('../BlendModes');
var BlitterBatch = require('./pipelines/blitterbatch/BlitterBatch');
var Class = require('../../utils/Class');
var CONST = require('../../const');
var EffectRenderer = require('./pipelines/effectrenderer/EffectRenderer');
var IsSizePowerOfTwo = require('../../math/pow2/IsSizePowerOfTwo');
var MaskRenderer = require('./pipelines/maskrenderer/MaskRenderer');
var QuadBatch = require('./pipelines/quadbatch/QuadBatch');
var ParticleRenderer = require('./pipelines/particlerenderer/ParticleRenderer');
var ResourceManager = require('./ResourceManager');
var Resources = require('./resources');
var ScaleModes = require('../ScaleModes');
var ShapeBatch = require('./pipelines/shapebatch/ShapeBatch');
var SpriteRenderer = require('./pipelines/spriterenderer/SpriteRenderer');
var TileBatch = require('./pipelines/tilebatch/TileBatch');
var TilemapRenderer = require('./pipelines/tilemaprenderer/TilemapRenderer');
var WebGLSnapshot = require('../snapshot/WebGLSnapshot');
var WebGLpipeline = new Class({
initialize:
function WebGLpipeline (game)
{
var _this = this;
this.game = game;
this.onContextLostCallbacks = [];
this.onContextRestoredCallbacks = [];
this.type = CONST.WEBGL;
this.width = game.config.width * game.config.resolution;
this.height = game.config.height * game.config.resolution;
this.resolution = game.config.resolution;
this.view = game.canvas;
this.view.addEventListener('webglcontextlost', function (evt) {
var callbacks = _this.onContextLostCallbacks;
var pipelines = _this.pipelines;
for (var index = 0; index < pipelines.length; ++index)
{
pipelines[index].destroy();
}
_this.contextLost = true;
for (var index = 0; index < callbacks.length; ++index)
{
callbacks[index](_this);
}
evt.preventDefault();
}, false);
this.view.addEventListener('webglcontextrestored', function (evt) {
var callbacks = _this.onContextRestoredCallbacks;
_this.pipelines.length = 0;
_this.resourceManager.shaderCache = {};
_this.resourceManager.shaderCount = 0;
_this.contextLost = false;
_this.init();
_this.game.textures.each(function (texture) {
for (var i = 0; i < texture.source.length; ++i)
{
texture.source[i].init(_this.game);
}
}, null);
for (var index = 0; index < callbacks.length; ++index)
{
callbacks[index](_this);
}
}, false);
// 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.contextLost = false;
this.maxTextures = 1;
this.multiTexture = false;
this.blendModes = [];
this.gl = null;
this.extensions = null;
this.extensionList = {};
this.pipelines = [];
this.blitterBatch = null;
this.aaQuadBatch = null;
this.spriteBatch = null;
this.shapeBatch = null;
this.EffectRenderer = null;
this.MaskRenderer = null;
this.currentPipeline = null;
this.currentTexture = [];
this.shaderCache = {};
this.currentShader = null;
this.resourceManager = null;
this.currentRenderTarget = null;
this.snapshotCallback = null;
this.snapshotType = null;
this.snapshotEncoder = null;
this.scissor = {
enabled: false,
x: 0,
y: 0,
width: 0,
height: 0
};
this.init();
},
init: function ()
{
this.gl = this.view.getContext('webgl', this.config.WebGLContextOptions) || this.view.getContext('experimental-webgl', this.config.WebGLContextOptions);
if (!this.gl)
{
this.contextLost = true;
throw new Error('This browser does not support WebGL. Try using the Canvas pipeline.');
}
var gl = this.gl;
this.lostContext = this.getExtension('WEBGL_lose_context');
var color = this.game.config.backgroundColor;
this.resourceManager = new ResourceManager(gl);
gl.disable(gl.DEPTH_TEST);
gl.disable(gl.CULL_FACE);
gl.enable(gl.BLEND);
gl.clearColor(color.redGL, color.greenGL, color.blueGL, color.alphaGL);
// Map Blend Modes
this.blendModes = [];
for (var i = 0; i <= 16; i++)
{
this.blendModes.push({ func: [ gl.ONE, gl.ONE_MINUS_SRC_ALPHA ], equation: gl.FUNC_ADD });
}
// Add
this.blendModes[1].func = [ gl.ONE, gl.DST_ALPHA ];
// Multiply
this.blendModes[2].func = [ gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA ];
// Screen
this.blendModes[3].func = [ gl.ONE, gl.ONE_MINUS_SRC_COLOR ];
this.blendMode = -1;
this.extensions = gl.getSupportedExtensions();
this.blitterBatch = this.addPipeline(new BlitterBatch(this.game, gl, this));
//this.quadBatch = this.addPipeline(new QuadBatch(this.game, gl, this));
this.spriteRenderer = this.addPipeline(new SpriteRenderer(this.game, gl, this));
//this.shapeBatch = this.addPipeline(new ShapeBatch(this.game, gl, this));
//this.EffectRenderer = this.addPipeline(new EffectRenderer(this.game, gl, this));
//this.tileBatch = this.addPipeline(new TileBatch(this.game, gl, this));
//this.TilemapRenderer = this.addPipeline(new TilemapRenderer(this.game, gl, this));
//this.ParticleRenderer = this.addPipeline(new ParticleRenderer(this.game, gl, this));
//this.MaskRenderer = this.addPipeline(new MaskRenderer(this.game, gl, this));
this.currentPipeline = this.spriteRenderer;
this.currentVertexBuffer = null;
this.setBlendMode(0);
this.resize(this.width, this.height);
},
// https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/blendFunc
// https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/blendFuncSeparate
// https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/blendEquation
// https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/blendEquationSeparate
// https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/blendColor
addBlendMode: function (func, equation)
{
var index = this.blendModes.push({ func: func, equation: equation });
return index - 1;
},
updateBlendMode: function (index, func, equation)
{
if (this.blendModes[index])
{
this.blendModes[index].func = func;
if (equation)
{
this.blendModes[index].equation = equation;
}
}
return this;
},
removeBlendMode: function (index)
{
if (index > 16 && this.blendModes[index])
{
this.blendModes.splice(index, 1);
}
return this;
},
getExtension: function (name)
{
if (!(name in this.extensionList))
{
this.extensionList[name] = this.gl.getExtension(name);
}
return this.extensionList[name];
},
addContextLostCallback: function (callback)
{
if (this.onContextLostCallbacks.indexOf(callback) === -1)
{
this.onContextLostCallbacks.push(callback);
}
},
addContextRestoredCallback: function (callback)
{
if (this.onContextRestoredCallbacks.indexOf(callback) === -1)
{
this.onContextRestoredCallbacks.push(callback);
}
},
createTexture: function (source, width, height)
{
width = source ? source.width : width;
height = source ? source.height : height;
var gl = this.gl;
var filter = gl.NEAREST;
var wrap = IsSizePowerOfTwo(width, height) ? gl.REPEAT : gl.CLAMP_TO_EDGE;
if (!source.glTexture)
{
if (source.scaleMode === ScaleModes.LINEAR)
{
filter = gl.LINEAR;
}
else if (source.scaleMode === ScaleModes.NEAREST || this.game.config.pixelArt)
{
filter = gl.NEAREST;
}
if (!source && typeof width === 'number' && typeof height === 'number')
{
source.glTexture = this.resourceManager.createTexture(
0,
filter,
filter,
wrap,
wrap,
gl.RGBA,
null,
width, height
);
}
else
{
source.glTexture = this.resourceManager.createTexture(
0,
filter,
filter,
wrap,
wrap,
gl.RGBA,
source.image
);
}
}
this.currentTexture[0] = null;
},
setTexture: function (texture, unit)
{
unit = unit || 0;
if (this.currentTexture[unit] !== texture)
{
var gl = this.gl;
this.currentPipeline.flush();
gl.activeTexture(gl.TEXTURE0 + unit);
if (texture !== null)
{
gl.bindTexture(gl.TEXTURE_2D, texture.texture);
}
else
{
gl.bindTexture(gl.TEXTURE_2D, null);
}
this.currentTexture[unit] = texture;
}
},
setPipeline: function (pipeline)
{
if (this.currentPipeline !== pipeline ||
this.currentPipeline.shouldFlush())
{
this.currentPipeline.flush();
this.currentPipeline.endPass();
this.currentPipeline = pipeline;
}
},
setRenderTarget: function (renderTarget)
{
var gl = this.gl;
if (this.currentRenderTarget !== renderTarget)
{
this.currentPipeline.flush();
if (renderTarget !== null)
{
gl.bindFramebuffer(gl.FRAMEBUFFER, renderTarget.framebufferObject);
if (renderTarget.shouldClear)
{
gl.clearColor(0, 0, 0, renderTarget.clearAlpha);
gl.clear(gl.COLOR_BUFFER_BIT);
renderTarget.shouldClear = false;
}
}
else
{
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, this.width, this.height);
}
this.currentRenderTarget = renderTarget;
}
},
resize: function (width, height)
{
var resolution = this.game.config.resolution;
this.width = width * resolution;
this.height = height * resolution;
this.view.width = this.width;
this.view.height = this.height;
if (this.autoResize)
{
this.view.style.width = (this.width / resolution) + 'px';
this.view.style.height = (this.height / resolution) + 'px';
}
this.gl.viewport(0, 0, this.width, this.height);
for (var i = 0, l = this.pipelines.length; i < l; ++i)
{
//this.pipelines[i].bind();
//this.pipelines[i].resize(width, height, resolution);
}
//this.currentPipeline.bind();
},
// Call at the start of the render loop
preRender: function ()
{
// No point rendering if our context has been blown up!
if (this.contextLost)
{
return;
}
this.setRenderTarget(null);
// Add Pre-render hook
var gl = this.gl;
var color = this.game.config.backgroundColor;
gl.clearColor(color.redGL, color.greenGL, color.blueGL, color.alphaGL);
// Some drivers require to call glClear
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
this.setBlendMode(BlendModes.NORMAL);
this.gl.viewport(0, 0, this.width, this.height);
},
/**
* Renders a single Scene.
*
* @method render
* @param {Phaser.Scene} scene - The Scene to be rendered.
* @param {number} interpolationPercentage - The cumulative amount of time that hasn't been simulated yet, divided
* by the amount of time that will be simulated the next time update()
* runs. Useful for interpolating frames.
*/
render: function (scene, children, interpolationPercentage, camera)
{
if (this.contextLost) return;
var gl = this.gl;
var quadBatch = this.quadBatch;
this.scissor.enabled = (camera.x !== 0 || camera.y !== 0 || camera.width !== gl.canvas.width || camera.height !== gl.canvas.height);
this.setRenderTarget(null);
if (this.scissor.enabled)
{
gl.enable(gl.SCISSOR_TEST);
this.scissor.x = camera.x;
this.scissor.y = gl.drawingBufferHeight - camera.y - camera.height;
this.scissor.width = camera.width;
this.scissor.height = camera.height;
gl.scissor(this.scissor.x, this.scissor.y, this.scissor.width, this.scissor.height);
}
if (camera.backgroundColor.alphaGL > 0)
{
var color = camera.backgroundColor;
//quadBatch.bind();
//quadBatch.add(
// camera.x, camera.y, camera.width, camera.height,
// color.redGL, color.greenGL, color.blueGL, color.alphaGL
//);
//quadBatch.flush();
//this.currentPipeline.bind();
}
var list = children.list;
var length = list.length;
var pipeline;
for (var index = 0; index < length; ++index)
{
var child = list[index];
if (!child.willRender())
{
continue;
}
if (child.blendMode !== this.blendMode)
{
this.setBlendMode(child.blendMode);
}
if (child.mask)
{
child.mask.preRenderWebGL(this, child, camera);
}
// drawing child
child.renderWebGL(this, child, interpolationPercentage, camera);
if (child.mask)
{
child.mask.postRenderWebGL(this, child);
}
pipeline = this.currentPipeline;
if (pipeline.shouldFlush())
{
pipeline.flush();
}
}
this.currentPipeline.flush();
if (camera._fadeAlpha > 0 || camera._flashAlpha > 0)
{
this.setRenderTarget(null);
this.setBlendMode(BlendModes.NORMAL);
// // fade rendering
// quadBatch.add(
// camera.x, camera.y, camera.width, camera.height,
// camera._fadeRed,
// camera._fadeGreen,
// camera._fadeBlue,
// camera._fadeAlpha
// );
// // flash rendering
// quadBatch.add(
// camera.x, camera.y, camera.width, camera.height,
// camera._flashRed,
// camera._flashGreen,
// camera._flashBlue,
// camera._flashAlpha
// );
// quadBatch.flush();
// this.currentPipeline.bind();
}
if (this.scissor.enabled)
{
gl.disable(gl.SCISSOR_TEST);
}
},
// Called at the end of the render loop (tidy things up, etc)
postRender: function ()
{
if (this.contextLost) return;
this.currentPipeline.flush();
if (this.snapshotCallback)
{
this.snapshotCallback(WebGLSnapshot(this.view, this.snapshotType, this.snapshotEncoder));
this.snapshotCallback = null;
}
// Add Post-render hook
// console.log('%c render end ', 'color: #ffffff; background: #ff0000;');
},
snapshot: function (callback, type, encoderOptions)
{
this.snapshotCallback = callback;
this.snapshotType = type;
this.snapshotEncoder = encoderOptions;
},
createFBO: function () {},
setBlendMode: function (newBlendMode)
{
var gl = this.gl;
if (newBlendMode === BlendModes.SKIP_CHECK)
{
return;
}
var pipeline = this.currentPipeline;
if (this.blendMode !== newBlendMode)
{
if (pipeline)
{
pipeline.flush();
}
var blend = this.blendModes[newBlendMode].func;
gl.enable(gl.BLEND);
gl.blendEquation(this.blendModes[newBlendMode].equation);
if (blend.length > 2)
{
gl.blendFuncSeparate(blend[0], blend[1], blend[2], blend[3]);
}
else
{
gl.blendFunc(blend[0], blend[1]);
}
this.blendMode = newBlendMode;
}
},
addPipeline: function (pipelineInstance)
{
var index = this.pipelines.indexOf(pipelineInstance);
if (index < 0)
{
this.pipelines.push(pipelineInstance);
return pipelineInstance;
}
return null;
},
setTextureFilterMode: function (texture, filterMode)
{
var gl = this.gl;
var glFilter = [ gl.LINEAR, gl.NEAREST ][filterMode];
gl.bindTexture(gl.TEXTURE_2D, texture.texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, glFilter);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, glFilter);
if (this.currentTexture[0] !== null)
{
gl.bindTexture(gl.TEXTURE_2D, this.currentTexture[0].texture);
}
else
{
gl.bindTexture(gl.TEXTURE_2D, null);
}
return texture;
},
uploadCanvasToGPU: function (srcCanvas, dstTexture, shouldReallocate)
{
var gl = this.gl;
if (!dstTexture)
{
dstTexture = new Resources.Texture(null, 0, 0);
// Only call this once
dstTexture.texture = gl.createTexture();
}
if (dstTexture != this.currentTexture[0])
{
this.currentPipeline.flush();
}
gl.activeTexture(gl.TEXTURE0);
if (!shouldReallocate)
{
// Update resource
gl.bindTexture(gl.TEXTURE_2D, dstTexture.texture);
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, srcCanvas);
}
else
{
// Allocate or Reallocate resource
gl.bindTexture(gl.TEXTURE_2D, dstTexture.texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, srcCanvas);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
dstTexture.width = srcCanvas.width;
dstTexture.height = srcCanvas.height;
}
// We must rebind old texture
if (this.currentTexture.length > 0 && dstTexture != this.currentTexture[0] && this.currentTexture[0] !== null)
{
gl.bindTexture(gl.TEXTURE_2D, this.currentTexture[0].texture);
}
return dstTexture;
},
destroy: function ()
{
if (this.lostContext)
{
this.lostContext.loseContext();
}
this.gl = null;
}
});
module.exports = WebGLpipeline;

View file

@ -290,7 +290,7 @@ var SpriteRenderer = new Class({
orthoViewMatrix[0] = +2.0 / this.width;
orthoViewMatrix[5] = -2.0 / this.height;
shader.setConstantMatrix4x4(shader.getUniformLocation('uOrthoMatrix'), orthoViewMatrix);
this.setMatrix4('uOrthoMatrix', orthoViewMatrix);
manager.setTexture(texture, 0);
vertexViewF32[0] = tx0;