mirror of
https://github.com/photonstorm/phaser
synced 2025-01-12 05:08:54 +00:00
568 lines
19 KiB
JavaScript
568 lines
19 KiB
JavaScript
/**
|
|
* @author Richard Davey <rich@photonstorm.com>
|
|
* @author Felipe Alfonso <@bitnenfer>
|
|
* @copyright 2013-2023 Photon Storm Ltd.
|
|
* @license {@link https://opensource.org/licenses/MIT|MIT License}
|
|
*/
|
|
|
|
var Class = require('../../../utils/Class');
|
|
var GetFastValue = require('../../../utils/object/GetFastValue');
|
|
var LightShaderSourceFS = require('../shaders/Light-frag.js');
|
|
var MultiPipeline = require('./MultiPipeline');
|
|
var TransformMatrix = require('../../../gameobjects/components/TransformMatrix');
|
|
var Vec2 = require('../../../math/Vector2');
|
|
var WebGLPipeline = require('../WebGLPipeline');
|
|
|
|
/**
|
|
* @classdesc
|
|
* The Light Pipeline is an extension of the Multi Pipeline and uses a custom shader
|
|
* designed to handle forward diffused rendering of 2D lights in a Scene.
|
|
*
|
|
* The shader works in tandem with Light Game Objects, and optionally texture normal maps,
|
|
* to provide an ambient illumination effect.
|
|
*
|
|
* If you wish to provide your own shader, you can use the `%LIGHT_COUNT%` declaration in the source,
|
|
* and it will be automatically replaced at run-time with the total number of configured lights.
|
|
*
|
|
* The maximum number of lights can be set in the Render Config `maxLights` property and defaults to 10.
|
|
*
|
|
* Prior to Phaser v3.50 this pipeline was called the `ForwardDiffuseLightPipeline`.
|
|
*
|
|
* The fragment shader it uses can be found in `shaders/src/Light.frag`.
|
|
* The vertex shader it uses can be found in `shaders/src/Multi.vert`.
|
|
*
|
|
* The default shader attributes for this pipeline are:
|
|
*
|
|
* `inPosition` (vec2, offset 0)
|
|
* `inTexCoord` (vec2, offset 8)
|
|
* `inTexId` (float, offset 16)
|
|
* `inTintEffect` (float, offset 20)
|
|
* `inTint` (vec4, offset 24, normalized)
|
|
*
|
|
* The default shader uniforms for this pipeline are those from the Multi Pipeline, plus:
|
|
*
|
|
* `uMainSampler` (sampler2D)
|
|
* `uNormSampler` (sampler2D)
|
|
* `uCamera` (vec4)
|
|
* `uResolution` (vec2)
|
|
* `uAmbientLightColor` (vec3)
|
|
* `uInverseRotationMatrix` (mat3)
|
|
* `uLights` (Light struct)
|
|
*
|
|
* @class LightPipeline
|
|
* @extends Phaser.Renderer.WebGL.Pipelines.MultiPipeline
|
|
* @memberof Phaser.Renderer.WebGL.Pipelines
|
|
* @constructor
|
|
* @since 3.50.0
|
|
*
|
|
* @param {Phaser.Types.Renderer.WebGL.WebGLPipelineConfig} config - The configuration options for this pipeline.
|
|
*/
|
|
var LightPipeline = new Class({
|
|
|
|
Extends: MultiPipeline,
|
|
|
|
initialize:
|
|
|
|
function LightPipeline (config)
|
|
{
|
|
var fragShader = GetFastValue(config, 'fragShader', LightShaderSourceFS);
|
|
|
|
config.fragShader = fragShader.replace('%LIGHT_COUNT%', config.game.renderer.config.maxLights);
|
|
|
|
MultiPipeline.call(this, config);
|
|
|
|
/**
|
|
* Inverse rotation matrix for normal map rotations.
|
|
*
|
|
* @name Phaser.Renderer.WebGL.Pipelines.LightPipeline#inverseRotationMatrix
|
|
* @type {Float32Array}
|
|
* @private
|
|
* @since 3.16.0
|
|
*/
|
|
this.inverseRotationMatrix = new Float32Array([
|
|
1, 0, 0,
|
|
0, 1, 0,
|
|
0, 0, 1
|
|
]);
|
|
|
|
/**
|
|
* 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.LightPipeline#defaultNormalMap
|
|
* @type {object}
|
|
* @since 3.50.0
|
|
*/
|
|
this.defaultNormalMap;
|
|
|
|
/**
|
|
* The currently bound normal map texture at texture unit one, if any.
|
|
*
|
|
* @name Phaser.Renderer.WebGL.WebGLRenderer#currentNormalMap;
|
|
* @type {?WebGLTexture}
|
|
* @since 3.60.0
|
|
*/
|
|
this.currentNormalMap;
|
|
|
|
/**
|
|
* A boolean that is set automatically during `onRender` that determines
|
|
* if the Scene LightManager is active, or not.
|
|
*
|
|
* @name Phaser.Renderer.WebGL.Pipelines.LightPipeline#lightsActive
|
|
* @type {boolean}
|
|
* @readonly
|
|
* @since 3.53.0
|
|
*/
|
|
this.lightsActive = true;
|
|
|
|
/**
|
|
* A persistent calculation vector used when processing the lights.
|
|
*
|
|
* @name Phaser.Renderer.WebGL.Pipelines.LightPipeline#tempVec2
|
|
* @type {Phaser.Math.Vector2}
|
|
* @since 3.60.0
|
|
*/
|
|
this.tempVec2 = new Vec2();
|
|
|
|
/**
|
|
* A temporary Transform Matrix used for parent Container calculations without them needing their own local copy.
|
|
*
|
|
* @name Phaser.Renderer.WebGL.Pipelines.LightPipeline#_tempMatrix
|
|
* @type {Phaser.GameObjects.Components.TransformMatrix}
|
|
* @private
|
|
* @since 3.60.0
|
|
*/
|
|
this._tempMatrix = new TransformMatrix();
|
|
|
|
/**
|
|
* A temporary Transform Matrix used for parent Container calculations without them needing their own local copy.
|
|
*
|
|
* @name Phaser.Renderer.WebGL.Pipelines.LightPipeline#_tempMatrix2
|
|
* @type {Phaser.GameObjects.Components.TransformMatrix}
|
|
* @private
|
|
* @since 3.60.0
|
|
*/
|
|
this._tempMatrix2 = new TransformMatrix();
|
|
},
|
|
|
|
/**
|
|
* 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.Pipelines.LightPipeline#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 };
|
|
},
|
|
|
|
/**
|
|
* This function sets all the needed resources for each camera pass.
|
|
*
|
|
* @method Phaser.Renderer.WebGL.Pipelines.LightPipeline#onRender
|
|
* @ignore
|
|
* @since 3.0.0
|
|
*
|
|
* @param {Phaser.Scene} scene - The Scene being rendered.
|
|
* @param {Phaser.Cameras.Scene2D.Camera} camera - The Scene Camera being rendered with.
|
|
*/
|
|
onRender: function (scene, camera)
|
|
{
|
|
var lightManager = scene.sys.lights;
|
|
|
|
this.lightsActive = false;
|
|
|
|
if (!lightManager || !lightManager.active)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var lights = lightManager.getLights(camera);
|
|
var lightsCount = lights.length;
|
|
|
|
// Ok, we're good to go ...
|
|
|
|
this.lightsActive = true;
|
|
|
|
var i;
|
|
var renderer = this.renderer;
|
|
var height = renderer.height;
|
|
var cameraMatrix = camera.matrix;
|
|
var tempVec2 = this.tempVec2;
|
|
|
|
this.set1i('uMainSampler', 0);
|
|
this.set1i('uNormSampler', 1);
|
|
this.set2f('uResolution', this.width / 2, this.height / 2);
|
|
this.set4f('uCamera', camera.x, camera.y, camera.rotation, camera.zoom);
|
|
this.set3f('uAmbientLightColor', lightManager.ambientColor.r, lightManager.ambientColor.g, lightManager.ambientColor.b);
|
|
this.set1i('uLightCount', lightsCount);
|
|
|
|
for (i = 0; i < lightsCount; i++)
|
|
{
|
|
var light = lights[i].light;
|
|
var color = light.color;
|
|
|
|
var lightName = 'uLights[' + i + '].';
|
|
|
|
cameraMatrix.transformPoint(light.x, light.y, tempVec2);
|
|
|
|
this.set2f(lightName + 'position', tempVec2.x - (camera.scrollX * light.scrollFactorX * camera.zoom), height - (tempVec2.y - (camera.scrollY * light.scrollFactorY) * camera.zoom));
|
|
this.set3f(lightName + 'color', color.r, color.g, color.b);
|
|
this.set1f(lightName + 'intensity', light.intensity);
|
|
this.set1f(lightName + 'radius', light.radius);
|
|
}
|
|
|
|
this.currentNormalMapRotation = null;
|
|
},
|
|
|
|
/**
|
|
* Rotates the normal map vectors inversely by the given angle.
|
|
* Only works in 2D space.
|
|
*
|
|
* @method Phaser.Renderer.WebGL.Pipelines.LightPipeline#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.setMatrix3fv('uInverseRotationMatrix', false, inverseRotationMatrix);
|
|
|
|
this.currentNormalMapRotation = rotation;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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.LightPipeline#setTexture2D
|
|
* @ignore
|
|
* @since 3.50.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;
|
|
|
|
if (texture === undefined) { texture = renderer.whiteTexture; }
|
|
|
|
var normalMap = this.getNormalMap(gameObject);
|
|
|
|
if (this.isNewNormalMap(texture, normalMap))
|
|
{
|
|
this.flush();
|
|
|
|
this.createBatch(texture);
|
|
|
|
this.addTextureToBatch(normalMap);
|
|
|
|
this.currentNormalMap = normalMap;
|
|
}
|
|
|
|
var rotation = 0;
|
|
|
|
if (gameObject && gameObject.parentContainer)
|
|
{
|
|
var matrix = gameObject.getWorldTransformMatrix(this._tempMatrix, this._tempMatrix2);
|
|
|
|
rotation = matrix.rotationNormalized;
|
|
}
|
|
else if (gameObject)
|
|
{
|
|
rotation = gameObject.rotation;
|
|
}
|
|
|
|
this.setNormalMapRotation(rotation);
|
|
|
|
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.LightPipeline#setGameObject
|
|
* @ignore
|
|
* @since 3.50.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 texture = frame.glTexture;
|
|
var normalMap = this.getNormalMap(gameObject);
|
|
|
|
if (this.isNewNormalMap(texture, normalMap))
|
|
{
|
|
this.flush();
|
|
|
|
this.createBatch(texture);
|
|
|
|
this.addTextureToBatch(normalMap);
|
|
|
|
this.currentNormalMap = normalMap;
|
|
}
|
|
|
|
if (gameObject.parentContainer)
|
|
{
|
|
var matrix = gameObject.getWorldTransformMatrix(this._tempMatrix, this._tempMatrix2);
|
|
|
|
this.setNormalMapRotation(matrix.rotationNormalized);
|
|
}
|
|
else
|
|
{
|
|
this.setNormalMapRotation(gameObject.rotation);
|
|
}
|
|
|
|
return 0;
|
|
},
|
|
|
|
/**
|
|
* Checks to see if the given diffuse and normal map textures are already bound, or not.
|
|
*
|
|
* @method Phaser.Renderer.WebGL.WebGLRenderer#isNewNormalMap
|
|
* @since 3.50.0
|
|
*
|
|
* @param {WebGLTexture} texture - The WebGL diffuse texture.
|
|
* @param {WebGLTexture} normalMap - The WebGL normal map texture.
|
|
*
|
|
* @return {boolean} Returns `false` if this combination is already set, or `true` if it's a new combination.
|
|
*/
|
|
isNewNormalMap: function (texture, normalMap)
|
|
{
|
|
return (this.currentTexture !== texture || this.currentNormalMap !== normalMap);
|
|
},
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* @method Phaser.Renderer.WebGL.Pipelines.LightPipeline#getNormalMap
|
|
* @since 3.50.0
|
|
*
|
|
* @param {Phaser.GameObjects.GameObject} [gameObject] - The Game Object to get the normal map from.
|
|
*
|
|
* @return {WebGLTexture} The normal map texture.
|
|
*/
|
|
getNormalMap: function (gameObject)
|
|
{
|
|
var normalMap;
|
|
|
|
if (!gameObject)
|
|
{
|
|
normalMap = this.defaultNormalMap;
|
|
}
|
|
else if (gameObject.displayTexture)
|
|
{
|
|
normalMap = gameObject.displayTexture.dataSource[gameObject.displayFrame.sourceIndex];
|
|
}
|
|
else if (gameObject.texture)
|
|
{
|
|
normalMap = gameObject.texture.dataSource[gameObject.frame.sourceIndex];
|
|
}
|
|
else if (gameObject.tileset)
|
|
{
|
|
if (Array.isArray(gameObject.tileset))
|
|
{
|
|
normalMap = gameObject.tileset[0].image.dataSource[0];
|
|
}
|
|
else
|
|
{
|
|
normalMap = gameObject.tileset.image.dataSource[0];
|
|
}
|
|
}
|
|
|
|
if (!normalMap)
|
|
{
|
|
normalMap = this.defaultNormalMap;
|
|
}
|
|
|
|
return normalMap.glTexture;
|
|
},
|
|
|
|
/**
|
|
* Takes a Sprite Game Object, or any object that extends it, and adds it to the batch.
|
|
*
|
|
* @method Phaser.Renderer.WebGL.Pipelines.LightPipeline#batchSprite
|
|
* @since 3.50.0
|
|
*
|
|
* @param {(Phaser.GameObjects.Image|Phaser.GameObjects.Sprite)} gameObject - The texture based Game Object to add to the batch.
|
|
* @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera to use for the rendering transform.
|
|
* @param {Phaser.GameObjects.Components.TransformMatrix} [parentTransformMatrix] - The transform matrix of the parent container, if set.
|
|
*/
|
|
batchSprite: function (gameObject, camera, parentTransformMatrix)
|
|
{
|
|
if (this.lightsActive)
|
|
{
|
|
MultiPipeline.prototype.batchSprite.call(this, gameObject, camera, parentTransformMatrix);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Generic function for batching a textured quad using argument values instead of a Game Object.
|
|
*
|
|
* @method Phaser.Renderer.WebGL.Pipelines.LightPipeline#batchTexture
|
|
* @since 3.50.0
|
|
*
|
|
* @param {Phaser.GameObjects.GameObject} gameObject - Source GameObject.
|
|
* @param {WebGLTexture} texture - Raw WebGLTexture associated with the quad.
|
|
* @param {number} textureWidth - Real texture width.
|
|
* @param {number} textureHeight - Real texture height.
|
|
* @param {number} srcX - X coordinate of the quad.
|
|
* @param {number} srcY - Y coordinate of the quad.
|
|
* @param {number} srcWidth - Width of the quad.
|
|
* @param {number} srcHeight - Height of the quad.
|
|
* @param {number} scaleX - X component of scale.
|
|
* @param {number} scaleY - Y component of scale.
|
|
* @param {number} rotation - Rotation of the quad.
|
|
* @param {boolean} flipX - Indicates if the quad is horizontally flipped.
|
|
* @param {boolean} flipY - Indicates if the quad is vertically flipped.
|
|
* @param {number} scrollFactorX - By which factor is the quad affected by the camera horizontal scroll.
|
|
* @param {number} scrollFactorY - By which factor is the quad effected by the camera vertical scroll.
|
|
* @param {number} displayOriginX - Horizontal origin in pixels.
|
|
* @param {number} displayOriginY - Vertical origin in pixels.
|
|
* @param {number} frameX - X coordinate of the texture frame.
|
|
* @param {number} frameY - Y coordinate of the texture frame.
|
|
* @param {number} frameWidth - Width of the texture frame.
|
|
* @param {number} frameHeight - Height of the texture frame.
|
|
* @param {number} tintTL - Tint for top left.
|
|
* @param {number} tintTR - Tint for top right.
|
|
* @param {number} tintBL - Tint for bottom left.
|
|
* @param {number} tintBR - Tint for bottom right.
|
|
* @param {number} tintEffect - The tint effect.
|
|
* @param {number} uOffset - Horizontal offset on texture coordinate.
|
|
* @param {number} vOffset - Vertical offset on texture coordinate.
|
|
* @param {Phaser.Cameras.Scene2D.Camera} camera - Current used camera.
|
|
* @param {Phaser.GameObjects.Components.TransformMatrix} parentTransformMatrix - Parent container.
|
|
* @param {boolean} [skipFlip=false] - Skip the renderTexture check.
|
|
* @param {number} [textureUnit] - Use the currently bound texture unit?
|
|
*/
|
|
batchTexture: function (
|
|
gameObject,
|
|
texture,
|
|
textureWidth, textureHeight,
|
|
srcX, srcY,
|
|
srcWidth, srcHeight,
|
|
scaleX, scaleY,
|
|
rotation,
|
|
flipX, flipY,
|
|
scrollFactorX, scrollFactorY,
|
|
displayOriginX, displayOriginY,
|
|
frameX, frameY, frameWidth, frameHeight,
|
|
tintTL, tintTR, tintBL, tintBR, tintEffect,
|
|
uOffset, vOffset,
|
|
camera,
|
|
parentTransformMatrix,
|
|
skipFlip,
|
|
textureUnit)
|
|
{
|
|
if (this.lightsActive)
|
|
{
|
|
MultiPipeline.prototype.batchTexture.call(
|
|
this,
|
|
gameObject,
|
|
texture,
|
|
textureWidth, textureHeight,
|
|
srcX, srcY,
|
|
srcWidth, srcHeight,
|
|
scaleX, scaleY,
|
|
rotation,
|
|
flipX, flipY,
|
|
scrollFactorX, scrollFactorY,
|
|
displayOriginX, displayOriginY,
|
|
frameX, frameY, frameWidth, frameHeight,
|
|
tintTL, tintTR, tintBL, tintBR, tintEffect,
|
|
uOffset, vOffset,
|
|
camera,
|
|
parentTransformMatrix,
|
|
skipFlip,
|
|
textureUnit
|
|
);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Adds a Texture Frame into the batch for rendering.
|
|
*
|
|
* @method Phaser.Renderer.WebGL.Pipelines.LightPipeline#batchTextureFrame
|
|
* @since 3.50.0
|
|
*
|
|
* @param {Phaser.Textures.Frame} frame - The Texture Frame to be rendered.
|
|
* @param {number} x - The horizontal position to render the texture at.
|
|
* @param {number} y - The vertical position to render the texture at.
|
|
* @param {number} tint - The tint color.
|
|
* @param {number} alpha - The alpha value.
|
|
* @param {Phaser.GameObjects.Components.TransformMatrix} transformMatrix - The Transform Matrix to use for the texture.
|
|
* @param {Phaser.GameObjects.Components.TransformMatrix} [parentTransformMatrix] - A parent Transform Matrix.
|
|
*/
|
|
batchTextureFrame: function (
|
|
frame,
|
|
x, y,
|
|
tint, alpha,
|
|
transformMatrix,
|
|
parentTransformMatrix
|
|
)
|
|
{
|
|
if (this.lightsActive)
|
|
{
|
|
MultiPipeline.prototype.batchTextureFrame.call(
|
|
this,
|
|
frame,
|
|
x, y,
|
|
tint, alpha,
|
|
transformMatrix,
|
|
parentTransformMatrix
|
|
);
|
|
}
|
|
}
|
|
|
|
});
|
|
|
|
module.exports = LightPipeline;
|