UNPKG

phaser

Version:

A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers.

547 lines (451 loc) 19.4 kB
/** * @author Richard Davey <rich@photonstorm.com> * @author Felipe Alfonso <@bitnenfer> * @copyright 2020 Photon Storm Ltd. * @license {@link https://opensource.org/licenses/MIT|MIT License} */ var Class = require('../../../utils/Class'); var GetFastValue = require('../../../utils/object/GetFastValue'); var ShaderSourceFS = require('../shaders/Multi-frag.js'); var ShaderSourceVS = require('../shaders/Multi-vert.js'); var TransformMatrix = require('../../../gameobjects/components/TransformMatrix'); var Utils = require('../Utils'); var WEBGL_CONST = require('../const'); var WebGLPipeline = require('../WebGLPipeline'); /** * @classdesc * * The Multi Pipeline is the core 2D texture rendering pipeline used by Phaser in WebGL. * Virtually all Game Objects use this pipeline by default, including Sprites, Graphics * and Tilemaps. It handles the batching of quads and tris, as well as methods for * drawing and batching geometry data. * * Prior to Phaser v3.50 this pipeline was called the `TextureTintPipeline`. * * In previous versions of Phaser only one single texture unit was supported at any one time. * The Multi Pipeline is an evolution of the old Texture Tint Pipeline, updated to support * multi-textures for increased performance. * * The fragment shader it uses can be found in `shaders/src/Multi.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: * * `uProjectionMatrix` (mat4) * `uMainSampler` (sampler2D array) * * If you wish to create a custom pipeline extending from this one, you can use two string * declarations in your fragment shader source: `%count%` and `%forloop%`, where `count` is * used to set the number of `sampler2Ds` available, and `forloop` is a block of GLSL code * that will get the currently bound texture unit. * * This pipeline will automatically inject that code for you, should those values exist * in your shader source. If you wish to handle this yourself, you can also use the * function `Utils.parseFragmentShaderMaxTextures`. * * If you wish to create a pipeline that works from a single texture, or that doesn't have * internal texture iteration, please see the `SinglePipeline` instead. * * @class MultiPipeline * @extends Phaser.Renderer.WebGL.WebGLPipeline * @memberof Phaser.Renderer.WebGL.Pipelines * @constructor * @since 3.50.0 * * @param {Phaser.Types.Renderer.WebGL.WebGLPipelineConfig} config - The configuration options for this pipeline. */ var MultiPipeline = new Class({ Extends: WebGLPipeline, initialize: function MultiPipeline (config) { var renderer = config.game.renderer; var fragmentShaderSource = GetFastValue(config, 'fragShader', ShaderSourceFS); config.fragShader = Utils.parseFragmentShaderMaxTextures(fragmentShaderSource, renderer.maxTextures); config.vertShader = GetFastValue(config, 'vertShader', ShaderSourceVS); config.attributes = GetFastValue(config, 'attributes', [ { name: 'inPosition', size: 2 }, { name: 'inTexCoord', size: 2 }, { name: 'inTexId' }, { name: 'inTintEffect' }, { name: 'inTint', size: 4, type: WEBGL_CONST.UNSIGNED_BYTE, normalized: true } ]); WebGLPipeline.call(this, config); /** * A temporary Transform Matrix, re-used internally during batching. * * @name Phaser.Renderer.WebGL.Pipelines.MultiPipeline#_tempMatrix1 * @private * @type {Phaser.GameObjects.Components.TransformMatrix} * @since 3.11.0 */ this._tempMatrix1 = new TransformMatrix(); /** * A temporary Transform Matrix, re-used internally during batching. * * @name Phaser.Renderer.WebGL.Pipelines.MultiPipeline#_tempMatrix2 * @private * @type {Phaser.GameObjects.Components.TransformMatrix} * @since 3.11.0 */ this._tempMatrix2 = new TransformMatrix(); /** * A temporary Transform Matrix, re-used internally during batching. * * @name Phaser.Renderer.WebGL.Pipelines.MultiPipeline#_tempMatrix3 * @private * @type {Phaser.GameObjects.Components.TransformMatrix} * @since 3.11.0 */ this._tempMatrix3 = new TransformMatrix(); }, /** * 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. * * @method Phaser.Renderer.WebGL.Pipelines.MultiPipeline#bind * @since 3.50.0 * * @return {this} This WebGLPipeline instance. */ boot: function () { WebGLPipeline.prototype.boot.call(this); this.currentShader.set1iv('uMainSampler', this.renderer.textureIndexes); }, /** * Takes a Sprite Game Object, or any object that extends it, and adds it to the batch. * * @method Phaser.Renderer.WebGL.Pipelines.MultiPipeline#batchSprite * @since 3.0.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) { this.manager.set(this, gameObject); var camMatrix = this._tempMatrix1; var spriteMatrix = this._tempMatrix2; var calcMatrix = this._tempMatrix3; var frame = gameObject.frame; var texture = frame.glTexture; var u0 = frame.u0; var v0 = frame.v0; var u1 = frame.u1; var v1 = frame.v1; var frameX = frame.x; var frameY = frame.y; var frameWidth = frame.cutWidth; var frameHeight = frame.cutHeight; var customPivot = frame.customPivot; var displayOriginX = gameObject.displayOriginX; var displayOriginY = gameObject.displayOriginY; var x = -displayOriginX + frameX; var y = -displayOriginY + frameY; if (gameObject.isCropped) { var crop = gameObject._crop; if (crop.flipX !== gameObject.flipX || crop.flipY !== gameObject.flipY) { frame.updateCropUVs(crop, gameObject.flipX, gameObject.flipY); } u0 = crop.u0; v0 = crop.v0; u1 = crop.u1; v1 = crop.v1; frameWidth = crop.width; frameHeight = crop.height; frameX = crop.x; frameY = crop.y; x = -displayOriginX + frameX; y = -displayOriginY + frameY; } var flipX = 1; var flipY = 1; if (gameObject.flipX) { if (!customPivot) { x += (-frame.realWidth + (displayOriginX * 2)); } flipX = -1; } // Auto-invert the flipY if this is coming from a GLTexture if (gameObject.flipY || (frame.source.isGLTexture && !texture.flipY)) { if (!customPivot) { y += (-frame.realHeight + (displayOriginY * 2)); } flipY = -1; } spriteMatrix.applyITRS(gameObject.x, gameObject.y, gameObject.rotation, gameObject.scaleX * flipX, gameObject.scaleY * flipY); camMatrix.copyFrom(camera.matrix); if (parentTransformMatrix) { // Multiply the camera by the parent matrix camMatrix.multiplyWithOffset(parentTransformMatrix, -camera.scrollX * gameObject.scrollFactorX, -camera.scrollY * gameObject.scrollFactorY); // Undo the camera scroll spriteMatrix.e = gameObject.x; spriteMatrix.f = gameObject.y; } else { spriteMatrix.e -= camera.scrollX * gameObject.scrollFactorX; spriteMatrix.f -= camera.scrollY * gameObject.scrollFactorY; } // Multiply by the Sprite matrix, store result in calcMatrix camMatrix.multiply(spriteMatrix, calcMatrix); var xw = x + frameWidth; var yh = y + frameHeight; var roundPixels = camera.roundPixels; var tx0 = calcMatrix.getXRound(x, y, roundPixels); var ty0 = calcMatrix.getYRound(x, y, roundPixels); var tx1 = calcMatrix.getXRound(x, yh, roundPixels); var ty1 = calcMatrix.getYRound(x, yh, roundPixels); var tx2 = calcMatrix.getXRound(xw, yh, roundPixels); var ty2 = calcMatrix.getYRound(xw, yh, roundPixels); var tx3 = calcMatrix.getXRound(xw, y, roundPixels); var ty3 = calcMatrix.getYRound(xw, y, roundPixels); var getTint = Utils.getTintAppendFloatAlpha; var cameraAlpha = camera.alpha; var tintTL = getTint(gameObject.tintTopLeft, cameraAlpha * gameObject._alphaTL); var tintTR = getTint(gameObject.tintTopRight, cameraAlpha * gameObject._alphaTR); var tintBL = getTint(gameObject.tintBottomLeft, cameraAlpha * gameObject._alphaBL); var tintBR = getTint(gameObject.tintBottomRight, cameraAlpha * gameObject._alphaBR); if (this.shouldFlush(6)) { this.flush(); } var unit = this.setGameObject(gameObject, frame); this.manager.preBatch(gameObject); this.batchQuad(gameObject, tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3, u0, v0, u1, v1, tintTL, tintTR, tintBL, tintBR, gameObject.tintFill, texture, unit); this.manager.postBatch(gameObject); }, /** * Generic function for batching a textured quad using argument values instead of a Game Object. * * @method Phaser.Renderer.WebGL.Pipelines.MultiPipeline#batchTexture * @since 3.0.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) { this.manager.set(this, gameObject); 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; var width = srcWidth; var height = srcHeight; var x = -displayOriginX; var y = -displayOriginY; if (gameObject.isCropped) { var crop = gameObject._crop; var cropWidth = crop.width; var cropHeight = crop.height; width = cropWidth; height = cropHeight; srcWidth = cropWidth; srcHeight = cropHeight; frameX = crop.x; frameY = crop.y; var ox = frameX; var oy = frameY; if (flipX) { ox = (frameWidth - crop.x - cropWidth); } if (flipY) { oy = (frameHeight - crop.y - cropHeight); } u0 = (ox / textureWidth) + uOffset; v0 = (oy / textureHeight) + vOffset; u1 = (ox + cropWidth) / textureWidth + uOffset; v1 = (oy + cropHeight) / textureHeight + vOffset; x = -displayOriginX + frameX; y = -displayOriginY + frameY; } // Invert the flipY if this is a RenderTexture flipY = flipY ^ (!skipFlip && texture.isRenderTexture ? 1 : 0); if (flipX) { width *= -1; x += srcWidth; } if (flipY) { height *= -1; y += srcHeight; } 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; } 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 roundPixels = camera.roundPixels; var tx0 = calcMatrix.getXRound(x, y, roundPixels); var ty0 = calcMatrix.getYRound(x, y, roundPixels); var tx1 = calcMatrix.getXRound(x, yh, roundPixels); var ty1 = calcMatrix.getYRound(x, yh, roundPixels); var tx2 = calcMatrix.getXRound(xw, yh, roundPixels); var ty2 = calcMatrix.getYRound(xw, yh, roundPixels); var tx3 = calcMatrix.getXRound(xw, y, roundPixels); var ty3 = calcMatrix.getYRound(xw, y, roundPixels); if (textureUnit === undefined) { textureUnit = this.renderer.setTexture2D(texture); } if (gameObject) { this.manager.preBatch(gameObject); } this.batchQuad(gameObject, tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3, u0, v0, u1, v1, tintTL, tintTR, tintBL, tintBR, tintEffect, texture, textureUnit); if (gameObject) { this.manager.postBatch(gameObject); } }, /** * Adds a Texture Frame into the batch for rendering. * * @method Phaser.Renderer.WebGL.Pipelines.MultiPipeline#batchTextureFrame * @since 3.12.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 ) { this.manager.set(this); var spriteMatrix = this._tempMatrix1.copyFrom(transformMatrix); var calcMatrix = this._tempMatrix2; var xw = x + frame.width; var yh = y + frame.height; if (parentTransformMatrix) { spriteMatrix.multiply(parentTransformMatrix, calcMatrix); } else { calcMatrix = spriteMatrix; } var tx0 = calcMatrix.getX(x, y); var ty0 = calcMatrix.getY(x, y); var tx1 = calcMatrix.getX(x, yh); var ty1 = calcMatrix.getY(x, yh); var tx2 = calcMatrix.getX(xw, yh); var ty2 = calcMatrix.getY(xw, yh); var tx3 = calcMatrix.getX(xw, y); var ty3 = calcMatrix.getY(xw, y); var unit = this.renderer.setTextureSource(frame.source); tint = Utils.getTintAppendFloatAlpha(tint, alpha); this.batchQuad(null, tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3, frame.u0, frame.v0, frame.u1, frame.v1, tint, tint, tint, tint, 0, frame.glTexture, unit); } }); module.exports = MultiPipeline;