Add lighting and dynamic vertex indexes to Graphics.

This commit is contained in:
Ben Richards 2024-06-24 12:36:20 +12:00
parent ff4980d85d
commit bd54cbb965
14 changed files with 648 additions and 413 deletions

View file

@ -65,6 +65,7 @@ var Render = require('./GraphicsRender');
* @extends Phaser.GameObjects.Components.AlphaSingle
* @extends Phaser.GameObjects.Components.BlendMode
* @extends Phaser.GameObjects.Components.Depth
* @extends Phaser.GameObjects.Components.Lighting
* @extends Phaser.GameObjects.Components.Mask
* @extends Phaser.GameObjects.Components.Pipeline
* @extends Phaser.GameObjects.Components.PostPipeline
@ -83,6 +84,7 @@ var Graphics = new Class({
Components.AlphaSingle,
Components.BlendMode,
Components.Depth,
Components.Lighting,
Components.Mask,
Components.PostPipeline,
Components.RenderNode,

View file

@ -70,6 +70,7 @@ var GraphicsWebGLRenderer = function (renderer, src, drawingContext, parentMatri
var customRenderNodes = src.customRenderNodes;
var defaultRenderNodes = src.defaultRenderNodes;
var submitterNode = customRenderNodes.Submitter || defaultRenderNodes.Submitter;
var currentContext = drawingContext;
@ -133,8 +134,9 @@ var GraphicsWebGLRenderer = function (renderer, src, drawingContext, parentMatri
(customRenderNodes.FillPath || defaultRenderNodes.FillPath).run(
currentContext,
path[pathIndex].points,
renderMatrix,
submitterNode,
path[pathIndex].points,
fillTint.TL,
fillTint.TR,
fillTint.BL
@ -151,6 +153,7 @@ var GraphicsWebGLRenderer = function (renderer, src, drawingContext, parentMatri
(customRenderNodes.StrokePath || defaultRenderNodes.StrokePath).run(
currentContext,
submitterNode,
path[pathIndex].points,
lineWidth,
pathOpen,
@ -281,6 +284,7 @@ var GraphicsWebGLRenderer = function (renderer, src, drawingContext, parentMatri
(customRenderNodes.FillRect || defaultRenderNodes.FillRect).run(
currentContext,
renderMatrix,
submitterNode,
commands[++cmdIndex],
commands[++cmdIndex],
commands[++cmdIndex],
@ -301,6 +305,7 @@ var GraphicsWebGLRenderer = function (renderer, src, drawingContext, parentMatri
(customRenderNodes.FillTri || defaultRenderNodes.FillTri).run(
currentContext,
renderMatrix,
submitterNode,
commands[++cmdIndex],
commands[++cmdIndex],
commands[++cmdIndex],
@ -337,6 +342,7 @@ var GraphicsWebGLRenderer = function (renderer, src, drawingContext, parentMatri
(customRenderNodes.StrokePath || defaultRenderNodes.StrokePath).run(
currentContext,
submitterNode,
trianglePath,
lineWidth,
false,

View file

@ -39,6 +39,15 @@ var BatchHandlerTriFlat = new Class({
* @readonly
*/
this._emptyTextures = [];
/**
* The number of vertices currently in the batch.
*
* @name Phaser.Renderer.WebGL.RenderNodes.BatchHandlerTriFlat#vertexCount
* @type {number}
* @since 3.90.0
*/
this.vertexCount = 0;
},
defaultConfig: {
@ -47,6 +56,7 @@ var BatchHandlerTriFlat = new Class({
indicesPerInstance: 3,
vertexSource: ShaderSourceVS,
fragmentSource: ShaderSourceFS,
indexBufferDynamic: true,
vertexBufferLayout: {
usage: 'DYNAMIC_DRAW',
layout: [
@ -68,11 +78,6 @@ var BatchHandlerTriFlat = new Class({
* 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
@ -81,17 +86,7 @@ var BatchHandlerTriFlat = new Class({
*/
_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;
return new ArrayBuffer(instances * 3 * 2);
},
/**
@ -127,12 +122,19 @@ var BatchHandlerTriFlat = new Class({
var vao = this.vao;
var renderer = this.manager.renderer;
var vertexBuffer = this.vertexBufferLayout.buffer;
var stride = this.vertexBufferLayout.layout.stride;
// 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);
vertexBuffer.update(this.vertexCount * stride);
// Update index buffer.
// We must bind the VAO before updating the index buffer.
// Each index is a 16-bit unsigned integer, so 2 bytes.
vao.bind();
vao.indexBuffer.update(instanceCount * indicesPerInstance * 2);
renderer.drawElements(
drawingContext,
@ -146,66 +148,99 @@ var BatchHandlerTriFlat = new Class({
// Reset batch accumulation.
this.instanceCount = 0;
this.vertexCount = 0;
this.onRunEnd(drawingContext);
},
/**
* Add a triangle to the batch.
* Add data to the batch.
*
* The vertices are not textured, and are named A, B, and C.
* The data is composed of vertices and indexed triangles.
* Each triangle is defined by three indices into the vertices array.
*
* @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.
* @param {number[]} indexes - The index data. Each triangle is defined by three indices into the vertices array, so the length of this should be a multiple of 3.
* @param {number[]} vertices - The vertices data. Each vertex is defined by an x-coordinate, a y-coordinate, a tint color, a pass ID (initially -1), and an index ID (initially -1).
*/
batch: function (currentContext, xA, yA, xB, yB, xC, yC, tintA, tintB, tintC)
batch: function (currentContext, indexes, vertices)
{
if (this.instanceCount === 0)
{
this.manager.setCurrentBatchNode(this, currentContext);
}
// Update the vertex buffer.
var vertexOffset32 = this.instanceCount * this.floatsPerInstance;
var passID = 0;
var instanceCompletion = 0;
var instancesPerBatch = this.instancesPerBatch;
// Buffer data
var stride = this.vertexBufferLayout.layout.stride;
var verticesPerInstance = this.verticesPerInstance;
var indexBuffer = this.vao.indexBuffer;
var indexView16 = indexBuffer.viewU16;
var indexOffset16 = this.instanceCount * this.indicesPerInstance;
var vertexBuffer = this.vertexBufferLayout.buffer;
var vertexViewF32 = vertexBuffer.viewF32;
var vertexViewU32 = vertexBuffer.viewU32;
var vertexOffset32 = this.vertexCount * stride / vertexViewF32.BYTES_PER_ELEMENT;
// 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)
for (var i = 0; i < indexes.length; i++)
{
this.run(currentContext);
var index = indexes[i] * 5;
// Now the batch is empty.
if (vertices[index + 3] !== passID)
{
// Update the vertex buffer.
vertexViewF32[vertexOffset32++] = vertices[index];
vertexViewF32[vertexOffset32++] = vertices[index + 1];
vertexViewU32[vertexOffset32++] = vertices[index + 2];
// Assign the vertex to the current pass.
vertices[index + 3] = passID;
// Record the index where the vertex was stored.
vertices[index + 4] = this.vertexCount;
this.vertexCount++;
}
var id = vertices[index + 4];
// Update the index buffer.
// There is always at least one index per vertex,
// so we can assume that the vertex buffer is large enough.
indexView16[indexOffset16++] = id;
// Check whether the instance is complete.
instanceCompletion++;
if (instanceCompletion === verticesPerInstance)
{
this.instanceCount++;
instanceCompletion = 0;
}
// Check whether the batch should be rendered immediately.
// This guarantees that none of the arrays are full above.
if (
// The instance count has been reached.
this.instanceCount === instancesPerBatch ||
// This triangle is complete, and another would exceed the index buffer size.
(instanceCompletion === 0 && indexOffset16 + verticesPerInstance >= indexView16.length)
)
{
passID++;
this.run(currentContext);
indexOffset16 = this.instanceCount * this.indicesPerInstance;
vertexOffset32 = this.vertexCount * stride / vertexViewF32.BYTES_PER_ELEMENT;
// Now the batch is empty.
}
}
}
});

View file

@ -0,0 +1,209 @@
/**
* @author Benjamin D. Richards <benjamindrichards@gmail.com>
* @copyright 2013-2024 Phaser Studio Inc.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var Vector2 = require('../../../math/Vector2');
var Class = require('../../../utils/Class');
var LightShaderSourceFS = require('../shaders/FlatLight-frag');
var ShaderSourceVS = require('../shaders/Flat-vert');
var BatchHandlerTriFlat = require('./BatchHandlerTriFlat');
/**
* @classdesc
* This RenderNode draws vertex tinted triangles with a Light Shader
* in batches.
*
* The fragment shader used by this RenderNode will be compiled
* with a maximum light count defined by the renderer configuration.
* The string `%LIGHT_COUNT%` in the fragment shader source will be
* replaced with this value.
*
* @class BatchHandlerTriFlatLight
* @memberof Phaser.Renderer.WebGL.RenderNodes
* @constructor
* @since 3.90.0
* @extends Phaser.Renderer.WebGL.RenderNodes.BatchHandlerQuad
* @param {Phaser.Renderer.WebGL.RenderNodes.RenderNodeManager} manager - The manager that owns this RenderNode.
* @param {Phaser.Types.Renderer.WebGL.RenderNodes.BatchHandlerConfig} config - The configuration object for this RenderNode.
*/
var BatchHandlerTriFlatLight = new Class({
Extends: BatchHandlerTriFlat,
initialize: function BatchHandlerTriFlatLight (manager, config)
{
BatchHandlerTriFlat.call(this, manager, config);
/**
* Inverse rotation matrix for normal map rotations.
*
* @name Phaser.Renderer.WebGL.RenderNodes.BatchHandlerTriFlatLight#inverseRotationMatrix
* @type {Float32Array}
* @private
* @since 3.90.0
*/
this.inverseRotationMatrix = new Float32Array([
1, 0, 0,
0, 1, 0,
0, 0, 1
]);
/**
* A persistent calculation vector used when processing the lights.
*
* @name Phaser.Renderer.WebGL.RenderNodes.BatchHandlerTriFlatLight#_lightVector
* @type {Phaser.Math.Vector2}
* @private
* @since 3.90.0
*/
this._lightVector = new Vector2();
/**
* The rotation of the normal map texture.
*
* @name Phaser.Renderer.WebGL.RenderNodes.BatchHandlerTriFlatLight#_normalMapRotation
* @type {number}
* @private
* @since 3.90.0
*/
this._normalMapRotation = 0;
},
/**
* The default configuration settings for BatchHandlerTriFlatLight.
*
* @name Phaser.Renderer.WebGL.RenderNodes.BatchHandlerTriFlatLight#defaultConfig
* @type {Phaser.Types.Renderer.WebGL.RenderNodes.BatchHandlerConfig}
* @since 3.90.0
* @readonly
*/
defaultConfig: {
name: 'BatchHandlerTriFlatLight',
verticesPerInstance: 3,
indicesPerInstance: 3,
vertexSource: ShaderSourceVS,
fragmentSource: LightShaderSourceFS,
vertexBufferLayout: {
usage: 'DYNAMIC_DRAW',
layout: [
{
name: 'inPosition',
size: 2
},
{
name: 'inTint',
size: 4,
type: 'UNSIGNED_BYTE',
normalized: true
}
]
}
},
_copyAndCompleteConfig: function (manager, config, defaultConfig)
{
var newConfig = BatchHandlerTriFlat.prototype._copyAndCompleteConfig.call(this, manager, config, defaultConfig);
newConfig.fragmentSource = newConfig.fragmentSource.replace(
'%LIGHT_COUNT%',
manager.renderer.config.maxLights
);
return newConfig;
},
/**
* Set new dimensions for the renderer. This is called automatically when the renderer is resized.
*
* @method Phaser.Renderer.WebGL.RenderNodes.BatchHandlerTriFlatLight#resize
* @since 3.90.0
* @param {number} width - The new width of the renderer.
* @param {number} height - The new height of the renderer.
*/
resize: function (width, height)
{
BatchHandlerTriFlat.prototype.resize.call(this, width, height);
this.program.setUniform('uResolution', [ width, height ]);
},
/**
* Called at the start of the `run` method.
*
* @method Phaser.Renderer.WebGL.RenderNodes.BatchHandlerTriFlatLight#onRunBegin
* @since 3.90.0
* @param {Phaser.Types.Renderer.WebGL.DrawingContext} drawingContext - The current drawing context.
*/
onRunBegin: function (drawingContext)
{
var camera = drawingContext.camera;
var cameraMatrix = camera.matrix;
var program = this.program;
var scene = camera.scene;
var lightManager = scene.sys.lights;
var lights = lightManager.getLights(camera);
var lightsCount = lights.length;
var ambientColor = lightManager.ambientColor;
var vec = this._lightVector;
var height = this.manager.renderer.height;
program.setUniform(
'uCamera',
[
camera.x,
camera.y,
camera.rotation,
camera.zoom
]
);
program.setUniform(
'uAmbientLightColor',
[
ambientColor.r,
ambientColor.g,
ambientColor.b
]
);
program.setUniform(
'uLightCount',
lightsCount
);
for (var i = 0; i < lightsCount; i++)
{
var light = lights[i].light;
var color = light.color;
var lightName = 'uLights[' + i + '].';
cameraMatrix.transformPoint(light.x, light.y, vec);
program.setUniform(
lightName + 'position',
[
vec.x - (camera.scrollX * light.scrollFactorX * camera.zoom),
height - (vec.y - (camera.scrollY * light.scrollFactorY * camera.zoom))
]
);
program.setUniform(
lightName + 'color',
[
color.r,
color.g,
color.b
]
);
program.setUniform(
lightName + 'intensity',
light.intensity
);
program.setUniform(
lightName + 'radius',
light.radius
);
}
}
});
module.exports = BatchHandlerTriFlatLight;

View file

@ -9,7 +9,7 @@ var RenderNode = require('./RenderNode');
/**
* @classdesc
* A RenderNode which renders a line segment.
* A RenderNode which computes the geometry of a line segment.
*
* @class DrawLine
* @memberof Phaser.Renderer.WebGL.RenderNodes
@ -26,79 +26,32 @@ var DrawLine = new Class({
RenderNode.call(this, 'DrawLine', manager);
/**
* The render node that handles the rendering of triangles.
* The vertices of the line segment as a quad.
* These values have been transformed.
* They should be used before the next call to `run`,
* whereupon they will be overridden.
*
* @name Phaser.Renderer.WebGL.RenderNodes.DrawLine#batchHandler
* @type {Phaser.Renderer.WebGL.RenderNodes.BatchHandlerTriFlat}
* @name Phaser.Renderer.WebGL.RenderNodes.DrawLine#quad
* @type {{ xTL: number, yTL: number, xTR: number, yTR: number, xBL: number, yBL: number, xBR: number, yBR: number }}
* @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 ];
this.quad = {
xTL: 0,
yTL: 0,
xTR: 0,
yTR: 0,
xBL: 0,
yBL: 0,
xBR: 0,
yBR: 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.
* Get the transformed vertices of a line segment as a quad.
* The values are stored in the `quad` property.
* Access the values directly or copy them to another object,
* before the next call to `run`, whereupon they will be overridden.
*
* @method Phaser.Renderer.WebGL.RenderNodes.DrawLine#run
* @since 3.90.0
@ -110,14 +63,8 @@ var DrawLine = new Class({
* @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)
run: function (drawingContext, currentMatrix, ax, ay, bx, by, aLineWidth, bLineWidth)
{
this.onRunBegin(drawingContext);
@ -126,11 +73,7 @@ var DrawLine = new Class({
var len = Math.sqrt(dx * dx + dy * dy);
if (len === 0)
{
// Because we cannot (and should not) divide by zero!
return;
}
// A well-formed path has no zero length segments, so we don't check.
var al0 = aLineWidth * (by - ay) / len;
var al1 = aLineWidth * (ax - bx) / len;
@ -146,113 +89,29 @@ var DrawLine = new Class({
var lx3 = ax + al0;
var ly3 = ay + al1;
var brX, brY, blX, blY, trX, trY, tlX, tlY;
var quad = this.quad;
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);
quad.xTL = currentMatrix.getX(lx3, ly3);
quad.yTL = currentMatrix.getY(lx3, ly3);
quad.xBL = currentMatrix.getX(lx1, ly1);
quad.yBL = currentMatrix.getY(lx1, ly1);
quad.xTR = currentMatrix.getX(lx2, ly2);
quad.yTR = currentMatrix.getY(lx2, ly2);
quad.xBR = currentMatrix.getX(lx0, ly0);
quad.yBR = currentMatrix.getY(lx0, ly0);
}
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;
}
quad.xTL = lx3;
quad.yTL = ly3;
quad.xBL = lx1;
quad.yBL = ly1;
quad.xTR = lx2;
quad.yTR = ly2;
quad.xBR = lx0;
quad.yBR = ly0;
}
this.onRunEnd(drawingContext);

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);
this.fillRectNode.run(drawingContext, null, null, cx, cy, cw, ch, color, color, color, color);
this.onRunEnd(drawingContext);
}

View file

@ -31,25 +31,6 @@ var FillPath = new Class({
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 = [];
},
/**
@ -58,55 +39,118 @@ var FillPath = new Class({
* @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 {Phaser.Renderer.WebGL.RenderNodes.SubmitterGraphics} submitterNode - The Submitter node to use.
* @param {{ x: number, y: number, width: number }[]} path - The points that define the line segments.
* @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)
run: function (drawingContext, currentMatrix, submitterNode, path, tintTL, tintTR, tintBL)
{
this.onRunBegin(drawingContext);
var length = path.length;
var polygonCache = this.polygonCache;
var polygonIndexArray;
var point;
var index, pathIndex, point, polygonIndexArray, x, y;
for (var pathIndex = 0; pathIndex < length; pathIndex++)
var polygonCacheIndex = 0;
var verticesIndex = 0;
var indexedTrianglesIndex = 0;
var polygonCache = Array(length * 2);
var vertices = Array(length * 5);
if (tintTL === tintTR && tintTL === tintBL)
{
point = path[pathIndex];
polygonCache.push(point.x, point.y);
// If the tint colors are all the same,
// then we can share vertices between the triangles.
for (pathIndex = 0; pathIndex < length; pathIndex++)
{
point = path[pathIndex];
// Transform the point.
x = currentMatrix.getX(point.x, point.y);
y = currentMatrix.getY(point.x, point.y);
polygonCache[polygonCacheIndex++] = x;
polygonCache[polygonCacheIndex++] = y;
vertices[verticesIndex++] = x;
vertices[verticesIndex++] = y;
vertices[verticesIndex++] = tintTL;
vertices[verticesIndex++] = -1;
vertices[verticesIndex++] = -1;
}
polygonIndexArray = Earcut(polygonCache);
length = polygonIndexArray.length;
submitterNode.batch(drawingContext, polygonIndexArray, vertices);
}
polygonIndexArray = Earcut(polygonCache);
length = polygonIndexArray.length;
for (var index = 0; index < length; index += 3)
else
{
var p0 = polygonIndexArray[index + 0] * 2;
var p1 = polygonIndexArray[index + 1] * 2;
var p2 = polygonIndexArray[index + 2] * 2;
// If the tint colors are different,
// then we need to create a new vertex for each triangle.
for (pathIndex = 0; pathIndex < length; pathIndex++)
{
point = path[pathIndex];
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];
// Transform the point.
x = currentMatrix.getX(point.x, point.y);
y = currentMatrix.getY(point.x, point.y);
this.polygonNode.run(
drawingContext,
currentMatrix,
x0, y0,
x1, y1,
x2, y2,
tintTL, tintTR, tintBL
);
polygonCache[polygonCacheIndex++] = x;
polygonCache[polygonCacheIndex++] = y;
}
polygonIndexArray = Earcut(polygonCache);
length = polygonIndexArray.length;
var indexedTriangles = Array(length);
for (index = 0; index < length; index += 3)
{
// Vertex A
var p = polygonIndexArray[index] * 2;
x = polygonCache[p + 0];
y = polygonCache[p + 1];
vertices[verticesIndex++] = x;
vertices[verticesIndex++] = y;
vertices[verticesIndex++] = tintTL;
vertices[verticesIndex++] = -1;
vertices[verticesIndex++] = -1;
// Vertex B
p = polygonIndexArray[index + 1] * 2;
x = polygonCache[p + 0];
y = polygonCache[p + 1];
vertices[verticesIndex++] = x;
vertices[verticesIndex++] = y;
vertices[verticesIndex++] = tintTR;
vertices[verticesIndex++] = -1;
vertices[verticesIndex++] = -1;
// Vertex C
p = polygonIndexArray[index + 2] * 2;
x = polygonCache[p + 0];
y = polygonCache[p + 1];
vertices[verticesIndex++] = x;
vertices[verticesIndex++] = y;
vertices[verticesIndex++] = tintBL;
vertices[verticesIndex++] = -1;
vertices[verticesIndex++] = -1;
// Add new indices for the triangle.
indexedTriangles[indexedTrianglesIndex++] = index + 0;
indexedTriangles[indexedTrianglesIndex++] = index + 1;
indexedTriangles[indexedTrianglesIndex++] = index + 2;
}
submitterNode.batch(drawingContext, indexedTriangles, vertices);
}
polygonCache.length = 0;
this.onRunEnd(drawingContext);
}
});

View file

@ -28,13 +28,15 @@ var FillRect = new Class({
RenderNode.call(this, 'FillRect', manager);
/**
* The RenderNode that handles the rendering of quads.
* The fallback batch handler for this node.
*
* @name Phaser.Renderer.WebGL.RenderNodes.FillRect#batchHandler
* @name Phaser.Renderer.WebGL.RenderNodes.FillRect#_batchHandlerDefault
* @type {Phaser.Renderer.WebGL.RenderNodes.BatchHandlerTriFlat}
* @since 3.90.0
* @private
* @readonly
*/
this.batchHandler = this.manager.getNode('BatchHandlerTriFlat');
this._batchHandlerDefault = manager.getNode('BatchHandlerTriFlat');
/**
* An unchanging identity matrix.
@ -42,8 +44,24 @@ var FillRect = new Class({
* @name Phaser.Renderer.WebGL.RenderNodes.FillRect#_identityMatrix
* @type {Phaser.GameObjects.Components.TransformMatrix}
* @private
* @since 3.90.0
*/
this._identityMatrix = new TransformMatrix();
/**
* Vertex indices for the rectangle.
*
* @name Phaser.Renderer.WebGL.RenderNodes.FillRect#_indexedTriangles
* @type {number[]}
* @private
* @since 3.90
* @default [0, 1, 2, 2, 3, 0]
* @readonly
*/
this._indexedTriangles = [
0, 1, 2,
2, 3, 0
];
},
/**
@ -53,6 +71,7 @@ var FillRect = new Class({
* @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 identity matrix is used.
* @param {Phaser.Renderer.WebGL.RenderNodes.SubmitterGraphics} [submitterNode] - The Submitter node to use.
* @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.
@ -62,7 +81,7 @@ var FillRect = new Class({
* @param {number} tintBL - The bottom-left tint color.
* @param {number} tintBR - The bottom-right tint color.
*/
run: function (drawingContext, currentMatrix, x, y, width, height, tintTL, tintTR, tintBL, tintBR)
run: function (drawingContext, currentMatrix, submitterNode, x, y, width, height, tintTL, tintTR, tintBL, tintBR)
{
this.onRunBegin(drawingContext);
@ -71,22 +90,22 @@ var FillRect = new Class({
currentMatrix = this._identityMatrix;
}
if (!submitterNode)
{
submitterNode = this._batchHandlerDefault;
}
var quad = currentMatrix.setQuad(x, y, x + width, y + height);
this.batchHandler.batch(
submitterNode.batch(
drawingContext,
quad[0], quad[1],
quad[2], quad[3],
quad[4], quad[5],
tintTL, tintBL, tintBR
);
this.batchHandler.batch(
drawingContext,
quad[4], quad[5],
quad[6], quad[7],
quad[0], quad[1],
tintBR, tintTR, tintTL
this._indexedTriangles,
[
quad[0], quad[1], tintTL, -1, -1,
quad[2], quad[3], tintBL, -1, -1,
quad[4], quad[5], tintBR, -1, -1,
quad[6], quad[7], tintTR, -1, -1
]
);
this.onRunEnd(drawingContext);

View file

@ -10,8 +10,6 @@ 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
@ -28,13 +26,18 @@ var FillTri = new Class({
RenderNode.call(this, 'FillTri', manager);
/**
* The BatchHandlerQuad that handles the rendering of quads.
* Vertex indices for the triangle.
*
* @name Phaser.Renderer.WebGL.RenderNodes.FillTri#batchHandler
* @type {Phaser.Renderer.WebGL.RenderNodes.BatchHandlerTriFlat}
* @since 3.90.0
* @name Phaser.Renderer.WebGL.RenderNodes.FillTri#_indexedTriangles
* @type {number[]}
* @private
* @since 3.90
* @default [0, 1, 2]
* @readonly
*/
this.batchHandler = this.manager.getNode('BatchHandlerTriFlat');
this._indexedTriangles = [
0, 1, 2
];
},
/**
@ -44,42 +47,70 @@ var FillTri = new Class({
* @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 {Phaser.Renderer.WebGL.RenderNodes.SubmitterGraphics} submitterNode - The Submitter node to use.
* @param {number} xA - The x-coordinate of the first vertex.
* @param {number} yA - The y-coordinate of the first vertex.
* @param {number} xB - The x-coordinate of the second vertex.
* @param {number} yB - The y-coordinate of the second vertex.
* @param {number} xC - The x-coordinate of the third vertex.
* @param {number} yC - The y-coordinate of the third vertex.
* @param {number} tintA - The tint color of the first vertex.
* @param {number} tintB - The tint color of the second vertex.
* @param {number} tintC - The tint color of the third vertex.
*/
run: function (drawingContext, currentMatrix, xA, yA, xB, yB, xC, yC, tintA, tintB, tintC)
run: function (drawingContext, currentMatrix, submitterNode, 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);
submitterNode.batch(
drawingContext,
this._indexedTriangles,
[
currentMatrix.getX(xA, yA),
currentMatrix.getY(xA, yA),
tintA,
-1,
-1,
currentMatrix.getX(xB, yB),
currentMatrix.getY(xB, yB),
tintB,
-1,
-1,
currentMatrix.getX(xC, yC),
currentMatrix.getY(xC, yC),
tintC,
-1,
-1
]
);
}
else
{
txA = xA;
tyA = yA;
txB = xB;
tyB = yB;
txC = xC;
tyC = yC;
submitterNode.batch(
drawingContext,
this._indexedTriangles,
[
xA,
yA,
tintA,
-1,
-1,
xB,
yB,
tintB,
-1,
-1,
xC,
yC,
tintC,
-1,
-1
]
);
}
this.batchHandler.batch(
drawingContext,
txA, tyA,
txB, tyB,
txC, tyC,
tintA, tintB, tintC
);
this.onRunEnd(drawingContext);
}
});

View file

@ -17,6 +17,7 @@ var DefaultParticleEmitterNodes = require('./defaults/DefaultParticleEmitterNode
var BatchHandlerQuad = require('./BatchHandlerQuad');
var BatchHandlerQuadLight = require('./BatchHandlerQuadLight');
var BatchHandlerTriFlat = require('./BatchHandlerTriFlat');
var BatchHandlerTriFlatLight = require('./BatchHandlerTriFlatLight');
var Camera = require('./Camera');
var DrawLine = require('./DrawLine');
var FillCamera = require('./FillCamera');
@ -135,6 +136,7 @@ var RenderNodeManager = new Class({
BatchHandlerQuad: BatchHandlerQuad,
BatchHandlerQuadLight: BatchHandlerQuadLight,
BatchHandlerTriFlat: BatchHandlerTriFlat,
BatchHandlerTriFlatLight: BatchHandlerTriFlatLight,
Camera: Camera,
DrawLine: DrawLine,
FillCamera: FillCamera,

View file

@ -27,7 +27,7 @@ var StrokePath = new Class({
RenderNode.call(this, 'StrokePath', manager);
/**
* The RenderNode that draws a line segment.
* The RenderNode that generates a line segment.
*
* @name Phaser.Renderer.WebGL.RenderNodes.StrokePath#drawLineNode
* @type {Phaser.Renderer.WebGL.RenderNodes.DrawLine}
@ -42,6 +42,7 @@ var StrokePath = new Class({
* @method Phaser.Renderer.WebGL.RenderNodes.StrokePath#run
* @since 3.90.0
* @param {Phaser.Renderer.WebGL.DrawingContext} drawingContext - The context currently in use.
* @param {Phaser.Renderer.WebGL.RenderNodes.SubmitterGraphics} submitterNode - The Submitter node to 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.
@ -51,7 +52,7 @@ var StrokePath = new Class({
* @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)
run: function (drawingContext, submitterNode, path, lineWidth, open, currentMatrix, tintTL, tintTR, tintBL, tintBR)
{
this.onRunBegin(drawingContext);
@ -59,77 +60,39 @@ var StrokePath = new Class({
var pathLength = path.length - 1;
var pathIndex, point, nextPoint;
var point, nextPoint;
if (pathLength === 1)
// Determine size of index array.
var indexCount = pathLength * 6;
var connect = false;
var connectLoop = false;
if (lineWidth > 2 && pathLength > 1)
{
// Only one point, draw a single line
point = path[0];
nextPoint = path[1];
connect = true;
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++)
// Lines will be connected by a secondary quad.
indexCount *= 2;
if (open)
{
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
);
// The last line will not be connected to the first line.
indexCount -= 6;
}
else
{
connectLoop = true;
}
}
var indices = Array(indexCount);
var indexOffset = 0;
point = path[pathLength - 1];
nextPoint = path[pathLength];
var vertices = Array(pathLength * 4 * 5);
var vertexOffset = 0;
for (var i = 0; i < pathLength; i++)
{
point = path[i];
nextPoint = path[i + 1];
drawLineNode.run(
drawingContext,
@ -139,16 +102,79 @@ var StrokePath = new Class({
nextPoint.x,
nextPoint.y,
point.width / 2,
nextPoint.width / 2,
lineWidth,
tintTL,
tintTR,
tintBL,
tintBR,
open ? undefined : drawLineNode.LAST
nextPoint.width / 2
);
var quad = drawLineNode.quad;
vertices[vertexOffset++] = quad.xTL;
vertices[vertexOffset++] = quad.yTL;
vertices[vertexOffset++] = tintTL;
vertices[vertexOffset++] = -1;
vertices[vertexOffset++] = -1;
vertices[vertexOffset++] = quad.xBL;
vertices[vertexOffset++] = quad.yBL;
vertices[vertexOffset++] = tintBL;
vertices[vertexOffset++] = -1;
vertices[vertexOffset++] = -1;
vertices[vertexOffset++] = quad.xBR;
vertices[vertexOffset++] = quad.yBR;
vertices[vertexOffset++] = tintBR;
vertices[vertexOffset++] = -1;
vertices[vertexOffset++] = -1;
vertices[vertexOffset++] = quad.xTR;
vertices[vertexOffset++] = quad.yTR;
vertices[vertexOffset++] = tintTR;
vertices[vertexOffset++] = -1;
vertices[vertexOffset++] = -1;
// Draw two triangles.
// The vertices are in the order: TL, BL, BR, TR
indices[indexOffset++] = i * 4;
indices[indexOffset++] = i * 4 + 1;
indices[indexOffset++] = i * 4 + 2;
indices[indexOffset++] = i * 4 + 2;
indices[indexOffset++] = i * 4 + 3;
indices[indexOffset++] = i * 4;
if (connect && i !== 0)
{
// Draw a quad connecting to the previous line segment.
// The vertices are in the order:
// - TL
// - BL
// - Previous BR
// - Previous TR
indices[indexOffset++] = i * 4;
indices[indexOffset++] = i * 4 + 1;
indices[indexOffset++] = i * 4 - 2;
indices[indexOffset++] = i * 4 - 2;
indices[indexOffset++] = i * 4 - 1;
indices[indexOffset++] = i * 4;
if (connectLoop && i === pathLength - 1)
{
// Connect the last line segment to the first.
// The vertices are in the order:
// - BR
// - TR
// - First TL
// - First BL
indices[indexOffset++] = i * 4 + 2;
indices[indexOffset++] = i * 4 + 3;
indices[indexOffset++] = 0;
indices[indexOffset++] = 0;
indices[indexOffset++] = 1;
indices[indexOffset++] = i * 4 + 2;
}
}
}
submitterNode.batch(drawingContext, indices, vertices);
this.onRunEnd(drawingContext);
}
});

View file

@ -7,8 +7,8 @@
var Map = require('../../../../structs/Map');
var DefaultGraphicsNodes = new Map([
[ 'BatchHandlerTriFlat', 'BatchHandlerTriFlat' ],
// [ 'SubmitterLight', 'SubmitterQuadLight' ],
[ 'Submitter', 'BatchHandlerTriFlat' ],
[ 'SubmitterLight', 'BatchHandlerTriFlatLight' ],
[ 'FillPath', 'FillPath' ],
[ 'FillRect', 'FillRect' ],
[ 'FillTri', 'FillTri' ],

View file

@ -13,6 +13,7 @@ var RenderNodes = {
BatchHandlerQuad: require('./BatchHandlerQuad'),
BatchHandlerQuadLight: require('./BatchHandlerQuadLight'),
BatchHandlerTriFlat: require('./BatchHandlerTriFlat'),
BatchHandlerTriFlatLight: require('./BatchHandlerTriFlatLight'),
Camera: require('./Camera'),
DrawLine: require('./DrawLine'),
FillCamera: require('./FillCamera'),

View file

@ -34,6 +34,7 @@ module.exports = {
FXWipeFrag: require('./FXWipe-frag.js'),
FlatFrag: require('./Flat-frag.js'),
FlatVert: require('./Flat-vert.js'),
FlatLightFrag: require('./FlatLight-frag.js'),
LightFrag: require('./Light-frag.js'),
LinearBlendFrag: require('./LinearBlend-frag.js'),
MeshFrag: require('./Mesh-frag.js'),