UNPKG

phaser

Version:

A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.

722 lines (647 loc) 25.5 kB
/** * @author Benjamin D. Richards <benjamindrichards@gmail.com> * @copyright 2013-2026 Phaser Studio Inc. * @license {@link https://opensource.org/licenses/MIT|MIT License} */ var Camera = null; // Lazy loaded. var Vector2 = require('../../math/Vector2'); var TransformMatrix = require('./TransformMatrix'); /** * Provides methods used for setting the filters properties of a Game Object. * These apply special effects, post-processing and masks to the object. * Should be applied as a mixin and not used directly. * * Filters work by rendering the object to a texture. * The texture is then rendered again for each filter, using a shader. * See {@link Phaser.GameObjects.Components.FilterList} for more information. * * Enable filters with `enableFilters()`. * Each object with filters enabled, and any filters active, * makes a new draw call, plus one or more per active filter. * This can be expensive. Use sparingly. * * --- * * ## Camera * * Filters has a `filterCamera` property, which is a Camera. * The Camera does most of the hard work, including the filters. * * The Camera automatically focuses on the Game Object, * so you should not need to adjust it manually. * If you do want to adjust it, you can use `focusFiltersOverride`. * * --- * * ## Framebuffer Coverage * * Filters are rendered to a framebuffer, which is a texture. * Anything outside the bounds of the framebuffer is not rendered. * Think of it as a window into another world. * * To ensure that the game object fits into the framebuffer, * the internal camera is transformed to match the object. * The object can transform normally, and the camera will follow * while `filtersAutoFocus` is enabled. * * @namespace Phaser.GameObjects.Components.Filters * @since 4.0.0 */ var Filters = {}; if (typeof WEBGL_RENDERER) { Filters = { /** * The Camera used for filters. * You can use this to alter the perspective of filters. * It is not necessary to use this camera for ordinary rendering. * * This is only available if you use the `enableFilters` method. * * @name Phaser.GameObjects.Components.Filters#filterCamera * @type {Phaser.Cameras.Scene2D.Camera} * @default null * @since 4.0.0 * @webglOnly */ filterCamera: null, /** * The filter lists for this Game Object. * This is an object with `internal` and `external` properties. * Each list is a {@link Phaser.GameObjects.Components.FilterList} object. * * This is only available if you use the `enableFilters` method. * * @name Phaser.GameObjects.Components.Filters#filters * @type {Phaser.Types.GameObjects.FiltersInternalExternal|null} * @readonly * @since 4.0.0 * @webglOnly */ filters: { get: function () { if (this.filterCamera) { return this.filterCamera.filters; } return null; } }, /** * Whether any filters should be rendered on this Game Object. * This is `true` by default, even if there are no filters yet. * Disable this to skip filter rendering. * * Use `willRenderFilters()` to see if there are any active filters. * * @name Phaser.GameObjects.Components.Filters#renderFilters * @type {boolean} * @default true * @since 4.0.0 * @webglOnly */ renderFilters: true, /** * The maximum size of the base filter texture. * Filters may use a larger texture after the base texture is rendered. * The maximum texture size is at least 4096 in WebGL, based on the hardware. * You may set this lower to save memory or prevent resizing. * * @name Phaser.GameObjects.Components.Filters#maxFilterSize * @type {Phaser.Math.Vector2} * @since 4.0.0 * @webglOnly */ maxFilterSize: null, /** * Whether `filterCamera` should update every frame * to focus on the Game Object. * Disable this if you want to manually control the camera. * * @name Phaser.GameObjects.Components.Filters#filtersAutoFocus * @type {boolean} * @default true * @since 4.0.0 * @webglOnly */ filtersAutoFocus: true, /** * Whether the filters should focus on the context, * rather than attempt to focus on the Game Object. * This is enabled automatically when enabling filters on objects * which don't have well-defined bounds. * * This effectively sets the internal filters to render the same way * as the external filters. * * This is only used if `filtersAutoFocus` is enabled. * * The "context" is the framebuffer to which the Game Object is rendered. * This is usually the main framebuffer, but might be another framebuffer. * It can even be several different framebuffers if the Game Object is * rendered multiple times. * * @name Phaser.GameObjects.Components.Filters#filtersFocusContext * @type {boolean} * @default false * @since 4.0.0 * @webglOnly */ filtersFocusContext: false, /** * Whether the Filters component should always draw to a framebuffer, * even if there are no active filters. * * @name Phaser.GameObjects.Components.Filters#filtersForceComposite * @type {boolean} * @default false * @since 4.0.0 * @webglOnly */ filtersForceComposite: false, /** * A transform matrix used to render the filters. * It holds the transform of the Game Object. * * This is only available if you use the `enableFilters` method. * * @name Phaser.GameObjects.Components.Filters#_filtersMatrix * @type {Phaser.GameObjects.Components.TransformMatrix} * @private * @since 4.0.0 * @webglOnly */ _filtersMatrix: null, /** * A transform matrix used to render the filters. * It holds the view matrix for the filter camera, adjusted for the Game Object. * * This is only available if you use the `enableFilters` method. * * @name Phaser.GameObjects.Components.Filters#_filtersViewMatrix * @type {Phaser.GameObjects.Components.TransformMatrix} * @private * @since 4.0.0 * @webglOnly */ _filtersViewMatrix: null, /** * Whether this Game Object will render filters. * This is true if it has active filters, * and if the `renderFilters` property is also true. * * @method Phaser.GameObjects.Components.Filters#willRenderFilters * @since 4.0.0 * @webglOnly * @return {boolean} Whether the Game Object will render filters. */ willRenderFilters: function () { return this.renderFilters && this.filters && ( this.filters.internal.getActive().length > 0 || this.filters.external.getActive().length > 0 || this.filtersForceComposite ); }, /** * Enable this Game Object to have filters. * * You need to call this method if you want to use the `filterCamera` * and `filters` properties. It sets up the necessary data structures. * You may disable filter rendering with the `renderFilters` property. * * This is a WebGL only feature. It will return early if not available. * * @method Phaser.GameObjects.Components.Filters#enableFilters * @since 4.0.0 * @webglOnly * @return {this} */ enableFilters: function () { if (this.filterCamera || !this.scene.renderer.gl) { return this; } var scene = this.scene; if (!Camera) { // Lazy load the camera class to avoid circular dependencies. Camera = require('../../cameras/2d/Camera'); } this.filterCamera = new Camera(0, 0, 1, 1).setScene(scene, false); // Set up the filter camera to compute an inverse matrix for the object. this.filterCamera.isObjectInversion = true; if (scene.game.config.roundPixels) { this.filterCamera.roundPixels = true; } if (!this.maxFilterSize) { var maxTextureSize = scene.renderer.getMaxTextureSize(); this.maxFilterSize = new Vector2(maxTextureSize, maxTextureSize); } this._filtersMatrix = new TransformMatrix(); this._filtersViewMatrix = new TransformMatrix(); // Check whether the object is poorly bounded, and needs to focus on the context. if ( !this.getBounds || this.width === undefined || this.height === undefined || this.width === 0 || this.height === 0 ) { this.filtersFocusContext = true; } // Add filters as a render step. this.addRenderStep(this.renderWebGLFilters, 0); return this; }, /** * Render this object using filters. * * This function's scope is not guaranteed, so it doesn't refer to `this`. * * @method Phaser.GameObjects.Components.Filters#renderWebGLFilters * @webglOnly * @since 4.0.0 * @type {Phaser.Types.GameObjects.RenderWebGLStep} * @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] - The index of this function in the Game Object's list of render processes. Used to support multiple rendering functions. */ renderWebGLFilters: function ( renderer, gameObject, drawingContext, parentMatrix, renderStep ) { if (!gameObject.willRenderFilters()) { gameObject.renderWebGLStep( renderer, gameObject, drawingContext, parentMatrix, renderStep + 1 ); return; } var camera = drawingContext.camera; // Ordinarily, we would add the game object to the camera's render list here. // But because child objects are added to the filter camera's render list, // we wait for the object to be rendered, // and then take its filter camera's render list // and add it to the drawingContext's render list. var filtersAutoFocus = gameObject.filtersAutoFocus; var filtersFocusContext = gameObject.filtersFocusContext; if (filtersAutoFocus) { if (filtersFocusContext) { gameObject.focusFiltersOnCamera(camera); } else { gameObject.focusFilters(); } } var filterCamera = gameObject.filterCamera; filterCamera.preRender(); // Set the camera roundPixels property to reflect desired rounding. // This is necessary to avoid blurring from antialiasing // if coordinates are not integer. var filterCameraRoundPixels = filterCamera.roundPixels; filterCamera.roundPixels = gameObject.willRoundVertices( filterCamera, (gameObject.rotation % (Math.PI * 2) === 0) && (gameObject.scaleX === 1, gameObject.scaleY === 1) ); if (filtersAutoFocus && filtersFocusContext) { var parent = gameObject.parentContainer; if (parent) { // Apply the game object's parent world transform to the filter camera. var parentWorldMatrix = parent.getWorldTransformMatrix(); filterCamera.matrix.multiply(parentWorldMatrix); } } // Get transform. var transformMatrix = gameObject._filtersMatrix; var cameraMatrix = gameObject._filtersViewMatrix.copyWithScrollFactorFrom( camera.getViewMatrix(!drawingContext.useCanvas), camera.scrollX, camera.scrollY, gameObject.scrollFactorX, gameObject.scrollFactorY ); if (parentMatrix) { cameraMatrix.multiply(parentMatrix); } if (filtersFocusContext) { transformMatrix.loadIdentity(); } else { if (gameObject.type === 'Layer') { transformMatrix.loadIdentity(); } else { var flipX = gameObject.flipX ? -1 : 1; var flipY = gameObject.flipY ? -1 : 1; transformMatrix.applyITRS( gameObject.x, gameObject.y, gameObject.rotation, gameObject.scaleX * flipX, gameObject.scaleY * flipY ); } // Offset origin. var width = filterCamera.width; var height = filterCamera.height; transformMatrix.translate( -width * filterCamera.originX, -height * filterCamera.originY ); cameraMatrix.multiply(transformMatrix, transformMatrix); } // Set object scrollFactor to default. // We can't accurately focus the camera on the object if it has a scrollFactor, // because the camera needs to be set further away, // going to infinity at scrollFactor 0. // The scroll factor is baked into the transformMatrix, above. var scrollX = gameObject.scrollFactorX; var scrollY = gameObject.scrollFactorY; gameObject.scrollFactorX = 1; gameObject.scrollFactorY = 1; // Now we have the transform for the game object. // Render game object to framebuffer. renderer.cameraRenderNode.run( drawingContext, [ gameObject ], filterCamera, transformMatrix, true, renderStep + 1 ); // Restore scrollFactor. gameObject.scrollFactorX = scrollX; gameObject.scrollFactorY = scrollY; // Restore camera roundPixels. filterCamera.roundPixels = filterCameraRoundPixels; // Add the game object's filter camera's render list // to the drawingContext's render list. var filterRenderListLength = filterCamera.renderList.length; for (var i = 0; i < filterRenderListLength; i++) { camera.addToRenderList(filterCamera.renderList[i]); } }, /** * Focus the filter camera. * This sets the size and position of the filter camera to match the GameObject. * This is called automatically on render if `filtersAutoFocus` is enabled. * * This will focus on the GameObject's raw dimensions if available. * If the GameObject has no dimensions, this will focus on the context: * the camera belonging to the DrawingContext used to render the GameObject. * Context focus occurs during rendering, * as the context is not known until then. * * @method Phaser.GameObjects.Components.Filters#focusFilters * @webglOnly * @since 4.0.0 * @return {this} */ focusFilters: function () { var posX = this.x; var posY = this.y; var originX = this.originX; var originY = this.originY; var width = this.width; var height = this.height; if ( this.type === 'Layer' || isNaN(posX) || isNaN(posY) || isNaN(width) || isNaN(height) || isNaN(originX) || isNaN(originY) || width === 0 || height === 0 ) { this.filtersFocusContext = true; return this; } var rotation = this.rotation; var scaleX = this.scaleX; var scaleY = this.scaleY; // Handle flip. if (this.flipX) { scaleX *= -1; originX = 1 - originX; } if (this.flipY) { scaleY *= -1; originY = 1 - originY; } var centerX = posX + width * (0.5 - originX); var centerY = posY + height * (0.5 - originY); // Set the filter camera size to match the object. this.setFilterSize(width, height); // Set the filter camera to match the object. this.filterCamera .centerOn(centerX, centerY) .setRotation(-rotation) .setOrigin(originX, originY) .setZoom(1 / scaleX, 1 / scaleY); return this; }, /** * Focus the filter camera on a specific camera. * This is used internally when `filtersFocusContext` is enabled. * * @method Phaser.GameObjects.Components.Filters#focusFiltersOnCamera * @webglOnly * @since 4.0.0 * @param {Phaser.Cameras.Scene2D.Camera} camera - The camera to focus on. * @return {this} */ focusFiltersOnCamera: function (camera) { var width = camera.width; var height = camera.height; var posX = camera.scrollX; var posY = camera.scrollY; var rotation = camera.rotation; var zoomX = camera.zoomX; var zoomY = camera.zoomY; // Set the filter camera size to match the object. this.setFilterSize(width, height); this.filterCamera.setScroll(posX, posY); this.filterCamera.setRotation(rotation); this.filterCamera.setZoom(zoomX, zoomY); return this; }, /** * Manually override the focus of the filter camera. * This allows you to set the size and position of the filter camera manually. * It deactivates `filtersAutoFocus` when called. * * The camera will set scroll to place the game object at the * given position within a rectangle of the given width and height. * For example, calling `focusFiltersOverride(400, 200, 800, 600)` * will focus the camera to place the object's center * 100 pixels above the center of the camera (which is at 400x300). * * @method Phaser.GameObjects.Components.Filters#focusFiltersOverride * @webglOnly * @since 4.0.0 * @param {number} [x] - The x-coordinate of the focus point, relative to the filter size. Default is the center. * @param {number} [y] - The y-coordinate of the focus point, relative to the filter size. Default is the center. * @param {number} [width] - The width of the focus area. Default is the filter width. * @param {number} [height] - The height of the focus area. Default is the filter height. * @return {this} */ focusFiltersOverride: function (x, y, width, height) { var filterCamera = this.filterCamera; // Maintain size. if (width === undefined) { width = filterCamera.width; } if (height === undefined) { height = filterCamera.height; } // Default to center. if (x === undefined) { x = width / 2; } if (y === undefined) { y = height / 2; } var objectX = this.x; var objectY = this.y; this.setFilterSize(width, height); filterCamera.setScroll(objectX - x, objectY - y); var originX = x / width; var originY = y / height; filterCamera.setOrigin(originX, originY); // Stop automatic focus. this.filtersAutoFocus = false; return this; }, /** * Set the base size of the filter camera. * This is the size of the texture that internal filters will be drawn to. * External filters are drawn to the size of the context (usually the game canvas). * * This is typically the size of the GameObject. * It is set automatically when the Game Object is rendered * and `filtersAutoFocus` is enabled. * Turn off auto focus to set it manually. * * Technically, larger framebuffers may be used to provide padding. * This is the size of the final framebuffer used for "internal" rendering. * * @method Phaser.GameObjects.Components.Filters#setFilterSize * @webglOnly * @since 4.0.0 * @param {number} width - Base width of the filter texture. * @param {number} height - Base height of the filter texture. * @return {this} */ setFilterSize: function (width, height) { // Sanitize inputs. width = Math.max(1, Math.min(Math.ceil(width), this.maxFilterSize.x)); height = Math.max(1, Math.min(Math.ceil(height), this.maxFilterSize.y)); var filterCamera = this.filterCamera; if (!filterCamera) { return this; } filterCamera.setSize(width, height); return this; }, /** * Sets whether the filter camera should automatically re-focus on the Game Object every frame. * Sets the `filtersAutoFocus` property. * * @method Phaser.GameObjects.Components.Filters#setFiltersAutoFocus * @webglOnly * @since 4.0.0 * @param {boolean} value - Whether filters should be updated every frame. * @return {this} */ setFiltersAutoFocus: function (value) { this.filtersAutoFocus = value; return this; }, /** * Set whether the filters should focus on the context. * Sets the `filtersFocusContext` property. * * @method Phaser.GameObjects.Components.Filters#setFiltersFocusContext * @webglOnly * @since 4.0.0 * @param {boolean} value - Whether the filters should focus on the context. * @return {this} */ setFiltersFocusContext: function (value) { this.filtersFocusContext = value; return this; }, /** * Set whether the filters should always draw to a framebuffer. * Sets the `filtersForceComposite` property. * * @method Phaser.GameObjects.Components.Filters#setFiltersForceComposite * @webglOnly * @since 4.0.0 * @param {boolean} value - Whether the object should always draw to a framebuffer, even if there are no active filters. * @return {this} */ setFiltersForceComposite: function (value) { this.filtersForceComposite = value; return this; }, /** * Set whether the filters should be rendered. * Sets the `renderFilters` property. * * @method Phaser.GameObjects.Components.Filters#setRenderFilters * @webglOnly * @since 4.0.0 * @param {boolean} value - Whether the filters should be rendered. * @return {this} */ setRenderFilters: function (value) { this.renderFilters = value; return this; } }; } module.exports = Filters;