/**
A **Mesh** is an {{#crossLink "Object"}}{{/crossLink}} that represents a drawable 3D primitive.
## Overview
* A Mesh represents a WebGL draw call.
* Each Mesh has six components: {{#crossLink "Geometry"}}{{/crossLink}} for shape, {{#crossLink "Material"}}{{/crossLink}}
for normal rendered appearance, three {{#crossLink "EmphasisMaterial"}}EmphasisMaterials{{/crossLink}} for ghosted, highlighted and selected effects,
and {{#crossLink "EdgeMaterial"}}{{/crossLink}} for rendering emphasised edges.
* By default, Meshes in the same Scene share the same global scene flyweight instances of those components among themselves. The default
component instances are provided by the {{#crossLink "Scene"}}{{/crossLink}}'s {{#crossLink "Scene/geometry:property"}}{{/crossLink}},
{{#crossLink "Scene/material:property"}}{{/crossLink}}, {{#crossLink "Scene/ghostMaterial:property"}}{{/crossLink}}, {{#crossLink "Scene/highlightMaterial:property"}}{{/crossLink}},
{{#crossLink "Scene/selectedMaterial:property"}}{{/crossLink}} and {{#crossLink "Scene/edgeMaterial:property"}}{{/crossLink}} properties.
* A Mesh with all defaults is a white unit-sized box centered at the World-space origin.
* Customize your Meshes by attaching your own instances of those component types, to override the defaults as needed.
* For best performance, reuse as many of the same component instances among your Meshes as possible.
* Use {{#crossLink "Group"}}Group{{/crossLink}} components to organize Meshes into hierarchies, if required.
This page covers functionality specific to the Mesh component, while {{#crossLink "Object"}}{{/crossLink}} covers generic
functionality inherited from the base class.
## Usage
* [Creating a Mesh](#creating-a-mesh)
* [Creating hierarchies](#creating-hierarchies)
* [Controlling visibility](#controlling-visibility)
* [Controlling clipping](#controlling-clipping)
* [Controlling rendering order](#controlling-rendering-order)
* [Geometry](#geometry)
* [Material](#material)
* [Transforming](#transforming)
* [Ghosting](#ghosting)
* [Highlighting](#highlighting)
* [Outlining](#outlining)
* [Local-space boundary](#local-space-boundary)
* [World-space boundary](#world-space-boundary)
* [Skyboxing](#skyboxing)
* [Billboarding](#billboarding)
* [Shadows](#shadows) TODO
### Creating a Mesh
Creating a minimal Mesh that has all the default components:
<img src="../../assets/images/screenshots/Scene/defaultMesh.png"></img>
````javascript
var mesh = new xeogl.Mesh(); // A white unit-sized box centered at the World-space origin
````
Since our Mesh has all the default components, we can get those off either the Mesh or its Scene:
````javascript
mesh.material.diffuse = [1.0, 0.0, 0.0]; // This is the same Material component...
mesh.scene.material.diffuse = [1.0, 0.0, 0.0]; // ...as this one.
````
In practice, we would provide (at least) our own Geometry and Material for the Mesh:
<a href="../../examples/#geometry_primitives_teapot"><img src="../../assets/images/screenshots/Scene/teapot.png"></img></a>
````javascript
var mesh = new xeogl.Mesh({
geometry: new xeogl.TeapotGeometry(),
material: new xeogl.MetallicMaterial({
baseColor: [1.0, 1.0, 1.0]
})
});
````
### Creating hierarchies
In xeogl we represent an object hierarchy as a tree of {{#crossLink "Object"}}Objects{{/crossLink}} in which
the leaf Objects are Meshes. In an Object tree, an operation on an Object is recursively applied to sub-Objects, down
to the Meshes at the leaves.
See {{#crossLink "Object"}}{{/crossLink}} for information on organizing Meshes hierarchically.
### Controlling visibility
Show or hide a Mesh by setting its {{#crossLink "Mesh/visible:property"}}{{/crossLink}} property:
````javascript
mesh.visible = false; // Hide
mesh.visible = true; // Show (default)
````
This property is inherited from {{#crossLink "Object/visible:property"}}Object{{/crossLink}}.
### Controlling clipping
By default, a Mesh will be clipped by the
Scene's {{#crossLink "Scene/clips:property"}}clipping planes{{/crossLink}}.
Make a Mesh unclippable by setting its {{#crossLink "Mesh/clippable:property"}}{{/crossLink}} property false:
````javascript
mesh.clippable = false; // Default is true
````
### Controlling rendering order
Control the order in which a Mesh is rendered relative to others by setting its {{#crossLink "Mesh/layer:property"}}{{/crossLink}}
property. You would normally do this when you need to ensure that transparent Meshes are rendered in back-to-front order for correct alpha blending.
Assigning our Mesh to layer 0 (all Meshes are in layer 0 by default):
````javascript
mesh.layer = 0;
````
Create another Mesh in a higher layer, that will get rendered after layer 0:
````javascript
var mesh2 = new xeogl.Mesh({
geometry: new xeogl.Sphere(),
layer: 1
});
````
### Geometry
A Mesh has a {{#crossLink "Geometry"}}{{/crossLink}} which describes its shape. When we don't provide a Geometry,
a Mesh will automatically get its {{#crossLink "Scene"}}{{/crossLink}}'s {{#crossLink "Scene/geometry:property"}}{{/crossLink}} by default.
Creating a Mesh with its own Geometry:
````javascript
var mesh = new xeogl.Mesh({
geometry: new xeogl.TeapotGeometry()
});
````
Getting geometry arrays:
````javascript
ver geometry = mesh.geometry;
var primitive = geometry.primitive; // Default is "triangles"
var positions = geometry.positions; // Local-space vertex positions
var normals = geometry.normals; // Local-space vertex Normals
var uv = geometry.uv; // UV coordinates
var indices = mesh.geometry.indices; // Vertex indices for pimitives
````
The Mesh also has a convenience property which provides the vertex positions in World-space, ie. after they have been
transformed by the Mesh's {{#crossLink "Object/worldMatrix:property"}}{{/crossLink}}:
````javascript
// These are internally generated on-demand and cached. To free the cached
// vertex World positions when you're done with them, set this property to null or undefined
var worldPositions = mesh.worldPositions;
````
### Material
A Mesh has a {{#crossLink "Material"}}{{/crossLink}}, which describes its appearance. When we don't provide it with
a Material, it will automatically get its {{#crossLink "Scene"}}{{/crossLink}}'s {{#crossLink "Scene/material:property"}}{{/crossLink}} by default.
Creating a Mesh with its own custom {{#crossLink "Geometry"}}{{/crossLink}} and {{#crossLink "MetallicMaterial"}}{{/crossLink}}:
````javascript
var mesh = new xeogl.Mesh({
geometry: new xeogl.TeapotGeometry(),
material: new xeogl.MetallicMaterial({
baseColor: [0.0, 0.0, 1.0],
metallic: 1.0,
roughness: 1.0,
emissive: [0.0, 0.0, 0.0],
alpha: 1.0
})
});
````
Animating the {{#crossLink "MetallicMaterial"}}{{/crossLink}}'s diffuse color - making the Mesh rapidly pulse red:
````javascript
mesh.scene.on("tick", function(e) {
var t = e.time - e.startTime; // Millisecs
mesh.material.baseColor = [0.5 + Math.sin(t * 0.01), 0.0, 0.0]; // RGB
});
````
### Transforming
A Mesh can be positioned within the World-space coordinate system.
See {{#crossLink "Object"}}{{/crossLink}}.
### Ghosting
Ghost a Mesh by setting its {{#crossLink "Mesh/ghosted:property"}}{{/crossLink}} property true. The Mesh's
{{#crossLink "Mesh/ghostMaterial:property"}}{{/crossLink}} property holds the {{#crossLink "EmphasisMaterial"}}{{/crossLink}}
that controls its appearance while ghosted.
When we don't provide it with a EmphasisMaterial, the Mesh will automatically get its Scene's {{#crossLink "Scene/ghostMaterial:property"}}{{/crossLink}}
by default.
In the example below, we'll create a ghosted Mesh with its own EmphasisMaterial for ghosted appearance:
<a href="../../examples/#effects_ghost"><img src="../../assets/images/screenshots/EmphasisMaterial/teapot.png"></img></a>
````javascript
var mesh = new xeogl.Mesh({
geometry: new xeogl.TeapotGeometry(),
material: new xeogl.PhongMaterial({
diffuse: [0.2, 0.2, 1.0]
}),
ghostMaterial: new xeogl.EmphasisMaterial({
edges: true,
edgeColor: [0.2, 1.0, 0.2],
edgeAlpha: 1.0,
edgeWidth: 2,
vertices: true,
vertexColor: [0.6, 1.0, 0.6],
vertexAlpha: 1.0,
vertexSize: 8,
fill: true,
fillColor: [0, 0, 0],
fillAlpha: 0.7
}),
ghosted: true
});
````
#### Examples
* [Ghosted teapot](../../examples/#effects_demo_hoverToGhost)
### Highlighting
Highlight a Mesh by setting its {{#crossLink "Mesh/highlighted:property"}}{{/crossLink}} property true. The Mesh's
{{#crossLink "Mesh/highlightMaterial:property"}}{{/crossLink}} property holds the {{#crossLink "EmphasisMaterial"}}{{/crossLink}}
that controls its appearance while highlighted.
When we don't provide it with a EmphasisMaterial for highlighting, it will automatically get its Scene's {{#crossLink "Scene/highlightMaterial:property"}}{{/crossLink}}
by default.
In the example below, we'll create a highlighted Mesh with its own EmphasisMaterial for highlighted appearance:
<a href="../../examples/#effects_highlight"><img src="../../assets/images/screenshots/EmphasisMaterial/teapotHighlighted.png"></img></a>
````javascript
var mesh = new xeogl.Mesh({
geometry: new xeogl.TeapotGeometry(),
material: new xeogl.PhongMaterial({
diffuse: [0.2, 0.2, 1.0]
}),
highlightMaterial: new xeogl.EmphasisMaterial({
color: [1.0, 1.0, 0.0],
alpha: 0.6
}),
highlighted: true
});
````
#### Examples
* [Ghost and highlight effects](../../examples/#effects_demo_hoverToHighlight)
### Selecting
Make a Mesh appear selected by setting its {{#crossLink "Mesh/selected:property"}}{{/crossLink}} property true. The Mesh's
{{#crossLink "Mesh/selectedMaterial:property"}}{{/crossLink}} property holds the {{#crossLink "EmphasisMaterial"}}{{/crossLink}}
that controls its appearance while selected.
When we don't provide it with a EmphasisMaterial for selecting, it will automatically get its Scene's {{#crossLink "Scene/selectMaterial:property"}}{{/crossLink}}
by default.
In the example below, we'll create a selected Mesh with its own EmphasisMaterial for selection appearance:
<a href="../../examples/#effects_select"><img src="../../assets/images/screenshots/EmphasisMaterial/teapotSelected.png"></img></a>
````javascript
var mesh = new xeogl.Mesh({
geometry: new xeogl.TeapotGeometry(),
material: new xeogl.PhongMaterial({
diffuse: [0.2, 0.2, 1.0]
}),
selectMaterial: new xeogl.EmphasisMaterial({
color: [1.0, 1.0, 0.0],
alpha: 0.6
}),
selected: true
});
````
#### Examples
* [Ghost and select effects](../../examples/#effects_demo_gearbox)
### Edges
Emphasise a Mesh's edges by setting its {{#crossLink "Mesh/edges:property"}}{{/crossLink}} property true. The Mesh's
{{#crossLink "Mesh/edgeMaterial:property"}}{{/crossLink}} property holds the {{#crossLink "EdgeMaterial"}}{{/crossLink}}
that controls the appearance of the edges while they are emphasized.
When we don't provide it with an EdgeMaterial, the Mesh will automatically get its Scene's {{#crossLink "Scene/edgeMaterial:property"}}{{/crossLink}}
by default.
In the example below, we'll create a edges Mesh with its own EdgeMaterial for edges appearance:
<a href="../../examples/#effects_ghost"><img src="../../assets/images/screenshots/EdgeMaterial/teapot.png"></img></a>
````javascript
var mesh = new xeogl.Mesh({
geometry: new xeogl.TeapotGeometry(),
material: new xeogl.PhongMaterial({
diffuse: [0.2, 0.2, 1.0]
}),
edgeMaterial: new xeogl.EdgeMaterial({
edgeColor: [0.2, 1.0, 0.2],
edgeAlpha: 1.0,
edgeWidth: 2
}),
edges: true
});
````
### Outlining
Outline a Mesh by setting its {{#crossLink "Mesh/outlined:property"}}{{/crossLink}} property true. The Mesh's
{{#crossLink "Mesh/outlineMaterial:property"}}{{/crossLink}} property holds the {{#crossLink "OutlineMaterial"}}{{/crossLink}}
that controls its appearance while outlined.
When we don't provide it with an {{#crossLink "OutlineMaterial"}}{{/crossLink}}, it will automatically get its Scene's
{{#crossLink "Scene/outlineMaterial:property"}}{{/crossLink}} by default.
In the example below, we'll create a outlined Mesh with its own {{#crossLink "OutlineMaterial"}}{{/crossLink}}:
<a href="../../examples/#effects_outline"><img src="../../assets/images/screenshots/OutlineMaterial/teapot.png"></img></a>
````javascript
var mesh = new xeogl.Mesh({
geometry: new xeogl.TeapotGeometry(),
material: new xeogl.PhongMaterial({
diffuse: [0.2, 0.2, 1.0]
}),
outlineMaterial: new xeogl.OutlineMaterial({
color: [1.0, 1.0, 0.0],
alpha: 0.6,
width: 5
}),
outlined: true
});
````
### Local-space boundary
We can query a Mesh's Local-space boundary at any time, getting it as either an axis-aligned bounding box (AABB) or
an object-aligned bounding box (OBB).
The Local-space AABB and OBB belong to the Mesh's {{#crossLink "Geometry"}}{{/crossLink}}.
Getting the Local-space AABB:
````
var aabb = mesh.geometry.aabb; // [xmin, ymin, zmin, xmax, ymax, zmax]
````
Getting the Local-space OBB:
```` javascript
var obb = mesh.geometry.obb; // Flat array containing eight 3D corner vertices of a box
````
#### Examples
* [Local-space Geometry AABB](../../examples/#boundaries_geometry_aabb)
* [Local-space Geometry OBB](../../examples/#boundaries_geometry_obb)
### World-space boundary
We can query a Mesh's World-space boundary at any time, getting it as an axis-aligned bounding box (AABB).
The World-space AABB is the boundary of the Mesh's {{#crossLink "Geometry"}}{{/crossLink}} after transformation by the
Mesh's {{#crossLink "Object/worldMatrix:property"}}{{/crossLink}} and the {{#crossLink "Camera"}}{{/crossLink}}'s
{{#crossLink "Camera/matrix:property"}}{{/crossLink}}.
Getting the World-space boundary AABB:
````javascript
var aabb = mesh.aabb; // [xmin, ymin, zmin, xmax, ymax, zmax]
````
Subscribing to updates of the World-space boundary, which occur after each update to the
Mesh's {{#crossLink "Object/worldMatrix:property"}}{{/crossLink}} or the {{#crossLink "Camera"}}{{/crossLink}}:
````javascript
mesh.on("boundary", function() {
var aabb = mesh.aabb;
var obb = mesh.obb;
});
````
The {{#crossLink "Scene"}}{{/crossLink}} also has a {{#crossLink "Scene/getAABB:method"}}Scene#getAABB(){{/crossLink}}, which returns
the collective World-space AABBs of the {{#crossLink "Object"}}Objects{{/crossLink}} with the given IDs:
````JavaScript
var scene = mesh.scene;
scene.getAABB(); // Gets collective boundary of all meshes in the scene
scene.getAABB("saw"); // Gets collective boundary of all meshes in a model
scene.getAABB(["saw", "gearbox"]); // Gets collective boundary of all meshes in two models
scene.getAABB("saw#0.1"); // Get boundary of a mesh
scene.getAABB(["saw#0.1", "saw#0.2"]); // Get collective boundary of two meshes
````
#### Excluding from boundary calculations
The {{#crossLink "Scene/aabb:property"}}Scene aabb{{/crossLink}}
and parent {{#crossLink "Object/aabb:property"}}Object{{/crossLink}}'s {{#crossLink "Object/aabb:property"}}aabb{{/crossLink}}
properties provide AABBs that dynamically include the AABB of all contained Meshes, except those Meshes that have
their {{#crossLink "Mesh/collidable:property"}}collidable{{/crossLink}} properties set ````false````.
Toggle that inclusion like so:
````javascript
mesh.collidable = false; // Exclude mesh from calculation of its Scene/Model boundary
mesh.collidable = true; // Include mesh in calculation of its Scene/Model boundary
````
Setting this false is useful when a Mesh represents some element, such as a control gizmo, that you don't want to
contribute to the {{#crossLink "Scene"}}Scene{{/crossLink}} or parent {{#crossLink "Object"}}{{/crossLink}}'s AABB. It
also helps performance, since boundaries will not need dynamically re-calculated whenever the Mesh's boundary changes after
a {{#crossLink "Object/worldMatrix:property"}}{{/crossLink}} or {{#crossLink "Camera"}}{{/crossLink}} update.
#### Examples
* [World-space Mesh AABB](../../examples/#boundaries_mesh_aabb)
* [World-space Mesh OBB](../../examples/#boundaries_mesh_obb)
### Skyboxing
A Mesh has a {{#crossLink "Mesh/stationary:property"}}{{/crossLink}} property
that will cause it to never translate with respect to the viewpoint.
This is useful for using Meshes as skyboxes, like this:
````javascript
new xeogl.Mesh({
geometry: new xeogl.BoxGeometry({
xSize: 1000,
ySize: 1000,
zSize: 1000
}),
material: new xeogl.PhongMaterial({
diffuseMap: new xeogl.Texture({
src: "textures/diffuse/uvGrid2.jpg"
})
}),
stationary: true // Locks position with respect to viewpoint
});
````
#### Examples
* [Skybox component](../../examples/#skyboxes_skybox)
* [Custom skybox](../../examples/#skyboxes_skybox_custom)
### Billboarding
A Mesh has a {{#crossLink "Mesh/billboard:property"}}{{/crossLink}} property
that can make it behave as a billboard.
Two billboard types are supported:
* **Spherical** billboards are free to rotate their Meshes in any direction and always face the {{#crossLink "Camera"}}{{/crossLink}} perfectly.
* **Cylindrical** billboards rotate their Meshes towards the {{#crossLink "Camera"}}{{/crossLink}}, but only about the Y-axis.
Note that scaling transformations to have no effect on billboarded Meshes.
The example below shows a box that remains rotated directly towards the viewpoint, using spherical billboarding:
````javascript
new xeogl.Mesh({
geometry: new xeogl.BoxGeometry(),
material: new xeogl.PhongMaterial({
diffuseMap: new xeogl.Texture({
src: "textures/diffuse/uvGrid2.jpg"
})
}),
billboard: "spherical" // Or "cylindrical"
});
````
#### Examples
* [Spherical billboards](../../examples/#billboards_spherical)
* [Cylindrical billboards](../../examples/#billboards_cylindrical)
* [Clouds using billboards](../../examples/#billboards_spherical_clouds)
### Shadows
[Work-in-progress]
@class Mesh
@module xeogl
@submodule objects
@constructor
@param [owner] {Component} Owner component. When destroyed, the owner will destroy this component as well. Creates this component within the default {{#crossLink "Scene"}}{{/crossLink}} when omitted.
@param [cfg] {*} Configs
@param [cfg.id] {String} Optional ID, unique among all components in the parent {{#crossLink "Scene"}}Scene{{/crossLink}}, generated automatically when omitted.
@param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to this Mesh.
@param [cfg.entityType] {String} Optional entity classification when using within a semantic data model. See the {{#crossLink "Object"}}{{/crossLink}} documentation for usage.
@param [cfg.parent] {Object} The parent.
@param [cfg.position=[0,0,0]] {Float32Array} The Mesh's local 3D position.
@param [cfg.scale=[1,1,1]] {Float32Array} The Mesh's local scale.
@param [cfg.rotation=[0,0,0]] {Float32Array} The Mesh's local rotation, as Euler angles given in degrees, for each of the X, Y and Z axis.
@param [cfg.matrix=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1] {Float32Array} The Mesh's local modelling transform matrix. Overrides the position, scale and rotation parameters.
@param [cfg.geometry] {Geometry} Defines shape. Must be within the same {{#crossLink "Scene"}}Scene{{/crossLink}} as this Mesh. Defaults to the
parent {{#crossLink "Scene"}}Scene{{/crossLink}}'s default instance, {{#crossLink "Scene/geometry:property"}}geometry{{/crossLink}}, which is a 2x2x2 box.
@param [cfg.material] {Material} Defines normal rendered appearance. Must be within the same {{#crossLink "Scene"}}Scene{{/crossLink}} as this Mesh. Defaults to the
parent {{#crossLink "Scene"}}Scene{{/crossLink}}'s default instance, {{#crossLink "Scene/material:property"}}material{{/crossLink}}.
@param [cfg.outlineMaterial] {OutlineMaterial} Defines appearance when outlined. Must be within the same {{#crossLink "Scene"}}Scene{{/crossLink}} as this Mesh. Defaults to the
parent {{#crossLink "Scene"}}Scene{{/crossLink}}'s default instance, {{#crossLink "Scene/outlineMaterial:property"}}outlineMaterial{{/crossLink}}.
@param [cfg.ghostMaterial] Defines appearance when ghosted. Must be within the same {{#crossLink "Scene"}}Scene{{/crossLink}} as this Mesh. Defaults to the
parent {{#crossLink "Scene"}}Scene{{/crossLink}}'s default instance, {{#crossLink "Scene/ghostMaterial:property"}}ghostMaterial{{/crossLink}}.
@param [cfg.highlightMaterial] Defines appearance when highlighted. Must be within the same {{#crossLink "Scene"}}Scene{{/crossLink}} as this Mesh. Defaults to the
parent {{#crossLink "Scene"}}Scene{{/crossLink}}'s default instance, {{#crossLink "Scene/highlightMaterial:property"}}highlightMaterial{{/crossLink}}.
@param [cfg.selectedMaterial] Defines appearance when selected. Must be within the same {{#crossLink "Scene"}}Scene{{/crossLink}} as this Mesh. Defaults to the
parent {{#crossLink "Scene"}}Scene{{/crossLink}}'s default instance, {{#crossLink "Scene/selectedMaterial:property"}}selectedMaterial{{/crossLink}}.
@param [cfg.colorize=[1.0,1.0,1.0]] {Float32Array} RGB colorize color, multiplies by the rendered fragment colors.
@param [cfg.opacity=1.0] {Number} Opacity factor, multiplies by the rendered fragment alpha.
@param [cfg.layer=0] {Number} Indicates this Mesh's rendering priority, relative to other Meshes. Typically used for transparency sorting,
@param [cfg.stationary=false] {Boolean} Disables the effect of {{#crossLink "Camera"}}{{/crossLink}} translations for this Mesh. This is useful for making skyboxes.
@param [cfg.billboard="none"] {String} Specifies the billboarding behaviour for this Mesh. Options are "none", "spherical" and "cylindrical".
@param [cfg.visible=true] {Boolean} Indicates if this Mesh is visible. Mesh is only rendered when visible and not culled.
@param [cfg.culled=false] {Boolean} Indicates if this Mesh is culled from view. Mesh is only rendered when visible and not culled.
@param [cfg.pickable=true] {Boolean} Indicates if this Mesh is pickable. When false, the Mesh will never be picked by calls to the {{#crossLink "Scene/pick:method"}}Scene pick(){{/crossLink}} method, and picking will happen as "through" the Mesh, to attempt to pick whatever lies on the other side of it.
@param [cfg.clippable=true] {Boolean} Indicates if this Mesh is clippable by {{#crossLink "Clips"}}{{/crossLink}}. When false, Mesh will not be affected by the {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Clips"}}{{/crossLink}}.
@param [cfg.collidable=true] {Boolean} Whether this Mesh is included in boundary calculations. When false, the bounding boxes of the containing {{#crossLink "Scene"}}{{/crossLink}} and parent {{#crossLink "Object"}}{{/crossLink}}, {{#crossLink "Group"}}{{/crossLink}} or {{#crossLink "Model"}}{{/crossLink}} will not be calculated to enclose this Mesh.
@param [cfg.castShadow=true] {Boolean} Whether this Mesh casts shadows.
@param [cfg.receiveShadow=true] {Boolean} Whether this Mesh receives shadows.
@param [cfg.outlined=false] {Boolean} Whether an outline is rendered around this mesh.
@param [cfg.ghosted=false] {Boolean} Whether this Mesh is rendered with a ghosted appearance.
@param [cfg.highlighted=false] {Boolean} Whether this Mesh is rendered with a highlighted appearance.
@param [cfg.selected=false] {Boolean} Whether this Mesh is rendered with a selected appearance.
@param [cfg.aabbVisible=false] {Boolean} Whether this Mesh's World-space axis-aligned bounding box (AABB) is visible.
@param [cfg.obbVisible=false] {Boolean} Whether this Mesh's World-space oriented bounding box (OBB) is visible.
@param [cfg.colorize=[1.0,1.0,1.0]] {Float32Array} RGB colorize color, multiplies by the rendered fragment colors.
@param [cfg.opacity=1.0] {Number} Opacity factor, multiplies by the rendered fragment alpha.
@param [cfg.loading=false] {Boolean} Flag which indicates that this Mesh is freshly loaded.
@extends Object
*/
/**
Fired when this Mesh is picked via a call to {{#crossLink "Scene/pick:method"}}Scene#pick(){{/crossLink}}.
The event parameters will be the hit result returned by the {{#crossLink "Scene/pick:method"}}Scene#pick(){{/crossLink}} method.
@event picked
*/
import {math} from '../math/math.js';
import {xeoglObject} from './object.js';
import {State} from '../renderer/state.js';
import {DrawRenderer} from "../renderer/draw/drawRenderer.js";
import {EmphasisFillRenderer} from "../renderer/emphasis/emphasisFillRenderer.js";
import {EmphasisEdgesRenderer} from "../renderer/emphasis/emphasisEdgesRenderer.js";
import {EmphasisVerticesRenderer} from "../renderer/emphasis/emphasisVerticesRenderer.js";
import {ShadowRenderer} from "../renderer/shadow/shadowRenderer.js";
import {OutlineRenderer} from "../renderer/outline/outlineRenderer.js";
import {PickMeshRenderer} from "../renderer/pick/pickMeshRenderer.js";
import {PickVertexRenderer} from "../renderer/pick/pickVertexRenderer.js";
import {PickTriangleRenderer} from "../renderer/pick/pickTriangleRenderer.js";
import {componentClasses} from "./../componentClasses.js";
const obb = math.OBB3();
const type = "xeogl.Mesh";
class Mesh extends xeoglObject {
/**
JavaScript class name for this Component.
For example: "xeogl.AmbientLight", "xeogl.MetallicMaterial" etc.
@property type
@type String
@final
*/
get type() {
return type;
}
static _compareState(a, b) {
return (a._state.layer - b._state.layer)
|| (a._drawRenderer.id - b._drawRenderer.id) // Program state
|| (a._material._state.id - b._material._state.id) // Material state
|| (a._vertexBufs.id - b._vertexBufs.id) // SHared vertex bufs
|| (a._geometry._state.id - b._geometry._state.id); // Geometry state
}
init(cfg) {
this._state = new State({ // NOTE: Renderer gets modeling and normal matrices from xeogl.Object#matrix and xeogl.Object.#normalMatrix
visible: true,
culled: false,
pickable: null,
clippable: null,
colorize: null,
collidable: null,
castShadow: null,
receiveShadow: null,
outlined: null,
ghosted: false,
highlighted: false,
selected: false,
edges: false,
layer: null,
billboard: this._checkBillboard(cfg.billboard),
stationary: !!cfg.stationary,
hash: ""
});
this._drawRenderer = null;
this._shadowRenderer = null;
this._emphasisFillRenderer = null;
this._emphasisEdgesRenderer = null;
this._emphasisVerticesRenderer = null;
this._pickMeshRenderer = null;
this._pickTriangleRenderer = null;
this._worldPositions = null;
this._worldPositionsDirty = true;
this._geometry = cfg.geometry ? this._checkComponent("xeogl.Geometry", cfg.geometry) : this.scene.geometry;
this._vertexBufs = this._geometry._getVertexBufs();
this._material = cfg.material ? this._checkComponent("xeogl.Material", cfg.material) : this.scene.material;
this._ghostMaterial = cfg.ghostMaterial ? this._checkComponent("xeogl.EmphasisMaterial", cfg.ghostMaterial) : this.scene.ghostMaterial;
this._outlineMaterial = cfg.outlineMaterial ? this._checkComponent("xeogl.EmphasisMaterial", cfg.outlineMaterial) : this.scene.outlineMaterial;
this._highlightMaterial = cfg.highlightMaterial ? this._checkComponent("xeogl.EmphasisMaterial", cfg.highlightMaterial) : this.scene.highlightMaterial;
this._selectedMaterial = cfg.selectedMaterial ? this._checkComponent("xeogl.EmphasisMaterial", cfg.selectedMaterial) : this.scene.selectedMaterial;
this._edgeMaterial = cfg.edgeMaterial ? this._checkComponent("xeogl.EdgeMaterial", cfg.edgeMaterial) : this.scene.edgeMaterial;
this._compile();
super.init(cfg); // Call xeogl.Object._init()
this.scene._meshCreated(this);
}
_checkBillboard(value) {
value = value || "none";
if (value !== "spherical" && value !== "cylindrical" && value !== "none") {
this.error("Unsupported value for 'billboard': " + value + " - accepted values are " +
"'spherical', 'cylindrical' and 'none' - defaulting to 'none'.");
value = "none";
}
return value;
}
_compile() {
this._putRenderers();
this._makeHash();
this._drawRenderer = DrawRenderer.get(this);
this._shadowRenderer = ShadowRenderer.get(this);
this._emphasisFillRenderer = EmphasisFillRenderer.get(this);
this._emphasisEdgesRenderer = EmphasisEdgesRenderer.get(this);
this._emphasisVerticesRenderer = EmphasisVerticesRenderer.get(this);
this._pickMeshRenderer = PickMeshRenderer.get(this);
this._renderer.meshListDirty();
}
_webglContextRestored() {
if (this._drawRenderer) {
this._drawRenderer.webglContextRestored();
}
if (this._shadowRenderer) {
this._shadowRenderer.webglContextRestored();
}
if (this._emphasisFillRenderer) {
this._emphasisFillRenderer.webglContextRestored();
}
if (this._emphasisEdgesRenderer) {
this._emphasisEdgesRenderer.webglContextRestored();
}
if (this._emphasisVerticesRenderer) {
this._emphasisVerticesRenderer.webglContextRestored();
}
if (this._pickMeshRenderer) {
this._pickMeshRenderer.webglContextRestored();
}
if (this._pickTriangleRenderer) {
this._pickMeshRenderer.webglContextRestored();
}
}
_makeHash() {
const hash = [];
const state = this._state;
if (state.stationary) {
hash.push("/s");
}
if (state.billboard === "none") {
hash.push("/n");
} else if (state.billboard === "spherical") {
hash.push("/s");
} else if (state.billboard === "cylindrical") {
hash.push("/c");
}
if (state.receiveShadow) {
hash.push("/rs");
}
hash.push(";");
this._state.hash = hash.join("");
}
_buildMeshAABB(worldMatrix, aabb) { // TODO: factor out into class member
math.transformOBB3(worldMatrix, this._geometry.obb, obb);
math.OBB3ToAABB3(obb, aabb);
}
_getSceneHash() {
return (this.scene.gammaInput ? "gi;" : ";") + (this.scene.gammaOutput ? "go" : "");
}
//--------------------- Rendering ------------------------------------------------------------------------------
_draw(frame) {
if (this._drawRenderer || (this._drawRenderer = DrawRenderer.get(this))) {
this._drawRenderer.drawMesh(frame, this);
}
}
_drawGhostFill(frame) {
if (this._emphasisFillRenderer || (this._emphasisFillRenderer = EmphasisFillRenderer.get(this))) {
this._emphasisFillRenderer.drawMesh(frame, this, 0); // 0 == ghost
}
}
_drawGhostEdges(frame) {
if (this._emphasisEdgesRenderer || (this._emphasisEdgesRenderer = EmphasisEdgesRenderer.get(this))) {
this._emphasisEdgesRenderer.drawMesh(frame, this, 0); // 0 == ghost
}
}
_drawGhostVertices(frame) {
if (this._emphasisVerticesRenderer || (this._emphasisVerticesRenderer = EmphasisVerticesRenderer.get(this))) {
this._emphasisVerticesRenderer.drawMesh(frame, this, 0); // 0 == ghost
}
}
_drawHighlightFill(frame) {
if (this._emphasisFillRenderer || (this._emphasisFillRenderer = EmphasisFillRenderer.get(this))) {
this._emphasisFillRenderer.drawMesh(frame, this, 1); // 1 == highlight
}
}
_drawHighlightEdges(frame) {
if (this._emphasisEdgesRenderer || (this._emphasisEdgesRenderer = EmphasisEdgesRenderer.get(this))) {
this._emphasisEdgesRenderer.drawMesh(frame, this, 1); // 1 == highlight
}
}
_drawHighlightVertices(frame) {
if (this._emphasisVerticesRenderer || (this._emphasisVerticesRenderer = EmphasisVerticesRenderer.get(this))) {
this._emphasisVerticesRenderer.drawMesh(frame, this, 1); // 1 == highlight
}
}
_drawSelectedFill(frame) {
if (this._emphasisFillRenderer || (this._emphasisFillRenderer = EmphasisFillRenderer.get(this))) {
this._emphasisFillRenderer.drawMesh(frame, this, 2); // 2 == selected
}
}
_drawSelectedEdges(frame) {
if (this._emphasisEdgesRenderer || (this._emphasisEdgesRenderer = EmphasisEdgesRenderer.get(this))) {
this._emphasisEdgesRenderer.drawMesh(frame, this, 2); // 2 == selected
}
}
_drawSelectedVertices(frame) {
if (this._emphasisVerticesRenderer || (this._emphasisVerticesRenderer = EmphasisVerticesRenderer.get(this))) {
this._emphasisVerticesRenderer.drawMesh(frame, this, 2); // 2 == selected
}
}
_drawEdges(frame) {
if (this._emphasisEdgesRenderer || (this._emphasisEdgesRenderer = EmphasisEdgesRenderer.get(this))) {
this._emphasisEdgesRenderer.drawMesh(frame, this, 3); // 3 == edges
}
}
_drawShadow(frame, light) {
if (this._shadowRenderer || (this._shadowRenderer = ShadowRenderer.get(this))) {
this._shadowRenderer.drawMesh(frame, this, light);
}
}
_drawOutline(frame) {
if (this._shadowRenderer || (this._outlineRenderer = OutlineRenderer.get(this))) {
this._outlineRenderer.drawMesh(frame, this);
}
}
_pickMesh(frame) {
if (this._pickMeshRenderer || (this._pickMeshRenderer = PickMeshRenderer.get(this))) {
this._pickMeshRenderer.drawMesh(frame, this);
}
}
_pickTriangle(frame) {
if (this._pickTriangleRenderer || (this._pickTriangleRenderer = PickTriangleRenderer.get(this))) {
this._pickTriangleRenderer.drawMesh(frame, this);
}
}
_pickVertex(frame) {
if (this._pickVertexRenderer || (this._pickVertexRenderer = PickVertexRenderer.get(this))) {
this._pickVertexRenderer.drawMesh(frame, this);
}
}
_getOutlineRenderer() {
this._outlineRenderer = OutlineRenderer.get(this);
if (this._outlineRenderer.errors) {
this.errors = (this.errors || []).concat(this._outlineRenderer.errors);
this.error(this._outlineRenderer.errors.join("\n"));
return false;
}
return true;
}
_putRenderers() {
if (this._drawRenderer) {
this._drawRenderer.put();
this._drawRenderer = null;
}
if (this._shadowRenderer) {
this._shadowRenderer.put();
this._shadowRenderer = null;
}
if (this._emphasisFillRenderer) {
this._emphasisFillRenderer.put();
this._emphasisFillRenderer = null;
}
if (this._emphasisEdgesRenderer) {
this._emphasisEdgesRenderer.put();
this._emphasisEdgesRenderer = null;
}
if (this._emphasisVerticesRenderer) {
this._emphasisVerticesRenderer.put();
this._emphasisVerticesRenderer = null;
}
if (this._outlineRenderer) {
this._outlineRenderer.put();
this._outlineRenderer = null;
}
if (this._pickMeshRenderer) {
this._pickMeshRenderer.put();
this._pickMeshRenderer = null;
}
if (this._pickTriangleRenderer) {
this._pickTriangleRenderer.put();
this._pickTriangleRenderer = null;
}
if (this._pickVertexRenderer) {
this._pickVertexRenderer.put();
this._pickVertexRenderer = null;
}
}
/**
World-space 3D vertex positions.
These are internally generated on-demand and cached. To free the cached
vertex World positions when you're done with them, set this property to null or undefined.
@property worldPositions
@type Float32Array
@final
*/
get worldPositions() {
if (this._worldPositionsDirty) {
const positions = this._geometry.positions;
if (!this._worldPositions) {
this._worldPositions = new Float32Array(positions.length);
}
math.transformPositions3(this.worldMatrix, positions, this._worldPositions);
this._worldPositionsDirty = false;
}
return this._worldPositions;
}
set worldPositions(value) {
if (value = undefined || value === null) {
this._worldPositions = null; // Release memory
this._worldPositionsDirty = true;
}
}
/**
Defines the shape of this Mesh.
@property geometry
@type Geometry
@final
*/
get geometry() {
return this._geometry;
}
/**
Defines appearance when rendering normally, ie. when not ghosted, highlighted or selected.
@property material
@type Material
@final
*/
get material() {
return this._material;
}
/**
Defines surface appearance when ghosted.
@property ghostMaterial
@type EmphasisMaterial
@final
*/
get ghostMaterial() {
return this._ghostMaterial;
}
/**
Defines surface appearance when highlighted.
@property highlightMaterial
@type EmphasisMaterial
@final
*/
get highlightMaterial() {
return this._highlightMaterial;
}
/**
Defines surface appearance when selected.
@property selectedMaterial
@type EmphasisMaterial
*/
get selectedMaterial() {
return this._selectedMaterial;
}
/**
Defines surface appearance when edges are shown.
@property edgeMaterial
@type EdgeMaterial
*/
get edgeMaterial() {
return this._edgeMaterial;
}
/**
Defines surface appearance when outlined.
@property outlineMaterial
@type OutlineMaterial
*/
get outlineMaterial() {
return this._outlineMaterial;
}
/**
Indicates if visible.
The Mesh is only rendered when {{#crossLink "Mesh/visible:property"}}{{/crossLink}} is true and
{{#crossLink "Mesh/culled:property"}}{{/crossLink}} is false.
Each visible Mesh is registered in the {{#crossLink "Scene"}}{{/crossLink}}'s
{{#crossLink "Scene/visibleEntities:property"}}{{/crossLink}} map when its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
is set to a value.
@property visible
@default true
@type Boolean
*/
set visible(visible) {
visible = visible !== false;
this._state.visible = visible;
if (this._entityType) {
this.scene._entityVisibilityUpdated(this, visible);
}
this._renderer.imageDirty();
if (this._state.castShadow) {
this._renderer.shadowsDirty();
}
}
get visible() {
return this._state.visible;
}
/**
Indicates if ghosted.
The ghosted appearance is configured by {{#crossLink "Mesh/ghostMaterial:property"}}ghostMaterial{{/crossLink}}.
Each ghosted Mesh is registered in its {{#crossLink "Scene"}}{{/crossLink}}'s
{{#crossLink "Scene/ghostedEntities:property"}}{{/crossLink}} map when its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
is set to a value.
@property ghosted
@default false
@type Boolean
*/
set ghosted(ghosted) {
ghosted = !!ghosted;
if (this._state.ghosted === ghosted) {
return;
}
this._state.ghosted = ghosted;
if (this._entityType) {
this.scene._entityGhostedUpdated(this, ghosted);
}
this._renderer.imageDirty();
}
get ghosted() {
return this._state.ghosted;
}
/**
Indicates if highlighted.
The highlight appearance is configured by {{#crossLink "Mesh/highlightMaterial:property"}}highlightMaterial{{/crossLink}}.
Each highlighted Mesh is registered in its {{#crossLink "Scene"}}{{/crossLink}}'s
{{#crossLink "Scene/highlightedEntities:property"}}{{/crossLink}} map when its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
is set to a value.
@property highlighted
@default false
@type Boolean
*/
set highlighted(highlighted) {
highlighted = !!highlighted;
if (highlighted === this._state.highlighted) {
return;
}
this._state.highlighted = highlighted;
if (this._entityType) {
this.scene._entityHighlightedUpdated(this, highlighted);
}
this._renderer.imageDirty();
}
get highlighted() {
return this._state.highlighted;
}
/**
Indicates if selected.
The selected appearance is configured by {{#crossLink "Mesh/selectedMaterial:property"}}selectedMaterial{{/crossLink}}.
Each selected Mesh is registered in its {{#crossLink "Scene"}}{{/crossLink}}'s
{{#crossLink "Scene/selectedEntities:property"}}{{/crossLink}} map when its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
is set to a value.
@property selected
@default false
@type Boolean
*/
set selected(selected) {
selected = !!selected;
if (selected === this._state.selected) {
return;
}
this._state.selected = selected;
if (this._entityType) {
this.scene._entitySelectedUpdated(this, selected);
}
this._renderer.imageDirty();
}
get selected() {
return this._state.selected;
}
/**
Indicates if edges are shown.
The edges appearance is configured by {{#crossLink "Mesh/edgeMaterial:property"}}edgeMaterial{{/crossLink}}.
@property edges
@default false
@type Boolean
*/
set edges(edges) {
edges = !!edges;
if (edges === this._state.edges) {
return;
}
this._state.edges = edges;
this._renderer.imageDirty();
}
get edges() {
return this._state.edges;
}
/**
Indicates if culled from view.
The MEsh is only rendered when {{#crossLink "Mesh/visible:property"}}{{/crossLink}} is true and
{{#crossLink "Mesh/culled:property"}}{{/crossLink}} is false.
@property culled
@default false
@type Boolean
*/
set culled(value) {
this._state.culled = !!value;
this._renderer.imageDirty();
}
get culled() {
return this._state.culled;
}
/**
Indicates if pickable.
When false, the Mesh will never be picked by calls to the {{#crossLink "Scene/pick:method"}}Scene pick(){{/crossLink}} method, and picking will happen as "through" the Mesh, to attempt to pick whatever lies on the other side of it.
@property pickable
@default true
@type Boolean
*/
set pickable(value) {
value = value !== false;
if (this._state.pickable === value) {
return;
}
this._state.pickable = value;
// No need to trigger a render;
// state is only used when picking
}
get pickable() {
return this._state.pickable;
}
/**
Indicates if clippable.
When false, the {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Clips"}}{{/crossLink}} will have no effect on the Mesh.
@property clippable
@default true
@type Boolean
*/
set clippable(value) {
value = value !== false;
if (this._state.clippable === value) {
return;
}
this._state.clippable = value;
this._renderer.imageDirty();
if (this._state.castShadow) {
this._renderer.shadowsDirty();
}
}
get clippable() {
return this._state.clippable;
}
/**
Indicates if included in boundary calculations.
When false, this Mesh will not be included in the bounding boxes provided by parent components (
@property collidable
@default true
@type Boolean
*/
set collidable(value) {
value = value !== false;
if (value === this._state.collidable) {
return;
}
this._state.collidable = value;
}
get collidable() {
return this._state.collidable;
}
/**
Indicates if casting shadows.
@property castShadow
@default true
@type Boolean
*/
set castShadow(value) {
value = value !== false;
if (value === this._state.castShadow) {
return;
}
this._state.castShadow = value;
this._renderer.shadowsDirty();
}
get castShadow() {
return this._state.castShadow;
}
/**
Indicates if receiving shadows.
@property receiveShadow
@default true
@type Boolean
*/
set receiveShadow(value) {
this._state.receiveShadow = false; // Disables shadows for now
// value = value !== false;
// if (value === this._state.receiveShadow) {
// return;
// }
// this._state.receiveShadow = value;
// this._state.hash = value ? "/mod/rs;" : "/mod;";
// this.fire("dirty", this); // Now need to (re)compile objectRenderers to include/exclude shadow mapping
}
get receiveShadow() {
return this._state.receiveShadow;
}
/**
Indicates if rendered with an outline.
The outline appearance is configured by {{#crossLink "Mesh/outlineMaterial:property"}}outlineMaterial{{/crossLink}}.
@property outlined
@default false
@type Boolean
*/
set outlined(value) {
value = !!value;
if (value === this._state.outlined) {
return;
}
this._state.outlined = value;
this._renderer.imageDirty();
}
get outlined() {
return this._state.outlined;
}
/**
RGB colorize color, multiplies by the rendered fragment colors.
@property colorize
@default [1.0, 1.0, 1.0]
@type Float32Array
*/
set colorize(value) {
let colorize = this._state.colorize;
if (!colorize) {
colorize = this._state.colorize = new Float32Array(4);
colorize[3] = 1;
}
if (value) {
colorize[0] = value[0];
colorize[1] = value[1];
colorize[2] = value[2];
} else {
colorize[0] = 1;
colorize[1] = 1;
colorize[2] = 1;
}
this._renderer.imageDirty();
}
get colorize() {
return this._state.colorize;
}
/**
Opacity factor, multiplies by the rendered fragment alpha.
This is a factor in range ````[0..1]````.
@property opacity
@default 1.0
@type Number
*/
set opacity(opacity) {
let colorize = this._state.colorize;
if (!colorize) {
colorize = this._state.colorize = new Float32Array(4);
colorize[0] = 1;
colorize[1] = 1;
colorize[2] = 1;
}
colorize[3] = opacity !== null && opacity !== undefined ? opacity : 1.0;
this._renderer.imageDirty();
}
get opacity() {
return this._state.colorize[3];
}
/**
The rendering order.
This can be set on multiple transparent Meshes, to make them render in a specific order
for correct alpha blending.
@property layer
@default 0
@type Number
*/
set layer(value) {
// TODO: Only accept rendering layer in range [0...MAX_layer]
value = value || 0;
value = Math.round(value);
if (value === this._state.layer) {
return;
}
this._state.layer = value;
this._renderer.needStateSort();
}
get layer() {
return this._state.layer;
}
/**
Indicates if the position is stationary.
When true, will disable the effect of {{#crossLink "Lookat"}}view transform{{/crossLink}}
translations for this Mesh, while still allowing it to rotate. This is useful for skybox Meshes.
@property stationary
@default false
@type Boolean
@final
*/
get stationary() {
return this._state.stationary;
}
/**
Indicates the billboarding behaviour.
Options are:
* **"none"** - **(default)** - No billboarding.
* **"spherical"** - Mesh is billboarded to face the viewpoint, rotating both vertically and horizontally.
* **"cylindrical"** - Mesh is billboarded to face the viewpoint, rotating only about its vertically
axis. Use this mode for things like trees on a landscape.
@property billboard
@default "none"
@type String
@final
*/
get billboard() {
return this._state.billboard;
}
destroy() {
super.destroy(); // xeogl.Object
this._putRenderers();
this._renderer.meshListDirty();
this.scene._meshDestroyed(this);
if (this._state.castShadow) {
this._renderer.shadowsDirty();
}
}
}
componentClasses[type] = Mesh;
export {Mesh};