diff --git a/src/gameobjects/GameObject.js b/src/gameobjects/GameObject.js index 8a7c8af74..c56a32bf3 100644 --- a/src/gameobjects/GameObject.js +++ b/src/gameobjects/GameObject.js @@ -5,6 +5,7 @@ */ var Class = require('../utils/Class'); +var Components = require('./components'); var ComponentsToJSON = require('./components/ToJSON'); var DataManager = require('../data/DataManager'); var EventEmitter = require('eventemitter3'); @@ -30,6 +31,10 @@ var GameObject = new Class({ Extends: EventEmitter, + Mixins: [ + Components.RenderSteps, + ], + initialize: function GameObject (scene, type) diff --git a/src/gameobjects/components/RenderSteps.js b/src/gameobjects/components/RenderSteps.js new file mode 100644 index 000000000..7ba7a888c --- /dev/null +++ b/src/gameobjects/components/RenderSteps.js @@ -0,0 +1,151 @@ +/** + * @author Benjamin D. Richards + * @copyright 2013-2024 Phaser Studio Inc. + * @license {@link https://opensource.org/licenses/MIT|MIT License} + */ + +/** + * Handles render steps for a Game Object. + * The render step is a point in the render process that allows you to inject your own logic. + * + * @namespace Phaser.GameObjects.Components.RenderSteps + * @webglOnly + * @since 4.0.0 + */ +var RenderSteps = {}; + +if (typeof WEBGL_RENDERER) RenderSteps = { + /** + * The list of steps to run when this Game Object is rendered. + * This is used by `renderWebGLStep` to kick off rendering. + * The functions in this list are responsible for invoking any + * subsequent functions. + * + * @name Phaser.GameObjects.Components.RenderSteps#_renderSteps + * @private + * @webglOnly + * @since 4.0.0 + * @type {Phaser.Types.GameObjects.RenderWebGLStep[]} + */ + _renderSteps: null, + + /** + * Run a step in the render process. + * This is called automatically by the Render module. + * + * In most cases, it just runs the `renderWebGL` function. + * + * When `_renderSteps` has more than one entry, + * such as when Filters are enabled for this object, + * it allows those processes to defer `renderWebGL` + * and otherwise manage the flow of rendering + * + * The first time this method is called, + * it will run initialization logic that adds `renderWebGL` to the end + * of the `_renderSteps` array. + * Then it will replace itself with `_renderWebGLStep` + * and continue with the rendering process. + * + * @method Phaser.GameObjects.GameObject#renderWebGLStep + * @webglOnly + * @since 4.0.0 + * @param {Phaser.Renderer.WebGL.WebGLRenderer} renderer - The WebGL Renderer instance to render with. + * @param {Phaser.GameObjects.GameObject} gameObject - The Game Object being rendered. + * @param {Phaser.Renderer.WebGL.DrawingContext} drawingContext - The current drawing context. + * @param {Phaser.GameObjects.Components.TransformMatrix} [parentMatrix] - The parent matrix of the Game Object, if it has one. + * @param {number} [renderStep=0] - Which step of the rendering process should be run? + */ + renderWebGLStep: function ( + renderer, + gameObject, + drawingContext, + parentMatrix, + renderStep + ) + { + if (gameObject.renderWebGL) + { + gameObject.addRenderStep(gameObject.renderWebGL); + } + + gameObject.renderWebGLStep = gameObject._renderWebGLStep; + + gameObject._renderWebGLStep( + renderer, + gameObject, + drawingContext, + parentMatrix, + renderStep + ); + }, + + /** + * Run a step in the render process. + * This is called automatically by the Render module. + * + * In most cases, it just runs the `renderWebGL` function. + * + * When `_renderSteps` has more than one entry, + * such as when Filters are enabled for this object, + * it allows those processes to defer `renderWebGL` + * and otherwise manage the flow of rendering. + * + * This private method will be copied over `renderWebGLStep` + * the first time it runs. You should not call this method directly. + * + * @method Phaser.GameObjects.GameObject#_renderWebGLStep + * @private + * @webglOnly + * @since 4.0.0 + * @param {Phaser.Renderer.WebGL.WebGLRenderer} renderer - The WebGL Renderer instance to render with. + * @param {Phaser.GameObjects.GameObject} gameObject - The Game Object being rendered. + * @param {Phaser.Renderer.WebGL.DrawingContext} drawingContext - The current drawing context. + * @param {Phaser.GameObjects.Components.TransformMatrix} [parentMatrix] - The parent matrix of the Game Object, if it has one. + * @param {number} [renderStep=0] - Which step of the rendering process should be run? + */ + _renderWebGLStep: function ( + renderer, + gameObject, + drawingContext, + parentMatrix, + renderStep + ) + { + if (renderStep === undefined) + { + renderStep = 0; + } + + var fn = gameObject._renderSteps[renderStep]; + + if (!fn) + { + return; + } + + fn(renderer, gameObject, drawingContext, parentMatrix, renderStep); + }, + + addRenderStep: function (fn, index) + { + this.initRenderSteps(); + + if (index === undefined) + { + this._renderSteps.push(fn); + return; + } + + this._renderSteps.splice(index, 0, fn); + }, + + initRenderSteps: function () + { + if (!this._renderSteps) + { + this._renderSteps = []; + } + } +}; + +module.exports = RenderSteps; diff --git a/src/gameobjects/components/index.js b/src/gameobjects/components/index.js index e1ce45f15..73c4c2bab 100644 --- a/src/gameobjects/components/index.js +++ b/src/gameobjects/components/index.js @@ -25,6 +25,7 @@ module.exports = { Origin: require('./Origin'), PathFollower: require('./PathFollower'), RenderNodes: require('./RenderNodes'), + RenderSteps: require('./RenderSteps'), ScrollFactor: require('./ScrollFactor'), Size: require('./Size'), Texture: require('./Texture'), diff --git a/src/gameobjects/container/ContainerWebGLRenderer.js b/src/gameobjects/container/ContainerWebGLRenderer.js index fac947fda..ac7e022f7 100644 --- a/src/gameobjects/container/ContainerWebGLRenderer.js +++ b/src/gameobjects/container/ContainerWebGLRenderer.js @@ -118,7 +118,7 @@ var ContainerWebGLRenderer = function (renderer, container, drawingContext, pare child.setAlpha(childAlphaTopLeft * alpha, childAlphaTopRight * alpha, childAlphaBottomLeft * alpha, childAlphaBottomRight * alpha); // Render - child.renderWebGL(renderer, child, currentContext, transformMatrix, container); + child.renderWebGLStep(renderer, child, currentContext, transformMatrix); // Restore original values diff --git a/src/gameobjects/layer/Layer.js b/src/gameobjects/layer/Layer.js index 15230e6f7..b03628104 100644 --- a/src/gameobjects/layer/Layer.js +++ b/src/gameobjects/layer/Layer.js @@ -80,6 +80,7 @@ var Layer = new Class({ Components.BlendMode, Components.Depth, Components.Mask, + Components.RenderSteps, // This does not extend GameObject so it must mixin RenderSteps here. Components.Visible, EventEmitter, Render diff --git a/src/gameobjects/layer/LayerWebGLRenderer.js b/src/gameobjects/layer/LayerWebGLRenderer.js index fc908d30b..991161e8f 100644 --- a/src/gameobjects/layer/LayerWebGLRenderer.js +++ b/src/gameobjects/layer/LayerWebGLRenderer.js @@ -92,7 +92,7 @@ var LayerWebGLRenderer = function (renderer, layer, drawingContext) child.setAlpha(childAlphaTopLeft * alpha, childAlphaTopRight * alpha, childAlphaBottomLeft * alpha, childAlphaBottomRight * alpha); // Render - child.renderWebGL(renderer, child, currentContext); + child.renderWebGLStep(renderer, child, currentContext); // Restore original values child.setAlpha(childAlphaTopLeft, childAlphaTopRight, childAlphaBottomLeft, childAlphaBottomRight); diff --git a/src/gameobjects/shader/Shader.js b/src/gameobjects/shader/Shader.js index ba5e10fa5..562c8fb2c 100644 --- a/src/gameobjects/shader/Shader.js +++ b/src/gameobjects/shader/Shader.js @@ -386,7 +386,7 @@ var Shader = new Class({ this.renderToTexture = true; // Render at least once, so our texture isn't blank on the first update - this.renderWebGL(renderer, this, this.drawingContext); + this.renderWebGLStep(renderer, this, this.drawingContext); return this; }, diff --git a/src/gameobjects/typedefs/RenderWebGLStep.js b/src/gameobjects/typedefs/RenderWebGLStep.js new file mode 100644 index 000000000..c7619e7de --- /dev/null +++ b/src/gameobjects/typedefs/RenderWebGLStep.js @@ -0,0 +1,20 @@ +/** + * A function which performs a rendering operation on the given Game Object. + * This is usually the `renderWebGL` method of the Game Object itself, + * but `Phaser.GameObjects.Components.RenderSteps` allows you to define + * a series of steps that are run in sequence. + * The function is not expected to run in any particular scope, + * so it should not use `this`. Instead, all required properties should be + * accessed via `gameObject`. + * + * @callback Phaser.Types.GameObjects.RenderWebGLStep + * @since 4.0.0 + * + * @param {Phaser.Renderer.WebGL.WebGLRenderer} renderer - A reference to the current active WebGL renderer. + * @param {Phaser.GameObjects.GameObject} gameObject - The Game Object being rendered in this call. + * @param {Phaser.Renderer.WebGL.DrawingContext} drawingContext - The current drawing context. + * @param {Phaser.GameObjects.Components.TransformMatrix} [parentMatrix] - This transform matrix is defined if the game object is nested + * @param {number} [renderStep=0] - The index of this function in the Game Object's list of render processes. Used to support multiple rendering functions. + * + * @returns {void} + */ diff --git a/src/renderer/webgl/renderNodes/Camera.js b/src/renderer/webgl/renderNodes/Camera.js index e118f6005..a165965a0 100644 --- a/src/renderer/webgl/renderNodes/Camera.js +++ b/src/renderer/webgl/renderNodes/Camera.js @@ -75,8 +75,16 @@ var Camera = new Class({ * @param {Phaser.Cameras.Scene2D.Camera} camera - Current Camera. * @param {Phaser.GameObjects.Components.TransformMatrix} [parentTransformMatrix] - This transform matrix is defined if the game object is nested * @param {boolean} [forceFramebuffer=false] - Should the camera always draw to a new framebuffer? This will also be activated if the camera has filters enabled. + * @param {number} [renderStep=0] - Which step of the rendering process is this? This is the index of the currently running function in a list of functions. */ - run: function (drawingContext, children, camera, parentTransformMatrix, forceFramebuffer) + run: function ( + drawingContext, + children, + camera, + parentTransformMatrix, + forceFramebuffer, + renderStep + ) { this.onRunBegin(drawingContext); @@ -127,7 +135,7 @@ var Camera = new Class({ } // Draw children. - this.listCompositorNode.run(currentContext, children, useFramebuffers ? null : parentTransformMatrix); + this.listCompositorNode.run(currentContext, children, useFramebuffers ? null : parentTransformMatrix, renderStep); // Draw camera post effects. diff --git a/src/renderer/webgl/renderNodes/DynamicTextureHandler.js b/src/renderer/webgl/renderNodes/DynamicTextureHandler.js index 5d661523c..30944a5d0 100644 --- a/src/renderer/webgl/renderNodes/DynamicTextureHandler.js +++ b/src/renderer/webgl/renderNodes/DynamicTextureHandler.js @@ -307,7 +307,7 @@ var DynamicTextureHandler = new Class({ currentContext.use(); } - object.renderWebGL(renderer, object, currentContext); + object.renderWebGLStep(renderer, object, currentContext); return currentContext; } diff --git a/src/renderer/webgl/renderNodes/ListCompositor.js b/src/renderer/webgl/renderNodes/ListCompositor.js index 57aa072a4..67318cd7e 100644 --- a/src/renderer/webgl/renderNodes/ListCompositor.js +++ b/src/renderer/webgl/renderNodes/ListCompositor.js @@ -37,8 +37,14 @@ var ListCompositor = new Class({ * @param {Phaser.Renderer.WebGL.DrawingContext} displayContext - The context currently in use. * @param {Phaser.GameObjects.GameObject[]} children - The list of children to render. * @param {Phaser.GameObjects.Components.TransformMatrix} [parentTransformMatrix] - This transform matrix is defined if the game object is nested + * @param {number} [renderStep=0] - Which step of the rendering process is this? This is the index of the currently running function in a list of functions. */ - run: function (displayContext, children, parentTransformMatrix) + run: function ( + displayContext, + children, + parentTransformMatrix, + renderStep + ) { this.onRunBegin(displayContext); @@ -79,7 +85,7 @@ var ListCompositor = new Class({ } } - child.renderWebGL(renderer, child, currentContext, parentTransformMatrix); + child.renderWebGLStep(renderer, child, currentContext, parentTransformMatrix, renderStep); } // Release any remaining context. diff --git a/src/textures/DynamicTexture.js b/src/textures/DynamicTexture.js index a3d242401..2cf14700d 100644 --- a/src/textures/DynamicTexture.js +++ b/src/textures/DynamicTexture.js @@ -965,7 +965,7 @@ var DynamicTexture = new Class({ stamp.setTexture(this); stamp.setOrigin(0); - stamp.renderWebGL(renderer, stamp, camera, parentMatrix); + stamp.renderWebGLStep(renderer, stamp, camera, parentMatrix); }, /**