phaser/src/renderer/webgl/pipelines/ForwardDiffuseLightPipeline.js

516 lines
16 KiB
JavaScript
Raw Normal View History

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>
2019-01-15 16:20:22 +00:00
* @copyright 2019 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');
var ShaderSourceFS = require('../shaders/ForwardDiffuse-frag.js');
2018-02-09 19:19:21 +00:00
var TextureTintPipeline = require('./TextureTintPipeline');
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
* 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:
function ForwardDiffuseLightPipeline (config)
2018-01-26 23:17:11 +00:00
{
LIGHT_COUNT = config.maxLights;
config.fragShader = ShaderSourceFS.replace('%LIGHT_COUNT%', LIGHT_COUNT.toString());
TextureTintPipeline.call(this, config);
2018-07-13 10:14:22 +00:00
/**
* Default normal map texture to use.
*
* @name Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#defaultNormalMap
* @type {Phaser.Texture.Frame}
* @private
* @since 3.11.0
*/
this.defaultNormalMap;
/**
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
*/
this.inverseRotationMatrix = new Float32Array([
1, 0, 0,
0, 1, 0,
0, 0, 1
]);
2018-07-13 10:14:22 +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.Pipelines.ForwardDiffuseLightPipeline#boot
* @override
* @since 3.11.0
*/
boot: function ()
{
this.defaultNormalMap = this.game.textures.getFrame('__DEFAULT');
2018-01-26 23:17:11 +00:00
},
2018-02-09 19:19:21 +00:00
/**
2018-07-11 15:24:58 +00:00
* This function binds its base class resources and this lights 2D resources.
2018-02-09 19:19:21 +00:00
*
2018-04-18 11:13:49 +00:00
* @method Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#onBind
2018-04-19 12:28:10 +00:00
* @override
2018-02-09 19:19:21 +00:00
* @since 3.0.0
2018-07-11 15:24:58 +00:00
*
* @param {Phaser.GameObjects.GameObject} [gameObject] - The Game Object that invoked this pipeline, if any.
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-07-11 15:24:58 +00:00
onBind: function (gameObject)
2018-01-26 23:17:11 +00:00
{
TextureTintPipeline.prototype.onBind.call(this);
var renderer = this.renderer;
2018-01-30 03:38:31 +00:00
var program = this.program;
2018-01-26 23:17:11 +00:00
this.mvpUpdate();
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);
2018-07-11 15:24:58 +00:00
if (gameObject)
{
this.setNormalMap(gameObject);
}
2018-01-26 23:17:11 +00:00
return this;
},
2018-02-09 19:19:21 +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)
{
this.active = false;
var lightManager = scene.sys.lights;
2018-01-31 01:11:51 +00:00
if (!lightManager || lightManager.lights.length <= 0 || !lightManager.active)
2018-03-12 02:22:42 +00:00
{
// Passthru
2018-03-12 02:22:42 +00:00
return this;
}
var lights = lightManager.cull(camera);
var lightCount = Math.min(lights.length, LIGHT_COUNT);
2018-01-31 01:11:51 +00:00
if (lightCount === 0)
2018-01-31 01:11:51 +00:00
{
return this;
2018-01-31 01:11:51 +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;
2018-02-16 18:44:07 +00:00
var index;
2018-01-30 22:46:43 +00:00
2018-02-16 18:44:07 +00:00
for (index = 0; index < LIGHT_COUNT; ++index)
2018-01-31 01:11:51 +00:00
{
// Reset lights
renderer.setFloat1(program, 'uLights[' + index + '].radius', 0);
2018-01-31 01:11:51 +00:00
}
2018-01-30 22:46:43 +00:00
renderer.setFloat4(program, 'uCamera', camera.x, camera.y, camera.rotation, camera.zoom);
renderer.setFloat3(program, 'uAmbientLightColor', lightManager.ambientColor.r, lightManager.ambientColor.g, lightManager.ambientColor.b);
2018-02-16 18:44:07 +00:00
for (index = 0; index < lightCount; ++index)
2018-01-30 22:46:43 +00:00
{
var light = lights[index];
var lightName = 'uLights[' + index + '].';
2018-01-30 22:46:43 +00:00
cameraMatrix.transformPoint(light.x, light.y, point);
2018-01-30 22:46:43 +00:00
renderer.setFloat2(program, lightName + 'position', point.x - (camera.scrollX * light.scrollFactorX * camera.zoom), height - (point.y - (camera.scrollY * light.scrollFactorY) * camera.zoom));
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);
}
this.currentNormalMapRotation = null;
2018-01-29 21:46:48 +00:00
return this;
},
2018-07-13 10:14:22 +00:00
/**
* Generic function for batching a textured quad
*
2019-01-31 12:19:01 +00:00
* @method Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#batchTexture
2018-07-13 10:14:22 +00:00
* @since 3.0.0
*
* @param {Phaser.GameObjects.GameObject} gameObject - Source GameObject
* @param {WebGLTexture} texture - Raw WebGLTexture associated with the quad
* @param {integer} textureWidth - Real texture width
* @param {integer} 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 {integer} tintTL - Tint for top left
* @param {integer} tintTR - Tint for top right
* @param {integer} tintBL - Tint for bottom left
* @param {integer} tintBR - Tint for bottom right
* @param {number} tintEffect - The tint effect (0 for additive, 1 for replacement)
* @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
*/
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)
{
if (!this.active)
{
return;
}
this.renderer.setPipeline(this);
var normalTexture;
if (gameObject.displayTexture)
{
normalTexture = gameObject.displayTexture.dataSource[gameObject.displayFrame.sourceIndex];
}
else if (gameObject.texture)
2018-07-13 10:14:22 +00:00
{
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-07-13 10:14:22 +00:00
}
if (!normalTexture)
{
console.warn('Normal map missing or invalid');
return;
2018-07-13 10:14:22 +00:00
}
this.setTexture2D(normalTexture.glTexture, 1);
this.setNormalMapRotation(rotation);
2018-07-13 10:14:22 +00:00
var camMatrix = this._tempMatrix1;
var spriteMatrix = this._tempMatrix2;
var calcMatrix = this._tempMatrix3;
var u0 = (frameX / textureWidth) + uOffset;
var v0 = (frameY / textureHeight) + vOffset;
var u1 = (frameX + frameWidth) / textureWidth + uOffset;
var v1 = (frameY + frameHeight) / textureHeight + vOffset;
2018-07-13 10:14:22 +00:00
var width = srcWidth;
var height = srcHeight;
// var x = -displayOriginX + frameX;
// var y = -displayOriginY + frameY;
2018-07-13 10:14:22 +00:00
var x = -displayOriginX;
var y = -displayOriginY;
if (gameObject.isCropped)
{
var crop = gameObject._crop;
width = crop.width;
height = crop.height;
srcWidth = crop.width;
srcHeight = crop.height;
frameX = crop.x;
frameY = crop.y;
var ox = frameX;
var oy = frameY;
if (flipX)
{
ox = (frameWidth - crop.x - crop.width);
}
if (flipY && !texture.isRenderTexture)
{
oy = (frameHeight - crop.y - crop.height);
}
u0 = (ox / textureWidth) + uOffset;
v0 = (oy / textureHeight) + vOffset;
u1 = (ox + crop.width) / textureWidth + uOffset;
v1 = (oy + crop.height) / textureHeight + vOffset;
x = -displayOriginX + frameX;
y = -displayOriginY + frameY;
}
2018-07-13 10:14:22 +00:00
// Invert the flipY if this is a RenderTexture
flipY = flipY ^ (texture.isRenderTexture ? 1 : 0);
if (flipX)
{
width *= -1;
x += srcWidth;
}
if (flipY)
{
height *= -1;
y += srcHeight;
}
// Do we need this? (doubt it)
// if (camera.roundPixels)
// {
// x |= 0;
// y |= 0;
// }
2018-07-13 10:14:22 +00:00
var xw = x + width;
var yh = y + height;
spriteMatrix.applyITRS(srcX, srcY, rotation, scaleX, scaleY);
camMatrix.copyFrom(camera.matrix);
if (parentTransformMatrix)
{
// Multiply the camera by the parent matrix
camMatrix.multiplyWithOffset(parentTransformMatrix, -camera.scrollX * scrollFactorX, -camera.scrollY * scrollFactorY);
// Undo the camera scroll
spriteMatrix.e = srcX;
spriteMatrix.f = srcY;
// Multiply by the Sprite matrix, store result in calcMatrix
camMatrix.multiply(spriteMatrix, calcMatrix);
}
else
{
spriteMatrix.e -= camera.scrollX * scrollFactorX;
spriteMatrix.f -= camera.scrollY * scrollFactorY;
// Multiply by the Sprite matrix, store result in calcMatrix
camMatrix.multiply(spriteMatrix, calcMatrix);
}
var tx0 = calcMatrix.getX(x, y);
var ty0 = calcMatrix.getY(x, y);
2018-07-13 10:14:22 +00:00
var tx1 = calcMatrix.getX(x, yh);
var ty1 = calcMatrix.getY(x, yh);
2018-07-13 10:14:22 +00:00
var tx2 = calcMatrix.getX(xw, yh);
var ty2 = calcMatrix.getY(xw, yh);
2018-07-13 10:14:22 +00:00
var tx3 = calcMatrix.getX(xw, y);
var ty3 = calcMatrix.getY(xw, y);
2018-07-13 10:14:22 +00:00
if (camera.roundPixels)
{
tx0 = Math.round(tx0);
ty0 = Math.round(ty0);
2018-07-13 10:14:22 +00:00
tx1 = Math.round(tx1);
ty1 = Math.round(ty1);
2018-07-13 10:14:22 +00:00
tx2 = Math.round(tx2);
ty2 = Math.round(ty2);
2018-07-13 10:14:22 +00:00
tx3 = Math.round(tx3);
ty3 = Math.round(ty3);
2018-07-13 10:14:22 +00:00
}
this.setTexture2D(texture, 0);
this.batchQuad(tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3, u0, v0, u1, v1, tintTL, tintTR, tintBL, tintBR, tintEffect, texture, 0);
2018-07-13 10:14:22 +00:00
},
2018-02-09 19:19:21 +00:00
/**
* Sets the Game Objects normal map as the active texture.
2018-02-09 19:19:21 +00:00
*
* @method Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#setNormalMap
* @since 3.11.0
2018-02-09 19:19:21 +00:00
*
2018-10-19 11:32:43 +00:00
* @param {Phaser.GameObjects.GameObject} gameObject - The Game Object to update.
2018-02-09 19:19:21 +00:00
*/
2018-07-11 15:24:58 +00:00
setNormalMap: function (gameObject)
{
2018-07-13 10:14:22 +00:00
if (!this.active || !gameObject)
2018-07-11 15:24:58 +00:00
{
return;
}
2018-07-13 10:14:22 +00:00
var normalTexture;
2018-07-11 15:24:58 +00:00
2018-07-13 10:14:22 +00:00
if (gameObject.texture)
2018-07-11 15:24:58 +00:00
{
2018-07-13 10:14:22 +00:00
normalTexture = gameObject.texture.dataSource[gameObject.frame.sourceIndex];
2018-07-11 15:24:58 +00:00
}
2018-07-13 10:14:22 +00:00
if (!normalTexture)
{
normalTexture = this.defaultNormalMap;
}
this.setTexture2D(normalTexture.glTexture, 1);
this.renderer.setPipeline(gameObject.defaultPipeline);
2018-07-11 15:24:58 +00:00
},
/**
* Rotates the normal map vectors inversely by the given angle.
* Only works in 2D space.
2018-12-12 11:58:58 +00:00
*
* @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.batches.length === 0)
{
if (this.batches.length > 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;
}
},
2018-02-09 19:19:21 +00:00
/**
2018-10-19 11:32:43 +00:00
* Takes a Sprite Game Object, or any object that extends it, which has a normal texture and adds it to the batch.
2018-02-09 19:19:21 +00:00
*
2018-04-18 11:13:49 +00:00
* @method Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#batchSprite
2018-02-09 19:19:21 +00:00
* @since 3.0.0
*
2018-10-19 11:32:43 +00:00
* @param {Phaser.GameObjects.Sprite} sprite - 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 (sprite, camera, parentTransformMatrix)
2018-01-30 03:38:31 +00:00
{
if (!this.active)
{
return;
}
var normalTexture = sprite.texture.dataSource[sprite.frame.sourceIndex];
2018-01-30 03:38:31 +00:00
if (normalTexture)
{
this.renderer.setPipeline(this);
this.setTexture2D(normalTexture.glTexture, 1);
this.setNormalMapRotation(sprite.rotation);
2018-01-30 03:38:31 +00:00
TextureTintPipeline.prototype.batchSprite.call(this, sprite, camera, parentTransformMatrix);
2018-01-30 03:38:31 +00:00
}
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;