phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.
239 lines (211 loc) • 9.99 kB
JavaScript
/**
* @author Benjamin D. Richards <benjamindrichards@gmail.com>
* @copyright 2013-2026 Phaser Studio Inc.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var Vector2 = require('../../../math/Vector2');
var Class = require('../../../utils/Class');
var Utils = require('../Utils.js');
var BatchHandlerQuad = require('./BatchHandlerQuad');
var getTint = Utils.getTintAppendFloatAlpha;
/**
* @classdesc
* This RenderNode renders textured triangles individually, rather than
* combining them into quads. It is used by the `Mesh2D` Game Object when its
* `renderAsTriangles` property is enabled, which suits dynamic topology that
* cannot be optimized into quads ahead of time.
*
* It extends `BatchHandlerQuad` and reuses its shader, vertex layout, texture
* handling, and draw logic. The only differences are its configuration (three
* vertices and indices per instance, drawn as `gl.TRIANGLES`) and the
* `batchTriangles` method, which accepts vertex and index arrays directly,
* much like `BatchHandlerTriFlat`, but writes each vertex in the layout used by
* `BatchHandlerQuad`.
*
* @class BatchHandlerTri
* @memberof Phaser.Renderer.WebGL.RenderNodes
* @constructor
* @since 4.2.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 handler.
*/
var BatchHandlerTri = new Class({
Extends: BatchHandlerQuad,
initialize: function BatchHandlerTri (manager, config)
{
BatchHandlerQuad.call(this, manager, config);
/**
* Temporary point used to transform vertex positions into screen space.
*
* @name Phaser.Renderer.WebGL.RenderNodes.BatchHandlerTri#_tempPoint
* @type {Phaser.Math.Vector2}
* @private
* @since 4.2.0
*/
this._tempPoint = new Vector2();
},
/**
* The default configuration object for this handler. It is the
* `BatchHandlerQuad` configuration with the instance shape and topology
* changed to draw individual triangles.
*
* @name Phaser.Renderer.WebGL.RenderNodes.BatchHandlerTri#defaultConfig
* @type {Phaser.Types.Renderer.WebGL.RenderNodes.BatchHandlerConfig}
* @since 4.2.0
*/
defaultConfig: Object.assign({}, BatchHandlerQuad.prototype.defaultConfig, {
name: 'BatchHandlerTri',
verticesPerInstance: 3,
indicesPerInstance: 3,
topology: 0x0004 // gl.TRIANGLES
}),
/**
* Generate element indices for the instance vertices.
* This is called automatically when the node is initialized.
*
* Each instance is a single triangle of three vertices, drawn as
* `gl.TRIANGLES`. The vertices of each instance are written contiguously,
* so the indices are simply sequential.
*
* @method Phaser.Renderer.WebGL.RenderNodes.BatchHandlerTri#_generateElementIndices
* @since 4.2.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 len = indices.length;
for (var i = 0; i < len; i++)
{
indices[i] = i;
}
return buffer;
},
/**
* Adds a set of textured triangles to the batch. Each triangle is defined
* by a stride-4 entry in `indices` (`a, b, c, page`), where `a, b, c` index
* into `vertices` and `page` selects the texture source. Each vertex is a
* stride-4 entry in `vertices` (`x, y, u, v`).
*
* The vertex positions are transformed into screen space by the supplied
* transformer node, then written into the vertex buffer in the same layout
* used by `BatchHandlerQuad`. This handling mirrors `BatchHandlerTriFlat`,
* but produces fully textured and tinted vertices.
*
* This method is named `batchTriangles` rather than `batch` because its
* call signature differs from the standard quad batch handlers.
*
* @method Phaser.Renderer.WebGL.RenderNodes.BatchHandlerTri#batchTriangles
* @since 4.2.0
* @param {Phaser.Renderer.WebGL.DrawingContext} drawingContext - The current drawing context.
* @param {Phaser.GameObjects.Mesh2D} gameObject - The Mesh2D Game Object being rendered.
* @param {Phaser.GameObjects.Components.TransformMatrix | undefined} parentMatrix - The parent matrix of the Game Object, if it is nested.
* @param {Phaser.Renderer.WebGL.RenderNodes.TransformerVertex} transformerNode - The transformer node used to transform each vertex into screen space.
* @param {number[]} vertices - The vertex data, as a sequence of `x, y, u, v` with a step of 4.
* @param {number[]} indices - The index data, as a sequence of `a, b, c, page` with a step of 4.
* @param {Phaser.Types.Renderer.WebGL.RenderNodes.BatchHandlerQuadRenderOptions} renderOptions - The render options for the batch, as resolved by the submitter node.
*/
batchTriangles: function (
drawingContext,
gameObject,
parentMatrix,
transformerNode,
vertices,
indices,
renderOptions
)
{
if (this.instanceCount === 0)
{
this.manager.setCurrentBatchNode(this, drawingContext);
}
// Check render options and run the batch if they differ.
// The options are constant across the whole mesh, so we check once.
renderOptions.alphaStrategy = drawingContext.alphaStrategy;
this.updateRenderOptions(renderOptions);
if (this._renderOptionsChanged)
{
this.run(drawingContext);
this.updateShaderConfig();
}
// Build the transform matrix once for the whole mesh, then project each
// vertex cheaply below.
transformerNode.setupMatrix(drawingContext, gameObject, parentMatrix);
var step = 4;
var tempPoint = this._tempPoint;
var flipV = gameObject.flipV;
var tintEffect = gameObject.tintMode;
var tint = getTint(gameObject.tint, gameObject.alpha);
var tint2 = tintEffect << 24;
var textureSources = gameObject.texture.source;
var floatsPerInstance = this.floatsPerInstance;
var instancesPerBatch = this.instancesPerBatch;
var vertexBuffer = this.vertexBufferLayout.buffer;
var vertexViewF32 = vertexBuffer.viewF32;
var vertexViewU32 = vertexBuffer.viewU32;
var triCount = (indices.length / 4) | 0;
for (var i = 0; i < triCount; i++)
{
var i4 = i * 4;
var ia = indices[i4];
var ib = indices[i4 + 1];
var ic = indices[i4 + 2];
var page = indices[i4 + 3];
var glTexture = textureSources[page].glTexture;
// Process the texture. This may start a new batch entry.
var textureDatum = this.batchTextures(glTexture, renderOptions);
var vertexOffset32 = this.instanceCount * floatsPerInstance;
// Vertex A
var ia4 = ia * step;
tempPoint.set(vertices[ia4], vertices[ia4 + 1]);
transformerNode.transformVertex(tempPoint);
var vA = vertices[ia4 + 3];
vertexViewF32[vertexOffset32++] = tempPoint.x;
vertexViewF32[vertexOffset32++] = tempPoint.y;
vertexViewF32[vertexOffset32++] = vertices[ia4 + 2];
vertexViewF32[vertexOffset32++] = flipV ? 1 - vA : vA;
vertexViewF32[vertexOffset32++] = textureDatum;
vertexViewU32[vertexOffset32++] = tint2;
vertexViewU32[vertexOffset32++] = tint;
// Vertex B
var ib4 = ib * step;
tempPoint.set(vertices[ib4], vertices[ib4 + 1]);
transformerNode.transformVertex(tempPoint);
var vB = vertices[ib4 + 3];
vertexViewF32[vertexOffset32++] = tempPoint.x;
vertexViewF32[vertexOffset32++] = tempPoint.y;
vertexViewF32[vertexOffset32++] = vertices[ib4 + 2];
vertexViewF32[vertexOffset32++] = flipV ? 1 - vB : vB;
vertexViewF32[vertexOffset32++] = textureDatum;
vertexViewU32[vertexOffset32++] = tint2;
vertexViewU32[vertexOffset32++] = tint;
// Vertex C
var ic4 = ic * step;
tempPoint.set(vertices[ic4], vertices[ic4 + 1]);
transformerNode.transformVertex(tempPoint);
var vC = vertices[ic4 + 3];
vertexViewF32[vertexOffset32++] = tempPoint.x;
vertexViewF32[vertexOffset32++] = tempPoint.y;
vertexViewF32[vertexOffset32++] = vertices[ic4 + 2];
vertexViewF32[vertexOffset32++] = flipV ? 1 - vC : vC;
vertexViewF32[vertexOffset32++] = textureDatum;
vertexViewU32[vertexOffset32++] = tint2;
vertexViewU32[vertexOffset32++] = tint;
// Increment the instance count.
this.instanceCount++;
this.currentBatchEntry.count++;
// Check whether the batch should be rendered immediately.
// This guarantees that none of the arrays are full above.
if (this.instanceCount === instancesPerBatch)
{
this.run(drawingContext);
// Now the batch is empty.
}
}
}
});
module.exports = BatchHandlerTri;