Loads of new Mesh updates

* `Mesh.setVertices` is a new method that allows you to set the verts of a Mesh Game Object from the given parameters. This allows you to modify a mesh post-creation, or populate it with data at a later stage.
* The Mesh constructor signature has changed to `scene, x, y, vertices, uvs, indicies, colors, alphas, texture, frame`, where `indicies` is the new parameter added to the list. It allows you to provide indexed vertex data to create the Mesh from, where the `indicies` array holds the vertex index information. The final list of vertices is built from this index along with the provided vertices and uvs arrays.
* You can now supply just a single numerical value as the `color` parameter in the constructor, factory method and `setVertices` method. If a number, instead of an array, it will be used as the color for all vertices created.
* You can now supply just a single numerical value as the `alpha` parameter in the constructor, factory method and `setVertices` method. If a number, instead of an array, it will be used as the alpha for all vertices created.
* The `Mesh` Game Object now extends the `SingleAlpha` component and the alpha value is factored into the final alpha value per vertex during rendering. This means you can now set the whole alpha across the Mesh using the standard `setAlpha` methods. But, if you wish to, you can still control the alpha on a per-vertex basis as well.
* The `Mesh` Game Object now has the Animation State Component. This allows you to create and play animations across the texture of a Mesh, something that previously wasn't possible. As a result, the Mesh now adds itself to the Update List when added to a Scene.
* `Mesh.setDebug` is a new method that allows you to render a debug visualisation of the Mesh vertices to a Graphics Game Object. You can provide your own Graphics instance and optionally callback that is invoked during rendering. This allows you to easily visualise the vertices of your Mesh to help debug UV mapping.
* `Mesh.debugGraphic` is a new property that holds the debug Graphics instance reference.
* `Mesh.debugCallback` is a new property that holds the debug render callback.
* `Mesh.renderDebugVerts` is a new method that acts as the default render callback for `setDebug` if none is provided.
* `Mesh.preDestroy` is a new method that will clean-up the Mesh arrays and debug references on destruction.
This commit is contained in:
Richard Davey 2020-09-13 23:11:04 +01:00
parent 3fd52ecdd3
commit 25b5c2d1d1
2 changed files with 270 additions and 74 deletions

View file

@ -4,16 +4,21 @@
* @license {@link https://opensource.org/licenses/MIT|MIT License} * @license {@link https://opensource.org/licenses/MIT|MIT License}
*/ */
var AnimationState = require('../../animations/AnimationState');
var Class = require('../../utils/Class'); var Class = require('../../utils/Class');
var Components = require('../components'); var Components = require('../components');
var GameObject = require('../GameObject'); var GameObject = require('../GameObject');
var GameObjectEvents = require('../events');
var MeshRender = require('./MeshRender'); var MeshRender = require('./MeshRender');
var NOOP = require('../../utils/NOOP');
/** /**
* @classdesc * @classdesc
* A Mesh Game Object. * A Mesh Game Object.
* *
* The Mesh object is WebGL only and does not have a Canvas counterpart.
*
* The Mesh origin is always 0.5 x 0.5 and cannot be changed.
*
* @class Mesh * @class Mesh
* @extends Phaser.GameObjects.GameObject * @extends Phaser.GameObjects.GameObject
* @memberof Phaser.GameObjects * @memberof Phaser.GameObjects
@ -21,6 +26,7 @@ var NOOP = require('../../utils/NOOP');
* @webglOnly * @webglOnly
* @since 3.0.0 * @since 3.0.0
* *
* @extends Phaser.GameObjects.Components.AlphaSingle
* @extends Phaser.GameObjects.Components.BlendMode * @extends Phaser.GameObjects.Components.BlendMode
* @extends Phaser.GameObjects.Components.Depth * @extends Phaser.GameObjects.Components.Depth
* @extends Phaser.GameObjects.Components.Mask * @extends Phaser.GameObjects.Components.Mask
@ -35,17 +41,19 @@ var NOOP = require('../../utils/NOOP');
* @param {number} x - The horizontal position of this Game Object in the world. * @param {number} x - The horizontal position of this Game Object in the world.
* @param {number} y - The vertical position of this Game Object in the world. * @param {number} y - The vertical position of this Game Object in the world.
* @param {number[]} vertices - An array containing the vertices data for this Mesh. * @param {number[]} vertices - An array containing the vertices data for this Mesh.
* @param {number[]} uv - An array containing the uv data for this Mesh. * @param {number[]} uvs - An array containing the uv data for this Mesh.
* @param {number[]} colors - An array containing the color data for this Mesh. * @param {number[]} [indicies] - An array containing the vertex indicies for this Mesh.
* @param {number[]} alphas - An array containing the alpha data for this Mesh. * @param {number|number[]} [colors] - An array containing the color data for this Mesh.
* @param {(string|Phaser.Textures.Texture)} texture - The key, or instance of the Texture this Game Object will use to render with, as stored in the Texture Manager. * @param {number|number[]} [alphas] - An array containing the alpha data for this Mesh.
* @param {(string|integer)} [frame] - An optional frame from the Texture this Game Object is rendering with. * @param {string|Phaser.Textures.Texture} [texture] - The key, or instance of the Texture this Game Object will use to render with, as stored in the Texture Manager.
* @param {string|integer} [frame] - An optional frame from the Texture this Game Object is rendering with.
*/ */
var Mesh = new Class({ var Mesh = new Class({
Extends: GameObject, Extends: GameObject,
Mixins: [ Mixins: [
Components.AlphaSingle,
Components.BlendMode, Components.BlendMode,
Components.Depth, Components.Depth,
Components.Mask, Components.Mask,
@ -60,44 +68,18 @@ var Mesh = new Class({
initialize: initialize:
function Mesh (scene, x, y, vertices, uv, colors, alphas, texture, frame) function Mesh (scene, x, y, vertices, uvs, indicies, colors, alphas, texture, frame)
{ {
GameObject.call(this, scene, 'Mesh'); GameObject.call(this, scene, 'Mesh');
if (vertices.length !== uv.length) /**
{ * The Animation State of this Mesh.
throw new Error('Mesh Vertex count must match UV count'); *
} * @name Phaser.GameObjects.Mesh#anims
* @type {Phaser.Animation.AnimationState}
var verticesUB = (vertices.length / 2) | 0; * @since 3.50.0
*/
if (colors.length > 0 && colors.length < verticesUB) this.anims = new AnimationState(this);
{
throw new Error('Mesh Color count must match Vertex count');
}
if (alphas.length > 0 && alphas.length < verticesUB)
{
throw new Error('Mesh Alpha count must match Vertex count');
}
var i;
if (colors.length === 0)
{
for (i = 0; i < verticesUB; ++i)
{
colors[i] = 0xFFFFFF;
}
}
if (alphas.length === 0)
{
for (i = 0; i < verticesUB; ++i)
{
alphas[i] = 1.0;
}
}
/** /**
* An array containing the vertices data for this Mesh. * An array containing the vertices data for this Mesh.
@ -106,7 +88,7 @@ var Mesh = new Class({
* @type {Float32Array} * @type {Float32Array}
* @since 3.0.0 * @since 3.0.0
*/ */
this.vertices = new Float32Array(vertices); this.vertices;
/** /**
* An array containing the uv data for this Mesh. * An array containing the uv data for this Mesh.
@ -115,7 +97,7 @@ var Mesh = new Class({
* @type {Float32Array} * @type {Float32Array}
* @since 3.0.0 * @since 3.0.0
*/ */
this.uv = new Float32Array(uv); this.uv;
/** /**
* An array containing the color data for this Mesh. * An array containing the color data for this Mesh.
@ -124,7 +106,7 @@ var Mesh = new Class({
* @type {Uint32Array} * @type {Uint32Array}
* @since 3.0.0 * @since 3.0.0
*/ */
this.colors = new Uint32Array(colors); this.colors;
/** /**
* An array containing the alpha data for this Mesh. * An array containing the alpha data for this Mesh.
@ -133,32 +115,254 @@ var Mesh = new Class({
* @type {Float32Array} * @type {Float32Array}
* @since 3.0.0 * @since 3.0.0
*/ */
this.alphas = new Float32Array(alphas); this.alphas;
/** /**
* Fill or additive mode used when blending the color values? * The tint fill mode.
*
* 0 = An additive tint (the default), where vertices colors are blended with the texture.
* 1 = A fill tint, where the vertices colors replace the texture, but respects texture alpha.
* 2 = A complete tint, where the vertices colors replace the texture, including alpha, entirely.
* *
* @name Phaser.GameObjects.Mesh#tintFill * @name Phaser.GameObjects.Mesh#tintFill
* @type {boolean} * @type {integer}
* @default false
* @since 3.11.0 * @since 3.11.0
*/ */
this.tintFill = false; this.tintFill = 0;
/**
* You can optionally choose to render the vertices of this Mesh to a Graphics instance.
*
* Achieve this by setting the `debugCallback` and the `debugGraphic` properties.
*
* You can do this in a single call via the `Mesh.setDebug` method, which will use the
* built-in debug function. You can also set it to your own callback. The callback
* will be invoked _once per render_ and sent the following parameters:
*
* `debugCallback(src, meshLength, verts)`
*
* `src` is the Mesh instance being debugged.
* `meshLength` is the number of mesh vertices in total.
* `verts` is an array of the translated vertex coordinates.
*
* To disable rendering, set this property back to `null`.
*
* @name Phaser.GameObjects.Mesh#debugCallback
* @type {function}
* @since 3.50.0
*/
this.debugCallback = null;
/**
* The Graphics instance that the debug vertices will be drawn to, if `setDebug` has
* been called.
*
* @name Phaser.GameObjects.Mesh#debugGraphic
* @type {Phaser.GameObjects.Graphics}
* @since 3.50.0
*/
this.debugGraphic = null;
this.setTexture(texture, frame); this.setTexture(texture, frame);
this.setPosition(x, y); this.setPosition(x, y);
this.setSizeToFrame(); this.setSizeToFrame();
this.setVertices(vertices, uvs, indicies, colors, alphas);
this.initPipeline(); this.initPipeline();
this.on(GameObjectEvents.ADDED_TO_SCENE, this.addedToScene, this);
this.on(GameObjectEvents.REMOVED_FROM_SCENE, this.removedFromScene, this);
},
// Overrides Game Object method
addedToScene: function ()
{
this.scene.sys.updateList.add(this);
},
// Overrides Game Object method
removedFromScene: function ()
{
this.scene.sys.updateList.remove(this);
},
setVertices: function (vertices, uvs, indicies, colors, alphas)
{
if (colors === undefined) { colors = 0xffffff; }
if (alphas === undefined) { alphas = 1; }
if (vertices.length !== uvs.length)
{
throw new Error('Mesh - vertices and uv count not equal');
}
if (Array.isArray(indicies))
{
var verticesFull = [];
var uvsFull = [];
for (var i = 0; i < indicies.length; i++)
{
var index = indicies[i] * 2;
verticesFull.push(vertices[index], vertices[index + 1]);
uvsFull.push(uvs[index], uvs[index + 1]);
}
vertices = verticesFull;
uvs = uvsFull;
}
var halfVerts = Math.floor(vertices.length / 2);
if (!Array.isArray(colors))
{
var tempColor = colors;
colors = [];
for (var c = 0; c < halfVerts; c++)
{
colors.push(tempColor);
}
}
if (!Array.isArray(alphas))
{
var tempAlpha = alphas;
alphas = [];
for (var a = 0; a < halfVerts; a++)
{
alphas.push(tempAlpha);
}
}
this.vertices = new Float32Array(vertices);
this.uv = new Float32Array(uvs);
this.colors = new Uint32Array(colors);
this.alphas = new Float32Array(alphas);
return this;
}, },
/** /**
* This method is left intentionally empty and does not do anything. * This method enables rendering of the Mesh vertices to the given Graphics instance.
* It is retained to allow a Mesh or Quad to be added to a Container.
* *
* @method Phaser.GameObjects.Mesh#setAlpha * If you enable this feature, you **must** call `Graphics.clear()` in your Scene `update`,
* @since 3.17.0 * otherwise the Graphics instance you provide to debug will fill-up with draw calls,
* eventually crashing the browser. This is not done automatically to allow you to debug
* draw multiple Mesh objects to a single Graphics instance.
*
* The Mesh class has a built-in debug rendering callback `Mesh.renderDebugVerts`, however
* you can also provide your own callback to be used instead. Do this by setting the `callback` parameter.
*
* The callback is invoked _once per render_ and sent the following parameters:
*
* `callback(src, meshLength, verts)`
*
* `src` is the Mesh instance being debugged.
* `meshLength` is the number of mesh vertices in total.
* `verts` is an array of the translated vertex coordinates.
*
* If using your own callback you do not have to provide a Graphics instance to this method.
*
* To disable debug rendering, to either your own callback or the built-in one, call this method
* with no arguments.
*
* @method Phaser.GameObjects.Mesh#setDebug
* @since 3.50.0
*
* @param {Phaser.GameObjects.Graphics} [graphic] - The Graphic instance to render to if using the built-in callback.
* @param {function} [callback] - The callback to invoke during debug render. Leave as undefined to use the built-in callback.
*
* @return {this} This Game Object instance.
*/ */
setAlpha: NOOP setDebug: function (graphic, callback)
{
this.debugGraphic = graphic;
if (!graphic && !callback)
{
this.debugCallback = null;
}
else if (!callback)
{
this.debugCallback = this.renderDebugVerts;
}
else
{
this.debugCallback = callback;
}
return this;
},
/**
* The Mesh update loop.
*
* @method Phaser.GameObjects.Mesh#preUpdate
* @protected
* @since 3.50.0
*
* @param {number} time - The current timestamp.
* @param {number} delta - The delta time, in ms, elapsed since the last frame.
*/
preUpdate: function (time, delta)
{
this.anims.update(time, delta);
},
/**
* The built-in Mesh vertices debug rendering method.
*
* See `Mesh.setDebug` for more details.
*
* @method Phaser.GameObjects.Mesh#renderDebugVerts
* @since 3.50.0
*
* @param {Phaser.GameObjects.Mesh} src - The Mesh object being rendered.
* @param {integer} meshLength - The number of vertices in the mesh.
* @param {number[]} verts - An array of translated vertex coordinates.
*/
renderDebugVerts: function (src, meshLength, verts)
{
var graphic = src.debugGraphic;
for (var i = 0; i < meshLength; i += 6)
{
var x0 = verts[i + 0];
var y0 = verts[i + 1];
var x1 = verts[i + 2];
var y1 = verts[i + 3];
var x2 = verts[i + 4];
var y2 = verts[i + 5];
graphic.strokeTriangle(x0, y0, x1, y1, x2, y2);
}
},
/**
* Handles the pre-destroy step for the Mesh, which removes the Animation component and typed arrays.
*
* @method Phaser.GameObjects.Mesh#preDestroy
* @private
* @since 3.50.0
*/
preDestroy: function ()
{
this.anims.destroy();
this.anims = undefined;
this.vertices = null;
this.uv = null;
this.colors = null;
this.alphas = null;
this.debugCallback = null;
this.debugGraphic = null;
}
}); });

View file

@ -77,6 +77,9 @@ var MeshWebGLRenderer = function (renderer, src, interpolationPercentage, camera
var colorIndex = 0; var colorIndex = 0;
var tintEffect = src.tintFill; var tintEffect = src.tintFill;
var debugCallback = src.debugCallback;
var debugVerts = [];
for (var i = 0; i < meshVerticesLength; i += 2) for (var i = 0; i < meshVerticesLength; i += 2)
{ {
var x = vertices[i + 0]; var x = vertices[i + 0];
@ -91,40 +94,29 @@ var MeshWebGLRenderer = function (renderer, src, interpolationPercentage, camera
ty = Math.round(ty); ty = Math.round(ty);
} }
if (debugCallback)
{
debugVerts[i + 0] = tx;
debugVerts[i + 1] = ty;
}
vertexViewF32[++vertexOffset] = tx; vertexViewF32[++vertexOffset] = tx;
vertexViewF32[++vertexOffset] = ty; vertexViewF32[++vertexOffset] = ty;
vertexViewF32[++vertexOffset] = uvs[i + 0]; vertexViewF32[++vertexOffset] = uvs[i + 0];
vertexViewF32[++vertexOffset] = uvs[i + 1]; vertexViewF32[++vertexOffset] = uvs[i + 1];
vertexViewF32[++vertexOffset] = textureUnit; vertexViewF32[++vertexOffset] = textureUnit;
vertexViewF32[++vertexOffset] = tintEffect; vertexViewF32[++vertexOffset] = tintEffect;
vertexViewU32[++vertexOffset] = Utils.getTintAppendFloatAlpha(colors[colorIndex], camera.alpha * alphas[colorIndex]); vertexViewU32[++vertexOffset] = Utils.getTintAppendFloatAlpha(colors[colorIndex], camera.alpha * src.alpha * alphas[colorIndex]);
colorIndex++; colorIndex++;
} }
pipeline.vertexCount += vertexCount; pipeline.vertexCount += vertexCount;
/* if (debugCallback)
pipeline.flush();
for (var i = 0; i < meshVerticesLength; i += 2)
{ {
var x = vertices[i + 0]; debugCallback.call(src, src, meshVerticesLength, debugVerts);
var y = vertices[i + 1];
var tx = x * calcMatrix.a + y * calcMatrix.c + calcMatrix.e;
var ty = x * calcMatrix.b + y * calcMatrix.d + calcMatrix.f;
if (camera.roundPixels)
{
tx = Math.round(tx);
ty = Math.round(ty);
}
pipeline.drawFillRect(tx, ty, 2, 2, 0x00ff00, 1);
} }
*/
}; };
module.exports = MeshWebGLRenderer; module.exports = MeshWebGLRenderer;