Add Graphics rendering.

Lighting and independent Shapes are not in this commit.
This commit is contained in:
Ben Richards 2024-06-21 20:01:28 +12:00
parent aa69e4f8b3
commit 0569ecf73b
18 changed files with 1051 additions and 84 deletions

View file

@ -84,8 +84,8 @@ var Graphics = new Class({
Components.BlendMode,
Components.Depth,
Components.Mask,
Components.Pipeline,
Components.PostPipeline,
Components.RenderNode,
Components.Transform,
Components.Visible,
Components.ScrollFactor,
@ -102,7 +102,7 @@ var Graphics = new Class({
GameObject.call(this, scene, 'Graphics');
this.setPosition(x, y);
this.initPipeline();
this.initRenderNodes('Graphics');
this.initPostPipeline();
/**

View file

@ -6,8 +6,8 @@
var Commands = require('./Commands');
var GetCalcMatrix = require('../GetCalcMatrix');
var TransformMatrix = require('../components/TransformMatrix');
var Utils = require('../../renderer/webgl/Utils');
var TransformMatrix = require('../components/TransformMatrix');
var Point = function (x, y, width)
{
@ -16,15 +16,36 @@ var Point = function (x, y, width)
this.width = width;
};
var Path = function (x, y, width)
{
this.points = [];
this.pointsLength = 1;
this.points[0] = new Point(x, y, width);
this.addPoint = function (x, y, width)
{
var point = this.points[this.points.length - 1];
if (point.x === x && point.y === y)
{
return;
}
this.points.push(new Point(x, y, width));
};
};
var matrixStack = [];
var tempMatrix = new TransformMatrix();
var renderMatrix = new TransformMatrix();
var fillTint = { TL: 0, TR: 0, BL: 0, BR: 0 };
var strokeTint = { TL: 0, TR: 0, BL: 0, BR: 0 };
var trianglePath = [
{ x: 0, y: 0, width: 0 },
{ x: 0, y: 0, width: 0 },
{ x: 0, y: 0, width: 0 },
{ x: 0, y: 0, width: 0 }
];
/**
* Renders this Game Object with the WebGL Renderer to the given Camera.
@ -37,22 +58,24 @@ var tempMatrix = new TransformMatrix();
*
* @param {Phaser.Renderer.WebGL.WebGLRenderer} renderer - A reference to the current active WebGL renderer.
* @param {Phaser.GameObjects.Graphics} src - The Game Object being rendered in this call.
* @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera that is rendering the Game Object.
* @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
*/
var GraphicsWebGLRenderer = function (renderer, src, camera, parentMatrix)
var GraphicsWebGLRenderer = function (renderer, src, drawingContext, parentMatrix)
{
if (src.commandBuffer.length === 0)
{
return;
}
var customRenderNodes = src.customRenderNodes;
var defaultRenderNodes = src.defaultRenderNodes;
var currentContext = drawingContext;
var camera = currentContext.camera;
camera.addToRenderList(src);
var pipeline = renderer.pipelines.set(src.pipeline, src);
renderer.pipelines.preBatch(src);
var calcMatrix = GetCalcMatrix(src, camera, parentMatrix).calc;
var currentMatrix = tempMatrix.loadIdentity();
@ -61,8 +84,6 @@ var GraphicsWebGLRenderer = function (renderer, src, camera, parentMatrix)
var alpha = camera.alpha * src.alpha;
var lineWidth = 1;
var fillTint = pipeline.fillTint;
var strokeTint = pipeline.strokeTint;
var tx = 0;
var ty = 0;
@ -108,10 +129,15 @@ var GraphicsWebGLRenderer = function (renderer, src, camera, parentMatrix)
{
for (pathIndex = 0; pathIndex < path.length; pathIndex++)
{
pipeline.batchFillPath(
calcMatrix.multiply(currentMatrix, renderMatrix);
(customRenderNodes.FillPath || defaultRenderNodes.FillPath).run(
currentContext,
path[pathIndex].points,
currentMatrix,
calcMatrix
renderMatrix,
fillTint.TL,
fillTint.TR,
fillTint.BL
);
}
break;
@ -121,12 +147,18 @@ var GraphicsWebGLRenderer = function (renderer, src, camera, parentMatrix)
{
for (pathIndex = 0; pathIndex < path.length; pathIndex++)
{
pipeline.batchStrokePath(
calcMatrix.multiply(currentMatrix, renderMatrix);
(customRenderNodes.StrokePath || defaultRenderNodes.StrokePath).run(
currentContext,
path[pathIndex].points,
lineWidth,
pathOpen,
currentMatrix,
calcMatrix
renderMatrix,
strokeTint.TL,
strokeTint.TR,
strokeTint.BL,
strokeTint.BR
);
}
break;
@ -228,7 +260,7 @@ var GraphicsWebGLRenderer = function (renderer, src, camera, parentMatrix)
tx = x + Math.cos(ta) * radius;
ty = y + Math.sin(ta) * radius;
lastPath.points.push(new Point(tx, ty, lineWidth));
lastPath.addPoint(tx, ty, lineWidth);
iteration += iterStep;
}
@ -237,64 +269,98 @@ var GraphicsWebGLRenderer = function (renderer, src, camera, parentMatrix)
tx = x + Math.cos(ta) * radius;
ty = y + Math.sin(ta) * radius;
lastPath.points.push(new Point(tx, ty, lineWidth));
lastPath.addPoint(tx, ty, lineWidth);
break;
}
case Commands.FILL_RECT:
{
pipeline.batchFillRect(
calcMatrix.multiply(currentMatrix, renderMatrix);
(customRenderNodes.FillRect || defaultRenderNodes.FillRect).run(
currentContext,
renderMatrix,
commands[++cmdIndex],
commands[++cmdIndex],
commands[++cmdIndex],
commands[++cmdIndex],
currentMatrix,
calcMatrix
fillTint.TL,
fillTint.TR,
fillTint.BL,
fillTint.BR
);
break;
}
case Commands.FILL_TRIANGLE:
{
pipeline.batchFillTriangle(
calcMatrix.multiply(currentMatrix, renderMatrix);
(customRenderNodes.FillTri || defaultRenderNodes.FillTri).run(
currentContext,
renderMatrix,
commands[++cmdIndex],
commands[++cmdIndex],
commands[++cmdIndex],
commands[++cmdIndex],
commands[++cmdIndex],
commands[++cmdIndex],
currentMatrix,
calcMatrix
fillTint.TL,
fillTint.TR,
fillTint.BL
);
break;
}
case Commands.STROKE_TRIANGLE:
{
pipeline.batchStrokeTriangle(
commands[++cmdIndex],
commands[++cmdIndex],
commands[++cmdIndex],
commands[++cmdIndex],
commands[++cmdIndex],
commands[++cmdIndex],
calcMatrix.multiply(currentMatrix, renderMatrix);
trianglePath[0].x = commands[++cmdIndex];
trianglePath[0].y = commands[++cmdIndex];
trianglePath[0].width = lineWidth;
trianglePath[1].x = commands[++cmdIndex];
trianglePath[1].y = commands[++cmdIndex];
trianglePath[1].width = lineWidth;
trianglePath[2].x = commands[++cmdIndex];
trianglePath[2].y = commands[++cmdIndex];
trianglePath[2].width = lineWidth;
trianglePath[3].x = trianglePath[0].x;
trianglePath[3].y = trianglePath[0].y;
trianglePath[3].width = lineWidth;
(customRenderNodes.StrokePath || defaultRenderNodes.StrokePath).run(
currentContext,
trianglePath,
lineWidth,
currentMatrix,
calcMatrix
false,
renderMatrix,
strokeTint.TL,
strokeTint.TR,
strokeTint.BL,
strokeTint.BR
);
break;
}
case Commands.LINE_TO:
{
x = commands[++cmdIndex];
y = commands[++cmdIndex];
if (lastPath !== null)
{
lastPath.points.push(new Point(commands[++cmdIndex], commands[++cmdIndex], lineWidth));
lastPath.addPoint(x, y, lineWidth);
}
else
{
lastPath = new Path(commands[++cmdIndex], commands[++cmdIndex], lineWidth);
lastPath = new Path(x, y, lineWidth);
path.push(lastPath);
}
break;
@ -342,8 +408,6 @@ var GraphicsWebGLRenderer = function (renderer, src, camera, parentMatrix)
}
}
}
renderer.pipelines.postBatch(src);
};
module.exports = GraphicsWebGLRenderer;

View file

@ -2976,7 +2976,7 @@ var WebGLRenderer = new Class({
* Draw a number of vertices to a drawing context.
*
* This draws the vertices using an index buffer. The buffer should be
* bound to the VAO. Vertices are drawn as a `TRIANGLE_STRIP`.
* bound to the VAO. Vertices are drawn as a `TRIANGLE_STRIP` by default.
*
* This is the primary render method. It requires all the WebGL resources
* necessary to render the vertices, so they don't have to be set up
@ -2993,8 +2993,9 @@ var WebGLRenderer = new Class({
* @param {Phaser.Renderer.WebGL.Wrappers.WebGLVAOWrapper} vao - The Vertex Array Object to bind. It must have an index buffer attached.
* @param {number} count - The number of vertices to draw. Because of the TRIANGLE_STRIP topology, this should be `n + 2`, where `n` is the number of triangles to draw, including degenerate triangles.
* @param {number} offset - The offset to start drawing from in the index buffer. This is in bytes, and should be a multiple of 2 (for 16-bit `UNSIGNED_SHORT` indices).
* @param {number} topology - The type of primitives to render. Defaults to `TRIANGLE_STRIP`.
*/
drawElements: function (drawingContext, textures, program, vao, count, offset)
drawElements: function (drawingContext, textures, program, vao, count, offset, topology)
{
var gl = this.gl;
@ -3006,7 +3007,7 @@ var WebGLRenderer = new Class({
this.glTextureUnits.bindUnits(textures);
gl.drawElements(gl.TRIANGLE_STRIP, count, gl.UNSIGNED_SHORT, offset);
gl.drawElements(topology || gl.TRIANGLE_STRIP, count, gl.UNSIGNED_SHORT, offset);
},
/**

View file

@ -0,0 +1,213 @@
/**
* @author Benjamin D. Richards <benjamindrichards@gmail.com>
* @copyright 2013-2024 Phaser Studio Inc.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var Class = require('../../../utils/Class');
var ShaderSourceFS = require('../shaders/Flat-frag');
var ShaderSourceVS = require('../shaders/Flat-vert');
var BatchHandler = require('./BatchHandler');
/**
* @classdesc
* This render node draws triangles with vertex color in batches.
*
* @class BatchHandlerTriFlat
* @extends Phaser.Renderer.WebGL.Batch.BatchHandler
* @memberof Phaser.Renderer.WebGL.Batch
* @constructor
* @since 3.90.0
* @param {Phaser.Renderer.WebGL.WebGLPipeline} manager - The pipeline manager this BatchRenderer belongs to.
* @param {Phaser.Types.Renderer.WebGL.WebGLPipelineBatchConfig} config - The configuration object for this BatchRenderer.
*/
var BatchHandlerTriFlat = new Class({
Extends: BatchHandler,
initialize: function BatchHandlerTriFlat (manager, config)
{
BatchHandler.call(this, manager, config, this.defaultConfig);
/**
* An empty array. This is an internal space filler.
*
* @name Phaser.Renderer.WebGL.RenderNodes.BatchHandlerTriFlat#_emptyTextures
* @type {Array}
* @private
* @since 3.90.0
* @default []
* @readonly
*/
this._emptyTextures = [];
},
defaultConfig: {
name: 'BatchHandlerTriFlat',
verticesPerInstance: 3,
indicesPerInstance: 3,
vertexSource: ShaderSourceVS,
fragmentSource: ShaderSourceFS,
vertexBufferLayout: {
usage: 'DYNAMIC_DRAW',
layout: [
{
name: 'inPosition',
size: 2
},
{
name: 'inTint',
size: 4,
type: 'UNSIGNED_BYTE',
normalized: true
}
]
}
},
/**
* Generate element indices for the instance vertices.
* This is called automatically when the node is initialized.
*
* By default, each instance is a triangle.
* The triangle is drawn with TRIANGLES topology,
* so the vertices are in the order 0, 1, 2,
* and each instance is fully separate.
*
* @method Phaser.Renderer.WebGL.RenderNodes.BatchHandlerTriFlat#_generateElementIndices
* @since 3.90.0
* @private
* @param {number} instances - The number of instances to define.
* @return {ArrayBuffer} The index buffer data.
*/
_generateElementIndices: function (instances)
{
var buffer = new ArrayBuffer(instances * 3 * 2);
var indices = new Uint16Array(buffer);
var offset = 0;
for (var i = 0; i < instances; i++)
{
var index = i * 3;
indices[offset++] = index;
indices[offset++] = index + 1;
indices[offset++] = index + 2;
}
return buffer;
},
/**
* Set new dimensions for the renderer. This is called automatically when the renderer is resized.
*
* @method Phaser.Renderer.WebGL.RenderNodes.BatchHandlerTriFlat#resize
* @since 3.90.0
*/
resize: function ()
{
this.program.setUniform('uProjectionMatrix', this.manager.renderer.projectionMatrix.val);
},
/**
* Draw then empty the current batch.
*
* This method is called automatically, by either this node or the manager,
* when the batch is full, or when something else needs to be rendered.
*
* @method Phaser.Renderer.WebGL.RenderNodes.BatchHandlerTriFlat#run
* @since 3.90.0
* @param {Phaser.Types.Renderer.WebGL.DrawingContext} drawingContext - The current drawing context.
*/
run: function (drawingContext)
{
if (this.instanceCount === 0) { return; }
this.onRunBegin(drawingContext);
var indicesPerInstance = this.indicesPerInstance;
var instanceCount = this.instanceCount;
var program = this.program;
var vao = this.vao;
var renderer = this.manager.renderer;
var vertexBuffer = this.vertexBufferLayout.buffer;
// Update vertex buffers.
// Because we are probably using a generic vertex buffer
// which is larger than the current batch, we need to update
// the buffer with the correct size.
vertexBuffer.update(instanceCount * this.bytesPerInstance);
renderer.drawElements(
drawingContext,
this._emptyTextures,
program,
vao,
instanceCount * indicesPerInstance,
0,
renderer.gl.TRIANGLES
);
// Reset batch accumulation.
this.instanceCount = 0;
this.onRunEnd(drawingContext);
},
/**
* Add a triangle to the batch.
*
* The vertices are not textured, and are named A, B, and C.
*
* @method Phaser.Renderer.WebGL.RenderNodes.BatchHandlerTriFlat#batch
* @since 3.90.0
* @param {Phaser.Types.Renderer.WebGL.DrawingContext} currentContext - The current drawing context.
* @param {number} xA - The x-coordinate of vertex A.
* @param {number} yA - The y-coordinate of vertex A.
* @param {number} xB - The x-coordinate of vertex B.
* @param {number} yB - The y-coordinate of vertex B.
* @param {number} xC - The x-coordinate of vertex C.
* @param {number} yC - The y-coordinate of vertex C.
* @param {number} tintA - The vertex A tint color.
* @param {number} tintB - The vertex B tint color.
* @param {number} tintC - The vertex C tint color.
*/
batch: function (currentContext, xA, yA, xB, yB, xC, yC, tintA, tintB, tintC)
{
if (this.instanceCount === 0)
{
this.manager.setCurrentBatchNode(this, currentContext);
}
// Update the vertex buffer.
var vertexOffset32 = this.instanceCount * this.floatsPerInstance;
var vertexBuffer = this.vertexBufferLayout.buffer;
var vertexViewF32 = vertexBuffer.viewF32;
var vertexViewU32 = vertexBuffer.viewU32;
// Vertex A
vertexViewF32[vertexOffset32++] = xA;
vertexViewF32[vertexOffset32++] = yA;
vertexViewU32[vertexOffset32++] = tintA;
// Vertex B
vertexViewF32[vertexOffset32++] = xB;
vertexViewF32[vertexOffset32++] = yB;
vertexViewU32[vertexOffset32++] = tintB;
// Vertex C
vertexViewF32[vertexOffset32++] = xC;
vertexViewF32[vertexOffset32++] = yC;
vertexViewU32[vertexOffset32++] = tintC;
// Increment the instance count.
this.instanceCount++;
// Check whether the batch should be rendered immediately.
// This guarantees that none of the arrays are full above.
if (this.instanceCount === this.instancesPerBatch)
{
this.run(currentContext);
// Now the batch is empty.
}
}
});
module.exports = BatchHandlerTriFlat;

View file

@ -0,0 +1,262 @@
/**
* @author Benjamin D. Richards <benjamindrichards@gmail.com>
* @copyright 2013-2024 Phaser Studio Inc.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var Class = require('../../../utils/Class');
var RenderNode = require('./RenderNode');
/**
* @classdesc
* A RenderNode which renders a line segment.
*
* @class DrawLine
* @memberof Phaser.Renderer.WebGL.RenderNodes
* @constructor
* @since 3.90.0
* @extends Phaser.Renderer.WebGL.RenderNodes.RenderNode
* @param {Phaser.Renderer.WebGL.RenderNodes.RenderNodeManager} manager - The manager that owns this RenderNode.
*/
var DrawLine = new Class({
Extends: RenderNode,
initialize: function DrawLine (manager)
{
RenderNode.call(this, 'DrawLine', manager);
/**
* The render node that handles the rendering of triangles.
*
* @name Phaser.Renderer.WebGL.RenderNodes.DrawLine#batchHandler
* @type {Phaser.Renderer.WebGL.RenderNodes.BatchHandlerTriFlat}
* @since 3.90.0
*/
this.batchHandler = this.manager.getNode('BatchHandlerTriFlat');
/**
* The top and bottom vertices of the start of the first line in a loop.
*
* These values have been pre-transformed to save on redundant calculations.
*
* @name Phaser.Renderer.WebGL.RenderNodes.DrawLine#start
* @type {number[]}
* @since 3.90.0
*/
this.start = [ 0, 0, 0, 0 ];
/**
* The top and bottom vertices of the end of the last line in a loop.
*
* These values have been pre-transformed to save on redundant calculations.
*
* @name Phaser.Renderer.WebGL.RenderNodes.DrawLine#previous
* @type {number[]}
* @since 3.90.0
*/
this.previous = [ 0, 0, 0, 0 ];
},
/**
* Constant that defines the first line in a loop.
* If the line is not open, this is cached,
* and will join to the last line in the loop.
*
* @name Phaser.Renderer.WebGL.RenderNodes.DrawLine#FIRST
* @type {number}
* @const
* @since 3.90.0
* @default 1
* @readonly
*/
FIRST: 1,
/**
* Constant that defines the last line in a loop.
* This joins to the previous line in the loop.
* If the line is not open, this also joins to the first line in the loop.
*
* @name Phaser.Renderer.WebGL.RenderNodes.DrawLine#LAST
* @type {number}
* @const
* @since 3.90.0
* @default 2
* @readonly
*/
LAST: 2,
/**
* Constant that defines a single line with no joins.
*
* @name Phaser.Renderer.WebGL.RenderNodes.DrawLine#SINGLE
* @type {number}
* @const
* @since 3.90.0
* @default 3
* @readonly
*/
SINGLE: 3,
/**
* Render a line segment as a quad.
*
* @method Phaser.Renderer.WebGL.RenderNodes.DrawLine#run
* @since 3.90.0
* @param {Phaser.Renderer.WebGL.DrawingContext} drawingContext - The context currently in use.
* @param {Phaser.GameObjects.Components.TransformMatrix} [currentMatrix] - A transform matrix to apply to the vertices. If not defined, the vertices are not transformed.
* @param {number} ax - The x coordinate of the start of the line.
* @param {number} ay - The y coordinate of the start of the line.
* @param {number} bx - The x coordinate of the end of the line.
* @param {number} by - The y coordinate of the end of the line.
* @param {number} aLineWidth - The width of the line at the start.
* @param {number} bLineWidth - The width of the line at the end.
* @param {number} lineWidth - The width of the line.
* @param {number} tintTL - The top-left tint color.
* @param {number} tintTR - The top-right tint color.
* @param {number} tintBL - The bottom-left tint color.
* @param {number} tintBR - The bottom-right tint color.
* @param {this.FIRST|this.LAST|this.SINGLE} [connection] - The connection type. If omitted, the line start joins to the previous line.
*/
run: function (drawingContext, currentMatrix, ax, ay, bx, by, aLineWidth, bLineWidth, lineWidth, tintTL, tintTR, tintBL, tintBR, connection)
{
this.onRunBegin(drawingContext);
var dx = bx - ax;
var dy = by - ay;
var len = Math.sqrt(dx * dx + dy * dy);
if (len === 0)
{
// Because we cannot (and should not) divide by zero!
return;
}
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;
var brX, brY, blX, blY, trX, trY, tlX, tlY;
if (currentMatrix)
{
// Bottom right
brX = currentMatrix.getX(lx0, ly0);
brY = currentMatrix.getY(lx0, ly0);
// Bottom left
blX = currentMatrix.getX(lx1, ly1);
blY = currentMatrix.getY(lx1, ly1);
// Top right
trX = currentMatrix.getX(lx2, ly2);
trY = currentMatrix.getY(lx2, ly2);
// Top left
tlX = currentMatrix.getX(lx3, ly3);
tlY = currentMatrix.getY(lx3, ly3);
}
else
{
brX = lx0;
brY = ly0;
blX = lx1;
blY = ly1;
trX = lx2;
trY = ly2;
tlX = lx3;
tlY = ly3;
}
// Draw the line
this.batchHandler.batch(
drawingContext,
tlX, tlY,
blX, blY,
brX, brY,
tintTL, tintBL, tintBR
);
this.batchHandler.batch(
drawingContext,
brX, brY,
trX, trY,
tlX, tlY,
tintBR, tintTR, tintTL
);
if (connection !== this.SINGLE)
{
var start = this.start;
var previous = this.previous;
if (connection === this.FIRST)
{
start[0] = tlX;
start[1] = tlY;
start[2] = blX;
start[3] = blY;
}
else if (lineWidth > 2)
{
// No point doing a linejoin if the line isn't thick enough
// Connect to previous line
this.batchHandler.batch(
drawingContext,
tlX, tlY,
blX, blY,
previous[2], previous[3],
tintTL, tintBL, tintBR
);
this.batchHandler.batch(
drawingContext,
previous[2], previous[3],
previous[0], previous[1],
tlX, tlY,
tintBR, tintTR, tintTL
);
if (connection === this.LAST)
{
// Connect to first line
this.batchHandler.batch(
drawingContext,
brX, brY,
trX, trY,
start[0], start[1],
tintTL, tintBL, tintBR
);
this.batchHandler.batch(
drawingContext,
start[0], start[1],
start[2], start[3],
brX, brY,
tintBR, tintTR, tintTL
);
}
}
if (connection !== this.LAST)
{
previous[0] = trX;
previous[1] = trY;
previous[2] = brX;
previous[3] = brY;
}
}
this.onRunEnd(drawingContext);
}
});
module.exports = DrawLine;

View file

@ -54,7 +54,7 @@ var FillCamera = new Class({
var cw = camera.width;
var ch = camera.height;
this.fillRectNode.run(drawingContext, null, cx, cy, cw, ch, color, color, color, color, 2);
this.fillRectNode.run(drawingContext, null, cx, cy, cw, ch, color, color, color, color);
this.onRunEnd(drawingContext);
}

View file

@ -0,0 +1,114 @@
/**
* @author Benjamin D. Richards <benjamindrichards@gmail.com>
* @copyright 2013-2024 Phaser Studio Inc.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var Earcut = require('../../../geom/polygon/Earcut');
var Class = require('../../../utils/Class');
var RenderNode = require('./RenderNode');
/**
* @classdesc
* A RenderNode which fills a path.
*
* 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 polygons are triangles, but they're rendered as quads
* to be compatible with other batched quads.
*
* @class FillPath
* @memberof Phaser.Renderer.WebGL.RenderNodes
* @constructor
* @since 3.90.0
* @extends Phaser.Renderer.WebGL.RenderNodes.RenderNode
* @param {Phaser.Renderer.WebGL.RenderNodes.RenderNodeManager} manager - The manager that owns this RenderNode.
*/
var FillPath = new Class({
Extends: RenderNode,
initialize: function FillPath (manager)
{
RenderNode.call(this, 'FillPath', manager);
/**
* The RenderNode used to render polygons.
* By default, we use `FillTri`.
*
* @name Phaser.Renderer.WebGL.RenderNodes.FillPath#polygonNode
* @type {Phaser.Renderer.WebGL.RenderNodes.FillTri}
* @since 3.90.0
*/
this.polygonNode = this.manager.getNode('FillTri');
/**
* Used internally for triangulating a polygon.
*
* @name Phaser.Renderer.WebGL.RenderNodes.FillPath#polygonCache
* @type {number[]}
* @since 3.90.0
*/
this.polygonCache = [];
},
/**
* Render the path using Earcut.
*
* @method Phaser.Renderer.WebGL.RenderNodes.FillPath#run
* @since 3.90.0
* @param {Phaser.Renderer.WebGL.DrawingContext} drawingContext - The context currently in use.
* @param {{ x: number, y: number, width: number }[]} path - The points that define the line segments.
* @param {Phaser.GameObjects.Components.TransformMatrix} currentMatrix - The current transform matrix.
* @param {number} tintTL - The top-left tint color.
* @param {number} tintTR - The top-right tint color.
* @param {number} tintBL - The bottom-left tint color.
*/
run: function (drawingContext, path, currentMatrix, tintTL, tintTR, tintBL)
{
this.onRunBegin(drawingContext);
var length = path.length;
var polygonCache = this.polygonCache;
var polygonIndexArray;
var point;
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];
this.polygonNode.run(
drawingContext,
currentMatrix,
x0, y0,
x1, y1,
x2, y2,
tintTL, tintTR, tintBL
);
}
polygonCache.length = 0;
this.onRunEnd(drawingContext);
}
});
module.exports = FillPath;

View file

@ -13,9 +13,6 @@ var RenderNode = require('./RenderNode');
* A RenderNode which renders a filled rectangle.
* This is useful for full-screen effects and rectangle geometry.
*
* It works by drawing a tinted white texture. This can take advantage of
* the WebGL renderer's batching capabilities.
*
* @class FillRect
* @memberof Phaser.Renderer.WebGL.RenderNodes
* @constructor
@ -31,13 +28,13 @@ var FillRect = new Class({
RenderNode.call(this, 'FillRect', manager);
/**
* The BatchHandlerQuad that handles the rendering of quads.
* The RenderNode that handles the rendering of quads.
*
* @name Phaser.Renderer.WebGL.RenderNodes.FillRect#BatchHandlerQuadNode
* @type {Phaser.Renderer.WebGL.RenderNodes.BatchHandlerQuad}
* @name Phaser.Renderer.WebGL.RenderNodes.FillRect#batchHandler
* @type {Phaser.Renderer.WebGL.RenderNodes.BatchHandlerTriFlat}
* @since 3.90.0
*/
this.BatchHandlerQuadNode = this.manager.getNode('BatchHandlerQuad');
this.batchHandler = this.manager.getNode('BatchHandlerTriFlat');
/**
* An unchanging identity matrix.
@ -47,14 +44,6 @@ var FillRect = new Class({
* @private
*/
this._identityMatrix = new TransformMatrix();
/**
* Temporary matrix for calculating the screen space.
*
* @name Phaser.Renderer.WebGL.RenderNodes.FillRect#_calcMatrix
* @type {Phaser.GameObjects.Components.TransformMatrix}
*/
this._calcMatrix = new TransformMatrix();
},
/**
@ -63,7 +52,7 @@ var FillRect = new Class({
* @method Phaser.Renderer.WebGL.RenderNodes.FillRect#run
* @since 3.90.0
* @param {Phaser.Renderer.WebGL.DrawingContext} drawingContext - The context currently in use.
* @param {Phaser.GameObjects.Components.TransformMatrix} [parentMatrix] - This transform matrix is defined if the game object is nested.
* @param {Phaser.GameObjects.Components.TransformMatrix} [currentMatrix] - A transform matrix to apply to the vertices. If not defined, the identity matrix is used.
* @param {number} x - The x-coordinate of the rectangle.
* @param {number} y - The y-coordinate of the rectangle.
* @param {number} width - The width of the rectangle.
@ -72,45 +61,32 @@ var FillRect = new Class({
* @param {number} tintTR - The top-right tint color.
* @param {number} tintBL - The bottom-left tint color.
* @param {number} tintBR - The bottom-right tint color.
* @param {number|boolean} tintFill - The tint effect for the shader to use.
* @param {boolean} [inWorldSpace] - Is this in world space? By default, it's in screen space.
*/
run: function (drawingContext, parentMatrix, x, y, width, height, tintTL, tintTR, tintBL, tintBR, tintFill, inWorldSpace)
run: function (drawingContext, currentMatrix, x, y, width, height, tintTL, tintTR, tintBL, tintBR)
{
this.onRunBegin(drawingContext);
var currentMatrix = this._identityMatrix;
if (inWorldSpace)
if (!currentMatrix)
{
currentMatrix = drawingContext.camera.matrix;
}
if (parentMatrix)
{
parentMatrix.multiply(currentMatrix, this._calcMatrix);
currentMatrix = this._calcMatrix;
currentMatrix = this._identityMatrix;
}
var quad = currentMatrix.setQuad(x, y, x + width, y + height);
this.BatchHandlerQuadNode.batch(
this.batchHandler.batch(
drawingContext,
this.manager.renderer.whiteTexture,
// Quad vertices in TRIANGLE_STRIP order:
quad[0], quad[1],
quad[2], quad[3],
quad[6], quad[7],
quad[4], quad[5],
// Texture coordinates in X, Y, Width, Height:
0, 0, 1, 1,
tintTL, tintBL, tintBR
);
tintFill,
// Tint colors in TRIANGLE_STRIP order:
tintTL, tintTR, tintBL, tintBR
this.batchHandler.batch(
drawingContext,
quad[4], quad[5],
quad[6], quad[7],
quad[0], quad[1],
tintBR, tintTR, tintTL
);
this.onRunEnd(drawingContext);

View file

@ -0,0 +1,87 @@
/**
* @author Benjamin D. Richards <benjamindrichards@gmail.com>
* @copyright 2013-2024 Phaser Studio Inc.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var Class = require('../../../utils/Class');
var RenderNode = require('./RenderNode');
/**
* @classdesc
* A RenderNode which renders a filled triangle.
* This is useful for arbitrary geometry.
* This does not use textures, and is intended to form part of a larger batch.
*
* @class FillTri
* @memberof Phaser.Renderer.WebGL.RenderNodes
* @constructor
* @since 3.90.0
* @extends Phaser.Renderer.WebGL.RenderNodes.RenderNode
* @param {Phaser.Renderer.WebGL.RenderNodes.RenderNodeManager} manager - The manager that owns this RenderNode.
*/
var FillTri = new Class({
Extends: RenderNode,
initialize: function FillTri (manager)
{
RenderNode.call(this, 'FillTri', manager);
/**
* The BatchHandlerQuad that handles the rendering of quads.
*
* @name Phaser.Renderer.WebGL.RenderNodes.FillTri#batchHandler
* @type {Phaser.Renderer.WebGL.RenderNodes.BatchHandlerTriFlat}
* @since 3.90.0
*/
this.batchHandler = this.manager.getNode('BatchHandlerTriFlat');
},
/**
* Render the triangle.
*
* @method Phaser.Renderer.WebGL.RenderNodes.FillTri#run
* @since 3.90.0
* @param {Phaser.Renderer.WebGL.DrawingContext} drawingContext - The context currently in use.
* @param {Phaser.GameObjects.Components.TransformMatrix} [currentMatrix] - A transform matrix to apply to the vertices. If not defined, the vertices are not transformed.
*/
run: function (drawingContext, currentMatrix, xA, yA, xB, yB, xC, yC, tintA, tintB, tintC)
{
this.onRunBegin(drawingContext);
var txA, tyA, txB, tyB, txC, tyC;
if (currentMatrix)
{
txA = currentMatrix.getX(xA, yA);
tyA = currentMatrix.getY(xA, yA);
txB = currentMatrix.getX(xB, yB);
tyB = currentMatrix.getY(xB, yB);
txC = currentMatrix.getX(xC, yC);
tyC = currentMatrix.getY(xC, yC);
}
else
{
txA = xA;
tyA = yA;
txB = xB;
tyB = yB;
txC = xC;
tyC = yC;
}
this.batchHandler.batch(
drawingContext,
txA, tyA,
txB, tyB,
txC, tyC,
tintA, tintB, tintC
);
this.onRunEnd(drawingContext);
}
});
module.exports = FillTri;

View file

@ -9,17 +9,23 @@ var Class = require('../../../utils/Class');
var Events = require('../../events');
var DefaultBlitterNodes = require('./defaults/DefaultBlitterNodes');
var DefaultGraphicsNodes = require('./defaults/DefaultGraphicsNodes');
var DefaultImageNodes = require('./defaults/DefaultImageNodes');
var DefaultNineSliceNodes = require('./defaults/DefaultNineSliceNodes');
var DefaultParticleEmitterNodes = require('./defaults/DefaultParticleEmitterNodes');
var BatchHandlerQuad = require('./BatchHandlerQuad');
var BatchHandlerQuadLight = require('./BatchHandlerQuadLight');
var BatchHandlerTriFlat = require('./BatchHandlerTriFlat');
var Camera = require('./Camera');
var DrawLine = require('./DrawLine');
var FillCamera = require('./FillCamera');
var FillPath = require('./FillPath');
var FillRect = require('./FillRect');
var FillTri = require('./FillTri');
var ListCompositor = require('./ListCompositor');
var RebindContext = require('./RebindContext');
var StrokePath = require('./StrokePath');
var SubmitterQuad = require('./submitter/SubmitterQuad');
var SubmitterQuadLight = require('./submitter/SubmitterQuadLight');
var TexturerImage = require('./texturer/TexturerImage');
@ -91,6 +97,7 @@ var RenderNodeManager = new Class({
*/
this.defaultRenderNodes = {
Blitter: DefaultBlitterNodes,
Graphics: DefaultGraphicsNodes,
Image: DefaultImageNodes,
NineSlice: DefaultNineSliceNodes,
ParticleEmitter: DefaultParticleEmitterNodes
@ -127,11 +134,16 @@ var RenderNodeManager = new Class({
this._nodeConstructors = {
BatchHandlerQuad: BatchHandlerQuad,
BatchHandlerQuadLight: BatchHandlerQuadLight,
BatchHandlerTriFlat: BatchHandlerTriFlat,
Camera: Camera,
DrawLine: DrawLine,
FillCamera: FillCamera,
FillPath: FillPath,
FillRect: FillRect,
FillTri: FillTri,
ListCompositor: ListCompositor,
RebindContext: RebindContext,
StrokePath: StrokePath,
SubmitterQuad: SubmitterQuad,
SubmitterQuadLight: SubmitterQuadLight,
TexturerImage: TexturerImage,

View file

@ -0,0 +1,156 @@
/**
* @author Benjamin D. Richards <benjamindrichards@gmail.com>
* @copyright 2013-2024 Phaser Studio Inc.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var Class = require('../../../utils/Class');
var RenderNode = require('./RenderNode');
/**
* @classdesc
* A RenderNode which renders a stroke path consisting of several line segments,
* potentially closed at the end.
*
* @class StrokePath
* @memberof Phaser.Renderer.WebGL.RenderNodes
* @constructor
* @since 3.90.0
* @extends Phaser.Renderer.WebGL.RenderNodes.RenderNode
* @param {Phaser.Renderer.WebGL.RenderNodes.RenderNodeManager} manager - The manager that owns this RenderNode.
*/
var StrokePath = new Class({
Extends: RenderNode,
initialize: function StrokePath (manager)
{
RenderNode.call(this, 'StrokePath', manager);
/**
* The RenderNode that draws a line segment.
*
* @name Phaser.Renderer.WebGL.RenderNodes.StrokePath#drawLineNode
* @type {Phaser.Renderer.WebGL.RenderNodes.DrawLine}
* @since 3.90.0
*/
this.drawLineNode = this.manager.getNode('DrawLine');
},
/**
* Render a stroke path consisting of several line segments.
*
* @method Phaser.Renderer.WebGL.RenderNodes.StrokePath#run
* @since 3.90.0
* @param {Phaser.Renderer.WebGL.DrawingContext} drawingContext - The context currently in use.
* @param {{ x: number, y: number, width: number }[]} path - The points that define the line segments.
* @param {number} lineWidth - The width of the stroke.
* @param {boolean} open - Whether the stroke is open or closed.
* @param {Phaser.GameObjects.Components.TransformMatrix} currentMatrix - The current transform matrix.
* @param {number} tintTL - The top-left tint color.
* @param {number} tintTR - The top-right tint color.
* @param {number} tintBL - The bottom-left tint color.
* @param {number} tintBR - The bottom-right tint color.
*/
run: function (drawingContext, path, lineWidth, open, currentMatrix, tintTL, tintTR, tintBL, tintBR)
{
this.onRunBegin(drawingContext);
var drawLineNode = this.drawLineNode;
var pathLength = path.length - 1;
var pathIndex, point, nextPoint;
if (pathLength === 1)
{
// Only one point, draw a single line
point = path[0];
nextPoint = path[1];
drawLineNode.run(
drawingContext,
currentMatrix,
point.x,
point.y,
nextPoint.x,
nextPoint.y,
point.width / 2,
nextPoint.width / 2,
lineWidth,
tintTL,
tintTR,
tintBL,
tintBR,
drawLineNode.SINGLE
);
}
else if (pathLength > 1)
{
point = path[0];
nextPoint = path[1];
drawLineNode.run(
drawingContext,
currentMatrix,
point.x,
point.y,
nextPoint.x,
nextPoint.y,
point.width / 2,
nextPoint.width / 2,
lineWidth,
tintTL,
tintTR,
tintBL,
tintBR,
drawLineNode.FIRST
);
for (pathIndex = 1; pathIndex < pathLength - 1; pathIndex++)
{
point = path[pathIndex];
nextPoint = path[pathIndex + 1];
drawLineNode.run(
drawingContext,
currentMatrix,
point.x,
point.y,
nextPoint.x,
nextPoint.y,
point.width / 2,
nextPoint.width / 2,
lineWidth,
tintTL,
tintTR,
tintBL,
tintBR
);
}
point = path[pathLength - 1];
nextPoint = path[pathLength];
drawLineNode.run(
drawingContext,
currentMatrix,
point.x,
point.y,
nextPoint.x,
nextPoint.y,
point.width / 2,
nextPoint.width / 2,
lineWidth,
tintTL,
tintTR,
tintBL,
tintBR,
open ? undefined : drawLineNode.LAST
);
}
this.onRunEnd(drawingContext);
}
});
module.exports = StrokePath;

View file

@ -0,0 +1,18 @@
/**
* @author Benjamin D. Richards <benjamindrichards@gmail.com>
* @copyright 2013-2024 Phaser Studio Inc.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var Map = require('../../../../structs/Map');
var DefaultGraphicsNodes = new Map([
[ 'BatchHandlerTriFlat', 'BatchHandlerTriFlat' ],
// [ 'SubmitterLight', 'SubmitterQuadLight' ],
[ 'FillPath', 'FillPath' ],
[ 'FillRect', 'FillRect' ],
[ 'FillTri', 'FillTri' ],
[ 'StrokePath', 'StrokePath' ]
]);
module.exports = DefaultGraphicsNodes;

View file

@ -12,12 +12,17 @@ var RenderNodes = {
BatchHandler: require('./BatchHandler'),
BatchHandlerQuad: require('./BatchHandlerQuad'),
BatchHandlerQuadLight: require('./BatchHandlerQuadLight'),
BatchHandlerTriFlat: require('./BatchHandlerTriFlat'),
Camera: require('./Camera'),
DrawLine: require('./DrawLine'),
FillCamera: require('./FillCamera'),
FillPath: require('./FillPath'),
FillRect: require('./FillRect'),
FillTri: require('./FillTri'),
ListCompositor: require('./ListCompositor'),
RebindContext: require('./RebindContext'),
RenderNode: require('./RenderNode'),
StrokePath: require('./StrokePath'),
SubmitterQuad: require('./submitter/SubmitterQuad'),
SubmitterQuadLight: require('./submitter/SubmitterQuadLight'),
TexturerImage: require('./texturer/TexturerImage'),

View file

@ -0,0 +1,13 @@
module.exports = [
'#define SHADER_NAME PHASER_FLAT_FS',
'#ifdef GL_FRAGMENT_PRECISION_HIGH',
'precision highp float;',
'#else',
'precision mediump float;',
'#endif',
'varying vec4 outTint;',
'void main ()',
'{',
' gl_FragColor = outTint;',
'}',
].join('\n');

View file

@ -0,0 +1,13 @@
module.exports = [
'#define SHADER_NAME PHASER_FLAT_VS',
'precision mediump float;',
'uniform mat4 uProjectionMatrix;',
'attribute vec2 inPosition;',
'attribute vec4 inTint;',
'varying vec4 outTint;',
'void main ()',
'{',
' gl_Position = uProjectionMatrix * vec4(inPosition, 1.0, 1.0);',
' outTint = vec4(inTint.bgr * inTint.a, inTint.a);',
'}',
].join('\n');

View file

@ -32,6 +32,8 @@ module.exports = {
FXShineFrag: require('./FXShine-frag.js'),
FXVignetteFrag: require('./FXVignette-frag.js'),
FXWipeFrag: require('./FXWipe-frag.js'),
FlatFrag: require('./Flat-frag.js'),
FlatVert: require('./Flat-vert.js'),
LightFrag: require('./Light-frag.js'),
LinearBlendFrag: require('./LinearBlend-frag.js'),
MeshFrag: require('./Mesh-frag.js'),

View file

@ -0,0 +1,14 @@
#define SHADER_NAME PHASER_FLAT_FS
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
varying vec4 outTint;
void main ()
{
gl_FragColor = outTint;
}

View file

@ -0,0 +1,17 @@
#define SHADER_NAME PHASER_FLAT_VS
precision mediump float;
uniform mat4 uProjectionMatrix;
attribute vec2 inPosition;
attribute vec4 inTint;
varying vec4 outTint;
void main ()
{
gl_Position = uProjectionMatrix * vec4(inPosition, 1.0, 1.0);
outTint = vec4(inTint.bgr * inTint.a, inTint.a);
}