mirror of
https://github.com/photonstorm/phaser
synced 2024-11-28 07:31:11 +00:00
Merge branch 'master' of https://github.com/photonstorm/phaser
This commit is contained in:
commit
35b1cce047
14 changed files with 33 additions and 783 deletions
|
@ -74,15 +74,12 @@ var GameObjects = {
|
||||||
if (WEBGL_RENDERER)
|
if (WEBGL_RENDERER)
|
||||||
{
|
{
|
||||||
// WebGL only Game Objects
|
// WebGL only Game Objects
|
||||||
GameObjects.LightLayer = require('./lightlayer/LightLayer');
|
|
||||||
GameObjects.Mesh = require('./mesh/Mesh');
|
GameObjects.Mesh = require('./mesh/Mesh');
|
||||||
GameObjects.Quad = require('./quad/Quad');
|
GameObjects.Quad = require('./quad/Quad');
|
||||||
|
|
||||||
GameObjects.Factories.LightLayer = require('./lightlayer/LightLayerFactory');
|
|
||||||
GameObjects.Factories.Mesh = require('./mesh/MeshFactory');
|
GameObjects.Factories.Mesh = require('./mesh/MeshFactory');
|
||||||
GameObjects.Factories.Quad = require('./quad/QuadFactory');
|
GameObjects.Factories.Quad = require('./quad/QuadFactory');
|
||||||
|
|
||||||
GameObjects.Creators.LightLayer = require('./lightlayer/LightLayerCreator');
|
|
||||||
GameObjects.Creators.Mesh = require('./mesh/MeshCreator');
|
GameObjects.Creators.Mesh = require('./mesh/MeshCreator');
|
||||||
GameObjects.Creators.Quad = require('./quad/QuadCreator');
|
GameObjects.Creators.Quad = require('./quad/QuadCreator');
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
|
|
||||||
MAX_LIGHTS: 10,
|
|
||||||
DEFERRED_MAX_LIGHTS: 50
|
|
||||||
|
|
||||||
};
|
|
|
@ -1,165 +0,0 @@
|
||||||
var VertexBuffer = require('../../renderer/webgl/resources/VertexBuffer');
|
|
||||||
var GameObject = require('../GameObject');
|
|
||||||
|
|
||||||
var DeferredRenderer = function (renderer, lightLayer, interpolationPercentage, camera)
|
|
||||||
{
|
|
||||||
var spriteList = lightLayer.sprites;
|
|
||||||
var length = spriteList.length;
|
|
||||||
var batch = renderer.spriteBatch;
|
|
||||||
var gl = renderer.gl;
|
|
||||||
|
|
||||||
if (GameObject.RENDER_MASK !== lightLayer.renderFlags || length === 0 || (lightLayer.cameraFilter > 0 && (lightLayer.cameraFilter & camera._id)))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (renderer.currentRenderer !== null)
|
|
||||||
{
|
|
||||||
renderer.currentRenderer.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.bind(lightLayer.gBufferShaderPass);
|
|
||||||
batch.indexBufferObject.bind();
|
|
||||||
lightLayer.updateLights(renderer, camera, lightLayer.lightPassShader);
|
|
||||||
|
|
||||||
gl.bindFramebuffer(gl.FRAMEBUFFER, lightLayer.gBufferFbo);
|
|
||||||
gl.clearColor(0, 0, 0, 0);
|
|
||||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
for (var index = 0; index < length; ++index)
|
|
||||||
{
|
|
||||||
var spriteNormalPair = spriteList[index];
|
|
||||||
var gameObject = spriteNormalPair.spriteRef;
|
|
||||||
|
|
||||||
/* Inlined function of add sprite modified. */
|
|
||||||
{
|
|
||||||
var tempMatrix = batch.tempMatrix;
|
|
||||||
var frame = gameObject.frame;
|
|
||||||
var forceFlipY = (frame.texture.source[frame.sourceIndex].glTexture.isRenderTexture ? true : false);
|
|
||||||
var flipX = gameObject.flipX;
|
|
||||||
var flipY = gameObject.flipY ^ forceFlipY;
|
|
||||||
var vertexDataBuffer = batch.vertexDataBuffer;
|
|
||||||
var vertexBufferObjectF32 = vertexDataBuffer.floatView;
|
|
||||||
var vertexBufferObjectU32 = vertexDataBuffer.uintView;
|
|
||||||
var vertexOffset = 0;
|
|
||||||
var uvs = frame.uvs;
|
|
||||||
var width = frame.width * (flipX ? -1 : 1);
|
|
||||||
var height = frame.height * (flipY ? -1 : 1);
|
|
||||||
var translateX = gameObject.x - camera.scrollX * gameObject.scrollFactorX;
|
|
||||||
var translateY = gameObject.y - camera.scrollY * gameObject.scrollFactorY;
|
|
||||||
var scaleX = gameObject.scaleX;
|
|
||||||
var scaleY = gameObject.scaleY;
|
|
||||||
var rotation = -gameObject.rotation;
|
|
||||||
var tempMatrixMatrix = tempMatrix.matrix;
|
|
||||||
var x = -gameObject.displayOriginX + frame.x + ((frame.width) * (flipX ? 1 : 0.0));
|
|
||||||
var y = -gameObject.displayOriginY + frame.y + ((frame.height) * (flipY ? 1 : 0.0));
|
|
||||||
var xw = x + width;
|
|
||||||
var yh = y + height;
|
|
||||||
var cameraMatrix = camera.matrix.matrix;
|
|
||||||
var mva, mvb, mvc, mvd, mve, mvf, tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3;
|
|
||||||
var sra, srb, src, srd, sre, srf, cma, cmb, cmc, cmd, cme, cmf;
|
|
||||||
var alphaTL = gameObject._alphaTL;
|
|
||||||
var alphaTR = gameObject._alphaTR;
|
|
||||||
var alphaBL = gameObject._alphaBL;
|
|
||||||
var alphaBR = gameObject._alphaBR;
|
|
||||||
var tintTL = gameObject._tintTL;
|
|
||||||
var tintTR = gameObject._tintTR;
|
|
||||||
var tintBL = gameObject._tintBL;
|
|
||||||
var tintBR = gameObject._tintBR;
|
|
||||||
|
|
||||||
tempMatrix.applyITRS(translateX, translateY, rotation, scaleX, scaleY);
|
|
||||||
|
|
||||||
sra = tempMatrixMatrix[0];
|
|
||||||
srb = tempMatrixMatrix[1];
|
|
||||||
src = tempMatrixMatrix[2];
|
|
||||||
srd = tempMatrixMatrix[3];
|
|
||||||
sre = tempMatrixMatrix[4];
|
|
||||||
srf = tempMatrixMatrix[5];
|
|
||||||
|
|
||||||
cma = cameraMatrix[0];
|
|
||||||
cmb = cameraMatrix[1];
|
|
||||||
cmc = cameraMatrix[2];
|
|
||||||
cmd = cameraMatrix[3];
|
|
||||||
cme = cameraMatrix[4];
|
|
||||||
cmf = cameraMatrix[5];
|
|
||||||
|
|
||||||
mva = sra * cma + srb * cmc;
|
|
||||||
mvb = sra * cmb + srb * cmd;
|
|
||||||
mvc = src * cma + srd * cmc;
|
|
||||||
mvd = src * cmb + srd * cmd;
|
|
||||||
mve = sre * cma + srf * cmc + cme;
|
|
||||||
mvf = sre * cmb + srf * cmd + cmf;
|
|
||||||
|
|
||||||
tx0 = x * mva + y * mvc + mve;
|
|
||||||
ty0 = x * mvb + y * mvd + mvf;
|
|
||||||
tx1 = x * mva + yh * mvc + mve;
|
|
||||||
ty1 = x * mvb + yh * mvd + mvf;
|
|
||||||
tx2 = xw * mva + yh * mvc + mve;
|
|
||||||
ty2 = xw * mvb + yh * mvd + mvf;
|
|
||||||
tx3 = xw * mva + y * mvc + mve;
|
|
||||||
ty3 = xw * mvb + y * mvd + mvf;
|
|
||||||
|
|
||||||
|
|
||||||
if (renderer.currentTexture[0] !== frame.texture.source[frame.sourceIndex].glTexture ||
|
|
||||||
renderer.currentTexture[1] !== spriteNormalPair.normalTextureRef.source[spriteNormalPair.spriteRef.frame.sourceIndex].glTexture ||
|
|
||||||
batch.shouldFlush())
|
|
||||||
{
|
|
||||||
batch.flush(lightLayer.gBufferShaderPass, lightLayer.renderTarget);
|
|
||||||
renderer.setTexture(frame.texture.source[frame.sourceIndex].glTexture, 0);
|
|
||||||
renderer.setTexture(spriteNormalPair.normalTextureRef.source[spriteNormalPair.spriteRef.frame.sourceIndex].glTexture, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.drawIndexed = true;
|
|
||||||
batch.drawingMesh = false;
|
|
||||||
vertexOffset = vertexDataBuffer.allocate(24);
|
|
||||||
batch.elementCount += 6;
|
|
||||||
|
|
||||||
// Top Left
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = tx0;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = ty0;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = uvs.x0;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = uvs.y0;
|
|
||||||
vertexBufferObjectU32[vertexOffset++] = tintTL;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = alphaTL;
|
|
||||||
|
|
||||||
// Bottom Left
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = tx1;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = ty1;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = uvs.x1;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = uvs.y1;
|
|
||||||
vertexBufferObjectU32[vertexOffset++] = tintBL;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = alphaBL;
|
|
||||||
|
|
||||||
// Bottom Right
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = tx2;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = ty2;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = uvs.x2;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = uvs.y2;
|
|
||||||
vertexBufferObjectU32[vertexOffset++] = tintBR;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = alphaBR;
|
|
||||||
|
|
||||||
// Top Right
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = tx3;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = ty3;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = uvs.x3;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = uvs.y3;
|
|
||||||
vertexBufferObjectU32[vertexOffset++] = tintTR;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = alphaTR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.flush(lightLayer.gBufferShaderPass, lightLayer.renderTarget);
|
|
||||||
renderer.setTexture({texture: lightLayer.gBufferColorTex}, 0);
|
|
||||||
renderer.setTexture({texture: lightLayer.gBufferNormalTex}, 1);
|
|
||||||
|
|
||||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
||||||
lightLayer.lightPassShader.bind();
|
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, lightLayer.lightPassVBO);
|
|
||||||
gl.enableVertexAttribArray(0);
|
|
||||||
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, Float32Array.BYTES_PER_ELEMENT, 0);
|
|
||||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
||||||
VertexBuffer.SetDirty();
|
|
||||||
batch.bind();
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = DeferredRenderer;
|
|
|
@ -1,149 +0,0 @@
|
||||||
var GameObject = require('../GameObject');
|
|
||||||
|
|
||||||
var ForwardRenderer = function (renderer, lightLayer, interpolationPercentage, camera)
|
|
||||||
{
|
|
||||||
var spriteList = lightLayer.sprites;
|
|
||||||
var length = spriteList.length;
|
|
||||||
var batch = renderer.spriteBatch;
|
|
||||||
|
|
||||||
if (GameObject.RENDER_MASK !== lightLayer.renderFlags || length === 0 || (lightLayer.cameraFilter > 0 && (lightLayer.cameraFilter & camera._id)))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (renderer.currentRenderer !== null)
|
|
||||||
{
|
|
||||||
renderer.currentRenderer.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.bind(lightLayer.passShader);
|
|
||||||
batch.indexBufferObject.bind();
|
|
||||||
lightLayer.updateLights(renderer, camera, lightLayer.passShader);
|
|
||||||
|
|
||||||
for (var index = 0; index < length; ++index)
|
|
||||||
{
|
|
||||||
var spriteNormalPair = spriteList[index];
|
|
||||||
var gameObject = spriteNormalPair.spriteRef;
|
|
||||||
|
|
||||||
/* Inlined function of add sprite modified. */
|
|
||||||
{
|
|
||||||
var tempMatrix = batch.tempMatrix;
|
|
||||||
var frame = gameObject.frame;
|
|
||||||
var forceFlipY = (frame.texture.source[frame.sourceIndex].glTexture.isRenderTexture ? true : false);
|
|
||||||
var flipX = gameObject.flipX;
|
|
||||||
var flipY = gameObject.flipY ^ forceFlipY;
|
|
||||||
var vertexDataBuffer = batch.vertexDataBuffer;
|
|
||||||
var vertexBufferObjectF32 = vertexDataBuffer.floatView;
|
|
||||||
var vertexBufferObjectU32 = vertexDataBuffer.uintView;
|
|
||||||
var vertexOffset = 0;
|
|
||||||
var uvs = frame.uvs;
|
|
||||||
var width = frame.width * (flipX ? -1 : 1);
|
|
||||||
var height = frame.height * (flipY ? -1 : 1);
|
|
||||||
var translateX = gameObject.x - camera.scrollX * gameObject.scrollFactorX;
|
|
||||||
var translateY = gameObject.y - camera.scrollY * gameObject.scrollFactorY;
|
|
||||||
var scaleX = gameObject.scaleX;
|
|
||||||
var scaleY = gameObject.scaleY;
|
|
||||||
var rotation = -gameObject.rotation;
|
|
||||||
var tempMatrixMatrix = tempMatrix.matrix;
|
|
||||||
var x = -gameObject.displayOriginX + frame.x + ((frame.width) * (flipX ? 1 : 0.0));
|
|
||||||
var y = -gameObject.displayOriginY + frame.y + ((frame.height) * (flipY ? 1 : 0.0));
|
|
||||||
var xw = x + width;
|
|
||||||
var yh = y + height;
|
|
||||||
var cameraMatrix = camera.matrix.matrix;
|
|
||||||
var mva, mvb, mvc, mvd, mve, mvf, tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3;
|
|
||||||
var sra, srb, src, srd, sre, srf, cma, cmb, cmc, cmd, cme, cmf;
|
|
||||||
var alphaTL = gameObject._alphaTL;
|
|
||||||
var alphaTR = gameObject._alphaTR;
|
|
||||||
var alphaBL = gameObject._alphaBL;
|
|
||||||
var alphaBR = gameObject._alphaBR;
|
|
||||||
var tintTL = gameObject._tintTL;
|
|
||||||
var tintTR = gameObject._tintTR;
|
|
||||||
var tintBL = gameObject._tintBL;
|
|
||||||
var tintBR = gameObject._tintBR;
|
|
||||||
|
|
||||||
tempMatrix.applyITRS(translateX, translateY, rotation, scaleX, scaleY);
|
|
||||||
|
|
||||||
sra = tempMatrixMatrix[0];
|
|
||||||
srb = tempMatrixMatrix[1];
|
|
||||||
src = tempMatrixMatrix[2];
|
|
||||||
srd = tempMatrixMatrix[3];
|
|
||||||
sre = tempMatrixMatrix[4];
|
|
||||||
srf = tempMatrixMatrix[5];
|
|
||||||
|
|
||||||
cma = cameraMatrix[0];
|
|
||||||
cmb = cameraMatrix[1];
|
|
||||||
cmc = cameraMatrix[2];
|
|
||||||
cmd = cameraMatrix[3];
|
|
||||||
cme = cameraMatrix[4];
|
|
||||||
cmf = cameraMatrix[5];
|
|
||||||
|
|
||||||
mva = sra * cma + srb * cmc;
|
|
||||||
mvb = sra * cmb + srb * cmd;
|
|
||||||
mvc = src * cma + srd * cmc;
|
|
||||||
mvd = src * cmb + srd * cmd;
|
|
||||||
mve = sre * cma + srf * cmc + cme;
|
|
||||||
mvf = sre * cmb + srf * cmd + cmf;
|
|
||||||
|
|
||||||
tx0 = x * mva + y * mvc + mve;
|
|
||||||
ty0 = x * mvb + y * mvd + mvf;
|
|
||||||
tx1 = x * mva + yh * mvc + mve;
|
|
||||||
ty1 = x * mvb + yh * mvd + mvf;
|
|
||||||
tx2 = xw * mva + yh * mvc + mve;
|
|
||||||
ty2 = xw * mvb + yh * mvd + mvf;
|
|
||||||
tx3 = xw * mva + y * mvc + mve;
|
|
||||||
ty3 = xw * mvb + y * mvd + mvf;
|
|
||||||
|
|
||||||
|
|
||||||
if (renderer.currentTexture[0] !== frame.texture.source[frame.sourceIndex].glTexture ||
|
|
||||||
renderer.currentTexture[1] !== spriteNormalPair.normalTextureRef.source[spriteNormalPair.spriteRef.frame.sourceIndex].glTexture ||
|
|
||||||
batch.shouldFlush())
|
|
||||||
{
|
|
||||||
batch.flush(lightLayer.passShader, lightLayer.renderTarget);
|
|
||||||
renderer.setTexture(frame.texture.source[frame.sourceIndex].glTexture, 0);
|
|
||||||
renderer.setTexture(spriteNormalPair.normalTextureRef.source[spriteNormalPair.spriteRef.frame.sourceIndex].glTexture, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.drawIndexed = true;
|
|
||||||
batch.drawingMesh = false;
|
|
||||||
vertexOffset = vertexDataBuffer.allocate(24);
|
|
||||||
batch.elementCount += 6;
|
|
||||||
|
|
||||||
// Top Left
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = tx0;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = ty0;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = uvs.x0;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = uvs.y0;
|
|
||||||
vertexBufferObjectU32[vertexOffset++] = tintTL;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = alphaTL;
|
|
||||||
|
|
||||||
// Bottom Left
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = tx1;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = ty1;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = uvs.x1;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = uvs.y1;
|
|
||||||
vertexBufferObjectU32[vertexOffset++] = tintBL;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = alphaBL;
|
|
||||||
|
|
||||||
// Bottom Right
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = tx2;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = ty2;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = uvs.x2;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = uvs.y2;
|
|
||||||
vertexBufferObjectU32[vertexOffset++] = tintBR;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = alphaBR;
|
|
||||||
|
|
||||||
// Top Right
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = tx3;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = ty3;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = uvs.x3;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = uvs.y3;
|
|
||||||
vertexBufferObjectU32[vertexOffset++] = tintTR;
|
|
||||||
vertexBufferObjectF32[vertexOffset++] = alphaTR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.flush(lightLayer.passShader, lightLayer.renderTarget);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = ForwardRenderer;
|
|
|
@ -1,37 +0,0 @@
|
||||||
var Class = require('../../utils/Class');
|
|
||||||
|
|
||||||
var Light = new Class({
|
|
||||||
|
|
||||||
initialize:
|
|
||||||
|
|
||||||
function Light (x, y, z, radius, r, g, b, attenuation)
|
|
||||||
{
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.z = z;
|
|
||||||
this.radius = radius;
|
|
||||||
this.r = r;
|
|
||||||
this.g = g;
|
|
||||||
this.b = b;
|
|
||||||
this.attenuation = attenuation;
|
|
||||||
this.scrollFactorX = 1.0;
|
|
||||||
this.scrollFactorY = 1.0;
|
|
||||||
},
|
|
||||||
|
|
||||||
set: function (x, y, z, radius, r, g, b, attenuation)
|
|
||||||
{
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.z = z;
|
|
||||||
this.radius = radius;
|
|
||||||
this.r = r;
|
|
||||||
this.g = g;
|
|
||||||
this.b = b;
|
|
||||||
this.attenuation = attenuation;
|
|
||||||
this.scrollFactorX = 1.0;
|
|
||||||
this.scrollFactorY = 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = Light;
|
|
|
@ -1,336 +0,0 @@
|
||||||
|
|
||||||
var Class = require('../../utils/Class');
|
|
||||||
var Components = require('../components');
|
|
||||||
var Const = require('./Const');
|
|
||||||
var GameObject = require('../GameObject');
|
|
||||||
var Light = require('./Light');
|
|
||||||
var Render = require('./LightLayerRender');
|
|
||||||
var SpriteNormalPair = require('./SpriteNormalPair');
|
|
||||||
|
|
||||||
// http://cpetry.github.io/NormalMap-Online/
|
|
||||||
|
|
||||||
var LightLayer = new Class({
|
|
||||||
|
|
||||||
Extends: GameObject,
|
|
||||||
|
|
||||||
Mixins: [
|
|
||||||
Components.Alpha,
|
|
||||||
Components.BlendMode,
|
|
||||||
Components.Origin,
|
|
||||||
Components.ScrollFactor,
|
|
||||||
Components.Visible,
|
|
||||||
Render
|
|
||||||
],
|
|
||||||
|
|
||||||
initialize:
|
|
||||||
|
|
||||||
function LightLayer (scene)
|
|
||||||
{
|
|
||||||
var _this = this;
|
|
||||||
|
|
||||||
GameObject.call(this, scene, 'LightLayer');
|
|
||||||
|
|
||||||
this.renderer = scene.sys.game.renderer;
|
|
||||||
this.passShader = null;
|
|
||||||
this.gl = null;
|
|
||||||
this.ambientLightColorR = 0.0;
|
|
||||||
this.ambientLightColorG = 0.0;
|
|
||||||
this.ambientLightColorB = 0.0;
|
|
||||||
this.lightPool = [];
|
|
||||||
this.spritePool = [];
|
|
||||||
this.lights = [];
|
|
||||||
this.sprites = [];
|
|
||||||
this._z = 0;
|
|
||||||
this.setOrigin(0, 0);
|
|
||||||
|
|
||||||
this.renderer.onContextRestored(function (renderer) {
|
|
||||||
this.onContextRestored(renderer);
|
|
||||||
}, this);
|
|
||||||
|
|
||||||
this.init(scene.sys.game.renderer, this.renderer.hasExtension('WEBGL_draw_buffers'));
|
|
||||||
},
|
|
||||||
|
|
||||||
onContextRestored: function (renderer)
|
|
||||||
{
|
|
||||||
/* It won't allow the use of drawBuffers on restored context */
|
|
||||||
this.init(renderer, false);
|
|
||||||
//this.renderWebGL = require('./ForwardRenderer');
|
|
||||||
this.lights.length = Math.min(this.lights.length, Const.MAX_LIGHTS);
|
|
||||||
},
|
|
||||||
|
|
||||||
init: function (renderer, deferred)
|
|
||||||
{
|
|
||||||
/*var resourceManager = renderer.resourceManager;
|
|
||||||
|
|
||||||
this._isDeferred = deferred;
|
|
||||||
this.renderer = renderer;
|
|
||||||
this.lightsLocations = [];
|
|
||||||
|
|
||||||
if (resourceManager !== undefined && !this._isDeferred)
|
|
||||||
{
|
|
||||||
this.gl = renderer.gl;
|
|
||||||
|
|
||||||
this.passShader = resourceManager.createShader('Phong2DShaderForward', {
|
|
||||||
vert: TexturedAndNormalizedTintedShader.vert,
|
|
||||||
frag: LightFragmentShader(Const.MAX_LIGHTS)
|
|
||||||
});
|
|
||||||
|
|
||||||
this.ambientLightColorLoc = this.passShader.getUniformLocation('uAmbientLightColor');
|
|
||||||
this.uMainTextureLoc = this.passShader.getUniformLocation('uMainTexture');
|
|
||||||
this.uNormTextureLoc = this.passShader.getUniformLocation('uNormTexture');
|
|
||||||
this.uResolutionLoc = this.passShader.getUniformLocation('uResolution');
|
|
||||||
this.uCameraLoc = this.passShader.getUniformLocation('uCamera');
|
|
||||||
|
|
||||||
this.passShader.setConstantInt1(this.uMainTextureLoc, 0);
|
|
||||||
this.passShader.setConstantInt1(this.uNormTextureLoc, 1);
|
|
||||||
|
|
||||||
for (var index = 0; index < Const.MAX_LIGHTS; ++index)
|
|
||||||
{
|
|
||||||
this.lightsLocations[index] = {
|
|
||||||
position: this.passShader.getUniformLocation('uLights[' + index + '].position'),
|
|
||||||
color: this.passShader.getUniformLocation('uLights[' + index + '].color'),
|
|
||||||
attenuation: this.passShader.getUniformLocation('uLights[' + index + '].attenuation'),
|
|
||||||
radius: this.passShader.getUniformLocation('uLights[' + index + '].radius')
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var gl = this.gl = renderer.gl;
|
|
||||||
|
|
||||||
this.ext = renderer.getExtension('WEBGL_draw_buffers');
|
|
||||||
|
|
||||||
this.gBufferShaderPass = resourceManager.createShader('GBufferShader', {
|
|
||||||
vert: TexturedAndNormalizedTintedShader.vert,
|
|
||||||
frag: GBufferShader()
|
|
||||||
});
|
|
||||||
|
|
||||||
var phongShader = Phong2DShaderDeferred(Const.DEFERRED_MAX_LIGHTS);
|
|
||||||
|
|
||||||
this.lightPassShader = resourceManager.createShader('Phong2DShaderDeferred', {
|
|
||||||
vert: phongShader.vert,
|
|
||||||
frag: phongShader.frag
|
|
||||||
});
|
|
||||||
|
|
||||||
this.lightPassVBO = gl.createBuffer();
|
|
||||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.lightPassVBO);
|
|
||||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ -1, 7, -1, -1, 7, -1 ]), gl.STATIC_DRAW);
|
|
||||||
|
|
||||||
this.uMainTextureLoc = this.gBufferShaderPass.getUniformLocation('uMainTexture');
|
|
||||||
this.uNormTextureLoc = this.gBufferShaderPass.getUniformLocation('uNormTexture');
|
|
||||||
|
|
||||||
this.gBufferShaderPass.setConstantInt1(this.uMainTextureLoc, 0);
|
|
||||||
this.gBufferShaderPass.setConstantInt1(this.uNormTextureLoc, 1);
|
|
||||||
|
|
||||||
this.ambientLightColorLoc = this.lightPassShader.getUniformLocation('uAmbientLightColor');
|
|
||||||
this.uResolutionLoc = this.lightPassShader.getUniformLocation('uResolution');
|
|
||||||
this.uGbufferColorLoc = this.lightPassShader.getUniformLocation('uGbufferColor');
|
|
||||||
this.uGbufferNormalLoc = this.lightPassShader.getUniformLocation('uGbufferNormal');
|
|
||||||
this.uCameraLoc = this.lightPassShader.getUniformLocation('uCamera');
|
|
||||||
|
|
||||||
this.lightPassShader.setConstantInt1(this.uGbufferColorLoc, 0);
|
|
||||||
this.lightPassShader.setConstantInt1(this.uGbufferNormalLoc, 1);
|
|
||||||
|
|
||||||
this.gBufferShaderPass.bindAttribLocation(0, 'v_tex_coord');
|
|
||||||
this.gBufferShaderPass.bindAttribLocation(1, 'v_color');
|
|
||||||
this.gBufferShaderPass.bindAttribLocation(2, 'v_alpha');
|
|
||||||
this.lightPassShader.bindAttribLocation(0, 'vertexPosition');
|
|
||||||
|
|
||||||
for (var index = 0; index < Const.DEFERRED_MAX_LIGHTS; ++index)
|
|
||||||
{
|
|
||||||
this.lightsLocations[index] = {
|
|
||||||
position: this.lightPassShader.getUniformLocation('uLights[' + index + '].position'),
|
|
||||||
color: this.lightPassShader.getUniformLocation('uLights[' + index + '].color'),
|
|
||||||
attenuation: this.lightPassShader.getUniformLocation('uLights[' + index + '].attenuation'),
|
|
||||||
radius: this.lightPassShader.getUniformLocation('uLights[' + index + '].radius')
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup render targets
|
|
||||||
this.gBufferFbo = gl.createFramebuffer();
|
|
||||||
this.gBufferColorTex = gl.createTexture();
|
|
||||||
this.gBufferNormalTex = gl.createTexture();
|
|
||||||
|
|
||||||
gl.bindFramebuffer(gl.FRAMEBUFFER, this.gBufferFbo);
|
|
||||||
gl.bindTexture(gl.TEXTURE_2D, this.gBufferColorTex);
|
|
||||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, renderer.width, renderer.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
||||||
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);
|
|
||||||
|
|
||||||
gl.bindTexture(gl.TEXTURE_2D, this.gBufferNormalTex);
|
|
||||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, renderer.width, renderer.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
||||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
||||||
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);
|
|
||||||
|
|
||||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, this.ext.COLOR_ATTACHMENT0_WEBGL, gl.TEXTURE_2D, this.gBufferColorTex, 0);
|
|
||||||
gl.framebufferTexture2D(gl.FRAMEBUFFER, this.ext.COLOR_ATTACHMENT1_WEBGL, gl.TEXTURE_2D, this.gBufferNormalTex, 0);
|
|
||||||
|
|
||||||
this.ext.drawBuffersWEBGL([ this.ext.COLOR_ATTACHMENT0_WEBGL, this.ext.COLOR_ATTACHMENT1_WEBGL ]);
|
|
||||||
|
|
||||||
var complete = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
|
|
||||||
|
|
||||||
if (complete !== gl.FRAMEBUFFER_COMPLETE)
|
|
||||||
{
|
|
||||||
var errors = {
|
|
||||||
36054: 'Incomplete Attachment',
|
|
||||||
36055: 'Missing Attachment',
|
|
||||||
36057: 'Incomplete Dimensions',
|
|
||||||
36061: 'Framebuffer Unsupported'
|
|
||||||
};
|
|
||||||
throw new Error('Framebuffer incomplete. Framebuffer status: ' + errors[complete]);
|
|
||||||
}
|
|
||||||
|
|
||||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
||||||
VertexBuffer.SetDirty();
|
|
||||||
}*/
|
|
||||||
},
|
|
||||||
|
|
||||||
forEachLight: function (callback)
|
|
||||||
{
|
|
||||||
if (!callback)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var lights = this.lights;
|
|
||||||
var length = lights.length;
|
|
||||||
|
|
||||||
for (var index = 0; index < length; ++index)
|
|
||||||
{
|
|
||||||
callback(lights[index]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
get z ()
|
|
||||||
{
|
|
||||||
return this._z;
|
|
||||||
},
|
|
||||||
|
|
||||||
set z (newZ)
|
|
||||||
{
|
|
||||||
this._z = newZ;
|
|
||||||
},
|
|
||||||
|
|
||||||
setAmbientLightColor: function (r, g, b)
|
|
||||||
{
|
|
||||||
this.ambientLightColorR = r;
|
|
||||||
this.ambientLightColorG = g;
|
|
||||||
this.ambientLightColorB = b;
|
|
||||||
},
|
|
||||||
|
|
||||||
getMaxLights: function ()
|
|
||||||
{
|
|
||||||
return (this._isDeferred) ? Const.DEFERRED_MAX_LIGHTS : Const.MAX_LIGHTS;
|
|
||||||
},
|
|
||||||
|
|
||||||
getLightCount: function ()
|
|
||||||
{
|
|
||||||
return this.lights.length;
|
|
||||||
},
|
|
||||||
|
|
||||||
isDeferred: function ()
|
|
||||||
{
|
|
||||||
return this._isDeferred;
|
|
||||||
},
|
|
||||||
|
|
||||||
/* This will probably be removed later */
|
|
||||||
addSprite: function (sprite, normalTexture)
|
|
||||||
{
|
|
||||||
var spriteNormalPair;
|
|
||||||
|
|
||||||
if (this.spritePool.length > 0)
|
|
||||||
{
|
|
||||||
spriteNormalPair = this.spritePool.pop();
|
|
||||||
spriteNormalPair.set(sprite, normalTexture);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
spriteNormalPair = new SpriteNormalPair(sprite, normalTexture);
|
|
||||||
}
|
|
||||||
this.scene.sys.displayList.remove(sprite);
|
|
||||||
this.sprites.push(spriteNormalPair);
|
|
||||||
},
|
|
||||||
|
|
||||||
removeSprite: function (sprite)
|
|
||||||
{
|
|
||||||
var length = this.sprites.length;
|
|
||||||
for (var index = 0; index < length; ++index)
|
|
||||||
{
|
|
||||||
if (this.sprites[index].spriteRef === sprite)
|
|
||||||
{
|
|
||||||
this.spritePool.push(this.sprites[index]);
|
|
||||||
this.sprites.splice(index, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sprite;
|
|
||||||
},
|
|
||||||
|
|
||||||
addLight: function (x, y, z, radius, r, g, b, attenuation)
|
|
||||||
{
|
|
||||||
if (this.lights.length < this.getMaxLights())
|
|
||||||
{
|
|
||||||
var light = null;
|
|
||||||
if (this.lightPool.length > 0)
|
|
||||||
{
|
|
||||||
light = this.lightPool.pop();
|
|
||||||
light.set(x, y, z, radius, r, g, b, attenuation);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
light = new Light(x, y, z, radius, r, g, b, attenuation);
|
|
||||||
}
|
|
||||||
this.lights.push(light);
|
|
||||||
return light;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
|
|
||||||
removeLight: function (light)
|
|
||||||
{
|
|
||||||
var index = this.lights.indexOf(light);
|
|
||||||
|
|
||||||
if (index >= 0)
|
|
||||||
{
|
|
||||||
this.lightPool.push(light);
|
|
||||||
this.lights.splice(index, 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateLights: function (renderer, camera, shader)
|
|
||||||
{
|
|
||||||
/*if (this.gl !== null)
|
|
||||||
{
|
|
||||||
var locations = this.lightsLocations;
|
|
||||||
var lights = this.lights;
|
|
||||||
var length = lights.length;
|
|
||||||
var point = {x: 0, y: 0};
|
|
||||||
var height = renderer.height;
|
|
||||||
var cameraMatrix = camera.matrix;
|
|
||||||
var gl = this.gl;
|
|
||||||
|
|
||||||
shader.bind();
|
|
||||||
|
|
||||||
gl.uniform2f(this.uResolutionLoc, renderer.width, renderer.height);
|
|
||||||
gl.uniform3f(this.ambientLightColorLoc, this.ambientLightColorR, this.ambientLightColorG, this.ambientLightColorB);
|
|
||||||
gl.uniform4f(this.uCameraLoc, camera.x, camera.y, camera.rotation, camera.zoom);
|
|
||||||
|
|
||||||
for (var index = 0; index < length; ++index)
|
|
||||||
{
|
|
||||||
var light = lights[index];
|
|
||||||
cameraMatrix.transformPoint(light.x, light.y, point);
|
|
||||||
gl.uniform1f(locations[index].attenuation, light.attenuation);
|
|
||||||
gl.uniform1f(locations[index].radius, light.radius);
|
|
||||||
gl.uniform3f(locations[index].position, point.x - (camera.scrollX * light.scrollFactorX * camera.zoom), height - (point.y - (camera.scrollY * light.scrollFactorY) * camera.zoom), light.z);
|
|
||||||
gl.uniform3f(locations[index].color, light.r, light.g, light.b);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = LightLayer;
|
|
|
@ -1,7 +0,0 @@
|
||||||
var LightLayerCanvasRenderer = function (renderer, src, interpolationPercentage, camera)
|
|
||||||
{
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = LightLayerCanvasRenderer;
|
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
var BuildGameObject = require('../BuildGameObject');
|
|
||||||
var GameObjectCreator = require('../GameObjectCreator');
|
|
||||||
var GetAdvancedValue = require('../../utils/object/GetAdvancedValue');
|
|
||||||
var LightLayer = require('./LightLayer');
|
|
||||||
|
|
||||||
// When registering a factory function 'this' refers to the GameObjectCreator context.
|
|
||||||
|
|
||||||
GameObjectCreator.register('lightLayer', function (config)
|
|
||||||
{
|
|
||||||
var pass = new LightLayer(this.scene);
|
|
||||||
|
|
||||||
BuildGameObject(this.scene, pass, config);
|
|
||||||
|
|
||||||
return pass;
|
|
||||||
});
|
|
|
@ -1,18 +0,0 @@
|
||||||
var LightLayer = require('./LightLayer');
|
|
||||||
var GameObjectFactory = require('../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('lightLayer', function ()
|
|
||||||
{
|
|
||||||
return this.displayList.add(new LightLayer(this.scene));
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
var renderWebGL = require('../../utils/NOOP');
|
|
||||||
var renderCanvas = require('../../utils/NOOP');
|
|
||||||
|
|
||||||
if (WEBGL_RENDERER)
|
|
||||||
{
|
|
||||||
renderWebGL = require('./LightLayerWebGLRenderer');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CANVAS_RENDERER)
|
|
||||||
{
|
|
||||||
renderCanvas = require('./LightLayerCanvasRenderer');
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
|
|
||||||
renderWebGL: renderWebGL,
|
|
||||||
renderCanvas: renderCanvas
|
|
||||||
|
|
||||||
};
|
|
|
@ -1 +0,0 @@
|
||||||
module.exports = function () {};
|
|
|
@ -1,21 +0,0 @@
|
||||||
var Class = require('../../utils/Class');
|
|
||||||
|
|
||||||
var SpriteNormalPair = new Class({
|
|
||||||
|
|
||||||
initialize:
|
|
||||||
|
|
||||||
function SpriteNormalPair (sprite, normalTexture)
|
|
||||||
{
|
|
||||||
this.spriteRef = sprite;
|
|
||||||
this.normalTextureRef = normalTexture;
|
|
||||||
},
|
|
||||||
|
|
||||||
set: function (sprite, normalTexture)
|
|
||||||
{
|
|
||||||
this.spriteRef = sprite;
|
|
||||||
this.normalTextureRef = normalTexture;
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = SpriteNormalPair;
|
|
|
@ -118,11 +118,12 @@ var WebGLPipeline = new Class({
|
||||||
{
|
{
|
||||||
var gl = this.gl;
|
var gl = this.gl;
|
||||||
|
|
||||||
gl.deleteShader(this.program);
|
gl.deleteProgram(this.program);
|
||||||
gl.deleteBuffer(this.vertexBuffer);
|
gl.deleteBuffer(this.vertexBuffer);
|
||||||
|
|
||||||
this.program = null;
|
delete this.program;
|
||||||
this.vertexBuffer = null;
|
delete this.vertexBuffer;
|
||||||
|
delete this.gl;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ var WebGLRenderer = new Class({
|
||||||
this.lostContextCallbacks = [];
|
this.lostContextCallbacks = [];
|
||||||
this.restoredContextCallbacks = [];
|
this.restoredContextCallbacks = [];
|
||||||
this.blendModes = [];
|
this.blendModes = [];
|
||||||
|
this.nativeTextures = [];
|
||||||
this.contextLost = false;
|
this.contextLost = false;
|
||||||
this.autoResize = false;
|
this.autoResize = false;
|
||||||
this.pipelines = null;
|
this.pipelines = null;
|
||||||
|
@ -77,6 +78,7 @@ var WebGLRenderer = new Class({
|
||||||
this.canvas.addEventListener('webglcontextlost', function (event) {
|
this.canvas.addEventListener('webglcontextlost', function (event) {
|
||||||
renderer.contextLost = true;
|
renderer.contextLost = true;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
for (var index = 0; index < renderer.lostContextCallbacks.length; ++index)
|
for (var index = 0; index < renderer.lostContextCallbacks.length; ++index)
|
||||||
{
|
{
|
||||||
var callback = renderer.lostContextCallbacks[index];
|
var callback = renderer.lostContextCallbacks[index];
|
||||||
|
@ -498,6 +500,8 @@ var WebGLRenderer = new Class({
|
||||||
texture.width = width;
|
texture.width = width;
|
||||||
texture.height = height;
|
texture.height = height;
|
||||||
|
|
||||||
|
this.nativeTextures.push(texture);
|
||||||
|
|
||||||
return texture;
|
return texture;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -891,10 +895,32 @@ var WebGLRenderer = new Class({
|
||||||
|
|
||||||
destroy: function ()
|
destroy: function ()
|
||||||
{
|
{
|
||||||
// Clear-up anything that should be cleared :)
|
var gl = this.gl;
|
||||||
this.contextLost = true;
|
|
||||||
|
|
||||||
this.game = null;
|
// Clear-up anything that should be cleared :)
|
||||||
|
for (var key in this.pipelines)
|
||||||
|
{
|
||||||
|
this.pipelines[key].destroy();
|
||||||
|
delete this.pipelines[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var index = 0; index < this.nativeTextures.length; ++index)
|
||||||
|
{
|
||||||
|
this.deleteTexture(this.nativeTextures[index]);
|
||||||
|
delete this.nativeTextures[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hasExtension('WEBGL_lose_context'))
|
||||||
|
{
|
||||||
|
this.getExtension('WEBGL_lose_context').loseContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
delete this.gl;
|
||||||
|
delete this.game;
|
||||||
|
|
||||||
|
this.contextLost = true;
|
||||||
|
this.extensions = {};
|
||||||
|
this.nativeTextures.length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue