2018-02-12 16:01:20 +00:00
|
|
|
/**
|
|
|
|
* @author Richard Davey <rich@photonstorm.com>
|
2018-04-05 08:02:36 +00:00
|
|
|
* @author Felipe Alfonso <@bitnenfer>
|
2020-01-15 12:07:09 +00:00
|
|
|
* @copyright 2020 Photon Storm Ltd.
|
2019-05-10 15:15:04 +00:00
|
|
|
* @license {@link https://opensource.org/licenses/MIT|MIT License}
|
2018-02-12 16:01:20 +00:00
|
|
|
*/
|
|
|
|
|
2018-01-26 23:17:11 +00:00
|
|
|
var Class = require('../../../utils/Class');
|
2018-05-08 22:04:57 +00:00
|
|
|
var ShaderSourceFS = require('../shaders/ForwardDiffuse-frag.js');
|
2018-02-09 19:19:21 +00:00
|
|
|
var TextureTintPipeline = require('./TextureTintPipeline');
|
2020-07-16 01:15:53 +00:00
|
|
|
var WebGLPipeline = require('../WebGLPipeline');
|
2018-02-09 19:19:21 +00:00
|
|
|
|
2018-01-30 22:46:43 +00:00
|
|
|
var LIGHT_COUNT = 10;
|
2018-01-26 23:17:11 +00:00
|
|
|
|
2018-02-09 19:19:21 +00:00
|
|
|
/**
|
|
|
|
* @classdesc
|
2018-04-24 19:52:57 +00:00
|
|
|
* ForwardDiffuseLightPipeline implements a forward rendering approach for 2D lights.
|
|
|
|
* This pipeline extends TextureTintPipeline so it implements all it's rendering functions
|
|
|
|
* and batching system.
|
2018-02-09 19:19:21 +00:00
|
|
|
*
|
|
|
|
* @class ForwardDiffuseLightPipeline
|
2018-04-18 11:13:49 +00:00
|
|
|
* @extends Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline
|
2018-10-10 09:49:13 +00:00
|
|
|
* @memberof Phaser.Renderer.WebGL.Pipelines
|
2018-02-09 19:19:21 +00:00
|
|
|
* @constructor
|
|
|
|
* @since 3.0.0
|
|
|
|
*
|
2018-10-19 11:32:43 +00:00
|
|
|
* @param {object} config - The configuration of the pipeline, same as the {@link Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline}. The fragment shader will be replaced with the lighting shader.
|
2018-02-09 19:19:21 +00:00
|
|
|
*/
|
2018-01-26 23:17:11 +00:00
|
|
|
var ForwardDiffuseLightPipeline = new Class({
|
|
|
|
|
|
|
|
Extends: TextureTintPipeline,
|
|
|
|
|
|
|
|
initialize:
|
|
|
|
|
2018-03-05 14:29:48 +00:00
|
|
|
function ForwardDiffuseLightPipeline (config)
|
2018-01-26 23:17:11 +00:00
|
|
|
{
|
2018-10-02 10:09:58 +00:00
|
|
|
LIGHT_COUNT = config.maxLights;
|
|
|
|
|
2018-03-05 14:29:48 +00:00
|
|
|
config.fragShader = ShaderSourceFS.replace('%LIGHT_COUNT%', LIGHT_COUNT.toString());
|
|
|
|
|
|
|
|
TextureTintPipeline.call(this, config);
|
2018-07-13 10:14:22 +00:00
|
|
|
|
2018-10-31 00:03:34 +00:00
|
|
|
/**
|
2018-12-12 11:58:58 +00:00
|
|
|
* Inverse rotation matrix for normal map rotations.
|
|
|
|
*
|
|
|
|
* @name Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#inverseRotationMatrix
|
|
|
|
* @type {Float32Array}
|
|
|
|
* @private
|
|
|
|
* @since 3.16.0
|
2018-10-31 00:03:34 +00:00
|
|
|
*/
|
|
|
|
this.inverseRotationMatrix = new Float32Array([
|
|
|
|
1, 0, 0,
|
|
|
|
0, 1, 0,
|
|
|
|
0, 0, 1
|
|
|
|
]);
|
2020-07-16 02:24:19 +00:00
|
|
|
|
2020-07-16 10:04:10 +00:00
|
|
|
/**
|
|
|
|
* Stores a default normal map, which is an object with a `glTexture` property that
|
|
|
|
* maps to a 1x1 texture of the color #7f7fff created in the `boot` method.
|
|
|
|
*
|
|
|
|
* @name Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#defaultNormalMap
|
|
|
|
* @type {object}
|
|
|
|
* @since 3.25.0
|
|
|
|
*/
|
|
|
|
this.defaultNormalMap;
|
|
|
|
|
2020-07-16 02:24:19 +00:00
|
|
|
/**
|
|
|
|
* Stores the previous number of lights rendered.
|
|
|
|
*
|
|
|
|
* @name Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#lightCount
|
|
|
|
* @type {number}
|
|
|
|
* @since 3.25.0
|
|
|
|
*/
|
|
|
|
this.lightCount = 0;
|
2018-07-13 10:14:22 +00:00
|
|
|
},
|
|
|
|
|
2020-07-16 10:04:10 +00:00
|
|
|
/**
|
|
|
|
* Called when the Game has fully booted and the Renderer has finished setting up.
|
|
|
|
*
|
|
|
|
* By this stage all Game level systems are now in place and you can perform any final
|
|
|
|
* tasks that the pipeline may need that relied on game systems such as the Texture Manager.
|
|
|
|
*
|
|
|
|
* @method Phaser.Renderer.WebGL.ForwardDiffuseLightPipeline#boot
|
|
|
|
* @since 3.11.0
|
|
|
|
*/
|
|
|
|
boot: function ()
|
|
|
|
{
|
|
|
|
WebGLPipeline.prototype.boot.call(this);
|
|
|
|
|
|
|
|
var gl = this.gl;
|
|
|
|
|
|
|
|
var tempTexture = gl.createTexture();
|
|
|
|
|
|
|
|
gl.activeTexture(gl.TEXTURE0);
|
|
|
|
|
|
|
|
gl.bindTexture(gl.TEXTURE_2D, tempTexture);
|
|
|
|
|
|
|
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([ 127, 127, 255, 255 ]));
|
|
|
|
|
|
|
|
this.defaultNormalMap = { glTexture: tempTexture };
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2018-07-13 10:14:22 +00:00
|
|
|
/**
|
2020-07-16 01:15:53 +00:00
|
|
|
* Called every time the pipeline is bound by the renderer.
|
|
|
|
* Sets the shader program, vertex buffer and other resources.
|
|
|
|
* Should only be called when changing pipeline.
|
2018-02-09 19:19:21 +00:00
|
|
|
*
|
2020-07-16 01:15:53 +00:00
|
|
|
* @method Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#bind
|
|
|
|
* @since 3.25.0
|
2018-02-09 19:19:21 +00:00
|
|
|
*
|
2018-09-25 10:36:36 +00:00
|
|
|
* @return {this} This WebGLPipeline instance.
|
2018-02-09 19:19:21 +00:00
|
|
|
*/
|
2020-07-16 01:15:53 +00:00
|
|
|
bind: function ()
|
2018-01-26 23:17:11 +00:00
|
|
|
{
|
2020-07-16 01:15:53 +00:00
|
|
|
WebGLPipeline.prototype.bind.call(this);
|
2018-01-26 23:17:11 +00:00
|
|
|
|
|
|
|
var renderer = this.renderer;
|
2018-01-30 03:38:31 +00:00
|
|
|
var program = this.program;
|
2018-01-26 23:17:11 +00:00
|
|
|
|
2020-07-16 01:15:53 +00:00
|
|
|
renderer.setInt1(program, 'uMainSampler', 0);
|
2018-01-30 03:38:31 +00:00
|
|
|
renderer.setInt1(program, 'uNormSampler', 1);
|
2018-01-26 23:17:11 +00:00
|
|
|
renderer.setFloat2(program, 'uResolution', this.width, this.height);
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2018-02-09 19:19:21 +00:00
|
|
|
/**
|
2018-04-24 19:52:57 +00:00
|
|
|
* This function sets all the needed resources for each camera pass.
|
2018-02-09 19:19:21 +00:00
|
|
|
*
|
2018-04-18 11:13:49 +00:00
|
|
|
* @method Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#onRender
|
2018-02-09 19:19:21 +00:00
|
|
|
* @since 3.0.0
|
|
|
|
*
|
2018-10-19 11:32:43 +00:00
|
|
|
* @param {Phaser.Scene} scene - The Scene being rendered.
|
|
|
|
* @param {Phaser.Cameras.Scene2D.Camera} camera - The Scene Camera being rendered with.
|
2018-02-09 19:19:21 +00:00
|
|
|
*
|
2018-09-25 10:36:36 +00:00
|
|
|
* @return {this} This WebGLPipeline instance.
|
2018-02-09 19:19:21 +00:00
|
|
|
*/
|
2018-01-29 21:46:48 +00:00
|
|
|
onRender: function (scene, camera)
|
|
|
|
{
|
2018-05-31 15:57:21 +00:00
|
|
|
this.active = false;
|
|
|
|
|
2018-03-12 12:55:09 +00:00
|
|
|
var lightManager = scene.sys.lights;
|
2018-01-31 01:11:51 +00:00
|
|
|
|
2018-05-31 15:57:21 +00:00
|
|
|
if (!lightManager || lightManager.lights.length <= 0 || !lightManager.active)
|
2018-03-12 02:22:42 +00:00
|
|
|
{
|
2018-05-31 15:57:21 +00:00
|
|
|
// Passthru
|
2018-03-12 02:22:42 +00:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2018-05-31 15:57:21 +00:00
|
|
|
var lights = lightManager.cull(camera);
|
|
|
|
var lightCount = Math.min(lights.length, LIGHT_COUNT);
|
2018-01-31 01:11:51 +00:00
|
|
|
|
2018-05-31 15:57:21 +00:00
|
|
|
if (lightCount === 0)
|
2018-01-31 01:11:51 +00:00
|
|
|
{
|
2018-05-31 15:57:21 +00:00
|
|
|
return this;
|
2018-01-31 01:11:51 +00:00
|
|
|
}
|
|
|
|
|
2018-05-31 15:57:21 +00:00
|
|
|
this.active = true;
|
|
|
|
|
2018-01-30 03:38:31 +00:00
|
|
|
var renderer = this.renderer;
|
|
|
|
var program = this.program;
|
2018-01-30 22:46:43 +00:00
|
|
|
var cameraMatrix = camera.matrix;
|
|
|
|
var point = {x: 0, y: 0};
|
|
|
|
var height = renderer.height;
|
2020-07-16 02:24:19 +00:00
|
|
|
var i;
|
|
|
|
|
|
|
|
if (lightCount !== this.lightCount)
|
|
|
|
{
|
|
|
|
for (i = 0; i < LIGHT_COUNT; i++)
|
|
|
|
{
|
|
|
|
// Reset lights
|
|
|
|
renderer.setFloat1(program, 'uLights[' + i + '].radius', 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.lightCount = lightCount;
|
|
|
|
}
|
2018-01-30 22:46:43 +00:00
|
|
|
|
2020-07-16 02:24:19 +00:00
|
|
|
if (camera.dirty)
|
2018-01-31 01:11:51 +00:00
|
|
|
{
|
2020-07-16 02:24:19 +00:00
|
|
|
renderer.setFloat4(program, 'uCamera', camera.x, camera.y, camera.rotation, camera.zoom);
|
2018-01-31 01:11:51 +00:00
|
|
|
}
|
|
|
|
|
2020-07-16 02:24:19 +00:00
|
|
|
// TODO - Only if dirty! and cache the location
|
2018-01-30 22:46:43 +00:00
|
|
|
renderer.setFloat3(program, 'uAmbientLightColor', lightManager.ambientColor.r, lightManager.ambientColor.g, lightManager.ambientColor.b);
|
|
|
|
|
2020-07-16 02:24:19 +00:00
|
|
|
for (i = 0; i < lightCount; i++)
|
2018-01-30 22:46:43 +00:00
|
|
|
{
|
2020-07-16 02:24:19 +00:00
|
|
|
var light = lights[i];
|
2020-07-16 14:18:02 +00:00
|
|
|
var lightName = 'uLights[' + i + '].';
|
2020-07-16 02:24:19 +00:00
|
|
|
|
2020-07-16 14:18:02 +00:00
|
|
|
cameraMatrix.transformPoint(light.x, light.y, point);
|
2020-07-16 02:24:19 +00:00
|
|
|
|
2020-07-16 14:18:02 +00:00
|
|
|
// TODO - Cache the uniform locations!!!
|
|
|
|
renderer.setFloat2(program, lightName + 'position', point.x - (camera.scrollX * light.scrollFactorX * camera.zoom), height - (point.y - (camera.scrollY * light.scrollFactorY) * camera.zoom));
|
2018-05-31 15:57:21 +00:00
|
|
|
|
2020-07-16 14:18:02 +00:00
|
|
|
if (light.dirty)
|
|
|
|
{
|
2020-07-16 02:24:19 +00:00
|
|
|
renderer.setFloat3(program, lightName + 'color', light.r, light.g, light.b);
|
|
|
|
renderer.setFloat1(program, lightName + 'intensity', light.intensity);
|
|
|
|
renderer.setFloat1(program, lightName + 'radius', light.radius);
|
|
|
|
light.dirty = false;
|
|
|
|
}
|
2018-01-30 22:46:43 +00:00
|
|
|
}
|
2019-10-06 18:31:29 +00:00
|
|
|
|
|
|
|
this.currentNormalMapRotation = null;
|
|
|
|
|
2018-01-29 21:46:48 +00:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2018-07-13 10:14:22 +00:00
|
|
|
/**
|
2020-07-16 01:15:53 +00:00
|
|
|
* Rotates the normal map vectors inversely by the given angle.
|
|
|
|
* Only works in 2D space.
|
|
|
|
*
|
|
|
|
* @method Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#setNormalMapRotation
|
|
|
|
* @since 3.16.0
|
|
|
|
*
|
|
|
|
* @param {number} rotation - The angle of rotation in radians.
|
|
|
|
*/
|
|
|
|
setNormalMapRotation: function (rotation)
|
|
|
|
{
|
|
|
|
if (rotation !== this.currentNormalMapRotation || this.vertexCount === 0)
|
|
|
|
{
|
|
|
|
if (this.vertexCount > 0)
|
|
|
|
{
|
|
|
|
this.flush();
|
|
|
|
}
|
|
|
|
|
|
|
|
var inverseRotationMatrix = this.inverseRotationMatrix;
|
|
|
|
|
|
|
|
if (rotation)
|
|
|
|
{
|
|
|
|
var rot = -rotation;
|
|
|
|
var c = Math.cos(rot);
|
|
|
|
var s = Math.sin(rot);
|
|
|
|
|
|
|
|
inverseRotationMatrix[1] = s;
|
|
|
|
inverseRotationMatrix[3] = -s;
|
|
|
|
inverseRotationMatrix[0] = inverseRotationMatrix[4] = c;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
inverseRotationMatrix[0] = inverseRotationMatrix[4] = 1;
|
|
|
|
inverseRotationMatrix[1] = inverseRotationMatrix[3] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.renderer.setMatrix3(this.program, 'uInverseRotationMatrix', false, inverseRotationMatrix);
|
|
|
|
|
|
|
|
this.currentNormalMapRotation = rotation;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-07-16 14:18:02 +00:00
|
|
|
/**
|
|
|
|
* Assigns a texture to the current batch. If a different texture is already set it creates a new batch object.
|
|
|
|
*
|
|
|
|
* @method Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#setTexture2D
|
|
|
|
* @since 3.25.0
|
|
|
|
*
|
|
|
|
* @param {WebGLTexture} [texture] - WebGLTexture that will be assigned to the current batch. If not given uses blankTexture.
|
|
|
|
* @param {Phaser.GameObjects.GameObject} [gameObject] - The Game Object being rendered or added to the batch.
|
|
|
|
*/
|
|
|
|
setTexture2D: function (texture, gameObject)
|
|
|
|
{
|
|
|
|
var renderer = this.renderer;
|
2020-07-16 14:51:12 +00:00
|
|
|
|
|
|
|
if (texture === undefined) { texture = renderer.tempTextures[0]; }
|
|
|
|
|
2020-07-16 14:18:02 +00:00
|
|
|
var normalTexture = this.getNormalMap(gameObject);
|
|
|
|
|
|
|
|
if (renderer.isNewNormalMap())
|
2018-07-13 10:14:22 +00:00
|
|
|
{
|
2020-07-16 14:18:02 +00:00
|
|
|
this.flush();
|
|
|
|
|
|
|
|
renderer.setTextureZero(texture);
|
|
|
|
renderer.setNormalMap(normalTexture);
|
2018-07-13 10:14:22 +00:00
|
|
|
}
|
2020-07-16 14:18:02 +00:00
|
|
|
|
|
|
|
if (gameObject)
|
2018-07-13 10:14:22 +00:00
|
|
|
{
|
2020-07-16 14:18:02 +00:00
|
|
|
this.setNormalMapRotation(gameObject.rotation);
|
2018-07-13 10:14:22 +00:00
|
|
|
}
|
|
|
|
|
2020-07-16 14:18:02 +00:00
|
|
|
this.currentUnit = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Custom pipelines can use this method in order to perform any required pre-batch tasks
|
|
|
|
* for the given Game Object. It must return the texture unit the Game Object was assigned.
|
|
|
|
*
|
|
|
|
* @method Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#setGameObject
|
|
|
|
* @since 3.25.0
|
|
|
|
*
|
|
|
|
* @param {Phaser.GameObjects.GameObject} gameObject - The Game Object being rendered or added to the batch.
|
|
|
|
* @param {Phaser.Textures.Frame} [frame] - Optional frame to use. Can override that of the Game Object.
|
|
|
|
*
|
|
|
|
* @return {number} The texture unit the Game Object has been assigned.
|
|
|
|
*/
|
|
|
|
setGameObject: function (gameObject, frame)
|
|
|
|
{
|
|
|
|
if (frame === undefined) { frame = gameObject.frame; }
|
|
|
|
|
|
|
|
var renderer = this.renderer;
|
|
|
|
var texture = frame.glTexture;
|
|
|
|
var normalTexture = this.getNormalMap(gameObject);
|
|
|
|
|
|
|
|
if (renderer.isNewNormalMap())
|
2018-10-31 00:03:34 +00:00
|
|
|
{
|
2020-07-16 14:18:02 +00:00
|
|
|
this.flush();
|
|
|
|
|
|
|
|
renderer.setTextureZero(texture);
|
|
|
|
renderer.setNormalMap(normalTexture);
|
2020-07-16 10:04:10 +00:00
|
|
|
}
|
2018-10-31 00:03:34 +00:00
|
|
|
|
2020-07-16 14:18:02 +00:00
|
|
|
this.setNormalMapRotation(gameObject.rotation);
|
2018-10-31 00:03:34 +00:00
|
|
|
|
2020-07-16 14:18:02 +00:00
|
|
|
this.currentUnit = 0;
|
2020-07-16 10:04:10 +00:00
|
|
|
|
2020-07-16 14:18:02 +00:00
|
|
|
return 0;
|
2018-10-31 00:03:34 +00:00
|
|
|
},
|
|
|
|
|
2018-02-09 19:19:21 +00:00
|
|
|
/**
|
2020-07-16 14:18:02 +00:00
|
|
|
* Returns the normal map WebGLTexture from the given Game Object.
|
|
|
|
* If the Game Object doesn't have one, it returns the default normal map from this pipeline instead.
|
2018-02-09 19:19:21 +00:00
|
|
|
*
|
2020-07-16 14:18:02 +00:00
|
|
|
* @method Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#getNormalMap
|
|
|
|
* @since 3.25.0
|
2018-02-09 19:19:21 +00:00
|
|
|
*
|
2020-07-16 14:18:02 +00:00
|
|
|
* @param {Phaser.GameObjects.GameObject} [gameObject] - The Game Object to get the normal map from.
|
|
|
|
*
|
|
|
|
* @return {WebGLTexture} The normal map texture.
|
2018-07-11 15:55:18 +00:00
|
|
|
*/
|
2020-07-16 14:18:02 +00:00
|
|
|
getNormalMap: function (gameObject)
|
2018-01-30 03:38:31 +00:00
|
|
|
{
|
2020-07-16 14:18:02 +00:00
|
|
|
var normalTexture;
|
|
|
|
|
|
|
|
if (!gameObject)
|
2018-05-31 15:57:21 +00:00
|
|
|
{
|
2020-07-16 14:18:02 +00:00
|
|
|
normalTexture = this.defaultNormalMap;
|
|
|
|
}
|
|
|
|
else if (gameObject.displayTexture)
|
|
|
|
{
|
|
|
|
normalTexture = gameObject.displayTexture.dataSource[gameObject.displayFrame.sourceIndex];
|
|
|
|
}
|
|
|
|
else if (gameObject.texture)
|
|
|
|
{
|
|
|
|
normalTexture = gameObject.texture.dataSource[gameObject.frame.sourceIndex];
|
|
|
|
}
|
|
|
|
else if (gameObject.tileset)
|
|
|
|
{
|
|
|
|
if (Array.isArray(gameObject.tileset))
|
|
|
|
{
|
|
|
|
normalTexture = gameObject.tileset[0].image.dataSource[0];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
normalTexture = gameObject.tileset.image.dataSource[0];
|
|
|
|
}
|
2018-05-31 15:57:21 +00:00
|
|
|
}
|
2018-01-30 03:38:31 +00:00
|
|
|
|
2020-07-16 10:04:10 +00:00
|
|
|
if (!normalTexture)
|
2018-01-30 03:38:31 +00:00
|
|
|
{
|
2020-07-16 10:04:10 +00:00
|
|
|
normalTexture = this.defaultNormalMap;
|
|
|
|
}
|
2018-05-31 15:57:21 +00:00
|
|
|
|
2020-07-16 14:18:02 +00:00
|
|
|
return normalTexture.glTexture;
|
2018-01-26 23:17:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
2018-01-31 01:11:51 +00:00
|
|
|
ForwardDiffuseLightPipeline.LIGHT_COUNT = LIGHT_COUNT;
|
|
|
|
|
2018-01-26 23:17:11 +00:00
|
|
|
module.exports = ForwardDiffuseLightPipeline;
|