Merged the Layer3D Game Object and pipeline back in for now

This commit is contained in:
Richard Davey 2020-10-07 17:44:36 +01:00
parent c3fe480905
commit dda4431366
19 changed files with 3395 additions and 1 deletions

214
src/display/RGB.js Normal file
View file

@ -0,0 +1,214 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2020 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var Class = require('../utils/Class');
var NOOP = require('../utils/NOOP');
/**
* @classdesc
* The RGB class holds a single color value and allows for easy modification and reading of it,
* with optional on-change callback notification and a dirty flag.
*
* @class RGB
* @memberof Phaser.Display
* @constructor
* @since 3.50.0
*
* @param {number} [red=0] - The red color value. A number between 0 and 1.
* @param {number} [green=0] - The green color value. A number between 0 and 1.
* @param {number} [blue=0] - The blue color value. A number between 0 and 1.
*/
var RGB = new Class({
initialize:
function RGB (red, green, blue)
{
/**
* Cached RGB values.
*
* @name Phaser.Display.RGB#_rgb
* @type {number[]}
* @private
* @since 3.50.0
*/
this._rgb = [ 0, 0, 0 ];
/**
* This callback will be invoked each time one of the RGB color values change.
*
* The callback is sent the new color values as the parameters.
*
* @name Phaser.Display.RGB#onChangeCallback
* @type {function}
* @since 3.50.0
*/
this.onChangeCallback = NOOP;
/**
* Is this color dirty?
*
* @name Phaser.Display.RGB#dirty
* @type {boolean}
* @since 3.50.0
*/
this.dirty = false;
this.set(red, green, blue);
},
/**
* Sets the red, green and blue values of this RGB object, flags it as being
* dirty and then invokes the `onChangeCallback`, if set.
*
* @method Phaser.Display.RGB#set
* @since 3.50.0
*
* @param {number} [red=0] - The red color value. A number between 0 and 1.
* @param {number} [green=0] - The green color value. A number between 0 and 1.
* @param {number} [blue=0] - The blue color value. A number between 0 and 1.
*
* @return {this} This RGB instance.
*/
set: function (red, green, blue)
{
if (red === undefined) { red = 0; }
if (green === undefined) { green = 0; }
if (blue === undefined) { blue = 0; }
this._rgb = [ red, green, blue ];
this.onChange();
return this;
},
/**
* Compares the given rgb parameters with those in this object and returns
* a boolean `true` value if they are equal, otherwise it returns `false`.
*
* @method Phaser.Display.RGB#equals
* @since 3.50.0
*
* @param {number} red - The red value to compare with this object.
* @param {number} green - The green value to compare with this object.
* @param {number} blue - The blue value to compare with this object.
*
* @return {boolean} `true` if the given values match those in this object, otherwise `false`.
*/
equals: function (red, green, blue)
{
var rgb = this._rgb;
return (rgb.r === red && rgb.g === green && rgb.b === blue);
},
/**
* Internal on change handler. Sets this object as being dirty and
* then invokes the `onChangeCallback`, if set, passing in the
* new RGB values.
*
* @method Phaser.Display.RGB#onChange
* @since 3.50.0
*/
onChange: function ()
{
this.dirty = true;
var rgb = this._rgb;
this.onChangeCallback.call(this, rgb[0], rgb[1], rgb[2]);
},
/**
* The red color value. Between 0 and 1.
*
* Changing this property will flag this RGB object as being dirty
* and invoke the `onChangeCallback` , if set.
*
* @name Phaser.Display.RGB#r
* @type {number}
* @since 3.50.0
*/
r: {
get: function ()
{
return this._rgb[0];
},
set: function (value)
{
this._rgb[0] = value;
this.onChange();
}
},
/**
* The green color value. Between 0 and 1.
*
* Changing this property will flag this RGB object as being dirty
* and invoke the `onChangeCallback` , if set.
*
* @name Phaser.Display.RGB#g
* @type {number}
* @since 3.50.0
*/
g: {
get: function ()
{
return this._rgb[1];
},
set: function (value)
{
this._rgb[1] = value;
this.onChange();
}
},
/**
* The blue color value. Between 0 and 1.
*
* Changing this property will flag this RGB object as being dirty
* and invoke the `onChangeCallback` , if set.
*
* @name Phaser.Display.RGB#b
* @type {number}
* @since 3.50.0
*/
b: {
get: function ()
{
return this._rgb[2];
},
set: function (value)
{
this._rgb[2] = value;
this.onChange();
}
},
/**
* Nulls any external references this object contains.
*
* @method Phaser.Display.RGB#destroy
* @since 3.50.0
*/
destroy: function ()
{
this.onChangeCallback = null;
}
});
module.exports = RGB;

View file

@ -124,12 +124,15 @@ if (typeof WEBGL_RENDERER)
// WebGL only Game Objects // WebGL only Game Objects
GameObjects.Shader = require('./shader/Shader'); GameObjects.Shader = require('./shader/Shader');
GameObjects.Mesh = require('./mesh/Mesh'); GameObjects.Mesh = require('./mesh/Mesh');
GameObjects.Layer3D = require('./layer3d/Layer3D');
GameObjects.Factories.Shader = require('./shader/ShaderFactory'); GameObjects.Factories.Shader = require('./shader/ShaderFactory');
GameObjects.Factories.Mesh = require('./mesh/MeshFactory'); GameObjects.Factories.Mesh = require('./mesh/MeshFactory');
GameObjects.Factories.Layer3D = require('./layer3d/Layer3DFactory');
GameObjects.Creators.Shader = require('./shader/ShaderCreator'); GameObjects.Creators.Shader = require('./shader/ShaderCreator');
GameObjects.Creators.Mesh = require('./mesh/MeshCreator'); GameObjects.Creators.Mesh = require('./mesh/MeshCreator');
GameObjects.Creators.Layer3D = require('./layer3d/Layer3DCreator');
GameObjects.Light = require('./lights/Light'); GameObjects.Light = require('./lights/Light');
GameObjects.LightsManager = require('./lights/LightsManager'); GameObjects.LightsManager = require('./lights/LightsManager');

View file

@ -0,0 +1,575 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2020 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var Class = require('../../utils/Class');
var Components = require('../components');
var CONST = require('../../renderer/webgl/pipelines/const');
var GameObject = require('../GameObject');
var GameObjectEvents = require('../events');
var Layer3DCamera = require('./Layer3DCamera');
var Layer3DLight = require('./Layer3DLight');
var Layer3DRender = require('./Layer3DRender');
var Model = require('../../geom/mesh/Model');
var RGB = require('../../display/RGB');
/**
* @classdesc
* A Layer3D Game Object.
*
* The Mesh object is WebGL only and does not have a Canvas counterpart.
*
* TODO - Finish this.
*
* @class Layer3D
* @extends Phaser.GameObjects.GameObject
* @memberof Phaser.GameObjects
* @constructor
* @webglOnly
* @since 3.50.0
*
* @extends Phaser.GameObjects.Components.AlphaSingle
* @extends Phaser.GameObjects.Components.BlendMode
* @extends Phaser.GameObjects.Components.Depth
* @extends Phaser.GameObjects.Components.Mask
* @extends Phaser.GameObjects.Components.Pipeline
* @extends Phaser.GameObjects.Components.Transform
* @extends Phaser.GameObjects.Components.Visible
* @extends Phaser.GameObjects.Components.ScrollFactor
* @extends Phaser.GameObjects.Components.Size
*
* @param {Phaser.Scene} scene - The Scene to which this Game Object belongs. A Game Object can only belong to one Scene at a time.
* @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.
*/
var Layer3D = new Class({
Extends: GameObject,
Mixins: [
Components.AlphaSingle,
Components.BlendMode,
Components.Depth,
Components.Mask,
Components.Pipeline,
Components.Transform,
Components.Visible,
Components.ScrollFactor,
Components.Size,
Layer3DRender
],
initialize:
function Layer3D (scene, x, y)
{
GameObject.call(this, scene, 'Layer3D');
/**
* A Camera which can be used to control the view of the models being managed
* by this Layer3D. It will default to have an fov of 45 and be positioned at 0, 0, -10,
* with a near of 0.01 and far of 1000. You can change all of these by using the
* methods and properties available on the `Layer3DCamera` class.
*
* @name Phaser.GameObjects.Layer3D#camera
* @type {Phaser.GameObjects.Layer3DCamera}
* @since 3.50.0
*/
this.camera = new Layer3DCamera(this, 45, 0, 0, -10, 0.01, 1000);
/**
* An ambient light source for the entire Layer3D scene and all models it is rendering.
*
* It is created at a position of 0, -100, 0 with full ambient, diffuse and specular
* values. You can change all of these by using the methods and properties
* available on the `Layer3DLight` class.
*
* @name Phaser.GameObjects.Layer3D#light
* @type {Phaser.GameObjects.Layer3DLight}
* @since 3.50.0
*/
this.light = new Layer3DLight(this, 0, 100, 0);
/**
* The color of the fog.
*
* By default it is 0,0,0, which is black.
*
* @name Phaser.GameObjects.Layer3D#fogColor
* @type {Phaser.Display.RGB}
* @since 3.50.0
*/
this.fogColor = new RGB();
/**
* The minimum distance from which fog starts to affect objects closer than it.
*
* @name Phaser.GameObjects.Layer3D#fogNear
* @type {number}
* @since 3.50.0
*/
this.fogNear = 0;
/**
* The maximum distance from which fog starts to affect objects further than it.
*
* @name Phaser.GameObjects.Layer3D#fogFar
* @type {number}
* @since 3.50.0
*/
this.fogFar = Infinity;
/**
* An array of model instances that have been created in this Layer3D.
*
* This array can be sorted, by your own functions, to control model rendering order.
*
* @name Phaser.GameObjects.Layer3D#models
* @type {Phaser.Geom.Mesh.Model[]}
* @since 3.50.0
*/
this.models = [];
/**
* Internal cached value.
*
* @name Phaser.GameObjects.Layer3D#_prevWidth
* @type {number}
* @private
* @since 3.50.0
*/
this._prevWidth = 0;
/**
* Internal cached value.
*
* @name Phaser.GameObjects.Layer3D#_prevHeight
* @type {number}
* @private
* @since 3.50.0
*/
this._prevHeight = 0;
var renderer = scene.sys.renderer;
this.setPosition(x, y);
this.setSize(renderer.width, renderer.height);
this.initPipeline(CONST.MESH_PIPELINE);
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);
},
/**
* Removes all models from this Layer3D, calling `destroy` on each one of them.
*
* @method Phaser.GameObjects.Layer3D#clearModels
* @since 3.50.0
*/
clearModels: function ()
{
var models = this.models;
for (var i = 0; i < models.length; i++)
{
models[i].destroy();
}
this.models = [];
},
/**
* This method creates a new blank Model instance and adds it to this Layer3D.
*
* You still need to tell it how many vertices it's going to contain in total, but you can
* populate the vertex data at a later stage after calling this. It won't try to render
* while it has no vertices.
*
* @method Phaser.GameObjects.Layer3D#addModel
* @since 3.50.0
*
* @param {number} verticesCount - The total number of vertices this model can contain.
* @param {string|Phaser.Textures.Texture} [texture] - The key, or instance of the Texture this model will use to render with, as stored in the Texture Manager.
* @param {string|integer} [frame] - An optional frame from the Texture this model is rendering with. Ensure your UV data also matches this frame.
* @param {number} [x=0] - The x position of the Model.
* @param {number} [y=0] - The y position of the Model.
* @param {number} [z=0] - The z position of the Model.
*
* @return {Phaser.Geom.Mesh.Model} The model instance that was created.
*/
addModel: function (verticesCount, texture, frame, x, y, z)
{
var model = new Model(this, verticesCount, texture, frame, x, y, z);
this.models.push(model);
return model;
},
/**
* This method creates a new Model based on a loaded triangulated Wavefront OBJ.
*
* The obj file should have been loaded via OBJFile:
*
* ```javascript
* this.load.obj(key, url, [ flipUV ]);
* ```
*
* Then use the key it was loaded under in this method.
*
* If the model has a texture, you must provide it as the second parameter.
*
* The model is then added to this Layer3D. A single Layer3D can contain multiple models
* without impacting each other. Equally, multiple models can all share the same base OBJ
* data.
*
* Make sure your 3D package has triangulated the model data prior to exporting it.
*
* You can scale the model data during import, which will set the new 'base' scale for the model.
*
* You can also offset the models generated vertex positions via the `originX`, `originY` and `originZ`
* parameters, which will change the rotation origin of the model. The model itself can be positioned,
* rotated and scaled independantly of these settings, so don't use them to position the model, just
* use them to offset the base values.
*
* @method Phaser.GameObjects.Layer3D#addModelFromOBJ
* @since 3.50.0
*
* @param {string} key - The key of the data in the OBJ Cache to create the model from.
* @param {string|Phaser.Textures.Texture} [texture] - The key, or instance of the Texture this model will use to render with, as stored in the Texture Manager.
* @param {string|integer} [frame] - An optional frame from the Texture this model is rendering with. Ensure your UV data also matches this frame.
* @param {number} [scale=1] - An amount to scale the model data by during creation.
* @param {number} [originX=0] - The x origin of the model vertices during creation.
* @param {number} [originY=0] - The y origin of the model vertices during creation.
* @param {number} [originZ=0] - The z origin of the model vertices during creation.
*
* @return {Phaser.Geom.Mesh.Model|Phaser.Geom.Mesh.Model[]} The Model instance that was created. If the OBJ contained multiple models then an array of Model instances is returned.
*/
addModelFromOBJ: function (key, texture, frame, scale, originX, originY, originZ)
{
var model = [];
var data = this.scene.sys.cache.obj.get(key);
if (data)
{
model = this.addModelFromData(data, texture, frame, scale, originX, originY, originZ);
}
return (model.length === 1) ? model[0] : model;
},
/**
* This method creates a new Model based on the parsed triangulated model data.
*
* The data should have been parsed in advance via a function such as `ParseObj`:
*
* ```javascript
* const data = Phaser.Geom.Mesh.ParseObj(rawData, flipUV);
*
* Layer3D.addModelFromData(data, texture, frame);
* ```
*
* If the model has a texture, you must provide it as the second parameter.
*
* The model is then added to this Layer3D. A single Layer3D can contain multiple models
* without impacting each other. Equally, multiple models can all share the same base OBJ
* data.
*
* Make sure your 3D package has triangulated the model data prior to exporting it.
*
* You can scale the model data during import, which will set the new 'base' scale for the model.
*
* You can also offset the models generated vertex positions via the `originX`, `originY` and `originZ`
* parameters, which will change the rotation origin of the model. The model itself can be positioned,
* rotated and scaled independantly of these settings, so don't use them to position the model, just
* use them to offset the base values.
*
* @method Phaser.GameObjects.Layer3D#addModelFromData
* @since 3.50.0
*
* @param {array} data - The parsed model data.
* @param {string|Phaser.Textures.Texture} [texture] - The key, or instance of the Texture this model will use to render with, as stored in the Texture Manager.
* @param {string|integer} [frame] - An optional frame from the Texture this model is rendering with. Ensure your UV data also matches this frame.
* @param {number} [scale=1] - An amount to scale the model data by during creation.
* @param {number} [originX=0] - The x origin of the model vertices during creation.
* @param {number} [originY=0] - The y origin of the model vertices during creation.
* @param {number} [originZ=0] - The z origin of the model vertices during creation.
*
* @return {Phaser.Geom.Mesh.Model|Phaser.Geom.Mesh.Model[]} The Model instance that was created. If the data contained multiple models then an array of Model instances is returned.
*/
addModelFromData: function (data, texture, frame, scale, originX, originY, originZ)
{
if (scale === undefined) { scale = 1; }
if (originX === undefined) { originX = 0; }
if (originY === undefined) { originY = 0; }
if (originZ === undefined) { originZ = 0; }
var results = [];
for (var m = 0; m < data.models.length; m++)
{
var modelData = data.models[m];
var vertices = modelData.vertices;
var textureCoords = modelData.textureCoords;
var normals = modelData.vertexNormals;
var faces = modelData.faces;
var model = this.addModel(faces.length * 3, texture, frame);
var defaultUV1 = { u: 0, v: 1 };
var defaultUV2 = { u: 0, v: 0 };
var defaultUV3 = { u: 1, v: 1 };
for (var i = 0; i < faces.length; i++)
{
var face = faces[i];
// {textureCoordsIndex: 0, vertexIndex: 16, vertexNormalIndex: 16}
var v1 = face.vertices[0];
var v2 = face.vertices[1];
var v3 = face.vertices[2];
// {x: 0.19509, y: 0.980785, z: 0}
var m1 = vertices[v1.vertexIndex];
var m2 = vertices[v2.vertexIndex];
var m3 = vertices[v3.vertexIndex];
var n1 = normals[v1.vertexNormalIndex];
var n2 = normals[v2.vertexNormalIndex];
var n3 = normals[v3.vertexNormalIndex];
var t1 = v1.textureCoordsIndex;
var t2 = v2.textureCoordsIndex;
var t3 = v3.textureCoordsIndex;
var uv1 = (t1 === -1) ? defaultUV1 : textureCoords[t1];
var uv2 = (t2 === -1) ? defaultUV2 : textureCoords[t2];
var uv3 = (t3 === -1) ? defaultUV3 : textureCoords[t3];
model.addVertex(originX + m1.x * scale, originY + m1.y * scale, originZ + m1.z * scale, uv1.u, uv1.v, n1.x, n1.y, n1.z);
model.addVertex(originX + m2.x * scale, originY + m2.y * scale, originZ + m2.z * scale, uv2.u, uv2.v, n2.x, n2.y, n2.z);
model.addVertex(originX + m3.x * scale, originY + m3.y * scale, originZ + m3.z * scale, uv3.u, uv3.v, n3.x, n3.y, n3.z);
}
results.push(model);
}
return results;
},
/**
* This method creates a new Model based on the given triangulated vertices arrays.
*
* Adds vertices to this model by parsing the given arrays.
*
* This method will take vertex data in one of two formats, based on the `containsZ` parameter.
*
* If your vertex data are `x`, `y` pairs, then `containsZ` should be `false` (this is the default)
*
* If your vertex data is groups of `x`, `y` and `z` values, then the `containsZ` parameter must be true.
*
* The `uvs` parameter is a numeric array consisting of `u` and `v` pairs.
* The `normals` parameter is a numeric array consisting of `x`, `y` vertex normal values and, if `containsZ` is true, `z` values as well.
* The `indicies` parameter is an optional array that, if given, is an indexed list of vertices to be added.
*
* The following example will create a 256 x 256 sized quad using an index array:
*
* ```javascript
* const vertices = [
* -128, 128,
* 128, 128,
* -128, -128,
* 128, -128
* ];
*
* const uvs = [
* 0, 1,
* 1, 1,
* 0, 0,
* 1, 0
* ];
*
* const indices = [ 0, 2, 1, 2, 3, 1 ];
*
* Layer3D.addModelFromVertices(vertices, uvs, indicies);
* ```
*
* You cannot add more vertices to a model than the total specified when the model was created.
* If you need to clear all vertices first, call `Model.resetVertices`.
*
* @method Phaser.GameObjects.Layer3D#addModelFromVertices
* @since 3.50.0
*
* @param {number[]} vertices - The vertices array. Either `xy` pairs, or `xyz` if the `containsZ` parameter is `true`.
* @param {number[]} uvs - The UVs pairs array.
* @param {number[]} [normals] - Optional vertex normals array. If you don't have one, pass `null` or an empty array.
* @param {number[]} [indicies] - Optional vertex indicies array. If you don't have one, pass `null` or an empty array.
* @param {boolean} [containsZ=false] - Does the vertices data include a `z` component?
* @param {string|Phaser.Textures.Texture} [texture] - The key, or instance of the Texture the model will use to render with, as stored in the Texture Manager.
* @param {string|integer} [frame] - An optional frame from the Texture the model is rendering with.
*
* @return {Phaser.Geom.Mesh.Model} The Model instance that was created.
*/
addModelFromVertices: function (vertices, uvs, normals, indicies, containsZ, texture, frame)
{
var isIndexed = (Array.isArray(indicies) && indicies.length > 0);
var verticesCount = (isIndexed) ? indicies.length : vertices.length;
if (!isIndexed)
{
verticesCount /= 2;
}
var model = this.addModel(verticesCount, texture, frame, 0, 0, 0);
model.addVertices(vertices, uvs, normals, indicies, containsZ);
return model;
},
/**
* Sets the fog values for this Layer3D, including the fog color and the near and
* far distance values.
*
* By default, fog effects all models in this layer.
*
* If you do not wish to have a fog effect, see the `disableFog` method.
*
* @method Phaser.GameObjects.Layer3D#setFog
* @since 3.50.0
*
* @param {number} red - The red color component of the fog. A value between 0 and 1.
* @param {number} green - The green color component of the fog. A value between 0 and 1.
* @param {number} blue - The blue color component of the fog. A value between 0 and 1.
* @param {number} [near] - The 'near' value of the fog.
* @param {number} [far] - The 'far' value of the fog, beyond which objects are 'fogged' out.
*
* @return {this} This Layer3D Game Object.
*/
setFog: function (red, green, blue, near, far)
{
if (near === undefined) { near = this.fogNear; }
if (far === undefined) { far = this.fogFar; }
this.fogColor.set(red, green, blue);
this.fogNear = near;
this.fogFar = far;
return this;
},
/**
* Disables fog for this Layer3D and all models it renders.
*
* To re-enable fog, just call `setFog` and provide new color, near and far values.
*
* @method Phaser.GameObjects.Layer3D#disableFog
* @since 3.50.0
*
* @return {this} This Layer3D Game Object.
*/
disableFog: function ()
{
this.fogFar = Infinity;
return this;
},
/**
* The Layer3D update loop.
*
* @method Phaser.GameObjects.Layer3D#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)
{
var width = this.width;
var height = this.height;
var camera = this.camera;
if (camera.dirtyProjection || width !== this._prevWidth || height !== this._prevHeight)
{
camera.updateProjectionMatrix(width, height);
this._prevWidth = width;
this._prevHeight = height;
}
var models = this.models;
for (var i = 0; i < models.length; i++)
{
var model = models[i];
if (model.visible)
{
model.preUpdate(time, delta);
}
}
},
/**
* Resets all of the dirty cache values this Layer3D object uses.
*
* This is called automatically at the end of the render step.
*
* @method Phaser.GameObjects.Layer3D#resetDirtyFlags
* @protected
* @since 3.50.0
*/
resetDirtyFlags: function ()
{
this.camera.dirtyView = false;
this.camera.dirtyProjection = false;
this.light.ambient.dirty = false;
this.light.diffuse.dirty = false;
this.light.specular.dirty = false;
this.fogColor.dirty = false;
},
/**
* The destroy step for this Layer3D, which removes all models, destroys the camera and
* nulls external references.
*
* @method Phaser.GameObjects.Layer3D#preDestroy
* @private
* @since 3.50.0
*/
preDestroy: function ()
{
this.clearModels();
this.camera.destroy();
this.light.destroy();
this.camera = null;
this.light = null;
}
});
module.exports = Layer3D;

View file

@ -0,0 +1,716 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2020 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var Class = require('../../utils/Class');
var DegToRad = require('../../math/DegToRad');
var GetFastValue = require('../../utils/object/GetFastValue');
var INPUT_EVENTS = require('../../input/events');
var Matrix4 = require('../../math/Matrix4');
var Vector3 = require('../../math/Vector3');
var Vector4 = require('../../math/Vector4');
/**
* @classdesc
* The Layer3D Camera.
*
* @class Layer3DCamera
* @memberof Phaser.GameObjects
* @constructor
* @since 3.50.0
*/
var Layer3DCamera = new Class({
initialize:
function Layer3DCamera (layer, fov, x, y, z, near, far)
{
/**
* The Layer3D instance this camera belongs to.
*
* A camera can only belong to a single Layer3D instance.
*
* You should consider this property as being read-only. You cannot move a
* camera to another Layer3D by simply changing it.
*
* @name Phaser.GameObjects.Layer3DCamera#layer
* @type {Phaser.GameObjects.Layer3D}
* @since 3.50.0
*/
this.layer = layer;
/**
* The Scene Input Plugin, as referenced via the Layer3D parent.
*
* @name Phaser.GameObjects.Layer3DCamera#input
* @type {Phaser.Input.InputPlugin}
* @since 3.50.0
*/
this.input = layer.scene.sys.input;
/**
* Internal 'dirty' flag that tells the parent Layer3D if the
* view matrix of this camera needs recalculating at the next step.
*
* @name Phaser.GameObjects.Layer3DCamera#dirtyView
* @type {boolean}
* @since 3.50.0
*/
this.dirtyView = true;
/**
* Internal 'dirty' flag that tells the parent Layer3D if the
* projection matrix of this camera needs recalculating at the next step.
*
* @name Phaser.GameObjects.Layer3DCamera#dirtyProjection
* @type {boolean}
* @since 3.50.0
*/
this.dirtyProjection = true;
/**
* Internal fov value.
*
* @name Phaser.GameObjects.Layer3DCamera#_fov
* @type {number}
* @private
* @since 3.50.0
*/
this._fov = fov;
/**
* Internal near value.
*
* @name Phaser.GameObjects.Layer3DCamera#_near
* @type {number}
* @private
* @since 3.50.0
*/
this._near = near;
/**
* Internal far value.
*
* @name Phaser.GameObjects.Layer3DCamera#_far
* @type {number}
* @private
* @since 3.50.0
*/
this._far = far;
/**
* The aspect ratio of the camera.
*
* @name Phaser.GameObjects.Layer3DCamera#aspectRatio
* @type {number}
* @since 3.50.0
*/
this.aspectRatio = 1;
/**
* The position of the camera in 3D space.
*
* You can modify this vector directly, or use the `x`, `y` and `z`
* properties of this class.
*
* @name Phaser.GameObjects.Layer3DCamera#position
* @type {Phaser.Math.Vector3}
* @since 3.50.0
*/
this.position = new Vector3(x, y, z);
/**
* The rotation of the camera in 3D space.
*
* You can modify this vector directly, or use the `rotationX`, `rotationY`
* and `rotationZ` properties of this class.
*
* @name Phaser.GameObjects.Layer3DCamera#rotation
* @type {Phaser.Math.Vector3}
* @since 3.50.0
*/
this.rotation = new Vector3();
/**
* The forward facing vector of the camera.
*
* Calculated and updated automatically when the view matrix changes.
*
* @name Phaser.GameObjects.Layer3DCamera#forward
* @type {Phaser.Math.Vector4}
* @since 3.50.0
*/
this.forward = new Vector4();
/**
* The upward facing vector of the camera.
* Invert it to get the bottom vector.
*
* Calculated and updated automatically when the view matrix changes.
*
* @name Phaser.GameObjects.Layer3DCamera#up
* @type {Phaser.Math.Vector4}
* @since 3.50.0
*/
this.up = new Vector4();
/**
* The right facing vector of the camera.
* Invert it to get the left vector.
*
* Calculated and updated automatically when the view matrix changes.
*
* @name Phaser.GameObjects.Layer3DCamera#right
* @type {Phaser.Math.Vector4}
* @since 3.50.0
*/
this.right = new Vector4();
/**
* Internal transform matrix.
*
* Calculated and updated automatically when the camera is dirty.
*
* @name Phaser.GameObjects.Layer3DCamera#matrix
* @type {Phaser.Math.Matrix4}
* @since 3.50.0
*/
this.matrix = new Matrix4();
/**
* The inverse of the transform matrix.
*
* Calculated and updated automatically when the camera is dirty.
*
* @name Phaser.GameObjects.Layer3DCamera#viewMatrix
* @type {Phaser.Math.Matrix4}
* @since 3.50.0
*/
this.viewMatrix = new Matrix4();
/**
* The perspective projection matrix.
*
* Calculated and updated automatically when the camera is dirty.
*
* @name Phaser.GameObjects.Layer3DCamera#projectionMatrix
* @type {Phaser.Math.Matrix4}
* @since 3.50.0
*/
this.projectionMatrix = new Matrix4();
/**
* The perspective projection matrix, multiplied by the view matrix.
*
* Calculated and updated automatically when the camera is dirty.
*
* @name Phaser.GameObjects.Layer3DCamera#viewProjectionMatrix
* @type {Phaser.Math.Matrix4}
* @since 3.50.0
*/
this.viewProjectionMatrix = new Matrix4();
/**
* The movement and rotation mode of this camera.
* Either ORBIT, or FREE.
*
* @name Phaser.GameObjects.Layer3DCamera#mode
* @type {number}
* @since 3.50.0
*/
this.mode = Layer3DCamera.MODE_ORBIT;
/**
* How fast to rotate the camera, in degrees per delta.
*
* This value is only used after calling the `enableControls` method,
* it does not influence changing the rotation values directly.
*
* @name Phaser.GameObjects.Layer3DCamera#rotateSpeed
* @type {number}
* @since 3.50.0
*/
this.rotateSpeed = 0.5;
/**
* How fast to pan the camera, in units per delta.
*
* This value is only used after calling the `enableControls` method,
* it does not influence calling the pan methods directly.
*
* @name Phaser.GameObjects.Layer3DCamera#panSpeed
* @type {number}
* @since 3.50.0
*/
this.panSpeed = 4;
/**
* How fast to zoom the camera.
*
* This value is only used after calling the `enableControls` method,
* it does not influence calling the panZ method directly.
*
* @name Phaser.GameObjects.Layer3DCamera#zoomSpeed
* @type {number}
* @since 3.50.0
*/
this.zoomSpeed = 3;
this.allowPan = false;
this.lockXAxis = false;
this.lockYAxis = false;
},
enableOrbitControls: function (config)
{
this.rotateSpeed = GetFastValue(config, 'rotateSpeed', this.rotateSpeed);
this.panSpeed = GetFastValue(config, 'panSpeed', this.panSpeed);
this.allowPan = GetFastValue(config, 'allowPan', this.allowPan);
this.lockXAxis = GetFastValue(config, 'lockXAxis', this.lockXAxis);
this.lockYAxis = GetFastValue(config, 'lockYAxis', this.lockYAxis);
this.input.on(INPUT_EVENTS.POINTER_MOVE, this.pointerMoveHandler, this);
},
disableOrbitControls: function ()
{
this.input.off(INPUT_EVENTS.POINTER_MOVE, this.pointerMoveHandler, this);
},
enableZoom: function (zoomSpeed)
{
if (zoomSpeed === undefined) { zoomSpeed = 3; }
this.zoomSpeed = zoomSpeed;
this.input.on(INPUT_EVENTS.POINTER_WHEEL, this.pointerWheelHandler, this);
},
disableZoom: function ()
{
this.input.off(INPUT_EVENTS.POINTER_WHEEL, this.pointerWheelHandler, this);
},
pointerMoveHandler: function (pointer)
{
if (pointer.isDown)
{
var width = this.layer.width;
var height = this.layer.height;
if (pointer.event.shiftKey && this.allowPan)
{
this.panX(pointer.velocity.x * (this.panSpeed / width));
this.panY(pointer.velocity.y * (this.panSpeed / height));
}
else
{
if (!this.lockXAxis)
{
this.rotationX -= pointer.velocity.y * (this.rotateSpeed / height);
}
if (!this.lockYAxis)
{
this.rotationY -= pointer.velocity.x * (this.rotateSpeed / width);
}
}
}
},
pointerWheelHandler: function (pointer, over, deltaX, deltaY)
{
this.panZ(deltaY * (this.zoomSpeed / this.layer.height));
},
/**
* Pans this camera on the x axis by the given amount.
*
* @method Phaser.GameObjects.Layer3DCamera#panX
* @since 3.50.0
*
* @param {number} v - The amount to pan by.
*/
panX: function (v)
{
this.updateViewMatrix();
this.position.addScale(this.right, v);
},
/**
* Pans this camera on the y axis by the given amount.
*
* @method Phaser.GameObjects.Layer3DCamera#panY
* @since 3.50.0
*
* @param {number} v - The amount to pan by.
*/
panY: function (v)
{
this.updateViewMatrix();
this.y += this.up.y * v;
if (this.mode === Layer3DCamera.MODE_ORBIT)
{
// Can only move up and down the y axis in orbit mode
return;
}
this.x += this.up.x * v;
this.z += this.up.z * v;
},
/**
* Pans this camera on the z axis by the given amount.
*
* @method Phaser.GameObjects.Layer3DCamera#panZ
* @since 3.50.0
*
* @param {number} v - The amount to pan by.
*/
panZ: function (v)
{
this.updateViewMatrix();
if (this.mode === Layer3DCamera.MODE_ORBIT)
{
// Orbit mode translates after rotatation, so only need to set Z. The rotation will handle the rest.
this.z += v;
}
else
{
// In freemode to move forward, we move based on our forward, which is relative to our current rotation.
this.position.addScale(this.forward, v);
}
},
/**
* Internal method that is called by the Layer3D instance that owns this camera
* during its `render` step. If the view matrix is dirty, it is recalculated
* and then applied to the view projection matrix, ready for rendering.
*
* @method Phaser.GameObjects.Layer3DCamera#update
* @since 3.50.0
*/
update: function ()
{
if (this.dirtyView)
{
this.updateViewMatrix();
}
if (this.dirtyView || this.dirtyProjection)
{
this.projectionMatrix.multiplyToMat4(this.viewMatrix, this.viewProjectionMatrix);
}
},
/**
* Internal method that handles the update of the view transform matrix, based on the rotation
* and position of the camera. Called automatically when the camera is updated.
*
* @method Phaser.GameObjects.Layer3DCamera#updateViewMatrix
* @since 3.50.0
*/
updateViewMatrix: function ()
{
var matView = this.matrix;
if (this.mode === Layer3DCamera.MODE_FREE)
{
matView.fromRotationXYTranslation(this.rotation, this.position, true);
}
else
{
matView.fromRotationXYTranslation(this.rotation, this.position, false);
}
this.updateDirection();
this.viewMatrix.copy(matView).invert();
},
/**
* Internal method that is called by the Layer3D instance that owns this camera
* during its `preUpdate` step. If the projection matrix is dirty, or the renderer
* width or height has changed, then a new projection matrix is calculated.
*
* @method Phaser.GameObjects.Layer3DCamera#updateProjectionMatrix
* @since 3.50.0
*
* @param {number} width - The width of the renderer.
* @param {number} height - The height of the renderer.
*/
updateProjectionMatrix: function (width, height)
{
this.aspectRatio = width / height;
this.projectionMatrix.perspective(DegToRad(this._fov), this.aspectRatio, this._near, this._far);
},
/**
* Internal method that sets the forward, up and right vectors from
* the view matrix. This is called automatically as part of the
* `updateViewMatrix` method.
*
* @method Phaser.GameObjects.Layer3DCamera#updateDirection
* @since 3.50.0
*/
updateDirection: function ()
{
var matView = this.matrix;
this.forward.set(0, 0, 1, 0).transformMat4(matView);
this.up.set(0, 1, 0, 0).transformMat4(matView);
this.right.set(1, 0, 0, 0).transformMat4(matView);
},
/**
* The field of view, in degrees, of this camera.
*
* Limited to the range of 0 to 180.
*
* @name Phaser.GameObjects.Layer3DCamera#fov
* @type {number}
* @since 3.50.0
*/
fov: {
get: function ()
{
return this._fov;
},
set: function (value)
{
if (value > 0 && value < 180)
{
this._fov = value;
this.dirtyProjection = true;
}
}
},
/**
* The minimum distance the camera can see from.
*
* It's important to consider that depth buffers are not infinite and the closer
* a camera starts, the more you may encounter depth fighting issues.
*
* @name Phaser.GameObjects.Layer3DCamera#near
* @type {number}
* @since 3.50.0
*/
near: {
get: function ()
{
return this._near;
},
set: function (value)
{
if (value > 0)
{
this._near = value;
this.dirtyProjection = true;
}
}
},
/**
* The maximum distance the camera can see to.
*
* It's important to consider that depth buffers are not infinite and the further
* a camera ends, the more you may encounter depth fighting issues.
*
* @name Phaser.GameObjects.Layer3DCamera#far
* @type {number}
* @since 3.50.0
*/
far: {
get: function ()
{
return this._far;
},
set: function (value)
{
if (value > 0)
{
this._far = value;
this.dirtyProjection = true;
}
}
},
/**
* The x position of the camera.
*
* @name Phaser.GameObjects.Layer3DCamera#x
* @type {number}
* @since 3.50.0
*/
x: {
get: function ()
{
return this.position.x;
},
set: function (value)
{
this.position.x = value;
this.dirtyView = true;
}
},
/**
* The y position of the camera.
*
* @name Phaser.GameObjects.Layer3DCamera#y
* @type {number}
* @since 3.50.0
*/
y: {
get: function ()
{
return this.position.y;
},
set: function (value)
{
this.position.y = value;
this.dirtyView = true;
}
},
/**
* The z position of the camera.
*
* @name Phaser.GameObjects.Layer3DCamera#z
* @type {number}
* @since 3.50.0
*/
z: {
get: function ()
{
return this.position.z;
},
set: function (value)
{
this.position.z = value;
this.dirtyView = true;
}
},
/**
* The x axis rotation, in radians, of the camera.
*
* @name Phaser.GameObjects.Layer3DCamera#rotationX
* @type {number}
* @since 3.50.0
*/
rotationX: {
get: function ()
{
return this.rotation.x;
},
set: function (value)
{
this.rotation.x = value;
this.dirtyView = true;
}
},
/**
* The y axis rotation, in radians, of the camera.
*
* @name Phaser.GameObjects.Layer3DCamera#rotationY
* @type {number}
* @since 3.50.0
*/
rotationY: {
get: function ()
{
return this.rotation.y;
},
set: function (value)
{
this.rotation.y = value;
this.dirtyView = true;
}
},
/**
* The z axis rotation, in radians, of the camera.
*
* @name Phaser.GameObjects.Layer3DCamera#rotationZ
* @type {number}
* @since 3.50.0
*/
rotationZ: {
get: function ()
{
return this.rotation.z;
},
set: function (value)
{
this.rotation.z = value;
this.dirtyView = true;
}
},
/**
* Destroy handler for this camera.
*
* @method Phaser.GameObjects.Layer3DCamera#destroy
* @since 3.50.0
*/
destroy: function ()
{
this.layer = null;
this.position = null;
this.rotation = null;
this.forward = null;
this.up = null;
this.right = null;
this.matrix = null;
this.viewMatrix = null;
this.projectionMatrix = null;
this.viewProjectionMatrix = null;
}
});
// Allows free movement of position and rotation
Layer3DCamera.MODE_FREE = 0;
// Movement is locked to rotate around the origin
Layer3DCamera.MODE_ORBIT = 1;
module.exports = Layer3DCamera;

View file

@ -0,0 +1,22 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2020 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
/**
* This is a stub function for Layer3D.Render. There is no Canvas renderer for Layer3D objects.
*
* @method Phaser.GameObjects.Layer3D#renderCanvas
* @since 3.50.0
* @private
*
* @param {Phaser.Renderer.Canvas.CanvasRenderer} renderer - A reference to the current active Canvas renderer.
* @param {Phaser.GameObjects.Layer3D} src - The Game Object being rendered in this call.
* @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera that is rendering the Game Object.
*/
var Layer3DCanvasRenderer = function ()
{
};
module.exports = Layer3DCanvasRenderer;

View file

@ -0,0 +1,40 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2020 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var BuildGameObject = require('../BuildGameObject');
var GameObjectCreator = require('../GameObjectCreator');
var Layer3D = require('./Layer3D');
/**
* Creates a new Layer3D Game Object and returns it.
*
* Note: This method will only be available if the Layer3D Game Object and WebGL support have been built into Phaser.
*
* @method Phaser.GameObjects.GameObjectCreator#layer3d
* @since 3.50.0
*
* @param {object} config - The configuration object this Game Object will use to create itself.
* @param {boolean} [addToScene] - Add this Game Object to the Scene after creating it? If set this argument overrides the `add` property in the config object.
*
* @return {Phaser.GameObjects.Layer3D} The Game Object that was created.
*/
GameObjectCreator.register('layer3d', function (config, addToScene)
{
if (config === undefined) { config = {}; }
var layer = new Layer3D(this.scene, 0, 0);
if (addToScene !== undefined)
{
config.add = addToScene;
}
BuildGameObject(this.scene, layer, config);
return layer;
});
// When registering a factory function 'this' refers to the GameObjectCreator context.

View file

@ -0,0 +1,38 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2020 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var Layer3D = require('./Layer3D');
var GameObjectFactory = require('../GameObjectFactory');
/**
* Creates a new Layer3D Game Object and adds it to the Scene.
*
* Note: This method will only be available if the Layer3D Game Object and WebGL support have been built into Phaser.
*
* @method Phaser.GameObjects.GameObjectFactory#layer3d
* @webglOnly
* @since 3.50.0
*
* @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.
*
* @return {Phaser.GameObjects.Layer3D} The Game Object that was created.
*/
if (typeof WEBGL_RENDERER)
{
GameObjectFactory.register('layer3d', function (x, y)
{
return this.displayList.add(new Layer3D(this.scene, x, y));
});
}
// When registering a factory function 'this' refers to the GameObjectFactory context.
//
// There are several properties available to use:
//
// this.scene - a reference to the Scene that owns the GameObjectFactory
// this.displayList - a reference to the Display List the Scene owns
// this.updateList - a reference to the Update List the Scene owns

View file

@ -0,0 +1,295 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2020 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var Class = require('../../utils/Class');
var RGB = require('../../display/RGB');
var Vector3 = require('../../math/Vector3');
/**
* @classdesc
* A Layer3D Light.
*
* @class Layer3DLight
* @memberof Phaser.GameObjects
* @constructor
* @since 3.50.0
*/
var Layer3DLight = new Class({
initialize:
function Layer3DLight (layer, x, y, z)
{
/**
* The Layer3D instance this light belongs to.
*
* A light can only belong to a single Layer3D instance.
*
* You should consider this property as being read-only. You cannot move a
* light to another Layer3D by simply changing it.
*
* @name Phaser.GameObjects.Layer3DLight#layer
* @type {Phaser.GameObjects.Layer3D}
* @since 3.50.0
*/
this.layer = layer;
/**
* The position of the light in 3D space.
*
* You can modify this vector directly, or use the `x`, `y` and `z`
* properties of this class.
*
* @name Phaser.GameObjects.Layer3DLight#position
* @type {Phaser.Math.Vector3}
* @since 3.50.0
*/
this.position = new Vector3(x, y, z);
/**
* The ambient color of the light.
*
* The default ambient color is 1, 1, 1.
*
* You can modify the properties of this RGB object directly, or call
* the `setAmbient` method of this class.
*
* The values in this object are used by the `uLightAmbient` shader uniform.
*
* @name Phaser.GameObjects.Layer3DLight#ambient
* @type {Phaser.Display.RGB}
* @since 3.50.0
*/
this.ambient = new RGB(1, 1, 1);
/**
* The diffuse color of the light.
*
* The default diffuse color is 1, 1, 1.
*
* You can modify the properties of this RGB object directly, or call
* the `setDiffuse` method of this class.
*
* The values in this object are used by the `uLightDiffuse` shader uniform.
*
* @name Phaser.GameObjects.Layer3DLight#diffuse
* @type {Phaser.Display.RGB}
* @since 3.50.0
*/
this.diffuse = new RGB(1, 1, 1);
/**
* The specular color of the light.
*
* The default specular color is 1, 1, 1.
*
* You can modify the properties of this RGB object directly, or call
* the `setSpecular` method of this class.
*
* The values in this object are used by the `uLightSpecular` shader uniform.
*
* @name Phaser.GameObjects.Layer3DLight#specular
* @type {Phaser.Display.RGB}
* @since 3.50.0
*/
this.specular = new RGB(1, 1, 1);
/**
* Internal dirty cache array.
*
* @name Phaser.GameObjects.Layer3DLight#dirtyCache
* @type {number[]}
* @private
* @since 3.50.0
*/
this.dirtyCache = [ 0, 0, 0 ];
},
/**
* Checks if the position of this light is dirty.
*
* Called internally by the Mesh Pipeline `onBind` method and if dirty
* is used to set the `uLightPosition` uniform.
*
* @method Phaser.GameObjects.Layer3DLight#isDirty
* @since 3.50.0
*
* @return {boolean} `true` if this light is dirty, otherwise `false`.
*/
isDirty: function ()
{
var position = this.position;
var dirtyCache = this.dirtyCache;
var x = position.x;
var y = position.y;
var z = position.z;
var xCached = dirtyCache[0];
var yCached = dirtyCache[1];
var zCached = dirtyCache[2];
dirtyCache[0] = x;
dirtyCache[1] = y;
dirtyCache[2] = z;
return (xCached !== x || yCached !== y || zCached !== z);
},
/**
* Sets the position of this light.
*
* @method Phaser.GameObjects.Layer3DLight#setPosition
* @since 3.50.0
*
* @param {number} x - The x position of this light.
* @param {number} y - The y position of this light.
* @param {number} z - The z position of this light.
*
* @return {this} This Layer3DLight instance.
*/
setPosition: function (x, y, z)
{
this.position.set(x, y, z);
return this;
},
/**
* Sets the ambient color of this light.
*
* @method Phaser.GameObjects.Layer3DLight#setAmbient
* @since 3.50.0
*
* @param {number} r - The red color value. Between 0 and 1.
* @param {number} g - The green color value. Between 0 and 1.
* @param {number} b - The blue color value. Between 0 and 1.
*
* @return {this} This Layer3DLight instance.
*/
setAmbient: function (r, g, b)
{
this.ambient.set(r, g, b);
return this;
},
/**
* Sets the diffuse color of this light.
*
* @method Phaser.GameObjects.Layer3DLight#setDiffuse
* @since 3.50.0
*
* @param {number} r - The red color value. Between 0 and 1.
* @param {number} g - The green color value. Between 0 and 1.
* @param {number} b - The blue color value. Between 0 and 1.
*
* @return {this} This Layer3DLight instance.
*/
setDiffuse: function (r, g, b)
{
this.diffuse.set(r, g, b);
return this;
},
/**
* Sets the specular color of this light.
*
* @method Phaser.GameObjects.Layer3DLight#setSpecular
* @since 3.50.0
*
* @param {number} r - The red color value. Between 0 and 1.
* @param {number} g - The green color value. Between 0 and 1.
* @param {number} b - The blue color value. Between 0 and 1.
*
* @return {this} This Layer3DLight instance.
*/
setSpecular: function (r, g, b)
{
this.specular.set(r, g, b);
return this;
},
/**
* The x position of the light.
*
* @name Phaser.GameObjects.Layer3DLight#x
* @type {number}
* @since 3.50.0
*/
x: {
get: function ()
{
return this.position.x;
},
set: function (value)
{
this.position.x = value;
}
},
/**
* The y position of the light.
*
* @name Phaser.GameObjects.Layer3DLight#y
* @type {number}
* @since 3.50.0
*/
y: {
get: function ()
{
return this.position.y;
},
set: function (value)
{
this.position.y = value;
}
},
/**
* The z position of the light.
*
* @name Phaser.GameObjects.Layer3DLight#z
* @type {number}
* @since 3.50.0
*/
z: {
get: function ()
{
return this.position.z;
},
set: function (value)
{
this.position.z = value;
}
},
/**
* Destroy handler for this light.
*
* @method Phaser.GameObjects.Layer3DLight#destroy
* @since 3.50.0
*/
destroy: function ()
{
this.layer = null;
this.position = null;
}
});
module.exports = Layer3DLight;

View file

@ -0,0 +1,25 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2020 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var renderWebGL = require('../../utils/NOOP');
var renderCanvas = require('../../utils/NOOP');
if (typeof WEBGL_RENDERER)
{
renderWebGL = require('./Layer3DWebGLRenderer');
}
if (typeof CANVAS_RENDERER)
{
renderCanvas = require('./Layer3DCanvasRenderer');
}
module.exports = {
renderWebGL: renderWebGL,
renderCanvas: renderCanvas
};

View file

@ -0,0 +1,52 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2020 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
/**
* Renders this Game Object with the WebGL Renderer to the given Camera.
* The object will not render if any of its renderFlags are set or it is being actively filtered out by the Camera.
* This method should not be called directly. It is a utility function of the Render module.
*
* @method Phaser.GameObjects.Layer3D#renderWebGL
* @since 3.50.0
* @private
*
* @param {Phaser.Renderer.WebGL.WebGLRenderer} renderer - A reference to the current active WebGL renderer.
* @param {Phaser.GameObjects.Layer3D} 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.GameObjects.Components.TransformMatrix} parentMatrix - This transform matrix is defined if the game object is nested
*/
var Layer3DWebGLRenderer = function (renderer, src)
{
var models = src.models;
var totalModels = models.length;
if (totalModels === 0)
{
return;
}
renderer.pipelines.clear();
src.camera.update();
var pipeline = renderer.pipelines.set(src.pipeline, src);
for (var m = 0; m < totalModels; m++)
{
var model = models[m];
if (model.visible && model.vertexCount > 0)
{
pipeline.drawModel(src, model);
}
}
src.resetDirtyFlags();
renderer.pipelines.rebind();
};
module.exports = Layer3DWebGLRenderer;

918
src/geom/mesh/Model.js Normal file
View file

@ -0,0 +1,918 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2020 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var AnimationState = require('../../animations/AnimationState');
var Class = require('../../utils/Class');
var Components = require('../../gameobjects/components');
var Matrix4 = require('../../math/Matrix4');
var Quaternion = require('../../math/Quaternion');
var RGB = require('../../layer3d/math/RGB');
var Vector3 = require('../../math/Vector3');
/**
* @classdesc
* A 3D Model.
*
* @class Model
* @memberof Phaser.Geom.Mesh
* @constructor
* @since 3.50.0
*
* @param {Phaser.GameObjects.Layer3D} layer - A reference to the Layer3D instance that this model belongs to.
* @param {number} verticesCount - The total number of vertices this model can contain.
* @param {string|Phaser.Textures.Texture} [texture] - The key, or instance of the Texture this model will use to render with, as stored in the Texture Manager.
* @param {string|integer} [frame] - An optional frame from the Texture this model is rendering with. Ensure your UV data also matches this frame.
* @param {number} [x=0] - The x position of the Model.
* @param {number} [y=0] - The y position of the Model.
* @param {number} [z=0] - The z position of the Model.
*/
var Model = new Class({
Mixins: [
Components.AlphaSingle,
Components.Size,
Components.Texture,
Components.Visible
],
initialize:
function Model (layer, verticesCount, texture, frame, x, y, z)
{
if (x === undefined) { x = 0; }
if (y === undefined) { y = 0; }
if (z === undefined) { z = 0; }
/**
* The Layer3D instance this model belongs to.
*
* A model can only belong to a single Layer3D instance.
*
* You should consider this property as being read-only. You cannot move a
* model to another Layer3D by simply changing it.
*
* @name Phaser.Geom.Mesh.Model#layer
* @type {Phaser.GameObjects.Layer3D}
* @since 3.50.0
*/
this.layer = layer;
/**
* A reference to the Scene to which this the Layer3D Object which owns this model belongs.
*
* You should consider this property as being read-only. You cannot move a
* Game Object to another Scene by simply changing it.
*
* @name Phaser.Geom.Mesh.Model#scene
* @type {Phaser.Scene}
* @since 3.50.0
*/
this.scene = layer.scene;
/**
* The Animation State of this Model.
*
* @name Phaser.Geom.Mesh.Model#anims
* @type {Phaser.Animation.AnimationState}
* @since 3.50.0
*/
this.anims = new AnimationState(this);
/**
* The size of a single vertex, in bytes.
*
* The total of all 8 attributes * bytes size.
*
* @name Phaser.Geom.Mesh.Model#vertexSize
* @type {number}
* @since 3.50.0
*/
this.vertexSize = 32;
/**
* The total number of vertices the ArrayBuffer in this model can hold.
*
* @name Phaser.Geom.Mesh.Model#maxVertexCount
* @type {number}
* @since 3.50.0
*/
this.maxVertexCount = verticesCount;
/**
* The total number of vertices currently added to this model.
*
* @name Phaser.Geom.Mesh.Model#vertexCount
* @type {number}
* @since 3.50.0
*/
this.vertexCount = 0;
/**
* An ArrayBuffer that contains the GPU byte data for this model.
*
* The size of the buffer is set to `verticesCount * vertexSize` when
* this model is created and cannot be changed without resetting the vertices.
*
* @name Phaser.Geom.Mesh.Model#vertexData
* @type {ArrayBuffer}
* @since 3.50.0
*/
this.vertexData;
/**
* A Float32 View into the Array Buffer.
*
* @name Phaser.Geom.Mesh.Model#vertexViewF32
* @type {Float32Array}
* @since 3.50.0
*/
this.vertexViewF32;
/**
* A Vector3 containing the position of this model in 3D space.
*
* @name Phaser.Geom.Mesh.Model#position
* @type {Phaser.Math.Vector3}
* @since 3.50.0
*/
this.position = new Vector3(x, y, z);
/**
* A Vector3 containing the scale of this model in 3D space.
*
* @name Phaser.Geom.Mesh.Model#scale
* @type {Phaser.Math.Vector3}
* @since 3.50.0
*/
this.scale = new Vector3(1, 1, 1);
/**
* A Quaternion containing the rotation of this model in 3D space.
*
* @name Phaser.Geom.Mesh.Model#position
* @type {Phaser.Math.Quaternion}
* @since 3.50.0
*/
this.rotation = new Quaternion();
/**
* An RGB object containing the ambient material color of this model.
*
* You can adjust the ambient material color by calling the methods
* on this object and changing its properties.
*
* Remember that all color values should be specified in the range
* of 0 to 1.
*
* @name Phaser.Geom.Mesh.Model#ambient
* @type {Phaser.Display.RGB}
* @since 3.50.0
*/
this.ambient = new RGB(1, 1, 1);
/**
* An RGB object containing the diffuse material color of this model.
*
* You can adjust the diffuse material color by calling the methods
* on this object and changing its properties.
*
* Remember that all color values should be specified in the range
* of 0 to 1.
*
* @name Phaser.Geom.Mesh.Model#diffuse
* @type {Phaser.Display.RGB}
* @since 3.50.0
*/
this.diffuse = new RGB(1, 1, 1);
/**
* An RGB object containing the specular material color of this model.
*
* You can adjust the specular material color by calling the methods
* on this object and changing its properties.
*
* Remember that all color values should be specified in the range
* of 0 to 1.
*
* @name Phaser.Geom.Mesh.Model#specular
* @type {Phaser.Display.RGB}
* @since 3.50.0
*/
this.specular = new RGB(1, 1, 1);
/**
* The material shine value of this model.
*
* Default to 0.25. Keep this value in the range 0 to 1.
*
* @name Phaser.Geom.Mesh.Model#ambient
* @type {number}
* @default 0.25
* @since 3.50.0
*/
this.shine = 0.25;
/**
* A Matrix4 containing the transformed normal values for this model.
*
* You should consider this Matrix as being read-only. Its values are
* repopulated during `Model.preUpdate` as required.
*
* @name Phaser.Geom.Mesh.Model#normalMatrix
* @type {Phaser.Math.Matrix4}
* @since 3.50.0
*/
this.normalMatrix = new Matrix4();
/**
* A Matrix4 containing the transform matrix for this model.
*
* You should consider this Matrix as being read-only. Its values are
* repopulated during `Model.preUpdate` as required.
*
* @name Phaser.Geom.Mesh.Model#transformMatrix
* @type {Phaser.Math.Matrix4}
* @since 3.50.0
*/
this.transformMatrix = new Matrix4();
/**
* The culling mode used by this Model during rendering.
*
* Specifies whether front or back facing polygons are candidates
* for culling. The default value is `gl.BACK`. Possible values are:
*
* `gl.FRONT` (1028)
* `gl.BACK` (1029)
* `gl.FRONT_AND_BACK` (1032)
*
* @name Phaser.Geom.Mesh.Model#cullMode
* @type {GLenum}
* @since 3.50.0
*/
this.cullMode = 1029;
/**
* An internal cache, used to compare position, rotation, scale and verts data
* each frame, to avoid math calculates in `preUpdate`.
*
* cache structure = position | rotation | scale | verts count
*
* @name Phaser.Geom.Mesh.Model#dirtyCache
* @type {number[]}
* @private
* @since 3.50.0
*/
this.dirtyCache = [ x, y, z, 0, 0, 0, 1, 1, 1, 1, 0 ];
if (!texture)
{
texture = this.scene.sys.textures.get('__WHITE');
}
this.setTexture(texture, frame);
this.setSizeToFrame();
this.resetVertices(verticesCount);
},
/**
* Calls each of the listeners registered for a given event.
*
* This is a proxy for the Layer3D `emit` method.
*
* @method Phaser.Geom.Mesh.Model#emit
* @since 3.50.0
*
* @param {(string|symbol)} event - The event name.
* @param {...*} [args] - Additional arguments that will be passed to the event handler.
*
* @return {boolean} `true` if the event had listeners, else `false`.
*/
emit: function ()
{
return this.layer.emit.call(arguments);
},
/**
* Checks all of the current model values against the `dirtyCache` to see if the
* normal and transform matrices need updating.
*
* @method Phaser.Geom.Mesh.Model#isDirty
* @since 3.50.0
*
* @return {boolean} Returns `true` if any of the model values are dirty, otherwise `false`.
*/
isDirty: function ()
{
var position = this.position;
var rotation = this.rotation;
var scale = this.scale;
var dirtyCache = this.dirtyCache;
var px = position.x;
var py = position.y;
var pz = position.z;
var rx = rotation.x;
var ry = rotation.y;
var rz = rotation.z;
var rw = rotation.w;
var sx = scale.x;
var sy = scale.y;
var sz = scale.z;
var vertices = this.vertexCount;
var pxCached = dirtyCache[0];
var pyCached = dirtyCache[1];
var pzCached = dirtyCache[2];
var rxCached = dirtyCache[3];
var ryCached = dirtyCache[4];
var rzCached = dirtyCache[5];
var rwCached = dirtyCache[6];
var sxCached = dirtyCache[7];
var syCached = dirtyCache[8];
var szCached = dirtyCache[9];
var vCached = dirtyCache[10];
dirtyCache[0] = px;
dirtyCache[1] = py;
dirtyCache[2] = pz;
dirtyCache[3] = rx;
dirtyCache[4] = ry;
dirtyCache[5] = rz;
dirtyCache[6] = rw;
dirtyCache[7] = sx;
dirtyCache[8] = sy;
dirtyCache[9] = sz;
dirtyCache[10] = vertices;
return (
pxCached !== px || pyCached !== py || pzCached !== pz ||
rxCached !== rx || ryCached !== ry || rzCached !== rz || rwCached !== rw ||
sxCached !== sx || syCached !== sy || szCached !== sz ||
vCached !== vertices
);
},
/**
* Internal update handler. Advances any animations that are set on the model and,
* if the model data is dirty, recalculates the transform and normal matrices.
*
* This method is called automatically by the `Layer3D` to which this model belongs.
*
* @method Phaser.Geom.Mesh.Model#preUpdate
* @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);
// If the model isn't dirty we can bail out and save lots of math
if (this.isDirty())
{
var normalMatrix = this.normalMatrix;
var transformMatrix = this.transformMatrix;
// TODO - Merge scale into this op
transformMatrix.fromRotationTranslation(this.rotation, this.position);
transformMatrix.scale(this.scale);
normalMatrix.copy(transformMatrix);
normalMatrix.invert();
normalMatrix.transpose();
}
},
/**
* Returns the total number of Faces _currently added_ to this model.
*
* Models in Phaser 3 must always be triangulated, so this value is the same as
* `vertexCount / 3`.
*
* @method Phaser.Geom.Mesh.Model#getFaceCount
* @since 3.50.0
*
* @return {number} The number of Faces in this Model.
*/
getFaceCount: function ()
{
return this.vertexCount / 3;
},
/**
* Gets the Vertex at the given offset from this models data.
*
* Be aware that the returned Vertex is untranslated, so will need transforming if you wish
* to use its coordinates in world space.
*
* @method Phaser.Geom.Mesh.Model#getVertex
* @since 3.50.0
*
* @param {number} index - The index of the vertex to get. Cannot be negative, or exceed `Model.vertexCount`.
*
* @return {Phaser.Types.GameObjects.Vertex} A Vertex object.
*/
getVertex: function (index)
{
var vertexViewF32 = this.vertexViewF32;
// 8 = attribute count (number of items added into the view below)
var vertexOffset = (index * 8) - 1;
var x = vertexViewF32[++vertexOffset];
var y = vertexViewF32[++vertexOffset];
var z = vertexViewF32[++vertexOffset];
var normalX = vertexViewF32[++vertexOffset];
var normalY = vertexViewF32[++vertexOffset];
var normalZ = vertexViewF32[++vertexOffset];
var u = vertexViewF32[++vertexOffset];
var v = vertexViewF32[++vertexOffset];
return { x: x, y: y, z: z, u: u, v: v, normalX: normalX, normalY: normalY, normalZ: normalZ, alpha: 1 };
},
/**
* Returns the Face at the given index in this model.
*
* A face comprises of 3 vertices.
*
* Be aware that the Face vertices are untranslated, so will need transforming if you wish
* to use their coordinates in world space.
*
* @method Phaser.Geom.Mesh.Model#getFace
* @since 3.50.0
*
* @param {number} index - The index of the Face to get. Make sure the index is in range.
*
* @return {Phaser.Types.GameObjects.Face} The Face at the given index.
*/
getFace: function (index)
{
var offset = index * 3;
var v1 = this.getVertex(offset);
var v2 = this.getVertex(offset + 1);
var v3 = this.getVertex(offset + 2);
var ccw = (v2.x - v1.x) * (v3.y - v1.y) - (v2.y - v1.y) * (v3.x - v1.x) >= 0;
return { vertex1: v1, vertex2: v2, vertex3: 3, isCounterClockwise: ccw };
},
/**
* Resets the data in this model, clearing the `vertexData` ArrayBuffer and
* setting it to the new max count given.
*
* @method Phaser.Geom.Mesh.Model#resetVertices
* @since 3.50.0
*
* @param {number} verticesCount - The total number of vertices this model can contain.
*/
resetVertices: function (verticesCount)
{
this.vertexData = new ArrayBuffer(verticesCount * this.vertexSize);
this.vertexViewF32 = new Float32Array(this.vertexData);
this.vertexCount = 0;
this.maxVertexCount = verticesCount;
return this;
},
/**
* Updates all values of the vertex at the given index.
*
* Ensure that the index is in range.
*
* @method Phaser.Geom.Mesh.Model#updateVertex
* @since 3.50.0
*
* @param {number} index - The index of the vertex to update.
* @param {number} x - The x position of the vertex.
* @param {number} y - The y position of the vertex.
* @param {number} z - The z position of the vertex.
* @param {number} u - The UV u coordinate of the vertex.
* @param {number} v - The UV v coordinate of the vertex.
* @param {number} normalX - The x normal of the vertex.
* @param {number} normalY - The y normal of the vertex.
* @param {number} normalZ - The z normal of the vertex.
*/
updateVertex: function (index, x, y, z, u, v, normalX, normalY, normalZ)
{
var vertexViewF32 = this.vertexViewF32;
// 8 = attribute count
var vertexOffset = (index * 8) - 1;
vertexViewF32[++vertexOffset] = x;
vertexViewF32[++vertexOffset] = y;
vertexViewF32[++vertexOffset] = z;
vertexViewF32[++vertexOffset] = normalX;
vertexViewF32[++vertexOffset] = normalY;
vertexViewF32[++vertexOffset] = normalZ;
vertexViewF32[++vertexOffset] = u;
vertexViewF32[++vertexOffset] = v;
},
/**
* Adds a new vertex to this model and increments the `vertexCount` by one.
*
* You cannot add more vertices to this model than the total specified when the model was created.
* If you need to clear all vertices first, call `Model.resetVertices`.
*
* @method Phaser.Geom.Mesh.Model#addVertex
* @since 3.50.0
*
* @param {number} x - The x position of the vertex.
* @param {number} y - The y position of the vertex.
* @param {number} z - The z position of the vertex.
* @param {number} u - The UV u coordinate of the vertex.
* @param {number} v - The UV v coordinate of the vertex.
* @param {number} normalX - The x normal of the vertex.
* @param {number} normalY - The y normal of the vertex.
* @param {number} normalZ - The z normal of the vertex.
*/
addVertex: function (x, y, z, u, v, normalX, normalY, normalZ)
{
if (this.vertexCount < this.maxVertexCount)
{
this.updateVertex(this.vertexCount, x, y, z, u, v, normalX, normalY, normalZ);
this.vertexCount++;
}
},
/**
* Adds vertices to this model by parsing the given arrays.
*
* This method will take vertex data in one of two formats, based on the `containsZ` parameter.
*
* If your vertex data are `x`, `y` pairs, then `containsZ` should be `false` (this is the default)
*
* If your vertex data is groups of `x`, `y` and `z` values, then the `containsZ` parameter must be true.
*
* The `uvs` parameter is a numeric array consisting of `u` and `v` pairs.
* The `normals` parameter is a numeric array consisting of `x`, `y` vertex normal values and, if `containsZ` is true, `z` values as well.
* The `indicies` parameter is an optional array that, if given, is an indexed list of vertices to be added.
*
* The following example will create a 256 x 256 sized quad using an index array:
*
* ```javascript
* const vertices = [
* -128, 128,
* 128, 128,
* -128, -128,
* 128, -128
* ];
*
* const uvs = [
* 0, 1,
* 1, 1,
* 0, 0,
* 1, 0
* ];
*
* const indices = [ 0, 2, 1, 2, 3, 1 ];
*
* Model.addVertices(vertices, uvs, indicies);
* ```
*
* You cannot add more vertices to this model than the total specified when the model was created.
* If you need to clear all vertices first, call `Model.resetVertices`.
*
* @method Phaser.Geom.Mesh.Model#addVertices
* @since 3.50.0
*
* @param {number[]} vertices - The vertices array. Either `xy` pairs, or `xyz` if the `containsZ` parameter is `true`.
* @param {number[]} uvs - The UVs pairs array.
* @param {number[]} [normals] - Optional vertex normals array. If you don't have one, pass `null` or an empty array.
* @param {number[]} [indicies] - Optional vertex indicies array. If you don't have one, pass `null` or an empty array.
* @param {boolean} [containsZ=false] - Does the vertices data include a `z` component?
*/
addVertices: function (vertices, uvs, normals, indicies, containsZ)
{
if (containsZ === undefined) { containsZ = false; }
if (vertices.length !== uvs.length)
{
throw new Error('Model vertices and uv count not equal');
}
var i;
var x;
var y;
var z;
var u;
var v;
var normalX;
var normalY;
var normalZ;
var iInc = (containsZ) ? 3 : 2;
if (Array.isArray(indicies) && indicies.length > 0)
{
for (i = 0; i < indicies.length; i++)
{
var index = indicies[i] * iInc;
x = vertices[index];
y = vertices[index + 1];
z = (containsZ) ? vertices[index + 2] : 0;
u = uvs[index];
v = uvs[index + 1];
normalX = 0;
normalY = 0;
normalZ = 0;
if (normals)
{
normalX = normals[index];
normalY = normals[index + 1];
normalZ = (containsZ) ? normals[index + 2] : 0;
}
this.addVertex(
x, y, z,
u, v,
normalX, normalY, normalZ
);
}
}
else
{
for (i = 0; i < vertices.length; i += iInc)
{
x = vertices[i];
y = vertices[i + 1];
z = (containsZ) ? vertices[i + 2] : 0;
u = uvs[i];
v = uvs[i + 1];
normalX = 0;
normalY = 0;
normalZ = 0;
if (normals)
{
normalX = normals[i];
normalY = normals[i + 1];
normalZ = (containsZ) ? normals[i + 2] : 0;
}
this.addVertex(
x, y, z,
u, v,
normalX, normalY, normalZ
);
}
}
},
/**
* Rotates this model along the x axis by the given amount.
*
* This method works by calling the `rotateX` method of the `rotation` quaternion of this model.
*
* @method Phaser.Geom.Mesh.Model#rotateX
* @since 3.50.0
*
* @param {number} rad - The amount, in radians, to rotate the model by.
*
* @return {this} This model instance.
*/
rotateX: function (rad)
{
this.rotation.rotateX(rad);
return this;
},
/**
* Rotates this model along the y axis by the given amount.
*
* This method works by calling the `rotateY` method of the `rotation` quaternion of this model.
*
* @method Phaser.Geom.Mesh.Model#rotateY
* @since 3.50.0
*
* @param {number} rad - The amount, in radians, to rotate the model by.
*
* @return {this} This model instance.
*/
rotateY: function (rad)
{
this.rotation.rotateY(rad);
return this;
},
/**
* Rotates this model along the z axis by the given amount.
*
* This method works by calling the `rotateZ` method of the `rotation` quaternion of this model.
*
* @method Phaser.Geom.Mesh.Model#rotateZ
* @since 3.50.0
*
* @param {number} rad - The amount, in radians, to rotate the model by.
*
* @return {this} This model instance.
*/
rotateZ: function (rad)
{
this.rotation.rotateZ(rad);
return this;
},
setPosition: function (x, y, z)
{
this.position.set(x, y, z);
return this;
},
setScale: function (x, y, z)
{
this.scale.set(x, y, z);
return this;
},
/**
* The x position of this model in 3D space.
*
* @name Phaser.Geom.Mesh.Model#x
* @type {number}
* @since 3.50.0
*/
x: {
get: function ()
{
return this.position.x;
},
set: function (value)
{
this.position.x = value;
}
},
/**
* The y position of this model in 3D space.
*
* @name Phaser.Geom.Mesh.Model#y
* @type {number}
* @since 3.50.0
*/
y: {
get: function ()
{
return this.position.y;
},
set: function (value)
{
this.position.y = value;
}
},
/**
* The z position of this model in 3D space.
*
* @name Phaser.Geom.Mesh.Model#z
* @type {number}
* @since 3.50.0
*/
z: {
get: function ()
{
return this.position.z;
},
set: function (value)
{
this.position.z = value;
}
},
/**
* The x scale of this model in 3D space.
*
* @name Phaser.Geom.Mesh.Model#scaleX
* @type {number}
* @since 3.50.0
*/
scaleX: {
get: function ()
{
return this.scale.x;
},
set: function (value)
{
this.scale.x = value;
}
},
/**
* The y scale of this model in 3D space.
*
* @name Phaser.Geom.Mesh.Model#scaleY
* @type {number}
* @since 3.50.0
*/
scaleY: {
get: function ()
{
return this.scale.y;
},
set: function (value)
{
this.scale.y = value;
}
},
/**
* The z scale of this model in 3D space.
*
* @name Phaser.Geom.Mesh.Model#scaleZ
* @type {number}
* @since 3.50.0
*/
scaleZ: {
get: function ()
{
return this.scale.z;
},
set: function (value)
{
this.scale.z = value;
}
},
/**
* Destroys this Model instance, all of its vertex data and references.
*
* Calling this method will not remove it from any parent Layer3D, so be sure to do that first,
* prior to calling `destroy`.
*
* If a Layer3D object is destroyed, this is the method that is called on all of its models.
*
* @method Phaser.Geom.Mesh.Model#destroy
* @since 3.50.0
*/
destroy: function ()
{
this.anims.destroy();
this.layer = null;
this.scene = null;
this.anims = null;
this.vertexData = null;
this.vertexViewF32 = null;
this.position = null;
this.scale = null;
this.rotation = null;
this.ambient = null;
this.diffuse = null;
this.specular = null;
this.normalMatrix = null;
this.transformMatrix = null;
}
});
module.exports = Model;

View file

@ -70,6 +70,33 @@ var Vertex = new Class({
*/ */
this.vz = 0; this.vz = 0;
/**
* The projected x coordinate of this vertex.
*
* @name Phaser.Geom.Mesh.Vertex#vx
* @type {number}
* @since 3.50.0
*/
this.nx = 0;
/**
* The projected y coordinate of this vertex.
*
* @name Phaser.Geom.Mesh.Vertex#vy
* @type {number}
* @since 3.50.0
*/
this.ny = 0;
/**
* The projected z coordinate of this vertex.
*
* @name Phaser.Geom.Mesh.Vertex#vz
* @type {number}
* @since 3.50.0
*/
this.nz = 0;
/** /**
* UV u coordinate of this vertex. * UV u coordinate of this vertex.
* *
@ -107,6 +134,15 @@ var Vertex = new Class({
this.alpha = alpha; this.alpha = alpha;
}, },
setNormals: function (nx, ny, nz)
{
this.nx = nx;
this.ny = ny;
this.nz = nz;
return this;
},
/** /**
* Transforms this vertex by the given matrix, storing the results in `vx`, `vy` and `vz`. * Transforms this vertex by the given matrix, storing the results in `vx`, `vy` and `vz`.
* *

View file

@ -14,6 +14,7 @@ var LightPipeline = require('./pipelines/LightPipeline');
var MultiPipeline = require('./pipelines/MultiPipeline'); var MultiPipeline = require('./pipelines/MultiPipeline');
var RopePipeline = require('./pipelines/RopePipeline'); var RopePipeline = require('./pipelines/RopePipeline');
var SinglePipeline = require('./pipelines/SinglePipeline'); var SinglePipeline = require('./pipelines/SinglePipeline');
var MeshPipeline = require('./pipelines/MeshPipeline');
/** /**
* @classdesc * @classdesc
@ -146,6 +147,7 @@ var PipelineManager = new Class({
this.add(CONST.SINGLE_PIPELINE, new SinglePipeline({ game: game })); this.add(CONST.SINGLE_PIPELINE, new SinglePipeline({ game: game }));
this.add(CONST.ROPE_PIPELINE, new RopePipeline({ game: game })); this.add(CONST.ROPE_PIPELINE, new RopePipeline({ game: game }));
this.add(CONST.LIGHT_PIPELINE, new LightPipeline({ game: game })); this.add(CONST.LIGHT_PIPELINE, new LightPipeline({ game: game }));
this.add(CONST.MESH_PIPELINE, new MeshPipeline({ game: game }));
this.set(this.MULTI_PIPELINE); this.set(this.MULTI_PIPELINE);
}, },

View file

@ -0,0 +1,288 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2020 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var Class = require('../../../utils/Class');
var GetFastValue = require('../../../utils/object/GetFastValue');
var ShaderSourceFS = require('../shaders/Mesh-frag.js');
var ShaderSourceVS = require('../shaders/Mesh-vert.js');
var WebGLPipeline = require('../WebGLPipeline');
/**
* @classdesc
* TODO
*
* @class MeshPipeline
* @extends Phaser.Renderer.WebGL.WebGLPipeline
* @memberof Phaser.Renderer.WebGL.Pipelines
* @constructor
* @since 3.50.0
*
* @param {Phaser.Types.Renderer.WebGL.WebGLPipelineConfig} config - The configuration options for this pipeline.
*/
var MeshPipeline = new Class({
Extends: WebGLPipeline,
initialize:
function MeshPipeline (config)
{
var gl = config.game.renderer.gl;
config.fragShader = GetFastValue(config, 'fragShader', ShaderSourceFS),
config.vertShader = GetFastValue(config, 'vertShader', ShaderSourceVS),
config.vertexCapacity = GetFastValue(config, 'vertexCapacity', 8),
config.vertexSize = GetFastValue(config, 'vertexSize', 32),
config.attributes = GetFastValue(config, 'attributes', [
{
name: 'aVertexPosition',
size: 3,
type: gl.FLOAT,
normalized: false,
offset: 0,
enabled: false,
location: -1
},
{
name: 'aVertexNormal',
size: 3,
type: gl.FLOAT,
normalized: false,
offset: 12,
enabled: false,
location: -1
},
{
name: 'aTextureCoord',
size: 2,
type: gl.FLOAT,
normalized: false,
offset: 24,
enabled: false,
location: -1
}
]);
config.uniforms = GetFastValue(config, 'uniforms', [
'uViewProjectionMatrix',
'uLightPosition',
'uLightAmbient',
'uLightDiffuse',
'uLightSpecular',
'uCameraPosition',
'uFogColor',
'uFogNear',
'uFogFar',
'uModelMatrix',
'uNormalMatrix',
'uMaterialAmbient',
'uMaterialDiffuse',
'uMaterialSpecular',
'uMaterialShine',
'uTexture'
]);
WebGLPipeline.call(this, config);
this.forceZero = true;
// Cache structure:
// 0 fog near
// 1 fog far
// 2, 3, 4 model material ambient
// 5, 6, 7 model material diffuse
// 8, 9, 10 model material specular
// 11 model material shine
this.dirtyCache = [
-1,
-1,
-1, -1, -1,
-1, -1, -1,
-1, -1, -1,
-1
];
this.cullMode = 1029;
},
/**
* Called every time the pipeline is bound by the renderer.
* Sets the shader program, vertex buffer and other resources.
* Should only be called when changing pipeline.
*
* @method Phaser.Renderer.WebGL.Pipelines.MeshPipeline#bind
* @since 3.50.0
*
* @param {boolean} [reset=false] - Should the pipeline be fully re-bound after a renderer pipeline clear?
*
* @return {this} This WebGLPipeline instance.
*/
bind: function (reset)
{
if (reset === undefined) { reset = false; }
WebGLPipeline.prototype.bind.call(this, reset);
var gl = this.gl;
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
gl.cullFace(gl.BACK);
return this;
},
/**
* This method is called every time a Game Object asks the Pipeline Manager to use this pipeline.
*
* Unlike the `bind` method, which is only called once per frame, this is called for every object
* that requests it, allowing you to perform per-object GL set-up.
*
* @method Phaser.Renderer.WebGL.Pipelines.MeshPipeline#onBind
* @since 3.50.0
*
* @param {Phaser.GameObjects.Mesh} mesh - The Mesh that requested this pipeline.
*
* @return {this} This WebGLPipeline instance.
*/
onBind: function (mesh)
{
var camera = mesh.camera;
if (camera.dirtyView || camera.dirtyProjection)
{
this.setMatrix4fv('uViewProjectionMatrix', false, camera.viewProjectionMatrix.val);
this.set3f('uCameraPosition', camera.x, camera.y, camera.z);
}
var light = mesh.light;
if (light.isDirty())
{
this.set3f('uLightPosition', light.x, light.y, light.z);
}
var ambient = light.ambient;
var diffuse = light.diffuse;
var specular = light.specular;
if (ambient.dirty)
{
this.set3f('uLightAmbient', ambient.r, ambient.g, ambient.b);
}
if (diffuse.dirty)
{
this.set3f('uLightDiffuse', diffuse.r, diffuse.g, diffuse.b);
}
if (specular.dirty)
{
this.set3f('uLightSpecular', specular.r, specular.g, specular.b);
}
var fogColor = mesh.fogColor;
if (fogColor.dirty)
{
this.set3f('uFogColor', fogColor.r, fogColor.g, fogColor.b);
}
var cache = this.dirtyCache;
var fogNear = mesh.fogNear;
var fogFar = mesh.fogFar;
if (cache[0] !== fogNear)
{
this.set1f('uFogNear', fogNear);
cache[0] = fogNear;
}
if (cache[1] !== fogFar)
{
this.set1f('uFogFar', fogFar);
cache[1] = fogFar;
}
this.set1i('uTexture', 0);
},
drawModel: function (mesh, model)
{
var cache = this.dirtyCache;
this.setMatrix4fv('uModelMatrix', false, model.transformMatrix.val);
this.setMatrix4fv('uNormalMatrix', false, model.normalMatrix.val);
var ambient = model.ambient;
if (!ambient.equals(cache[2], cache[3], cache[4]))
{
this.set3f('uMaterialAmbient', ambient.r, ambient.g, ambient.b);
cache[2] = ambient.r;
cache[3] = ambient.g;
cache[4] = ambient.b;
}
var diffuse = model.diffuse;
if (!diffuse.equals(cache[5], cache[6], cache[7]))
{
this.set3f('uMaterialDiffuse', diffuse.r, diffuse.g, diffuse.b);
cache[5] = diffuse.r;
cache[6] = diffuse.g;
cache[7] = diffuse.b;
}
var specular = model.specular;
if (!specular.equals(cache[8], cache[9], cache[10]))
{
this.set3f('uMaterialSpecular', specular.r, specular.g, specular.b);
cache[8] = specular.r;
cache[9] = specular.g;
cache[10] = specular.b;
}
var shine = model.shine;
if (!shine !== cache[11])
{
this.set1f('uMaterialShine', shine);
cache[11] = specular.b;
}
this.renderer.setTextureZero(model.frame.glTexture);
// All the uniforms are finally bound, so let's buffer our data
var gl = this.gl;
var cullMode = model.cullMode;
if (cullMode !== this.cullMode)
{
this.cullMode = cullMode;
gl.cullFace(cullMode);
}
// STATIC because the buffer data doesn't change, the uniforms do
gl.bufferData(gl.ARRAY_BUFFER, model.vertexData, gl.STATIC_DRAW);
gl.drawArrays(this.topology, 0, model.vertexCount);
}
});
module.exports = MeshPipeline;

View file

@ -54,7 +54,17 @@ var PIPELINE_CONST = {
* @const * @const
* @since 3.50.0 * @since 3.50.0
*/ */
ROPE_PIPELINE: 'RopePipeline' ROPE_PIPELINE: 'RopePipeline',
/**
* The Mesh Pipeline.
*
* @name Phaser.Renderer.WebGL.Pipelines.MESH_PIPELINE
* @type {string}
* @const
* @since 3.50.0
*/
MESH_PIPELINE: 'MeshPipeline'
}; };

View file

@ -0,0 +1,54 @@
module.exports = [
'#define SHADER_NAME PHASER_MESH_FS',
'',
'precision mediump float;',
'',
'uniform vec3 uLightPosition;',
'uniform vec3 uLightAmbient;',
'uniform vec3 uLightDiffuse;',
'uniform vec3 uLightSpecular;',
'',
'uniform vec3 uFogColor;',
'uniform float uFogNear;',
'uniform float uFogFar;',
'',
'uniform vec3 uMaterialAmbient;',
'uniform vec3 uMaterialDiffuse;',
'uniform vec3 uMaterialSpecular;',
'uniform float uMaterialShine;',
'',
'uniform vec3 uCameraPosition;',
'',
'uniform sampler2D uTexture;',
'',
'varying vec2 vTextureCoord;',
'varying vec3 vNormal;',
'varying vec3 vPosition;',
'',
'void main (void)',
'{',
' vec4 color = texture2D(uTexture, vTextureCoord);',
'',
' vec3 ambient = uLightAmbient * uMaterialAmbient;',
'',
' vec3 norm = normalize(vNormal);',
' vec3 lightDir = normalize(uLightPosition - vPosition);',
' float diff = max(dot(norm, lightDir), 0.0);',
' vec3 diffuse = uLightDiffuse * (diff * uMaterialDiffuse);',
'',
' vec3 viewDir = normalize(uCameraPosition - vPosition);',
' vec3 reflectDir = reflect(-lightDir, norm);',
' float spec = pow(max(dot(viewDir, reflectDir), 0.0), uMaterialShine);',
' vec3 specular = uLightSpecular * (spec * uMaterialSpecular);',
'',
' vec3 result = (ambient + diffuse + specular) * color.rgb;',
'',
' float depth = gl_FragCoord.z / gl_FragCoord.w;',
'',
' float fogFactor = smoothstep(uFogNear, uFogFar, depth);',
'',
' gl_FragColor.rgb = mix(result.rgb, uFogColor, fogFactor);',
' gl_FragColor.a = color.a;',
'}',
''
].join('\n');

View file

@ -0,0 +1,29 @@
module.exports = [
'#define SHADER_NAME PHASER_MESH_VS',
'',
'precision mediump float;',
'',
'attribute vec3 aVertexPosition;',
'attribute vec3 aVertexNormal;',
'attribute vec2 aTextureCoord;',
'',
'uniform mat4 uViewProjectionMatrix;',
'uniform mat4 uModelMatrix;',
'uniform mat4 uNormalMatrix;',
'',
'varying vec2 vTextureCoord;',
'varying vec3 vNormal;',
'varying vec3 vPosition;',
'',
'void main ()',
'{',
' vTextureCoord = aTextureCoord;',
'',
' vPosition = vec3(uModelMatrix * vec4(aVertexPosition, 1.0));',
'',
' vNormal = vec3(uNormalMatrix * vec4(aVertexNormal, 1.0));',
'',
' gl_Position = uViewProjectionMatrix * uModelMatrix * vec4(aVertexPosition, 1.0);',
'}',
''
].join('\n');

View file

@ -0,0 +1,51 @@
#define SHADER_NAME PHASER_MESH_FS
precision mediump float;
uniform vec3 uLightPosition;
uniform vec3 uLightAmbient;
uniform vec3 uLightDiffuse;
uniform vec3 uLightSpecular;
uniform vec3 uFogColor;
uniform float uFogNear;
uniform float uFogFar;
uniform vec3 uMaterialAmbient;
uniform vec3 uMaterialDiffuse;
uniform vec3 uMaterialSpecular;
uniform float uMaterialShine;
uniform vec3 uCameraPosition;
uniform sampler2D uTexture;
varying vec2 vTextureCoord;
varying vec3 vNormal;
varying vec3 vPosition;
void main (void)
{
vec4 color = texture2D(uTexture, vTextureCoord);
vec3 ambient = uLightAmbient * uMaterialAmbient;
vec3 norm = normalize(vNormal);
vec3 lightDir = normalize(uLightPosition - vPosition);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = uLightDiffuse * (diff * uMaterialDiffuse);
vec3 viewDir = normalize(uCameraPosition - vPosition);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), uMaterialShine);
vec3 specular = uLightSpecular * (spec * uMaterialSpecular);
vec3 result = (ambient + diffuse + specular) * color.rgb;
float depth = gl_FragCoord.z / gl_FragCoord.w;
float fogFactor = smoothstep(uFogNear, uFogFar, depth);
gl_FragColor.rgb = mix(result.rgb, uFogColor, fogFactor);
gl_FragColor.a = color.a;
}

View file

@ -0,0 +1,26 @@
#define SHADER_NAME PHASER_MESH_VS
precision mediump float;
attribute vec3 aVertexPosition;
attribute vec3 aVertexNormal;
attribute vec2 aTextureCoord;
uniform mat4 uViewProjectionMatrix;
uniform mat4 uModelMatrix;
uniform mat4 uNormalMatrix;
varying vec2 vTextureCoord;
varying vec3 vNormal;
varying vec3 vPosition;
void main ()
{
vTextureCoord = aTextureCoord;
vPosition = vec3(uModelMatrix * vec4(aVertexPosition, 1.0));
vNormal = vec3(uNormalMatrix * vec4(aVertexNormal, 1.0));
gl_Position = uViewProjectionMatrix * uModelMatrix * vec4(aVertexPosition, 1.0);
}