phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers.
678 lines (583 loc) • 23.2 kB
JavaScript
/**
* @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 Earcut = require('../../../geom/polygon/Earcut');
var GetFastValue = require('../../../utils/object/GetFastValue');
var ShaderSourceFS = require('../shaders/Graphics-frag.js');
var ShaderSourceVS = require('../shaders/Graphics-vert.js');
var TransformMatrix = require('../../../gameobjects/components/TransformMatrix');
var WEBGL_CONST = require('../const');
var WebGLPipeline = require('../WebGLPipeline');
/**
* @classdesc
* The Graphics Pipeline is the rendering pipeline used by Phaser in WebGL when drawing
* primitive geometry objects, such as the Graphics Game Object, or the Shape Game Objects
* such as Arc, Line, Rectangle and Star. It handles the preperation and batching of related vertices.
*
* Prior to Phaser v3.50 the functions of this pipeline were merged with the `TextureTintPipeline`.
*
* The fragment shader it uses can be found in `shaders/src/Graphics.frag`.
* The vertex shader it uses can be found in `shaders/src/Graphics.vert`.
*
* The default shader attributes for this pipeline are:
*
* `inPosition` (vec2)
* `inColor` (vec4, normalized)
*
* The default shader uniforms for this pipeline are:
*
* `uProjectionMatrix` (mat4)
*
* @class GraphicsPipeline
* @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 GraphicsPipeline = new Class({
Extends: WebGLPipeline,
initialize:
function GraphicsPipeline (config)
{
config.fragShader = GetFastValue(config, 'fragShader', ShaderSourceFS);
config.vertShader = GetFastValue(config, 'vertShader', ShaderSourceVS);
config.attributes = GetFastValue(config, 'attributes', [
{
name: 'inPosition',
size: 2
},
{
name: 'inColor',
size: 4,
type: WEBGL_CONST.UNSIGNED_BYTE,
normalized: true
}
]);
WebGLPipeline.call(this, config);
/**
* A temporary Transform Matrix, re-used internally during batching by the
* Shape Game Objects.
*
* @name Phaser.Renderer.WebGL.Pipelines.GraphicsPipeline#calcMatrix
* @type {Phaser.GameObjects.Components.TransformMatrix}
* @since 3.50.0
*/
this.calcMatrix = new TransformMatrix();
/**
* Used internally to draw stroked triangles.
*
* @name Phaser.Renderer.WebGL.Pipelines.GraphicsPipeline#tempTriangle
* @type {array}
* @private
* @since 3.50.0
*/
this.tempTriangle = [
{ x: 0, y: 0, width: 0 },
{ x: 0, y: 0, width: 0 },
{ x: 0, y: 0, width: 0 },
{ x: 0, y: 0, width: 0 }
];
/**
* Cached stroke tint.
*
* @name Phaser.Renderer.WebGL.Pipelines.GraphicsPipeline#strokeTint
* @type {object}
* @private
* @since 3.50.0
*/
this.strokeTint = { TL: 0, TR: 0, BL: 0, BR: 0 };
/**
* Cached fill tint.
*
* @name Phaser.Renderer.WebGL.Pipelines.GraphicsPipeline#fillTint
* @type {object}
* @private
* @since 3.50.0
*/
this.fillTint = { TL: 0, TR: 0, BL: 0, BR: 0 };
/**
* Internal texture frame reference.
*
* @name Phaser.Renderer.WebGL.Pipelines.GraphicsPipeline#currentFrame
* @type {Phaser.Textures.Frame}
* @private
* @since 3.50.0
*/
this.currentFrame = { u0: 0, v0: 0, u1: 1, v1: 1 };
/**
* Internal path quad cache.
*
* @name Phaser.Renderer.WebGL.Pipelines.GraphicsPipeline#firstQuad
* @type {number[]}
* @private
* @since 3.50.0
*/
this.firstQuad = [ 0, 0, 0, 0, 0 ];
/**
* Internal path quad cache.
*
* @name Phaser.Renderer.WebGL.Pipelines.GraphicsPipeline#prevQuad
* @type {number[]}
* @private
* @since 3.50.0
*/
this.prevQuad = [ 0, 0, 0, 0, 0 ];
/**
* Used internally for triangulating a polygon.
*
* @name Phaser.Renderer.WebGL.Pipelines.GraphicsPipeline#polygonCache
* @type {array}
* @private
* @since 3.50.0
*/
this.polygonCache = [];
},
/**
* Pushes a filled rectangle into the vertex batch.
*
* Rectangle factors in the given transform matrices before adding to the batch.
*
* @method Phaser.Renderer.WebGL.Pipelines.GraphicsPipeline#batchFillRect
* @since 3.50.0
*
* @param {number} x - Horizontal top left coordinate of the rectangle.
* @param {number} y - Vertical top left coordinate of the rectangle.
* @param {number} width - Width of the rectangle.
* @param {number} height - Height of the rectangle.
* @param {Phaser.GameObjects.Components.TransformMatrix} currentMatrix - The current transform.
* @param {Phaser.GameObjects.Components.TransformMatrix} parentMatrix - The parent transform.
*/
batchFillRect: function (x, y, width, height, currentMatrix, parentMatrix)
{
this.renderer.pipelines.set(this);
var calcMatrix = this.calcMatrix;
// Multiply and store result in calcMatrix, only if the parentMatrix is set, otherwise we'll use whatever values are already in the calcMatrix
if (parentMatrix)
{
parentMatrix.multiply(currentMatrix, calcMatrix);
}
var xw = x + width;
var yh = y + height;
var x0 = calcMatrix.getX(x, y);
var y0 = calcMatrix.getY(x, y);
var x1 = calcMatrix.getX(x, yh);
var y1 = calcMatrix.getY(x, yh);
var x2 = calcMatrix.getX(xw, yh);
var y2 = calcMatrix.getY(xw, yh);
var x3 = calcMatrix.getX(xw, y);
var y3 = calcMatrix.getY(xw, y);
var tint = this.fillTint;
this.batchQuad(x0, y0, x1, y1, x2, y2, x3, y3, tint.TL, tint.TR, tint.BL, tint.BR);
},
/**
* Pushes a filled triangle into the vertex batch.
*
* Triangle factors in the given transform matrices before adding to the batch.
*
* @method Phaser.Renderer.WebGL.Pipelines.GraphicsPipeline#batchFillTriangle
* @since 3.50.0
*
* @param {number} x0 - Point 0 x coordinate.
* @param {number} y0 - Point 0 y coordinate.
* @param {number} x1 - Point 1 x coordinate.
* @param {number} y1 - Point 1 y coordinate.
* @param {number} x2 - Point 2 x coordinate.
* @param {number} y2 - Point 2 y coordinate.
* @param {Phaser.GameObjects.Components.TransformMatrix} currentMatrix - The current transform.
* @param {Phaser.GameObjects.Components.TransformMatrix} parentMatrix - The parent transform.
*/
batchFillTriangle: function (x0, y0, x1, y1, x2, y2, currentMatrix, parentMatrix)
{
this.renderer.pipelines.set(this);
var calcMatrix = this.calcMatrix;
// Multiply and store result in calcMatrix, only if the parentMatrix is set, otherwise we'll use whatever values are already in the calcMatrix
if (parentMatrix)
{
parentMatrix.multiply(currentMatrix, calcMatrix);
}
var tx0 = calcMatrix.getX(x0, y0);
var ty0 = calcMatrix.getY(x0, y0);
var tx1 = calcMatrix.getX(x1, y1);
var ty1 = calcMatrix.getY(x1, y1);
var tx2 = calcMatrix.getX(x2, y2);
var ty2 = calcMatrix.getY(x2, y2);
var tint = this.fillTint;
this.batchTri(tx0, ty0, tx1, ty1, tx2, ty2, tint.TL, tint.TR, tint.BL);
},
/**
* Pushes a stroked triangle into the vertex batch.
*
* Triangle factors in the given transform matrices before adding to the batch.
*
* The triangle is created from 3 lines and drawn using the `batchStrokePath` method.
*
* @method Phaser.Renderer.WebGL.Pipelines.GraphicsPipeline#batchStrokeTriangle
* @since 3.50.0
*
* @param {number} x0 - Point 0 x coordinate.
* @param {number} y0 - Point 0 y coordinate.
* @param {number} x1 - Point 1 x coordinate.
* @param {number} y1 - Point 1 y coordinate.
* @param {number} x2 - Point 2 x coordinate.
* @param {number} y2 - Point 2 y coordinate.
* @param {number} lineWidth - The width of the line in pixels.
* @param {Phaser.GameObjects.Components.TransformMatrix} currentMatrix - The current transform.
* @param {Phaser.GameObjects.Components.TransformMatrix} parentMatrix - The parent transform.
*/
batchStrokeTriangle: function (x0, y0, x1, y1, x2, y2, lineWidth, currentMatrix, parentMatrix)
{
var tempTriangle = this.tempTriangle;
tempTriangle[0].x = x0;
tempTriangle[0].y = y0;
tempTriangle[0].width = lineWidth;
tempTriangle[1].x = x1;
tempTriangle[1].y = y1;
tempTriangle[1].width = lineWidth;
tempTriangle[2].x = x2;
tempTriangle[2].y = y2;
tempTriangle[2].width = lineWidth;
tempTriangle[3].x = x0;
tempTriangle[3].y = y0;
tempTriangle[3].width = lineWidth;
this.batchStrokePath(tempTriangle, lineWidth, false, currentMatrix, parentMatrix);
},
/**
* Adds the given path to the vertex batch for rendering.
*
* It works by taking the array of path data and then passing it through Earcut, which
* creates a list of polygons. Each polygon is then added to the batch.
*
* The path is always automatically closed because it's filled.
*
* @method Phaser.Renderer.WebGL.Pipelines.GraphicsPipeline#batchFillPath
* @since 3.50.0
*
* @param {Phaser.Types.Math.Vector2Like[]} path - Collection of points that represent the path.
* @param {Phaser.GameObjects.Components.TransformMatrix} currentMatrix - The current transform.
* @param {Phaser.GameObjects.Components.TransformMatrix} parentMatrix - The parent transform.
*/
batchFillPath: function (path, currentMatrix, parentMatrix)
{
this.renderer.pipelines.set(this);
var calcMatrix = this.calcMatrix;
// Multiply and store result in calcMatrix, only if the parentMatrix is set, otherwise we'll use whatever values are already in the calcMatrix
if (parentMatrix)
{
parentMatrix.multiply(currentMatrix, calcMatrix);
}
var length = path.length;
var polygonCache = this.polygonCache;
var polygonIndexArray;
var point;
var tintTL = this.fillTint.TL;
var tintTR = this.fillTint.TR;
var tintBL = this.fillTint.BL;
for (var pathIndex = 0; pathIndex < length; ++pathIndex)
{
point = path[pathIndex];
polygonCache.push(point.x, point.y);
}
polygonIndexArray = Earcut(polygonCache);
length = polygonIndexArray.length;
for (var index = 0; index < length; index += 3)
{
var p0 = polygonIndexArray[index + 0] * 2;
var p1 = polygonIndexArray[index + 1] * 2;
var p2 = polygonIndexArray[index + 2] * 2;
var x0 = polygonCache[p0 + 0];
var y0 = polygonCache[p0 + 1];
var x1 = polygonCache[p1 + 0];
var y1 = polygonCache[p1 + 1];
var x2 = polygonCache[p2 + 0];
var y2 = polygonCache[p2 + 1];
var tx0 = calcMatrix.getX(x0, y0);
var ty0 = calcMatrix.getY(x0, y0);
var tx1 = calcMatrix.getX(x1, y1);
var ty1 = calcMatrix.getY(x1, y1);
var tx2 = calcMatrix.getX(x2, y2);
var ty2 = calcMatrix.getY(x2, y2);
this.batchTri(tx0, ty0, tx1, ty1, tx2, ty2, tintTL, tintTR, tintBL);
}
polygonCache.length = 0;
},
/**
* Adds the given path to the vertex batch for rendering.
*
* It works by taking the array of path data and calling `batchLine` for each section
* of the path.
*
* The path is optionally closed at the end.
*
* @method Phaser.Renderer.WebGL.Pipelines.GraphicsPipeline#batchStrokePath
* @since 3.50.0
*
* @param {Phaser.Types.Math.Vector2Like[]} path - Collection of points that represent the path.
* @param {number} lineWidth - The width of the line segments in pixels.
* @param {boolean} pathOpen - Indicates if the path should be closed or left open.
* @param {Phaser.GameObjects.Components.TransformMatrix} currentMatrix - The current transform.
* @param {Phaser.GameObjects.Components.TransformMatrix} parentMatrix - The parent transform.
*/
batchStrokePath: function (path, lineWidth, pathOpen, currentMatrix, parentMatrix)
{
this.renderer.pipelines.set(this);
// Reset the closePath booleans
this.prevQuad[4] = 0;
this.firstQuad[4] = 0;
var pathLength = path.length - 1;
for (var pathIndex = 0; pathIndex < pathLength; pathIndex++)
{
var point0 = path[pathIndex];
var point1 = path[pathIndex + 1];
this.batchLine(
point0.x,
point0.y,
point1.x,
point1.y,
point0.width / 2,
point1.width / 2,
lineWidth,
pathIndex,
!pathOpen && (pathIndex === pathLength - 1),
currentMatrix,
parentMatrix
);
}
},
/**
* Creates a line out of 4 quads and adds it to the vertex batch based on the given line values.
*
* @method Phaser.Renderer.WebGL.Pipelines.GraphicsPipeline#batchLine
* @since 3.50.0
*
* @param {number} ax - x coordinate of the start of the line.
* @param {number} ay - y coordinate of the start of the line.
* @param {number} bx - x coordinate of the end of the line.
* @param {number} by - y coordinate of the end of the line.
* @param {number} aLineWidth - Width of the start of the line.
* @param {number} bLineWidth - Width of the end of the line.
* @param {number} index - If this line is part of a multi-line draw, the index of the line in the draw.
* @param {boolean} closePath - Does this line close a multi-line path?
* @param {Phaser.GameObjects.Components.TransformMatrix} currentMatrix - The current transform.
* @param {Phaser.GameObjects.Components.TransformMatrix} parentMatrix - The parent transform.
*/
batchLine: function (ax, ay, bx, by, aLineWidth, bLineWidth, lineWidth, index, closePath, currentMatrix, parentMatrix)
{
this.renderer.pipelines.set(this);
var calcMatrix = this.calcMatrix;
// Multiply and store result in calcMatrix, only if the parentMatrix is set, otherwise we'll use whatever values are already in the calcMatrix
if (parentMatrix)
{
parentMatrix.multiply(currentMatrix, calcMatrix);
}
var dx = bx - ax;
var dy = by - ay;
var len = Math.sqrt(dx * dx + dy * dy);
var al0 = aLineWidth * (by - ay) / len;
var al1 = aLineWidth * (ax - bx) / len;
var bl0 = bLineWidth * (by - ay) / len;
var bl1 = bLineWidth * (ax - bx) / len;
var lx0 = bx - bl0;
var ly0 = by - bl1;
var lx1 = ax - al0;
var ly1 = ay - al1;
var lx2 = bx + bl0;
var ly2 = by + bl1;
var lx3 = ax + al0;
var ly3 = ay + al1;
// tx0 = bottom right
var brX = calcMatrix.getX(lx0, ly0);
var brY = calcMatrix.getY(lx0, ly0);
// tx1 = bottom left
var blX = calcMatrix.getX(lx1, ly1);
var blY = calcMatrix.getY(lx1, ly1);
// tx2 = top right
var trX = calcMatrix.getX(lx2, ly2);
var trY = calcMatrix.getY(lx2, ly2);
// tx3 = top left
var tlX = calcMatrix.getX(lx3, ly3);
var tlY = calcMatrix.getY(lx3, ly3);
var tint = this.strokeTint;
var tintTL = tint.TL;
var tintTR = tint.TR;
var tintBL = tint.BL;
var tintBR = tint.BR;
// TL, BL, BR, TR
this.batchQuad(tlX, tlY, blX, blY, brX, brY, trX, trY, tintTL, tintTR, tintBL, tintBR);
if (lineWidth <= 2)
{
// No point doing a linejoin if the line isn't thick enough
return;
}
var prev = this.prevQuad;
var first = this.firstQuad;
if (index > 0 && prev[4])
{
this.batchQuad(tlX, tlY, blX, blY, prev[0], prev[1], prev[2], prev[3], tintTL, tintTR, tintBL, tintBR);
}
else
{
first[0] = tlX;
first[1] = tlY;
first[2] = blX;
first[3] = blY;
first[4] = 1;
}
if (closePath && first[4])
{
// Add a join for the final path segment
this.batchQuad(brX, brY, trX, trY, first[0], first[1], first[2], first[3], tintTL, tintTR, tintBL, tintBR);
}
else
{
// Store it
prev[0] = brX;
prev[1] = brY;
prev[2] = trX;
prev[3] = trY;
prev[4] = 1;
}
},
/**
* Adds a single vertex to the current vertex buffer and increments the
* `vertexCount` property by 1.
*
* This method is called directly by `batchTri` and `batchQuad`.
*
* It does not perform any batch limit checking itself, so if you need to call
* this method directly, do so in the same way that `batchQuad` does, for example.
*
* @method Phaser.Renderer.WebGL.Pipelines.GraphicsPipeline#batchVert
* @since 3.50.0
*
* @param {number} x - The vertex x position.
* @param {number} y - The vertex y position.
* @param {number} tint - The tint color value.
*/
batchVert: function (x, y, tint)
{
var vertexViewF32 = this.vertexViewF32;
var vertexViewU32 = this.vertexViewU32;
var vertexOffset = (this.vertexCount * this.currentShader.vertexComponentCount) - 1;
vertexViewF32[++vertexOffset] = x;
vertexViewF32[++vertexOffset] = y;
vertexViewU32[++vertexOffset] = tint;
this.vertexCount++;
},
/**
* Adds the vertices data into the batch and flushes if full.
*
* Assumes 6 vertices in the following arrangement:
*
* ```
* 0----3
* |\ B|
* | \ |
* | \ |
* | A \|
* | \
* 1----2
* ```
*
* Where tx0/ty0 = 0, tx1/ty1 = 1, tx2/ty2 = 2 and tx3/ty3 = 3
*
* @method Phaser.Renderer.WebGL.Pipelines.GraphicsPipeline#batchQuad
* @override
* @since 3.50.0
*
* @param {number} x0 - The top-left x position.
* @param {number} y0 - The top-left y position.
* @param {number} x1 - The bottom-left x position.
* @param {number} y1 - The bottom-left y position.
* @param {number} x2 - The bottom-right x position.
* @param {number} y2 - The bottom-right y position.
* @param {number} x3 - The top-right x position.
* @param {number} y3 - The top-right y position.
* @param {number} tintTL - The top-left tint color value.
* @param {number} tintTR - The top-right tint color value.
* @param {number} tintBL - The bottom-left tint color value.
* @param {number} tintBR - The bottom-right tint color value.
*
* @return {boolean} `true` if this method caused the batch to flush, otherwise `false`.
*/
batchQuad: function (x0, y0, x1, y1, x2, y2, x3, y3, tintTL, tintTR, tintBL, tintBR)
{
var hasFlushed = false;
if (this.shouldFlush(6))
{
this.flush();
hasFlushed = true;
}
this.batchVert(x0, y0, tintTL);
this.batchVert(x1, y1, tintBL);
this.batchVert(x2, y2, tintBR);
this.batchVert(x0, y0, tintTL);
this.batchVert(x2, y2, tintBR);
this.batchVert(x3, y3, tintTR);
return hasFlushed;
},
/**
* Adds the vertices data into the batch and flushes if full.
*
* Assumes 3 vertices in the following arrangement:
*
* ```
* 0
* |\
* | \
* | \
* | \
* | \
* 1-----2
* ```
*
* @method Phaser.Renderer.WebGL.Pipelines.GraphicsPipeline#batchTri
* @override
* @since 3.50.0
*
* @param {number} x1 - The bottom-left x position.
* @param {number} y1 - The bottom-left y position.
* @param {number} x2 - The bottom-right x position.
* @param {number} y2 - The bottom-right y position.
* @param {number} x3 - The top-right x position.
* @param {number} y3 - The top-right y position.
* @param {number} tintTL - The top-left tint color value.
* @param {number} tintTR - The top-right tint color value.
* @param {number} tintBL - The bottom-left tint color value.
*
* @return {boolean} `true` if this method caused the batch to flush, otherwise `false`.
*/
batchTri: function (x0, y0, x1, y1, x2, y2, tintTL, tintTR, tintBL)
{
var hasFlushed = false;
if (this.shouldFlush(3))
{
this.flush();
hasFlushed = true;
}
this.batchVert(x0, y0, tintTL);
this.batchVert(x1, y1, tintTR);
this.batchVert(x2, y2, tintBL);
return hasFlushed;
},
/**
* Destroys all shader instances, removes all object references and nulls all external references.
*
* @method Phaser.Renderer.WebGL.Pipelines.GraphicsPipeline#destroy
* @since 3.50.0
*
* @return {this} This WebGLPipeline instance.
*/
destroy: function ()
{
WebGLPipeline.prototype.destroy.call(this);
this.polygonCache = null;
return this;
}
});
module.exports = GraphicsPipeline;