diff --git a/rollup.config.js b/rollup.config.js
new file mode 100644
index 000000000..5bcf039de
--- /dev/null
+++ b/rollup.config.js
@@ -0,0 +1,20 @@
+
+export default {
+ input: 'src/xeogl.js',
+ plugins: [
+ ],
+ // sourceMap: true,
+ output: [
+ {
+ format: 'umd',
+ name: 'xeogl',
+ file: 'build/xeogl.js',
+ indent: '\t'
+ },
+ {
+ format: 'es',
+ file: 'build/xeogl.module.js',
+ indent: '\t'
+ }
+ ]
+};
diff --git a/src/_utils/inheritance.js b/src/_utils/inheritance.js
deleted file mode 100644
index d20d7ea8c..000000000
--- a/src/_utils/inheritance.js
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- Based on Simple JavaScript Inheritance
- By John Resig http://ejohn.org/
- MIT Licensed.
- */
-// Inspired by base2 and Prototype
-(function () {
-
- let initializing = false;
-
- const fnTest = /xyz/.test(function () {
- xyz;
- }) ? /\b_super\b/ : /.*/;
-
- // The base Class implementation (does nothing)
- this.Class = function () {
- };
-
- // Create a new Class that inherits from this class
- Class.extend = function (prop) {
-
- const _super = this.prototype;
-
- // Instantiate a base class (but only create the instance,
- // don't run the init constructor)
- initializing = true;
- const prototype = new this();
- initializing = false;
-
- // Copy the properties over onto the new prototype
- for (const name in prop) {
-
- //
- if (name === "_props") {
- const props = prop[name];
- let descriptor;
- for (const key in props) {
- descriptor = props[key];
- if (key.indexOf(",") >= 0) { // Aliased property name of form "foo, bar, baz": { .. }
- const aliases = key.split(",");
- for (let i = 0, len = aliases.length; i < len; i++) {
- const alias = aliases[i].trim();
- if (!descriptor.set) {
- (function () {
- const name3 = alias;
- descriptor.set = function () {
- this.warn("Property '" + name3 + "' is read-only, ignoring assignment");
- };
- })();
- }
- descriptor.enumerable = true; // Want property to show up in inspectors
- Object.defineProperty(prototype, alias, descriptor);
- }
- } else {
-
- // If no setter is provided, then the property
- // is strictly read-only. Insert a dummy setter
- // to log a warning.
-
- if (!descriptor.set) {
- (function () {
- const name = key;
- descriptor.set = function () {
- this.warn("Property '" + name + "' is read-only, ignoring assignment");
- };
- })();
- }
- descriptor.enumerable = true; // Want property to show up in inspectors
- Object.defineProperty(prototype, key, descriptor);
- }
- }
- continue;
- }
-
- // Check if we're overwriting an existing function
-
- const existsOnSuper = !!_super[name];
- const isFunc = typeof prop[name] === "function";
- const superIsFunc = typeof _super[name] === "function";
- const passFnTest = fnTest.test(prop[name]);
-
- if (existsOnSuper) {
- if (isFunc && !superIsFunc) {
- throw "Can't override super class property with function: '" + name + "'";
- }
- if (!isFunc && superIsFunc) {
- throw "Can't override super class function with property: '" + name + "'";
- }
- }
-
- if (isFunc) {
-
- if (existsOnSuper) {
-
- // Exists on super, so overriding.
- // Allow possibility for sub-class function to call super function from within itself.
-
- prototype[name] = (function (name, fn) {
- return function () {
- const tmp = this._super;
-
- // Add a new ._super() method that is the same method
- // but on the super-class
- this._super = _super[name];
-
- // The method only need to be bound temporarily, so we
- // remove it when we're done executing
- const ret = fn.apply(this, arguments);
- this._super = tmp;
-
- return ret;
- };
- })(name, prop[name])
-
- } else {
-
- // Does not exist on super; just define on subclass.
-
- prototype[name] = prop[name];
- }
- } else {
-
- // Not a function; just define on subclass.
-
- prototype[name] = prop[name];
- }
- }
-
- // Create array of type names to indicate inheritance chain,
- // to support "isType" queries on components
- prototype.superTypes = _super.superTypes ? _super.superTypes.concat(_super.type) : [];
-
- if (!prop.type) {
- prop.type = _super.type + "_" + createUUID();
- } else {
- xeogl._superTypes[prop.type] = prototype.superTypes;
- }
-
- // The dummy class constructor
- function Class() {
-
- // All construction is actually done in the init method
- if (!initializing && this.__init)
- this.__init.apply(this, arguments);
- }
-
- // Populate our constructed prototype object
- Class.prototype = prototype;
-
- // Enforce the constructor to be what we expect
- Class.prototype.constructor = Class;
-
- // And make this class extendable
- Class.extend = arguments.callee;
-
- window[prop.type] = Class;
-
- return Class;
- };
-
- /**
- * Returns a new UUID.
- * @method createUUID
- * @static
- * @return string The new UUID
- */
- var createUUID = (function () {
- // http://www.broofa.com/Tools/Math.uuid.htm
- const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
- const uuid = new Array(36);
- let rnd = 0, r;
- return function () {
- for (let i = 0; i < 36; i++) {
- if (i === 8 || i === 13 || i === 18 || i === 23) {
- uuid[i] = '-';
- } else if (i === 14) {
- uuid[i] = '4';
- } else {
- if (rnd <= 0x02) {
- rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0;
- }
- r = rnd & 0xf;
- rnd = rnd >> 4;
- uuid[i] = chars[( i === 19 ) ? ( r & 0x3 ) | 0x8 : r];
- }
- }
- return uuid.join('');
- };
- })();
-})();
-
diff --git a/src/animation/cameraFlightAnimation.js b/src/animation/cameraFlightAnimation.js
index 8c173634d..d4bb674b8 100644
--- a/src/animation/cameraFlightAnimation.js
+++ b/src/animation/cameraFlightAnimation.js
@@ -74,7 +74,7 @@
@module xeogl
@submodule animation
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}}.
+ @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.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 CameraFlightAnimation.
@param [cfg.fit=true] {Boolean} When true, will ensure that when this CameraFlightAnimation has flown or jumped to a boundary
@@ -86,661 +86,536 @@
@param [cfg.duration=1] {Number} Flight duration, in seconds, when calling {{#crossLink "CameraFlightAnimation/flyTo:method"}}CameraFlightAnimation#flyTo(){{/crossLink}}.
@extends Component
*/
-(function () {
-
- "use strict";
-
- const math = xeogl.math;
-
- xeogl.CameraFlightAnimation = xeogl.Component.extend({
-
- /**
- JavaScript class name for this Component.
-
- @property type
- @type String
- @final
- */
- type: "xeogl.CameraFlightAnimation",
-
- _init: function (cfg) {
-
- // Shows a wireframe box for target AABBs
- this._aabbHelper = this.create({
- type: "xeogl.Mesh",
- geometry: this.create({
- type: "xeogl.AABBGeometry"
- }),
- material: this.create({
- type: "xeogl.PhongMaterial",
- diffuse: [0, 0, 0],
- ambient: [0, 0, 0],
- specular: [0, 0, 0],
- emissive: [0.5, 1.0, 0.5],
- lineWidth: 2
- }),
- visible: false,
- collidable: false
- });
-
- // Shows a wireframe box for target AABBs
- this._obbHelper = this.create({
- type: "xeogl.Mesh",
- geometry: this.create({
- type: "xeogl.OBBGeometry",
- material: this.create({
- type: "xeogl.PhongMaterial",
- diffuse: [0, 0, 0],
- ambient: [0, 0, 0],
- specular: [0, 0, 0],
- emissive: [0.5, 1.0, 0.5],
- lineWidth: 2
- })
- }),
- visible: false,
- collidable: false // Effectively has no boundary
- });
-
- this._look1 = math.vec3();
- this._eye1 = math.vec3();
- this._up1 = math.vec3();
-
- this._look2 = math.vec3();
- this._eye2 = math.vec3();
- this._up2 = math.vec3();
-
- this._orthoScale1 = 1;
- this._orthoScale2 = 1;
-
- this._flying = false;
- this._flyEyeLookUp = false;
- this._flyingEye = false;
- this._flyingLook = false;
- this._flyingUp = false;
-
- this._callback = null;
- this._callbackScope = null;
-
- this._onTick = null;
-
- this._time1 = null;
- this._time2 = null;
-
- this.easing = cfg.easing !== false;
-
- this.duration = cfg.duration;
- this.fit = cfg.fit;
- this.fitFOV = cfg.fitFOV;
- this.trail = cfg.trail;
- },
-
- /**
- * Begins flying the {{#crossLink "Camera"}}{{/crossLink}}'s {{#crossLink "Camera"}}{{/crossLink}} to the given target.
- *
- * * When the target is a boundary, the {{#crossLink "Camera"}}{{/crossLink}} will fly towards the target
- * and stop when the target fills most of the canvas.
- * * When the target is an explicit {{#crossLink "Camera"}}{{/crossLink}} position, given as ````eye````, ````look```` and ````up````
- * vectors, then this CameraFlightAnimation will interpolate the {{#crossLink "Camera"}}{{/crossLink}} to that target and stop there.
- * @method flyTo
- * @param [params=scene] {*|Component} Either a parameters object or a {{#crossLink "Component"}}{{/crossLink}} subtype that has an AABB.
- * @param[params.arc=0] {Number} Factor in range [0..1] indicating how much the
- * {{#crossLink "Lookat/eye:property"}}Camera's eye{{/crossLink}} position will
- * swing away from its {{#crossLink "Lookat/eye:property"}}look{{/crossLink}} position as it flies to the target.
- * @param [params.component] {Number|String|Component} ID or instance of a component to fly to. Defaults to the entire {{#crossLink "Scene"}}{{/crossLink}}.
- * @param [params.aabb] {*} World-space axis-aligned bounding box (AABB) target to fly to.
- * @param [params.eye] {Float32Array} Position to fly the eye position to.
- * @param [params.look] {Float32Array} Position to fly the look position to.
- * @param [params.up] {Float32Array} Position to fly the up vector to.
- * @param [params.fit=true] {Boolean} Whether to fit the target to the view volume. Overrides {{#crossLink "CameraFlightAnimation/fit:property"}}{{/crossLink}}.
- * @param [params.fitFOV] {Number} How much of field-of-view, in degrees, that a target {{#crossLink "Object"}}{{/crossLink}} or its AABB should
- * fill the canvas on arrival. Overrides {{#crossLink "CameraFlightAnimation/fitFOV:property"}}{{/crossLink}}.
- * @param [params.duration] {Number} Flight duration in seconds. Overrides {{#crossLink "CameraFlightAnimation/duration:property"}}{{/crossLink}}.
- * @param [params.orthoScale] {Number} TODO: document this
- * @param [callback] {Function} Callback fired on arrival
- * @param [scope] {Object} Optional scope for callback
- */
- flyTo: (function () {
-
- const tempVec3 = math.vec3();
-
- return function (params, callback, scope) {
-
- params = params || this.scene;
-
- if (this._flying) {
- this.stop();
- }
-
- this._flying = false;
-
- this._callback = callback;
- this._callbackScope = scope;
-
- const camera = this.scene.camera;
-
- this._eye1[0] = camera.eye[0];
- this._eye1[1] = camera.eye[1];
- this._eye1[2] = camera.eye[2];
-
- this._look1[0] = camera.look[0];
- this._look1[1] = camera.look[1];
- this._look1[2] = camera.look[2];
-
- this._up1[0] = camera.up[0];
- this._up1[1] = camera.up[1];
- this._up1[2] = camera.up[2];
-
- this._orthoScale1 = camera.ortho.scale;
- this._orthoScale2 = params.orthoScale || this._orthoScale1;
-
- let aabb;
- let eye;
- let look;
- let up;
- let componentId;
-
- if (params.aabb) {
- aabb = params.aabb;
-
- } else if (params.length === 6) {
- aabb = params;
-
- } else if ((params.eye && params.look) || params.up) {
- eye = params.eye;
- look = params.look;
- up = params.up;
-
- } else if (params.eye) {
- eye = params.eye;
-
- } else if (params.look) {
- look = params.look;
-
- } else {
-
- // Argument must be an instance or ID of a Component (subtype)
-
- let component = params;
-
- if (xeogl._isNumeric(component) || xeogl._isString(component)) {
-
- componentId = component;
-
- component = this.scene.components[componentId];
-
- if (!component) {
- this.error("Component not found: " + xeogl._inQuotes(componentId));
- if (callback) {
- if (scope) {
- callback.call(scope);
- } else {
- callback();
- }
- }
- return;
- }
- }
-
- aabb = component.aabb || this.scene.aabb;
- }
-
- const poi = params.poi;
-
- if (aabb) {
-
- if (aabb[3] < aabb[0] || aabb[4] < aabb[1] || aabb[5] < aabb[2]) {
-
- // Don't fly to an inverted boundary
- return;
- }
-
- if (aabb[3] === aabb[0] && aabb[4] === aabb[1] && aabb[5] === aabb[2]) {
-
- // Don't fly to an empty boundary
- return;
- }
-
- // Show boundary
-
- if (params.showAABB !== false) {
- this._aabbHelper.geometry.targetAABB = aabb;
- //this._aabbHelper.visible = true;
- }
-
- aabb = aabb.slice();
-
- const aabbCenter = math.getAABB3Center(aabb);
- this._look2 = poi || aabbCenter;
-
- const eyeLookVec = math.subVec3(this._eye1, this._look1, tempVec3);
- const eyeLookVecNorm = math.normalizeVec3(eyeLookVec);
- const diag = poi ? math.getAABB3DiagPoint(aabb, poi) : math.getAABB3Diag(aabb);
- const fitFOV = params.fitFOV || this._fitFOV;
- const sca = Math.abs(diag / Math.tan(fitFOV * xeogl.math.DEGTORAD));
-
- this._orthoScale2 = diag * 1.1;
-
- this._eye2[0] = this._look2[0] + (eyeLookVecNorm[0] * sca);
- this._eye2[1] = this._look2[1] + (eyeLookVecNorm[1] * sca);
- this._eye2[2] = this._look2[2] + (eyeLookVecNorm[2] * sca);
-
- this._up2[0] = this._up1[0];
- this._up2[1] = this._up1[1];
- this._up2[2] = this._up1[2];
-
- this._flyEyeLookUp = false;
-
- } else if (eye || look || up) {
-
- this._flyEyeLookUp = !!eye && !!look && !!up;
- this._flyingEye = !!eye && !look;
- this._flyingLook = !!look && !eye;
-
- if (look) {
- this._look2[0] = look[0];
- this._look2[1] = look[1];
- this._look2[2] = look[2];
- }
-
- if (eye) {
- this._eye2[0] = eye[0];
- this._eye2[1] = eye[1];
- this._eye2[2] = eye[2];
- }
-
- if (up) {
- this._up2[0] = up[0];
- this._up2[1] = up[1];
- this._up2[2] = up[2];
- }
- }
-
- this.fire("started", params, true);
-
- this._time1 = Date.now();
- this._time2 = this._time1 + (params.duration ? params.duration * 1000 : this._duration);
-
- this._flying = true; // False as soon as we stop
-
- xeogl.scheduleTask(this._update, this);
- };
- })(),
-
- /**
- * Jumps the {{#crossLink "Camera"}}{{/crossLink}}'s {{#crossLink "Camera"}}{{/crossLink}} to the given target.
- *
- * * When the target is a boundary, this CameraFlightAnimation will position the {{#crossLink "Camera"}}{{/crossLink}}
- * at where the target fills most of the canvas.
- * * When the target is an explicit {{#crossLink "Camera"}}{{/crossLink}} position, given as ````eye````, ````look```` and ````up````
- * vectors, then this CameraFlightAnimation will jump the {{#crossLink "Camera"}}{{/crossLink}} to that target.
- *
- * @method flyTo
- * @param params {*|Component} Either a parameters object or a {{#crossLink "Component"}}{{/crossLink}} subtype that has a World-space AABB.
- * @param[params.arc=0] {Number} Factor in range [0..1] indicating how much the
- * {{#crossLink "Camera/eye:property"}}Camera's eye{{/crossLink}} position will
- * swing away from its {{#crossLink "Camera/eye:property"}}look{{/crossLink}} position as it flies to the target.
- * @param [params.component] {Number|String|Component} ID or instance of a component to fly to.
- * @param [params.aabb] {*} World-space axis-aligned bounding box (AABB) target to fly to.
- * @param [params.eye] {Float32Array} Position to fly the eye position to.
- * @param [params.look] {Float32Array} Position to fly the look position to.
- * @param [params.up] {Float32Array} Position to fly the up vector to.
- * @param [params.fitFOV] {Number} How much of field-of-view, in degrees, that a target {{#crossLink "Object"}}{{/crossLink}} or its AABB should
- * fill the canvas on arrival. Overrides {{#crossLink "CameraFlightAnimation/fitFOV:property"}}{{/crossLink}}.
- * @param [params.fit] {Boolean} Whether to fit the target to the view volume. Overrides {{#crossLink "CameraFlightAnimation/fit:property"}}{{/crossLink}}.
- */
- jumpTo: function (params) {
- const self = this;
- // xeogl.scheduleTask(function () { // Ensures that required asynch boundaries are built first
- self._jumpTo(params);
- // });
- },
-
- _jumpTo: (function () {
-
- let newEye = math.vec3();
- let newLook = math.vec3();
- let newUp = math.vec3();
- const newLookEyeVec = math.vec3();
- const tempVec3e = math.vec3();
-
- return function (params) {
-
- if (this._flying) {
- this.stop();
- }
+import {core} from "./../core.js";
+import {math} from '../math/math.js';
+import {utils} from '../utils.js';
+import {tasks} from '../tasks.js';
+import {Component} from '../component.js';
+import {Mesh} from '../objects/mesh.js';
+import {AABBGeometry} from '../geometry/aabbGeometry.js';
+import {PhongMaterial} from '../materials/phongMaterial.js';
+
+const type = "xeogl.CameraFlightAnimation";
+
+const tempVec3 = math.vec3();
+const newLookEyeVec = math.vec3();
+const lookEyeVec = math.vec3();
+
+class CameraFlightAnimation extends Component {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return type;
+ }
+
+ init(cfg) {
+
+ super.init(cfg);
+
+ this._aabbHelper = new Mesh(this, { // Shows a wireframe box for target AABBs
+ geometry: new AABBGeometry(this),
+ material: new PhongMaterial(this, {
+ diffuse: [0, 0, 0],
+ ambient: [0, 0, 0],
+ specular: [0, 0, 0],
+ emissive: [0.5, 1.0, 0.5],
+ lineWidth: 2
+ }),
+ visible: false,
+ collidable: false
+ });
+
+ this._look1 = math.vec3();
+ this._eye1 = math.vec3();
+ this._up1 = math.vec3();
+ this._look2 = math.vec3();
+ this._eye2 = math.vec3();
+ this._up2 = math.vec3();
+ this._orthoScale1 = 1;
+ this._orthoScale2 = 1;
+ this._flying = false;
+ this._flyEyeLookUp = false;
+ this._flyingEye = false;
+ this._flyingLook = false;
+ this._callback = null;
+ this._callbackScope = null;
+ this._time1 = null;
+ this._time2 = null;
+ this.easing = cfg.easing !== false;
+
+ this.duration = cfg.duration;
+ this.fit = cfg.fit;
+ this.fitFOV = cfg.fitFOV;
+ this.trail = cfg.trail;
+ }
+
+ /**
+ * Begins flying the {{#crossLink "Camera"}}{{/crossLink}}'s {{#crossLink "Camera"}}{{/crossLink}} to the given target.
+ *
+ * * When the target is a boundary, the {{#crossLink "Camera"}}{{/crossLink}} will fly towards the target
+ * and stop when the target fills most of the canvas.
+ * * When the target is an explicit {{#crossLink "Camera"}}{{/crossLink}} position, given as ````eye````, ````look```` and ````up````
+ * vectors, then this CameraFlightAnimation will interpolate the {{#crossLink "Camera"}}{{/crossLink}} to that target and stop there.
+ * @method flyTo
+ * @param [params=scene] {*|Component} Either a parameters object or a {{#crossLink "Component"}}{{/crossLink}} subtype that has an AABB.
+ * @param[params.arc=0] {Number} Factor in range [0..1] indicating how much the
+ * {{#crossLink "Lookat/eye:property"}}Camera's eye{{/crossLink}} position will
+ * swing away from its {{#crossLink "Lookat/eye:property"}}look{{/crossLink}} position as it flies to the target.
+ * @param [params.component] {Number|String|Component} ID or instance of a component to fly to. Defaults to the entire {{#crossLink "Scene"}}{{/crossLink}}.
+ * @param [params.aabb] {*} World-space axis-aligned bounding box (AABB) target to fly to.
+ * @param [params.eye] {Float32Array} Position to fly the eye position to.
+ * @param [params.look] {Float32Array} Position to fly the look position to.
+ * @param [params.up] {Float32Array} Position to fly the up vector to.
+ * @param [params.fit=true] {Boolean} Whether to fit the target to the view volume. Overrides {{#crossLink "CameraFlightAnimation/fit:property"}}{{/crossLink}}.
+ * @param [params.fitFOV] {Number} How much of field-of-view, in degrees, that a target {{#crossLink "Object"}}{{/crossLink}} or its AABB should
+ * fill the canvas on arrival. Overrides {{#crossLink "CameraFlightAnimation/fitFOV:property"}}{{/crossLink}}.
+ * @param [params.duration] {Number} Flight duration in seconds. Overrides {{#crossLink "CameraFlightAnimation/duration:property"}}{{/crossLink}}.
+ * @param [params.orthoScale] {Number} TODO: document this
+ * @param [callback] {Function} Callback fired on arrival
+ * @param [scope] {Object} Optional scope for callback
+ */
+ flyTo(params, callback, scope) {
+
+ params = params || this.scene;
+
+ if (this._flying) {
+ this.stop();
+ }
- const camera = this.scene.camera;
+ this._flying = false;
- let aabb;
- let componentId;
+ this._callback = callback;
+ this._callbackScope = scope;
- if (params.aabb) { // Boundary3D
+ const camera = this.scene.camera;
- aabb = params.aabb;
+ this._eye1[0] = camera.eye[0];
+ this._eye1[1] = camera.eye[1];
+ this._eye1[2] = camera.eye[2];
- } else if (params.length === 6) { // AABB
+ this._look1[0] = camera.look[0];
+ this._look1[1] = camera.look[1];
+ this._look1[2] = camera.look[2];
- aabb = params;
+ this._up1[0] = camera.up[0];
+ this._up1[1] = camera.up[1];
+ this._up1[2] = camera.up[2];
- } else if (params.eye || params.look || params.up) { // Camera pose
+ this._orthoScale1 = camera.ortho.scale;
+ this._orthoScale2 = params.orthoScale || this._orthoScale1;
- newEye = params.eye;
- newLook = params.look;
- newUp = params.up;
+ let aabb;
+ let eye;
+ let look;
+ let up;
+ let componentId;
- } else {
+ if (params.aabb) {
+ aabb = params.aabb;
- // Argument must be an instance or ID of a Component (subtype)
+ } else if (params.length === 6) {
+ aabb = params;
- let component = params;
+ } else if ((params.eye && params.look) || params.up) {
+ eye = params.eye;
+ look = params.look;
+ up = params.up;
- if (xeogl._isNumeric(component) || xeogl._isString(component)) {
+ } else if (params.eye) {
+ eye = params.eye;
- componentId = component;
+ } else if (params.look) {
+ look = params.look;
- component = this.scene.components[componentId];
+ } else { // Argument must be an instance or ID of a Component (subtype)
- if (!component) {
- this.error("Component not found: " + xeogl._inQuotes(componentId));
- return;
+ let component = params;
+ if (utils.isNumeric(component) || utils.isString(component)) {
+ componentId = component;
+ component = this.scene.components[componentId];
+ if (!component) {
+ this.error("Component not found: " + utils.inQuotes(componentId));
+ if (callback) {
+ if (scope) {
+ callback.call(scope);
+ } else {
+ callback();
}
}
-
- aabb = component.aabb || this.scene.aabb;
- }
-
- const poi = params.poi;
-
- if (aabb) {
-
- var diag;
-
- if (aabb[3] <= aabb[0] || aabb[4] <= aabb[1] || aabb[5] <= aabb[2]) {
-
- // Don't fly to an empty boundary
- return;
- }
-
- var diag = poi ? math.getAABB3DiagPoint(aabb, poi) : math.getAABB3Diag(aabb);
- newLook = poi || math.getAABB3Center(aabb, newLook);
-
- if (this._trail) {
- math.subVec3(camera.look, newLook, newLookEyeVec);
- } else {
- math.subVec3(camera.eye, camera.look, newLookEyeVec);
- }
-
- math.normalizeVec3(newLookEyeVec);
-
- let dist;
-
- const fit = (params.fit !== undefined) ? params.fit : this._fit;
- if (fit) {
- dist = Math.abs((diag) / Math.tan((params.fitFOV || this._fitFOV) * xeogl.math.DEGTORAD));
-
- } else {
- dist = math.lenVec3(math.subVec3(camera.eye, camera.look, tempVec3e));
- }
-
- math.mulVec3Scalar(newLookEyeVec, dist);
-
- camera.eye = math.addVec3(newLook, newLookEyeVec, newEye);
- camera.look = newLook;
-
- } else if (newEye || newLook || newUp) {
-
- if (newEye) {
- camera.eye = newEye;
- }
-
- if (newLook) {
- camera.look = newLook;
- }
-
- if (newUp) {
- camera.up = newUp;
- }
+ return;
}
- };
- })(),
-
- _update: (function () {
+ }
+ aabb = component.aabb || this.scene.aabb;
+ }
- const newLookEyeVec = math.vec3();
- const newEye = math.vec3();
- const newLook = math.vec3();
- const newUp = math.vec3();
- const lookEyeVec = math.vec3();
+ const poi = params.poi;
- return function () {
+ if (aabb) {
+ if (aabb[3] < aabb[0] || aabb[4] < aabb[1] || aabb[5] < aabb[2]) { // Don't fly to an inverted boundary
+ return;
+ }
+ if (aabb[3] === aabb[0] && aabb[4] === aabb[1] && aabb[5] === aabb[2]) { // Don't fly to an empty boundary
+ return;
+ }
+ if (params.showAABB !== false) { // Show boundary
+ this._aabbHelper.geometry.targetAABB = aabb;
+ //this._aabbHelper.visible = true;
+ }
- if (!this._flying) {
- return;
- }
+ aabb = aabb.slice();
+ const aabbCenter = math.getAABB3Center(aabb);
- const time = Date.now();
+ this._look2 = poi || aabbCenter;
- let t = (time - this._time1) / (this._time2 - this._time1);
+ const eyeLookVec = math.subVec3(this._eye1, this._look1, tempVec3);
+ const eyeLookVecNorm = math.normalizeVec3(eyeLookVec);
+ const diag = poi ? math.getAABB3DiagPoint(aabb, poi) : math.getAABB3Diag(aabb);
+ const fitFOV = params.fitFOV || this._fitFOV;
+ const sca = Math.abs(diag / Math.tan(fitFOV * math.DEGTORAD));
- const stopping = (t >= 1);
+ this._orthoScale2 = diag * 1.1;
- if (t > 1) {
- t = 1;
- }
+ this._eye2[0] = this._look2[0] + (eyeLookVecNorm[0] * sca);
+ this._eye2[1] = this._look2[1] + (eyeLookVecNorm[1] * sca);
+ this._eye2[2] = this._look2[2] + (eyeLookVecNorm[2] * sca);
- t = this.easing ? this._ease(t, 0, 1, 1) : t;
+ this._up2[0] = this._up1[0];
+ this._up2[1] = this._up1[1];
+ this._up2[2] = this._up1[2];
- const camera = this.scene.camera;
+ this._flyEyeLookUp = false;
+ } else if (eye || look || up) {
- if (this._flyingEye || this._flyingLook) {
+ this._flyEyeLookUp = !!eye && !!look && !!up;
+ this._flyingEye = !!eye && !look;
+ this._flyingLook = !!look && !eye;
- if (this._flyingEye) {
- math.subVec3(camera.eye, camera.look, newLookEyeVec);
- camera.eye = math.lerpVec3(t, 0, 1, this._eye1, this._eye2, newEye);
- camera.look = math.subVec3(newEye, newLookEyeVec, newLook);
+ if (look) {
+ this._look2[0] = look[0];
+ this._look2[1] = look[1];
+ this._look2[2] = look[2];
+ }
- } else if (this._flyingLook) {
- camera.look = math.lerpVec3(t, 0, 1, this._look1, this._look2, newLook);
- // camera.eye = math.addVec3(newLook, newLookEyeVec, newEye);
- camera.up = math.lerpVec3(t, 0, 1, this._up1, this._up2, newUp);
- }
+ if (eye) {
+ this._eye2[0] = eye[0];
+ this._eye2[1] = eye[1];
+ this._eye2[2] = eye[2];
+ }
- } else if (this._flyEyeLookUp) {
+ if (up) {
+ this._up2[0] = up[0];
+ this._up2[1] = up[1];
+ this._up2[2] = up[2];
+ }
+ }
- camera.eye = math.lerpVec3(t, 0, 1, this._eye1, this._eye2, newEye);
- camera.look = math.lerpVec3(t, 0, 1, this._look1, this._look2, newLook);
- camera.up = math.lerpVec3(t, 0, 1, this._up1, this._up2, newUp);
- } else {
+ this.fire("started", params, true);
+
+ this._time1 = Date.now();
+ this._time2 = this._time1 + (params.duration ? params.duration * 1000 : this._duration);
+
+ this._flying = true; // False as soon as we stop
+
+ tasks.scheduleTask(this._update, this);
+ }
+
+ /**
+ * Jumps the {{#crossLink "Camera"}}{{/crossLink}}'s {{#crossLink "Camera"}}{{/crossLink}} to the given target.
+ *
+ * * When the target is a boundary, this CameraFlightAnimation will position the {{#crossLink "Camera"}}{{/crossLink}}
+ * at where the target fills most of the canvas.
+ * * When the target is an explicit {{#crossLink "Camera"}}{{/crossLink}} position, given as ````eye````, ````look```` and ````up````
+ * vectors, then this CameraFlightAnimation will jump the {{#crossLink "Camera"}}{{/crossLink}} to that target.
+ *
+ * @method flyTo
+ * @param params {*|Component} Either a parameters object or a {{#crossLink "Component"}}{{/crossLink}} subtype that has a World-space AABB.
+ * @param[params.arc=0] {Number} Factor in range [0..1] indicating how much the
+ * {{#crossLink "Camera/eye:property"}}Camera's eye{{/crossLink}} position will
+ * swing away from its {{#crossLink "Camera/eye:property"}}look{{/crossLink}} position as it flies to the target.
+ * @param [params.component] {Number|String|Component} ID or instance of a component to fly to.
+ * @param [params.aabb] {*} World-space axis-aligned bounding box (AABB) target to fly to.
+ * @param [params.eye] {Float32Array} Position to fly the eye position to.
+ * @param [params.look] {Float32Array} Position to fly the look position to.
+ * @param [params.up] {Float32Array} Position to fly the up vector to.
+ * @param [params.fitFOV] {Number} How much of field-of-view, in degrees, that a target {{#crossLink "Object"}}{{/crossLink}} or its AABB should
+ * fill the canvas on arrival. Overrides {{#crossLink "CameraFlightAnimation/fitFOV:property"}}{{/crossLink}}.
+ * @param [params.fit] {Boolean} Whether to fit the target to the view volume. Overrides {{#crossLink "CameraFlightAnimation/fit:property"}}{{/crossLink}}.
+ */
+ jumpTo(params) {
+ this._jumpTo(params);
+ }
+
+ _jumpTo(params) {
+
+ if (this._flying) {
+ this.stop();
+ }
- math.lerpVec3(t, 0, 1, this._look1, this._look2, newLook);
+ const camera = this.scene.camera;
- let dist;
+ var aabb;
+ var componentId;
+ var newEye;
+ var newLook;
+ var newUp;
- if (this._trail) {
- math.subVec3(newLook, camera.look, newLookEyeVec);
+ if (params.aabb) { // Boundary3D
+ aabb = params.aabb;
- } else {
- math.subVec3(camera.eye, camera.look, newLookEyeVec);
- }
+ } else if (params.length === 6) { // AABB
+ aabb = params;
- math.normalizeVec3(newLookEyeVec);
- math.lerpVec3(t, 0, 1, this._eye1, this._eye2, newEye);
- math.subVec3(newEye, newLook, lookEyeVec);
- dist = math.lenVec3(lookEyeVec);
- math.mulVec3Scalar(newLookEyeVec, dist);
+ } else if (params.eye || params.look || params.up) { // Camera pose
+ newEye = params.eye;
+ newLook = params.look;
+ newUp = params.up;
- camera.eye = math.addVec3(newLook, newLookEyeVec, newEye);
- camera.look = newLook;
- }
+ } else { // Argument must be an instance or ID of a Component (subtype)
- this.scene.camera.ortho.scale = this._orthoScale1 + (t * (this._orthoScale2 - this._orthoScale1));
+ let component = params;
- if (stopping) {
- this.stop();
+ if (utils.isNumeric(component) || utils.isString(component)) {
+ componentId = component;
+ component = this.scene.components[componentId];
+ if (!component) {
+ this.error("Component not found: " + utils.inQuotes(componentId));
return;
}
+ }
+ aabb = component.aabb || this.scene.aabb;
+ }
- xeogl.scheduleTask(this._update, this); // Keep flying
- };
- })(),
-
- // Quadratic easing out - decelerating to zero velocity
- // http://gizma.com/easing
-
- _ease: function (t, b, c, d) {
- t /= d;
- return -c * t * (t - 2) + b;
- },
+ const poi = params.poi;
- /**
- * Stops an earlier flyTo, fires arrival callback.
- * @method stop
- */
- stop: function () {
+ if (aabb) {
- if (!this._flying) {
+ if (aabb[3] <= aabb[0] || aabb[4] <= aabb[1] || aabb[5] <= aabb[2]) { // Don't fly to an empty boundary
return;
}
- this._aabbHelper.visible = false;
-
- this._flying = false;
-
- this._time1 = null;
- this._time2 = null;
-
- const callback = this._callback;
-
- if (callback) {
+ var diag = poi ? math.getAABB3DiagPoint(aabb, poi) : math.getAABB3Diag(aabb);
- this._callback = null;
+ newLook = poi || math.getAABB3Center(aabb, newLook);
- if (this._callbackScope) {
- callback.call(this._callbackScope);
- } else {
- callback();
- }
+ if (this._trail) {
+ math.subVec3(camera.look, newLook, newLookEyeVec);
+ } else {
+ math.subVec3(camera.eye, camera.look, newLookEyeVec);
}
- this.fire("stopped", true, true);
- },
+ math.normalizeVec3(newLookEyeVec);
+ let dist;
+ const fit = (params.fit !== undefined) ? params.fit : this._fit;
- /**
- * Cancels an earlier flyTo without calling the arrival callback.
- * @method cancel
- */
- cancel: function () {
+ if (fit) {
+ dist = Math.abs((diag) / Math.tan((params.fitFOV || this._fitFOV) * math.DEGTORAD));
- if (!this._flying) {
- return;
+ } else {
+ dist = math.lenVec3(math.subVec3(camera.eye, camera.look, tempVec3));
}
- this._aabbHelper.visible = false;
+ math.mulVec3Scalar(newLookEyeVec, dist);
- this._flying = false;
+ camera.eye = math.addVec3(newLook, newLookEyeVec, tempVec3);
+ camera.look = newLook;
- this._time1 = null;
- this._time2 = null;
+ } else if (newEye || newLook || newUp) {
- if (this._callback) {
- this._callback = null;
+ if (newEye) {
+ camera.eye = newEye;
}
-
- this.fire("canceled", true, true);
- },
-
- _props: {
-
- /**
- * Flight duration, in seconds, when calling {{#crossLink "CameraFlightAnimation/flyTo:method"}}CameraFlightAnimation#flyTo(){{/crossLink}}.
- *
- * Stops any flight currently in progress.
- *
- * @property duration
- * @default 0.5
- * @type Number
- */
- duration: {
-
- set: function (value) {
- this._duration = value ? (value * 1000.0) : 500;
-
- this.stop();
- },
-
- get: function () {
- return this._duration / 1000.0;
- }
- },
-
- /**
- * When true, will ensure that this CameraFlightAnimation is flying to a boundary it will always adjust the distance between the
- * {{#crossLink "CameraFlightAnimation/camera:property"}}camera{{/crossLink}}'s {{#crossLink "Lookat/eye:property"}}eye{{/crossLink}}
- * and {{#crossLink "Lookat/look:property"}}{{/crossLink}}
- * so as to ensure that the target boundary is always filling the view volume.
- *
- * When false, the eye will remain at its current distance from the look position.
- *
- * @property fit
- * @type Boolean
- * @default true
- */
- fit: {
-
- set: function (value) {
- this._fit = value !== false;
- },
-
- get: function () {
- return this._fit;
- }
- },
-
-
- /**
- * How much of the perspective field-of-view, in degrees, that a target {{#crossLink "Object"}}{{/crossLink}} or its AABB should
- * fill the canvas when calling {{#crossLink "CameraFlightAnimation/flyTo:method"}}CameraFlightAnimation#jumpTo(){{/crossLink}} or {{#crossLink "CameraFlightAnimation/jumpTo:method"}}{{/crossLink}}.
- *
- * @property fitFOV
- * @default 45
- * @type Number
- */
- fitFOV: {
-
- set: function (value) {
- this._fitFOV = value || 45;
- },
-
- get: function () {
- return this._fitFOV;
- }
- },
-
- /**
- * When true, will cause this CameraFlightAnimation to point the {{#crossLink "CameraFlightAnimation/camera:property"}}{{/crossLink}}
- * in the direction that it is travelling.
- *
- * @property trail
- * @type Boolean
- * @default false
- */
- trail: {
-
- set: function (value) {
- this._trail = !!value;
- },
-
- get: function () {
- return this._trail;
- }
+ if (newLook) {
+ camera.look = newLook;
}
- },
+ if (newUp) {
+ camera.up = newUp;
+ }
+ }
+ }
- _destroy: function () {
+ _update() {
+ if (!this._flying) {
+ return;
+ }
+ const time = Date.now();
+ let t = (time - this._time1) / (this._time2 - this._time1);
+ const stopping = (t >= 1);
+ if (t > 1) {
+ t = 1;
+ }
+ t = this.easing ? this._ease(t, 0, 1, 1) : t;
+ const camera = this.scene.camera;
+ if (this._flyingEye || this._flyingLook) {
+ if (this._flyingEye) {
+ math.subVec3(camera.eye, camera.look, newLookEyeVec);
+ camera.eye = math.lerpVec3(t, 0, 1, this._eye1, this._eye2, newEye);
+ camera.look = math.subVec3(newEye, newLookEyeVec, newLook);
+ } else if (this._flyingLook) {
+ camera.look = math.lerpVec3(t, 0, 1, this._look1, this._look2, newLook);
+ // camera.eye = math.addVec3(newLook, newLookEyeVec, newEye);
+ camera.up = math.lerpVec3(t, 0, 1, this._up1, this._up2, newUp);
+ }
+ } else if (this._flyEyeLookUp) {
+ camera.eye = math.lerpVec3(t, 0, 1, this._eye1, this._eye2, newEye);
+ camera.look = math.lerpVec3(t, 0, 1, this._look1, this._look2, newLook);
+ camera.up = math.lerpVec3(t, 0, 1, this._up1, this._up2, newUp);
+ } else {
+ math.lerpVec3(t, 0, 1, this._look1, this._look2, newLook);
+ let dist;
+ if (this._trail) {
+ math.subVec3(newLook, camera.look, newLookEyeVec);
+ } else {
+ math.subVec3(camera.eye, camera.look, newLookEyeVec);
+ }
+ math.normalizeVec3(newLookEyeVec);
+ math.lerpVec3(t, 0, 1, this._eye1, this._eye2, newEye);
+ math.subVec3(newEye, newLook, lookEyeVec);
+ dist = math.lenVec3(lookEyeVec);
+ math.mulVec3Scalar(newLookEyeVec, dist);
+ camera.eye = math.addVec3(newLook, newLookEyeVec, newEye);
+ camera.look = newLook;
+ }
+ this.scene.camera.ortho.scale = this._orthoScale1 + (t * (this._orthoScale2 - this._orthoScale1));
+ if (stopping) {
this.stop();
+ return;
}
- });
-
-})();
+ tasks.scheduleTask(this._update, this); // Keep flying
+ }
+
+ _ease(t, b, c, d) { // Quadratic easing out - decelerating to zero velocity http://gizma.com/easing
+ t /= d;
+ return -c * t * (t - 2) + b;
+ }
+
+ /**
+ * Stops an earlier flyTo, fires arrival callback.
+ * @method stop
+ */
+ stop() {
+ if (!this._flying) {
+ return;
+ }
+ this._aabbHelper.visible = false;
+ this._flying = false;
+ this._time1 = null;
+ this._time2 = null;
+ const callback = this._callback;
+ if (callback) {
+ this._callback = null;
+ if (this._callbackScope) {
+ callback.call(this._callbackScope);
+ } else {
+ callback();
+ }
+ }
+ this.fire("stopped", true, true);
+ }
+
+ /**
+ * Cancels an earlier flyTo without calling the arrival callback.
+ * @method cancel
+ */
+ cancel() {
+ if (!this._flying) {
+ return;
+ }
+ this._aabbHelper.visible = false;
+ this._flying = false;
+ this._time1 = null;
+ this._time2 = null;
+ if (this._callback) {
+ this._callback = null;
+ }
+ this.fire("canceled", true, true);
+ }
+
+ /**
+ * Flight duration, in seconds, when calling {{#crossLink "CameraFlightAnimation/flyTo:method"}}CameraFlightAnimation#flyTo(){{/crossLink}}.
+ *
+ * Stops any flight currently in progress.
+ *
+ * @property duration
+ * @default 0.5
+ * @type Number
+ */
+ set duration(value) {
+ this._duration = value ? (value * 1000.0) : 500;
+ this.stop();
+ }
+
+ get duration() {
+ return this._duration / 1000.0;
+ }
+
+ /**
+ * When true, will ensure that this CameraFlightAnimation is flying to a boundary it will always adjust the distance between the
+ * {{#crossLink "CameraFlightAnimation/camera:property"}}camera{{/crossLink}}'s {{#crossLink "Lookat/eye:property"}}eye{{/crossLink}}
+ * and {{#crossLink "Lookat/look:property"}}{{/crossLink}}
+ * so as to ensure that the target boundary is always filling the view volume.
+ *
+ * When false, the eye will remain at its current distance from the look position.
+ *
+ * @property fit
+ * @type Boolean
+ * @default true
+ */
+ set fit(value) {
+ this._fit = value !== false;
+ }
+
+ get fit() {
+ return this._fit;
+ }
+
+
+ /**
+ * How much of the perspective field-of-view, in degrees, that a target {{#crossLink "Object"}}{{/crossLink}} or its AABB should
+ * fill the canvas when calling {{#crossLink "CameraFlightAnimation/flyTo:method"}}CameraFlightAnimation#jumpTo(){{/crossLink}} or {{#crossLink "CameraFlightAnimation/jumpTo:method"}}{{/crossLink}}.
+ *
+ * @property fitFOV
+ * @default 45
+ * @type Number
+ */
+ set fitFOV(value) {
+ this._fitFOV = value || 45;
+ }
+
+ get fitFOV() {
+ return this._fitFOV;
+ }
+
+ /**
+ * When true, will cause this CameraFlightAnimation to point the {{#crossLink "CameraFlightAnimation/camera:property"}}{{/crossLink}}
+ * in the direction that it is travelling.
+ *
+ * @property trail
+ * @type Boolean
+ * @default false
+ */
+ set trail(value) {
+ this._trail = !!value;
+ }
+
+ get trail() {
+ return this._trail;
+ }
+
+ destroy() {
+ super.destroy();
+ this._state.destroy();
+ super.destroy();
+ }
+}
+
+export {CameraFlightAnimation};
diff --git a/src/camera/camera.js b/src/camera/camera.js
index aba6fff09..d4b1f2200 100644
--- a/src/camera/camera.js
+++ b/src/camera/camera.js
@@ -189,727 +189,689 @@
@module xeogl
@submodule camera
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}}, creates this Camera within the
- default {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
+ @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.
You only need to supply an ID if you need to be able to find the Camera by ID within its parent {{#crossLink "Scene"}}Scene{{/crossLink}} later.
@param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to this Camera.
@extends Component
*/
-(function () {
+import {core} from "./../core.js";
+import {math} from '../math/math.js';
+import {Component} from '../component.js';
+import {State} from '../renderer/state.js';
+import {Perspective} from './perspective.js';
+import {Ortho} from './ortho.js';
+import {Frustum} from './frustum.js';
+import {CustomProjection} from './customProjection.js';
+
+const tempVec3 = math.vec3();
+const tempVec3b = math.vec3();
+const tempVec3c = math.vec3();
+const tempVec3d = math.vec3();
+const tempVec3e = math.vec3();
+const tempVec3f = math.vec3();
+const tempMat = math.mat4();
+const eyeLookVec = math.vec3();
+const eyeLookVecNorm = math.vec3();
+const eyeLookOffset = math.vec3();
+const offsetEye = math.vec3();
+
+const type = "xeogl.Camera";
+
+class Camera extends Component {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return type;
+ }
+
+ init(cfg) {
+
+ super.init(cfg);
+
+ this._state = new State({
+ deviceMatrix: math.mat4(),
+ hasDeviceMatrix: false, // True when deviceMatrix set to other than identity
+ matrix: math.mat4(),
+ normalMatrix: math.mat4()
+ });
+
+ this._perspective = new Perspective(this);
+ this._ortho = new Ortho(this);
+ this._frustum = new Frustum(this);
+ this._customProjection = new CustomProjection(this);
+ this._project = this._perspective;
+
+ this._eye = math.vec3([0, 0, 10.0]);
+ this._look = math.vec3([0, 0, 0]);
+ this._up = math.vec3([0, 1, 0]);
+
+ this._worldUp = math.vec3([0, 1, 0]);
+ this._worldRight = math.vec3([1, 0, 0]);
+ this._worldForward = math.vec3([0, 0, -1]);
+
+ this.deviceMatrix = cfg.deviceMatrix;
+ this.eye = cfg.eye;
+ this.look = cfg.look;
+ this.up = cfg.up;
+ this.worldAxis = cfg.worldAxis;
+ this.gimbalLock = cfg.gimbalLock;
+ this.constrainPitch = cfg.constrainPitch;
+
+ this.projection = cfg.projection;
+
+ this._perspective.on("matrix", () => {
+ if (this._projectionType === "perspective") {
+ this.fire("projMatrix", this._perspective.matrix);
+ }
+ });
+ this._ortho.on("matrix", () => {
+ if (this._projectionType === "ortho") {
+ this.fire("projMatrix", this._ortho.matrix);
+ }
+ });
+ this._frustum.on("matrix", () => {
+ if (this._projectionType === "frustum") {
+ this.fire("projMatrix", this._frustum.matrix);
+ }
+ });
+ this._customProjection.on("matrix", () => {
+ if (this._projectionType === "customProjection") {
+ this.fire("projMatrix", this._customProjection.matrix);
+ }
+ });
+ }
+
+ _update() {
+ const state = this._state;
+ // In ortho mode, build the view matrix with an eye position that's translated
+ // well back from look, so that the front clip plane doesn't unexpectedly cut
+ // the front off the view (not a problem with perspective, since objects close enough
+ // to be clipped by the front plane are usually too big to see anything of their cross-sections).
+ let eye;
+ if (this.projection === "ortho") {
+ math.subVec3(this._eye, this._look, eyeLookVec);
+ math.normalizeVec3(eyeLookVec, eyeLookVecNorm);
+ math.mulVec3Scalar(eyeLookVecNorm, 1000.0, eyeLookOffset);
+ math.addVec3(this._look, eyeLookOffset, offsetEye);
+ eye = offsetEye;
+ } else {
+ eye = this._eye;
+ }
+ if (state.hasDeviceMatrix) {
+ math.lookAtMat4v(eye, this._look, this._up, tempMat);
+ math.mulMat4(state.deviceMatrix, tempMat, state.matrix);
+ //state.matrix.set(state.deviceMatrix);
+ } else {
+ math.lookAtMat4v(eye, this._look, this._up, state.matrix);
+ }
+ math.inverseMat4(this._state.matrix, this._state.normalMatrix);
+ math.transposeMat4(this._state.normalMatrix);
+ this._renderer.imageDirty();
+ this.fire("matrix", this._state.matrix);
+ this.fire("viewMatrix", this._state.matrix);
+ }
+
+ /**
+ Rotates {{#crossLink "Camera/eye:property"}}{{/crossLink}} about {{#crossLink "Camera/look:property"}}{{/crossLink}}, around the {{#crossLink "Camera/up:property"}}{{/crossLink}} vector
+
+ @method orbitYaw
+ @param {Number} angle Angle of rotation in degrees
+ */
+ orbitYaw(angle) {
+ let lookEyeVec = math.subVec3(this._eye, this._look, tempVec3);
+ math.rotationMat4v(angle * 0.0174532925, this._gimbalLock ? this._worldUp : this._up, tempMat);
+ lookEyeVec = math.transformPoint3(tempMat, lookEyeVec, tempVec3b);
+ this.eye = math.addVec3(this._look, lookEyeVec, tempVec3c); // Set eye position as 'look' plus 'eye' vector
+ this.up = math.transformPoint3(tempMat, this._up, tempVec3d); // Rotate 'up' vector
+ }
+
+ /**
+ Rotates {{#crossLink "Camera/eye:property"}}{{/crossLink}} about {{#crossLink "Camera/look:property"}}{{/crossLink}} around the right axis (orthogonal to {{#crossLink "Camera/up:property"}}{{/crossLink}} and "look").
+
+ @method orbitPitch
+ @param {Number} angle Angle of rotation in degrees
+ */
+ orbitPitch(angle) {
+ let eye2 = math.subVec3(this._eye, this._look, tempVec3);
+ const left = math.cross3Vec3(math.normalizeVec3(eye2, tempVec3b), math.normalizeVec3(this._up, tempVec3c));
+ math.rotationMat4v(angle * 0.0174532925, left, tempMat);
+ eye2 = math.transformPoint3(tempMat, eye2, tempVec3d);
+ const up = math.transformPoint3(tempMat, this._up, tempVec3e);
+ if (this._constrainPitch) {
+ var angle = math.dotVec3(up, this._worldUp) / math.DEGTORAD;
+ if (angle < 1) {
+ return;
+ }
+ }
+ this.up = up;
+ this.eye = math.addVec3(eye2, this._look, tempVec3f);
+ }
+
+ /**
+ Rotates {{#crossLink "Camera/look:property"}}{{/crossLink}} about {{#crossLink "Camera/eye:property"}}{{/crossLink}}, around the {{#crossLink "Camera/up:property"}}{{/crossLink}} vector.
+
+ @method yaw
+ @param {Number} angle Angle of rotation in degrees
+ */
+ yaw(angle) {
+ let look2 = math.subVec3(this._look, this._eye, tempVec3);
+ math.rotationMat4v(angle * 0.0174532925, this._gimbalLock ? this._worldUp : this._up, tempMat);
+ look2 = math.transformPoint3(tempMat, look2, tempVec3b);
+ this.look = math.addVec3(look2, this._eye, tempVec3c);
+ if (this._gimbalLock) {
+ this.up = math.transformPoint3(tempMat, this._up, tempVec3d);
+ }
+ }
+
+ /**
+ Rotates {{#crossLink "Camera/look:property"}}{{/crossLink}} about {{#crossLink "Camera/eye:property"}}{{/crossLink}}, around the right axis (orthogonal to {{#crossLink "Camera/up:property"}}{{/crossLink}} and "look").
+
+ @method pitch
+ @param {Number} angle Angle of rotation in degrees
+ */
+ pitch(angle) {
+ let look2 = math.subVec3(this._look, this._eye, tempVec3);
+ const left = math.cross3Vec3(math.normalizeVec3(look2, tempVec3b), math.normalizeVec3(this._up, tempVec3c));
+ math.rotationMat4v(angle * 0.0174532925, left, tempMat);
+ const up = math.transformPoint3(tempMat, this._up, tempVec3f);
+ if (this._constrainPitch) {
+ var angle = math.dotVec3(up, this._worldUp) / math.DEGTORAD;
+ if (angle < 1) {
+ return;
+ }
+ }
+ this.up = up;
+ look2 = math.transformPoint3(tempMat, look2, tempVec3d);
+ this.look = math.addVec3(look2, this._eye, tempVec3e);
+ }
+
+ /**
+ Pans the camera along the camera's local X, Y and Z axis.
+
+ @method pan
+ @param pan The pan vector
+ */
+ pan(pan) {
+ const eye2 = math.subVec3(this._eye, this._look, tempVec3);
+ const vec = [0, 0, 0];
+ let v;
+ if (pan[0] !== 0) {
+ const left = math.cross3Vec3(math.normalizeVec3(eye2, []), math.normalizeVec3(this._up, tempVec3b));
+ v = math.mulVec3Scalar(left, pan[0]);
+ vec[0] += v[0];
+ vec[1] += v[1];
+ vec[2] += v[2];
+ }
+ if (pan[1] !== 0) {
+ v = math.mulVec3Scalar(math.normalizeVec3(this._up, tempVec3c), pan[1]);
+ vec[0] += v[0];
+ vec[1] += v[1];
+ vec[2] += v[2];
+ }
+ if (pan[2] !== 0) {
+ v = math.mulVec3Scalar(math.normalizeVec3(eye2, tempVec3d), pan[2]);
+ vec[0] += v[0];
+ vec[1] += v[1];
+ vec[2] += v[2];
+ }
+ this.eye = math.addVec3(this._eye, vec, tempVec3e);
+ this.look = math.addVec3(this._look, vec, tempVec3f);
+ }
+
+ /**
+ Increments/decrements zoom factor, ie. distance between {{#crossLink "Camera/eye:property"}}{{/crossLink}}
+ and {{#crossLink "Camera/look:property"}}{{/crossLink}}.
+
+ @method zoom
+ @param delta
+ */
+ zoom(delta) {
+ const vec = math.subVec3(this._eye, this._look, tempVec3);
+ const lenLook = Math.abs(math.lenVec3(vec, tempVec3b));
+ const newLenLook = Math.abs(lenLook + delta);
+ if (newLenLook < 0.5) {
+ return;
+ }
+ const dir = math.normalizeVec3(vec, tempVec3c);
+ this.eye = math.addVec3(this._look, math.mulVec3Scalar(dir, newLenLook), tempVec3d);
+ }
- "use strict";
- const math = xeogl.math;
+ /**
+ Position of this Camera's eye.
- const tempVec3 = math.vec3();
- const tempVec3b = math.vec3();
- const tempVec3c = math.vec3();
- const tempVec3d = math.vec3();
- const tempVec3e = math.vec3();
- const tempVec3f = math.vec3();
+ Fires an {{#crossLink "Camera/eye:event"}}{{/crossLink}} event on change.
- xeogl.Camera = xeogl.Component.extend({
+ @property eye
+ @default [0,0,10]
+ @type Float32Array
+ */
+ set eye(value) {
+ this._eye.set(value || [0, 0, 10]);
+ this._needUpdate(0); // Ensure matrix built on next "tick"
+ /**
+ Fired whenever this Camera's {{#crossLink "Camera/eye:property"}}{{/crossLink}} property changes.
- type: "xeogl.Camera",
+ @event eye
+ @param value The property's new value
+ */
+ this.fire("eye", this._eye);
+ }
- _init: function (cfg) {
+ get eye() {
+ return this._eye;
+ }
- this._state = new xeogl.renderer.State({
- deviceMatrix: math.mat4(),
- hasDeviceMatrix: false, // True when deviceMatrix set to other than identity
- matrix: math.mat4(),
- normalMatrix: math.mat4()
- });
+ /**
+ Position of this Camera's point-of-interest.
- this._perspective = new xeogl.Perspective(this);
- this._ortho = new xeogl.Ortho(this);
- this._frustum = new xeogl.Frustum(this);
- this._customProjection = new xeogl.CustomProjection(this);
- this._project = this._perspective;
-
- const self = this;
-
- this._eye = math.vec3([0, 0, 10.0]);
- this._look = math.vec3([0, 0, 0]);
- this._up = math.vec3([0, 1, 0]);
-
- this._worldUp = math.vec3([0, 1, 0]);
- this._worldRight = math.vec3([1, 0, 0]);
- this._worldForward = math.vec3([0, 0, -1]);
-
- this.deviceMatrix = cfg.deviceMatrix;
- this.eye = cfg.eye;
- this.look = cfg.look;
- this.up = cfg.up;
- this.worldAxis = cfg.worldAxis;
- this.gimbalLock = cfg.gimbalLock;
- this.constrainPitch = cfg.constrainPitch;
-
- this.projection = cfg.projection;
-
- this._perspective.on("matrix", function () {
- if (self._projectionType === "perspective") {
- self.fire("projMatrix", self._perspective.matrix);
- }
- });
- this._ortho.on("matrix", function () {
- if (self._projectionType === "ortho") {
- self.fire("projMatrix", self._ortho.matrix);
- }
- });
- this._frustum.on("matrix", function () {
- if (self._projectionType === "frustum") {
- self.fire("projMatrix", self._frustum.matrix);
- }
- });
- this._customProjection.on("matrix", function () {
- if (self._projectionType === "customProjection") {
- self.fire("projMatrix", self._customProjection.matrix);
- }
- });
- },
-
- _update: (function () {
- const eyeLookVec = math.vec3();
- const eyeLookVecNorm = math.vec3();
- const eyeLookOffset = math.vec3();
- const offsetEye = math.vec3();
- const tempMat = math.mat4();
- return function () {
- const state = this._state;
- // In ortho mode, build the view matrix with an eye position that's translated
- // well back from look, so that the front clip plane doesn't unexpectedly cut
- // the front off the view (not a problem with perspective, since objects close enough
- // to be clipped by the front plane are usually too big to see anything of their cross-sections).
- let eye;
- if (this.projection === "ortho") {
- math.subVec3(this._eye, this._look, eyeLookVec);
- math.normalizeVec3(eyeLookVec, eyeLookVecNorm);
- math.mulVec3Scalar(eyeLookVecNorm, 1000.0, eyeLookOffset);
- math.addVec3(this._look, eyeLookOffset, offsetEye);
- eye = offsetEye;
- } else {
- eye = this._eye;
- }
- if (state.hasDeviceMatrix) {
- math.lookAtMat4v(eye, this._look, this._up, tempMat);
- math.mulMat4(state.deviceMatrix, tempMat, state.matrix);
- //state.matrix.set(state.deviceMatrix);
- } else {
- math.lookAtMat4v(eye, this._look, this._up, state.matrix);
- }
- math.inverseMat4(this._state.matrix, this._state.normalMatrix);
- math.transposeMat4(this._state.normalMatrix);
- this._renderer.imageDirty();
- this.fire("matrix", this._state.matrix);
- this.fire("viewMatrix", this._state.matrix);
- };
- })(),
+ Fires a {{#crossLink "Camera/look:event"}}{{/crossLink}} event on change.
+ @property look
+ @default [0,0,0]
+ @type Float32Array
+ */
+ set look(value) {
+ this._look.set(value || [0, 0, 0]);
+ this._needUpdate(0); // Ensure matrix built on next "tick"
/**
- Rotates {{#crossLink "Camera/eye:property"}}{{/crossLink}} about {{#crossLink "Camera/look:property"}}{{/crossLink}}, around the {{#crossLink "Camera/up:property"}}{{/crossLink}} vector
+ Fired whenever this Camera's {{#crossLink "Camera/look:property"}}{{/crossLink}} property changes.
- @method orbitYaw
- @param {Number} angle Angle of rotation in degrees
+ @event look
+ @param value The property's new value
*/
- orbitYaw: (function () {
- const mat = math.mat4();
- return function (angle) {
- let lookEyeVec = math.subVec3(this._eye, this._look, tempVec3);
- math.rotationMat4v(angle * 0.0174532925, this._gimbalLock ? this._worldUp : this._up, mat);
- lookEyeVec = math.transformPoint3(mat, lookEyeVec, tempVec3b);
- this.eye = math.addVec3(this._look, lookEyeVec, tempVec3c); // Set eye position as 'look' plus 'eye' vector
- this.up = math.transformPoint3(mat, this._up, tempVec3d); // Rotate 'up' vector
- };
- })(),
+ this.fire("look", this._look);
+ }
- /**
- Rotates {{#crossLink "Camera/eye:property"}}{{/crossLink}} about {{#crossLink "Camera/look:property"}}{{/crossLink}} around the right axis (orthogonal to {{#crossLink "Camera/up:property"}}{{/crossLink}} and "look").
+ get look() {
+ return this._look;
+ }
- @method orbitPitch
- @param {Number} angle Angle of rotation in degrees
- */
- orbitPitch: (function () {
- const mat = math.mat4();
- return function (angle) {
- let eye2 = math.subVec3(this._eye, this._look, tempVec3);
- const left = math.cross3Vec3(math.normalizeVec3(eye2, tempVec3b), math.normalizeVec3(this._up, tempVec3c));
- math.rotationMat4v(angle * 0.0174532925, left, mat);
- eye2 = math.transformPoint3(mat, eye2, tempVec3d);
- const up = math.transformPoint3(mat, this._up, tempVec3e);
- if (this._constrainPitch) {
- var angle = math.dotVec3(up, this._worldUp) / math.DEGTORAD;
- if (angle < 1) {
- return;
- }
- }
- this.up = up;
- this.eye = math.addVec3(eye2, this._look, tempVec3f);
- };
- })(),
+ /**
+ Direction of this Camera's {{#crossLink "Camera/up:property"}}{{/crossLink}} vector.
+ Fires an {{#crossLink "Camera/up:event"}}{{/crossLink}} event on change.
+
+ @property up
+ @default [0,1,0]
+ @type Float32Array
+ */
+ set up(value) {
+ this._up.set(value || [0, 1, 0]);
+ this._needUpdate(0);
/**
- Rotates {{#crossLink "Camera/look:property"}}{{/crossLink}} about {{#crossLink "Camera/eye:property"}}{{/crossLink}}, around the {{#crossLink "Camera/up:property"}}{{/crossLink}} vector.
+ Fired whenever this Camera's {{#crossLink "Camera/up:property"}}{{/crossLink}} property changes.
- @method yaw
- @param {Number} angle Angle of rotation in degrees
+ @event up
+ @param value The property's new value
*/
- yaw: (function () {
- const mat = math.mat4();
- return function (angle) {
- let look2 = math.subVec3(this._look, this._eye, tempVec3);
- math.rotationMat4v(angle * 0.0174532925, this._gimbalLock ? this._worldUp : this._up, mat);
- look2 = math.transformPoint3(mat, look2, tempVec3b);
- this.look = math.addVec3(look2, this._eye, tempVec3c);
- if (this._gimbalLock) {
- this.up = math.transformPoint3(mat, this._up, tempVec3d);
- }
- };
- })(),
+ this.fire("up", this._up);
+ }
+
+ get up() {
+ return this._up;
+ }
+ /**
+ Sets an optional matrix to premultiply into this Camera's {{#crossLink "Camera/matrix:property"}}{{/crossLink}} matrix.
+
+ This is intended to be used for stereo rendering with WebVR etc.
+
+ @property deviceMatrix
+ @type {Float32Array}
+ */
+ set deviceMatrix(matrix) {
+ this._state.deviceMatrix.set(matrix || [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
+ this._state.hasDeviceMatrix = !!matrix;
+ this._needUpdate(0);
/**
- Rotates {{#crossLink "Camera/look:property"}}{{/crossLink}} about {{#crossLink "Camera/eye:property"}}{{/crossLink}}, around the right axis (orthogonal to {{#crossLink "Camera/up:property"}}{{/crossLink}} and "look").
+ Fired whenever this CustomProjection's {{#crossLink "CustomProjection/matrix:property"}}{{/crossLink}} property changes.
- @method pitch
- @param {Number} angle Angle of rotation in degrees
+ @event deviceMatrix
+ @param value The property's new value
*/
- pitch: (function () {
- const mat = math.mat4();
- return function (angle) {
- let look2 = math.subVec3(this._look, this._eye, tempVec3);
- const left = math.cross3Vec3(math.normalizeVec3(look2, tempVec3b), math.normalizeVec3(this._up, tempVec3c));
- math.rotationMat4v(angle * 0.0174532925, left, mat);
- const up = math.transformPoint3(mat, this._up, tempVec3f);
- if (this._constrainPitch) {
- var angle = math.dotVec3(up, this._worldUp) / math.DEGTORAD;
- if (angle < 1) {
- return;
- }
- }
- this.up = up;
- look2 = math.transformPoint3(mat, look2, tempVec3d);
- this.look = math.addVec3(look2, this._eye, tempVec3e);
- };
- })(),
-
+ this.fire("deviceMatrix", this._state.deviceMatrix);
+ }
+
+ get deviceMatrix() {
+ return this._state.deviceMatrix;
+ }
+
+ /**
+ Indicates the up, right and forward axis of the World coordinate system.
+
+ Has format: ````[rightX, rightY, rightZ, upX, upY, upZ, forwardX, forwardY, forwardZ]````
+
+ @property worldAxis
+ @default [1, 0, 0, 0, 1, 0, 0, 0, 1]
+ @type Float32Array
+ */
+ set worldAxis(value) {
+ value = value || [1, 0, 0, 0, 1, 0, 0, 0, 1];
+ if (!this._worldAxis) {
+ this._worldAxis = new Float32Array(value);
+ } else {
+ this._worldAxis.set(value);
+ }
+ this._worldRight[0] = this._worldAxis[0];
+ this._worldRight[1] = this._worldAxis[1];
+ this._worldRight[2] = this._worldAxis[2];
+ this._worldUp[0] = this._worldAxis[3];
+ this._worldUp[1] = this._worldAxis[4];
+ this._worldUp[2] = this._worldAxis[5];
+ this._worldForward[0] = this._worldAxis[6];
+ this._worldForward[1] = this._worldAxis[7];
+ this._worldForward[2] = this._worldAxis[8];
/**
- Pans the camera along the camera's local X, Y and Z axis.
+ * Fired whenever this Camera's {{#crossLink "Camera/worldAxis:property"}}{{/crossLink}} property changes.
+ *
+ * @event worldAxis
+ * @param value The property's new value
+ */
+ this.fire("worldAxis", this._worldAxis);
+ }
+
+ get worldAxis() {
+ return this._worldAxis;
+ }
+
+ /**
+ Direction of World-space "up".
+
+ @property worldUp
+ @default [0,1,0]
+ @type Float32Array
+ @final
+ */
+ get worldUp() {
+ return this._worldUp;
+ }
+
+ /**
+ Direction of World-space "right".
+
+ @property worldRight
+ @default [1,0,0]
+ @type Float32Array
+ @final
+ */
+ get worldRight() {
+ return this._worldRight;
+ }
+
+ /**
+ Direction of World-space "forwards".
+
+ @property worldForward
+ @default [0,0,-1]
+ @type Float32Array
+ @final
+ */
+ get worldForward() {
+ return this._worldForward;
+ }
+
+ /**
+ Whether to lock yaw rotation to pivot about the World-space "up" axis.
+
+ Fires a {{#crossLink "Camera/gimbalLock:event"}}{{/crossLink}} event on change.
+
+ @property gimbalLock
+ @default true
+ @type Boolean
+ */
+ set gimbalLock(value) {
+ this._gimbalLock = value !== false;
+ /**
+ Fired whenever this Camera's {{#crossLink "Camera/gimbalLock:property"}}{{/crossLink}} property changes.
- @method pan
- @param pan The pan vector
+ @event gimbalLock
+ @param value The property's new value
*/
- pan: function (pan) {
- const eye2 = math.subVec3(this._eye, this._look, tempVec3);
- const vec = [0, 0, 0];
- let v;
- if (pan[0] !== 0) {
- const left = math.cross3Vec3(math.normalizeVec3(eye2, []), math.normalizeVec3(this._up, tempVec3b));
- v = math.mulVec3Scalar(left, pan[0]);
- vec[0] += v[0];
- vec[1] += v[1];
- vec[2] += v[2];
- }
- if (pan[1] !== 0) {
- v = math.mulVec3Scalar(math.normalizeVec3(this._up, tempVec3c), pan[1]);
- vec[0] += v[0];
- vec[1] += v[1];
- vec[2] += v[2];
- }
- if (pan[2] !== 0) {
- v = math.mulVec3Scalar(math.normalizeVec3(eye2, tempVec3d), pan[2]);
- vec[0] += v[0];
- vec[1] += v[1];
- vec[2] += v[2];
- }
- this.eye = math.addVec3(this._eye, vec, tempVec3e);
- this.look = math.addVec3(this._look, vec, tempVec3f);
- },
+ this.fire("gimbalLock", this._gimbalLock);
+ }
+
+ get gimbalLock() {
+ return this._gimbalLock;
+ }
+ /**
+ Whether to prevent camera from being pitched upside down.
+
+ The camera is upside down when the angle
+ between {{#crossLink "Camera/up:property"}}{{/crossLink}} and {{#crossLink "Camera/worldUp:property"}}{{/crossLink}} is less than one degree.
+
+ Fires a {{#crossLink "Camera/constrainPitch:event"}}{{/crossLink}} event on change.
+
+ @property constrainPitch
+ @default false
+ @type Boolean
+ */
+ set constrainPitch(value) {
+ this._constrainPitch = !!value;
/**
- Increments/decrements zoom factor, ie. distance between {{#crossLink "Camera/eye:property"}}{{/crossLink}}
- and {{#crossLink "Camera/look:property"}}{{/crossLink}}.
+ Fired whenever this Camera's {{#crossLink "Camera/constrainPitch:property"}}{{/crossLink}} property changes.
- @method zoom
- @param delta
+ @event constrainPitch
+ @param value The property's new value
*/
- zoom: function (delta) {
- const vec = math.subVec3(this._eye, this._look, tempVec3);
- const lenLook = Math.abs(math.lenVec3(vec, tempVec3b));
- const newLenLook = Math.abs(lenLook + delta);
- if (newLenLook < 0.5) {
- return;
- }
- const dir = math.normalizeVec3(vec, tempVec3c);
- this.eye = math.addVec3(this._look, math.mulVec3Scalar(dir, newLenLook), tempVec3d);
- },
-
- _props: {
-
- /**
- Position of this Camera's eye.
-
- Fires an {{#crossLink "Camera/eye:event"}}{{/crossLink}} event on change.
-
- @property eye
- @default [0,0,10]
- @type Float32Array
- */
- eye: {
- set: function (value) {
- this._eye.set(value || [0, 0, 10]);
- this._needUpdate(0); // Ensure matrix built on next "tick"
- /**
- Fired whenever this Camera's {{#crossLink "Camera/eye:property"}}{{/crossLink}} property changes.
-
- @event eye
- @param value The property's new value
- */
- this.fire("eye", this._eye);
- },
- get: function () {
- return this._eye;
- }
- },
-
- /**
- Position of this Camera's point-of-interest.
-
- Fires a {{#crossLink "Camera/look:event"}}{{/crossLink}} event on change.
-
- @property look
- @default [0,0,0]
- @type Float32Array
- */
- look: {
- set: function (value) {
- this._look.set(value || [0, 0, 0]);
- this._needUpdate(0); // Ensure matrix built on next "tick"
- /**
- Fired whenever this Camera's {{#crossLink "Camera/look:property"}}{{/crossLink}} property changes.
-
- @event look
- @param value The property's new value
- */
- this.fire("look", this._look);
- },
- get: function () {
- return this._look;
- }
- },
-
- /**
- Direction of this Camera's {{#crossLink "Camera/up:property"}}{{/crossLink}} vector.
-
- Fires an {{#crossLink "Camera/up:event"}}{{/crossLink}} event on change.
-
- @property up
- @default [0,1,0]
- @type Float32Array
- */
- up: {
- set: function (value) {
- this._up.set(value || [0, 1, 0]);
- this._needUpdate(0);
- /**
- Fired whenever this Camera's {{#crossLink "Camera/up:property"}}{{/crossLink}} property changes.
-
- @event up
- @param value The property's new value
- */
- this.fire("up", this._up);
- },
- get: function () {
- return this._up;
- }
- },
-
- /**
- Sets an optional matrix to premultiply into this Camera's {{#crossLink "Camera/matrix:property"}}{{/crossLink}} matrix.
-
- This is intended to be used for stereo rendering with WebVR etc.
-
- @property deviceMatrix
- @type {Float32Array}
- */
- deviceMatrix: {
- set: function (matrix) {
- this._state.deviceMatrix.set(matrix || [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
- this._state.hasDeviceMatrix = !!matrix;
- this._needUpdate(0);
- /**
- Fired whenever this CustomProjection's {{#crossLink "CustomProjection/matrix:property"}}{{/crossLink}} property changes.
-
- @event deviceMatrix
- @param value The property's new value
- */
- this.fire("deviceMatrix", this._state.deviceMatrix);
- },
- get: function () {
- return this._state.deviceMatrix;
- }
- },
-
- /**
- Indicates the up, right and forward axis of the World coordinate system.
-
- Has format: ````[rightX, rightY, rightZ, upX, upY, upZ, forwardX, forwardY, forwardZ]````
-
- @property worldAxis
- @default [1, 0, 0, 0, 1, 0, 0, 0, 1]
- @type Float32Array
- */
- worldAxis: {
- set: function (value) {
- value = value || [1, 0, 0, 0, 1, 0, 0, 0, 1];
- if (!this._worldAxis) {
- this._worldAxis = new Float32Array(value);
- } else {
- this._worldAxis.set(value);
- }
- this._worldRight[0] = this._worldAxis[0];
- this._worldRight[1] = this._worldAxis[1];
- this._worldRight[2] = this._worldAxis[2];
- this._worldUp[0] = this._worldAxis[3];
- this._worldUp[1] = this._worldAxis[4];
- this._worldUp[2] = this._worldAxis[5];
- this._worldForward[0] = this._worldAxis[6];
- this._worldForward[1] = this._worldAxis[7];
- this._worldForward[2] = this._worldAxis[8];
- /**
- * Fired whenever this Camera's {{#crossLink "Camera/worldAxis:property"}}{{/crossLink}} property changes.
- *
- * @event worldAxis
- * @param value The property's new value
- */
- this.fire("worldAxis", this._worldAxis);
- },
- get: function () {
- return this._worldAxis;
- }
- },
-
- /**
- Direction of World-space "up".
-
- @property worldUp
- @default [0,1,0]
- @type Float32Array
- @final
- */
- worldUp: {
- get: function () {
- return this._worldUp;
- }
- },
-
- /**
- Direction of World-space "right".
-
- @property worldRight
- @default [1,0,0]
- @type Float32Array
- @final
- */
- worldRight: {
- get: function () {
- return this._worldRight;
- }
- },
-
- /**
- Direction of World-space "forwards".
-
- @property worldForward
- @default [0,0,-1]
- @type Float32Array
- @final
- */
- worldForward: {
- get: function () {
- return this._worldForward;
- }
- },
-
- /**
- Whether to lock yaw rotation to pivot about the World-space "up" axis.
-
- Fires a {{#crossLink "Camera/gimbalLock:event"}}{{/crossLink}} event on change.
-
- @property gimbalLock
- @default true
- @type Boolean
- */
- gimbalLock: {
- set: function (value) {
- this._gimbalLock = value !== false;
- /**
- Fired whenever this Camera's {{#crossLink "Camera/gimbalLock:property"}}{{/crossLink}} property changes.
-
- @event gimbalLock
- @param value The property's new value
- */
- this.fire("gimbalLock", this._gimbalLock);
- },
- get: function () {
- return this._gimbalLock;
- }
- },
-
- /**
- Whether to prevent camera from being pitched upside down.
-
- The camera is upside down when the angle
- between {{#crossLink "Camera/up:property"}}{{/crossLink}} and {{#crossLink "Camera/worldUp:property"}}{{/crossLink}} is less than one degree.
-
- Fires a {{#crossLink "Camera/constrainPitch:event"}}{{/crossLink}} event on change.
-
- @property constrainPitch
- @default false
- @type Boolean
- */
- constrainPitch: {
- set: function (value) {
- this._constrainPitch = !!value;
- /**
- Fired whenever this Camera's {{#crossLink "Camera/constrainPitch:property"}}{{/crossLink}} property changes.
-
- @event constrainPitch
- @param value The property's new value
- */
- this.fire("constrainPitch", this._constrainPitch);
- },
- get: function () {
- return this._constrainPitch;
- }
- },
-
-
- /**
- Distance from "look" to "eye".
- @property eyeLookDist
- @type Number
- @final
- */
- eyeLookDist: {
- get: (function () {
- const vec = new Float32Array(3);
- return function () {
- return math.lenVec3(math.subVec3(this._look, this._eye, vec));
- };
- })()
- },
-
- /**
- The Camera's viewing transformation matrix.
-
- Fires a {{#crossLink "Camera/matrix:event"}}{{/crossLink}} event on change.
-
- @property matrix
- @type {Float32Array}
- @final
- @deprecated
- */
- matrix: {
- get: function () {
- if (this._updateScheduled) {
- this._doUpdate();
- }
- return this._state.matrix;
- }
- },
-
- /**
- The Camera's viewing transformation matrix.
-
- Fires a {{#crossLink "Camera/matrix:event"}}{{/crossLink}} event on change.
-
- @property viewMatrix
- @final
- @type {Float32Array}
- */
- viewMatrix: {
- get: function () {
- if (this._updateScheduled) {
- this._doUpdate();
- }
- return this._state.matrix;
- }
- },
-
- /**
- The Camera's viewing normal transformation matrix.
-
- Fires a {{#crossLink "Camera/matrix:event"}}{{/crossLink}} event on change.
-
- @property normalMatrix
- @type {Float32Array}
- @final
- @deprecated
- */
- normalMatrix: {
- get: function () {
- if (this._updateScheduled) {
- this._doUpdate();
- }
- return this._state.normalMatrix;
- }
- },
-
- /**
- The Camera's viewing normal transformation matrix.
-
- Fires a {{#crossLink "Camera/matrix:event"}}{{/crossLink}} event on change.
-
- @property viewNormalMatrix
- @final
- @type {Float32Array}
- */
- viewNormalMatrix: {
- get: function () {
- if (this._updateScheduled) {
- this._doUpdate();
- }
- return this._state.normalMatrix;
- }
- },
-
- /**
- Camera's projection transformation projMatrix.
-
- Fires a {{#crossLink "Camera/projMatrix:event"}}{{/crossLink}} event on change.
-
- @property projMatrix
- @final
- @type {Float32Array}
- */
- projMatrix: {
- get: function () {
- return this[this.projection].matrix;
- }
- },
-
- /**
- The perspective projection transform for this Camera.
-
- This is used while {{#crossLink "Camera/projection:property"}}{{/crossLink}} equals "perspective".
-
- @property perspective
- @type Perspective
- @final
- */
- perspective: {
- get: function () {
- return this._perspective;
- }
- },
-
- /**
- The orthographic projection transform for this Camera.
-
- This is used while {{#crossLink "Camera/projection:property"}}{{/crossLink}} equals "ortho".
-
- @property ortho
- @type Ortho
- @final
- */
- ortho: {
- get: function () {
- return this._ortho;
- }
- },
-
- /**
- The frustum projection transform for this Camera.
-
- This is used while {{#crossLink "Camera/projection:property"}}{{/crossLink}} equals "frustum".
-
- @property frustum
- @type Frustum
- @final
- */
- frustum: {
- get: function () {
- return this._frustum;
- }
- },
-
- /**
- A custom projection transform, given as a 4x4 matrix.
-
- This is used while {{#crossLink "Camera/projection:property"}}{{/crossLink}} equals "customProjection".
-
- @property customProjection
- @type CustomProjection
- @final
- */
- customProjection: {
- get: function () {
- return this._customProjection;
- }
- },
-
- /**
- The active projection type.
-
- Accepted values are "perspective", "ortho", "frustum" and "customProjection".
-
- @property projection
- @default "perspective"
- @type {String}
- */
- projection: {
- set: function (value) {
- value = value || "perspective";
- if (this._projectionType === value) {
- return;
- }
- if (value === "perspective") {
- this._project = this._perspective;
- } else if (value === "ortho") {
- this._project = this._ortho;
- } else if (value === "frustum") {
- this._project = this._frustum;
- } else if (value === "customProjection") {
- this._project = this._customProjection;
- } else {
- this.error("Unsupported value for 'projection': " + value + " defaulting to 'perspective'");
- this._project = this._perspective;
- value = "perspective";
- }
- this._projectionType = value;
- this._renderer.imageDirty();
- this._update(); // Need to rebuild lookat matrix with full eye, look & up
- this.fire("dirty");
- },
- get: function () {
- return this._projectionType;
- }
- },
-
- /**
- The active projection transform for this Camera.
-
- @property project
- @type Transform
- @final
- */
- project: {
- get: function () {
- return this._project;
- }
- },
-
- view: { // Baackwards compat
- get: function () {
- return this;
- }
- }
- },
+ this.fire("constrainPitch", this._constrainPitch);
+ }
+
+ get constrainPitch() {
+ return this._constrainPitch;
+ }
+
+ /**
+ Distance from "look" to "eye".
+ @property eyeLookDist
+ @type Number
+ @final
+ */
+ get eyeLookDist() {
+ return math.lenVec3(math.subVec3(this._look, this._eye, tempVec3));
+ }
+
+ /**
+ The Camera's viewing transformation matrix.
+
+ Fires a {{#crossLink "Camera/matrix:event"}}{{/crossLink}} event on change.
+
+ @property matrix
+ @type {Float32Array}
+ @final
+ @deprecated
+ */
+ get matrix() {
+ if (this._updateScheduled) {
+ this._doUpdate();
+ }
+ return this._state.matrix;
+ }
+
+ /**
+ The Camera's viewing transformation matrix.
+
+ Fires a {{#crossLink "Camera/matrix:event"}}{{/crossLink}} event on change.
+
+ @property viewMatrix
+ @final
+ @type {Float32Array}
+ */
+ get viewMatrix() {
+ if (this._updateScheduled) {
+ this._doUpdate();
+ }
+ return this._state.matrix;
+ }
+
+
+ /**
+ The Camera's viewing normal transformation matrix.
+
+ Fires a {{#crossLink "Camera/matrix:event"}}{{/crossLink}} event on change.
+
+ @property normalMatrix
+ @type {Float32Array}
+ @final
+ @deprecated
+ */
+ get normalMatrix() {
+ if (this._updateScheduled) {
+ this._doUpdate();
+ }
+ return this._state.normalMatrix;
+ }
+
+ /**
+ The Camera's viewing normal transformation matrix.
+
+ Fires a {{#crossLink "Camera/matrix:event"}}{{/crossLink}} event on change.
+
+ @property viewNormalMatrix
+ @final
+ @type {Float32Array}
+ */
+ get viewNormalMatrix() {
+ if (this._updateScheduled) {
+ this._doUpdate();
+ }
+ return this._state.normalMatrix;
+ }
+
+ /**
+ Camera's projection transformation projMatrix.
+
+ Fires a {{#crossLink "Camera/projMatrix:event"}}{{/crossLink}} event on change.
+
+ @property projMatrix
+ @final
+ @type {Float32Array}
+ */
+ get projMatrix() {
+ return this[this.projection].matrix;
+ }
- _destroy: function () {
- this._state.destroy();
+
+ /**
+ The perspective projection transform for this Camera.
+
+ This is used while {{#crossLink "Camera/projection:property"}}{{/crossLink}} equals "perspective".
+
+ @property perspective
+ @type Perspective
+ @final
+ */
+ get perspective() {
+ return this._perspective;
+ }
+
+ /**
+ The orthographic projection transform for this Camera.
+
+ This is used while {{#crossLink "Camera/projection:property"}}{{/crossLink}} equals "ortho".
+
+ @property ortho
+ @type Ortho
+ @final
+ */
+ get ortho() {
+ return this._ortho;
+ }
+
+
+ /**
+ The frustum projection transform for this Camera.
+
+ This is used while {{#crossLink "Camera/projection:property"}}{{/crossLink}} equals "frustum".
+
+ @property frustum
+ @type Frustum
+ @final
+ */
+ get frustum() {
+ return this._frustum;
+ }
+
+ /**
+ A custom projection transform, given as a 4x4 matrix.
+
+ This is used while {{#crossLink "Camera/projection:property"}}{{/crossLink}} equals "customProjection".
+
+ @property customProjection
+ @type CustomProjection
+ @final
+ */
+ get customProjection() {
+ return this._customProjection;
+ }
+
+ /**
+ The active projection type.
+
+ Accepted values are "perspective", "ortho", "frustum" and "customProjection".
+
+ @property projection
+ @default "perspective"
+ @type {String}
+ */
+ set projection(value) {
+ value = value || "perspective";
+ if (this._projectionType === value) {
+ return;
+ }
+ if (value === "perspective") {
+ this._project = this._perspective;
+ } else if (value === "ortho") {
+ this._project = this._ortho;
+ } else if (value === "frustum") {
+ this._project = this._frustum;
+ } else if (value === "customProjection") {
+ this._project = this._customProjection;
+ } else {
+ this.error("Unsupported value for 'projection': " + value + " defaulting to 'perspective'");
+ this._project = this._perspective;
+ value = "perspective";
}
- });
-})();
+ this._projectionType = value;
+ this._renderer.imageDirty();
+ this._update(); // Need to rebuild lookat matrix with full eye, look & up
+ this.fire("dirty");
+ }
+
+ get projection() {
+ return this._projectionType;
+ }
+
+ /**
+ The active projection transform for this Camera.
+
+ @property project
+ @type Transform
+ @final
+ */
+ get project() {
+ return this._project;
+ }
+
+ get view() {
+ return this;
+ }
+
+ destroy() {
+ super.destroy();
+ this._state.destroy();
+ }
+}
+
+export{Camera};
\ No newline at end of file
diff --git a/src/camera/customProjection.js b/src/camera/customProjection.js
index efbb2a0af..f413b7c57 100755
--- a/src/camera/customProjection.js
+++ b/src/camera/customProjection.js
@@ -18,59 +18,73 @@
@module xeogl
@submodule camera
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}}, creates this CustomProjection within the
- default {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
+ @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 scene, generated automatically when omitted.
@param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to this CustomProjection.
@param [cfg.matrix=] {Float32Array} 4x4 transform matrix.
@extends Component
*/
-(function () {
-
- "use strict";
-
- xeogl.CustomProjection = xeogl.Component.extend({
-
- type: "xeogl.CustomProjection",
-
- _init: function (cfg) {
- this._state = new xeogl.renderer.State({
- matrix: xeogl.math.mat4()
- });
- this.matrix = cfg.matrix;
- },
-
- _props: {
-
- /**
- The CustomProjection's projection transform matrix.
-
- Fires a {{#crossLink "CustomProjection/matrix:event"}}{{/crossLink}} event on change.
-
- @property matrix
- @default [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
- @type {Float32Array}
- */
- matrix: {
- set: function (matrix) {
- this._state.matrix.set(matrix || [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
- /**
- Fired whenever this CustomProjection's {{#crossLink "CustomProjection/matrix:property"}}{{/crossLink}} property changes.
-
- @event matrix
- @param value The property's new value
- */
- this.fire("far", this._state.matrix);
- },
- get: function () {
- return this._state.matrix;
- }
- }
- },
-
- _destroy: function () {
- this._state.destroy();
- }
- });
-})();
+import {math} from '../math/math.js';
+import {Component} from '../component.js';
+import {State} from '../renderer/state.js';
+
+const type = "xeogl.CustomProjection";
+
+class CustomProjection extends Component {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return type;
+ }
+
+ init(cfg) {
+ super.init(cfg);
+ this._state = new State({
+ matrix: math.mat4()
+ });
+ this.matrix = cfg.matrix;
+ }
+
+
+ /**
+ The CustomProjection's projection transform matrix.
+
+ Fires a {{#crossLink "CustomProjection/matrix:event"}}{{/crossLink}} event on change.
+
+ @property matrix
+ @default [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
+ @type {Float32Array}
+ */
+ set matrix(matrix) {
+
+ this._state.matrix.set(matrix || [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
+
+ /**
+ Fired whenever this CustomProjection's {{#crossLink "CustomProjection/matrix:property"}}{{/crossLink}} property changes.
+
+ @event matrix
+ @param value The property's new value
+ */
+ this.fire("far", this._state.matrix);
+ }
+
+ get matrix() {
+ return this._state.matrix;
+ }
+
+ destroy() {
+ super.destroy();
+ this._state.destroy();
+ }
+}
+
+export{CustomProjection};
diff --git a/src/camera/frustum.js b/src/camera/frustum.js
index 354f51ea8..ab98efccc 100755
--- a/src/camera/frustum.js
+++ b/src/camera/frustum.js
@@ -22,8 +22,7 @@
@module xeogl
@submodule camera
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}}, creates this Frustum within the
- default {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
+ @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 scene, generated automatically when omitted.
@param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to this Frustum.
@@ -35,223 +34,231 @@
@param [cfg.far=1000] {Number} Position of the Frustum's far plane on the positive View-space Z-axis.
@extends Component
*/
-(function () {
-
- "use strict";
-
- xeogl.Frustum = xeogl.Component.extend({
-
- type: "xeogl.Frustum",
-
- _init: function (cfg) {
-
- this._state = new xeogl.renderer.State({
- matrix: xeogl.math.mat4()
- });
-
- this._left = -1.0;
- this._right = 1.0;
- this._bottom = -1.0;
- this._top = 1.0;
- this._near = 0.1;
- this._far = 5000.0;
-
- // Set component properties
-
- this.left = cfg.left;
- this.right = cfg.right;
- this.bottom = cfg.bottom;
- this.top = cfg.top;
- this.near = cfg.near;
- this.far = cfg.far;
- },
-
- _update: function () {
- xeogl.math.frustumMat4(this._left, this._right, this._bottom, this._top, this._near, this._far, this._state.matrix);
- this._renderer.imageDirty();
- this.fire("matrix", this._state.matrix);
- },
-
- _props: {
-
- /**
- Position of this Frustum's left plane on the View-space X-axis.
-
- Fires a {{#crossLink "Frustum/left:event"}}{{/crossLink}} event on change.
-
- @property left
- @default -1.0
- @type Number
- */
- left: {
- set: function (value) {
- this._left = (value !== undefined && value !== null) ? value : -1.0;
- this._needUpdate();
- /**
- Fired whenever this Frustum's {{#crossLink "Frustum/left:property"}}{{/crossLink}} property changes.
-
- @event left
- @param value The property's new value
- */
- this.fire("left", this._left);
- },
- get: function () {
- return this._left;
- }
- },
-
- /**
- Position of this Frustum's right plane on the View-space X-axis.
-
- Fires a {{#crossLink "Frustum/right:event"}}{{/crossLink}} event on change.
-
- @property right
- @default 1.0
- @type Number
- */
- right: {
- set: function (value) {
- this._right = (value !== undefined && value !== null) ? value : 1.0;
- this._needUpdate();
- /**
- Fired whenever this Frustum's {{#crossLink "Frustum/right:property"}}{{/crossLink}} property changes.
-
- @event right
- @param value The property's new value
- */
- this.fire("right", this._right);
- },
- get: function () {
- return this._right;
- }
- },
-
- /**
- Position of this Frustum's top plane on the View-space Y-axis.
-
- Fires a {{#crossLink "Frustum/top:event"}}{{/crossLink}} event on change.
-
- @property top
- @default 1.0
- @type Number
- */
- top: {
- set: function (value) {
- this._top = (value !== undefined && value !== null) ? value : 1.0;
- this._needUpdate();
- /**
- Fired whenever this Frustum's {{#crossLink "Frustum/top:property"}}{{/crossLink}} property changes.
-
- @event top
- @param value The property's new value
- */
- this.fire("top", this._top);
- },
- get: function () {
- return this._top;
- }
- },
-
- /**
- Position of this Frustum's bottom plane on the View-space Y-axis.
-
- Fires a {{#crossLink "Frustum/bottom:event"}}{{/crossLink}} event on change.
-
- @property bottom
- @default -1.0
- @type Number
- */
- bottom: {
- set: function (value) {
- this._bottom = (value !== undefined && value !== null) ? value : -1.0;
- this._needUpdate();
- /**
- Fired whenever this Frustum's {{#crossLink "Frustum/bottom:property"}}{{/crossLink}} property changes.
-
- @event bottom
- @param value The property's new value
- */
- this.fire("bottom", this._bottom);
- },
- get: function () {
- return this._bottom;
- }
- },
-
- /**
- Position of this Frustum's near plane on the positive View-space Z-axis.
-
- Fires a {{#crossLink "Frustum/near:event"}}{{/crossLink}} event on change.
-
- @property near
- @default 0.1
- @type Number
- */
- near: {
- set: function (value) {
- this._near = (value !== undefined && value !== null) ? value : 0.1;
- this._needUpdate();
- /**
- Fired whenever this Frustum's {{#crossLink "Frustum/near:property"}}{{/crossLink}} property changes.
-
- @event near
- @param value The property's new value
- */
- this.fire("near", this._near);
- },
- get: function () {
- return this._near;
- }
- },
-
- /**
- Position of this Frustum's far plane on the positive View-space Z-axis.
-
- Fires a {{#crossLink "Frustum/far:event"}}{{/crossLink}} event on change.
-
- @property far
- @default 10000.0
- @type Number
- */
- far: {
- set: function (value) {
- this._far = (value !== undefined && value !== null) ? value : 10000.0;
- this._needUpdate();
- /**
- Fired whenever this Frustum's {{#crossLink "Frustum/far:property"}}{{/crossLink}} property changes.
-
- @event far
- @param value The property's new value
- */
- this.fire("far", this._far);
- },
-
- get: function () {
- return this._far;
- }
- },
-
- /**
- The Frustum's projection transform matrix.
-
- Fires a {{#crossLink "Frustum/matrix:event"}}{{/crossLink}} event on change.
-
- @property matrix
- @default [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
- @type {Float32Array}
- */
- matrix: {
- get: function () {
- if (this._updateScheduled) {
- this._doUpdate();
- }
- return this._state.matrix;
- }
- }
- },
-
- _destroy: function () {
- this._state.destroy();
+import {core} from "./../core.js";
+import {Component} from '../component.js';
+import {State} from '../renderer/state.js';
+import {math} from '../math/math.js';
+
+const type = "xeogl.Frustum";
+
+class Frustum extends Component {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return type;
+ }
+
+ init(cfg) {
+
+ super.init(cfg);
+
+ this._state = new State({
+ matrix: math.mat4()
+ });
+
+ this._left = -1.0;
+ this._right = 1.0;
+ this._bottom = -1.0;
+ this._top = 1.0;
+ this._near = 0.1;
+ this._far = 5000.0;
+
+ // Set component properties
+
+ this.left = cfg.left;
+ this.right = cfg.right;
+ this.bottom = cfg.bottom;
+ this.top = cfg.top;
+ this.near = cfg.near;
+ this.far = cfg.far;
+ }
+
+ _update() {
+ math.frustumMat4(this._left, this._right, this._bottom, this._top, this._near, this._far, this._state.matrix);
+ this._renderer.imageDirty();
+ this.fire("matrix", this._state.matrix);
+ }
+
+ /**
+ Position of this Frustum's left plane on the View-space X-axis.
+
+ Fires a {{#crossLink "Frustum/left:event"}}{{/crossLink}} event on change.
+
+ @property left
+ @default -1.0
+ @type Number
+ */
+
+ set left(value) {
+ this._left = (value !== undefined && value !== null) ? value : -1.0;
+ this._needUpdate();
+ /**
+ Fired whenever this Frustum's {{#crossLink "Frustum/left:property"}}{{/crossLink}} property changes.
+
+ @event left
+ @param value The property's new value
+ */
+ this.fire("left", this._left);
+ }
+
+ get left() {
+ return this._left;
+ }
+
+ /**
+ Position of this Frustum's right plane on the View-space X-axis.
+
+ Fires a {{#crossLink "Frustum/right:event"}}{{/crossLink}} event on change.
+
+ @property right
+ @default 1.0
+ @type Number
+ */
+ set right(value) {
+ this._right = (value !== undefined && value !== null) ? value : 1.0;
+ this._needUpdate();
+ /**
+ Fired whenever this Frustum's {{#crossLink "Frustum/right:property"}}{{/crossLink}} property changes.
+
+ @event right
+ @param value The property's new value
+ */
+ this.fire("right", this._right);
+ }
+
+ get right() {
+ return this._right;
+ }
+
+ /**
+ Position of this Frustum's top plane on the View-space Y-axis.
+
+ Fires a {{#crossLink "Frustum/top:event"}}{{/crossLink}} event on change.
+
+ @property top
+ @default 1.0
+ @type Number
+ */
+ set top(value) {
+ this._top = (value !== undefined && value !== null) ? value : 1.0;
+ this._needUpdate();
+ /**
+ Fired whenever this Frustum's {{#crossLink "Frustum/top:property"}}{{/crossLink}} property changes.
+
+ @event top
+ @param value The property's new value
+ */
+ this.fire("top", this._top);
+ }
+
+ get top() {
+ return this._top;
+ }
+
+ /**
+ Position of this Frustum's bottom plane on the View-space Y-axis.
+
+ Fires a {{#crossLink "Frustum/bottom:event"}}{{/crossLink}} event on change.
+
+ @property bottom
+ @default -1.0
+ @type Number
+ */
+ set bottom(value) {
+ this._bottom = (value !== undefined && value !== null) ? value : -1.0;
+ this._needUpdate();
+ /**
+ Fired whenever this Frustum's {{#crossLink "Frustum/bottom:property"}}{{/crossLink}} property changes.
+
+ @event bottom
+ @param value The property's new value
+ */
+ this.fire("bottom", this._bottom);
+ }
+
+ get bottom() {
+ return this._bottom;
+ }
+
+ /**
+ Position of this Frustum's near plane on the positive View-space Z-axis.
+
+ Fires a {{#crossLink "Frustum/near:event"}}{{/crossLink}} event on change.
+
+ @property near
+ @default 0.1
+ @type Number
+ */
+ set near(value) {
+ this._near = (value !== undefined && value !== null) ? value : 0.1;
+ this._needUpdate();
+ /**
+ Fired whenever this Frustum's {{#crossLink "Frustum/near:property"}}{{/crossLink}} property changes.
+
+ @event near
+ @param value The property's new value
+ */
+ this.fire("near", this._near);
+ }
+
+ get near() {
+ return this._near;
+ }
+
+ /**
+ Position of this Frustum's far plane on the positive View-space Z-axis.
+
+ Fires a {{#crossLink "Frustum/far:event"}}{{/crossLink}} event on change.
+
+ @property far
+ @default 10000.0
+ @type Number
+ */
+ set far(value) {
+ this._far = (value !== undefined && value !== null) ? value : 10000.0;
+ this._needUpdate();
+ /**
+ Fired whenever this Frustum's {{#crossLink "Frustum/far:property"}}{{/crossLink}} property changes.
+
+ @event far
+ @param value The property's new value
+ */
+ this.fire("far", this._far);
+ }
+
+ get far() {
+ return this._far;
+ }
+
+ /**
+ The Frustum's projection transform matrix.
+
+ Fires a {{#crossLink "Frustum/matrix:event"}}{{/crossLink}} event on change.
+
+ @property matrix
+ @default [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
+ @type {Float32Array}
+ */
+ get matrix() {
+ if (this._updateScheduled) {
+ this._doUpdate();
}
- });
-})();
+ return this._state.matrix;
+ }
+
+ destroy() {
+ super.destroy();
+ this._state.destroy();
+ super.destroy();
+ }
+}
+
+export{Frustum};
\ No newline at end of file
diff --git a/src/camera/ortho.js b/src/camera/ortho.js
index eb4d755f0..d55357954 100755
--- a/src/camera/ortho.js
+++ b/src/camera/ortho.js
@@ -23,8 +23,7 @@
@module xeogl
@submodule camera
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}}, creates this Ortho within the
- default {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
+ @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 scene, generated automatically when omitted.
@param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to this Ortho.
@@ -36,176 +35,188 @@
@author Artur-Sampaio / https://github.com/Artur-Sampaio
@extends Component
*/
-(function () {
+import {core} from "./../core.js";
+import {Component} from '../component.js';
+import {State} from '../renderer/state.js';
+import {math} from '../math/math.js';
- "use strict";
+const type = "xeogl.Ortho";
- xeogl.Ortho = xeogl.Component.extend({
+class Ortho extends Component {
- type: "xeogl.Ortho",
-
- _init: function (cfg) {
-
- this._state = new xeogl.renderer.State({
- matrix: xeogl.math.mat4()
- });
-
- this.scale = cfg.scale;
- this.near = cfg.near;
- this.far = cfg.far;
-
- this._onCanvasBoundary = this.scene.canvas.on("boundary", this._needUpdate, this);
- },
-
- _update: function () {
-
- const WIDTH_INDEX = 2;
- const HEIGHT_INDEX = 3;
-
- const scene = this.scene;
- const scale = this._scale;
- const halfSize = 0.5 * scale;
-
- const boundary = scene.viewport.boundary;
- const boundaryWidth = boundary[WIDTH_INDEX];
- const boundaryHeight = boundary[HEIGHT_INDEX];
- const aspect = boundaryWidth / boundaryHeight;
-
- let left;
- let right;
- let top;
- let bottom;
-
- if (boundaryWidth > boundaryHeight) {
- left = -halfSize;
- right = halfSize;
- top = halfSize / aspect;
- bottom = -halfSize / aspect;
-
- } else {
- left = -halfSize * aspect;
- right = halfSize * aspect;
- top = halfSize;
- bottom = -halfSize;
- }
-
- xeogl.math.orthoMat4c(left, right, bottom, top, this._near, this._far, this._state.matrix);
-
- this._renderer.imageDirty();
-
- this.fire("matrix", this._state.matrix);
- },
-
- _props: {
-
- /**
- Scale factor for this Ortho's extents on X and Y axis.
-
- Clamps to minimum value of ````0.01```.
-
- Fires a {{#crossLink "Ortho/scale:event"}}{{/crossLink}} event on change.
-
- @property scale
- @default 1.0
- @type Number
- */
- scale: {
- set: function (value) {
- if (value === undefined || value === null) {
- value = 1.0;
- }
- if (value <= 0) {
- value = 0.01;
- }
- this._scale = value;
- this._needUpdate();
- /**
- Fired whenever this Ortho's {{#crossLink "Ortho/scale:property"}}{{/crossLink}} property changes.
-
- @event scale
- @param value The property's new value
- */
- this.fire("scale", this._scale);
- },
- get: function () {
- return this._scale;
- }
- },
-
- /**
- Position of this Ortho's near plane on the positive View-space Z-axis.
-
- Fires a {{#crossLink "Ortho/near:event"}}{{/crossLink}} event on change.
-
- @property near
- @default 0.1
- @type Number
- */
- near: {
- set: function (value) {
- this._near = (value !== undefined && value !== null) ? value : 0.1;
- this._needUpdate();
- /**
- Fired whenever this Ortho's {{#crossLink "Ortho/near:property"}}{{/crossLink}} property changes.
-
- @event near
- @param value The property's new value
- */
- this.fire("near", this._near);
- },
- get: function () {
- return this._near;
- }
- },
-
- /**
- Position of this Ortho's far plane on the positive View-space Z-axis.
-
- Fires a {{#crossLink "Ortho/far:event"}}{{/crossLink}} event on change.
-
- @property far
- @default 10000.0
- @type Number
- */
- far: {
- set: function (value) {
- this._far = (value !== undefined && value !== null) ? value : 10000.0;
- this._needUpdate();
- /**
- Fired whenever this Ortho's {{#crossLink "Ortho/far:property"}}{{/crossLink}} property changes.
-
- @event far
- @param value The property's new value
- */
- this.fire("far", this._far);
- },
- get: function () {
- return this._far;
- }
- },
-
- /**
- The Ortho's projection transform matrix.
-
- Fires a {{#crossLink "Ortho/matrix:event"}}{{/crossLink}} event on change.
-
- @property matrix
- @default [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
- @type {Float32Array}
- */
- matrix: {
- get: function () {
- if (this._updateScheduled) {
- this._doUpdate();
- }
- return this._state.matrix;
- }
- }
- },
-
- _destroy: function () {
- this._state.destroy();
- this.scene.canvas.off(this._onCanvasBoundary);
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return type;
+ }
+
+ init(cfg) {
+
+ super.init(cfg);
+
+ this._state = new State({
+ matrix: math.mat4()
+ });
+
+ this.scale = cfg.scale;
+ this.near = cfg.near;
+ this.far = cfg.far;
+
+ this._onCanvasBoundary = this.scene.canvas.on("boundary", this._needUpdate, this);
+ }
+
+ _update() {
+
+ const WIDTH_INDEX = 2;
+ const HEIGHT_INDEX = 3;
+
+ const scene = this.scene;
+ const scale = this._scale;
+ const halfSize = 0.5 * scale;
+
+ const boundary = scene.viewport.boundary;
+ const boundaryWidth = boundary[WIDTH_INDEX];
+ const boundaryHeight = boundary[HEIGHT_INDEX];
+ const aspect = boundaryWidth / boundaryHeight;
+
+ let left;
+ let right;
+ let top;
+ let bottom;
+
+ if (boundaryWidth > boundaryHeight) {
+ left = -halfSize;
+ right = halfSize;
+ top = halfSize / aspect;
+ bottom = -halfSize / aspect;
+
+ } else {
+ left = -halfSize * aspect;
+ right = halfSize * aspect;
+ top = halfSize;
+ bottom = -halfSize;
}
- });
-})();
\ No newline at end of file
+
+ math.orthoMat4c(left, right, bottom, top, this._near, this._far, this._state.matrix);
+
+ this._renderer.imageDirty();
+
+ this.fire("matrix", this._state.matrix);
+ }
+
+
+ /**
+ Scale factor for this Ortho's extents on X and Y axis.
+
+ Clamps to minimum value of ````0.01```.
+
+ Fires a {{#crossLink "Ortho/scale:event"}}{{/crossLink}} event on change.
+
+ @property scale
+ @default 1.0
+ @type Number
+ */
+
+ set scale(value) {
+ if (value === undefined || value === null) {
+ value = 1.0;
+ }
+ if (value <= 0) {
+ value = 0.01;
+ }
+ this._scale = value;
+ this._needUpdate();
+ /**
+ Fired whenever this Ortho's {{#crossLink "Ortho/scale:property"}}{{/crossLink}} property changes.
+
+ @event scale
+ @param value The property's new value
+ */
+ this.fire("scale", this._scale);
+ }
+
+ get scale() {
+ return this._scale;
+ }
+
+ /**
+ Position of this Ortho's near plane on the positive View-space Z-axis.
+
+ Fires a {{#crossLink "Ortho/near:event"}}{{/crossLink}} event on change.
+
+ @property near
+ @default 0.1
+ @type Number
+ */
+ set near(value) {
+ this._near = (value !== undefined && value !== null) ? value : 0.1;
+ this._needUpdate();
+ /**
+ Fired whenever this Ortho's {{#crossLink "Ortho/near:property"}}{{/crossLink}} property changes.
+
+ @event near
+ @param value The property's new value
+ */
+ this.fire("near", this._near);
+ }
+
+ get near() {
+ return this._near;
+ }
+
+ /**
+ Position of this Ortho's far plane on the positive View-space Z-axis.
+
+ Fires a {{#crossLink "Ortho/far:event"}}{{/crossLink}} event on change.
+
+ @property far
+ @default 10000.0
+ @type Number
+ */
+ set far(value) {
+ this._far = (value !== undefined && value !== null) ? value : 10000.0;
+ this._needUpdate();
+ /**
+ Fired whenever this Ortho's {{#crossLink "Ortho/far:property"}}{{/crossLink}} property changes.
+
+ @event far
+ @param value The property's new value
+ */
+ this.fire("far", this._far);
+ }
+
+ get far() {
+ return this._far;
+ }
+
+ /**
+ The Ortho's projection transform matrix.
+
+ Fires a {{#crossLink "Ortho/matrix:event"}}{{/crossLink}} event on change.
+
+ @property matrix
+ @default [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
+ @type {Float32Array}
+ */
+ get matrix() {
+ if (this._updateScheduled) {
+ this._doUpdate();
+ }
+ return this._state.matrix;
+ }
+
+ destroy() {
+ super.destroy();
+ this._state.destroy();
+ this.scene.canvas.off(this._onCanvasBoundary);
+ }
+}
+
+export{Ortho};
\ No newline at end of file
diff --git a/src/camera/perspective.js b/src/camera/perspective.js
index d1a5ca3c0..985767244 100755
--- a/src/camera/perspective.js
+++ b/src/camera/perspective.js
@@ -17,8 +17,7 @@
@module xeogl
@submodule camera
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}}, creates this Perspective within the
- default {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
+ @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 scene, generated automatically when omitted.
@param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to this Perspective.
@@ -31,188 +30,197 @@
@author Artur-Sampaio / https://github.com/Artur-Sampaio
@extends Component
*/
-(function () {
-
- "use strict";
-
- xeogl.Perspective = xeogl.Component.extend({
-
- type: "xeogl.Perspective",
-
- _init: function (cfg) {
-
- this._state = new xeogl.renderer.State({
- matrix: xeogl.math.mat4()
- });
-
- this._dirty = false;
- this._fov = 60.0;
- this._near = 0.1;
- this._far = 10000.0;
-
- // Recompute aspect from change in canvas size
- this._canvasResized = this.scene.canvas.on("boundary", this._needUpdate, this);
-
- this.fov = cfg.fov;
- this.fovAxis = cfg.fovAxis;
- this.near = cfg.near;
- this.far = cfg.far;
- },
-
- _update: function () {
- const WIDTH_INDEX = 2;
- const HEIGHT_INDEX = 3;
- const boundary = this.scene.viewport.boundary;
- const aspect = boundary[WIDTH_INDEX] / boundary[HEIGHT_INDEX];
- let fov = this._fov;
- const fovAxis = this._fovAxis;
- if (fovAxis == "x" || (fovAxis == "min" && aspect < 1) || (fovAxis == "max" && aspect > 1)) {
- fov = fov / aspect;
- }
- fov = Math.min(fov, 120);
- xeogl.math.perspectiveMat4(fov * (Math.PI / 180.0), aspect, this._near, this._far, this._state.matrix);
- this._renderer.imageDirty();
- this.fire("matrix", this._state.matrix);
- },
-
- _props: {
-
- /**
- The field-of-view angle (FOV).
-
- Fires a {{#crossLink "Perspective/fov:event"}}{{/crossLink}} event on change.
-
- @property fov
- @default 60.0
- @type Number
- */
- fov: {
- set: function (value) {
- this._fov = (value !== undefined && value !== null) ? value : 60.0;
- this._needUpdate(0); // Ensure matrix built on next "tick"
- /**
- Fired whenever this Perspective's {{#crossLink "Perspective/fov:property"}}{{/crossLink}} property changes.
-
- @event fov
- @param value The property's new value
- */
- this.fire("fov", this._fov);
- },
- get: function () {
- return this._fov;
- }
- },
-
- /**
- The FOV axis.
-
- Options are "x", "y" or "min", to use the minimum axis.
-
- Fires a {{#crossLink "Perspective/fov:event"}}{{/crossLink}} event on change.
-
- @property fov
- @default "min"
- @type String
- */
- fovAxis: {
- set: function (value) {
- value = value || "min";
- if (this._fovAxis === value) {
- return;
- }
- if (value !== "x" && value !== "y" && value !== "min") {
- this.error("Unsupported value for 'fovAxis': " + value + " - defaulting to 'min'");
- value = "min";
- }
- this._fovAxis = value;
- this._needUpdate(0); // Ensure matrix built on next "tick"
- /**
- Fired whenever this Perspective's {{#crossLink "Perspective/fovAxis:property"}}{{/crossLink}} property changes.
-
- @event fovAxis
- @param value The property's new value
- */
- this.fire("fovAxis", this._fovAxis);
- },
- get: function () {
- return this._fovAxis;
- }
- },
-
- /**
- Position of this Perspective's near plane on the positive View-space Z-axis.
-
- Fires a {{#crossLink "Perspective/near:event"}}{{/crossLink}} event on change.
-
- @property near
- @default 0.1
- @type Number
- */
- near: {
- set: function (value) {
- this._near = (value !== undefined && value !== null) ? value : 0.1;
- this._needUpdate(0); // Ensure matrix built on next "tick"
- /**
- Fired whenever this Perspective's {{#crossLink "Perspective/near:property"}}{{/crossLink}} property changes.
- @event near
- @param value The property's new value
- */
- this.fire("near", this._near);
- },
- get: function () {
- return this._near;
- }
- },
-
- /**
- Position of this Perspective's far plane on the positive View-space Z-axis.
-
- Fires a {{#crossLink "Perspective/far:event"}}{{/crossLink}} event on change.
-
- @property far
- @default 10000.0
- @type Number
- */
- far: {
- set: function (value) {
- this._far = (value !== undefined && value !== null) ? value : 10000;
- this._needUpdate(0); // Ensure matrix built on next "tick"
- /**
- Fired whenever this Perspective's {{#crossLink "Perspective/far:property"}}{{/crossLink}} property changes.
-
- @event far
- @param value The property's new value
- */
- this.fire("far", this._far);
- },
- get: function () {
- return this._far;
- }
- },
-
- /**
- The Perspective's projection transform matrix.
-
- Fires a {{#crossLink "Perspective/matrix:event"}}{{/crossLink}} event on change.
-
- @property matrix
- @default [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
- @type {Float32Array}
- */
- matrix: {
- get: function () {
- if (this._updateScheduled) {
- this._doUpdate();
- }
- return this._state.matrix;
- }
- }
- },
-
- _destroy: function () {
- this._state.destroy();
- this.scene.canvas.off(this._canvasResized);
+import {core} from "./../core.js";
+import {math} from '../math/math.js';
+import {Component} from '../component.js';
+import {State} from '../renderer/state.js';
+
+const type = "xeogl.Perspective";
+
+class Perspective extends Component {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return type;
+ }
+
+ init(cfg) {
+
+ super.init(cfg);
+
+ this._state = State({
+ matrix: math.mat4()
+ });
+
+ this._dirty = false;
+ this._fov = 60.0;
+ this._near = 0.1;
+ this._far = 10000.0;
+
+ // Recompute aspect from change in canvas size
+ this._canvasResized = this.scene.canvas.on("boundary", this._needUpdate, this);
+
+ this.fov = cfg.fov;
+ this.fovAxis = cfg.fovAxis;
+ this.near = cfg.near;
+ this.far = cfg.far;
+ }
+
+ _update() {
+ const WIDTH_INDEX = 2;
+ const HEIGHT_INDEX = 3;
+ const boundary = this.scene.viewport.boundary;
+ const aspect = boundary[WIDTH_INDEX] / boundary[HEIGHT_INDEX];
+ let fov = this._fov;
+ const fovAxis = this._fovAxis;
+ if (fovAxis == "x" || (fovAxis == "min" && aspect < 1) || (fovAxis == "max" && aspect > 1)) {
+ fov = fov / aspect;
}
- });
-
-})();
\ No newline at end of file
+ fov = Math.min(fov, 120);
+ math.perspectiveMat4(fov * (Math.PI / 180.0), aspect, this._near, this._far, this._state.matrix);
+ this._renderer.imageDirty();
+ this.fire("matrix", this._state.matrix);
+ }
+
+ /**
+ The field-of-view angle (FOV).
+
+ Fires a {{#crossLink "Perspective/fov:event"}}{{/crossLink}} event on change.
+
+ @property fov
+ @default 60.0
+ @type Number
+ */
+ set fov(value) {
+ this._fov = (value !== undefined && value !== null) ? value : 60.0;
+ this._needUpdate(0); // Ensure matrix built on next "tick"
+ /**
+ Fired whenever this Perspective's {{#crossLink "Perspective/fov:property"}}{{/crossLink}} property changes.
+
+ @event fov
+ @param value The property's new value
+ */
+ this.fire("fov", this._fov);
+ }
+
+ get fov() {
+ return this._fov;
+ }
+
+ /**
+ The FOV axis.
+
+ Options are "x", "y" or "min", to use the minimum axis.
+
+ Fires a {{#crossLink "Perspective/fov:event"}}{{/crossLink}} event on change.
+
+ @property fovAxis
+ @default "min"
+ @type String
+ */
+ set fovAxis(value) {
+ value = value || "min";
+ if (this._fovAxis === value) {
+ return;
+ }
+ if (value !== "x" && value !== "y" && value !== "min") {
+ this.error("Unsupported value for 'fovAxis': " + value + " - defaulting to 'min'");
+ value = "min";
+ }
+ this._fovAxis = value;
+ this._needUpdate(0); // Ensure matrix built on next "tick"
+ /**
+ Fired whenever this Perspective's {{#crossLink "Perspective/fovAxis:property"}}{{/crossLink}} property changes.
+
+ @event fovAxis
+ @param value The property's new value
+ */
+ this.fire("fovAxis", this._fovAxis);
+ }
+
+ get fovAxis() {
+ return this._fovAxis;
+ }
+
+ /**
+ Position of this Perspective's near plane on the positive View-space Z-axis.
+
+ Fires a {{#crossLink "Perspective/near:event"}}{{/crossLink}} event on change.
+
+ @property near
+ @default 0.1
+ @type Number
+ */
+ set near(value) {
+ this._near = (value !== undefined && value !== null) ? value : 0.1;
+ this._needUpdate(0); // Ensure matrix built on next "tick"
+ /**
+ Fired whenever this Perspective's {{#crossLink "Perspective/near:property"}}{{/crossLink}} property changes.
+ @event near
+ @param value The property's new value
+ */
+ this.fire("near", this._near);
+ }
+
+ get near() {
+ return this._near;
+ }
+
+ /**
+ Position of this Perspective's far plane on the positive View-space Z-axis.
+
+ Fires a {{#crossLink "Perspective/far:event"}}{{/crossLink}} event on change.
+
+ @property far
+ @default 10000.0
+ @type Number
+ */
+ set far(value) {
+ this._far = (value !== undefined && value !== null) ? value : 10000;
+ this._needUpdate(0); // Ensure matrix built on next "tick"
+ /**
+ Fired whenever this Perspective's {{#crossLink "Perspective/far:property"}}{{/crossLink}} property changes.
+
+ @event far
+ @param value The property's new value
+ */
+ this.fire("far", this._far);
+ }
+
+ get far() {
+ return this._far;
+ }
+
+ /**
+ The Perspective's projection transform matrix.
+
+ Fires a {{#crossLink "Perspective/matrix:event"}}{{/crossLink}} event on change.
+
+ @property matrix
+ @default [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
+ @type {Float32Array}
+ */
+ get matrix() {
+ if (this._updateScheduled) {
+ this._doUpdate();
+ }
+ return this._state.matrix;
+ }
+
+ destroy() {
+ super.destroy();
+ this._state.destroy();
+ super.destroy();
+ this.scene.canvas.off(this._canvasResized);
+ }
+}
+
+export{Perspective};
\ No newline at end of file
diff --git a/src/canvas/canvas.js b/src/canvas/canvas.js
index 49253bd08..3ca9ee9ec 100644
--- a/src/canvas/canvas.js
+++ b/src/canvas/canvas.js
@@ -97,435 +97,421 @@
@param {Scene} scene Parent scene
@extends Component
*/
-(function () {
+import {core} from "./../core.js";
+import {utils} from '../utils.js';
+import {math} from '../math/math.js';
+import {stats} from './../stats.js';
+import {Component} from '../component.js';
+import {Spinner} from './spinner.js';
+import {WEBGL_INFO} from './../webglInfo.js';
- "use strict";
+const type = "xeogl.Canvas";
- xeogl.Canvas = xeogl.Component.extend({
+const WEBGL_CONTEXT_NAMES = [
+ "webgl",
+ "experimental-webgl",
+ "webkit-3d",
+ "moz-webgl",
+ "moz-glweb20"
+];
- type: "xeogl.Canvas",
+class Canvas extends Component {
- serializable: false,
+ /**
+ JavaScript class name for this Component.
- // Names of recognised WebGL contexts
- _WEBGL_CONTEXT_NAMES: [
- "webgl",
- "experimental-webgl",
- "webkit-3d",
- "moz-webgl",
- "moz-glweb20"
- ],
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
- _init: function (cfg) {
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return type;
+ }
- /**
- * The HTML canvas. When the {{#crossLink "Viewer"}}{{/crossLink}} was configured with the ID of an existing canvas within the DOM,
- * then this property will be that element, otherwise it will be a full-page canvas that this Canvas has
- * created by default, with a z-index of -10000.
- *
- * @property canvas
- * @type {HTMLCanvasElement}
- * @final
- */
- this.canvas = null;
-
- /**
- * The WebGL rendering context.
- *
- * @property gl
- * @type {WebGLRenderingContext}
- * @final
- */
- this.gl = null;
-
- /**
- * True when WebGL 2 support is enabled.
- *
- * @property webgl2
- * @type {Boolean}
- * @final
- */
- this.webgl2 = false; // Will set true in _initWebGL if WebGL is requested and we succeed in getting it.
-
- /**
- * Indicates whether this Canvas is transparent.
- *
- * @property transparent
- * @type {Boolean}
- * @default {false}
- * @final
- */
- this.transparent = !!cfg.transparent;
-
- /**
- * Attributes for the WebGL context
- *
- * @type {{}|*}
- */
- this.contextAttr = cfg.contextAttr || {};
- this.contextAttr.alpha = this.transparent;
-
- if (this.contextAttr.preserveDrawingBuffer === undefined || this.contextAttr.preserveDrawingBuffer === null) {
- this.contextAttr.preserveDrawingBuffer = true;
- }
-
- this.contextAttr.stencil = false;
- this.contextAttr.antialias = true;
- this.contextAttr.premultipliedAlpha = this.contextAttr.premultipliedAlpha !== false;
- this.contextAttr.antialias = this.contextAttr.antialias !== false;
-
- if (!cfg.canvas) {
-
- // Canvas not supplied, create one automatically
-
- this._createCanvas();
-
- } else {
+ init(cfg) {
- // Canvas supplied
+ super.init(cfg);
- if (xeogl._isString(cfg.canvas)) {
-
- // Canvas ID supplied - find the canvas
-
- this.canvas = document.getElementById(cfg.canvas);
+ /**
+ * The HTML canvas. When the {{#crossLink "Viewer"}}{{/crossLink}} was configured with the ID of an existing canvas within the DOM,
+ * then this property will be that element, otherwise it will be a full-page canvas that this Canvas has
+ * created by default, with a z-index of -10000.
+ *
+ * @property canvas
+ * @type {HTMLCanvasElement}
+ * @final
+ */
+ this.canvas = null;
- if (!this.canvas) {
+ /**
+ * The WebGL rendering context.
+ *
+ * @property gl
+ * @type {WebGLRenderingContext}
+ * @final
+ */
+ this.gl = null;
- // Canvas not found - create one automatically
+ /**
+ * True when WebGL 2 support is enabled.
+ *
+ * @property webgl2
+ * @type {Boolean}
+ * @final
+ */
+ this.webgl2 = false; // Will set true in _initWebGL if WebGL is requested and we succeed in getting it.
- this.error("Canvas element not found: " + xeogl._inQuotes(cfg.canvas)
- + " - creating default canvas instead.");
+ /**
+ * Indicates whether this Canvas is transparent.
+ *
+ * @property transparent
+ * @type {Boolean}
+ * @default {false}
+ * @final
+ */
+ this.transparent = !!cfg.transparent;
- this._createCanvas();
- }
+ /**
+ * Attributes for the WebGL context
+ *
+ * @type {{}|*}
+ */
+ this.contextAttr = cfg.contextAttr || {};
+ this.contextAttr.alpha = this.transparent;
- } else {
+ if (this.contextAttr.preserveDrawingBuffer === undefined || this.contextAttr.preserveDrawingBuffer === null) {
+ this.contextAttr.preserveDrawingBuffer = true;
+ }
- this.canvas = cfg.canvas;
+ this.contextAttr.stencil = false;
+ this.contextAttr.antialias = true;
+ this.contextAttr.premultipliedAlpha = this.contextAttr.premultipliedAlpha !== false;
+ this.contextAttr.antialias = this.contextAttr.antialias !== false;
+
+ if (!cfg.canvas) { // Canvas not supplied, create one automatically
+ this._createCanvas();
+ } else { // Canvas supplied
+ if (utils.isString(cfg.canvas)) { // Canvas ID supplied - find the canvas
+ this.canvas = document.getElementById(cfg.canvas);
+ if (!this.canvas) { // Canvas not found - create one automatically
+ this.error("Canvas element not found: " + utils.inQuotes(cfg.canvas) + " - creating default canvas instead.");
+ this._createCanvas();
}
+ } else {
+ this.canvas = cfg.canvas;
}
+ }
- if (!this.canvas) {
-
- this.error("Faied to create canvas");
-
- return;
- }
+ if (!this.canvas) {
+ this.error("Faied to create canvas");
+ return;
+ }
- // If the canvas uses css styles to specify the sizes make sure the basic
- // width and height attributes match or the WebGL context will use 300 x 150
+ // If the canvas uses css styles to specify the sizes make sure the basic
+ // width and height attributes match or the WebGL context will use 300 x 150
- this.canvas.width = this.canvas.clientWidth;
- this.canvas.height = this.canvas.clientHeight;
+ this.canvas.width = this.canvas.clientWidth;
+ this.canvas.height = this.canvas.clientHeight;
- /**
- * Boundary of the Canvas in absolute browser window coordinates.
- *
- * ### Usage:
- *
- * ````javascript
- * var boundary = myScene.canvas.boundary;
- *
- * var xmin = boundary[0];
- * var ymin = boundary[1];
- * var width = boundary[2];
- * var height = boundary[3];
- * ````
- *
- * @property boundary
- * @type {{Array of Number}}
- * @final
- */
- this.boundary = [
- this.canvas.offsetLeft, this.canvas.offsetTop,
- this.canvas.clientWidth, this.canvas.clientHeight
- ];
+ /**
+ * Boundary of the Canvas in absolute browser window coordinates.
+ *
+ * ### Usage:
+ *
+ * ````javascript
+ * var boundary = myScene.canvas.boundary;
+ *
+ * var xmin = boundary[0];
+ * var ymin = boundary[1];
+ * var width = boundary[2];
+ * var height = boundary[3];
+ * ````
+ *
+ * @property boundary
+ * @type {{Array of Number}}
+ * @final
+ */
+ this.boundary = [
+ this.canvas.offsetLeft, this.canvas.offsetTop,
+ this.canvas.clientWidth, this.canvas.clientHeight
+ ];
- this._createBackground();
+ this._createBackground();
- // Get WebGL context
+ // Get WebGL context
- if (cfg.simulateWebGLContextLost) {
- if (window.WebGLDebugUtils) {
- this.canvas = WebGLDebugUtils.makeLostContextSimulatingCanvas(this.canvas);
- } else {
- this.error("To simulate context loss, please include WebGLDebugUtils");
- }
+ if (cfg.simulateWebGLContextLost) {
+ if (window.WebGLDebugUtils) {
+ this.canvas = WebGLDebugUtils.makeLostContextSimulatingCanvas(this.canvas);
+ } else {
+ this.error("To simulate context loss, please include WebGLDebugUtils");
}
+ }
- this._initWebGL(cfg);
-
- // Bind context loss and recovery handlers
-
- const self = this;
+ this._initWebGL(cfg);
- this.canvas.addEventListener("webglcontextlost", this._webglcontextlostListener = function (event) {
+ // Bind context loss and recovery handlers
- console.time("webglcontextrestored");
+ const self = this;
- self.scene._webglContextLost();
+ this.canvas.addEventListener("webglcontextlost", this._webglcontextlostListener = function (event) {
+ console.time("webglcontextrestored");
+ self.scene._webglContextLost();
+ /**
+ * Fired whenever the WebGL context has been lost
+ * @event webglcontextlost
+ */
+ self.fire("webglcontextlost");
+ event.preventDefault();
+ },
+ false);
+ this.canvas.addEventListener("webglcontextrestored", this._webglcontextrestoredListener = function (event) {
+ self._initWebGL();
+ if (self.gl) {
+ self.scene._webglContextRestored(self.gl);
/**
- * Fired whenever the WebGL context has been lost
- * @event webglcontextlost
+ * Fired whenever the WebGL context has been restored again after having previously being lost
+ * @event webglContextRestored
+ * @param value The WebGL context object
*/
- self.fire("webglcontextlost");
-
-
+ self.fire("webglcontextrestored", self.gl);
event.preventDefault();
- },
- false);
-
- this.canvas.addEventListener("webglcontextrestored", this._webglcontextrestoredListener = function (event) {
-
- self._initWebGL();
-
- if (self.gl) {
-
- self.scene._webglContextRestored(self.gl);
-
- /**
- * Fired whenever the WebGL context has been restored again after having previously being lost
- * @event webglContextRestored
- * @param value The WebGL context object
- */
- self.fire("webglcontextrestored", self.gl);
- event.preventDefault();
- }
-
- console.timeEnd("webglcontextrestored");
- },
- false);
+ }
+ console.timeEnd("webglcontextrestored");
+ },
+ false);
- // Publish canvas size and position changes on each scene tick
+ // Publish canvas size and position changes on each scene tick
- let lastWindowWidth = null;
- let lastWindowHeight = null;
+ let lastWindowWidth = null;
+ let lastWindowHeight = null;
- let lastCanvasWidth = null;
- let lastCanvasHeight = null;
+ let lastCanvasWidth = null;
+ let lastCanvasHeight = null;
- let lastCanvasOffsetLeft = null;
- let lastCanvasOffsetTop = null;
+ let lastCanvasOffsetLeft = null;
+ let lastCanvasOffsetTop = null;
- let lastParent = null;
+ let lastParent = null;
- this._tick = this.scene.on("tick", function () {
+ this._tick = this.scene.on("tick", function () {
- const canvas = self.canvas;
+ const canvas = self.canvas;
- const newWindowSize = (window.innerWidth !== lastWindowWidth || window.innerHeight !== lastWindowHeight);
- const newCanvasSize = (canvas.clientWidth !== lastCanvasWidth || canvas.clientHeight !== lastCanvasHeight);
- const newCanvasPos = (canvas.offsetLeft !== lastCanvasOffsetLeft || canvas.offsetTop !== lastCanvasOffsetTop);
+ const newWindowSize = (window.innerWidth !== lastWindowWidth || window.innerHeight !== lastWindowHeight);
+ const newCanvasSize = (canvas.clientWidth !== lastCanvasWidth || canvas.clientHeight !== lastCanvasHeight);
+ const newCanvasPos = (canvas.offsetLeft !== lastCanvasOffsetLeft || canvas.offsetTop !== lastCanvasOffsetTop);
- const parent = canvas.parentElement;
- const newParent = (parent !== lastParent);
+ const parent = canvas.parentElement;
+ const newParent = (parent !== lastParent);
- if (newWindowSize || newCanvasSize || newCanvasPos || newParent) {
+ if (newWindowSize || newCanvasSize || newCanvasPos || newParent) {
- self._spinner._adjustPosition();
+ self._spinner._adjustPosition();
- if (newCanvasSize || newCanvasPos) {
+ if (newCanvasSize || newCanvasPos) {
- const newWidth = canvas.clientWidth;
- const newHeight = canvas.clientHeight;
+ const newWidth = canvas.clientWidth;
+ const newHeight = canvas.clientHeight;
- // TODO: Wasteful to re-count pixel size of each canvas on each canvas' resize
- if (newCanvasSize) {
- let countPixels = 0;
- let scene;
- for (const sceneId in xeogl.scenes) {
- if (xeogl.scenes.hasOwnProperty(sceneId)) {
- scene = xeogl.scenes[sceneId];
- countPixels += scene.canvas.canvas.clientWidth * scene.canvas.canvas.clientHeight;
- }
+ // TODO: Wasteful to re-count pixel size of each canvas on each canvas' resize
+ if (newCanvasSize) {
+ let countPixels = 0;
+ let scene;
+ for (const sceneId in core.scenes) {
+ if (core.scenes.hasOwnProperty(sceneId)) {
+ scene = core.scenes[sceneId];
+ countPixels += scene.canvas.canvas.clientWidth * scene.canvas.canvas.clientHeight;
}
- xeogl.stats.memory.pixels = countPixels;
-
- canvas.width = canvas.clientWidth;
- canvas.height = canvas.clientHeight;
}
+ stats.memory.pixels = countPixels;
- const boundary = self.boundary;
-
- boundary[0] = canvas.offsetLeft;
- boundary[1] = canvas.offsetTop;
- boundary[2] = newWidth;
- boundary[3] = newHeight;
-
- /**
- * Fired whenever this Canvas's {{#crossLink "Canvas/boundary:property"}}{{/crossLink}} property changes.
- *
- * @event boundary
- * @param value The property's new value
- */
- self.fire("boundary", boundary);
-
- lastCanvasWidth = newWidth;
- lastCanvasHeight = newHeight;
+ canvas.width = canvas.clientWidth;
+ canvas.height = canvas.clientHeight;
}
- if (newWindowSize) {
- lastWindowWidth = window.innerWidth;
- lastWindowHeight = window.innerHeight;
- }
+ const boundary = self.boundary;
- if (newCanvasPos) {
- lastCanvasOffsetLeft = canvas.offsetLeft;
- lastCanvasOffsetTop = canvas.offsetTop;
- }
+ boundary[0] = canvas.offsetLeft;
+ boundary[1] = canvas.offsetTop;
+ boundary[2] = newWidth;
+ boundary[3] = newHeight;
- lastParent = parent;
- }
- });
-
- this.canvas.oncontextmenu = function (e) {
- e.preventDefault();
- };
-
- this._spinner = new xeogl.Spinner(this.scene, {
- canvas: this.canvas
- });
-
- // Set property, see definition further down
- this.backgroundColor = cfg.backgroundColor;
- this.backgroundImage = cfg.backgroundImage;
- },
+ /**
+ * Fired whenever this Canvas's {{#crossLink "Canvas/boundary:property"}}{{/crossLink}} property changes.
+ *
+ * @event boundary
+ * @param value The property's new value
+ */
+ self.fire("boundary", boundary);
- /**
- * Creates a default canvas in the DOM.
- * @private
- */
- _createCanvas: function () {
+ lastCanvasWidth = newWidth;
+ lastCanvasHeight = newHeight;
+ }
- const canvasId = "xeogl-canvas-" + xeogl.math.createUUID();
- const body = document.getElementsByTagName("body")[0];
- const div = document.createElement('div');
+ if (newWindowSize) {
+ lastWindowWidth = window.innerWidth;
+ lastWindowHeight = window.innerHeight;
+ }
- const style = div.style;
- style.height = "100%";
- style.width = "100%";
- style.padding = "0";
- style.margin = "0";
- style.background = "rgba(0,0,0,0);";
- style.float = "left";
- style.left = "0";
- style.top = "0";
- style.position = "absolute";
- style.opacity = "1.0";
- style["z-index"] = "-10000";
+ if (newCanvasPos) {
+ lastCanvasOffsetLeft = canvas.offsetLeft;
+ lastCanvasOffsetTop = canvas.offsetTop;
+ }
- div.innerHTML += '';
+ lastParent = parent;
+ }
+ });
+
+ this.canvas.oncontextmenu = function (e) {
+ e.preventDefault();
+ };
+
+ this._spinner = new Spinner(this.scene, {
+ canvas: this.canvas
+ });
+
+ // Set property, see definition further down
+ this.backgroundColor = cfg.backgroundColor;
+ this.backgroundImage = cfg.backgroundImage;
+ }
+
+ /**
+ * Creates a default canvas in the DOM.
+ * @private
+ */
+ _createCanvas() {
+
+ const canvasId = "xeogl-canvas-" + math.createUUID();
+ const body = document.getElementsByTagName("body")[0];
+ const div = document.createElement('div');
+
+ const style = div.style;
+ style.height = "100%";
+ style.width = "100%";
+ style.padding = "0";
+ style.margin = "0";
+ style.background = "rgba(0,0,0,0);";
+ style.float = "left";
+ style.left = "0";
+ style.top = "0";
+ style.position = "absolute";
+ style.opacity = "1.0";
+ style["z-index"] = "-10000";
+
+ div.innerHTML += '';
+
+ body.appendChild(div);
+
+ this.canvas = document.getElementById(canvasId);
+ }
+
+ /**
+ * Creates a image element behind the canvas, for purpose of showing a custom background.
+ * @private
+ */
+ _createBackground() {
+
+ const div = document.createElement('div');
+ const style = div.style;
+ style.padding = "0";
+ style.margin = "0";
+ style.background = null;
+ style.backgroundImage = null;
+ style.float = "left";
+ style.left = "0";
+ style.top = "0";
+ style.width = "100%";
+ style.height = "100%";
+ style.position = "absolute";
+ style.opacity = 1;
+ style["z-index"] = "-20000";
+
+ this.canvas.parentElement.appendChild(div);
+
+ this._backgroundElement = div;
+ }
+
+ _getElementXY(e) {
+ let x = 0, y = 0;
+ while (e) {
+ x += (e.offsetLeft - e.scrollLeft);
+ y += (e.offsetTop - e.scrollTop);
+ e = e.offsetParent;
+ }
+ return {x: x, y: y};
+ }
- body.appendChild(div);
+ /**
+ * Initialises the WebGL context
+ * @private
+ */
+ _initWebGL(cfg) {
- this.canvas = document.getElementById(canvasId);
- },
+ // Default context attribute values
- /**
- * Creates a image element behind the canvas, for purpose of showing a custom background.
- * @private
- */
- _createBackground: function () {
-
- const div = document.createElement('div');
- const style = div.style;
- style.padding = "0";
- style.margin = "0";
- style.background = null;
- style.backgroundImage = null;
- style.float = "left";
- style.left = "0";
- style.top = "0";
- style.width = "100%";
- style.height = "100%";
- style.position = "absolute";
- style.opacity = 1;
- style["z-index"] = "-20000";
-
- this.canvas.parentElement.appendChild(div);
-
- this._backgroundElement = div;
- },
-
- _getElementXY: function (e) {
- let x = 0, y = 0;
- while (e) {
- x += (e.offsetLeft - e.scrollLeft);
- y += (e.offsetTop - e.scrollTop);
- e = e.offsetParent;
+ if (false && cfg.webgl2) {
+ try {
+ this.gl = this.canvas.getContext("webgl2", this.contextAttr);
+ } catch (e) { // Try with next context name
}
- return {x: x, y: y};
- },
-
- /**
- * Initialises the WebGL context
- * @private
- */
- _initWebGL: function (cfg) {
-
- // Default context attribute values
+ if (!this.gl) {
+ this.warn('Failed to get a WebGL 2 context - defaulting to WebGL 1.');
+ } else {
+ this.webgl2 = true;
+ }
+ }
- if (false && cfg.webgl2) {
+ if (!this.gl) {
+ for (let i = 0; !this.gl && i < WEBGL_CONTEXT_NAMES.length; i++) {
try {
- this.gl = this.canvas.getContext("webgl2", this.contextAttr);
+ this.gl = this.canvas.getContext(WEBGL_CONTEXT_NAMES[i], this.contextAttr);
} catch (e) { // Try with next context name
}
- if (!this.gl) {
- this.warn('Failed to get a WebGL 2 context - defaulting to WebGL 1.');
- } else {
- this.webgl2 = true;
- }
}
+ }
- if (!this.gl) {
- for (let i = 0; !this.gl && i < this._WEBGL_CONTEXT_NAMES.length; i++) {
- try {
- this.gl = this.canvas.getContext(this._WEBGL_CONTEXT_NAMES[i], this.contextAttr);
- } catch (e) { // Try with next context name
- }
- }
- }
+ if (!this.gl) {
- if (!this.gl) {
+ this.error('Failed to get a WebGL context');
- this.error('Failed to get a WebGL context');
-
- /**
- * Fired whenever the canvas failed to get a WebGL context, which probably means that WebGL
- * is either unsupported or has been disabled.
- * @event webglContextFailed
- */
- this.fire("webglContextFailed", true, true);
- }
+ /**
+ * Fired whenever the canvas failed to get a WebGL context, which probably means that WebGL
+ * is either unsupported or has been disabled.
+ * @event webglContextFailed
+ */
+ this.fire("webglContextFailed", true, true);
+ }
- if (this.gl) {
- // Setup extension (if necessary) and hints for fragment shader derivative functions
- if (this.webgl2) {
- this.gl.hint(this.gl.FRAGMENT_SHADER_DERIVATIVE_HINT, this.gl.FASTEST);
- } else if (xeogl.WEBGL_INFO.SUPPORTED_EXTENSIONS["OES_standard_derivatives"]) {
- const ext = this.gl.getExtension("OES_standard_derivatives");
- this.gl.hint(ext.FRAGMENT_SHADER_DERIVATIVE_HINT_OES, this.gl.FASTEST);
- }
+ if (this.gl) {
+ // Setup extension (if necessary) and hints for fragment shader derivative functions
+ if (this.webgl2) {
+ this.gl.hint(this.gl.FRAGMENT_SHADER_DERIVATIVE_HINT, this.gl.FASTEST);
+ } else if (WEBGL_INFO.SUPPORTED_EXTENSIONS["OES_standard_derivatives"]) {
+ const ext = this.gl.getExtension("OES_standard_derivatives");
+ this.gl.hint(ext.FRAGMENT_SHADER_DERIVATIVE_HINT_OES, this.gl.FASTEST);
}
- },
+ }
+ }
- /**
- Returns a snapshot of this Canvas as a Base64-encoded image.
+ /**
+ Returns a snapshot of this Canvas as a Base64-encoded image.
- When a callback is given, this method will capture the snapshot asynchronously, on the next animation frame,
- and return it via the callback.
+ When a callback is given, this method will capture the snapshot asynchronously, on the next animation frame,
+ and return it via the callback.
- When no callback is given, this method captures and returns the snapshot immediately. Note that is only
- possible when you have configured the Canvas's {{#crossLink "Scene"}}Scene{{/crossLink}} to preserve the
- WebGL drawing buffer, which has a performance overhead.
+ When no callback is given, this method captures and returns the snapshot immediately. Note that is only
+ possible when you have configured the Canvas's {{#crossLink "Scene"}}Scene{{/crossLink}} to preserve the
+ WebGL drawing buffer, which has a performance overhead.
- #### Usage:
+ #### Usage:
- ````javascript
- // Get snapshot asynchronously
- myScene.canvas.getSnapshot({
+ ````javascript
+ // Get snapshot asynchronously
+ myScene.canvas.getSnapshot({
width: 500, // Defaults to size of canvas
height: 500,
format: "png" // Options are "jpeg" (default), "png" and "bmp"
@@ -533,197 +519,176 @@
imageElement.src = imageDataURL;
});
- // Get snapshot synchronously, requires that Scene be
- // configured with preserveDrawingBuffer; true
- imageElement.src = myScene.canvas.getSnapshot({
+ // Get snapshot synchronously, requires that Scene be
+ // configured with preserveDrawingBuffer; true
+ imageElement.src = myScene.canvas.getSnapshot({
width: 500,
height: 500,
format: "png"
});
- ````
- @method getSnapshot
- @param {*} [params] Capture options.
- @param {Number} [params.width] Desired width of result in pixels - defaults to width of canvas.
- @param {Number} [params.height] Desired height of result in pixels - defaults to height of canvas.
- @param {String} [params.format="jpeg"] Desired format; "jpeg", "png" or "bmp".
- @param {Function} [ok] Callback to return the image data when taking a snapshot asynchronously.
- @returns {String} String-encoded image data when taking the snapshot synchronously. Returns null when the ````ok```` callback is given.
- */
- getSnapshot: function (params, ok) {
-
- if (!this.canvas) {
- this.error("Can't get snapshot - no canvas.");
- ok(null);
- return;
- }
+ ````
+ @method getSnapshot
+ @param {*} [params] Capture options.
+ @param {Number} [params.width] Desired width of result in pixels - defaults to width of canvas.
+ @param {Number} [params.height] Desired height of result in pixels - defaults to height of canvas.
+ @param {String} [params.format="jpeg"] Desired format; "jpeg", "png" or "bmp".
+ @param {Function} [ok] Callback to return the image data when taking a snapshot asynchronously.
+ @returns {String} String-encoded image data when taking the snapshot synchronously. Returns null when the ````ok```` callback is given.
+ */
+ getSnapshot(params, ok) {
+
+ if (!this.canvas) {
+ this.error("Can't get snapshot - no canvas.");
+ ok(null);
+ return;
+ }
- if (ok) { // Asynchronous
- const self = this;
- requestAnimationFrame(function () {
- self.scene.render(true); // Force-render a frame
- ok(self._getSnapshot(params));
- });
- } else {
- return this._getSnapshot(params);
- }
- },
-
- _getSnapshot: function (params) {
- params = params || {};
- const width = params.width || this.canvas.width;
- const height = params.height || this.canvas.height;
- const format = params.format || "jpeg";
- let image;
- switch (format) {
- case "jpeg":
- image = Canvas2Image.saveAsJPEG(this.canvas, false, width, height);
- break;
- case "png":
- image = Canvas2Image.saveAsPNG(this.canvas, true, width, height);
- break;
- case "bmp":
- image = Canvas2Image.saveAsBMP(this.canvas, true, width, height);
- break;
- default:
- this.error("Unsupported snapshot format: '" + format
- + "' - supported types are 'jpeg', 'bmp' and 'png' - defaulting to 'jpeg'");
- image = Canvas2Image.saveAsJPEG(this.canvas, true, width, height);
- }
- return image.src;
- },
+ if (ok) { // Asynchronous
+ const self = this;
+ requestAnimationFrame(function () {
+ self.scene.render(true); // Force-render a frame
+ ok(self._getSnapshot(params));
+ });
+ } else {
+ return this._getSnapshot(params);
+ }
+ }
+
+ _getSnapshot(params) {
+ params = params || {};
+ const width = params.width || this.canvas.width;
+ const height = params.height || this.canvas.height;
+ const format = params.format || "jpeg";
+ let image;
+ switch (format) {
+ case "jpeg":
+ image = Canvas2Image.saveAsJPEG(this.canvas, false, width, height);
+ break;
+ case "png":
+ image = Canvas2Image.saveAsPNG(this.canvas, true, width, height);
+ break;
+ case "bmp":
+ image = Canvas2Image.saveAsBMP(this.canvas, true, width, height);
+ break;
+ default:
+ this.error("Unsupported snapshot format: '" + format
+ + "' - supported types are 'jpeg', 'bmp' and 'png' - defaulting to 'jpeg'");
+ image = Canvas2Image.saveAsJPEG(this.canvas, true, width, height);
+ }
+ return image.src;
+ }
- /**
- Reads colors of pixels from the last rendered frame.
+ /**
+ Reads colors of pixels from the last rendered frame.
-
Call this method like this:
+ Call this method like this:
- ````JavaScript
+ ````JavaScript
- // Ignore transparent pixels (default is false)
- var opaqueOnly = true;
+ // Ignore transparent pixels (default is false)
+ var opaqueOnly = true;
- var colors = new Float32Array(8);
+ var colors = new Float32Array(8);
- myCanvas.readPixels([ 100, 22, 12, 33 ], colors, 2, opaqueOnly);
- ````
+ myCanvas.readPixels([ 100, 22, 12, 33 ], colors, 2, opaqueOnly);
+ ````
- Then the r,g,b components of the colors will be set to the colors at those pixels.
+ Then the r,g,b components of the colors will be set to the colors at those pixels.
- @param {Float32Array} pixels
- @param {Float32Array} colors
- @param {Number} size
- @param {Boolean} opaqueOnly
- */
- readPixels: function (pixels, colors, size, opaqueOnly) {
- return this.scene._renderer.readPixels(pixels, colors, size, opaqueOnly);
- },
+ @param {Float32Array} pixels
+ @param {Float32Array} colors
+ @param {Number} size
+ @param {Boolean} opaqueOnly
+ */
+ readPixels(pixels, colors, size, opaqueOnly) {
+ return this.scene._renderer.readPixels(pixels, colors, size, opaqueOnly);
+ }
- /**
- * Simulates lost WebGL context.
- */
- loseWebGLContext: function () {
- if (this.canvas.loseContext) {
- this.canvas.loseContext();
+ /**
+ * Simulates lost WebGL context.
+ */
+ loseWebGLContext() {
+ if (this.canvas.loseContext) {
+ this.canvas.loseContext();
+ }
+ }
+
+ /**
+ A background color for the canvas. This is overridden by {{#crossLink "Canvas/backgroundImage:property"}}{{/crossLink}}.
+
+ You can set this to a new color at any time.
+
+ @property backgroundColor
+ @type Float32Array
+ @default null
+ */
+ set backgroundColor(value) {
+ if (!value) {
+ this._backgroundColor = null;
+ } else {
+ (this._backgroundColor = this._backgroundColor || new math.vec4()).set(value || [0, 0, 0, 1]);
+ if (!this._backgroundImageSrc) {
+ const rgb = "rgb(" + Math.round(this._backgroundColor[0] * 255) + ", " + Math.round(this._backgroundColor[1] * 255) + "," + Math.round(this._backgroundColor[2] * 255) + ")";
+ this._backgroundElement.style.background = rgb;
}
- },
-
- _props: {
-
- /**
- A background color for the canvas. This is overridden by {{#crossLink "Canvas/backgroundImage:property"}}{{/crossLink}}.
-
- You can set this to a new color at any time.
-
- @property backgroundColor
- @type Float32Array
- @default null
- */
- backgroundColor: {
-
- set: function (value) {
-
- if (!value) {
-
- this._backgroundColor = null;
-
- } else {
-
- (this._backgroundColor = this._backgroundColor || new xeogl.math.vec4()).set(value || [0, 0, 0, 1]);
-
- if (!this._backgroundImageSrc) {
- const rgb = "rgb(" + Math.round(this._backgroundColor[0] * 255) + ", " + Math.round(this._backgroundColor[1] * 255) + "," + Math.round(this._backgroundColor[2] * 255) + ")";
- this._backgroundElement.style.background = rgb;
- }
- }
- },
-
- get: function () {
- return this._backgroundColor;
- }
- },
-
- /**
- URL of a background image for the canvas. This is overrided by {{#crossLink "Canvas/backgroundColor/property"}}{{/crossLink}}.
-
- You can set this to a new file path at any time.
-
- @property backgroundImage
- @type String
- */
- backgroundImage: {
-
- set: function (value) {
-
- if (!value) {
- return;
- }
-
- if (!xeogl._isString(value)) {
- this.error("Value for 'backgroundImage' should be a string");
- return;
- }
-
- if (value === this._backgroundImageSrc) { // Already loaded this image
- return;
- }
+ }
+ }
- this._backgroundElement.style.backgroundImage = "url('" + value + "')";
- this._backgroundImageSrc = value;
+ get backgroundColor() {
+ return this._backgroundColor;
+ }
- if (!this._backgroundImageSrc) {
- const rgb = "rgb(" + Math.round(this._backgroundColor[0] * 255) + ", " + Math.round(this._backgroundColor[1] * 255) + "," + Math.round(this._backgroundColor[2] * 255) + ")";
- this._backgroundElement.style.background = rgb;
- }
- },
+ /**
+ URL of a background image for the canvas. This is overrided by {{#crossLink "Canvas/backgroundColor/property"}}{{/crossLink}}.
- get: function () {
- return this._backgroundImageSrc;
- }
- },
+ You can set this to a new file path at any time.
- /**
- The busy {{#crossLink "Spinner"}}{{/crossLink}} for this Canvas.
-
- @property spinner
- @type Spinner
- @final
- */
- spinner: {
-
- get: function () {
- return this._spinner;
- }
- }
- },
-
- _destroy: function () {
- this.scene.off(this._tick);
- // Memory leak avoidance
- this.canvas.removeEventListener("webglcontextlost", this._webglcontextlostListener);
- this.canvas.removeEventListener("webglcontextrestored", this._webglcontextrestoredListener);
- this.canvas = null;
- //this.gl = null;
+ @property backgroundImage
+ @type String
+ */
+ set backgroundImage(value) {
+ if (!value) {
+ return;
}
- });
-
-})();
+ if (!utils.isString(value)) {
+ this.error("Value for 'backgroundImage' should be a string");
+ return;
+ }
+ if (value === this._backgroundImageSrc) { // Already loaded this image
+ return;
+ }
+ this._backgroundElement.style.backgroundImage = "url('" + value + "')";
+ this._backgroundImageSrc = value;
+ if (!this._backgroundImageSrc) {
+ const rgb = "rgb(" + Math.round(this._backgroundColor[0] * 255) + ", " + Math.round(this._backgroundColor[1] * 255) + "," + Math.round(this._backgroundColor[2] * 255) + ")";
+ this._backgroundElement.style.background = rgb;
+ }
+ }
+
+ get backgroundImage() {
+ return this._backgroundImageSrc;
+ }
+
+ /**
+ The busy {{#crossLink "Spinner"}}{{/crossLink}} for this Canvas.
+
+ @property spinner
+ @type Spinner
+ @final
+ */
+ get spinner() {
+ return this._spinner;
+ }
+
+ destroy() {
+ this.scene.off(this._tick);
+ // Memory leak avoidance
+ this.canvas.removeEventListener("webglcontextlost", this._webglcontextlostListener);
+ this.canvas.removeEventListener("webglcontextrestored", this._webglcontextrestoredListener);
+ this.canvas = null;
+ this.gl = null;
+ super.destroy();
+ }
+}
+
+export {Canvas};
diff --git a/src/canvas/spinner.js b/src/canvas/spinner.js
index 0f7678c82..9970d6a14 100644
--- a/src/canvas/spinner.js
+++ b/src/canvas/spinner.js
@@ -37,7 +37,7 @@
// Decrement the count; count now zero,
// so spinner becomes invisible
spinner.process--;
-````
+ ````
By default, a Spinner shows while resources are loading for components like
{{#crossLink "Texture"}}{{/crossLink}}. We can disable that like this:
@@ -52,168 +52,15 @@
@submodule canvas
@extends Component
*/
-(function () {
-
- "use strict";
-
- // Ensures lazy-injected CSS only injected once
- let spinnerCSSInjected = false;
-
- xeogl.Spinner = xeogl.Component.extend({
-
- type: "xeogl.Spinner",
-
- serializable: false,
-
- _init: function (cfg) {
-
- this._canvas = cfg.canvas;
-
- this._injectSpinnerCSS();
-
- // Create spinner elements
- const div = document.createElement('div');
- const style = div.style;
-
- style["z-index"] = "9000";
- style.position = "absolute";
-
- div.innerHTML = '\
-
\
-
\
-
\
-
\
-
\
-
\
-
\
-
\
-
\
-
\
-
\
-
\
-
';
-
- this._canvas.parentElement.appendChild(div);
- this._element = div;
-
- this._adjustPosition();
-
- this.processes = 0;
-
- this.textures = cfg.textures;
- },
-
- _props: {
-
- /**
- * Whether Spinner shows while images are loading for components like {{#crossLink "Texture"}}{{/crossLink}}.
- *
- * @property textures
- * @default true
- * @type Boolean
- */
- textures: {
-
- set: function (value) {
-
- value = value !== false;
-
- this._textures = value;
- },
-
- get: function () {
- return this._textures;
- }
- },
-
- /**
- The number of processes this Spinner represents.
+import {core} from "./../core.js";
+import {Component} from '../component.js';
- The Spinner is visible while this property is greater than zero.
+const type = "xeogl.Spinner";
- Increment this property whenever you commence some process during which you want
- the Spinner to be visible, then decrement it again when the process is complete.
+let spinnerCSSInjected = false; // Ensures lazy-injected CSS only injected once
- Clamps to zero if you attempt to set to to a negative value.
-
- Fires a {{#crossLink "Spinner/processes:event"}}{{/crossLink}} event on change.
-
- @property processes
- @default 0
- @type Number
- */
- processes: {
-
- set: function (value) {
-
- value = value || 0;
-
- if (this._processes === value) {
- return;
- }
-
- if (value < 0) {
- return;
- }
-
- const prevValue = this._processes;
-
- this._processes = value;
-
- this._element.style["visibility"] = (this._processes > 0) ? "visible" : "hidden";
-
- /**
- Fired whenever this Spinner's {{#crossLink "Spinner/visible:property"}}{{/crossLink}} property changes.
-
- @event processes
- @param value The property's new value
- */
- this.fire("processes", this._processes);
-
- if (this._processes === 0 && this._processes !== prevValue) {
-
- /**
- Fired whenever this Spinner's {{#crossLink "Spinner/visible:property"}}{{/crossLink}} property becomes zero.
-
- @event zeroProcesses
- */
- this.fire("zeroProcesses", this._processes);
- }
- },
-
- get: function () {
- return this._processes;
- }
- }
- },
-
- // (Re)positions spinner DIV over the center of the canvas
- _adjustPosition: function () {
-
- if (!this._canvas || !this._element) {
- return;
- }
-
- const canvas = this._canvas;
- const spinner = this._element;
- const spinnerStyle = spinner.style;
-
- spinnerStyle["left"] = (canvas.offsetLeft + (canvas.clientWidth * 0.5) - (spinner.clientWidth * 0.5)) + "px";
- spinnerStyle["top"] = (canvas.offsetTop + (canvas.clientHeight * 0.5) - (spinner.clientHeight * 0.5)) + "px";
- },
-
- _injectSpinnerCSS: function () {
- if (spinnerCSSInjected) {
- return;
- }
- const node = document.createElement('style');
- node.innerHTML = this._spinnerCSS;
- document.body.appendChild(node);
- spinnerCSSInjected = true;
- },
-
- _spinnerCSS: ".sk-fading-circle {\
+const spinnerCSS = ".sk-fading-circle {\
background: transparent;\
margin: 20px auto;\
width: 50px;\
@@ -344,6 +191,119 @@
@keyframes sk-circleFadeDelay {\
0%, 39%, 100% { opacity: 0; }\
40% { opacity: 1; }\
- }"
- });
-})();
+ }";
+
+class Spinner extends Component {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return type;
+ }
+
+ init(cfg) {
+ super.init(cfg);
+ this._canvas = cfg.canvas;
+ this._injectSpinnerCSS();
+ const div = document.createElement('div');
+ const style = div.style;
+ style["z-index"] = "9000";
+ style.position = "absolute";
+ div.innerHTML = '\
+
\
+
\
+
\
+
\
+
\
+
\
+
\
+
\
+
\
+
\
+
\
+
\
+
';
+ this._canvas.parentElement.appendChild(div);
+ this._element = div;
+ this._adjustPosition();
+ this.processes = 0;
+ }
+
+ /**
+ The number of processes this Spinner represents.
+
+ The Spinner is visible while this property is greater than zero.
+
+ Increment this property whenever you commence some process during which you want
+ the Spinner to be visible, then decrement it again when the process is complete.
+
+ Clamps to zero if you attempt to set to to a negative value.
+
+ Fires a {{#crossLink "Spinner/processes:event"}}{{/crossLink}} event on change.
+
+ @property processes
+ @default 0
+ @type Number
+ */
+ set processes(value) {
+ value = value || 0;
+ if (this._processes === value) {
+ return;
+ }
+ if (value < 0) {
+ return;
+ }
+ const prevValue = this._processes;
+ this._processes = value;
+ this._element.style["visibility"] = (this._processes > 0) ? "visible" : "hidden";
+ /**
+ Fired whenever this Spinner's {{#crossLink "Spinner/visible:property"}}{{/crossLink}} property changes.
+
+ @event processes
+ @param value The property's new value
+ */
+ this.fire("processes", this._processes);
+ if (this._processes === 0 && this._processes !== prevValue) {
+ /**
+ Fired whenever this Spinner's {{#crossLink "Spinner/visible:property"}}{{/crossLink}} property becomes zero.
+
+ @event zeroProcesses
+ */
+ this.fire("zeroProcesses", this._processes);
+ }
+ }
+
+ get processes() {
+ return this._processes;
+ }
+
+ _adjustPosition() { // (Re)positions spinner DIV over the center of the canvas
+ if (!this._canvas || !this._element) {
+ return;
+ }
+ const canvas = this._canvas;
+ const spinner = this._element;
+ const spinnerStyle = spinner.style;
+ spinnerStyle["left"] = (canvas.offsetLeft + (canvas.clientWidth * 0.5) - (spinner.clientWidth * 0.5)) + "px";
+ spinnerStyle["top"] = (canvas.offsetTop + (canvas.clientHeight * 0.5) - (spinner.clientHeight * 0.5)) + "px";
+ }
+
+ _injectSpinnerCSS() {
+ if (spinnerCSSInjected) {
+ return;
+ }
+ const node = document.createElement('style');
+ node.innerHTML = spinnerCSS;
+ document.body.appendChild(node);
+ spinnerCSSInjected = true;
+ }
+}
+
+export{Spinner};
diff --git a/src/clipping/clip.js b/src/clipping/clip.js
index abd9c13d4..835c9e4fd 100644
--- a/src/clipping/clip.js
+++ b/src/clipping/clip.js
@@ -63,8 +63,7 @@
@module xeogl
@submodule clipping
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}} - creates this Clip in the
- default {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
+ @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] {*} Clip configuration
@param [cfg.id] {String} Optional ID, unique among all components in the parent {{#crossLink "Scene"}}Scene{{/crossLink}}, generated automatically when omitted.
You only need to supply an ID if you need to be able to find the Clip by ID within the {{#crossLink "Scene"}}Scene{{/crossLink}}.
@@ -74,109 +73,120 @@
@param [cfg.dir=[0,0 -1]] {Array of Number} Vector perpendicular to the plane surface, indicating its orientation.
@extends Component
*/
-(function () {
-
- "use strict";
-
- xeogl.Clip = xeogl.Component.extend({
-
- type: "xeogl.Clip",
-
- _init: function (cfg) {
-
- this._state = new xeogl.renderer.State({
- active: true,
- pos: new Float32Array(3),
- dir: new Float32Array(3)
- });
-
- this.active = cfg.active;
- this.pos = cfg.pos;
- this.dir = cfg.dir;
-
- this.scene._clipCreated(this);
- },
-
- _props: {
-
- /**
- Indicates whether this Clip is active or not.
-
- @property active
- @default true
- @type Boolean
- */
- active: {
- set: function (value) {
- this._state.active = value !== false;
- this._renderer.imageDirty();
- /**
- Fired whenever this Clip's {{#crossLink "Clip/active:property"}}{{/crossLink}} property changes.
-
- @event active
- @param value {Boolean} The property's new value
- */
- this.fire("active", this._state.active);
- },
- get: function () {
- return this._state.active;
- }
- },
-
- /**
- The World-space position of this Clip's plane.
-
- @property pos
- @default [0, 0, 0]
- @type Float32Array
- */
- pos: {
- set: function (value) {
- this._state.pos.set(value || [0, 0, 0]);
- this._renderer.imageDirty();
- /**
- Fired whenever this Clip's {{#crossLink "Clip/pos:property"}}{{/crossLink}} property changes.
-
- @event pos
- @param value Float32Array The property's new value
- */
- this.fire("pos", this._state.pos);
- },
- get: function () {
- return this._state.pos;
- }
- },
-
- /**
- Vector indicating the orientation of this Clip plane.
-
- The vector originates at {{#crossLink "Clip/pos:property"}}{{/crossLink}}. Elements on the
- same side of the vector are clipped.
-
- @property dir
- @default [0, 0, -1]
- @type Float32Array
- */
- dir: {
- set: function (value) {
- this._state.dir.set(value || [0, 0, -1]);
- this._renderer.imageDirty();
- /**
- Fired whenever this Clip's {{#crossLink "Clip/dir:property"}}{{/crossLink}} property changes.
-
- @event dir
- @param value {Float32Array} The property's new value
- */
- this.fire("dir", this._state.dir);
- },
- get: function () {
- return this._state.dir;
- }
- }
- },
-
- _destroy: function () {
- this.scene._clipDestroyed(this);
- }
- });
-})();
+import {core} from "./../core.js";
+import {Component} from '../component.js';
+import {State} from '../renderer/state.js';
+
+const type = "xeogl.Clip";
+
+class Clip extends Component {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return type;
+ }
+
+ init(cfg) {
+
+ super.init(cfg);
+
+ this._state = State({
+ active: true,
+ pos: new Float32Array(3),
+ dir: new Float32Array(3)
+ });
+
+ this.active = cfg.active;
+ this.pos = cfg.pos;
+ this.dir = cfg.dir;
+
+ this.scene._clipCreated(this);
+ }
+
+ /**
+ Indicates whether this Clip is active or not.
+
+ @property active
+ @default true
+ @type Boolean
+ */
+ set active(value) {
+ this._state.active = value !== false;
+ this._renderer.imageDirty();
+ /**
+ Fired whenever this Clip's {{#crossLink "Clip/active:property"}}{{/crossLink}} property changes.
+
+ @event active
+ @param value {Boolean} The property's new value
+ */
+ this.fire("active", this._state.active);
+ }
+
+ get active() {
+ return this._state.active;
+ }
+
+ /**
+ The World-space position of this Clip's plane.
+
+ @property pos
+ @default [0, 0, 0]
+ @type Float32Array
+ */
+ set pos(value) {
+ this._state.pos.set(value || [0, 0, 0]);
+ this._renderer.imageDirty();
+ /**
+ Fired whenever this Clip's {{#crossLink "Clip/pos:property"}}{{/crossLink}} property changes.
+
+ @event pos
+ @param value Float32Array The property's new value
+ */
+ this.fire("pos", this._state.pos);
+ }
+
+ get pos() {
+ return this._state.pos;
+ }
+
+ /**
+ Vector indicating the orientation of this Clip plane.
+
+ The vector originates at {{#crossLink "Clip/pos:property"}}{{/crossLink}}. Elements on the
+ same side of the vector are clipped.
+
+ @property dir
+ @default [0, 0, -1]
+ @type Float32Array
+ */
+ set dir(value) {
+ this._state.dir.set(value || [0, 0, -1]);
+ this._renderer.imageDirty();
+ /**
+ Fired whenever this Clip's {{#crossLink "Clip/dir:property"}}{{/crossLink}} property changes.
+
+ @event dir
+ @param value {Float32Array} The property's new value
+ */
+ this.fire("dir", this._state.dir);
+ }
+
+ get dir() {
+ return this._state.dir;
+ }
+
+ destroy() {
+ this.scene._clipDestroyed(this);
+ super.destroy();
+ }
+}
+
+export{Clip};
diff --git a/src/component.js b/src/component.js
index b42831591..c8d46c6d5 100644
--- a/src/component.js
+++ b/src/component.js
@@ -162,11 +162,15 @@
Subclassing a Component to create a new ````xeogl.ColoredTorus```` type:
````javascript
- xeogl.ColoredTorus = xeogl.Component.extend({
+ class ColoredTorus extends xeogl.Component{
- type: "xeogl.ColoredTorus",
+ static get type() {
+ return "ColoredTorus";
+ }
+
+ constructor(scene=null, cfg) { // Constructor
- _init: function (cfg) { // Constructor
+ super(scene. cfg);
this._torus = new xeogl.Mesh({
geometry: new xeogl.TorusGeometry({radius: 2, tube: .6}),
@@ -180,25 +184,21 @@
this.color = cfg.color;
},
- _props: {
-
- // The color of this ColoredTorus.
- color: {
- set: function (color) {
- this._torus.material.baseColor = color;
- },
- get: function () {
- return this._torus.material.baseColor;
- }
- }
- },
+ set color(color) {
+ this._torus.material.baseColor = color;
+ }
- _destroy: function () {
+ get color() {
+ return this._torus.material.baseColor;
+ }
+
+ destroy() {
+ super.destroy();
this._torus.geometry.destroy();
this._torus.material.destroy();
this._torus.destroy();
}
- });
+ };
````
#### Examples
@@ -211,928 +211,891 @@
@class Component
@module xeogl
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}} - creates this Component
- within the default {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
+ @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] {*} DepthBuf configuration
@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 Component.
@param [cfg.isDefault] {Boolean} Set true when this is one of xeogl's default components.
*/
-(function () {
-
- "use strict";
-
- xeogl.Component = Class.extend({
- __init: function () {
+import {core} from "./core.js";
+import {utils} from './utils.js';
+import {tasks} from './tasks.js';
+import {Map} from "./utils/map.js";
- let cfg = {};
+class Component {
- const arg1 = arguments[0];
- const arg2 = arguments[1];
+ /**
+ JavaScript class name for this Component.
- /**
- The parent {{#crossLink "Scene"}}{{/crossLink}} that contains this Component.
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
- @property scene
- @type {Scene}
- @final
- */
- this.scene = null;
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return "xeogl.Component";
+ }
- this._model = null;
+ constructor() {
- let adopter = null;
+ var cfg = {};
- if (this.type === "xeogl.Scene") {
- this.scene = this;
- if (arg1) {
- cfg = arg1;
- }
+ var arg1 = arguments[0];
+ var arg2 = arguments[1];
- } else {
- if (arg1) {
- if (arg1.type === "xeogl.Scene") {
- this.scene = arg1;
- adopter = this.scene;
- if (arg2) {
- cfg = arg2;
- }
+ var owner = null;
- } else if (arg1.isType && arg1.isType("xeogl.Component")) {
- this.scene = arg1.scene;
- adopter = arg1;
- if (arg2) {
- cfg = arg2;
- }
+ if (this.type === "xeogl.Scene") {
+ this.scene = this;
+ if (arg1) {
+ cfg = arg1;
+ }
- } else {
- // Create this component within the default xeogl Scene
- this.scene = xeogl.scene;
- adopter = this.scene;
+ } else {
+ if (arg1) {
+ if (arg1.type === "xeogl.Scene") {
+ this.scene = arg1;
+ owner = this.scene;
+ if (arg2) {
+ cfg = arg2;
+ }
- cfg = arg1;
+ } else if (arg1.isType && arg1.isType("xeogl.Component")) {
+ this.scene = arg1.scene;
+ owner = arg1;
+ if (arg2) {
+ cfg = arg2;
}
+
} else {
// Create this component within the default xeogl Scene
- this.scene = xeogl.scene;
- adopter = this.scene;
+ this.scene = core.scene;
+ owner = this.scene;
+ cfg = arg1;
}
- this._renderer = this.scene._renderer;
+ } else {
+ // Create this component within the default xeogl Scene
+ this.scene = core.scene;
+ owner = this.scene;
}
+ this._renderer = this.scene._renderer;
+ }
- /**
- Arbitrary, user-defined metadata on this component.
-
- @property metadata
- @type Object
- */
- this.meta = cfg.meta || {};
-
- /**
- Unique ID for this Component within its parent {{#crossLink "Scene"}}Scene{{/crossLink}}.
-
- @property id
- @type String
- @final
- */
- this.id = cfg.id; // Auto-generated by xeogl.Scene by default
-
- /**
- True as soon as this Component has been destroyed
-
- @property destroyed
- @type Boolean
- */
- this.destroyed = false;
-
- /// Attached components with names.
- this._attached = {}; // Protected
-
- // Attached components keyed to IDs
- this._attachments = null; // Lazy-instantiated map
-
- // Event support - lazy creating these properties because
- // they are expensive to have around if not using them
- this._subIdMap = null; // Subscription subId pool
- this._subIdEvents = null; // Subscription subIds mapped to event names
- this._eventSubs = null; // Event names mapped to subscribers
- this._events = null; // Maps names to events
- this._eventCallDepth = 0; // Helps us catch stack overflows from recursive events
-
- // Components created with #create
- this._adoptees = null; // Lazy-instantiated map
-
- const isScene = this.type === "xeogl.Scene";
+ /**
+ The parent {{#crossLink "Scene"}}{{/crossLink}} that contains this Component.
- if (this.scene && !isScene) { // HACK: Don't add scene to itself
- // Register this component on its scene
- // Assigns this component an automatic ID if not yet assigned
- this.scene._addComponent(this);
- }
+ @property scene
+ @type {Scene}
+ @final
+ */
+ this.scene = owner.scene; // Note that a xeogl.Scene has a 'scene' property set to itself
- // True when #_update will be called on next tick
- this._updateScheduled = false;
+ this._model = null;
+ this._renderer = this.scene._renderer;
- // Initialize this component
- if (this._init) {
- this._init(cfg);
- }
+ /**
+ Arbitrary, user-defined metadata on this component.
- if (adopter) {
- adopter._adopt(this);
- }
- },
+ @property metadata
+ @type Object
+ */
+ this.meta = cfg.meta || {};
/**
- JavaScript class name for this Component.
+ Unique ID for this Component within its parent {{#crossLink "Scene"}}Scene{{/crossLink}}.
- For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
-
- @property type
+ @property id
@type String
@final
*/
- type: "xeogl.Component",
+ this.id = cfg.id; // Auto-generated by xeogl.Scene by default
/**
- An array of strings that indicates the chain of super-types within this component's inheritance hierarchy.
-
- For example, if this component is a {{#crossLink "Rotate"}}{{/crossLink}}, which
- extends {{#crossLink "Transform"}}{{/crossLink}}, which in turn extends {{#crossLink "Component"}}{{/crossLink}},
- then this property will have the value:
+ True as soon as this Component has been destroyed
- ````json
- ["xeogl.Component", "xeogl.Transform"]
- ````
-
- Note that the chain is ordered downwards in the hierarchy, ie. from super-class down towards sub-class.
-
- @property superTypes
- @type {Array of String}
- @final
+ @property destroyed
+ @type Boolean
*/
- superTypes: [],
+ this.destroyed = false;
+
+ this._attached = {}; // Attached components with names.
+ this._attachments = null; // Attached components keyed to IDs - lazy-instantiated
+ this._subIdMap = null; // Subscription subId pool
+ this._subIdEvents = null; // Subscription subIds mapped to event names
+ this._eventSubs = null; // Event names mapped to subscribers
+ this._events = null; // Maps names to events
+ this._eventCallDepth = 0; // Helps us catch stack overflows from recursive events
+ this._adoptees = null; // // Components created with #create - lazy-instantiated
+
+ if (this === this.scene) { // Don't add scene to itself
+ this.scene._addComponent(this); // Assigns this component an automatic ID if not yet assigned
+ }
- _addedToModel: function (model) { // Called by xeogl.Model.add()
- this._model = model;
- },
+ this._updateScheduled = false; // True when #_update will be called on next tick
- _removedFromModel: function (model) { // Called by xeogl.Model.remove()
- this._model = null;
- },
+ this.init(cfg);
- _props: {
+ if (owner) {
+ owner._adopt(this);
+ }
+ }
- /**
- The {{#crossLink "Model"}}{{/crossLink}} which contains this Component, if any.
+ init() { // No-op
- Will be null if this Component is not in a Model.
+ }
- @property model
- @final
- @type Model
- */
- model: {
+ /**
+ An array of strings that indicates the chain of super-types within this component's inheritance hierarchy.
- get: function () {
- return this._model;
- }
- }
- },
+ For example, if this component is a {{#crossLink "Rotate"}}{{/crossLink}}, which
+ extends {{#crossLink "Transform"}}{{/crossLink}}, which in turn extends {{#crossLink "Component"}}{{/crossLink}},
+ then this property will have the value:
- /**
- Tests if this component is of the given type, or is a subclass of the given type.
+ ````json
+ ["xeogl.Component", "xeogl.Transform"]
+ ````
- The type may be given as either a string or a component constructor.
+ Note that the chain is ordered downwards in the hierarchy, ie. from super-class down towards sub-class.
- This method works by walking up the inheritance type chain, which this component provides in
- property {{#crossLink "Component/superTypes:property"}}{{/crossLink}}, returning true as soon as one of the type strings in
- the chain matches the given type, of false if none match.
+ @property superTypes
+ @type {Array of String}
+ @final
+ */
+ superTypes() {
+ return []
+ }
- #### Examples:
+ _addedToModel(model) { // Called by xeogl.Model.add()
+ this._model = model;
+ }
- ````javascript
- var myRotate = new xeogl.Rotate({ ... });
+ _removedFromModel(model) { // Called by xeogl.Model.remove()
+ this._model = null;
+ }
- myRotate.isType(xeogl.Component); // Returns true for all xeogl components
- myRotate.isType("xeogl.Component"); // Returns true for all xeogl components
- myRotate.isType(xeogl.Rotate); // Returns true
- myRotate.isType(xeogl.Transform); // Returns true
- myRotate.isType("xeogl.Transform"); // Returns true
- myRotate.isType(xeogl.Mesh); // Returns false, because xeogl.Rotate does not (even indirectly) extend xeogl.Mesh
- ````
+ /**
+ The {{#crossLink "Model"}}{{/crossLink}} which contains this Component, if any.
- @method isType
- @param {String|Function} type Component type to compare with, eg "xeogl.PhongMaterial", or a xeogl component constructor.
- @returns {Boolean} True if this component is of given type or is subclass of the given type.
- */
- isType: function (type) {
- if (!xeogl._isString(type)) {
- type = type.type;
- if (!type) {
- return false;
- }
- }
- return xeogl._isComponentType(this.type, type);
- },
+ Will be null if this Component is not in a Model.
- /**
- * Initializes this component
- * @param cfg
- * @private
- */
- _init: function (cfg) {
- },
+ @property model
+ @final
+ @type Model
+ */
+ get model() {
+ return this._model;
+ }
- /**
- * Fires an event on this component.
- *
- * Notifies existing subscribers to the event, optionally retains the event to give to
- * any subsequent notifications on the event as they are made.
- *
- * @method fire
- * @param {String} event The event type name
- * @param {Object} value The event parameters
- * @param {Boolean} [forget=false] When true, does not retain for subsequent subscribers
- */
- fire: function (event, value, forget) {
- if (!this._events) {
- this._events = {};
+ /**
+ Tests if this component is of the given type, or is a subclass of the given type.
+
+ The type may be given as either a string or a component constructor.
+
+ This method works by walking up the inheritance type chain, which this component provides in
+ property {{#crossLink "Component/superTypes:property"}}{{/crossLink}}, returning true as soon as one of the type strings in
+ the chain matches the given type, of false if none match.
+
+ #### Examples:
+
+ ````javascript
+ var myRotate = new xeogl.Rotate({ ... });
+
+ myRotate.isType(xeogl.Component); // Returns true for all xeogl components
+ myRotate.isType("xeogl.Component"); // Returns true for all xeogl components
+ myRotate.isType(xeogl.Rotate); // Returns true
+ myRotate.isType(xeogl.Transform); // Returns true
+ myRotate.isType("xeogl.Transform"); // Returns true
+ myRotate.isType(xeogl.Mesh); // Returns false, because xeogl.Rotate does not (even indirectly) extend xeogl.Mesh
+ ````
+
+ @method isType
+ @param {String|Function} type Component type to compare with, eg "xeogl.PhongMaterial", or a xeogl component constructor.
+ @returns {Boolean} True if this component is of given type or is subclass of the given type.
+ */
+ isType(type) {
+ if (!utils.isString(type)) {
+ type = type.type;
+ if (!type) {
+ return false;
}
- if (!this._eventSubs) {
- this._eventSubs = {};
- }
- if (forget !== true) {
- this._events[event] = value || true; // Save notification
- }
- const subs = this._eventSubs[event];
- let sub;
- if (subs) { // Notify subscriptions
- for (const subId in subs) {
- if (subs.hasOwnProperty(subId)) {
- sub = subs[subId];
- this._eventCallDepth++;
- if (this._eventCallDepth < 300) {
- sub.callback.call(sub.scope, value);
- } else {
- this.error("fire: potential stack overflow from recursive event '" + event + "' - dropping this event");
- }
- this._eventCallDepth--;
+ }
+ return utils.isComponentType(this.type, type);
+ }
+
+ /**
+ * Fires an event on this component.
+ *
+ * Notifies existing subscribers to the event, optionally retains the event to give to
+ * any subsequent notifications on the event as they are made.
+ *
+ * @method fire
+ * @param {String} event The event type name
+ * @param {Object} value The event parameters
+ * @param {Boolean} [forget=false] When true, does not retain for subsequent subscribers
+ */
+ fire(event, value, forget) {
+ if (!this._events) {
+ this._events = {};
+ }
+ if (!this._eventSubs) {
+ this._eventSubs = {};
+ }
+ if (forget !== true) {
+ this._events[event] = value || true; // Save notification
+ }
+ const subs = this._eventSubs[event];
+ let sub;
+ if (subs) { // Notify subscriptions
+ for (const subId in subs) {
+ if (subs.hasOwnProperty(subId)) {
+ sub = subs[subId];
+ this._eventCallDepth++;
+ if (this._eventCallDepth < 300) {
+ sub.callback.call(sub.scope, value);
+ } else {
+ this.error("fire: potential stack overflow from recursive event '" + event + "' - dropping this event");
}
+ this._eventCallDepth--;
}
}
- },
+ }
+ }
- /**
- * Subscribes to an event on this component.
- *
- * The callback is be called with this component as scope.
- *
- * @method on
- * @param {String} event The event
- * @param {Function} callback Called fired on the event
- * @param {Object} [scope=this] Scope for the callback
- * @return {String} Handle to the subscription, which may be used to unsubscribe with {@link #off}.
- */
- on: function (event, callback, scope) {
- if (!this._events) {
- this._events = {};
- }
- if (!this._subIdMap) {
- this._subIdMap = new xeogl.utils.Map(); // Subscription subId pool
- }
- if (!this._subIdEvents) {
- this._subIdEvents = {};
- }
- if (!this._eventSubs) {
- this._eventSubs = {};
- }
- let subs = this._eventSubs[event];
- if (!subs) {
- subs = {};
- this._eventSubs[event] = subs;
- }
- const subId = this._subIdMap.addItem(); // Create unique subId
- subs[subId] = {
- callback: callback,
- scope: scope || this
- };
- this._subIdEvents[subId] = event;
- const value = this._events[event];
- if (value !== undefined) { // A publication exists, notify callback immediately
- callback.call(scope || this, value);
- }
- return subId;
- },
-
- /**
- * Cancels an event subscription that was previously made with {{#crossLink "Component/on:method"}}Component#on(){{/crossLink}} or
- * {{#crossLink "Component/once:method"}}Component#once(){{/crossLink}}.
- *
- * @method off
- * @param {String} subId Publication subId
- */
- off: function (subId) {
- if (subId === undefined || subId === null) {
- return;
- }
- if (!this._subIdEvents) {
- return;
- }
- const event = this._subIdEvents[subId];
- if (event) {
- delete this._subIdEvents[subId];
- const subs = this._eventSubs[event];
- if (subs) {
- delete subs[subId];
- }
- this._subIdMap.removeItem(subId); // Release subId
- }
- },
+ /**
+ * Subscribes to an event on this component.
+ *
+ * The callback is be called with this component as scope.
+ *
+ * @method on
+ * @param {String} event The event
+ * @param {Function} callback Called fired on the event
+ * @param {Object} [scope=this] Scope for the callback
+ * @return {String} Handle to the subscription, which may be used to unsubscribe with {@link #off}.
+ */
+ on(event, callback, scope) {
+ if (!this._events) {
+ this._events = {};
+ }
+ if (!this._subIdMap) {
+ this._subIdMap = new Map(); // Subscription subId pool
+ }
+ if (!this._subIdEvents) {
+ this._subIdEvents = {};
+ }
+ if (!this._eventSubs) {
+ this._eventSubs = {};
+ }
+ let subs = this._eventSubs[event];
+ if (!subs) {
+ subs = {};
+ this._eventSubs[event] = subs;
+ }
+ const subId = this._subIdMap.addItem(); // Create unique subId
+ subs[subId] = {
+ callback: callback,
+ scope: scope || this
+ };
+ this._subIdEvents[subId] = event;
+ const value = this._events[event];
+ if (value !== undefined) { // A publication exists, notify callback immediately
+ callback.call(scope || this, value);
+ }
+ return subId;
+ }
- /**
- * Subscribes to the next occurrence of the given event, then un-subscribes as soon as the event is subIdd.
- *
- * This is equivalent to calling {{#crossLink "Component/on:method"}}Component#on(){{/crossLink}}, and then calling
- * {{#crossLink "Component/off:method"}}Component#off(){{/crossLink}} inside the callback function.
- *
- * @method once
- * @param {String} event Data event to listen to
- * @param {Function(data)} callback Called when fresh data is available at the event
- * @param {Object} [scope=this] Scope for the callback
- */
- once: function (event, callback, scope) {
- const self = this;
- const subId = this.on(event,
- function (value) {
- self.off(subId);
- callback(value);
- },
- scope);
- },
+ /**
+ * Cancels an event subscription that was previously made with {{#crossLink "Component/on:method"}}Component#on(){{/crossLink}} or
+ * {{#crossLink "Component/once:method"}}Component#once(){{/crossLink}}.
+ *
+ * @method off
+ * @param {String} subId Publication subId
+ */
+ off(subId) {
+ if (subId === undefined || subId === null) {
+ return;
+ }
+ if (!this._subIdEvents) {
+ return;
+ }
+ const event = this._subIdEvents[subId];
+ if (event) {
+ delete this._subIdEvents[subId];
+ const subs = this._eventSubs[event];
+ if (subs) {
+ delete subs[subId];
+ }
+ this._subIdMap.removeItem(subId); // Release subId
+ }
+ }
- /**
- * Returns true if there are any subscribers to the given event on this component.
- *
- * @method hasSubs
- * @param {String} event The event
- * @return {Boolean} True if there are any subscribers to the given event on this component.
- */
- hasSubs: function (event) {
- return (this._eventSubs && !!this._eventSubs[event]);
- },
+ /**
+ * Subscribes to the next occurrence of the given event, then un-subscribes as soon as the event is subIdd.
+ *
+ * This is equivalent to calling {{#crossLink "Component/on:method"}}Component#on(){{/crossLink}}, and then calling
+ * {{#crossLink "Component/off:method"}}Component#off(){{/crossLink}} inside the callback function.
+ *
+ * @method once
+ * @param {String} event Data event to listen to
+ * @param {Function(data)} callback Called when fresh data is available at the event
+ * @param {Object} [scope=this] Scope for the callback
+ */
+ once(event, callback, scope) {
+ const self = this;
+ const subId = this.on(event,
+ function (value) {
+ self.off(subId);
+ callback(value);
+ },
+ scope);
+ }
- /**
- * Logs a console debugging message for this component.
- *
- * The console message will have this format: *````[LOG] [ : ````*
- *
- * Also fires the message as a {{#crossLink "Scene/log:event"}}{{/crossLink}} event on the
- * parent {{#crossLink "Scene"}}Scene{{/crossLink}}.
- *
- * @method log
- * @param {String} message The message to log
- */
- log: function (message) {
- message = "[LOG]" + this._message(message);
- window.console.log(message);
- this.scene.fire("log", message);
- },
+ /**
+ * Returns true if there are any subscribers to the given event on this component.
+ *
+ * @method hasSubs
+ * @param {String} event The event
+ * @return {Boolean} True if there are any subscribers to the given event on this component.
+ */
+ hasSubs(event) {
+ return (this._eventSubs && !!this._eventSubs[event]);
+ }
- _message: function (message) {
- return " [" + this.type + " " + xeogl._inQuotes(this.id) + "]: " + message;
- },
+ /**
+ * Logs a console debugging message for this component.
+ *
+ * The console message will have this format: *````[LOG] [ : ````*
+ *
+ * Also fires the message as a {{#crossLink "Scene/log:event"}}{{/crossLink}} event on the
+ * parent {{#crossLink "Scene"}}Scene{{/crossLink}}.
+ *
+ * @method log
+ * @param {String} message The message to log
+ */
+ log(message) {
+ message = "[LOG]" + this._message(message);
+ window.console.log(message);
+ this.scene.fire("log", message);
+ }
- /**
- * Logs a warning for this component to the JavaScript console.
- *
- * The console message will have this format: *````[WARN] [ =: ````*
- *
- * Also fires the message as a {{#crossLink "Scene/warn:event"}}{{/crossLink}} event on the
- * parent {{#crossLink "Scene"}}Scene{{/crossLink}}.
- *
- * @method warn
- * @param {String} message The message to log
- */
- warn: function (message) {
- message = "[WARN]" + this._message(message);
- window.console.warn(message);
- this.scene.fire("warn", message);
- },
+ _message(message) {
+ return " [" + this.type + " " + utils.inQuotes(this.id) + "]: " + message;
+ }
- /**
- * Logs an error for this component to the JavaScript console.
- *
- * The console message will have this format: *````[ERROR] [ =: ````*
- *
- * Also fires the message as an {{#crossLink "Scene/error:event"}}{{/crossLink}} event on the
- * parent {{#crossLink "Scene"}}Scene{{/crossLink}}.
- *
- * @method error
- * @param {String} message The message to log
- */
- error: function (message) {
- message = "[ERROR]" + this._message(message);
- window.console.error(message);
- this.scene.fire("error", message);
- },
+ /**
+ * Logs a warning for this component to the JavaScript console.
+ *
+ * The console message will have this format: *````[WARN] [ =: ````*
+ *
+ * Also fires the message as a {{#crossLink "Scene/warn:event"}}{{/crossLink}} event on the
+ * parent {{#crossLink "Scene"}}Scene{{/crossLink}}.
+ *
+ * @method warn
+ * @param {String} message The message to log
+ */
+ warn(message) {
+ message = "[WARN]" + this._message(message);
+ window.console.warn(message);
+ this.scene.fire("warn", message);
+ }
- /**
- * Adds a child component to this.
- * When component not given, attaches the scene's default instance for the given name (if any).
- * Publishes the new child component on this component, keyed to the given name.
- *
- * @param {*} params
- * @param {String} params.name component name
- * @param {Component} [params.component] The component
- * @param {String} [params.type] Optional expected type of base type of the child; when supplied, will
- * cause an exception if the given child is not the same type or a subtype of this.
- * @param {Boolean} [params.sceneDefault=false]
- * @param {Boolean} [params.sceneSingleton=false]
- * @param {Function} [params.onAttached] Optional callback called when component attached
- * @param {Function} [params.onAttached.callback] Callback function
- * @param {Function} [params.onAttached.scope] Optional scope for callback
- * @param {Function} [params.onDetached] Optional callback called when component is detached
- * @param {Function} [params.onDetached.callback] Callback function
- * @param {Function} [params.onDetached.scope] Optional scope for callback
- * @param {{String:Function}} [params.on] Callbacks to subscribe to properties on component
- * @param {Boolean} [params.recompiles=true] When true, fires "dirty" events on this component
- * @private
- */
- _attach: function (params) {
+ /**
+ * Logs an error for this component to the JavaScript console.
+ *
+ * The console message will have this format: *````[ERROR] [ =: ````*
+ *
+ * Also fires the message as an {{#crossLink "Scene/error:event"}}{{/crossLink}} event on the
+ * parent {{#crossLink "Scene"}}Scene{{/crossLink}}.
+ *
+ * @method error
+ * @param {String} message The message to log
+ */
+ error(message) {
+ message = "[ERROR]" + this._message(message);
+ window.console.error(message);
+ this.scene.fire("error", message);
+ }
- const name = params.name;
+ /**
+ * Adds a child component to this.
+ * When component not given, attaches the scene's default instance for the given name (if any).
+ * Publishes the new child component on this component, keyed to the given name.
+ *
+ * @param {*} params
+ * @param {String} params.name component name
+ * @param {Component} [params.component] The component
+ * @param {String} [params.type] Optional expected type of base type of the child; when supplied, will
+ * cause an exception if the given child is not the same type or a subtype of this.
+ * @param {Boolean} [params.sceneDefault=false]
+ * @param {Boolean} [params.sceneSingleton=false]
+ * @param {Function} [params.onAttached] Optional callback called when component attached
+ * @param {Function} [params.onAttached.callback] Callback function
+ * @param {Function} [params.onAttached.scope] Optional scope for callback
+ * @param {Function} [params.onDetached] Optional callback called when component is detached
+ * @param {Function} [params.onDetached.callback] Callback function
+ * @param {Function} [params.onDetached.scope] Optional scope for callback
+ * @param {{String:Function}} [params.on] Callbacks to subscribe to properties on component
+ * @param {Boolean} [params.recompiles=true] When true, fires "dirty" events on this component
+ * @private
+ */
+ _attach(params) {
+
+ const name = params.name;
+
+ if (!name) {
+ this.error("Component 'name' expected");
+ return;
+ }
- if (!name) {
- this.error("Component 'name' expected");
- return;
- }
+ let component = params.component;
+ const sceneDefault = params.sceneDefault;
+ const sceneSingleton = params.sceneSingleton;
+ const type = params.type;
+ const on = params.on;
+ const recompiles = params.recompiles !== false;
- let component = params.component;
- const sceneDefault = params.sceneDefault;
- const sceneSingleton = params.sceneSingleton;
- const type = params.type;
- const on = params.on;
- const recompiles = params.recompiles !== false;
+ // True when child given as config object, where parent manages its instantiation and destruction
+ let managingLifecycle = false;
- // True when child given as config object, where parent manages its instantiation and destruction
- let managingLifecycle = false;
+ if (component) {
- if (component) {
+ if (utils.isNumeric(component) || utils.isString(component)) {
- if (xeogl._isNumeric(component) || xeogl._isString(component)) {
+ // Component ID given
+ // Both numeric and string IDs are supported
- // Component ID given
- // Both numeric and string IDs are supported
+ const id = component;
- const id = component;
+ component = this.scene.components[id];
- component = this.scene.components[id];
+ if (!component) {
- if (!component) {
+ // Quote string IDs in errors
- // Quote string IDs in errors
+ this.error("Component not found: " + utils.inQuotes(id));
+ return;
+ }
- this.error("Component not found: " + xeogl._inQuotes(id));
- return;
- }
+ } else if (utils.isObject(component)) {
- } else if (xeogl._isObject(component)) {
+ // Component config given
- // Component config given
+ const componentCfg = component;
+ const componentType = componentCfg.type || type || "xeogl.Component";
+ const componentClass = window[componentType];
- const componentCfg = component;
- const componentType = componentCfg.type || type || "xeogl.Component";
- const componentClass = window[componentType];
+ if (!componentClass) {
+ this.error("Component type not found: " + componentType);
+ return;
+ }
- if (!componentClass) {
- this.error("Component type not found: " + componentType);
+ if (type) {
+ if (!utils.isComponentType(componentType, type)) {
+ this.error("Expected a " + type + " type or subtype, not a " + componentType);
return;
}
+ }
- if (type) {
- if (!xeogl._isComponentType(componentType, type)) {
- this.error("Expected a " + type + " type or subtype, not a " + componentType);
- return;
- }
- }
-
- component = new componentClass(this.scene, componentCfg);
+ component = new componentClass(this.scene, componentCfg);
- managingLifecycle = true;
- }
+ managingLifecycle = true;
}
+ }
+ if (!component) {
- if (!component) {
-
- if (sceneSingleton === true) {
+ if (sceneSingleton === true) {
- // Using the first instance of the component type we find
+ // Using the first instance of the component type we find
- const instances = this.scene.types[type];
- for (const id2 in instances) {
- if (instances.hasOwnProperty) {
- component = instances[id2];
- break;
- }
+ const instances = this.scene.types[type];
+ for (const id2 in instances) {
+ if (instances.hasOwnProperty) {
+ component = instances[id2];
+ break;
}
+ }
- if (!component) {
- this.error("Scene has no default component for '" + name + "'");
- return null;
- }
+ if (!component) {
+ this.error("Scene has no default component for '" + name + "'");
+ return null;
+ }
- } else if (sceneDefault === true) {
+ } else if (sceneDefault === true) {
- // Using a default scene component
+ // Using a default scene component
- component = this.scene[name];
+ component = this.scene[name];
- if (!component) {
- this.error("Scene has no default component for '" + name + "'");
- return null;
- }
+ if (!component) {
+ this.error("Scene has no default component for '" + name + "'");
+ return null;
}
}
+ }
- if (component) {
+ if (component) {
- if (component.scene.id !== this.scene.id) {
- this.error("Not in same scene: " + component.type + " " + xeogl._inQuotes(component.id));
- return;
- }
+ if (component.scene.id !== this.scene.id) {
+ this.error("Not in same scene: " + component.type + " " + utils.inQuotes(component.id));
+ return;
+ }
- if (type) {
+ if (type) {
- if (!component.isType(type)) {
- this.error("Expected a " + type + " type or subtype: " + component.type + " " + xeogl._inQuotes(component.id));
- return;
- }
+ if (!component.isType(type)) {
+ this.error("Expected a " + type + " type or subtype: " + component.type + " " + utils.inQuotes(component.id));
+ return;
}
}
+ }
- if (!this._attachments) {
- this._attachments = {};
- }
+ if (!this._attachments) {
+ this._attachments = {};
+ }
- const oldComponent = this._attached[name];
- let subs;
- let i;
- let len;
+ const oldComponent = this._attached[name];
+ let subs;
+ let i;
+ let len;
- if (oldComponent) {
+ if (oldComponent) {
- if (component && oldComponent.id === component.id) {
+ if (component && oldComponent.id === component.id) {
- // Reject attempt to reattach same component
- return;
- }
+ // Reject attempt to reattach same component
+ return;
+ }
- const oldAttachment = this._attachments[oldComponent.id];
+ const oldAttachment = this._attachments[oldComponent.id];
- // Unsubscribe from events on old component
+ // Unsubscribe from events on old component
- subs = oldAttachment.subs;
+ subs = oldAttachment.subs;
- for (i = 0, len = subs.length; i < len; i++) {
- oldComponent.off(subs[i]);
- }
+ for (i = 0, len = subs.length; i < len; i++) {
+ oldComponent.off(subs[i]);
+ }
- delete this._attached[name];
- delete this._attachments[oldComponent.id];
+ delete this._attached[name];
+ delete this._attachments[oldComponent.id];
- const onDetached = oldAttachment.params.onDetached;
- if (onDetached) {
- if (xeogl._isFunction(onDetached)) {
- onDetached(oldComponent);
- } else {
- onDetached.scope ? onDetached.callback.call(onDetached.scope, oldComponent) : onDetached.callback(oldComponent);
- }
+ const onDetached = oldAttachment.params.onDetached;
+ if (onDetached) {
+ if (utils.isFunction(onDetached)) {
+ onDetached(oldComponent);
+ } else {
+ onDetached.scope ? onDetached.callback.call(onDetached.scope, oldComponent) : onDetached.callback(oldComponent);
}
+ }
- if (oldAttachment.managingLifecycle) {
+ if (oldAttachment.managingLifecycle) {
- // Note that we just unsubscribed from all events fired by the child
- // component, so destroying it won't fire events back at us now.
+ // Note that we just unsubscribed from all events fired by the child
+ // component, so destroying it won't fire events back at us now.
- oldComponent.destroy();
- }
+ oldComponent.destroy();
}
+ }
- if (component) {
+ if (component) {
- // Set and publish the new component on this component
+ // Set and publish the new component on this component
- const attachment = {
- params: params,
- component: component,
- subs: [],
- managingLifecycle: managingLifecycle
- };
+ const attachment = {
+ params: params,
+ component: component,
+ subs: [],
+ managingLifecycle: managingLifecycle
+ };
+ attachment.subs.push(
+ component.on("destroyed",
+ function () {
+ attachment.params.component = null;
+ this._attach(attachment.params);
+ },
+ this));
+
+ if (recompiles) {
attachment.subs.push(
- component.on("destroyed",
+ component.on("dirty",
function () {
- attachment.params.component = null;
- this._attach(attachment.params);
+ this.fire("dirty", this);
},
this));
+ }
- if (recompiles) {
- attachment.subs.push(
- component.on("dirty",
- function () {
- this.fire("dirty", this);
- },
- this));
- }
-
- this._attached[name] = component;
- this._attachments[component.id] = attachment;
+ this._attached[name] = component;
+ this._attachments[component.id] = attachment;
- // Bind destruct listener to new component to remove it
- // from this component when destroyed
+ // Bind destruct listener to new component to remove it
+ // from this component when destroyed
- const onAttached = params.onAttached;
- if (onAttached) {
- if (xeogl._isFunction(onAttached)) {
- onAttached(component);
- } else {
- onAttached.scope ? onAttached.callback.call(onAttached.scope, component) : onAttached.callback(component);
- }
+ const onAttached = params.onAttached;
+ if (onAttached) {
+ if (utils.isFunction(onAttached)) {
+ onAttached(component);
+ } else {
+ onAttached.scope ? onAttached.callback.call(onAttached.scope, component) : onAttached.callback(component);
}
+ }
- if (on) {
-
- let event;
- let subIdr;
- let callback;
- let scope;
+ if (on) {
- for (event in on) {
- if (on.hasOwnProperty(event)) {
+ let event;
+ let subIdr;
+ let callback;
+ let scope;
- subIdr = on[event];
+ for (event in on) {
+ if (on.hasOwnProperty(event)) {
- if (xeogl._isFunction(subIdr)) {
- callback = subIdr;
- scope = null;
- } else {
- callback = subIdr.callback;
- scope = subIdr.scope;
- }
+ subIdr = on[event];
- if (!callback) {
- continue;
- }
+ if (utils.isFunction(subIdr)) {
+ callback = subIdr;
+ scope = null;
+ } else {
+ callback = subIdr.callback;
+ scope = subIdr.scope;
+ }
- attachment.subs.push(component.on(event, callback, scope));
+ if (!callback) {
+ continue;
}
+
+ attachment.subs.push(component.on(event, callback, scope));
}
}
}
+ }
- if (recompiles) {
- this.fire("dirty", this); // FIXME: May trigger spurous mesh recompilations unless able to limit with param?
- }
+ if (recompiles) {
+ this.fire("dirty", this); // FIXME: May trigger spurous mesh recompilations unless able to limit with param?
+ }
- this.fire(name, component); // Component can be null
+ this.fire(name, component); // Component can be null
- return component;
- },
+ return component;
+ }
- _checkComponent: function (expectedType, component) {
- if (xeogl._isObject(component)) {
- if (component.type) {
- if (!xeogl._isComponentType(component.type, expectedType)) {
- this.error("Expected a " + expectedType + " type or subtype: " + component.type + " " + xeogl._inQuotes(component.id));
- return;
- }
- } else {
- component.type = expectedType;
+ _checkComponent(expectedType, component) {
+ if (utils.isObject(component)) {
+ if (component.type) {
+ if (!utils.isComponentType(component.type, expectedType)) {
+ this.error("Expected a " + expectedType + " type or subtype: " + component.type + " " + utils.inQuotes(component.id));
+ return;
}
- component = new window[component.type](this.scene, component);
} else {
- if (xeogl._isID(component)) { // Expensive test
- const id = component;
- component = this.scene.components[id];
- if (!component) {
- this.error("Component not found: " + xeogl._inQuotes(component.id));
- return;
- }
- }
- }
- if (component.scene.id !== this.scene.id) {
- this.error("Not in same scene: " + component.type + " " + xeogl._inQuotes(component.id));
- return;
+ component.type = expectedType;
}
- if (!component.isType(expectedType)) {
- this.error("Expected a " + expectedType + " type or subtype: " + component.type + " " + xeogl._inQuotes(component.id));
- return;
+ component = new window[component.type](this.scene, component);
+ } else {
+ if (utils.isID(component)) { // Expensive test
+ const id = component;
+ component = this.scene.components[id];
+ if (!component) {
+ this.error("Component not found: " + utils.inQuotes(component.id));
+ return;
+ }
}
- return component;
- },
-
+ }
+ if (component.scene.id !== this.scene.id) {
+ this.error("Not in same scene: " + component.type + " " + utils.inQuotes(component.id));
+ return;
+ }
+ if (!component.isType(expectedType)) {
+ this.error("Expected a " + expectedType + " type or subtype: " + component.type + " " + utils.inQuotes(component.id));
+ return;
+ }
+ return component;
+ }
- /**
- * Convenience method for creating a Component within this Component's {{#crossLink "Scene"}}{{/crossLink}}.
- *
- * The method is given a component configuration, like so:
- *
- * ````javascript
- * var material = myComponent.create({
+ /**
+ * Convenience method for creating a Component within this Component's {{#crossLink "Scene"}}{{/crossLink}}.
+ *
+ * The method is given a component configuration, like so:
+ *
+ * ````javascript
+ * var material = myComponent.create({
* type: "xeogl.PhongMaterial",
* diffuse: [1,0,0],
* specular: [1,1,0]
* }, "myMaterial");
- * ````
- *
- * @method create
- * @param {*} [cfg] Configuration for the component instance.
- * @returns {*}
- */
- create: function (cfg) {
-
- let type;
- let claz;
-
- if (xeogl._isObject(cfg)) {
- type = cfg.type || "xeogl.Component";
- claz = xeogl[type.substring(6)];
-
- } else if (xeogl._isString(cfg)) {
- type = cfg;
- claz = xeogl[type.substring(6)];
-
- } else {
- claz = cfg;
- type = cfg.prototype.type;
- // TODO: catch unknown component class
- }
+ * ````
+ *
+ * @method create
+ * @param {*} [cfg] Configuration for the component instance.
+ * @returns {*}
+ */
+ create(cfg) {
+
+ let type;
+ let claz;
+
+ if (utils.isObject(cfg)) {
+ type = cfg.type || "xeogl.Component";
+ claz = xeogl[type.substring(6)];
+
+ } else if (utils.isString(cfg)) {
+ type = cfg;
+ claz = xeogl[type.substring(6)];
+
+ } else {
+ claz = cfg;
+ type = cfg.prototype.type;
+ // TODO: catch unknown component class
+ }
- if (!claz) {
- this.error("Component type not found: " + type);
- return;
- }
+ if (!claz) {
+ this.error("Component type not found: " + type);
+ return;
+ }
- if (!xeogl._isComponentType(type, "xeogl.Component")) {
- this.error("Expected a xeogl.Component type or subtype");
- return;
- }
+ if (!utils.isComponentType(type, "xeogl.Component")) {
+ this.error("Expected a xeogl.Component type or subtype");
+ return;
+ }
- if (cfg && cfg.id && this.components[cfg.id]) {
- this.error("Component " + xeogl._inQuotes(cfg.id) + " already exists in Scene - ignoring ID, will randomly-generate instead");
- cfg.id = undefined;
- //return null;
- }
+ if (cfg && cfg.id && this.components[cfg.id]) {
+ this.error("Component " + utils.inQuotes(cfg.id) + " already exists in Scene - ignoring ID, will randomly-generate instead");
+ cfg.id = undefined;
+ //return null;
+ }
- const component = new claz(this, cfg);
- if (component) {
- this._adopt(component);
- }
+ const component = new claz(this, cfg);
+ if (component) {
+ this._adopt(component);
+ }
- return component;
- },
+ return component;
+ }
- _adopt: function (component) {
- if (!this._adoptees) {
- this._adoptees = {};
- }
- if (!this._adoptees[component.id]) {
- this._adoptees[component.id] = component;
- }
- component.on("destroyed", function () {
- delete this._adoptees[component.id];
- }, this);
- },
+ _adopt(component) {
+ if (!this._adoptees) {
+ this._adoptees = {};
+ }
+ if (!this._adoptees[component.id]) {
+ this._adoptees[component.id] = component;
+ }
+ component.on("destroyed", function () {
+ delete this._adoptees[component.id];
+ }, this);
+ }
- /**
- * Protected method, called by sub-classes to queue a call to _update().
- * @protected
- * @param {Number} [priority=1]
- */
- _needUpdate: function (priority) {
- if (!this._updateScheduled) {
- this._updateScheduled = true;
- if (priority === 0) {
- xeogl.deferTask(this._doUpdate, this);
- } else {
- xeogl.scheduleTask(this._doUpdate, this);
- }
+ /**
+ * Protected method, called by sub-classes to queue a call to _update().
+ * @protected
+ * @param {Number} [priority=1]
+ */
+ _needUpdate(priority) {
+ if (!this._updateScheduled) {
+ this._updateScheduled = true;
+ if (priority === 0) {
+ this._doUpdate();
+ } else {
+ tasks.scheduleTask(this._doUpdate, this);
}
- },
+ }
+ }
- /**
- * @private
- */
- _doUpdate: function () {
- if (this._updateScheduled) {
- this._updateScheduled = false;
- if (this._update) {
- this._update();
- }
+ /**
+ * @private
+ */
+ _doUpdate() {
+ if (this._updateScheduled) {
+ this._updateScheduled = false;
+ if (this._update) {
+ this._update();
}
- },
-
- /**
- * Protected virtual template method, optionally implemented
- * by sub-classes to perform a scheduled task.
- *
- * @protected
- */
- _update: null,
+ }
+ }
- /**
- * Destroys this component.
- *
- * Fires a {{#crossLink "Component/destroyed:event"}}{{/crossLink}} event on this Component.
- *
- * Automatically disassociates this component from other components, causing them to fall back on any
- * defaults that this component overrode on them.
- *
- * TODO: describe effect with respect to #create
- *
- * @method destroy
- */
- destroy: function () {
+ /**
+ * Protected virtual template method, optionally implemented
+ * by sub-classes to perform a scheduled task.
+ *
+ * @protected
+ */
+ _update() {
+ }
- if (this.destroyed) {
- return;
- }
+ /**
+ * Destroys this component.
+ *
+ * Fires a {{#crossLink "Component/destroyed:event"}}{{/crossLink}} event on this Component.
+ *
+ * Automatically disassociates this component from other components, causing them to fall back on any
+ * defaults that this component overrode on them.
+ *
+ * TODO: describe effect with respect to #create
+ *
+ * @method destroy
+ */
+ destroy() {
+
+ if (this.destroyed) {
+ return;
+ }
- // Unsubscribe from child components and destroy then
-
- let id;
- let attachment;
- let component;
- let subs;
- let i;
- let len;
-
- if (this._attachments) {
- for (id in this._attachments) {
- if (this._attachments.hasOwnProperty(id)) {
- attachment = this._attachments[id];
- component = attachment.component;
- subs = attachment.subs;
- for (i = 0, len = subs.length; i < len; i++) {
- component.off(subs[i]);
- }
- if (attachment.managingLifecycle) {
- component.destroy();
- }
+ // Unsubscribe from child components and destroy then
+
+ let id;
+ let attachment;
+ let component;
+ let subs;
+ let i;
+ let len;
+
+ if (this._attachments) {
+ for (id in this._attachments) {
+ if (this._attachments.hasOwnProperty(id)) {
+ attachment = this._attachments[id];
+ component = attachment.component;
+ subs = attachment.subs;
+ for (i = 0, len = subs.length; i < len; i++) {
+ component.off(subs[i]);
}
- }
- }
-
- // Release components created with #create
-
- if (this._adoptees) {
- for (id in this._adoptees) {
- if (this._adoptees.hasOwnProperty(id)) {
- component = this._adoptees[id];
+ if (attachment.managingLifecycle) {
component.destroy();
- delete this._adoptees[id];
}
}
}
+ }
- // Execute subclass behaviour
+ // Release components created with #create
- if (this._destroy) {
- this._destroy();
+ if (this._adoptees) {
+ for (id in this._adoptees) {
+ if (this._adoptees.hasOwnProperty(id)) {
+ component = this._adoptees[id];
+ component.destroy();
+ delete this._adoptees[id];
+ }
}
+ }
- this.scene._removeComponent(this);
-
- // Memory leak avoidance
- this._attached = {};
- this._attachments = null;
- this._subIdMap = null;
- this._subIdEvents = null;
- this._eventSubs = null;
- this._events = null;
- this._eventCallDepth = 0;
- this._adoptees = null;
- this._updateScheduled = false;
+ this.scene._removeComponent(this);
- /**
- * Fired when this Component is destroyed.
- * @event destroyed
- */
- this.fire("destroyed", this.destroyed = true);
- },
+ // Memory leak avoidance
+ this._attached = {};
+ this._attachments = null;
+ this._subIdMap = null;
+ this._subIdEvents = null;
+ this._eventSubs = null;
+ this._events = null;
+ this._eventCallDepth = 0;
+ this._adoptees = null;
+ this._updateScheduled = false;
/**
- * Protected template method, implemented by sub-classes
- * to clean up just before the component is destroyed.
- *
- * @protected
+ * Fired when this Component is destroyed.
+ * @event destroyed
*/
- _destroy: function () {
+ this.fire("destroyed", this.destroyed = true);
+ }
+}
- }
- });
-})();
+export {Component};
diff --git a/src/controls/cameraControl.js b/src/controls/cameraControl.js
index e430cb731..ba1413138 100644
--- a/src/controls/cameraControl.js
+++ b/src/controls/cameraControl.js
@@ -50,7 +50,7 @@
@module xeogl
@submodule controls
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}{{/crossLink}}.
+ @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 scene, generated automatically when omitted.
@param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to this CameraControl.
@@ -69,492 +69,443 @@
@author DerSchmale / http://www.derschmale.com
@extends Component
*/
-(function () {
- "use strict";
+import {core} from "./../core.js";
+import {math} from '../math/math.js';
+import {Component} from '../component.js';
+import {Mesh} from '../objects/mesh.js';
+import {AABBGeometry} from '../geometry/aabbGeometry.js';
+import {PhongMaterial} from '../materials/phongMaterial.js';
+import {CameraFlightAnimation} from '../animation/cameraFlightAnimation.js';
+const type = "xeogl.CameraControl";
- xeogl.CameraControl = xeogl.Component.extend({
+class CameraControl extends Component {
- /**
- JavaScript class name for this Component.
+ /**
+ JavaScript class name for this Component.
- @property type
- @type String
- @final
- */
- type: "xeogl.CameraControl",
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
- _init: function (cfg) {
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return type;
+ }
- const self = this;
+ init(cfg) {
- this._boundaryHelper = new xeogl.Mesh(this, {
- geometry: new xeogl.AABBGeometry(this),
- material: new xeogl.PhongMaterial(this, {
- diffuse: [0, 0, 0],
- ambient: [0, 0, 0],
- specular: [0, 0, 0],
- emissive: [1.0, 1.0, 0.6],
- lineWidth: 4
- }),
- visible: false,
- collidable: false
- });
+ super.init(cfg);
- this._pivoter = new (function () { // Pivots the Camera around an arbitrary World-space position
-
- // Pivot math by: http://www.derschmale.com/
-
- const math = xeogl.math;
- const scene = self.scene;
- const camera = scene.camera;
- const canvas = scene.canvas;
- const pivotPoint = new Float32Array(3);
- let cameraOffset;
- let azimuth = 0;
- let polar = 0;
- let radius = 0;
- let pivoting = false; // True while pivoting
-
- const spot = document.createElement("div");
- spot.innerText = " ";
- spot.style.color = "#ffffff";
- spot.style.position = "absolute";
- spot.style.width = "25px";
- spot.style.height = "25px";
- spot.style.left = "0px";
- spot.style.top = "0px";
- spot.style["border-radius"] = "15px";
- spot.style["border"] = "2px solid #ffffff";
- spot.style["background"] = "black";
- spot.style.visibility = "hidden";
- spot.style["box-shadow"] = "5px 5px 15px 1px #000000";
- spot.style["z-index"] = 0;
- spot.style["pointer-events"] = "none";
- document.body.appendChild(spot);
+ const self = this;
- (function () {
- const viewPos = math.vec4();
- const projPos = math.vec4();
- const canvasPos = math.vec2();
- let distDirty = true;
- camera.on("viewMatrix", function () {
- distDirty = true;
- });
- camera.on("projMatrix", function () {
- distDirty = true;
- });
- scene.on("tick", function () {
- if (pivoting && distDirty) {
- math.transformPoint3(camera.viewMatrix, pivotPoint, viewPos);
- viewPos[3] = 1;
- math.transformPoint4(camera.projMatrix, viewPos, projPos);
- const aabb = canvas.boundary;
- canvasPos[0] = Math.floor((1 + projPos[0] / projPos[3]) * aabb[2] / 2);
- canvasPos[1] = Math.floor((1 - projPos[1] / projPos[3]) * aabb[3] / 2);
- const canvasElem = canvas.canvas;
- const rect = canvasElem.getBoundingClientRect();
- spot.style.left = (Math.floor(rect.left + canvasPos[0]) - 12) + "px";
- spot.style.top = (Math.floor(rect.top + canvasPos[1]) - 12) + "px";
- spot.style.visibility = "visible";
- distDirty = false;
- }
- });
- })();
+ this._boundaryHelper = new Mesh(this, {
+ geometry: new AABBGeometry(this),
+ material: new PhongMaterial(this, {
+ diffuse: [0, 0, 0],
+ ambient: [0, 0, 0],
+ specular: [0, 0, 0],
+ emissive: [1.0, 1.0, 0.6],
+ lineWidth: 4
+ }),
+ visible: false,
+ collidable: false
+ });
- this.startPivot = function (worldPos) {
- if (worldPos) { // Use last pivotPoint by default
- pivotPoint.set(worldPos);
- }
- let lookat = math.lookAtMat4v(camera.eye, camera.look, camera.worldUp);
- cameraOffset = math.transformPoint3(lookat, pivotPoint);
- cameraOffset[2] += math.distVec3(camera.eye, pivotPoint);
- lookat = math.inverseMat4(lookat);
- const offset = math.transformVec3(lookat, cameraOffset);
- const diff = math.vec3();
- math.subVec3(camera.eye, pivotPoint, diff);
- math.addVec3(diff, offset);
- if (camera.worldUp[2] === 1) {
- const t = diff[1];
- diff[1] = diff[2];
- diff[2] = t;
- }
- radius = math.lenVec3(diff);
- polar = Math.acos(diff[1] / radius);
- azimuth = Math.atan2(diff[0], diff[2]);
- pivoting = true;
- };
+ this._pivoter = new (function () { // Pivots the Camera around an arbitrary World-space position
- this.getPivoting = function () {
- return pivoting;
- };
+ // Pivot math by: http://www.derschmale.com/
- this.getPivotPos = function () {
- return pivotPoint;
- };
+ const scene = self.scene;
+ const camera = scene.camera;
+ const canvas = scene.canvas;
+ const pivotPoint = new Float32Array(3);
+ let cameraOffset;
+ let azimuth = 0;
+ let polar = 0;
+ let radius = 0;
+ let pivoting = false; // True while pivoting
+
+ const spot = document.createElement("div");
+ spot.innerText = " ";
+ spot.style.color = "#ffffff";
+ spot.style.position = "absolute";
+ spot.style.width = "25px";
+ spot.style.height = "25px";
+ spot.style.left = "0px";
+ spot.style.top = "0px";
+ spot.style["border-radius"] = "15px";
+ spot.style["border"] = "2px solid #ffffff";
+ spot.style["background"] = "black";
+ spot.style.visibility = "hidden";
+ spot.style["box-shadow"] = "5px 5px 15px 1px #000000";
+ spot.style["z-index"] = 0;
+ spot.style["pointer-events"] = "none";
+ document.body.appendChild(spot);
- this.continuePivot = function (yawInc, pitchInc) {
- if (!pivoting) {
- return;
- }
- if (yawInc === 0 && pitchInc === 0) {
- return;
- }
- if (camera.worldUp[2] === 1) {
- dx = -dx;
- }
- var dx = -yawInc;
- const dy = -pitchInc;
- azimuth += -dx * .01;
- polar += dy * .01;
- polar = math.clamp(polar, .001, Math.PI - .001);
- const pos = [
- radius * Math.sin(polar) * Math.sin(azimuth),
- radius * Math.cos(polar),
- radius * Math.sin(polar) * Math.cos(azimuth)
- ];
- if (camera.worldUp[2] === 1) {
- const t = pos[1];
- pos[1] = pos[2];
- pos[2] = t;
+ (function () {
+ const viewPos = math.vec4();
+ const projPos = math.vec4();
+ const canvasPos = math.vec2();
+ let distDirty = true;
+ camera.on("viewMatrix", function () {
+ distDirty = true;
+ });
+ camera.on("projMatrix", function () {
+ distDirty = true;
+ });
+ scene.on("tick", function () {
+ if (pivoting && distDirty) {
+ math.transformPoint3(camera.viewMatrix, pivotPoint, viewPos);
+ viewPos[3] = 1;
+ math.transformPoint4(camera.projMatrix, viewPos, projPos);
+ const aabb = canvas.boundary;
+ canvasPos[0] = Math.floor((1 + projPos[0] / projPos[3]) * aabb[2] / 2);
+ canvasPos[1] = Math.floor((1 - projPos[1] / projPos[3]) * aabb[3] / 2);
+ const canvasElem = canvas.canvas;
+ const rect = canvasElem.getBoundingClientRect();
+ spot.style.left = (Math.floor(rect.left + canvasPos[0]) - 12) + "px";
+ spot.style.top = (Math.floor(rect.top + canvasPos[1]) - 12) + "px";
+ spot.style.visibility = "visible";
+ distDirty = false;
}
- // Preserve the eye->look distance, since in xeogl "look" is the point-of-interest, not the direction vector.
- const eyeLookLen = math.lenVec3(math.subVec3(camera.look, camera.eye, math.vec3()));
- math.addVec3(pos, pivotPoint);
- let lookat = math.lookAtMat4v(pos, pivotPoint, camera.worldUp);
- lookat = math.inverseMat4(lookat);
- const offset = math.transformVec3(lookat, cameraOffset);
- lookat[12] -= offset[0];
- lookat[13] -= offset[1];
- lookat[14] -= offset[2];
- const zAxis = [lookat[8], lookat[9], lookat[10]];
- camera.eye = [lookat[12], lookat[13], lookat[14]];
- math.subVec3(camera.eye, math.mulVec3Scalar(zAxis, eyeLookLen), camera.look);
- camera.up = [lookat[4], lookat[5], lookat[6]];
- spot.style.visibility = "visible";
- };
-
- this.endPivot = function () {
- spot.style.visibility = "hidden";
- pivoting = false;
- };
-
+ });
})();
- this._cameraFlight = new xeogl.CameraFlightAnimation(this, {
- duration: 0.5
- });
-
- this.firstPerson = cfg.firstPerson;
- this.walking = cfg.walking;
- this.keyboardLayout = cfg.keyboardLayout;
- this.doublePickFlyTo = cfg.doublePickFlyTo;
- this.active = cfg.active;
- this.pivoting = cfg.pivoting;
- this.panToPointer = cfg.panToPointer;
- this.panToPivot = cfg.panToPivot;
- this.inertia = cfg.inertia;
-
- this._initEvents(); // Set up all the mouse/touch/kb handlers
- },
-
- _props: {
-
- /**
- Indicates whether this CameraControl is active or not.
-
- @property active
- @default true
- @type Boolean
- */
- active: {
- set: function (value) {
- this._active = value !== false;
- },
- get: function () {
- return this._active;
- }
- },
-
- /**
- When true, clicking on a {{#crossLink "Mesh"}}{{/crossLink}} and dragging will pivot
- the {{#crossLink "Camera"}}{{/crossLink}} about the picked point on the Mesh's surface.
-
- @property pivoting
- @default false
- @type Boolean
- */
- pivoting: {
- set: function (value) {
- this._pivoting = !!value;
- },
- get: function () {
- return this._pivoting;
- }
- },
-
-
- /**
- When true, mouse wheel when mouse is over a {{#crossLink "Mesh"}}{{/crossLink}} will zoom
- the {{#crossLink "Camera"}}{{/crossLink}} towards the hovered point on the Mesh's surface.
-
- @property panToPointer
- @default false
- @type Boolean
- */
- panToPointer: {
- set: function (value) {
- this._panToPointer = !!value;
- if (this._panToPointer) {
- this._panToPivot = false;
- }
- },
- get: function () {
- return this._panToPointer;
- }
- },
-
- /**
- When true, mouse wheel when mouse is over a {{#crossLink "Mesh"}}{{/crossLink}} will zoom
- the {{#crossLink "Camera"}}{{/crossLink}} towards the pivot point.
-
- @property panToPivot
- @default false
- @type Boolean
- */
- panToPivot: {
- set: function (value) {
- this._panToPivot = !!value;
- if (this._panToPivot) {
- this._panToPointer = false;
- }
- },
- get: function () {
- return this._panToPivot;
+ this.startPivot = function (worldPos) {
+ if (worldPos) { // Use last pivotPoint by default
+ pivotPoint.set(worldPos);
}
- },
-
- /**
- Indicates whether this CameraControl is in "first person" mode.
-
- In "first person" mode (disabled by default) the look position rotates about the eye position. Otherwise,
- the eye rotates about the look.
-
- @property firstPerson
- @default false
- @type Boolean
- */
- firstPerson: {
- set: function (value) {
- this._firstPerson = !!value;
- },
- get: function () {
- return this._firstPerson;
+ let lookat = math.lookAtMat4v(camera.eye, camera.look, camera.worldUp);
+ cameraOffset = math.transformPoint3(lookat, pivotPoint);
+ cameraOffset[2] += math.distVec3(camera.eye, pivotPoint);
+ lookat = math.inverseMat4(lookat);
+ const offset = math.transformVec3(lookat, cameraOffset);
+ const diff = math.vec3();
+ math.subVec3(camera.eye, pivotPoint, diff);
+ math.addVec3(diff, offset);
+ if (camera.worldUp[2] === 1) {
+ const t = diff[1];
+ diff[1] = diff[2];
+ diff[2] = t;
}
- },
-
- /**
- Indicates whether this CameraControl is in "walking" mode.
+ radius = math.lenVec3(diff);
+ polar = Math.acos(diff[1] / radius);
+ azimuth = Math.atan2(diff[0], diff[2]);
+ pivoting = true;
+ };
- When set true, this constrains eye movement to the horizontal X-Z plane. When doing a walkthrough,
- this is useful to allow us to look upwards or downwards as we move, while keeping us moving in the
- horizontal plane.
+ this.getPivoting = function () {
+ return pivoting;
+ };
- This only has an effect when also in "first person" mode.
+ this.getPivotPos = function () {
+ return pivotPoint;
+ };
- @property walking
- @default false
- @type Boolean
- */
- walking: {
- set: function (value) {
- this._walking = !!value;
- },
- get: function () {
- return this._walking;
+ this.continuePivot = function (yawInc, pitchInc) {
+ if (!pivoting) {
+ return;
}
- },
-
- /**
- * TODO
- *
- *
- * @property doublePickFlyTo
- * @default true
- * @type Boolean
- */
- doublePickFlyTo: {
- set: function (value) {
- this._doublePickFlyTo = value !== false;
- },
- get: function () {
- return this._doublePickFlyTo;
+ if (yawInc === 0 && pitchInc === 0) {
+ return;
}
- },
-
-
- /**
- Factor in range [0..1] indicating how much the camera keeps moving after you finish
- panning or rotating it.
-
- A value of 0.0 causes it to immediately stop, 0.5 causes its movement to decay 50% on each tick,
- while 1.0 causes no decay, allowing it continue moving, by the current rate of pan or rotation.
-
- You may choose an inertia of zero when you want be able to precisely position or rotate the camera,
- without interference from inertia. ero inertia can also mean that less frames are rendered while
- you are positioning the camera.
-
- @property inertia
- @default 0.5
- @type Number
- */
- inertia: {
- set: function (value) {
- this._inertia = value === undefined ? 0.5 : value;
- },
- get: function () {
- return this._inertia;
+ if (camera.worldUp[2] === 1) {
+ dx = -dx;
}
- },
-
- /**
- * TODO
- *
- * @property keyboardLayout
- * @default "qwerty"
- * @type String
- */
- keyboardLayout: {
- set: function (value) {
- this._keyboardLayout = value || "qwerty";
- },
- get: function () {
- return this._keyboardLayout;
+ var dx = -yawInc;
+ const dy = -pitchInc;
+ azimuth += -dx * .01;
+ polar += dy * .01;
+ polar = math.clamp(polar, .001, Math.PI - .001);
+ const pos = [
+ radius * Math.sin(polar) * Math.sin(azimuth),
+ radius * Math.cos(polar),
+ radius * Math.sin(polar) * Math.cos(azimuth)
+ ];
+ if (camera.worldUp[2] === 1) {
+ const t = pos[1];
+ pos[1] = pos[2];
+ pos[2] = t;
}
- }
- },
-
- _destroy: function () {
- this.active = false;
- },
-
- _initEvents: function () {
-
- const self = this;
- const scene = this.scene;
- const input = scene.input;
- const camera = scene.camera;
- const math = xeogl.math;
- const canvas = this.scene.canvas.canvas;
- let over = false;
- const mouseHoverDelay = 500;
- const mouseOrbitRate = 0.4;
- const mousePanRate = 0.4;
- const mouseZoomRate = 0.8;
- const mouseWheelPanRate = 0.4;
- const keyboardOrbitRate = .02;
- const keyboardPanRate = .02;
- const keyboardZoomRate = .02;
- const touchRotateRate = 0.3;
- const touchPanRate = 0.2;
- const touchZoomRate = 0.05;
-
- canvas.oncontextmenu = function (e) {
- e.preventDefault();
+ // Preserve the eye->look distance, since in xeogl "look" is the point-of-interest, not the direction vector.
+ const eyeLookLen = math.lenVec3(math.subVec3(camera.look, camera.eye, math.vec3()));
+ math.addVec3(pos, pivotPoint);
+ let lookat = math.lookAtMat4v(pos, pivotPoint, camera.worldUp);
+ lookat = math.inverseMat4(lookat);
+ const offset = math.transformVec3(lookat, cameraOffset);
+ lookat[12] -= offset[0];
+ lookat[13] -= offset[1];
+ lookat[14] -= offset[2];
+ const zAxis = [lookat[8], lookat[9], lookat[10]];
+ camera.eye = [lookat[12], lookat[13], lookat[14]];
+ math.subVec3(camera.eye, math.mulVec3Scalar(zAxis, eyeLookLen), camera.look);
+ camera.up = [lookat[4], lookat[5], lookat[6]];
+ spot.style.visibility = "visible";
};
- const getCanvasPosFromEvent = function (event, canvasPos) {
- if (!event) {
- event = window.event;
- canvasPos[0] = event.x;
- canvasPos[1] = event.y;
- } else {
- let element = event.target;
- let totalOffsetLeft = 0;
- let totalOffsetTop = 0;
- while (element.offsetParent) {
- totalOffsetLeft += element.offsetLeft;
- totalOffsetTop += element.offsetTop;
- element = element.offsetParent;
- }
- canvasPos[0] = event.pageX - totalOffsetLeft;
- canvasPos[1] = event.pageY - totalOffsetTop;
- }
- return canvasPos;
+ this.endPivot = function () {
+ spot.style.visibility = "hidden";
+ pivoting = false;
};
- const pickCursorPos = [0, 0];
- let needPickMesh = false;
- let needPickSurface = false;
- let lastPickedMeshId;
- let hit;
- let picked = false;
- let pickedSurface = false;
-
- function updatePick() {
- if (!needPickMesh && !needPickSurface) {
- return;
- }
- picked = false;
- pickedSurface = false;
- if (needPickSurface || self.hasSubs("hoverSurface")) {
- hit = scene.pick({
- pickSurface: true,
- canvasPos: pickCursorPos
- });
- } else { // needPickMesh == true
- hit = scene.pick({
- canvasPos: pickCursorPos
- });
+ })();
+
+ this._cameraFlight = new CameraFlightAnimation(this, {
+ duration: 0.5
+ });
+
+ this.firstPerson = cfg.firstPerson;
+ this.walking = cfg.walking;
+ this.keyboardLayout = cfg.keyboardLayout;
+ this.doublePickFlyTo = cfg.doublePickFlyTo;
+ this.active = cfg.active;
+ this.pivoting = cfg.pivoting;
+ this.panToPointer = cfg.panToPointer;
+ this.panToPivot = cfg.panToPivot;
+ this.inertia = cfg.inertia;
+
+ this._initEvents(); // Set up all the mouse/touch/kb handlers
+ }
+
+ /**
+ Indicates whether this CameraControl is active or not.
+
+ @property active
+ @default true
+ @type Boolean
+ */
+ set active(value) {
+ this._active = value !== false;
+ }
+
+ get active() {
+ return this._active;
+ }
+
+ /**
+ When true, clicking on a {{#crossLink "Mesh"}}{{/crossLink}} and dragging will pivot
+ the {{#crossLink "Camera"}}{{/crossLink}} about the picked point on the Mesh's surface.
+
+ @property pivoting
+ @default false
+ @type Boolean
+ */
+ set pivoting(value) {
+ this._pivoting = !!value;
+ }
+
+ get pivoting() {
+ return this._pivoting;
+ }
+
+ /**
+ When true, mouse wheel when mouse is over a {{#crossLink "Mesh"}}{{/crossLink}} will zoom
+ the {{#crossLink "Camera"}}{{/crossLink}} towards the hovered point on the Mesh's surface.
+
+ @property panToPointer
+ @default false
+ @type Boolean
+ */
+ set panToPointer(value) {
+ this._panToPointer = !!value;
+ if (this._panToPointer) {
+ this._panToPivot = false;
+ }
+ }
+
+ get panToPointer() {
+ return this._panToPointer;
+ }
+
+ /**
+ When true, mouse wheel when mouse is over a {{#crossLink "Mesh"}}{{/crossLink}} will zoom
+ the {{#crossLink "Camera"}}{{/crossLink}} towards the pivot point.
+
+ @property panToPivot
+ @default false
+ @type Boolean
+ */
+ set panToPivot(value) {
+ this._panToPivot = !!value;
+ if (this._panToPivot) {
+ this._panToPointer = false;
+ }
+ }
+
+ get panToPivot() {
+ return this._panToPivot;
+ }
+
+ /**
+ Indicates whether this CameraControl is in "first person" mode.
+
+ In "first person" mode (disabled by default) the look position rotates about the eye position. Otherwise,
+ the eye rotates about the look.
+
+ @property firstPerson
+ @default false
+ @type Boolean
+ */
+ set firstPerson(value) {
+ this._firstPerson = !!value;
+ }
+
+ get firstPerson() {
+ return this._firstPerson;
+ }
+
+ /**
+ Indicates whether this CameraControl is in "walking" mode.
+
+ When set true, this constrains eye movement to the horizontal X-Z plane. When doing a walkthrough,
+ this is useful to allow us to look upwards or downwards as we move, while keeping us moving in the
+ horizontal plane.
+
+ This only has an effect when also in "first person" mode.
+
+ @property walking
+ @default false
+ @type Boolean
+ */
+ set walking(value) {
+ this._walking = !!value;
+ }
+
+ get walking() {
+ return this._walking;
+ }
+
+ /**
+ * TODO
+ *
+ *
+ * @property doublePickFlyTo
+ * @default true
+ * @type Boolean
+ */
+ set doublePickFlyTo(value) {
+ this._doublePickFlyTo = value !== false;
+ }
+
+ get doublePickFlyTo() {
+ return this._doublePickFlyTo;
+ }
+
+ /**
+ Factor in range [0..1] indicating how much the camera keeps moving after you finish
+ panning or rotating it.
+
+ A value of 0.0 causes it to immediately stop, 0.5 causes its movement to decay 50% on each tick,
+ while 1.0 causes no decay, allowing it continue moving, by the current rate of pan or rotation.
+
+ You may choose an inertia of zero when you want be able to precisely position or rotate the camera,
+ without interference from inertia. ero inertia can also mean that less frames are rendered while
+ you are positioning the camera.
+
+ @property inertia
+ @default 0.5
+ @type Number
+ */
+ set inertia(value) {
+ this._inertia = value === undefined ? 0.5 : value;
+ }
+
+ get inertia() {
+ return this._inertia;
+ }
+
+ /**
+ * TODO
+ *
+ * @property keyboardLayout
+ * @default "qwerty"
+ * @type String
+ */
+ set keyboardLayout(value) {
+ this._keyboardLayout = value || "qwerty";
+ }
+
+ get keyboardLayout() {
+ return this._keyboardLayout;
+ }
+
+ _initEvents() {
+
+ const self = this;
+ const scene = this.scene;
+ const input = scene.input;
+ const camera = scene.camera;
+ const canvas = this.scene.canvas.canvas;
+ let over = false;
+ const mouseHoverDelay = 500;
+ const mouseOrbitRate = 0.4;
+ const mousePanRate = 0.4;
+ const mouseZoomRate = 0.8;
+ const mouseWheelPanRate = 0.4;
+ const keyboardOrbitRate = .02;
+ const keyboardPanRate = .02;
+ const keyboardZoomRate = .02;
+ const touchRotateRate = 0.3;
+ const touchPanRate = 0.2;
+ const touchZoomRate = 0.05;
+
+ canvas.oncontextmenu = function (e) {
+ e.preventDefault();
+ };
+
+ const getCanvasPosFromEvent = function (event, canvasPos) {
+ if (!event) {
+ event = window.event;
+ canvasPos[0] = event.x;
+ canvasPos[1] = event.y;
+ } else {
+ let element = event.target;
+ let totalOffsetLeft = 0;
+ let totalOffsetTop = 0;
+ while (element.offsetParent) {
+ totalOffsetLeft += element.offsetLeft;
+ totalOffsetTop += element.offsetTop;
+ element = element.offsetParent;
}
- if (hit) {
- picked = true;
- const pickedMeshId = hit.mesh.id;
- if (lastPickedMeshId !== pickedMeshId) {
- if (lastPickedMeshId !== undefined) {
-
- /**
- * Fired whenever the pointer no longer hovers over an {{#crossLink "Mesh"}}{{/crossLink}}.
- * @event hoverOut
- * @param mesh The Mesh
- */
- self.fire("hoverOut", {
- mesh: scene.meshes[lastPickedMeshId]
- });
- }
-
- /**
- * Fired when the pointer is over a new {{#crossLink "Mesh"}}{{/crossLink}}.
- * @event hoverEnter
- * @param hit A pick hit result containing the ID of the Mesh - see {{#crossLink "Scene/pick:method"}}Scene#pick(){{/crossLink}}.
- */
- self.fire("hoverEnter", hit);
- lastPickedMeshId = pickedMeshId;
- }
- /**
- * Fired continuously while the pointer is moving while hovering over an {{#crossLink "Mesh"}}{{/crossLink}}.
- * @event hover
- * @param hit A pick hit result containing the ID of the Mesh - see {{#crossLink "Scene/pick:method"}}Scene#pick(){{/crossLink}}.
- */
- self.fire("hover", hit);
- if (hit.worldPos) {
- pickedSurface = true;
-
- /**
- * Fired while the pointer hovers over the surface of an {{#crossLink "Mesh"}}{{/crossLink}}.
- *
- * This event provides 3D information about the point on the surface that the pointer is
- * hovering over.
- *
- * @event hoverSurface
- * @param hit A surface pick hit result, containing the ID of the Mesh and 3D info on the
- * surface position - see {{#crossLink "Scene/pick:method"}}Scene#pick(){{/crossLink}}.
- */
- self.fire("hoverSurface", hit);
- }
- } else {
+ canvasPos[0] = event.pageX - totalOffsetLeft;
+ canvasPos[1] = event.pageY - totalOffsetTop;
+ }
+ return canvasPos;
+ };
+
+ const pickCursorPos = [0, 0];
+ let needPickMesh = false;
+ let needPickSurface = false;
+ let lastPickedMeshId;
+ let hit;
+ let picked = false;
+ let pickedSurface = false;
+
+ function updatePick() {
+ if (!needPickMesh && !needPickSurface) {
+ return;
+ }
+ picked = false;
+ pickedSurface = false;
+ if (needPickSurface || self.hasSubs("hoverSurface")) {
+ hit = scene.pick({
+ pickSurface: true,
+ canvasPos: pickCursorPos
+ });
+ } else { // needPickMesh == true
+ hit = scene.pick({
+ canvasPos: pickCursorPos
+ });
+ }
+ if (hit) {
+ picked = true;
+ const pickedMeshId = hit.mesh.id;
+ if (lastPickedMeshId !== pickedMeshId) {
if (lastPickedMeshId !== undefined) {
+
/**
* Fired whenever the pointer no longer hovers over an {{#crossLink "Mesh"}}{{/crossLink}}.
* @event hoverOut
@@ -563,505 +514,571 @@
self.fire("hoverOut", {
mesh: scene.meshes[lastPickedMeshId]
});
- lastPickedMeshId = undefined;
}
+
/**
- * Fired continuously while the pointer is moving but not hovering over anything.
+ * Fired when the pointer is over a new {{#crossLink "Mesh"}}{{/crossLink}}.
+ * @event hoverEnter
+ * @param hit A pick hit result containing the ID of the Mesh - see {{#crossLink "Scene/pick:method"}}Scene#pick(){{/crossLink}}.
+ */
+ self.fire("hoverEnter", hit);
+ lastPickedMeshId = pickedMeshId;
+ }
+ /**
+ * Fired continuously while the pointer is moving while hovering over an {{#crossLink "Mesh"}}{{/crossLink}}.
+ * @event hover
+ * @param hit A pick hit result containing the ID of the Mesh - see {{#crossLink "Scene/pick:method"}}Scene#pick(){{/crossLink}}.
+ */
+ self.fire("hover", hit);
+ if (hit.worldPos) {
+ pickedSurface = true;
+
+ /**
+ * Fired while the pointer hovers over the surface of an {{#crossLink "Mesh"}}{{/crossLink}}.
*
- * @event hoverOff
+ * This event provides 3D information about the point on the surface that the pointer is
+ * hovering over.
+ *
+ * @event hoverSurface
+ * @param hit A surface pick hit result, containing the ID of the Mesh and 3D info on the
+ * surface position - see {{#crossLink "Scene/pick:method"}}Scene#pick(){{/crossLink}}.
+ */
+ self.fire("hoverSurface", hit);
+ }
+ } else {
+ if (lastPickedMeshId !== undefined) {
+ /**
+ * Fired whenever the pointer no longer hovers over an {{#crossLink "Mesh"}}{{/crossLink}}.
+ * @event hoverOut
+ * @param mesh The Mesh
*/
- self.fire("hoverOff", {
- canvasPos: pickCursorPos
+ self.fire("hoverOut", {
+ mesh: scene.meshes[lastPickedMeshId]
});
+ lastPickedMeshId = undefined;
}
- needPickMesh = false;
- needPickSurface = false;
+ /**
+ * Fired continuously while the pointer is moving but not hovering over anything.
+ *
+ * @event hoverOff
+ */
+ self.fire("hoverOff", {
+ canvasPos: pickCursorPos
+ });
}
+ needPickMesh = false;
+ needPickSurface = false;
+ }
- scene.on("tick", updatePick);
+ scene.on("tick", updatePick);
- //------------------------------------------------------------------------------------
- // Mouse, touch and keyboard camera control
- //------------------------------------------------------------------------------------
+ //------------------------------------------------------------------------------------
+ // Mouse, touch and keyboard camera control
+ //------------------------------------------------------------------------------------
- (function () {
+ (function () {
- let rotateVx = 0;
- let rotateVy = 0;
- let panVx = 0;
- let panVy = 0;
- let panVz = 0;
- let vZoom = 0;
- const mousePos = math.vec2();
- let panToMouse = false;
-
- let ctrlDown = false;
- let altDown = false;
- let shiftDown = false;
- const keyDown = {};
-
- const EPSILON = 0.001;
-
- const getEyeLookDist = (function () {
- const vec = new Float32Array(3);
- return function () {
- return math.lenVec3(math.subVec3(camera.look, camera.eye, vec));
- };
- })();
+ let rotateVx = 0;
+ let rotateVy = 0;
+ let panVx = 0;
+ let panVy = 0;
+ let panVz = 0;
+ let vZoom = 0;
+ const mousePos = math.vec2();
+ let panToMouse = false;
- const getInverseProjectMat = (function () {
- let projMatDirty = true;
- camera.on("projMatrix", function () {
- projMatDirty = true;
- });
- const inverseProjectMat = math.mat4();
- return function () {
- if (projMatDirty) {
- math.inverseMat4(camera.projMatrix, inverseProjectMat);
- }
- return inverseProjectMat;
+ let ctrlDown = false;
+ let altDown = false;
+ let shiftDown = false;
+ const keyDown = {};
+
+ const EPSILON = 0.001;
+
+ const getEyeLookDist = (function () {
+ const vec = new Float32Array(3);
+ return function () {
+ return math.lenVec3(math.subVec3(camera.look, camera.eye, vec));
+ };
+ })();
+
+ const getInverseProjectMat = (function () {
+ let projMatDirty = true;
+ camera.on("projMatrix", function () {
+ projMatDirty = true;
+ });
+ const inverseProjectMat = math.mat4();
+ return function () {
+ if (projMatDirty) {
+ math.inverseMat4(camera.projMatrix, inverseProjectMat);
}
- })();
+ return inverseProjectMat;
+ }
+ })();
- const getTransposedProjectMat = (function () {
- let projMatDirty = true;
- camera.on("projMatrix", function () {
- projMatDirty = true;
- });
- const transposedProjectMat = math.mat4();
- return function () {
- if (projMatDirty) {
- math.transposeMat4(camera.projMatrix, transposedProjectMat);
- }
- return transposedProjectMat;
+ const getTransposedProjectMat = (function () {
+ let projMatDirty = true;
+ camera.on("projMatrix", function () {
+ projMatDirty = true;
+ });
+ const transposedProjectMat = math.mat4();
+ return function () {
+ if (projMatDirty) {
+ math.transposeMat4(camera.projMatrix, transposedProjectMat);
}
- })();
+ return transposedProjectMat;
+ }
+ })();
- const getInverseViewMat = (function () {
- let viewMatDirty = true;
- camera.on("viewMatrix", function () {
- viewMatDirty = true;
- });
- const inverseViewMat = math.mat4();
- return function () {
- if (viewMatDirty) {
- math.inverseMat4(camera.viewMatrix, inverseViewMat);
- }
- return inverseViewMat;
+ const getInverseViewMat = (function () {
+ let viewMatDirty = true;
+ camera.on("viewMatrix", function () {
+ viewMatDirty = true;
+ });
+ const inverseViewMat = math.mat4();
+ return function () {
+ if (viewMatDirty) {
+ math.inverseMat4(camera.viewMatrix, inverseViewMat);
}
- })();
+ return inverseViewMat;
+ }
+ })();
- const getSceneDiagSize = (function () {
- let sceneSizeDirty = true;
- let diag = 1; // Just in case
- scene.on("boundary", function () {
- sceneSizeDirty = true;
- });
- return function () {
- if (sceneSizeDirty) {
- diag = math.getAABB3Diag(scene.aabb);
- }
- return diag;
- };
- })();
+ const getSceneDiagSize = (function () {
+ let sceneSizeDirty = true;
+ let diag = 1; // Just in case
+ scene.on("boundary", function () {
+ sceneSizeDirty = true;
+ });
+ return function () {
+ if (sceneSizeDirty) {
+ diag = math.getAABB3Diag(scene.aabb);
+ }
+ return diag;
+ };
+ })();
- const panToMousePos = (function () {
-
- const cp = math.vec4();
- const viewPos = math.vec4();
- const worldPos = math.vec4();
- const eyeCursorVec = math.vec3();
-
- const unproject = function (inverseProjMat, inverseViewMat, mousePos, z, viewPos, worldPos) {
- const canvas = scene.canvas.canvas;
- const halfCanvasWidth = canvas.offsetWidth / 2.0;
- const halfCanvasHeight = canvas.offsetHeight / 2.0;
- cp[0] = (mousePos[0] - halfCanvasWidth) / halfCanvasWidth;
- cp[1] = (mousePos[1] - halfCanvasHeight) / halfCanvasHeight;
- cp[2] = z;
- cp[3] = 1.0;
- math.mulMat4v4(inverseProjMat, cp, viewPos);
- math.mulVec3Scalar(viewPos, 1.0 / viewPos[3]); // Normalize homogeneous coord
- viewPos[3] = 1.0;
- viewPos[1] *= -1; // TODO: Why is this reversed?
- math.mulMat4v4(inverseViewMat, viewPos, worldPos);
- };
+ const panToMousePos = (function () {
+
+ const cp = math.vec4();
+ const viewPos = math.vec4();
+ const worldPos = math.vec4();
+ const eyeCursorVec = math.vec3();
+
+ const unproject = function (inverseProjMat, inverseViewMat, mousePos, z, viewPos, worldPos) {
+ const canvas = scene.canvas.canvas;
+ const halfCanvasWidth = canvas.offsetWidth / 2.0;
+ const halfCanvasHeight = canvas.offsetHeight / 2.0;
+ cp[0] = (mousePos[0] - halfCanvasWidth) / halfCanvasWidth;
+ cp[1] = (mousePos[1] - halfCanvasHeight) / halfCanvasHeight;
+ cp[2] = z;
+ cp[3] = 1.0;
+ math.mulMat4v4(inverseProjMat, cp, viewPos);
+ math.mulVec3Scalar(viewPos, 1.0 / viewPos[3]); // Normalize homogeneous coord
+ viewPos[3] = 1.0;
+ viewPos[1] *= -1; // TODO: Why is this reversed?
+ math.mulMat4v4(inverseViewMat, viewPos, worldPos);
+ };
- return function (mousePos, factor) {
+ return function (mousePos, factor) {
- const lastHoverDistance = 0;
- const inverseProjMat = getInverseProjectMat();
- const inverseViewMat = getInverseViewMat();
+ const lastHoverDistance = 0;
+ const inverseProjMat = getInverseProjectMat();
+ const inverseViewMat = getInverseViewMat();
- // Get last two columns of projection matrix
- const transposedProjectMat = getTransposedProjectMat();
- const Pt3 = transposedProjectMat.subarray(8, 12);
- const Pt4 = transposedProjectMat.subarray(12);
- const D = [0, 0, -(lastHoverDistance || getSceneDiagSize()), 1];
- const Z = math.dotVec4(D, Pt3) / math.dotVec4(D, Pt4);
+ // Get last two columns of projection matrix
+ const transposedProjectMat = getTransposedProjectMat();
+ const Pt3 = transposedProjectMat.subarray(8, 12);
+ const Pt4 = transposedProjectMat.subarray(12);
+ const D = [0, 0, -(lastHoverDistance || getSceneDiagSize()), 1];
+ const Z = math.dotVec4(D, Pt3) / math.dotVec4(D, Pt4);
- unproject(inverseProjMat, inverseViewMat, mousePos, Z, viewPos, worldPos);
+ unproject(inverseProjMat, inverseViewMat, mousePos, Z, viewPos, worldPos);
- math.subVec3(worldPos, camera.eye, eyeCursorVec);
- math.normalizeVec3(eyeCursorVec);
+ math.subVec3(worldPos, camera.eye, eyeCursorVec);
+ math.normalizeVec3(eyeCursorVec);
- const px = eyeCursorVec[0] * factor;
- const py = eyeCursorVec[1] * factor;
- const pz = eyeCursorVec[2] * factor;
+ const px = eyeCursorVec[0] * factor;
+ const py = eyeCursorVec[1] * factor;
+ const pz = eyeCursorVec[2] * factor;
- const eye = camera.eye;
- const look = camera.look;
+ const eye = camera.eye;
+ const look = camera.look;
- camera.eye = [eye[0] + px, eye[1] + py, eye[2] + pz];
- camera.look = [look[0] + px, look[1] + py, look[2] + pz];
- };
- })();
+ camera.eye = [eye[0] + px, eye[1] + py, eye[2] + pz];
+ camera.look = [look[0] + px, look[1] + py, look[2] + pz];
+ };
+ })();
- const panToWorldPos = (function () {
- const eyeCursorVec = math.vec3();
- return function (worldPos, factor) {
- math.subVec3(worldPos, camera.eye, eyeCursorVec);
- math.normalizeVec3(eyeCursorVec);
- const px = eyeCursorVec[0] * factor;
- const py = eyeCursorVec[1] * factor;
- const pz = eyeCursorVec[2] * factor;
- const eye = camera.eye;
- const look = camera.look;
- camera.eye = [eye[0] + px, eye[1] + py, eye[2] + pz];
- camera.look = [look[0] + px, look[1] + py, look[2] + pz];
- };
- })();
+ const panToWorldPos = (function () {
+ const eyeCursorVec = math.vec3();
+ return function (worldPos, factor) {
+ math.subVec3(worldPos, camera.eye, eyeCursorVec);
+ math.normalizeVec3(eyeCursorVec);
+ const px = eyeCursorVec[0] * factor;
+ const py = eyeCursorVec[1] * factor;
+ const pz = eyeCursorVec[2] * factor;
+ const eye = camera.eye;
+ const look = camera.look;
+ camera.eye = [eye[0] + px, eye[1] + py, eye[2] + pz];
+ camera.look = [look[0] + px, look[1] + py, look[2] + pz];
+ };
+ })();
- scene.on("tick", function () {
+ scene.on("tick", function () {
- const cameraInertia = self._inertia;
+ const cameraInertia = self._inertia;
- if (Math.abs(rotateVx) < EPSILON) {
- rotateVx = 0;
- }
+ if (Math.abs(rotateVx) < EPSILON) {
+ rotateVx = 0;
+ }
- if (Math.abs(rotateVy) < EPSILON) {
- rotateVy = 0;
- }
+ if (Math.abs(rotateVy) < EPSILON) {
+ rotateVy = 0;
+ }
- if (rotateVy !== 0 || rotateVx !== 0) {
+ if (rotateVy !== 0 || rotateVx !== 0) {
- if (self._pivoter.getPivoting()) {
- self._pivoter.continuePivot(rotateVy, rotateVx);
+ if (self._pivoter.getPivoting()) {
+ self._pivoter.continuePivot(rotateVy, rotateVx);
- } else {
+ } else {
- if (rotateVx !== 0) {
+ if (rotateVx !== 0) {
- if (self._firstPerson) {
- camera.pitch(-rotateVx);
+ if (self._firstPerson) {
+ camera.pitch(-rotateVx);
- } else {
- camera.orbitPitch(rotateVx);
- }
+ } else {
+ camera.orbitPitch(rotateVx);
}
+ }
- if (rotateVy !== 0) {
+ if (rotateVy !== 0) {
- if (self._firstPerson) {
- camera.yaw(rotateVy);
+ if (self._firstPerson) {
+ camera.yaw(rotateVy);
- } else {
- camera.orbitYaw(rotateVy);
- }
+ } else {
+ camera.orbitYaw(rotateVy);
}
}
-
- rotateVx *= cameraInertia;
- rotateVy *= cameraInertia;
}
- if (Math.abs(panVx) < EPSILON) {
- panVx = 0;
- }
+ rotateVx *= cameraInertia;
+ rotateVy *= cameraInertia;
+ }
- if (Math.abs(panVy) < EPSILON) {
- panVy = 0;
- }
+ if (Math.abs(panVx) < EPSILON) {
+ panVx = 0;
+ }
+
+ if (Math.abs(panVy) < EPSILON) {
+ panVy = 0;
+ }
+
+ if (Math.abs(panVz) < EPSILON) {
+ panVz = 0;
+ }
- if (Math.abs(panVz) < EPSILON) {
- panVz = 0;
+ if (panVx !== 0 || panVy !== 0 || panVz !== 0) {
+ const f = getEyeLookDist() / 80;
+ if (self._walking) {
+ var y = camera.eye[1];
+ camera.pan([panVx * f, panVy * f, panVz * f]);
+ var eye = camera.eye;
+ eye[1] = y;
+ camera.eye = eye;
+ } else {
+ camera.pan([panVx * f, panVy * f, panVz * f]);
}
+ }
+
+ panVx *= cameraInertia;
+ panVy *= cameraInertia;
+ panVz *= cameraInertia;
- if (panVx !== 0 || panVy !== 0 || panVz !== 0) {
- const f = getEyeLookDist() / 80;
+ if (Math.abs(vZoom) < EPSILON) {
+ vZoom = 0;
+ }
+
+ if (vZoom !== 0) {
+ if (self._firstPerson) {
+ var y;
+ if (self._walking) {
+ y = camera.eye[1];
+ }
+ if (panToMouse) { // Using mouse input
+ panToMousePos(mousePos, -vZoom * 2);
+ } else {
+ camera.pan([0, 0, vZoom]); // Touchscreen input with no cursor
+ }
if (self._walking) {
- var y = camera.eye[1];
- camera.pan([panVx * f, panVy * f, panVz * f]);
var eye = camera.eye;
eye[1] = y;
camera.eye = eye;
- } else {
- camera.pan([panVx * f, panVy * f, panVz * f]);
}
- }
-
- panVx *= cameraInertia;
- panVy *= cameraInertia;
- panVz *= cameraInertia;
-
- if (Math.abs(vZoom) < EPSILON) {
- vZoom = 0;
- }
-
- if (vZoom !== 0) {
- if (self._firstPerson) {
- var y;
- if (self._walking) {
- y = camera.eye[1];
- }
- if (panToMouse) { // Using mouse input
- panToMousePos(mousePos, -vZoom * 2);
- } else {
- camera.pan([0, 0, vZoom]); // Touchscreen input with no cursor
- }
- if (self._walking) {
- var eye = camera.eye;
- eye[1] = y;
- camera.eye = eye;
- }
- } else {
- // Do both zoom and ortho scale so that we can switch projections without weird scale jumps
- if (self._panToPointer) {
- updatePick();
- if (pickedSurface) {
- panToWorldPos(hit.worldPos, -vZoom);
- } else {
- camera.zoom(vZoom);
- }
- } else if (self._panToPivot) {
- panToWorldPos(self._pivoter.getPivotPos(), -vZoom); // FIXME: What about when pivotPos undefined?
+ } else {
+ // Do both zoom and ortho scale so that we can switch projections without weird scale jumps
+ if (self._panToPointer) {
+ updatePick();
+ if (pickedSurface) {
+ panToWorldPos(hit.worldPos, -vZoom);
} else {
camera.zoom(vZoom);
}
- camera.ortho.scale = camera.ortho.scale + vZoom;
+ } else if (self._panToPivot) {
+ panToWorldPos(self._pivoter.getPivotPos(), -vZoom); // FIXME: What about when pivotPos undefined?
+ } else {
+ camera.zoom(vZoom);
}
- vZoom *= cameraInertia;
+ camera.ortho.scale = camera.ortho.scale + vZoom;
}
- });
+ vZoom *= cameraInertia;
+ }
+ });
+
+ function getZoomRate() {
+ const aabb = scene.aabb;
+ const xsize = aabb[3] - aabb[0];
+ const ysize = aabb[4] - aabb[1];
+ const zsize = aabb[5] - aabb[2];
+ let max = (xsize > ysize ? xsize : ysize);
+ max = (zsize > max ? zsize : max);
+ return max / 30;
+ }
- function getZoomRate() {
- const aabb = scene.aabb;
- const xsize = aabb[3] - aabb[0];
- const ysize = aabb[4] - aabb[1];
- const zsize = aabb[5] - aabb[2];
- let max = (xsize > ysize ? xsize : ysize);
- max = (zsize > max ? zsize : max);
- return max / 30;
+ document.addEventListener("keyDown", function (e) {
+ if (!self._active) {
+ return;
+ }
+ if (e.target.tagName !== "INPUT" && e.target.tagName !== "TEXTAREA") {
+ ctrlDown = e.ctrlKey || e.keyCode === 17 || e.metaKey; // !important, treat Windows or Mac Command Key as ctrl
+ altDown = e.altKey || e.keyCode === 18;
+ shiftDown = e.keyCode === 16;
+ keyDown[e.keyCode] = true;
}
+ }, true);
+
+ document.addEventListener("keyup", function (e) {
+ if (!self._active) {
+ return;
+ }
+ if (e.target.tagName !== "INPUT" && e.target.tagName !== "TEXTAREA") {
+ if (e.ctrlKey || e.keyCode === 17) {
+ ctrlDown = false;
+ }
+ if (e.altKey || e.keyCode === 18) {
+ altDown = false;
+ }
+ if (e.keyCode === 16) {
+ shiftDown = false;
+ }
+ keyDown[e.keyCode] = false;
+ }
+ });
+
+ // Mouse camera rotate, pan and zoom
+
+ (function () {
+
+ let lastX;
+ let lastY;
+ let xDelta = 0;
+ let yDelta = 0;
+ let down = false;
- document.addEventListener("keyDown", function (e) {
+ let mouseDownLeft;
+ let mouseDownMiddle;
+ let mouseDownRight;
+
+ canvas.addEventListener("mousedown", function (e) {
if (!self._active) {
return;
}
- if (e.target.tagName !== "INPUT" && e.target.tagName !== "TEXTAREA") {
- ctrlDown = e.ctrlKey || e.keyCode === 17 || e.metaKey; // !important, treat Windows or Mac Command Key as ctrl
- altDown = e.altKey || e.keyCode === 18;
- shiftDown = e.keyCode === 16;
- keyDown[e.keyCode] = true;
+ over = true;
+ switch (e.which) {
+ case 1: // Left button
+ mouseDownLeft = true;
+ down = true;
+ xDelta = 0;
+ yDelta = 0;
+ getCanvasPosFromEvent(e, mousePos);
+ lastX = mousePos[0];
+ lastY = mousePos[1];
+ break;
+ case 2: // Middle/both buttons
+ mouseDownMiddle = true;
+ break;
+ case 3: // Right button
+ mouseDownRight = true;
+ down = true;
+ xDelta = 0;
+ yDelta = 0;
+ getCanvasPosFromEvent(e, mousePos);
+ lastX = mousePos[0];
+ lastY = mousePos[1];
+ break;
+ break;
+ default:
+ break;
}
- }, true);
+ });
- document.addEventListener("keyup", function (e) {
+ canvas.addEventListener("mouseup", function (e) {
if (!self._active) {
return;
}
- if (e.target.tagName !== "INPUT" && e.target.tagName !== "TEXTAREA") {
- if (e.ctrlKey || e.keyCode === 17) {
- ctrlDown = false;
- }
- if (e.altKey || e.keyCode === 18) {
- altDown = false;
- }
- if (e.keyCode === 16) {
- shiftDown = false;
- }
- keyDown[e.keyCode] = false;
+ switch (e.which) {
+ case 1: // Left button
+ mouseDownLeft = false;
+ break;
+ case 2: // Middle/both buttons
+ mouseDownMiddle = false;
+ break;
+ case 3: // Right button
+ mouseDownRight = false;
+ break;
+ default:
+ break;
}
+ down = false;
+ xDelta = 0;
+ yDelta = 0;
});
- // Mouse camera rotate, pan and zoom
-
- (function () {
+ document.addEventListener("mouseup", function (e) {
+ if (!self._active) {
+ return;
+ }
+ switch (e.which) {
+ case 1: // Left button
+ mouseDownLeft = false;
+ break;
+ case 2: // Middle/both buttons
+ mouseDownMiddle = false;
+ break;
+ case 3: // Right button
+ mouseDownRight = false;
+ break;
+ default:
+ break;
+ }
+ down = false;
+ xDelta = 0;
+ yDelta = 0;
+ });
- let lastX;
- let lastY;
- let xDelta = 0;
- let yDelta = 0;
- let down = false;
+ canvas.addEventListener("mouseenter", function () {
+ if (!self._active) {
+ return;
+ }
+ over = true;
+ xDelta = 0;
+ yDelta = 0;
+ });
- let mouseDownLeft;
- let mouseDownMiddle;
- let mouseDownRight;
+ canvas.addEventListener("mouseleave", function () {
+ if (!self._active) {
+ return;
+ }
+ over = false;
+ xDelta = 0;
+ yDelta = 0;
+ });
- canvas.addEventListener("mousedown", function (e) {
- if (!self._active) {
- return;
- }
- over = true;
- switch (e.which) {
- case 1: // Left button
- mouseDownLeft = true;
- down = true;
- xDelta = 0;
- yDelta = 0;
- getCanvasPosFromEvent(e, mousePos);
- lastX = mousePos[0];
- lastY = mousePos[1];
- break;
- case 2: // Middle/both buttons
- mouseDownMiddle = true;
- break;
- case 3: // Right button
- mouseDownRight = true;
- down = true;
- xDelta = 0;
- yDelta = 0;
- getCanvasPosFromEvent(e, mousePos);
- lastX = mousePos[0];
- lastY = mousePos[1];
- break;
- break;
- default:
- break;
- }
- });
+ canvas.addEventListener("mousemove", function (e) {
+ if (!self._active) {
+ return;
+ }
+ if (!over) {
+ return;
+ }
+ getCanvasPosFromEvent(e, mousePos);
+ panToMouse = true;
+ if (!down) {
+ return;
+ }
+ const x = mousePos[0];
+ const y = mousePos[1];
+ xDelta += (x - lastX) * mouseOrbitRate;
+ yDelta += (y - lastY) * mouseOrbitRate;
+ lastX = x;
+ lastY = y;
+ });
- canvas.addEventListener("mouseup", function (e) {
- if (!self._active) {
- return;
- }
- switch (e.which) {
- case 1: // Left button
- mouseDownLeft = false;
- break;
- case 2: // Middle/both buttons
- mouseDownMiddle = false;
- break;
- case 3: // Right button
- mouseDownRight = false;
- break;
- default:
- break;
- }
- down = false;
- xDelta = 0;
- yDelta = 0;
- });
+ scene.on("tick", function () {
+ if (!self._active) {
+ return;
+ }
+ if (Math.abs(xDelta) === 0 && Math.abs(yDelta) === 0) {
+ return;
+ }
- document.addEventListener("mouseup", function (e) {
- if (!self._active) {
- return;
- }
- switch (e.which) {
- case 1: // Left button
- mouseDownLeft = false;
- break;
- case 2: // Middle/both buttons
- mouseDownMiddle = false;
- break;
- case 3: // Right button
- mouseDownRight = false;
- break;
- default:
- break;
- }
- down = false;
- xDelta = 0;
- yDelta = 0;
- });
+ const panning = shiftDown || mouseDownRight;
- canvas.addEventListener("mouseenter", function () {
- if (!self._active) {
- return;
- }
- over = true;
- xDelta = 0;
- yDelta = 0;
- });
+ if (panning) {
- canvas.addEventListener("mouseleave", function () {
- if (!self._active) {
- return;
- }
- over = false;
- xDelta = 0;
- yDelta = 0;
- });
+ // Panning
- canvas.addEventListener("mousemove", function (e) {
- if (!self._active) {
- return;
- }
- if (!over) {
- return;
- }
- getCanvasPosFromEvent(e, mousePos);
- panToMouse = true;
- if (!down) {
- return;
- }
- const x = mousePos[0];
- const y = mousePos[1];
- xDelta += (x - lastX) * mouseOrbitRate;
- yDelta += (y - lastY) * mouseOrbitRate;
- lastX = x;
- lastY = y;
- });
+ panVx = xDelta * mousePanRate;
+ panVy = yDelta * mousePanRate;
- scene.on("tick", function () {
- if (!self._active) {
- return;
- }
- if (Math.abs(xDelta) === 0 && Math.abs(yDelta) === 0) {
- return;
- }
+ } else {
- const panning = shiftDown || mouseDownRight;
+ // Orbiting
- if (panning) {
+ rotateVy = -xDelta * mouseOrbitRate;
+ rotateVx = yDelta * mouseOrbitRate;
+ }
- // Panning
+ xDelta = 0;
+ yDelta = 0;
+ });
- panVx = xDelta * mousePanRate;
- panVy = yDelta * mousePanRate;
+ // Mouse wheel zoom
- } else {
+ canvas.addEventListener("wheel", function (e) {
+ if (!self._active) {
+ return;
+ }
+ if (self._panToPointer) {
+ needPickSurface = true;
+ }
+ const delta = Math.max(-1, Math.min(1, -e.deltaY * 40));
+ if (delta === 0) {
+ return;
+ }
+ const d = delta / Math.abs(delta);
+ vZoom = -d * getZoomRate() * mouseZoomRate;
+ e.preventDefault();
+ });
- // Orbiting
+ // Keyboard zoom
- rotateVy = -xDelta * mouseOrbitRate;
- rotateVx = yDelta * mouseOrbitRate;
+ scene.on("tick", function (e) {
+ if (!self._active) {
+ return;
+ }
+ if (!over) {
+ return;
+ }
+ const elapsed = e.deltaTime;
+ if (!self.ctrlDown && !self.altDown) {
+ const wkey = input.keyDown[input.KEY_ADD];
+ const skey = input.keyDown[input.KEY_SUBTRACT];
+ if (wkey || skey) {
+ if (skey) {
+ vZoom = elapsed * getZoomRate() * keyboardZoomRate;
+ } else if (wkey) {
+ vZoom = -elapsed * getZoomRate() * keyboardZoomRate;
+ }
}
+ }
+ });
- xDelta = 0;
- yDelta = 0;
- });
-
- // Mouse wheel zoom
-
- canvas.addEventListener("wheel", function (e) {
- if (!self._active) {
- return;
- }
- if (self._panToPointer) {
- needPickSurface = true;
- }
- const delta = Math.max(-1, Math.min(1, -e.deltaY * 40));
- if (delta === 0) {
- return;
- }
- const d = delta / Math.abs(delta);
- vZoom = -d * getZoomRate() * mouseZoomRate;
- e.preventDefault();
- });
+ // Keyboard panning
- // Keyboard zoom
+ (function () {
scene.on("tick", function (e) {
if (!self._active) {
@@ -1070,737 +1087,717 @@
if (!over) {
return;
}
+
const elapsed = e.deltaTime;
- if (!self.ctrlDown && !self.altDown) {
- const wkey = input.keyDown[input.KEY_ADD];
- const skey = input.keyDown[input.KEY_SUBTRACT];
- if (wkey || skey) {
- if (skey) {
- vZoom = elapsed * getZoomRate() * keyboardZoomRate;
- } else if (wkey) {
- vZoom = -elapsed * getZoomRate() * keyboardZoomRate;
- }
+
+ // if (!self.ctrlDown && !self.altDown) {
+ let front, back, left, right, up, down;
+ if (self._keyboardLayout == 'azerty') {
+ front = input.keyDown[input.KEY_Z];
+ back = input.keyDown[input.KEY_S];
+ left = input.keyDown[input.KEY_Q];
+ right = input.keyDown[input.KEY_D];
+ up = input.keyDown[input.KEY_W];
+ down = input.keyDown[input.KEY_X];
+ } else {
+ front = input.keyDown[input.KEY_W];
+ back = input.keyDown[input.KEY_S];
+ left = input.keyDown[input.KEY_A];
+ right = input.keyDown[input.KEY_D];
+ up = input.keyDown[input.KEY_Z];
+ down = input.keyDown[input.KEY_X];
+ }
+ if (front || back || left || right || up || down) {
+ if (down) {
+ panVy += elapsed * keyboardPanRate;
+ } else if (up) {
+ panVy -= -elapsed * keyboardPanRate;
+ }
+ if (right) {
+ panVx += -elapsed * keyboardPanRate;
+ } else if (left) {
+ panVx = elapsed * keyboardPanRate;
+ }
+ if (back) {
+ panVz = elapsed * keyboardPanRate;
+ } else if (front) {
+ panVz = -elapsed * keyboardPanRate;
}
}
+ // }
});
+ })();
+ })();
- // Keyboard panning
+ // Touch camera rotate, pan and zoom
- (function () {
+ (function () {
- scene.on("tick", function (e) {
- if (!self._active) {
- return;
- }
- if (!over) {
- return;
- }
+ let touchStartTime;
+ const tapStartPos = new Float32Array(2);
+ let tapStartTime = -1;
- const elapsed = e.deltaTime;
-
- // if (!self.ctrlDown && !self.altDown) {
- let front, back, left, right, up, down;
- if (self._keyboardLayout == 'azerty') {
- front = input.keyDown[input.KEY_Z];
- back = input.keyDown[input.KEY_S];
- left = input.keyDown[input.KEY_Q];
- right = input.keyDown[input.KEY_D];
- up = input.keyDown[input.KEY_W];
- down = input.keyDown[input.KEY_X];
- } else {
- front = input.keyDown[input.KEY_W];
- back = input.keyDown[input.KEY_S];
- left = input.keyDown[input.KEY_A];
- right = input.keyDown[input.KEY_D];
- up = input.keyDown[input.KEY_Z];
- down = input.keyDown[input.KEY_X];
- }
- if (front || back || left || right || up || down) {
- if (down) {
- panVy += elapsed * keyboardPanRate;
- } else if (up) {
- panVy -= -elapsed * keyboardPanRate;
- }
- if (right) {
- panVx += -elapsed * keyboardPanRate;
- } else if (left) {
- panVx = elapsed * keyboardPanRate;
- }
- if (back) {
- panVz = elapsed * keyboardPanRate;
- } else if (front) {
- panVz = -elapsed * keyboardPanRate;
- }
- }
- // }
- });
- })();
- })();
+ const lastTouches = [];
+ let numTouches = 0;
- // Touch camera rotate, pan and zoom
+ const touch0Vec = new Float32Array(2);
+ const touch1Vec = new Float32Array(2);
- (function () {
+ const MODE_CHANGE_TIMEOUT = 50;
+ const MODE_NONE = 0;
+ const MODE_ROTATE = 1;
+ const MODE_PAN = 1 << 1;
+ const MODE_ZOOM = 1 << 2;
+ let currentMode = MODE_NONE;
+ let transitionTime = Date.now();
- let touchStartTime;
- const tapStartPos = new Float32Array(2);
- let tapStartTime = -1;
-
- const lastTouches = [];
- let numTouches = 0;
-
- const touch0Vec = new Float32Array(2);
- const touch1Vec = new Float32Array(2);
-
- const MODE_CHANGE_TIMEOUT = 50;
- const MODE_NONE = 0;
- const MODE_ROTATE = 1;
- const MODE_PAN = 1 << 1;
- const MODE_ZOOM = 1 << 2;
- let currentMode = MODE_NONE;
- let transitionTime = Date.now();
-
- function checkMode(mode) {
- const currentTime = Date.now();
- if (currentMode === MODE_NONE) {
- currentMode = mode;
- return true;
- }
- if (currentMode === mode) {
- return currentTime - transitionTime > MODE_CHANGE_TIMEOUT;
- }
+ function checkMode(mode) {
+ const currentTime = Date.now();
+ if (currentMode === MODE_NONE) {
currentMode = mode;
- transitionTime = currentTime;
- return false;
+ return true;
}
+ if (currentMode === mode) {
+ return currentTime - transitionTime > MODE_CHANGE_TIMEOUT;
+ }
+ currentMode = mode;
+ transitionTime = currentTime;
+ return false;
+ }
- canvas.addEventListener("touchstart", function (event) {
- if (!self._active) {
- return;
- }
- const touches = event.touches;
- const changedTouches = event.changedTouches;
+ canvas.addEventListener("touchstart", function (event) {
+ if (!self._active) {
+ return;
+ }
+ const touches = event.touches;
+ const changedTouches = event.changedTouches;
- touchStartTime = Date.now();
+ touchStartTime = Date.now();
- if (touches.length === 1 && changedTouches.length === 1) {
- tapStartTime = touchStartTime;
- tapStartPos[0] = touches[0].pageX;
- tapStartPos[1] = touches[0].pageY;
- } else {
- tapStartTime = -1;
- }
+ if (touches.length === 1 && changedTouches.length === 1) {
+ tapStartTime = touchStartTime;
+ tapStartPos[0] = touches[0].pageX;
+ tapStartPos[1] = touches[0].pageY;
+ } else {
+ tapStartTime = -1;
+ }
- while (lastTouches.length < touches.length) {
- lastTouches.push(new Float32Array(2));
- }
+ while (lastTouches.length < touches.length) {
+ lastTouches.push(new Float32Array(2));
+ }
- for (let i = 0, len = touches.length; i < len; ++i) {
- lastTouches[i][0] = touches[i].pageX;
- lastTouches[i][1] = touches[i].pageY;
- }
+ for (let i = 0, len = touches.length; i < len; ++i) {
+ lastTouches[i][0] = touches[i].pageX;
+ lastTouches[i][1] = touches[i].pageY;
+ }
+
+ currentMode = MODE_NONE;
+ numTouches = touches.length;
+
+ event.stopPropagation();
+ }, {passive: true});
+
+ canvas.addEventListener("touchmove", function (event) {
+ if (!self._active) {
+ return;
+ }
+ const touches = event.touches;
- currentMode = MODE_NONE;
- numTouches = touches.length;
+ if (numTouches === 1) {
- event.stopPropagation();
- }, {passive: true});
+ var touch0 = touches[0];
- canvas.addEventListener("touchmove", function (event) {
- if (!self._active) {
- return;
+ if (checkMode(MODE_ROTATE)) {
+ const deltaX = touch0.pageX - lastTouches[0][0];
+ const deltaY = touch0.pageY - lastTouches[0][1];
+ const rotateX = deltaX * touchRotateRate;
+ const rotateY = deltaY * touchRotateRate;
+ rotateVx = rotateY;
+ rotateVy = -rotateX;
}
- const touches = event.touches;
-
- if (numTouches === 1) {
- var touch0 = touches[0];
+ } else if (numTouches === 2) {
- if (checkMode(MODE_ROTATE)) {
- const deltaX = touch0.pageX - lastTouches[0][0];
- const deltaY = touch0.pageY - lastTouches[0][1];
- const rotateX = deltaX * touchRotateRate;
- const rotateY = deltaY * touchRotateRate;
- rotateVx = rotateY;
- rotateVy = -rotateX;
- }
+ var touch0 = touches[0];
+ const touch1 = touches[1];
- } else if (numTouches === 2) {
+ math.subVec2([touch0.pageX, touch0.pageY], lastTouches[0], touch0Vec);
+ math.subVec2([touch1.pageX, touch1.pageY], lastTouches[1], touch1Vec);
- var touch0 = touches[0];
- const touch1 = touches[1];
+ const panning = math.dotVec2(touch0Vec, touch1Vec) > 0;
+ if (panning && checkMode(MODE_PAN)) {
math.subVec2([touch0.pageX, touch0.pageY], lastTouches[0], touch0Vec);
- math.subVec2([touch1.pageX, touch1.pageY], lastTouches[1], touch1Vec);
-
- const panning = math.dotVec2(touch0Vec, touch1Vec) > 0;
-
- if (panning && checkMode(MODE_PAN)) {
- math.subVec2([touch0.pageX, touch0.pageY], lastTouches[0], touch0Vec);
- panVx = touch0Vec[0] * touchPanRate;
- panVy = touch0Vec[1] * touchPanRate;
- }
-
- if (!panning && checkMode(MODE_ZOOM)) {
- const d1 = math.distVec2([touch0.pageX, touch0.pageY], [touch1.pageX, touch1.pageY]);
- const d2 = math.distVec2(lastTouches[0], lastTouches[1]);
- vZoom = (d2 - d1) * getZoomRate() * touchZoomRate;
- }
+ panVx = touch0Vec[0] * touchPanRate;
+ panVy = touch0Vec[1] * touchPanRate;
}
- for (let i = 0; i < numTouches; ++i) {
- lastTouches[i][0] = touches[i].pageX;
- lastTouches[i][1] = touches[i].pageY;
+ if (!panning && checkMode(MODE_ZOOM)) {
+ const d1 = math.distVec2([touch0.pageX, touch0.pageY], [touch1.pageX, touch1.pageY]);
+ const d2 = math.distVec2(lastTouches[0], lastTouches[1]);
+ vZoom = (d2 - d1) * getZoomRate() * touchZoomRate;
}
+ }
- event.stopPropagation();
- }, {passive: true});
+ for (let i = 0; i < numTouches; ++i) {
+ lastTouches[i][0] = touches[i].pageX;
+ lastTouches[i][1] = touches[i].pageY;
+ }
- })();
+ event.stopPropagation();
+ }, {passive: true});
- // Keyboard rotation
+ })();
- (function () {
+ // Keyboard rotation
- scene.on("tick", function (e) {
- if (!self._active) {
- return;
- }
- if (!over) {
- return;
- }
- const elapsed = e.deltaTime;
- const left = input.keyDown[input.KEY_LEFT_ARROW];
- const right = input.keyDown[input.KEY_RIGHT_ARROW];
- const up = input.keyDown[input.KEY_UP_ARROW];
- const down = input.keyDown[input.KEY_DOWN_ARROW];
- if (left || right || up || down) {
- if (right) {
- rotateVy += -elapsed * keyboardOrbitRate;
+ (function () {
- } else if (left) {
- rotateVy += elapsed * keyboardOrbitRate;
- }
- if (down) {
- rotateVx += elapsed * keyboardOrbitRate;
+ scene.on("tick", function (e) {
+ if (!self._active) {
+ return;
+ }
+ if (!over) {
+ return;
+ }
+ const elapsed = e.deltaTime;
+ const left = input.keyDown[input.KEY_LEFT_ARROW];
+ const right = input.keyDown[input.KEY_RIGHT_ARROW];
+ const up = input.keyDown[input.KEY_UP_ARROW];
+ const down = input.keyDown[input.KEY_DOWN_ARROW];
+ if (left || right || up || down) {
+ if (right) {
+ rotateVy += -elapsed * keyboardOrbitRate;
+
+ } else if (left) {
+ rotateVy += elapsed * keyboardOrbitRate;
+ }
+ if (down) {
+ rotateVx += elapsed * keyboardOrbitRate;
- } else if (up) {
- rotateVx += -elapsed * keyboardOrbitRate;
- }
+ } else if (up) {
+ rotateVx += -elapsed * keyboardOrbitRate;
}
- });
- })();
+ }
+ });
+ })();
- // First-person rotation about vertical axis with A and E keys for AZERTY layout
+ // First-person rotation about vertical axis with A and E keys for AZERTY layout
- (function () {
+ (function () {
- scene.on("tick", function (e) {
- if (!self._active) {
- return;
- }
- if (!over) {
- return;
- }
- const elapsed = e.deltaTime;
- let rotateLeft;
- let rotateRight;
- if (self._keyboardLayout == 'azerty') {
- rotateLeft = input.keyDown[input.KEY_A];
- rotateRight = input.keyDown[input.KEY_E];
- } else {
- rotateLeft = input.keyDown[input.KEY_Q];
- rotateRight = input.keyDown[input.KEY_E];
- }
- if (rotateRight || rotateLeft) {
- if (rotateLeft) {
- rotateVy += elapsed * keyboardOrbitRate;
- } else if (rotateRight) {
- rotateVy += -elapsed * keyboardOrbitRate;
- }
+ scene.on("tick", function (e) {
+ if (!self._active) {
+ return;
+ }
+ if (!over) {
+ return;
+ }
+ const elapsed = e.deltaTime;
+ let rotateLeft;
+ let rotateRight;
+ if (self._keyboardLayout == 'azerty') {
+ rotateLeft = input.keyDown[input.KEY_A];
+ rotateRight = input.keyDown[input.KEY_E];
+ } else {
+ rotateLeft = input.keyDown[input.KEY_Q];
+ rotateRight = input.keyDown[input.KEY_E];
+ }
+ if (rotateRight || rotateLeft) {
+ if (rotateLeft) {
+ rotateVy += elapsed * keyboardOrbitRate;
+ } else if (rotateRight) {
+ rotateVy += -elapsed * keyboardOrbitRate;
}
- });
+ }
+ });
- })();
})();
+ })();
- //------------------------------------------------------------------------------------
- // Mouse and touch picking
- //------------------------------------------------------------------------------------
+ //------------------------------------------------------------------------------------
+ // Mouse and touch picking
+ //------------------------------------------------------------------------------------
+
+ (function () {
+
+ // Mouse picking
(function () {
- // Mouse picking
+ canvas.addEventListener("mousemove", function (e) {
- (function () {
+ if (!self._active) {
+ return;
+ }
- canvas.addEventListener("mousemove", function (e) {
+ getCanvasPosFromEvent(e, pickCursorPos);
- if (!self._active) {
- return;
- }
+ if (self.hasSubs("hover") || self.hasSubs("hoverOut") || self.hasSubs("hoverOff") || self.hasSubs("hoverSurface")) {
+ needPickMesh = true;
+ }
+ });
- getCanvasPosFromEvent(e, pickCursorPos);
+ let downX;
+ let downY;
+ let downCursorX;
+ let downCursorY;
- if (self.hasSubs("hover") || self.hasSubs("hoverOut") || self.hasSubs("hoverOff") || self.hasSubs("hoverSurface")) {
- needPickMesh = true;
+ canvas.addEventListener('mousedown', function (e) {
+ if (!self._active) {
+ return;
+ }
+ downX = e.clientX;
+ downY = e.clientY;
+ downCursorX = pickCursorPos[0];
+ downCursorY = pickCursorPos[1];
+
+ needPickSurface = self._pivoting;
+ updatePick();
+ if (self._pivoting) {
+ if (hit) {
+ self._pivoter.startPivot(hit.worldPos);
+ } else {
+ self._pivoter.startPivot(); // Continue to use last pivot point
}
- });
+ }
+ });
+
+ canvas.addEventListener('mouseup', (function (e) {
+
+ let clicks = 0;
+ let timeout;
- let downX;
- let downY;
- let downCursorX;
- let downCursorY;
+ return function (e) {
- canvas.addEventListener('mousedown', function (e) {
if (!self._active) {
return;
}
- downX = e.clientX;
- downY = e.clientY;
- downCursorX = pickCursorPos[0];
- downCursorY = pickCursorPos[1];
-
- needPickSurface = self._pivoting;
- updatePick();
- if (self._pivoting) {
- if (hit) {
- self._pivoter.startPivot(hit.worldPos);
- } else {
- self._pivoter.startPivot(); // Continue to use last pivot point
- }
- }
- });
-
- canvas.addEventListener('mouseup', (function (e) {
-
- let clicks = 0;
- let timeout;
-
- return function (e) {
- if (!self._active) {
- return;
- }
+ self._pivoter.endPivot();
- self._pivoter.endPivot();
+ if (Math.abs(e.clientX - downX) > 3 || Math.abs(e.clientY - downY) > 3) {
+ return;
+ }
- if (Math.abs(e.clientX - downX) > 3 || Math.abs(e.clientY - downY) > 3) {
- return;
- }
+ if (!self._doublePickFlyTo && !self.hasSubs("doublePicked") && !self.hasSubs("doublePickedSurface") && !self.hasSubs("doublePickedNothing")) {
- if (!self._doublePickFlyTo && !self.hasSubs("doublePicked") && !self.hasSubs("doublePickedSurface") && !self.hasSubs("doublePickedNothing")) {
+ // Avoid the single/double click differentiation timeout
- // Avoid the single/double click differentiation timeout
+ needPickSurface = !!self.hasSubs("pickedSurface");
- needPickSurface = !!self.hasSubs("pickedSurface");
+ updatePick();
- updatePick();
+ if (hit) {
- if (hit) {
+ /**
+ * Fired whenever the pointer has picked (ie. clicked or tapped) an {{#crossLink "Mesh"}}{{/crossLink}}.
+ *
+ * @event picked
+ * @param hit A surface pick hit result containing the ID of the Mesh - see {{#crossLink "Scene/pick:method"}}Scene#pick(){{/crossLink}}.
+ */
+ self.fire("picked", hit);
+ if (pickedSurface) {
/**
- * Fired whenever the pointer has picked (ie. clicked or tapped) an {{#crossLink "Mesh"}}{{/crossLink}}.
+ * Fired when the pointer has picked (ie. clicked or tapped) the surface of an {{#crossLink "Mesh"}}{{/crossLink}}.
*
- * @event picked
- * @param hit A surface pick hit result containing the ID of the Mesh - see {{#crossLink "Scene/pick:method"}}Scene#pick(){{/crossLink}}.
- */
- self.fire("picked", hit);
- if (pickedSurface) {
-
- /**
- * Fired when the pointer has picked (ie. clicked or tapped) the surface of an {{#crossLink "Mesh"}}{{/crossLink}}.
- *
- * This event provides 3D information about the point on the surface that the pointer has picked.
- *
- * @event pickedSurface
- * @param hit A surface pick hit result, containing the ID of the Mesh and 3D info on the
- * surface possition - see {{#crossLink "Scene/pick:method"}}Scene#pick(){{/crossLink}}.
- */
- self.fire("pickedSurface", hit);
- }
- } else {
-
- /**
- * Fired when the pointer attempted a pick (ie. clicked or tapped), but has hit nothing.
+ * This event provides 3D information about the point on the surface that the pointer has picked.
*
- * @event pickedNothing
+ * @event pickedSurface
+ * @param hit A surface pick hit result, containing the ID of the Mesh and 3D info on the
+ * surface possition - see {{#crossLink "Scene/pick:method"}}Scene#pick(){{/crossLink}}.
*/
- self.fire("pickedNothing");
+ self.fire("pickedSurface", hit);
}
+ } else {
- return;
+ /**
+ * Fired when the pointer attempted a pick (ie. clicked or tapped), but has hit nothing.
+ *
+ * @event pickedNothing
+ */
+ self.fire("pickedNothing");
}
- clicks++;
-
- if (clicks == 1) {
- timeout = setTimeout(function () {
-
- needPickMesh = self._doublePickFlyTo;
- needPickSurface = needPickMesh || !!self.hasSubs("pickedSurface");
- pickCursorPos[0] = downCursorX;
- pickCursorPos[1] = downCursorY;
-
- updatePick();
-
- if (hit) {
- self.fire("picked", hit);
- if (pickedSurface) {
- self.fire("pickedSurface", hit);
- }
- } else {
- self.fire("pickedNothing");
- }
-
- clicks = 0;
- }, 250); // FIXME: Too short for track pads
+ return;
+ }
- } else {
+ clicks++;
- clearTimeout(timeout);
+ if (clicks == 1) {
+ timeout = setTimeout(function () {
needPickMesh = self._doublePickFlyTo;
- needPickSurface = needPickMesh && !!self.hasSubs("doublePickedSurface");
+ needPickSurface = needPickMesh || !!self.hasSubs("pickedSurface");
+ pickCursorPos[0] = downCursorX;
+ pickCursorPos[1] = downCursorY;
updatePick();
if (hit) {
- /**
- * Fired whenever the pointer has double-picked (ie. double-clicked or double-tapped) an {{#crossLink "Mesh"}}{{/crossLink}}.
- *
- * @event picked
- * @param hit A surface pick hit result containing the ID of the Mesh - see {{#crossLink "Scene/pick:method"}}Scene#pick(){{/crossLink}}.
- */
- self.fire("doublePicked", hit);
+ self.fire("picked", hit);
if (pickedSurface) {
- /**
- * Fired when the pointer has double-picked (ie. double-clicked or double-tapped) the surface of an {{#crossLink "Mesh"}}{{/crossLink}}.
- *
- * This event provides 3D information about the point on the surface that the pointer has picked.
- *
- * @event doublePickedSurface
- * @param hit A surface pick hit result, containing the ID of the Mesh and 3D info on the
- * surface possition - see {{#crossLink "Scene/pick:method"}}Scene#pick(){{/crossLink}}.
- */
- self.fire("doublePickedSurface", hit);
- }
- if (self._doublePickFlyTo) {
- self._flyTo(hit);
+ self.fire("pickedSurface", hit);
}
} else {
+ self.fire("pickedNothing");
+ }
+
+ clicks = 0;
+ }, 250); // FIXME: Too short for track pads
+
+ } else {
+
+ clearTimeout(timeout);
+
+ needPickMesh = self._doublePickFlyTo;
+ needPickSurface = needPickMesh && !!self.hasSubs("doublePickedSurface");
+
+ updatePick();
+ if (hit) {
+ /**
+ * Fired whenever the pointer has double-picked (ie. double-clicked or double-tapped) an {{#crossLink "Mesh"}}{{/crossLink}}.
+ *
+ * @event picked
+ * @param hit A surface pick hit result containing the ID of the Mesh - see {{#crossLink "Scene/pick:method"}}Scene#pick(){{/crossLink}}.
+ */
+ self.fire("doublePicked", hit);
+ if (pickedSurface) {
/**
- * Fired when the pointer attempted a double-pick (ie. double-clicked or double-tapped), but has hit nothing.
+ * Fired when the pointer has double-picked (ie. double-clicked or double-tapped) the surface of an {{#crossLink "Mesh"}}{{/crossLink}}.
*
- * @event doublePickedNothing
+ * This event provides 3D information about the point on the surface that the pointer has picked.
+ *
+ * @event doublePickedSurface
+ * @param hit A surface pick hit result, containing the ID of the Mesh and 3D info on the
+ * surface possition - see {{#crossLink "Scene/pick:method"}}Scene#pick(){{/crossLink}}.
*/
- self.fire("doublePickedNothing");
- if (self._doublePickFlyTo) {
- self._flyTo();
- }
+ self.fire("doublePickedSurface", hit);
+ }
+ if (self._doublePickFlyTo) {
+ self._flyTo(hit);
+ }
+ } else {
+
+ /**
+ * Fired when the pointer attempted a double-pick (ie. double-clicked or double-tapped), but has hit nothing.
+ *
+ * @event doublePickedNothing
+ */
+ self.fire("doublePickedNothing");
+ if (self._doublePickFlyTo) {
+ self._flyTo();
}
- clicks = 0;
}
- };
- })(), false);
+ clicks = 0;
+ }
+ };
+ })(), false);
- })();
+ })();
- // Touch picking
+ // Touch picking
- (function () {
+ (function () {
- const TAP_INTERVAL = 150;
- const DBL_TAP_INTERVAL = 325;
- const TAP_DISTANCE_THRESHOLD = 4;
+ const TAP_INTERVAL = 150;
+ const DBL_TAP_INTERVAL = 325;
+ const TAP_DISTANCE_THRESHOLD = 4;
- let touchStartTime;
- const activeTouches = [];
- const tapStartPos = new Float32Array(2);
- let tapStartTime = -1;
- let lastTapTime = -1;
+ let touchStartTime;
+ const activeTouches = [];
+ const tapStartPos = new Float32Array(2);
+ let tapStartTime = -1;
+ let lastTapTime = -1;
- canvas.addEventListener("touchstart", function (event) {
+ canvas.addEventListener("touchstart", function (event) {
- if (!self._active) {
- return;
- }
+ if (!self._active) {
+ return;
+ }
- const touches = event.touches;
- const changedTouches = event.changedTouches;
+ const touches = event.touches;
+ const changedTouches = event.changedTouches;
- touchStartTime = Date.now();
+ touchStartTime = Date.now();
- if (touches.length === 1 && changedTouches.length === 1) {
- tapStartTime = touchStartTime;
- tapStartPos[0] = touches[0].pageX;
- tapStartPos[1] = touches[0].pageY;
- } else {
- tapStartTime = -1;
- }
+ if (touches.length === 1 && changedTouches.length === 1) {
+ tapStartTime = touchStartTime;
+ tapStartPos[0] = touches[0].pageX;
+ tapStartPos[1] = touches[0].pageY;
+ } else {
+ tapStartTime = -1;
+ }
- while (activeTouches.length < touches.length) {
- activeTouches.push(new Float32Array(2))
- }
+ while (activeTouches.length < touches.length) {
+ activeTouches.push(new Float32Array(2))
+ }
- for (let i = 0, len = touches.length; i < len; ++i) {
- activeTouches[i][0] = touches[i].pageX;
- activeTouches[i][1] = touches[i].pageY;
- }
+ for (let i = 0, len = touches.length; i < len; ++i) {
+ activeTouches[i][0] = touches[i].pageX;
+ activeTouches[i][1] = touches[i].pageY;
+ }
- activeTouches.length = touches.length;
+ activeTouches.length = touches.length;
- event.stopPropagation();
- }, {passive: true});
+ event.stopPropagation();
+ }, {passive: true});
- //canvas.addEventListener("touchmove", function (event) {
- // event.preventDefault();
- // event.stopPropagation();
- //});
+ //canvas.addEventListener("touchmove", function (event) {
+ // event.preventDefault();
+ // event.stopPropagation();
+ //});
- canvas.addEventListener("touchend", function (event) {
+ canvas.addEventListener("touchend", function (event) {
- if (!self._active) {
- return;
- }
+ if (!self._active) {
+ return;
+ }
- const currentTime = Date.now();
- const touches = event.touches;
- const changedTouches = event.changedTouches;
+ const currentTime = Date.now();
+ const touches = event.touches;
+ const changedTouches = event.changedTouches;
- // process tap
+ // process tap
- if (touches.length === 0 && changedTouches.length === 1) {
+ if (touches.length === 0 && changedTouches.length === 1) {
- if (tapStartTime > -1 && currentTime - tapStartTime < TAP_INTERVAL) {
+ if (tapStartTime > -1 && currentTime - tapStartTime < TAP_INTERVAL) {
- if (lastTapTime > -1 && tapStartTime - lastTapTime < DBL_TAP_INTERVAL) {
+ if (lastTapTime > -1 && tapStartTime - lastTapTime < DBL_TAP_INTERVAL) {
- // Double-tap
+ // Double-tap
- pickCursorPos[0] = Math.round(changedTouches[0].clientX);
- pickCursorPos[1] = Math.round(changedTouches[0].clientY);
- needPickMesh = true;
- needPickSurface = !!self.hasSubs("pickedSurface");
+ pickCursorPos[0] = Math.round(changedTouches[0].clientX);
+ pickCursorPos[1] = Math.round(changedTouches[0].clientY);
+ needPickMesh = true;
+ needPickSurface = !!self.hasSubs("pickedSurface");
- updatePick();
+ updatePick();
- if (hit) {
- self.fire("doublePicked", hit);
- if (pickedSurface) {
- self.fire("doublePickedSurface", hit);
- }
- if (self._doublePickFlyTo) {
- self._flyTo(hit);
- }
- } else {
- self.fire("doublePickedNothing");
- if (self._doublePickFlyTo) {
- self._flyTo();
- }
+ if (hit) {
+ self.fire("doublePicked", hit);
+ if (pickedSurface) {
+ self.fire("doublePickedSurface", hit);
+ }
+ if (self._doublePickFlyTo) {
+ self._flyTo(hit);
+ }
+ } else {
+ self.fire("doublePickedNothing");
+ if (self._doublePickFlyTo) {
+ self._flyTo();
}
+ }
- lastTapTime = -1;
+ lastTapTime = -1;
- } else if (xeogl.math.distVec2(activeTouches[0], tapStartPos) < TAP_DISTANCE_THRESHOLD) {
+ } else if (math.distVec2(activeTouches[0], tapStartPos) < TAP_DISTANCE_THRESHOLD) {
- // Single-tap
+ // Single-tap
- pickCursorPos[0] = Math.round(changedTouches[0].clientX);
- pickCursorPos[1] = Math.round(changedTouches[0].clientY);
- needPickMesh = true;
- needPickSurface = !!self.hasSubs("pickedSurface");
+ pickCursorPos[0] = Math.round(changedTouches[0].clientX);
+ pickCursorPos[1] = Math.round(changedTouches[0].clientY);
+ needPickMesh = true;
+ needPickSurface = !!self.hasSubs("pickedSurface");
- updatePick();
+ updatePick();
- if (hit) {
- self.fire("picked", hit);
- if (pickedSurface) {
- self.fire("pickedSurface", hit);
- }
- } else {
- self.fire("pickedNothing");
+ if (hit) {
+ self.fire("picked", hit);
+ if (pickedSurface) {
+ self.fire("pickedSurface", hit);
}
-
- lastTapTime = currentTime;
+ } else {
+ self.fire("pickedNothing");
}
- tapStartTime = -1
+ lastTapTime = currentTime;
}
- }
-
- activeTouches.length = touches.length;
- for (let i = 0, len = touches.length; i < len; ++i) {
- activeTouches[i][0] = touches[i].pageX;
- activeTouches[i][1] = touches[i].pageY;
+ tapStartTime = -1
}
+ }
- event.stopPropagation();
- }, {passive: true});
- })();
- })();
+ activeTouches.length = touches.length;
- //------------------------------------------------------------------------------------
- // Keyboard camera axis views
- //------------------------------------------------------------------------------------
+ for (let i = 0, len = touches.length; i < len; ++i) {
+ activeTouches[i][0] = touches[i].pageX;
+ activeTouches[i][1] = touches[i].pageY;
+ }
- (function () {
+ event.stopPropagation();
+ }, {passive: true});
+ })();
+ })();
+
+ //------------------------------------------------------------------------------------
+ // Keyboard camera axis views
+ //------------------------------------------------------------------------------------
+
+ (function () {
+
+ const KEY_NUM_1 = 49;
+ const KEY_NUM_2 = 50;
+ const KEY_NUM_3 = 51;
+ const KEY_NUM_4 = 52;
+ const KEY_NUM_5 = 53;
+ const KEY_NUM_6 = 54;
+
+ const center = new math.vec3();
+ const tempVec3a = new math.vec3();
+ const tempVec3b = new math.vec3();
+ const tempVec3c = new math.vec3();
+
+ const cameraTarget = {
+ eye: new Float32Array(3),
+ look: new Float32Array(3),
+ up: new Float32Array(3)
+ };
- const KEY_NUM_1 = 49;
- const KEY_NUM_2 = 50;
- const KEY_NUM_3 = 51;
- const KEY_NUM_4 = 52;
- const KEY_NUM_5 = 53;
- const KEY_NUM_6 = 54;
-
- const center = new math.vec3();
- const tempVec3a = new math.vec3();
- const tempVec3b = new math.vec3();
- const tempVec3c = new math.vec3();
-
- const cameraTarget = {
- eye: new Float32Array(3),
- look: new Float32Array(3),
- up: new Float32Array(3)
- };
+ document.addEventListener("keydown", function (e) {
- document.addEventListener("keydown", function (e) {
+ if (!self._active) {
+ return;
+ }
- if (!self._active) {
- return;
- }
+ if (!over) {
+ return;
+ }
- if (!over) {
- return;
- }
+ const keyCode = e.keyCode;
- const keyCode = e.keyCode;
+ if (keyCode !== KEY_NUM_1 &&
+ keyCode !== KEY_NUM_2 &&
+ keyCode !== KEY_NUM_3 &&
+ keyCode !== KEY_NUM_4 &&
+ keyCode !== KEY_NUM_5 &&
+ keyCode !== KEY_NUM_6) {
+ return;
+ }
- if (keyCode !== KEY_NUM_1 &&
- keyCode !== KEY_NUM_2 &&
- keyCode !== KEY_NUM_3 &&
- keyCode !== KEY_NUM_4 &&
- keyCode !== KEY_NUM_5 &&
- keyCode !== KEY_NUM_6) {
- return;
- }
+ const aabb = scene.aabb;
+ const diag = math.getAABB3Diag(aabb);
+ center[0] = aabb[0] + aabb[3] / 2.0;
+ center[1] = aabb[1] + aabb[4] / 2.0;
+ center[2] = aabb[2] + aabb[5] / 2.0;
+ const dist = Math.abs((diag) / Math.tan(self._cameraFlight.fitFOV / 2));
- const aabb = scene.aabb;
- const diag = math.getAABB3Diag(aabb);
- center[0] = aabb[0] + aabb[3] / 2.0;
- center[1] = aabb[1] + aabb[4] / 2.0;
- center[2] = aabb[2] + aabb[5] / 2.0;
- const dist = Math.abs((diag) / Math.tan(self._cameraFlight.fitFOV / 2));
+ switch (keyCode) {
- switch (keyCode) {
+ case KEY_NUM_1: // Right
- case KEY_NUM_1: // Right
+ cameraTarget.eye.set(math.mulVec3Scalar(camera.worldRight, dist, tempVec3a));
+ cameraTarget.look.set(center);
+ cameraTarget.up.set(camera.worldUp);
- cameraTarget.eye.set(math.mulVec3Scalar(camera.worldRight, dist, tempVec3a));
- cameraTarget.look.set(center);
- cameraTarget.up.set(camera.worldUp);
+ break;
- break;
+ case KEY_NUM_2: // Back
- case KEY_NUM_2: // Back
+ cameraTarget.eye.set(math.mulVec3Scalar(camera.worldForward, dist, tempVec3a));
+ cameraTarget.look.set(center);
+ cameraTarget.up.set(camera.worldUp);
- cameraTarget.eye.set(math.mulVec3Scalar(camera.worldForward, dist, tempVec3a));
- cameraTarget.look.set(center);
- cameraTarget.up.set(camera.worldUp);
+ break;
- break;
+ case KEY_NUM_3: // Left
- case KEY_NUM_3: // Left
+ cameraTarget.eye.set(math.mulVec3Scalar(camera.worldRight, -dist, tempVec3a));
+ cameraTarget.look.set(center);
+ cameraTarget.up.set(camera.worldUp);
- cameraTarget.eye.set(math.mulVec3Scalar(camera.worldRight, -dist, tempVec3a));
- cameraTarget.look.set(center);
- cameraTarget.up.set(camera.worldUp);
+ break;
- break;
+ case KEY_NUM_4: // Front
- case KEY_NUM_4: // Front
+ cameraTarget.eye.set(math.mulVec3Scalar(camera.worldForward, -dist, tempVec3a));
+ cameraTarget.look.set(center);
+ cameraTarget.up.set(camera.worldUp);
- cameraTarget.eye.set(math.mulVec3Scalar(camera.worldForward, -dist, tempVec3a));
- cameraTarget.look.set(center);
- cameraTarget.up.set(camera.worldUp);
+ break;
- break;
+ case KEY_NUM_5: // Top
- case KEY_NUM_5: // Top
+ cameraTarget.eye.set(math.mulVec3Scalar(camera.worldUp, dist, tempVec3a));
+ cameraTarget.look.set(center);
+ cameraTarget.up.set(math.normalizeVec3(math.mulVec3Scalar(camera.worldForward, 1, tempVec3b), tempVec3c));
- cameraTarget.eye.set(math.mulVec3Scalar(camera.worldUp, dist, tempVec3a));
- cameraTarget.look.set(center);
- cameraTarget.up.set(math.normalizeVec3(math.mulVec3Scalar(camera.worldForward, 1, tempVec3b), tempVec3c));
+ break;
- break;
+ case KEY_NUM_6: // Bottom
- case KEY_NUM_6: // Bottom
+ cameraTarget.eye.set(math.mulVec3Scalar(camera.worldUp, -dist, tempVec3a));
+ cameraTarget.look.set(center);
+ cameraTarget.up.set(math.normalizeVec3(math.mulVec3Scalar(camera.worldForward, -1, tempVec3b)));
- cameraTarget.eye.set(math.mulVec3Scalar(camera.worldUp, -dist, tempVec3a));
- cameraTarget.look.set(center);
- cameraTarget.up.set(math.normalizeVec3(math.mulVec3Scalar(camera.worldForward, -1, tempVec3b)));
+ break;
- break;
+ default:
+ return;
+ }
- default:
- return;
- }
+ if (self._cameraFlight.duration > 0) {
+ self._cameraFlight.flyTo(cameraTarget);
+ } else {
+ self._cameraFlight.jumpTo(cameraTarget);
+ }
+ });
- if (self._cameraFlight.duration > 0) {
- self._cameraFlight.flyTo(cameraTarget);
- } else {
- self._cameraFlight.jumpTo(cameraTarget);
- }
- });
+ })();
+ }
- })();
- },
+ _flyTo(hit) {
- _flyTo: function (hit) {
+ let pos;
- let pos;
+ if (hit && hit.worldPos) {
+ pos = hit.worldPos
+ }
- if (hit && hit.worldPos) {
- pos = hit.worldPos
- }
+ const aabb = hit ? hit.mesh.aabb : this.scene.aabb;
- const aabb = hit ? hit.mesh.aabb : this.scene.aabb;
+ this._boundaryHelper.geometry.targetAABB = aabb;
+ // this._boundaryHelper.visible = true;
- this._boundaryHelper.geometry.targetAABB = aabb;
- // this._boundaryHelper.visible = true;
+ if (pos) {
- if (pos) {
+ // Fly to look at point, don't change eye->look dist
- // Fly to look at point, don't change eye->look dist
+ const camera = this.scene.camera;
+ const diff = math.subVec3(camera.eye, camera.look, []);
- const camera = this.scene.camera;
- const diff = xeogl.math.subVec3(camera.eye, camera.look, []);
+ this._cameraFlight.flyTo({
+ // look: pos,
+ // eye: xeogl.math.addVec3(pos, diff, []),
+ // up: camera.up,
+ aabb: aabb
+ },
+ this._hideBoundary, this);
- this._cameraFlight.flyTo({
- // look: pos,
- // eye: xeogl.math.addVec3(pos, diff, []),
- // up: camera.up,
- aabb: aabb
- },
- this._hideBoundary, this);
+ // TODO: Option to back off to fit AABB in view
- // TODO: Option to back off to fit AABB in view
+ } else {
- } else {
+ // Fly to fit target boundary in view
- // Fly to fit target boundary in view
+ this._cameraFlight.flyTo({
+ aabb: aabb
+ },
+ this._hideBoundary, this);
+ }
+ }
- this._cameraFlight.flyTo({
- aabb: aabb
- },
- this._hideBoundary, this);
- }
- },
+ _hideBoundary() {
+ // this._boundaryHelper.visible = false;
+ }
- _hideBoundary: function () {
- // this._boundaryHelper.visible = false;
- }
- });
+ destroy() {
+ this.active = false;
+ super.destroy();
+ }
+}
-})();
+export {CameraControl};
diff --git a/src/core.js b/src/core.js
new file mode 100644
index 000000000..71befea9e
--- /dev/null
+++ b/src/core.js
@@ -0,0 +1,216 @@
+import {Map} from './utils/map.js';
+import {stats} from './stats.js';
+import {utils} from './utils.js';
+import {tasks} from './tasks.js';
+
+const scenesRenderInfo = {}; // Used for throttling FPS for each Scene
+const sceneIDMap = new Map(); // Ensures unique scene IDs
+let defaultScene = null;// Default singleton Scene, lazy-initialized in getter
+
+const core = {
+
+ _debug: {
+ forceHighShaderPrecision: false
+ },
+
+ /**
+ Semantic version number. The value for this is set by an expression that's concatenated to
+ the end of the built binary by the xeogl build script.
+ @property version
+ @namespace xeogl
+ @type {String}
+ */
+ version: null,
+
+ /**
+ Existing {{#crossLink "Scene"}}Scene{{/crossLink}}s , mapped to their IDs
+ @property scenes
+ @namespace xeogl
+ @type {{String:xeogl.Scene}}
+ */
+ scenes: {},
+
+ _superTypes: {}, // For each component type, a list of its supertypes, ordered upwards in the hierarchy.
+
+
+ /**
+ The default {{#crossLink "Scene"}}Scene{{/crossLink}}.
+
+ Components created without an explicit parent {{#crossLink "Scene"}}Scene{{/crossLink}} will be created within this
+ {{#crossLink "Scene"}}Scene{{/crossLink}} by default.
+
+ xeogl creates the default {{#crossLink "Scene"}}Scene{{/crossLink}} as soon as you either
+ reference this property for the first time, or create your first {{#crossLink "Mesh"}}Mesh{{/crossLink}} without
+ a specified {{#crossLink "Scene"}}Scene{{/crossLink}}.
+
+ @property scene
+ @namespace xeogl
+ @type Scene
+ */
+ get scene() {
+ return defaultScene || (defaultScene = new Scene({id: "default.scene"}));
+ },
+
+ set scene(value) {
+ defaultScene = value;
+ },
+
+ /**
+ Registers a scene on xeogl.
+ This is called within the xeogl.Scene constructor.
+
+ @method _addScene
+ @param {Scene} scene The scene
+ @private
+ */
+ _addScene(scene) {
+ if (scene.id) { // User-supplied ID
+ if (core.scenes[scene.id]) {
+ console.error(`[ERROR] Scene ${utils.inQuotes(scene.id)} already exists`);
+ return;
+ }
+ } else { // Auto-generated ID
+ scene.id = sceneIDMap.addItem({});
+ }
+ core.scenes[scene.id] = scene;
+ const ticksPerRender = scene.ticksPerRender;
+ scenesRenderInfo[scene.id] = {
+ ticksPerRender,
+ renderCountdown: ticksPerRender
+ };
+ stats.components.scenes++;
+ scene.on("destroyed", () => { // Unregister destroyed scenes
+ sceneIDMap.removeItem(scene.id);
+ delete core.scenes[scene.id];
+ delete scenesRenderInfo[scene.id];
+ stats.components.scenes--;
+ });
+ },
+
+ /**
+ Destroys all user-created {{#crossLink "Scene"}}Scenes{{/crossLink}} and
+ clears the default {{#crossLink "Scene"}}Scene{{/crossLink}}.
+
+ @method clear
+ @demo foo
+ */
+ clear() {
+ let scene;
+ for (const id in core.scenes) {
+ if (core.scenes.hasOwnProperty(id)) {
+ scene = core.scenes[id];
+ // Only clear the default Scene
+ // but destroy all the others
+ if (id === "default.scene") {
+ scene.clear();
+ } else {
+ scene.destroy();
+ }
+ }
+ }
+ core.scenes = {};
+ }
+};
+
+//---------------------------------------------------------------------------------------------------------------------
+// Game loop
+//---------------------------------------------------------------------------------------------------------------------
+
+const tickEvent = {
+ sceneId: null,
+ time: null,
+ startTime: null,
+ prevTime: null,
+ deltaTime: null
+};
+
+const taskBudget = 10; // Millisecs we're allowed to spend on tasks in each frame
+const fpsSamples = [];
+const numFPSSamples = 30;
+let lastTime = 0;
+let elapsedTime;
+let totalFPS = 0;
+
+const frame = function () {
+
+ let time = Date.now();
+
+ if (lastTime > 0) { // Log FPS stats
+ elapsedTime = time - lastTime;
+ var newFPS = 1000 / elapsedTime; // Moving average of FPS
+ totalFPS += newFPS;
+ fpsSamples.push(newFPS);
+ if (fpsSamples.length >= numFPSSamples) {
+ totalFPS -= fpsSamples.shift();
+ }
+ stats.frame.fps = Math.round(totalFPS / fpsSamples.length);
+ }
+
+ runTasks(time);
+ fireTickEvents(time);
+ renderScenes();
+
+ lastTime = time;
+ window.requestAnimationFrame(frame);
+};
+
+function runTasks(time) { // Process as many enqueued tasks as we can within the per-frame task budget
+ const tasksRun = tasks.runTasks(time + taskBudget);
+ const tasksScheduled = tasks.getNumTasks();
+ stats.frame.tasksRun = tasksRun;
+ stats.frame.tasksScheduled = tasksScheduled;
+ stats.frame.tasksBudget = taskBudget;
+}
+
+function fireTickEvents(time) { // Fire tick event on each Scene
+ tickEvent.time = time;
+ for (var id in core.scenes) {
+ if (core.scenes.hasOwnProperty(id)) {
+ var scene = core.scenes[id];
+ tickEvent.sceneId = id;
+ tickEvent.startTime = scene.startTime;
+ tickEvent.deltaTime = tickEvent.prevTime != null ? tickEvent.time - tickEvent.prevTime : 0;
+ /**
+ * Fired on each game loop iteration.
+ *
+ * @event tick
+ * @param {String} sceneID The ID of this Scene.
+ * @param {Number} startTime The time in seconds since 1970 that this Scene was instantiated.
+ * @param {Number} time The time in seconds since 1970 of this "tick" event.
+ * @param {Number} prevTime The time of the previous "tick" event from this Scene.
+ * @param {Number} deltaTime The time in seconds since the previous "tick" event from this Scene.
+ */
+ scene.fire("tick", tickEvent, true);
+ }
+ }
+ tickEvent.prevTime = time;
+}
+
+function renderScenes() {
+ const scenes = core.scenes;
+ const forceRender = false;
+ let scene;
+ let renderInfo;
+ let ticksPerRender;
+ let id;
+ for (id in scenes) {
+ if (scenes.hasOwnProperty(id)) {
+ scene = scenes[id];
+ renderInfo = scenesRenderInfo[id];
+ ticksPerRender = scene.ticksPerRender;
+ if (renderInfo.ticksPerRender !== ticksPerRender) {
+ renderInfo.ticksPerRender = ticksPerRender;
+ renderInfo.renderCountdown = ticksPerRender;
+ }
+ if (--renderInfo.renderCountdown === 0) {
+ scene.render(forceRender);
+ renderInfo.renderCountdown = ticksPerRender;
+ }
+ }
+ }
+}
+
+window.requestAnimationFrame(frame);
+
+export {core};
+
diff --git a/src/geometry/aabbGeometry.js b/src/geometry/aabbGeometry.js
index cbc9dd026..c936b7f56 100644
--- a/src/geometry/aabbGeometry.js
+++ b/src/geometry/aabbGeometry.js
@@ -62,8 +62,7 @@
@module xeogl
@submodule geometry
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}} - creates this AABBGeometry in the default
- {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
+ @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.
@@ -73,126 +72,131 @@
containing the min/max extents of the axis-aligned volume, ie. ````(xmin,ymin,zmin,xmax,ymax,zmax)````.
@extends Component
*/
-(function () {
- "use strict";
-
- xeogl.AABBGeometry = xeogl.Geometry.extend({
-
- type: "xeogl.AABBGeometry",
-
- _init: function (cfg) {
-
- this._super(xeogl._apply(cfg, {
-
- combined: true,
- quantized: true, // Quantized geometry is immutable
-
- primitive: cfg.primitive || "lines",
- indices: [
- 0, 1, 1, 2, 2, 3, 3, 0, 4,
- 5, 5, 6, 6, 7, 7, 4, 0, 4,
- 1, 5, 2, 6, 3, 7
- ],
- positions: cfg.positions || [
- 1.0, 1.0, 1.0,
- 1.0, -1.0, 1.0,
- -1.0, -1.0, 1.0,
- -1.0, 1.0, 1.0,
- 1.0, 1.0, -1.0,
- 1.0, -1.0, -1.0,
- -1.0, -1.0, -1.0,
- -1.0, 1.0, -1.0
- ]
- }));
-
- if (cfg.target) {
- this.target = cfg.target;
-
- } else if (cfg.targetAABB) {
- this.targetAABB = cfg.targetAABB;
- }
- },
-
- _props: {
-
- /**
- A component whose AABB we'll dynamically fit this AABBGeometry to.
-
- This property effectively replaces the {{#crossLink "AABBGeometry/targetAABB:property"}}{{/crossLink}} property.
-
- @property target
- @type Component
- */
- target: {
-
- set: function (value) {
-
- let geometryDirty = false;
- const self = this;
-
- this._attach({
- name: "target",
- type: "xeogl.Component",
- component: value,
- sceneDefault: false,
- on: {
- boundary: function () {
- if (geometryDirty) {
- return;
- }
- geometryDirty = true;
- xeogl.scheduleTask(function () {
- self._setPositionsFromAABB(self._attached.target.aabb);
- geometryDirty = false;
- });
- }
- },
- onAttached: function () {
- self._setPositionsFromAABB(self._attached.target.aabb);
- }
- });
- },
-
- get: function () {
- return this._attached.target;
- }
- },
-
- /**
- Sets this AABBGeometry to an axis-aligned box (AABB), given as a six-element Float32Array
- containing the min/max extents of the
- axis-aligned volume, ie. ````[xmin,ymin,zmin,xmax,ymax,zmax]````.
-
- This property overrides the {{#crossLink "AABBGeometry/target:property"}}{{/crossLink}} property, causing it to become null.
-
- @property targetAABB
- @type Float32Array
- */
- targetAABB: {
- set: function (value) {
- if (!value) {
+import {core} from "./../core.js";
+import {utils} from '../utils.js';
+import {tasks} from '../tasks.js';
+import {Geometry} from './geometry.js';
+
+const type = "xeogl.AABBGeometry";
+
+class AABBGeometry extends Geometry {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return type;
+ }
+
+ init(cfg) {
+
+ super(owner, utils.apply(cfg, {
+ combined: true,
+ quantized: true, // Quantized geometry is immutable
+ primitive: cfg.primitive || "lines",
+ indices: [
+ 0, 1, 1, 2, 2, 3, 3, 0, 4,
+ 5, 5, 6, 6, 7, 7, 4, 0, 4,
+ 1, 5, 2, 6, 3, 7
+ ],
+ positions: cfg.positions || [
+ 1.0, 1.0, 1.0,
+ 1.0, -1.0, 1.0,
+ -1.0, -1.0, 1.0,
+ -1.0, 1.0, 1.0,
+ 1.0, 1.0, -1.0,
+ 1.0, -1.0, -1.0,
+ -1.0, -1.0, -1.0,
+ -1.0, 1.0, -1.0
+ ]
+ }));
+
+ if (cfg.target) {
+ this.target = cfg.target;
+
+ } else if (cfg.targetAABB) {
+ this.targetAABB = cfg.targetAABB;
+ }
+ }
+
+
+ /**
+ A component whose AABB we'll dynamically fit this AABBGeometry to.
+
+ This property effectively replaces the {{#crossLink "AABBGeometry/targetAABB:property"}}{{/crossLink}} property.
+
+ @property target
+ @type Component
+ */
+ set target(target) {
+ let geometryDirty = false;
+ const self = this;
+ this._attach({
+ name: "target",
+ type: "xeogl.Component",
+ component: target,
+ sceneDefault: false,
+ on: {
+ boundary: function () {
+ if (geometryDirty) {
return;
}
- if (this._attached.target) {
- this.target = null;
- }
- this._setPositionsFromAABB(value);
+ geometryDirty = true;
+ tasks.scheduleTask(function () {
+ self._setPositionsFromAABB(self._attached.target.aabb);
+ geometryDirty = false;
+ });
}
+ },
+ onAttached: function () {
+ self._setPositionsFromAABB(self._attached.target.aabb);
}
- },
-
- _setPositionsFromAABB: function (aabb) {
- this.positions = [
- aabb[3], aabb[4], aabb[5],
- aabb[3], aabb[1], aabb[5],
- aabb[0], aabb[1], aabb[5],
- aabb[0], aabb[4], aabb[5],
- aabb[3], aabb[4], aabb[2],
- aabb[3], aabb[1], aabb[2],
- aabb[0], aabb[1], aabb[2],
- aabb[0], aabb[4], aabb[2]
- ];
+ });
+ }
+
+ get target() {
+ return this._attached.target;
+ }
+
+ /**
+ Sets this AABBGeometry to an axis-aligned box (AABB), given as a six-element Float32Array
+ containing the min/max extents of the
+ axis-aligned volume, ie. ````[xmin,ymin,zmin,xmax,ymax,zmax]````.
+
+ This property overrides the {{#crossLink "AABBGeometry/target:property"}}{{/crossLink}} property, causing it to become null.
+
+ @property targetAABB
+ @type Float32Array
+ */
+ set targetAABB(aabb) {
+ if (!aabb) {
+ return;
+ }
+ if (this._attached.target) {
+ this.target = null;
}
- });
-})();
+ this._setPositionsFromAABB(aabb);
+ }
+
+ _setPositionsFromAABB(aabb) {
+ this.positions = [
+ aabb[3], aabb[4], aabb[5],
+ aabb[3], aabb[1], aabb[5],
+ aabb[0], aabb[1], aabb[5],
+ aabb[0], aabb[4], aabb[5],
+ aabb[3], aabb[4], aabb[2],
+ aabb[3], aabb[1], aabb[2],
+ aabb[0], aabb[1], aabb[2],
+ aabb[0], aabb[4], aabb[2]
+ ];
+ }
+}
+
+export{AABBGeometry};
diff --git a/src/geometry/boxGeometry.js b/src/geometry/boxGeometry.js
index c1e304e42..534b9249a 100644
--- a/src/geometry/boxGeometry.js
+++ b/src/geometry/boxGeometry.js
@@ -40,8 +40,7 @@
@module xeogl
@submodule geometry
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}} - creates this BoxGeometry in the default
- {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
+ @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.
@@ -53,211 +52,225 @@
@param [cfg.zSize=1.0] {Number} Half-size on the Z-axis.
@extends Geometry
*/
-(function () {
-
- "use strict";
-
- xeogl.BoxGeometry = xeogl.Geometry.extend({
-
- type: "xeogl.BoxGeometry",
-
- _init: function (cfg) {
-
- let xSize = cfg.xSize || 1;
- if (xSize < 0) {
- this.error("negative xSize not allowed - will invert");
- xSize *= -1;
- }
-
- let ySize = cfg.ySize || 1;
- if (ySize < 0) {
- this.error("negative ySize not allowed - will invert");
- ySize *= -1;
- }
-
- let zSize = cfg.zSize || 1;
- if (zSize < 0) {
- this.error("negative zSize not allowed - will invert");
- zSize *= -1;
- }
-
- const center = cfg.center;
- const centerX = center ? center[0] : 0;
- const centerY = center ? center[1] : 0;
- const centerZ = center ? center[2] : 0;
-
- const xmin = -xSize + centerX;
- const ymin = -ySize + centerY;
- const zmin = -zSize + centerZ;
- const xmax = xSize + centerX;
- const ymax = ySize + centerY;
- const zmax = zSize + centerZ;
-
- this._super(xeogl._apply(cfg, {
-
- // The vertices - eight for our cube, each
- // one spanning three array elements for X,Y and Z
- positions: [
-
- // v0-v1-v2-v3 front
- xmax, ymax, zmax,
- xmin, ymax, zmax,
- xmin, ymin, zmax,
- xmax, ymin, zmax,
-
- // v0-v3-v4-v1 right
- xmax, ymax, zmax,
- xmax, ymin, zmax,
- xmax, ymin, zmin,
- xmax, ymax, zmin,
-
- // v0-v1-v6-v1 top
- xmax, ymax, zmax,
- xmax, ymax, zmin,
- xmin, ymax, zmin,
- xmin, ymax, zmax,
-
- // v1-v6-v7-v2 left
- xmin, ymax, zmax,
- xmin, ymax, zmin,
- xmin, ymin, zmin,
- xmin, ymin, zmax,
-
- // v7-v4-v3-v2 bottom
- xmin, ymin, zmin,
- xmax, ymin, zmin,
- xmax, ymin, zmax,
- xmin, ymin, zmax,
-
- // v4-v7-v6-v1 back
- xmax, ymin, zmin,
- xmin, ymin, zmin,
- xmin, ymax, zmin,
- xmax, ymax, zmin
- ],
-
- // Normal vectors, one for each vertex
- normals: [
-
- // v0-v1-v2-v3 front
- 0, 0, 1,
- 0, 0, 1,
- 0, 0, 1,
- 0, 0, 1,
-
- // v0-v3-v4-v5 right
- 1, 0, 0,
- 1, 0, 0,
- 1, 0, 0,
- 1, 0, 0,
-
- // v0-v5-v6-v1 top
- 0, 1, 0,
- 0, 1, 0,
- 0, 1, 0,
- 0, 1, 0,
-
- // v1-v6-v7-v2 left
- -1, 0, 0,
- -1, 0, 0,
- -1, 0, 0,
- -1, 0, 0,
-
- // v7-v4-v3-v2 bottom
- 0, -1, 0,
- 0, -1, 0,
- 0, -1, 0,
- 0, -1, 0,
-
- // v4-v7-v6-v5 back
- 0, 0, -1,
- 0, 0, -1,
- 0, 0, -1,
- 0, 0, -1
- ],
-
- // UV coords
- uv: [
-
- // v0-v1-v2-v3 front
- 1, 0,
- 0, 0,
- 0, 1,
- 1, 1,
-
- // v0-v3-v4-v1 right
- 0, 0,
- 0, 1,
- 1, 1,
- 1, 0,
-
- // v0-v1-v6-v1 top
- 1, 1,
- 1, 0,
- 0, 0,
- 0, 1,
-
- // v1-v6-v7-v2 left
- 1, 0,
- 0, 0,
- 0, 1,
- 1, 1,
-
- // v7-v4-v3-v2 bottom
- 0, 1,
- 1, 1,
- 1, 0,
- 0, 0,
-
- // v4-v7-v6-v1 back
- 0, 1,
- 1, 1,
- 1, 0,
- 0, 0
- ],
-
- // Indices - these organise the
- // positions and uv texture coordinates
- // into geometric primitives in accordance
- // with the "primitive" parameter,
- // in this case a set of three indices
- // for each triangle.
- //
- // Note that each triangle is specified
- // in counter-clockwise winding order.
- //
- // You can specify them in clockwise
- // order if you configure the Modes
- // node's frontFace flag as "cw", instead of
- // the default "ccw".
- indices: [
- 0, 1, 2,
- 0, 2, 3,
- // front
- 4, 5, 6,
- 4, 6, 7,
- // right
- 8, 9, 10,
- 8, 10, 11,
- // top
- 12, 13, 14,
- 12, 14, 15,
- // left
- 16, 17, 18,
- 16, 18, 19,
- // bottom
- 20, 21, 22,
- 20, 22, 23
- ],
-
- // Tangents are lazy-computed from normals and UVs
- // for Normal mapping once we know we have texture
-
- tangents: null
- }));
-
- this.box = true;
+
+import {core} from "./../core.js";
+import {utils} from '../utils.js';
+import {Geometry} from './geometry.js';
+
+const type = "xeogl.BoxGeometry";
+
+class BoxGeometry extends Geometry {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return type;
+ }
+
+ init(cfg) {
+
+ let xSize = cfg.xSize || 1;
+ if (xSize < 0) {
+ this.error("negative xSize not allowed - will invert");
+ xSize *= -1;
+ }
+
+ let ySize = cfg.ySize || 1;
+ if (ySize < 0) {
+ this.error("negative ySize not allowed - will invert");
+ ySize *= -1;
+ }
+
+ let zSize = cfg.zSize || 1;
+ if (zSize < 0) {
+ this.error("negative zSize not allowed - will invert");
+ zSize *= -1;
}
- });
-})();
+ const center = cfg.center;
+ const centerX = center ? center[0] : 0;
+ const centerY = center ? center[1] : 0;
+ const centerZ = center ? center[2] : 0;
+
+ const xmin = -xSize + centerX;
+ const ymin = -ySize + centerY;
+ const zmin = -zSize + centerZ;
+ const xmax = xSize + centerX;
+ const ymax = ySize + centerY;
+ const zmax = zSize + centerZ;
+
+ super(owner, utils.apply(cfg, {
+
+ // The vertices - eight for our cube, each
+ // one spanning three array elements for X,Y and Z
+ positions: [
+
+ // v0-v1-v2-v3 front
+ xmax, ymax, zmax,
+ xmin, ymax, zmax,
+ xmin, ymin, zmax,
+ xmax, ymin, zmax,
+
+ // v0-v3-v4-v1 right
+ xmax, ymax, zmax,
+ xmax, ymin, zmax,
+ xmax, ymin, zmin,
+ xmax, ymax, zmin,
+
+ // v0-v1-v6-v1 top
+ xmax, ymax, zmax,
+ xmax, ymax, zmin,
+ xmin, ymax, zmin,
+ xmin, ymax, zmax,
+
+ // v1-v6-v7-v2 left
+ xmin, ymax, zmax,
+ xmin, ymax, zmin,
+ xmin, ymin, zmin,
+ xmin, ymin, zmax,
+
+ // v7-v4-v3-v2 bottom
+ xmin, ymin, zmin,
+ xmax, ymin, zmin,
+ xmax, ymin, zmax,
+ xmin, ymin, zmax,
+
+ // v4-v7-v6-v1 back
+ xmax, ymin, zmin,
+ xmin, ymin, zmin,
+ xmin, ymax, zmin,
+ xmax, ymax, zmin
+ ],
+
+ // Normal vectors, one for each vertex
+ normals: [
+
+ // v0-v1-v2-v3 front
+ 0, 0, 1,
+ 0, 0, 1,
+ 0, 0, 1,
+ 0, 0, 1,
+
+ // v0-v3-v4-v5 right
+ 1, 0, 0,
+ 1, 0, 0,
+ 1, 0, 0,
+ 1, 0, 0,
+
+ // v0-v5-v6-v1 top
+ 0, 1, 0,
+ 0, 1, 0,
+ 0, 1, 0,
+ 0, 1, 0,
+
+ // v1-v6-v7-v2 left
+ -1, 0, 0,
+ -1, 0, 0,
+ -1, 0, 0,
+ -1, 0, 0,
+
+ // v7-v4-v3-v2 bottom
+ 0, -1, 0,
+ 0, -1, 0,
+ 0, -1, 0,
+ 0, -1, 0,
+
+ // v4-v7-v6-v5 back
+ 0, 0, -1,
+ 0, 0, -1,
+ 0, 0, -1,
+ 0, 0, -1
+ ],
+
+ // UV coords
+ uv: [
+
+ // v0-v1-v2-v3 front
+ 1, 0,
+ 0, 0,
+ 0, 1,
+ 1, 1,
+
+ // v0-v3-v4-v1 right
+ 0, 0,
+ 0, 1,
+ 1, 1,
+ 1, 0,
+
+ // v0-v1-v6-v1 top
+ 1, 1,
+ 1, 0,
+ 0, 0,
+ 0, 1,
+
+ // v1-v6-v7-v2 left
+ 1, 0,
+ 0, 0,
+ 0, 1,
+ 1, 1,
+
+ // v7-v4-v3-v2 bottom
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0,
+
+ // v4-v7-v6-v1 back
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0
+ ],
+
+ // Indices - these organise the
+ // positions and uv texture coordinates
+ // into geometric primitives in accordance
+ // with the "primitive" parameter,
+ // in this case a set of three indices
+ // for each triangle.
+ //
+ // Note that each triangle is specified
+ // in counter-clockwise winding order.
+ //
+ // You can specify them in clockwise
+ // order if you configure the Modes
+ // node's frontFace flag as "cw", instead of
+ // the default "ccw".
+ indices: [
+ 0, 1, 2,
+ 0, 2, 3,
+ // front
+ 4, 5, 6,
+ 4, 6, 7,
+ // right
+ 8, 9, 10,
+ 8, 10, 11,
+ // top
+ 12, 13, 14,
+ 12, 14, 15,
+ // left
+ 16, 17, 18,
+ 16, 18, 19,
+ // bottom
+ 20, 21, 22,
+ 20, 22, 23
+ ],
+
+ // Tangents are lazy-computed from normals and UVs
+ // for Normal mapping once we know we have texture
+
+ tangents: null
+ }));
+
+ this.box = true;
+ }
+}
+
+export{BoxGeometry};
diff --git a/src/geometry/cylinderGeometry.js b/src/geometry/cylinderGeometry.js
index 64bc9cda9..ebb24a7b8 100644
--- a/src/geometry/cylinderGeometry.js
+++ b/src/geometry/cylinderGeometry.js
@@ -37,8 +37,7 @@
@module xeogl
@submodule geometry
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}} - creates this CylinderGeometry in the default
- {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
+ @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.
@@ -54,229 +53,242 @@
@param [cfg.lod=1] {Number} Level-of-detail, in range [0..1].
@extends Geometry
*/
-(function () {
+import {core} from "./../core.js";
+import {utils} from '../utils.js';
+import {Geometry} from './geometry.js';
- "use strict";
+const type = "xeogl.CylinderGeometry";
- xeogl.CylinderGeometry = xeogl.Geometry.extend({
+class CylinderGeometry extends Geometry {
- type: "xeogl.CylinderGeometry",
+ /**
+ JavaScript class name for this Component.
- _init: function (cfg) {
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
- let radiusTop = cfg.radiusTop || 1;
- if (radiusTop < 0) {
- this.error("negative radiusTop not allowed - will invert");
- radiusTop *= -1;
- }
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return type;
+ }
- let radiusBottom = cfg.radiusBottom || 1;
- if (radiusBottom < 0) {
- this.error("negative radiusBottom not allowed - will invert");
- radiusBottom *= -1;
- }
+ constructor(owner = nul, cfg) {
- let height = cfg.height || 1;
- if (height < 0) {
- this.error("negative height not allowed - will invert");
- height *= -1;
- }
+ let radiusTop = cfg.radiusTop || 1;
+ if (radiusTop < 0) {
+ this.error("negative radiusTop not allowed - will invert");
+ radiusTop *= -1;
+ }
- let radialSegments = cfg.radialSegments || 32;
- if (radialSegments < 0) {
- this.error("negative radialSegments not allowed - will invert");
- radialSegments *= -1;
- }
- if (radialSegments < 3) {
- radialSegments = 3;
- }
+ let radiusBottom = cfg.radiusBottom || 1;
+ if (radiusBottom < 0) {
+ this.error("negative radiusBottom not allowed - will invert");
+ radiusBottom *= -1;
+ }
- let heightSegments = cfg.heightSegments || 1;
- if (heightSegments < 0) {
- this.error("negative heightSegments not allowed - will invert");
- heightSegments *= -1;
- }
- if (heightSegments < 1) {
- heightSegments = 1;
- }
+ let height = cfg.height || 1;
+ if (height < 0) {
+ this.error("negative height not allowed - will invert");
+ height *= -1;
+ }
+
+ let radialSegments = cfg.radialSegments || 32;
+ if (radialSegments < 0) {
+ this.error("negative radialSegments not allowed - will invert");
+ radialSegments *= -1;
+ }
+ if (radialSegments < 3) {
+ radialSegments = 3;
+ }
- const openEnded = !!cfg.openEnded;
+ let heightSegments = cfg.heightSegments || 1;
+ if (heightSegments < 0) {
+ this.error("negative heightSegments not allowed - will invert");
+ heightSegments *= -1;
+ }
+ if (heightSegments < 1) {
+ heightSegments = 1;
+ }
- let center = cfg.center;
- const centerX = center ? center[0] : 0;
- const centerY = center ? center[1] : 0;
- const centerZ = center ? center[2] : 0;
+ const openEnded = !!cfg.openEnded;
- const heightHalf = height / 2;
- const heightLength = height / heightSegments;
- const radialAngle = (2.0 * Math.PI / radialSegments);
- const radialLength = 1.0 / radialSegments;
- //var nextRadius = this._radiusBottom;
- const radiusChange = (radiusTop - radiusBottom) / heightSegments;
+ let center = cfg.center;
+ const centerX = center ? center[0] : 0;
+ const centerY = center ? center[1] : 0;
+ const centerZ = center ? center[2] : 0;
- const positions = [];
- const normals = [];
- const uvs = [];
- const indices = [];
+ const heightHalf = height / 2;
+ const heightLength = height / heightSegments;
+ const radialAngle = (2.0 * Math.PI / radialSegments);
+ const radialLength = 1.0 / radialSegments;
+ //var nextRadius = this._radiusBottom;
+ const radiusChange = (radiusTop - radiusBottom) / heightSegments;
- let h;
- let i;
+ const positions = [];
+ const normals = [];
+ const uvs = [];
+ const indices = [];
- let x;
- let z;
+ let h;
+ let i;
- let currentRadius;
- let currentHeight;
+ let x;
+ let z;
- let first;
- let second;
+ let currentRadius;
+ let currentHeight;
- let startIndex;
- let tu;
- let tv;
+ let first;
+ let second;
- // create vertices
- const normalY = (90.0 - (Math.atan(height / (radiusBottom - radiusTop))) * 180 / Math.PI) / 90.0;
+ let startIndex;
+ let tu;
+ let tv;
- for (h = 0; h <= heightSegments; h++) {
- currentRadius = radiusTop - h * radiusChange;
- currentHeight = heightHalf - h * heightLength;
+ // create vertices
+ const normalY = (90.0 - (Math.atan(height / (radiusBottom - radiusTop))) * 180 / Math.PI) / 90.0;
- for (i = 0; i <= radialSegments; i++) {
- x = Math.sin(i * radialAngle);
- z = Math.cos(i * radialAngle);
+ for (h = 0; h <= heightSegments; h++) {
+ currentRadius = radiusTop - h * radiusChange;
+ currentHeight = heightHalf - h * heightLength;
- normals.push(currentRadius * x);
- normals.push(normalY); //todo
- normals.push(currentRadius * z);
+ for (i = 0; i <= radialSegments; i++) {
+ x = Math.sin(i * radialAngle);
+ z = Math.cos(i * radialAngle);
- uvs.push((i * radialLength));
- uvs.push(h * 1 / heightSegments);
+ normals.push(currentRadius * x);
+ normals.push(normalY); //todo
+ normals.push(currentRadius * z);
- positions.push((currentRadius * x) + centerX);
- positions.push((currentHeight) + centerY);
- positions.push((currentRadius * z) + centerZ);
- }
+ uvs.push((i * radialLength));
+ uvs.push(h * 1 / heightSegments);
+
+ positions.push((currentRadius * x) + centerX);
+ positions.push((currentHeight) + centerY);
+ positions.push((currentRadius * z) + centerZ);
}
+ }
- // create faces
- for (h = 0; h < heightSegments; h++) {
- for (i = 0; i <= radialSegments; i++) {
+ // create faces
+ for (h = 0; h < heightSegments; h++) {
+ for (i = 0; i <= radialSegments; i++) {
- first = h * (radialSegments + 1) + i;
- second = first + radialSegments;
+ first = h * (radialSegments + 1) + i;
+ second = first + radialSegments;
- indices.push(first);
- indices.push(second);
- indices.push(second + 1);
+ indices.push(first);
+ indices.push(second);
+ indices.push(second + 1);
- indices.push(first);
- indices.push(second + 1);
- indices.push(first + 1);
- }
+ indices.push(first);
+ indices.push(second + 1);
+ indices.push(first + 1);
}
+ }
+
+ // create top cap
+ if (!openEnded && radiusTop > 0) {
+ startIndex = (positions.length / 3);
+
+ // top center
+ normals.push(0.0);
+ normals.push(1.0);
+ normals.push(0.0);
+
+ uvs.push(0.5);
+ uvs.push(0.5);
- // create top cap
- if (!openEnded && radiusTop > 0) {
- startIndex = (positions.length / 3);
+ positions.push(0 + centerX);
+ positions.push(heightHalf + centerY);
+ positions.push(0 + centerZ);
- // top center
- normals.push(0.0);
+ // top triangle fan
+ for (i = 0; i <= radialSegments; i++) {
+ x = Math.sin(i * radialAngle);
+ z = Math.cos(i * radialAngle);
+ tu = (0.5 * Math.sin(i * radialAngle)) + 0.5;
+ tv = (0.5 * Math.cos(i * radialAngle)) + 0.5;
+
+ normals.push(radiusTop * x);
normals.push(1.0);
- normals.push(0.0);
-
- uvs.push(0.5);
- uvs.push(0.5);
-
- positions.push(0 + centerX);
- positions.push(heightHalf + centerY);
- positions.push(0 + centerZ);
-
- // top triangle fan
- for (i = 0; i <= radialSegments; i++) {
- x = Math.sin(i * radialAngle);
- z = Math.cos(i * radialAngle);
- tu = (0.5 * Math.sin(i * radialAngle)) + 0.5;
- tv = (0.5 * Math.cos(i * radialAngle)) + 0.5;
-
- normals.push(radiusTop * x);
- normals.push(1.0);
- normals.push(radiusTop * z);
-
- uvs.push(tu);
- uvs.push(tv);
-
- positions.push((radiusTop * x) + centerX);
- positions.push((heightHalf) + centerY);
- positions.push((radiusTop * z) + centerZ);
- }
-
- for (i = 0; i < radialSegments; i++) {
- center = startIndex;
- first = startIndex + 1 + i;
-
- indices.push(first);
- indices.push(first + 1);
- indices.push(center);
- }
- }
+ normals.push(radiusTop * z);
- // create bottom cap
- if (!openEnded && radiusBottom > 0) {
+ uvs.push(tu);
+ uvs.push(tv);
- startIndex = (positions.length / 3);
+ positions.push((radiusTop * x) + centerX);
+ positions.push((heightHalf) + centerY);
+ positions.push((radiusTop * z) + centerZ);
+ }
- // top center
- normals.push(0.0);
- normals.push(-1.0);
- normals.push(0.0);
+ for (i = 0; i < radialSegments; i++) {
+ center = startIndex;
+ first = startIndex + 1 + i;
- uvs.push(0.5);
- uvs.push(0.5);
+ indices.push(first);
+ indices.push(first + 1);
+ indices.push(center);
+ }
+ }
- positions.push(0 + centerX);
- positions.push(0 - heightHalf + centerY);
- positions.push(0 + centerZ);
+ // create bottom cap
+ if (!openEnded && radiusBottom > 0) {
- // top triangle fan
- for (i = 0; i <= radialSegments; i++) {
+ startIndex = (positions.length / 3);
- x = Math.sin(i * radialAngle);
- z = Math.cos(i * radialAngle);
+ // top center
+ normals.push(0.0);
+ normals.push(-1.0);
+ normals.push(0.0);
- tu = (0.5 * Math.sin(i * radialAngle)) + 0.5;
- tv = (0.5 * Math.cos(i * radialAngle)) + 0.5;
+ uvs.push(0.5);
+ uvs.push(0.5);
- normals.push(radiusBottom * x);
- normals.push(-1.0);
- normals.push(radiusBottom * z);
+ positions.push(0 + centerX);
+ positions.push(0 - heightHalf + centerY);
+ positions.push(0 + centerZ);
- uvs.push(tu);
- uvs.push(tv);
+ // top triangle fan
+ for (i = 0; i <= radialSegments; i++) {
- positions.push((radiusBottom * x) + centerX);
- positions.push((0 - heightHalf) + centerY);
- positions.push((radiusBottom * z) + centerZ);
- }
+ x = Math.sin(i * radialAngle);
+ z = Math.cos(i * radialAngle);
- for (i = 0; i < radialSegments; i++) {
+ tu = (0.5 * Math.sin(i * radialAngle)) + 0.5;
+ tv = (0.5 * Math.cos(i * radialAngle)) + 0.5;
+
+ normals.push(radiusBottom * x);
+ normals.push(-1.0);
+ normals.push(radiusBottom * z);
- center = startIndex;
- first = startIndex + 1 + i;
+ uvs.push(tu);
+ uvs.push(tv);
- indices.push(center);
- indices.push(first + 1);
- indices.push(first);
- }
+ positions.push((radiusBottom * x) + centerX);
+ positions.push((0 - heightHalf) + centerY);
+ positions.push((radiusBottom * z) + centerZ);
}
- this._super(xeogl._apply(cfg, {
- positions: positions,
- normals: normals,
- uv: uvs,
- indices: indices
- }));
+ for (i = 0; i < radialSegments; i++) {
+
+ center = startIndex;
+ first = startIndex + 1 + i;
+
+ indices.push(center);
+ indices.push(first + 1);
+ indices.push(first);
+ }
}
- });
-})();
+ super(owner, utils.apply(cfg, {
+ positions: positions,
+ normals: normals,
+ uv: uvs,
+ indices: indices
+ }));
+ }
+}
+
+export{CylinderGeometry};
diff --git a/src/geometry/geometry.js b/src/geometry/geometry.js
index 5a8129e6e..6eeed6d55 100644
--- a/src/geometry/geometry.js
+++ b/src/geometry/geometry.js
@@ -70,8 +70,7 @@
@module xeogl
@submodule geometry
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}} - creates this Geometry in the default
- {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
+ @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.
@@ -92,1004 +91,995 @@
this indicates the threshold angle (in degrees) between the face normals of adjacent triangles below which the edge is discarded.
@extends Component
*/
-(function () {
-
- "use strict";
-
- const CHUNK_LEN = bigIndicesSupported ? (Number.MAX_SAFE_INTEGER / 6) : (64000 * 4); // RGBA is largest item
- const memoryStats = xeogl.stats.memory;
- var bigIndicesSupported = xeogl.WEBGL_INFO.SUPPORTED_EXTENSIONS["OES_element_index_uint"];
- const IndexArrayType = bigIndicesSupported ? Uint32Array : Uint16Array;
- const nullVertexBufs = new xeogl.renderer.State({});
-
- xeogl.Geometry = xeogl.Component.extend({
-
- type: "xeogl.Geometry",
-
- _init: function (cfg) {
-
- const self = this;
-
- this._state = new xeogl.renderer.State({ // Arrays for emphasis effects are got from xeogl.Geometry friend methods
- combined: !!cfg.combined,
- quantized: !!cfg.quantized,
- autoVertexNormals: !!cfg.autoVertexNormals,
- primitive: null, // WebGL enum
- primitiveName: null, // String
- positions: null, // Uint16Array when quantized == true, else Float32Array
- normals: null, // Uint8Array when quantized == true, else Float32Array
- colors: null,
- uv: null, // Uint8Array when quantized == true, else Float32Array
- indices: null,
- positionsDecodeMatrix: null, // Set when quantized == true
- uvDecodeMatrix: null, // Set when quantized == true
- positionsBuf: null,
- normalsBuf: null,
- colorsbuf: null,
- uvBuf: null,
- indicesBuf: null,
- indicesBufCombined: null, // Indices into a shared VertexBufs, set when combined == true
- hash: ""
- });
-
- this._edgeThreshold = cfg.edgeThreshold || 2.0;
-
- // Lazy-generated VBOs
-
- this._edgesIndicesBuf = null;
- this._pickTrianglePositionsBuf = null;
- this._pickTriangleColorsBuf = null;
-
- // Local-space Boundary3D
-
- this._boundaryDirty = true;
-
- this._aabb = null;
- this._aabbDirty = true;
-
- this._obb = null;
- this._obbDirty = true;
-
- const state = this._state;
- const gl = this.scene.canvas.gl;
-
- // Primitive type
-
- cfg.primitive = cfg.primitive || "triangles";
- switch (cfg.primitive) {
- case "points":
- state.primitive = gl.POINTS;
- state.primitiveName = cfg.primitive;
- break;
- case "lines":
- state.primitive = gl.LINES;
- state.primitiveName = cfg.primitive;
- break;
- case "line-loop":
- state.primitive = gl.LINE_LOOP;
- state.primitiveName = cfg.primitive;
- break;
- case "line-strip":
- state.primitive = gl.LINE_STRIP;
- state.primitiveName = cfg.primitive;
- break;
- case "triangles":
- state.primitive = gl.TRIANGLES;
- state.primitiveName = cfg.primitive;
- break;
- case "triangle-strip":
- state.primitive = gl.TRIANGLE_STRIP;
- state.primitiveName = cfg.primitive;
- break;
- case "triangle-fan":
- state.primitive = gl.TRIANGLE_FAN;
- state.primitiveName = cfg.primitive;
- break;
- default:
- this.error("Unsupported value for 'primitive': '" + cfg.primitive +
- "' - supported values are 'points', 'lines', 'line-loop', 'line-strip', 'triangles', " +
- "'triangle-strip' and 'triangle-fan'. Defaulting to 'triangles'.");
- state.primitive = gl.TRIANGLES;
- state.primitiveName = cfg.primitive;
- }
- if (cfg.positions) {
- if (this._state.quantized) {
- var bounds = getBounds(cfg.positions, 3);
- var quantized = quantizeVec3(cfg.positions, bounds.min, bounds.max);
- state.positions = quantized.quantized;
- state.positionsDecodeMatrix = quantized.decode;
- } else {
- state.positions = cfg.positions.constructor === Float32Array ? cfg.positions : new Float32Array(cfg.positions);
- }
- }
- if (cfg.colors) {
- state.colors = cfg.colors.constructor === Float32Array ? cfg.colors : new Float32Array(cfg.colors);
+import {core} from "./../core.js";
+import {Component} from '../component.js';
+import {State} from '../renderer/state.js';
+import {ArrayBuffer} from '../renderer/arrayBuffer.js';
+import {getSceneVertexBufs} from './sceneVertexBufs.js';
+import {math} from '../math/math.js';
+import {stats} from './../stats.js';
+import {WEBGL_INFO} from './../webglInfo.js';
+
+const type = "xeogl.Geometry";
+
+const memoryStats = stats.memory;
+var bigIndicesSupported = WEBGL_INFO.SUPPORTED_EXTENSIONS["OES_element_index_uint"];
+const IndexArrayType = bigIndicesSupported ? Uint32Array : Uint16Array;
+const nullVertexBufs = new State({});
+const tempAABB = math.AABB3();
+
+class Geometry extends Component {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return type;
+ }
+
+ init(cfg) {
+
+ super.init(cfg);
+
+ const self = this;
+
+ this._state = new State({ // Arrays for emphasis effects are got from xeogl.Geometry friend methods
+ combined: !!cfg.combined,
+ quantized: !!cfg.quantized,
+ autoVertexNormals: !!cfg.autoVertexNormals,
+ primitive: null, // WebGL enum
+ primitiveName: null, // String
+ positions: null, // Uint16Array when quantized == true, else Float32Array
+ normals: null, // Uint8Array when quantized == true, else Float32Array
+ colors: null,
+ uv: null, // Uint8Array when quantized == true, else Float32Array
+ indices: null,
+ positionsDecodeMatrix: null, // Set when quantized == true
+ uvDecodeMatrix: null, // Set when quantized == true
+ positionsBuf: null,
+ normalsBuf: null,
+ colorsbuf: null,
+ uvBuf: null,
+ indicesBuf: null,
+ indicesBufCombined: null, // Indices into a shared VertexBufs, set when combined == true
+ hash: ""
+ });
+
+ this._edgeThreshold = cfg.edgeThreshold || 2.0;
+
+ // Lazy-generated VBOs
+
+ this._edgesIndicesBuf = null;
+ this._pickTrianglePositionsBuf = null;
+ this._pickTriangleColorsBuf = null;
+
+ // Local-space Boundary3D
+
+ this._boundaryDirty = true;
+
+ this._aabb = null;
+ this._aabbDirty = true;
+
+ this._obb = null;
+ this._obbDirty = true;
+
+ const state = this._state;
+ const gl = this.scene.canvas.gl;
+
+ // Primitive type
+
+ cfg.primitive = cfg.primitive || "triangles";
+ switch (cfg.primitive) {
+ case "points":
+ state.primitive = gl.POINTS;
+ state.primitiveName = cfg.primitive;
+ break;
+ case "lines":
+ state.primitive = gl.LINES;
+ state.primitiveName = cfg.primitive;
+ break;
+ case "line-loop":
+ state.primitive = gl.LINE_LOOP;
+ state.primitiveName = cfg.primitive;
+ break;
+ case "line-strip":
+ state.primitive = gl.LINE_STRIP;
+ state.primitiveName = cfg.primitive;
+ break;
+ case "triangles":
+ state.primitive = gl.TRIANGLES;
+ state.primitiveName = cfg.primitive;
+ break;
+ case "triangle-strip":
+ state.primitive = gl.TRIANGLE_STRIP;
+ state.primitiveName = cfg.primitive;
+ break;
+ case "triangle-fan":
+ state.primitive = gl.TRIANGLE_FAN;
+ state.primitiveName = cfg.primitive;
+ break;
+ default:
+ this.error("Unsupported value for 'primitive': '" + cfg.primitive +
+ "' - supported values are 'points', 'lines', 'line-loop', 'line-strip', 'triangles', " +
+ "'triangle-strip' and 'triangle-fan'. Defaulting to 'triangles'.");
+ state.primitive = gl.TRIANGLES;
+ state.primitiveName = cfg.primitive;
+ }
+
+ if (cfg.positions) {
+ if (this._state.quantized) {
+ var bounds = getBounds(cfg.positions, 3);
+ var quantized = quantizeVec3(cfg.positions, bounds.min, bounds.max);
+ state.positions = quantized.quantized;
+ state.positionsDecodeMatrix = quantized.decode;
+ } else {
+ state.positions = cfg.positions.constructor === Float32Array ? cfg.positions : new Float32Array(cfg.positions);
}
- if (cfg.uv) {
- if (this._state.quantized) {
- var bounds = getBounds(cfg.uv, 2);
- var quantized = quantizeVec2(cfg.uv, bounds.min, bounds.max);
- state.uv = quantized.quantized;
- state.uvDecodeMatrix = quantized.decode;
- } else {
- state.uv = cfg.uv.constructor === Float32Array ? cfg.uv : new Float32Array(cfg.uv);
- }
+ }
+ if (cfg.colors) {
+ state.colors = cfg.colors.constructor === Float32Array ? cfg.colors : new Float32Array(cfg.colors);
+ }
+ if (cfg.uv) {
+ if (this._state.quantized) {
+ var bounds = getBounds(cfg.uv, 2);
+ var quantized = quantizeVec2(cfg.uv, bounds.min, bounds.max);
+ state.uv = quantized.quantized;
+ state.uvDecodeMatrix = quantized.decode;
+ } else {
+ state.uv = cfg.uv.constructor === Float32Array ? cfg.uv : new Float32Array(cfg.uv);
}
- if (cfg.normals) {
- if (this._state.quantized) {
- state.normals = octEncode(cfg.normals);
- } else {
- state.normals = cfg.normals.constructor === Float32Array ? cfg.normals : new Float32Array(cfg.normals);
- }
+ }
+ if (cfg.normals) {
+ if (this._state.quantized) {
+ state.normals = octEncode(cfg.normals);
+ } else {
+ state.normals = cfg.normals.constructor === Float32Array ? cfg.normals : new Float32Array(cfg.normals);
}
- if (cfg.indices) {
- if (!bigIndicesSupported && cfg.indices.constructor === Uint32Array) {
- this.error("This WebGL implementation does not support Uint32Array");
- return;
- }
- state.indices = (cfg.indices.constructor === Uint32Array || cfg.indices.constructor === Uint16Array) ? cfg.indices : new IndexArrayType(cfg.indices);
+ }
+ if (cfg.indices) {
+ if (!bigIndicesSupported && cfg.indices.constructor === Uint32Array) {
+ this.error("This WebGL implementation does not support Uint32Array");
+ return;
}
+ state.indices = (cfg.indices.constructor === Uint32Array || cfg.indices.constructor === Uint16Array) ? cfg.indices : new IndexArrayType(cfg.indices);
+ }
- if (state.indices) {
- state.indicesBuf = new xeogl.renderer.ArrayBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, state.indices, state.indices.length, 1, gl.STATIC_DRAW);
- memoryStats.indices += state.indicesBuf.numItems;
- }
+ if (state.indices) {
+ state.indicesBuf = new ArrayBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, state.indices, state.indices.length, 1, gl.STATIC_DRAW);
+ memoryStats.indices += state.indicesBuf.numItems;
+ }
- this._buildHash();
+ this._buildHash();
- memoryStats.meshes++;
+ memoryStats.meshes++;
- if (this._state.combined) {
- this._sceneVertexBufs = xeogl.SceneVertexBufs.get(this.scene, this._state);
- this._sceneVertexBufs.addGeometry(this._state);
- }
+ if (this._state.combined) {
+ this._sceneVertexBufs = getSceneVertexBufs(this.scene, this._state);
+ this._sceneVertexBufs.addGeometry(this._state);
+ }
- this._buildVBOs();
+ this._buildVBOs();
- self.fire("created", this.created = true);
- },
+ self.fire("created", this.created = true);
+ }
- _buildVBOs: function () {
- const state = this._state;
- const gl = this.scene.canvas.gl;
+ _buildVBOs() {
+ const state = this._state;
+ const gl = this.scene.canvas.gl;
+ if (state.indices) {
+ state.indicesBuf = new ArrayBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, state.indices, state.indices.length, 1, gl.STATIC_DRAW);
+ memoryStats.indices += state.indicesBuf.numItems;
+ }
+ if (state.combined) {
if (state.indices) {
- state.indicesBuf = new xeogl.renderer.ArrayBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, state.indices, state.indices.length, 1, gl.STATIC_DRAW);
- memoryStats.indices += state.indicesBuf.numItems;
- }
- if (state.combined) {
- if (state.indices) {
- // indicesBufCombined is created when VertexBufs are built for this Geometry
- }
- } else {
- if (state.positions) {
- state.positionsBuf = new xeogl.renderer.ArrayBuffer(gl, gl.ARRAY_BUFFER, state.positions, state.positions.length, 3, gl.STATIC_DRAW);
- memoryStats.positions += state.positionsBuf.numItems;
- }
- if (state.normals) {
- state.normalsBuf = new xeogl.renderer.ArrayBuffer(gl, gl.ARRAY_BUFFER, state.normals, state.normals.length, 3, gl.STATIC_DRAW);
- memoryStats.normals += state.normalsBuf.numItems;
- }
- if (state.colors) {
- state.colorsBuf = new xeogl.renderer.ArrayBuffer(gl, gl.ARRAY_BUFFER, state.colors, state.colors.length, 4, gl.STATIC_DRAW);
- memoryStats.colors += state.colorsBuf.numItems;
- }
- if (state.uv) {
- state.uvBuf = new xeogl.renderer.ArrayBuffer(gl, gl.ARRAY_BUFFER, state.uv, state.uv.length, 2, gl.STATIC_DRAW);
- memoryStats.uvs += state.uvBuf.numItems;
- }
+ // indicesBufCombined is created when VertexBufs are built for this Geometry
}
- },
-
- _buildHash: function () {
- const state = this._state;
- const hash = ["/g"];
- hash.push("/" + state.primitive + ";");
+ } else {
if (state.positions) {
- hash.push("p");
+ state.positionsBuf = new ArrayBuffer(gl, gl.ARRAY_BUFFER, state.positions, state.positions.length, 3, gl.STATIC_DRAW);
+ memoryStats.positions += state.positionsBuf.numItems;
}
- if (state.colors) {
- hash.push("c");
+ if (state.normals) {
+ state.normalsBuf = new ArrayBuffer(gl, gl.ARRAY_BUFFER, state.normals, state.normals.length, 3, gl.STATIC_DRAW);
+ memoryStats.normals += state.normalsBuf.numItems;
}
- if (state.normals || state.autoVertexNormals) {
- hash.push("n");
+ if (state.colors) {
+ state.colorsBuf = new ArrayBuffer(gl, gl.ARRAY_BUFFER, state.colors, state.colors.length, 4, gl.STATIC_DRAW);
+ memoryStats.colors += state.colorsBuf.numItems;
}
if (state.uv) {
- hash.push("u");
- }
- if (state.quantized) {
- hash.push("cp");
+ state.uvBuf = new ArrayBuffer(gl, gl.ARRAY_BUFFER, state.uv, state.uv.length, 2, gl.STATIC_DRAW);
+ memoryStats.uvs += state.uvBuf.numItems;
}
- hash.push(";");
- state.hash = hash.join("");
- },
+ }
+ }
- _getEdgesIndices: function () {
- if (!this._edgesIndicesBuf) {
- this._buildEdgesIndices();
- }
- return this._edgesIndicesBuf;
- },
+ _buildHash() {
+ const state = this._state;
+ const hash = ["/g"];
+ hash.push("/" + state.primitive + ";");
+ if (state.positions) {
+ hash.push("p");
+ }
+ if (state.colors) {
+ hash.push("c");
+ }
+ if (state.normals || state.autoVertexNormals) {
+ hash.push("n");
+ }
+ if (state.uv) {
+ hash.push("u");
+ }
+ if (state.quantized) {
+ hash.push("cp");
+ }
+ hash.push(";");
+ state.hash = hash.join("");
+ }
- _getPickTrianglePositions: function () {
- if (!this._pickTrianglePositionsBuf) {
- this._buildPickTriangleVBOs();
- }
- return this._pickTrianglePositionsBuf;
- },
+ _getEdgesIndices() {
+ if (!this._edgesIndicesBuf) {
+ this._buildEdgesIndices();
+ }
+ return this._edgesIndicesBuf;
+ }
- _getPickTriangleColors: function () {
- if (!this._pickTriangleColorsBuf) {
- this._buildPickTriangleVBOs();
- }
- return this._pickTriangleColorsBuf;
- },
+ _getPickTrianglePositions() {
+ if (!this._pickTrianglePositionsBuf) {
+ this._buildPickTriangleVBOs();
+ }
+ return this._pickTrianglePositionsBuf;
+ }
- _buildEdgesIndices: function () { // FIXME: Does not adjust indices after other objects are deleted from vertex buffer!!
- const state = this._state;
- if (!state.positions || !state.indices) {
- return;
- }
- const gl = this.scene.canvas.gl;
- const edgesIndices = buildEdgesIndices(state.positions, state.indices, state.positionsDecodeMatrix, this._edgeThreshold, state.combined);
- if (state.combined) {
- const indicesOffset = this._sceneVertexBufs.getIndicesOffset(state);
- for (let i = 0, len = edgesIndices.length; i < len; i++) {
- edgesIndices[i] += indicesOffset;
- }
- }
- this._edgesIndicesBuf = new xeogl.renderer.ArrayBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, edgesIndices, edgesIndices.length, 1, gl.STATIC_DRAW);
- memoryStats.indices += this._edgesIndicesBuf.numItems;
- },
+ _getPickTriangleColors() {
+ if (!this._pickTriangleColorsBuf) {
+ this._buildPickTriangleVBOs();
+ }
+ return this._pickTriangleColorsBuf;
+ }
- _buildPickTriangleVBOs: function () { // Builds positions and indices arrays that allow each triangle to have a unique color
- const state = this._state;
- if (!state.positions || !state.indices) {
- return;
- }
- const gl = this.scene.canvas.gl;
- const arrays = xeogl.math.buildPickTriangles(state.positions, state.indices, state.quantized);
- const positions = arrays.positions;
- const colors = arrays.colors;
- this._pickTrianglePositionsBuf = new xeogl.renderer.ArrayBuffer(gl, gl.ARRAY_BUFFER, positions, positions.length, 3, gl.STATIC_DRAW);
- this._pickTriangleColorsBuf = new xeogl.renderer.ArrayBuffer(gl, gl.ARRAY_BUFFER, colors, colors.length, 4, gl.STATIC_DRAW, true);
- memoryStats.positions += this._pickTrianglePositionsBuf.numItems;
- memoryStats.colors += this._pickTriangleColorsBuf.numItems;
- },
-
- _buildPickVertexVBOs: function () {
- // var state = this._state;
- // if (!state.positions || !state.indices) {
- // return;
- // }
- // var gl = this.scene.canvas.gl;
- // var arrays = xeogl.math.buildPickVertices(state.positions, state.indices, state.quantized);
- // var pickVertexPositions = arrays.positions;
- // var pickColors = arrays.colors;
- // this._pickVertexPositionsBuf = new xeogl.renderer.ArrayBuffer(gl, gl.ARRAY_BUFFER, pickVertexPositions, pickVertexPositions.length, 3, gl.STATIC_DRAW);
- // this._pickVertexColorsBuf = new xeogl.renderer.ArrayBuffer(gl, gl.ARRAY_BUFFER, pickColors, pickColors.length, 4, gl.STATIC_DRAW, true);
- // memoryStats.positions += this._pickVertexPositionsBuf.numItems;
- // memoryStats.colors += this._pickVertexColorsBuf.numItems;
- },
-
- _webglContextLost: function() {
- if (this._sceneVertexBufs) {
- this._sceneVertexBufs.webglContextLost();
+ _buildEdgesIndices() { // FIXME: Does not adjust indices after other objects are deleted from vertex buffer!!
+ const state = this._state;
+ if (!state.positions || !state.indices) {
+ return;
+ }
+ const gl = this.scene.canvas.gl;
+ const edgesIndices = buildEdgesIndices(state.positions, state.indices, state.positionsDecodeMatrix, this._edgeThreshold, state.combined);
+ if (state.combined) {
+ const indicesOffset = this._sceneVertexBufs.getIndicesOffset(state);
+ for (let i = 0, len = edgesIndices.length; i < len; i++) {
+ edgesIndices[i] += indicesOffset;
}
- },
+ }
+ this._edgesIndicesBuf = new ArrayBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, edgesIndices, edgesIndices.length, 1, gl.STATIC_DRAW);
+ memoryStats.indices += this._edgesIndicesBuf.numItems;
+ }
- _webglContextRestored: function () {
- if (this._sceneVertexBufs) {
- this._sceneVertexBufs.webglContextRestored();
- }
- this._buildVBOs();
- this._edgesIndicesBuf = null;
- this._pickVertexPositionsBuf = null;
- this._pickTrianglePositionsBuf = null;
- this._pickTriangleColorsBuf = null;
- this._pickVertexPositionsBuf = null;
- this._pickVertexColorsBuf = null;
- },
-
- _props: {
-
- /**
- The Geometry's primitive type.
-
- Valid types are: 'points', 'lines', 'line-loop', 'line-strip', 'triangles', 'triangle-strip' and 'triangle-fan'.
-
- @property primitive
- @default "triangles"
- @type String
- */
- primitive: {
- get: function () {
- return this._state.primitiveName;
- }
- },
+ _buildPickTriangleVBOs() { // Builds positions and indices arrays that allow each triangle to have a unique color
+ const state = this._state;
+ if (!state.positions || !state.indices) {
+ return;
+ }
+ const gl = this.scene.canvas.gl;
+ const arrays = math.buildPickTriangles(state.positions, state.indices, state.quantized);
+ const positions = arrays.positions;
+ const colors = arrays.colors;
+ this._pickTrianglePositionsBuf = new ArrayBuffer(gl, gl.ARRAY_BUFFER, positions, positions.length, 3, gl.STATIC_DRAW);
+ this._pickTriangleColorsBuf = new ArrayBuffer(gl, gl.ARRAY_BUFFER, colors, colors.length, 4, gl.STATIC_DRAW, true);
+ memoryStats.positions += this._pickTrianglePositionsBuf.numItems;
+ memoryStats.colors += this._pickTriangleColorsBuf.numItems;
+ }
- /**
- Indicates if this Geometry is quantized.
+ _buildPickVertexVBOs() {
+ // var state = this._state;
+ // if (!state.positions || !state.indices) {
+ // return;
+ // }
+ // var gl = this.scene.canvas.gl;
+ // var arrays = math.buildPickVertices(state.positions, state.indices, state.quantized);
+ // var pickVertexPositions = arrays.positions;
+ // var pickColors = arrays.colors;
+ // this._pickVertexPositionsBuf = new xeogl.renderer.ArrayBuffer(gl, gl.ARRAY_BUFFER, pickVertexPositions, pickVertexPositions.length, 3, gl.STATIC_DRAW);
+ // this._pickVertexColorsBuf = new xeogl.renderer.ArrayBuffer(gl, gl.ARRAY_BUFFER, pickColors, pickColors.length, 4, gl.STATIC_DRAW, true);
+ // memoryStats.positions += this._pickVertexPositionsBuf.numItems;
+ // memoryStats.colors += this._pickVertexColorsBuf.numItems;
+ }
- Compression is an internally-performed optimization which stores positions, colors, normals and UVs
- in quantized and oct-encoded formats for reduced memory footprint and GPU bus usage.
+ _webglContextLost() {
+ if (this._sceneVertexBufs) {
+ this._sceneVertexBufs.webglContextLost();
+ }
+ }
- Quantized geometry may not be updated.
+ _webglContextRestored() {
+ if (this._sceneVertexBufs) {
+ this._sceneVertexBufs.webglContextRestored();
+ }
+ this._buildVBOs();
+ this._edgesIndicesBuf = null;
+ this._pickVertexPositionsBuf = null;
+ this._pickTrianglePositionsBuf = null;
+ this._pickTriangleColorsBuf = null;
+ this._pickVertexPositionsBuf = null;
+ this._pickVertexColorsBuf = null;
+ }
- @property quantized
- @default false
- @type Boolean
- @final
- */
- quantized: {
- get: function () {
- return this._state.quantized;
- }
- },
-
- /**
- Indicates if this Geometry is combined.
-
- Combination is an internally-performed optimization which combines positions, colors, normals and UVs into
- the same WebGL vertex buffers with other Geometries, in order to reduce the number of buffer binds
- performed per frame.
-
- @property combined
- @default false
- @type Boolean
- @final
- */
- combined: {
- get: function () {
- return this._state.combined;
- }
- },
-
-
- /**
- The Geometry's vertex positions.
-
- @property positions
- @default null
- @type Float32Array
- */
- positions: {
- get: function () {
- if (!this._state.positions) {
- return;
- }
- if (!this._state.quantized) {
- return this._state.positions;
- }
- if (!this._decompressedPositions) {
- this._decompressedPositions = new Float32Array(this._state.positions.length);
- xeogl.math.decompressPositions(this._state.positions, this._state.positionsDecodeMatrix, this._decompressedPositions);
- }
- return this._decompressedPositions;
- },
-
- set: function (newPositions) {
- const state = this._state;
- const positions = state.positions;
- if (!positions) {
- this.error("can't update geometry positions - geometry has no positions");
- return;
- }
- if (positions.length !== newPositions.length) {
- this.error("can't update geometry positions - new positions are wrong length");
- return;
- }
- if (this._state.quantized) {
- const bounds = getBounds(newPositions, 3);
- const quantized = quantizeVec3(newPositions, bounds.min, bounds.max);
- newPositions = quantized.quantized; // TODO: Copy in-place
- state.positionsDecodeMatrix = quantized.decode;
- }
- positions.set(newPositions);
- if (state.positionsBuf) {
- state.positionsBuf.setData(positions);
- }
- if (this._state.combined) {
- this._sceneVertexBufs.setPositions(state);
- }
- this._setBoundaryDirty();
- this._renderer.imageDirty();
- }
- },
-
- /**
- The Geometry's vertex normals.
-
- @property normals
- @default null
- @type Float32Array
- */
- normals: {
- get: function () {
- if (!this._state.normals) {
- return;
- }
- if (!this._state.quantized) {
- return this._state.normals;
- }
- if (!this._decompressedNormals) {
- const lenCompressed = this._state.normals.length;
- const lenDecompressed = lenCompressed + (lenCompressed / 2); // 2 -> 3
- this._decompressedNormals = new Float32Array(lenDecompressed);
- xeogl.math.octDecodeVec2s(this._state.normals, this._decompressedNormals);
- }
- return this._decompressedNormals;
- },
-
- set: function (newNormals) {
- if (this._state.quantized) {
- this.error("can't update geometry normals - quantized geometry is immutable"); // But will be eventually
- return;
- }
- const state = this._state;
- const normals = state.normals;
- if (!normals) {
- this.error("can't update geometry normals - geometry has no normals");
- return;
- }
- if (normals.length !== newNormals.length) {
- this.error("can't update geometry normals - new normals are wrong length");
- return;
- }
- normals.set(newNormals);
- if (state.normalsBuf) {
- state.normalsBuf.setData(normals);
- }
- if (this._state.combined) {
- this._sceneVertexBufs.setNormals(state);
- }
- this._renderer.imageDirty();
- }
- },
-
- /**
- The Geometry's UV coordinates.
-
- @property uv
- @default null
- @type Float32Array
- */
- uv: {
- get: function () {
- if (!this._state.uv) {
- return;
- }
- if (!this._state.quantized) {
- return this._state.uv;
- }
- if (!this._decompressedUV) {
- this._decompressedUV = new Float32Array(this._state.uv.length);
- xeogl.math.decompressUVs(this._state.uv, this._state.uvDecodeMatrix, this._decompressedUV);
- }
- return this._decompressedUV;
- },
-
- set: function (newUV) {
- if (this._state.quantized) {
- this.error("can't update geometry UVs - quantized geometry is immutable"); // But will be eventually
- return;
- }
- const state = this._state;
- const uv = state.uv;
- if (!uv) {
- this.error("can't update geometry UVs - geometry has no UVs");
- return;
- }
- if (uv.length !== newUV.length) {
- this.error("can't update geometry UVs - new UVs are wrong length");
- return;
- }
- uv.set(newUV);
- if (state.uvBuf) {
- state.uvBuf.setData(uv);
- }
- if (this._state.combined) {
- this._sceneVertexBufs.setUVs(state);
- }
- this._renderer.imageDirty();
- }
- },
-
- /**
- The Geometry's vertex colors.
-
- @property colors
- @default null
- @type Float32Array
- */
- colors: {
- get: function () {
- return this._state.colors;
- },
-
- set: function (newColors) {
- if (this._state.quantized) {
- this.error("can't update geometry colors - quantized geometry is immutable"); // But will be eventually
- return;
- }
- const state = this._state;
- const colors = state.colors;
- if (!colors) {
- this.error("can't update geometry colors - geometry has no colors");
- return;
- }
- if (colors.length !== newColors.length) {
- this.error("can't update geometry colors - new colors are wrong length");
- return;
- }
- colors.set(newColors);
- if (state.colorsBuf) {
- state.colorsBuf.setData(colors);
- }
- if (this._state.combined) {
- this._sceneVertexBufs.setColors(state);
- }
- this._renderer.imageDirty();
- }
- },
-
- /**
- The Geometry's indices.
-
- If ````xeogl.WEBGL_INFO.SUPPORTED_EXTENSIONS["OES_element_index_uint"]```` is true, then this can be
- a ````Uint32Array````, otherwise it needs to be a ````Uint16Array````.
-
- @property indices
- @default null
- @type Uint16Array | Uint32Array
- @final
- */
- indices: {
- get: function () {
- return this._state.indices;
- }
- },
-
- /**
- * Local-space axis-aligned 3D boundary (AABB) of this geometry.
- *
- * The AABB is represented by a six-element Float32Array containing the min/max extents of the
- * axis-aligned volume, ie. ````[xmin, ymin,zmin,xmax,ymax, zmax]````.
- *
- * @property aabb
- * @final
- * @type {Float32Array}
- */
- aabb: {
- get: function () {
- if (this._aabbDirty) {
- if (!this._aabb) {
- this._aabb = xeogl.math.AABB3();
- }
- xeogl.math.positions3ToAABB3(this._state.positions, this._aabb, this._state.positionsDecodeMatrix);
- this._aabbDirty = false;
- }
- return this._aabb;
- }
- },
-
- /**
- * Local-space oriented 3D boundary (OBB) of this geometry.
- *
- * The OBB is represented by a 32-element Float32Array containing the eight vertices of the box,
- * where each vertex is a homogeneous coordinate having [x,y,z,w] elements.
- *
- * @property obb
- * @final
- * @type {Float32Array}
- */
- obb: {
- get: (function () {
- const aabb = xeogl.math.AABB3();
- return function () {
- if (this._obbDirty) {
- if (!this._obb) {
- this._obb = xeogl.math.OBB3();
- }
- xeogl.math.positions3ToAABB3(this._state.positions, aabb, this._state.positionsDecodeMatrix);
- xeogl.math.AABB3ToOBB3(aabb, this._obb);
- this._obbDirty = false;
- }
- return this._obb;
- };
- })()
- },
-
- kdtree: {
- get: function () {
- const state = this._state;
- if (!state.indices || !state.positions) {
- this.error("Can't provide a KD-tree: no indices/positions");
- return;
- }
- if (!this._kdtree) {
- this._kdtree = xeogl.math.buildKDTree(state.indices, state.positions, this._state.positionsDecodeMatrix);
- }
- return this._kdtree;
- }
- }
- },
+ /**
+ The Geometry's primitive type.
- _setBoundaryDirty: function () {
- if (this._boundaryDirty) {
- return;
- }
- this._boundaryDirty = true;
- this._aabbDirty = true;
- this._obbDirty = true;
+ Valid types are: 'points', 'lines', 'line-loop', 'line-strip', 'triangles', 'triangle-strip' and 'triangle-fan'.
- /**
- Fired whenever this Geometry's boundary changes.
+ @property primitive
+ @default "triangles"
+ @type String
+ */
+ get primitive() {
+ return this._state.primitiveName;
+ }
- Get the latest boundary from the Geometry's {{#crossLink "Geometry/aabb:property"}}{{/crossLink}}
- and {{#crossLink "Geometry/obb:property"}}{{/crossLink}} properties.
+ /**
+ Indicates if this Geometry is quantized.
- @event boundary
+ Compression is an internally-performed optimization which stores positions, colors, normals and UVs
+ in quantized and oct-encoded formats for reduced memory footprint and GPU bus usage.
- */
- this.fire("boundary");
- },
+ Quantized geometry may not be updated.
- _getState: function () {
- return this._state;
- },
+ @property quantized
+ @default false
+ @type Boolean
+ @final
+ */
+ get quantized() {
+ return this._state.quantized;
+ }
- _getVertexBufs: function () {
- return this._state && this._state.combined ? this._sceneVertexBufs.getVertexBufs(this._state) : nullVertexBufs;
- },
+ /**
+ Indicates if this Geometry is combined.
- _destroy: function () {
- const state = this._state;
- if (state.indicesBuf) {
- state.indicesBuf.destroy();
- }
- if (this._edgesIndicesBuf) {
- this._edgesIndicesBuf.destroy();
- }
- if (this._pickTrianglePositionsBuf) {
- this._pickTrianglePositionsBuf.destroy();
- }
- if (this._pickTriangleColorsBuf) {
- this._pickTriangleColorsBuf.destroy();
- }
- if (this._pickVertexPositionsBuf) {
- this._pickVertexPositionsBuf.destroy();
- }
- if (this._pickVertexColorsBuf) {
- this._pickVertexColorsBuf.destroy();
- }
- if (this._state.combined) {
- this._sceneVertexBufs.removeGeometry(state);
- }
- state.destroy();
- memoryStats.meshes--;
- }
- });
-
- function getBounds(array, stride) {
- const min = new Float32Array(stride);
- const max = new Float32Array(stride);
- let i, j;
- for (i = 0; i < stride; i++) {
- min[i] = Number.MAX_VALUE;
- max[i] = -Number.MAX_VALUE;
- }
- for (i = 0; i < array.length; i += stride) {
- for (j = 0; j < stride; j++) {
- min[j] = Math.min(min[j], array[i + j]);
- max[j] = Math.max(max[j], array[i + j]);
- }
+ Combination is an internally-performed optimization which combines positions, colors, normals and UVs into
+ the same WebGL vertex buffers with other Geometries, in order to reduce the number of buffer binds
+ performed per frame.
+
+ @property combined
+ @default false
+ @type Boolean
+ @final
+ */
+ get combined() {
+ return this._state.combined;
+ }
+
+ /**
+ The Geometry's vertex positions.
+
+ @property positions
+ @default null
+ @type Float32Array
+ */
+ get positions() {
+ if (!this._state.positions) {
+ return;
}
- return {
- min: min,
- max: max
- };
+ if (!this._state.quantized) {
+ return this._state.positions;
+ }
+ if (!this._decompressedPositions) {
+ this._decompressedPositions = new Float32Array(this._state.positions.length);
+ math.decompressPositions(this._state.positions, this._state.positionsDecodeMatrix, this._decompressedPositions);
+ }
+ return this._decompressedPositions;
}
- // http://cg.postech.ac.kr/research/mesh_comp_mobile/mesh_comp_mobile_conference.pdf
- var quantizeVec3 = (function () {
- const math = xeogl.math;
- const translate = math.mat4();
- const scale = math.mat4();
- return function (array, min, max) {
- const quantized = new Uint16Array(array.length);
- const multiplier = new Float32Array([
- 65535 / (max[0] - min[0]),
- 65535 / (max[1] - min[1]),
- 65535 / (max[2] - min[2])
- ]);
- let i;
- for (i = 0; i < array.length; i += 3) {
- quantized[i + 0] = Math.floor((array[i + 0] - min[0]) * multiplier[0]);
- quantized[i + 1] = Math.floor((array[i + 1] - min[1]) * multiplier[1]);
- quantized[i + 2] = Math.floor((array[i + 2] - min[2]) * multiplier[2]);
- }
- math.identityMat4(translate);
- math.translationMat4v(min, translate);
- math.identityMat4(scale);
- math.scalingMat4v([
- (max[0] - min[0]) / 65535,
- (max[1] - min[1]) / 65535,
- (max[2] - min[2]) / 65535
- ], scale);
- const decodeMat = math.mulMat4(translate, scale, math.identityMat4());
- return {
- quantized: quantized,
- decode: decodeMat
- };
- };
- })();
-
- var quantizeVec2 = (function () {
- const math = xeogl.math;
- const translate = math.mat3();
- const scale = math.mat3();
- return function (array, min, max) {
- const quantized = new Uint16Array(array.length);
- const multiplier = new Float32Array([
- 65535 / (max[0] - min[0]),
- 65535 / (max[1] - min[1])
- ]);
- let i;
- for (i = 0; i < array.length; i += 2) {
- quantized[i + 0] = Math.floor((array[i + 0] - min[0]) * multiplier[0]);
- quantized[i + 1] = Math.floor((array[i + 1] - min[1]) * multiplier[1]);
- }
- math.identityMat3(translate);
- math.translationMat3v(min, translate);
- math.identityMat3(scale);
- math.scalingMat3v([
- (max[0] - min[0]) / 65535,
- (max[1] - min[1]) / 65535
- ], scale);
- const decodeMat = math.mulMat3(translate, scale, math.identityMat3());
- return {
- quantized: quantized,
- decode: decodeMat
- };
- };
- })();
-
- // http://jcgt.org/published/0003/02/01/
- function octEncode(array) {
- const encoded = new Int8Array(array.length * 2 / 3);
- let oct, dec, best, currentCos, bestCos;
- let i, ei;
- for (i = 0, ei = 0; i < array.length; i += 3, ei += 2) {
- // Test various combinations of ceil and floor
- // to minimize rounding errors
- best = oct = octEncodeVec3(array, i, "floor", "floor");
- dec = octDecodeVec2(oct);
- currentCos = bestCos = dot(array, i, dec);
- oct = octEncodeVec3(array, i, "ceil", "floor");
- dec = octDecodeVec2(oct);
- currentCos = dot(array, i, dec);
- if (currentCos > bestCos) {
- best = oct;
- bestCos = currentCos;
- }
- oct = octEncodeVec3(array, i, "floor", "ceil");
- dec = octDecodeVec2(oct);
- currentCos = dot(array, i, dec);
- if (currentCos > bestCos) {
- best = oct;
- bestCos = currentCos;
+ set positions(newPositions) {
+ const state = this._state;
+ const positions = state.positions;
+ if (!positions) {
+ this.error("can't update geometry positions - geometry has no positions");
+ return;
+ }
+ if (positions.length !== newPositions.length) {
+ this.error("can't update geometry positions - new positions are wrong length");
+ return;
+ }
+ if (this._state.quantized) {
+ const bounds = getBounds(newPositions, 3);
+ const quantized = quantizeVec3(newPositions, bounds.min, bounds.max);
+ newPositions = quantized.quantized; // TODO: Copy in-place
+ state.positionsDecodeMatrix = quantized.decode;
+ }
+ positions.set(newPositions);
+ if (state.positionsBuf) {
+ state.positionsBuf.setData(positions);
+ }
+ if (this._state.combined) {
+ this._sceneVertexBufs.setPositions(state);
+ }
+ this._setBoundaryDirty();
+ this._renderer.imageDirty();
+ }
+
+ /**
+ The Geometry's vertex normals.
+
+ @property normals
+ @default null
+ @type Float32Array
+ */
+ get normals() {
+ if (!this._state.normals) {
+ return;
+ }
+ if (!this._state.quantized) {
+ return this._state.normals;
+ }
+ if (!this._decompressedNormals) {
+ const lenCompressed = this._state.normals.length;
+ const lenDecompressed = lenCompressed + (lenCompressed / 2); // 2 -> 3
+ this._decompressedNormals = new Float32Array(lenDecompressed);
+ math.octDecodeVec2s(this._state.normals, this._decompressedNormals);
+ }
+ return this._decompressedNormals;
+ }
+
+ set normals(newNormals) {
+ if (this._state.quantized) {
+ this.error("can't update geometry normals - quantized geometry is immutable"); // But will be eventually
+ return;
+ }
+ const state = this._state;
+ const normals = state.normals;
+ if (!normals) {
+ this.error("can't update geometry normals - geometry has no normals");
+ return;
+ }
+ if (normals.length !== newNormals.length) {
+ this.error("can't update geometry normals - new normals are wrong length");
+ return;
+ }
+ normals.set(newNormals);
+ if (state.normalsBuf) {
+ state.normalsBuf.setData(normals);
+ }
+ if (this._state.combined) {
+ this._sceneVertexBufs.setNormals(state);
+ }
+ this._renderer.imageDirty();
+ }
+
+
+ /**
+ The Geometry's UV coordinates.
+
+ @property uv
+ @default null
+ @type Float32Array
+ */
+ get uv() {
+ if (!this._state.uv) {
+ return;
+ }
+ if (!this._state.quantized) {
+ return this._state.uv;
+ }
+ if (!this._decompressedUV) {
+ this._decompressedUV = new Float32Array(this._state.uv.length);
+ math.decompressUVs(this._state.uv, this._state.uvDecodeMatrix, this._decompressedUV);
+ }
+ return this._decompressedUV;
+ }
+
+ set uv(newUV) {
+ if (this._state.quantized) {
+ this.error("can't update geometry UVs - quantized geometry is immutable"); // But will be eventually
+ return;
+ }
+ const state = this._state;
+ const uv = state.uv;
+ if (!uv) {
+ this.error("can't update geometry UVs - geometry has no UVs");
+ return;
+ }
+ if (uv.length !== newUV.length) {
+ this.error("can't update geometry UVs - new UVs are wrong length");
+ return;
+ }
+ uv.set(newUV);
+ if (state.uvBuf) {
+ state.uvBuf.setData(uv);
+ }
+ if (this._state.combined) {
+ this._sceneVertexBufs.setUVs(state);
+ }
+ this._renderer.imageDirty();
+ }
+
+ /**
+ The Geometry's vertex colors.
+
+ @property colors
+ @default null
+ @type Float32Array
+ */
+ get colors() {
+ return this._state.colors;
+ }
+
+ set colors(newColors) {
+ if (this._state.quantized) {
+ this.error("can't update geometry colors - quantized geometry is immutable"); // But will be eventually
+ return;
+ }
+ const state = this._state;
+ const colors = state.colors;
+ if (!colors) {
+ this.error("can't update geometry colors - geometry has no colors");
+ return;
+ }
+ if (colors.length !== newColors.length) {
+ this.error("can't update geometry colors - new colors are wrong length");
+ return;
+ }
+ colors.set(newColors);
+ if (state.colorsBuf) {
+ state.colorsBuf.setData(colors);
+ }
+ if (this._state.combined) {
+ this._sceneVertexBufs.setColors(state);
+ }
+ this._renderer.imageDirty();
+ }
+
+ /**
+ The Geometry's indices.
+
+ If ````xeogl.WEBGL_INFO.SUPPORTED_EXTENSIONS["OES_element_index_uint"]```` is true, then this can be
+ a ````Uint32Array````, otherwise it needs to be a ````Uint16Array````.
+
+ @property indices
+ @default null
+ @type Uint16Array | Uint32Array
+ @final
+ */
+ get indices() {
+ return this._state.indices;
+ }
+
+ /**
+ * Local-space axis-aligned 3D boundary (AABB) of this geometry.
+ *
+ * The AABB is represented by a six-element Float32Array containing the min/max extents of the
+ * axis-aligned volume, ie. ````[xmin, ymin,zmin,xmax,ymax, zmax]````.
+ *
+ * @property aabb
+ * @final
+ * @type {Float32Array}
+ */
+ get aabb() {
+ if (this._aabbDirty) {
+ if (!this._aabb) {
+ this._aabb = math.AABB3();
}
- oct = octEncodeVec3(array, i, "ceil", "ceil");
- dec = octDecodeVec2(oct);
- currentCos = dot(array, i, dec);
- if (currentCos > bestCos) {
- best = oct;
- bestCos = currentCos;
+ math.positions3ToAABB3(this._state.positions, this._aabb, this._state.positionsDecodeMatrix);
+ this._aabbDirty = false;
+ }
+ return this._aabb;
+ }
+
+ /**
+ * Local-space oriented 3D boundary (OBB) of this geometry.
+ *
+ * The OBB is represented by a 32-element Float32Array containing the eight vertices of the box,
+ * where each vertex is a homogeneous coordinate having [x,y,z,w] elements.
+ *
+ * @property obb
+ * @final
+ * @type {Float32Array}
+ */
+ get obb() {
+ if (this._obbDirty) {
+ if (!this._obb) {
+ this._obb = math.OBB3();
}
- encoded[ei] = best[0];
- encoded[ei + 1] = best[1];
+ math.positions3ToAABB3(this._state.positions, tempAABB, this._state.positionsDecodeMatrix);
+ math.AABB3ToOBB3(tempAABB, this._obb);
+ this._obbDirty = false;
}
- return encoded;
+ return this._obb;
}
- // Oct-encode single normal vector in 2 bytes
- function octEncodeVec3(array, i, xfunc, yfunc) {
- let x = array[i] / (Math.abs(array[i]) + Math.abs(array[i + 1]) + Math.abs(array[i + 2]));
- let y = array[i + 1] / (Math.abs(array[i]) + Math.abs(array[i + 1]) + Math.abs(array[i + 2]));
- if (array[i + 2] < 0) {
- let tempx = x;
- let tempy = y;
- tempx = (1 - Math.abs(y)) * (x >= 0 ? 1 : -1);
- tempy = (1 - Math.abs(x)) * (y >= 0 ? 1 : -1);
- x = tempx;
- y = tempy;
- }
- return new Int8Array([
- Math[xfunc](x * 127.5 + (x < 0 ? -1 : 0)),
- Math[yfunc](y * 127.5 + (y < 0 ? -1 : 0))
- ]);
+ get kdtree() {
+ const state = this._state;
+ if (!state.indices || !state.positions) {
+ this.error("Can't provide a KD-tree: no indices/positions");
+ return;
+ }
+ if (!this._kdtree) {
+ this._kdtree = math.buildKDTree(state.indices, state.positions, this._state.positionsDecodeMatrix);
+ }
+ return this._kdtree;
}
- // Decode an oct-encoded normal
- function octDecodeVec2(oct) {
- let x = oct[0];
- let y = oct[1];
- x /= x < 0 ? 127 : 128;
- y /= y < 0 ? 127 : 128;
- const z = 1 - Math.abs(x) - Math.abs(y);
- if (z < 0) {
- x = (1 - Math.abs(y)) * (x >= 0 ? 1 : -1);
- y = (1 - Math.abs(x)) * (y >= 0 ? 1 : -1);
- }
- const length = Math.sqrt(x * x + y * y + z * z);
- return [
- x / length,
- y / length,
- z / length
- ];
+ _setBoundaryDirty() {
+ if (this._boundaryDirty) {
+ return;
+ }
+ this._boundaryDirty = true;
+ this._aabbDirty = true;
+ this._obbDirty = true;
+
+ /**
+ Fired whenever this Geometry's boundary changes.
+
+ Get the latest boundary from the Geometry's {{#crossLink "Geometry/aabb:property"}}{{/crossLink}}
+ and {{#crossLink "Geometry/obb:property"}}{{/crossLink}} properties.
+
+ @event boundary
+
+ */
+ this.fire("boundary");
}
- // Dot product of a normal in an array against a candidate decoding
- function dot(array, i, vec3) {
- return array[i] * vec3[0] + array[i + 1] * vec3[1] + array[i + 2] * vec3[2];
+ _getState() {
+ return this._state;
}
- var buildEdgesIndices = (function () {
-
- const math = xeogl.math;
-
- // TODO: Optimize with caching, but need to cater to both compressed and uncompressed positions
-
- const uniquePositions = [];
- const indicesLookup = [];
- const indicesReverseLookup = [];
- const weldedIndices = [];
-
- function weldVertices(positions, indices) {
- const positionsMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique)
- let vx;
- let vy;
- let vz;
- let key;
- const precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001
- const precision = Math.pow(10, precisionPoints);
- let i;
- let len;
- let lenUniquePositions = 0;
- for (i = 0, len = positions.length; i < len; i += 3) {
- vx = positions[i];
- vy = positions[i + 1];
- vz = positions[i + 2];
- key = Math.round(vx * precision) + '_' + Math.round(vy * precision) + '_' + Math.round(vz * precision);
- if (positionsMap[key] === undefined) {
- positionsMap[key] = lenUniquePositions / 3;
- uniquePositions[lenUniquePositions++] = vx;
- uniquePositions[lenUniquePositions++] = vy;
- uniquePositions[lenUniquePositions++] = vz;
- }
- indicesLookup[i / 3] = positionsMap[key];
+ _getVertexBufs() {
+ return this._state && this._state.combined ? this._sceneVertexBufs.getVertexBufs(this._state) : nullVertexBufs;
+ }
+
+ destroy() {
+ super.destroy();
+ const state = this._state;
+ if (state.indicesBuf) {
+ state.indicesBuf.destroy();
+ }
+ if (this._edgesIndicesBuf) {
+ this._edgesIndicesBuf.destroy();
+ }
+ if (this._pickTrianglePositionsBuf) {
+ this._pickTrianglePositionsBuf.destroy();
+ }
+ if (this._pickTriangleColorsBuf) {
+ this._pickTriangleColorsBuf.destroy();
+ }
+ if (this._pickVertexPositionsBuf) {
+ this._pickVertexPositionsBuf.destroy();
+ }
+ if (this._pickVertexColorsBuf) {
+ this._pickVertexColorsBuf.destroy();
+ }
+ if (this._state.combined) {
+ this._sceneVertexBufs.removeGeometry(state);
+ }
+ state.destroy();
+ memoryStats.meshes--;
+ }
+}
+
+function getBounds(array, stride) {
+ const min = new Float32Array(stride);
+ const max = new Float32Array(stride);
+ let i, j;
+ for (i = 0; i < stride; i++) {
+ min[i] = Number.MAX_VALUE;
+ max[i] = -Number.MAX_VALUE;
+ }
+ for (i = 0; i < array.length; i += stride) {
+ for (j = 0; j < stride; j++) {
+ min[j] = Math.min(min[j], array[i + j]);
+ max[j] = Math.max(max[j], array[i + j]);
+ }
+ }
+ return {
+ min: min,
+ max: max
+ };
+}
+
+// http://cg.postech.ac.kr/research/mesh_comp_mobile/mesh_comp_mobile_conference.pdf
+var quantizeVec3 = (function () {
+ const translate = math.mat4();
+ const scale = math.mat4();
+ return function (array, min, max) {
+ const quantized = new Uint16Array(array.length);
+ const multiplier = new Float32Array([
+ 65535 / (max[0] - min[0]),
+ 65535 / (max[1] - min[1]),
+ 65535 / (max[2] - min[2])
+ ]);
+ let i;
+ for (i = 0; i < array.length; i += 3) {
+ quantized[i + 0] = Math.floor((array[i + 0] - min[0]) * multiplier[0]);
+ quantized[i + 1] = Math.floor((array[i + 1] - min[1]) * multiplier[1]);
+ quantized[i + 2] = Math.floor((array[i + 2] - min[2]) * multiplier[2]);
+ }
+ math.identityMat4(translate);
+ math.translationMat4v(min, translate);
+ math.identityMat4(scale);
+ math.scalingMat4v([
+ (max[0] - min[0]) / 65535,
+ (max[1] - min[1]) / 65535,
+ (max[2] - min[2]) / 65535
+ ], scale);
+ const decodeMat = math.mulMat4(translate, scale, math.identityMat4());
+ return {
+ quantized: quantized,
+ decode: decodeMat
+ };
+ };
+})();
+
+var quantizeVec2 = (function () {
+ const translate = math.mat3();
+ const scale = math.mat3();
+ return function (array, min, max) {
+ const quantized = new Uint16Array(array.length);
+ const multiplier = new Float32Array([
+ 65535 / (max[0] - min[0]),
+ 65535 / (max[1] - min[1])
+ ]);
+ let i;
+ for (i = 0; i < array.length; i += 2) {
+ quantized[i + 0] = Math.floor((array[i + 0] - min[0]) * multiplier[0]);
+ quantized[i + 1] = Math.floor((array[i + 1] - min[1]) * multiplier[1]);
+ }
+ math.identityMat3(translate);
+ math.translationMat3v(min, translate);
+ math.identityMat3(scale);
+ math.scalingMat3v([
+ (max[0] - min[0]) / 65535,
+ (max[1] - min[1]) / 65535
+ ], scale);
+ const decodeMat = math.mulMat3(translate, scale, math.identityMat3());
+ return {
+ quantized: quantized,
+ decode: decodeMat
+ };
+ };
+})();
+
+// http://jcgt.org/published/0003/02/01/
+function octEncode(array) {
+ const encoded = new Int8Array(array.length * 2 / 3);
+ let oct, dec, best, currentCos, bestCos;
+ let i, ei;
+ for (i = 0, ei = 0; i < array.length; i += 3, ei += 2) {
+ // Test various combinations of ceil and floor
+ // to minimize rounding errors
+ best = oct = octEncodeVec3(array, i, "floor", "floor");
+ dec = octDecodeVec2(oct);
+ currentCos = bestCos = dot(array, i, dec);
+ oct = octEncodeVec3(array, i, "ceil", "floor");
+ dec = octDecodeVec2(oct);
+ currentCos = dot(array, i, dec);
+ if (currentCos > bestCos) {
+ best = oct;
+ bestCos = currentCos;
+ }
+ oct = octEncodeVec3(array, i, "floor", "ceil");
+ dec = octDecodeVec2(oct);
+ currentCos = dot(array, i, dec);
+ if (currentCos > bestCos) {
+ best = oct;
+ bestCos = currentCos;
+ }
+ oct = octEncodeVec3(array, i, "ceil", "ceil");
+ dec = octDecodeVec2(oct);
+ currentCos = dot(array, i, dec);
+ if (currentCos > bestCos) {
+ best = oct;
+ bestCos = currentCos;
+ }
+ encoded[ei] = best[0];
+ encoded[ei + 1] = best[1];
+ }
+ return encoded;
+}
+
+// Oct-encode single normal vector in 2 bytes
+function octEncodeVec3(array, i, xfunc, yfunc) {
+ let x = array[i] / (Math.abs(array[i]) + Math.abs(array[i + 1]) + Math.abs(array[i + 2]));
+ let y = array[i + 1] / (Math.abs(array[i]) + Math.abs(array[i + 1]) + Math.abs(array[i + 2]));
+ if (array[i + 2] < 0) {
+ let tempx = x;
+ let tempy = y;
+ tempx = (1 - Math.abs(y)) * (x >= 0 ? 1 : -1);
+ tempy = (1 - Math.abs(x)) * (y >= 0 ? 1 : -1);
+ x = tempx;
+ y = tempy;
+ }
+ return new Int8Array([
+ Math[xfunc](x * 127.5 + (x < 0 ? -1 : 0)),
+ Math[yfunc](y * 127.5 + (y < 0 ? -1 : 0))
+ ]);
+}
+
+// Decode an oct-encoded normal
+function octDecodeVec2(oct) {
+ let x = oct[0];
+ let y = oct[1];
+ x /= x < 0 ? 127 : 128;
+ y /= y < 0 ? 127 : 128;
+ const z = 1 - Math.abs(x) - Math.abs(y);
+ if (z < 0) {
+ x = (1 - Math.abs(y)) * (x >= 0 ? 1 : -1);
+ y = (1 - Math.abs(x)) * (y >= 0 ? 1 : -1);
+ }
+ const length = Math.sqrt(x * x + y * y + z * z);
+ return [
+ x / length,
+ y / length,
+ z / length
+ ];
+}
+
+// Dot product of a normal in an array against a candidate decoding
+function dot(array, i, vec3) {
+ return array[i] * vec3[0] + array[i + 1] * vec3[1] + array[i + 2] * vec3[2];
+}
+
+var buildEdgesIndices = (function () {
+
+ // TODO: Optimize with caching, but need to cater to both compressed and uncompressed positions
+
+ const uniquePositions = [];
+ const indicesLookup = [];
+ const indicesReverseLookup = [];
+ const weldedIndices = [];
+
+ function weldVertices(positions, indices) {
+ const positionsMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique)
+ let vx;
+ let vy;
+ let vz;
+ let key;
+ const precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001
+ const precision = Math.pow(10, precisionPoints);
+ let i;
+ let len;
+ let lenUniquePositions = 0;
+ for (i = 0, len = positions.length; i < len; i += 3) {
+ vx = positions[i];
+ vy = positions[i + 1];
+ vz = positions[i + 2];
+ key = Math.round(vx * precision) + '_' + Math.round(vy * precision) + '_' + Math.round(vz * precision);
+ if (positionsMap[key] === undefined) {
+ positionsMap[key] = lenUniquePositions / 3;
+ uniquePositions[lenUniquePositions++] = vx;
+ uniquePositions[lenUniquePositions++] = vy;
+ uniquePositions[lenUniquePositions++] = vz;
}
- for (i = 0, len = indices.length; i < len; i++) {
- weldedIndices[i] = indicesLookup[indices[i]];
- indicesReverseLookup[weldedIndices[i]] = indices[i];
+ indicesLookup[i / 3] = positionsMap[key];
+ }
+ for (i = 0, len = indices.length; i < len; i++) {
+ weldedIndices[i] = indicesLookup[indices[i]];
+ indicesReverseLookup[weldedIndices[i]] = indices[i];
+ }
+ }
+
+ const faces = [];
+ let numFaces = 0;
+ const compa = new Uint16Array(3);
+ const compb = new Uint16Array(3);
+ const compc = new Uint16Array(3);
+ const a = math.vec3();
+ const b = math.vec3();
+ const c = math.vec3();
+ const cb = math.vec3();
+ const ab = math.vec3();
+ const cross = math.vec3();
+ const normal = math.vec3();
+
+ function buildFaces(numIndices, positionsDecodeMatrix) {
+ numFaces = 0;
+ for (let i = 0, len = numIndices; i < len; i += 3) {
+ const ia = ((weldedIndices[i]) * 3);
+ const ib = ((weldedIndices[i + 1]) * 3);
+ const ic = ((weldedIndices[i + 2]) * 3);
+ if (positionsDecodeMatrix) {
+ compa[0] = uniquePositions[ia];
+ compa[1] = uniquePositions[ia + 1];
+ compa[2] = uniquePositions[ia + 2];
+ compb[0] = uniquePositions[ib];
+ compb[1] = uniquePositions[ib + 1];
+ compb[2] = uniquePositions[ib + 2];
+ compc[0] = uniquePositions[ic];
+ compc[1] = uniquePositions[ic + 1];
+ compc[2] = uniquePositions[ic + 2];
+ // Decode
+ math.decompressPosition(compa, positionsDecodeMatrix, a);
+ math.decompressPosition(compb, positionsDecodeMatrix, b);
+ math.decompressPosition(compc, positionsDecodeMatrix, c);
+ } else {
+ a[0] = uniquePositions[ia];
+ a[1] = uniquePositions[ia + 1];
+ a[2] = uniquePositions[ia + 2];
+ b[0] = uniquePositions[ib];
+ b[1] = uniquePositions[ib + 1];
+ b[2] = uniquePositions[ib + 2];
+ c[0] = uniquePositions[ic];
+ c[1] = uniquePositions[ic + 1];
+ c[2] = uniquePositions[ic + 2];
}
+ math.subVec3(c, b, cb);
+ math.subVec3(a, b, ab);
+ math.cross3Vec3(cb, ab, cross);
+ math.normalizeVec3(cross, normal);
+ const face = faces[numFaces] || (faces[numFaces] = {normal: math.vec3()});
+ face.normal[0] = normal[0];
+ face.normal[1] = normal[1];
+ face.normal[2] = normal[2];
+ numFaces++;
}
+ }
- const faces = [];
- let numFaces = 0;
- const compa = new Uint16Array(3);
- const compb = new Uint16Array(3);
- const compc = new Uint16Array(3);
- const a = math.vec3();
- const b = math.vec3();
- const c = math.vec3();
- const cb = math.vec3();
- const ab = math.vec3();
- const cross = math.vec3();
- const normal = math.vec3();
-
- function buildFaces(numIndices, positionsDecodeMatrix) {
- numFaces = 0;
- for (let i = 0, len = numIndices; i < len; i += 3) {
- const ia = ((weldedIndices[i]) * 3);
- const ib = ((weldedIndices[i + 1]) * 3);
- const ic = ((weldedIndices[i + 2]) * 3);
- if (positionsDecodeMatrix) {
- compa[0] = uniquePositions[ia];
- compa[1] = uniquePositions[ia + 1];
- compa[2] = uniquePositions[ia + 2];
- compb[0] = uniquePositions[ib];
- compb[1] = uniquePositions[ib + 1];
- compb[2] = uniquePositions[ib + 2];
- compc[0] = uniquePositions[ic];
- compc[1] = uniquePositions[ic + 1];
- compc[2] = uniquePositions[ic + 2];
- // Decode
- math.decompressPosition(compa, positionsDecodeMatrix, a);
- math.decompressPosition(compb, positionsDecodeMatrix, b);
- math.decompressPosition(compc, positionsDecodeMatrix, c);
+ return function (positions, indices, positionsDecodeMatrix, edgeThreshold, combined) {
+ weldVertices(positions, indices);
+ buildFaces(indices.length, positionsDecodeMatrix);
+ const edgeIndices = [];
+ const thresholdDot = Math.cos(math.DEGTORAD * edgeThreshold);
+ const edges = {};
+ let edge1;
+ let edge2;
+ let index1;
+ let index2;
+ let key;
+ let largeIndex = false;
+ let edge;
+ let normal1;
+ let normal2;
+ let dot;
+ let ia;
+ let ib;
+ for (let i = 0, len = indices.length; i < len; i += 3) {
+ const faceIndex = i / 3;
+ for (let j = 0; j < 3; j++) {
+ edge1 = weldedIndices[i + j];
+ edge2 = weldedIndices[i + ((j + 1) % 3)];
+ index1 = Math.min(edge1, edge2);
+ index2 = Math.max(edge1, edge2);
+ key = index1 + "," + index2;
+ if (edges[key] === undefined) {
+ edges[key] = {
+ index1: index1,
+ index2: index2,
+ face1: faceIndex,
+ face2: undefined
+ };
} else {
- a[0] = uniquePositions[ia];
- a[1] = uniquePositions[ia + 1];
- a[2] = uniquePositions[ia + 2];
- b[0] = uniquePositions[ib];
- b[1] = uniquePositions[ib + 1];
- b[2] = uniquePositions[ib + 2];
- c[0] = uniquePositions[ic];
- c[1] = uniquePositions[ic + 1];
- c[2] = uniquePositions[ic + 2];
+ edges[key].face2 = faceIndex;
}
- math.subVec3(c, b, cb);
- math.subVec3(a, b, ab);
- math.cross3Vec3(cb, ab, cross);
- math.normalizeVec3(cross, normal);
- const face = faces[numFaces] || (faces[numFaces] = {normal: math.vec3()});
- face.normal[0] = normal[0];
- face.normal[1] = normal[1];
- face.normal[2] = normal[2];
- numFaces++;
}
}
-
- return function (positions, indices, positionsDecodeMatrix, edgeThreshold, combined) {
- weldVertices(positions, indices);
- buildFaces(indices.length, positionsDecodeMatrix);
- const edgeIndices = [];
- const thresholdDot = Math.cos(xeogl.math.DEGTORAD * edgeThreshold);
- const edges = {};
- let edge1;
- let edge2;
- let index1;
- let index2;
- let key;
- let largeIndex = false;
- let edge;
- let normal1;
- let normal2;
- let dot;
- let ia;
- let ib;
- for (let i = 0, len = indices.length; i < len; i += 3) {
- const faceIndex = i / 3;
- for (let j = 0; j < 3; j++) {
- edge1 = weldedIndices[i + j];
- edge2 = weldedIndices[i + ((j + 1) % 3)];
- index1 = Math.min(edge1, edge2);
- index2 = Math.max(edge1, edge2);
- key = index1 + "," + index2;
- if (edges[key] === undefined) {
- edges[key] = {
- index1: index1,
- index2: index2,
- face1: faceIndex,
- face2: undefined
- };
- } else {
- edges[key].face2 = faceIndex;
- }
+ for (key in edges) {
+ edge = edges[key];
+ // an edge is only rendered if the angle (in degrees) between the face normals of the adjoining faces exceeds this value. default = 1 degree.
+ if (edge.face2 !== undefined) {
+ normal1 = faces[edge.face1].normal;
+ normal2 = faces[edge.face2].normal;
+ dot = math.dotVec3(normal1, normal2);
+ if (dot > thresholdDot) {
+ continue;
}
}
- for (key in edges) {
- edge = edges[key];
- // an edge is only rendered if the angle (in degrees) between the face normals of the adjoining faces exceeds this value. default = 1 degree.
- if (edge.face2 !== undefined) {
- normal1 = faces[edge.face1].normal;
- normal2 = faces[edge.face2].normal;
- dot = math.dotVec3(normal1, normal2);
- if (dot > thresholdDot) {
- continue;
- }
- }
- ia = edge.index1;
- ib = edge.index2;
- if (!largeIndex && ia > 65535 || ib > 65535) {
- largeIndex = true;
- }
- edgeIndices.push(indicesReverseLookup[ia]);
- edgeIndices.push(indicesReverseLookup[ib]);
+ ia = edge.index1;
+ ib = edge.index2;
+ if (!largeIndex && ia > 65535 || ib > 65535) {
+ largeIndex = true;
}
- return (largeIndex || combined) ? new Uint32Array(edgeIndices) : new Uint16Array(edgeIndices);
- };
- })();
-})();
\ No newline at end of file
+ edgeIndices.push(indicesReverseLookup[ia]);
+ edgeIndices.push(indicesReverseLookup[ib]);
+ }
+ return (largeIndex || combined) ? new Uint32Array(edgeIndices) : new Uint16Array(edgeIndices);
+ };
+})();
+
+export {Geometry};
\ No newline at end of file
diff --git a/src/geometry/obbGeometry.js b/src/geometry/obbGeometry.js
index 9195dc7ca..a6c4ab58b 100644
--- a/src/geometry/obbGeometry.js
+++ b/src/geometry/obbGeometry.js
@@ -62,8 +62,7 @@
@module xeogl
@submodule geometry
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}} - creates this OBBGeometry in the default
- {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
+ @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.
@@ -73,115 +72,113 @@
containing homogeneous coordinates for the eight corner vertices, ie. each having elements (x,y,z,w).
@extends Component
*/
-(function () {
-
- "use strict";
-
- xeogl.OBBGeometry = xeogl.Geometry.extend({
-
- type: "xeogl.OBBGeometry",
-
- _init: function (cfg) {
-
- this._super(xeogl._apply(cfg, {
- combined: true,
- quantized: false, // Quantized geometry is immutable
- primitive: cfg.primitive || "lines",
- positions: cfg.positions || [1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0,
- 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0],
- indices: [0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7]
- }));
-
- if (cfg.target) {
- this.target = cfg.target;
-
- } else if (cfg.targetOBB) {
- this.targetOBB = cfg.targetOBB;
- }
- },
-
- _props: {
-
- /**
- A component whose OBB we'll dynamically fit this AABBGeometry to.
-
- This property effectively replaces the {{#crossLink "OBBGeometry/targetOBB:property"}}{{/crossLink}} property.
-
- @property target
- @type Component
- */
- target: {
-
- set: function (value) {
-
- let geometryDirty = false;
- const self = this;
-
- this._attach({
- name: "target",
- type: "xeogl.Component",
- component: value,
- sceneDefault: false,
- on: {
- boundary: function () {
- if (geometryDirty) {
- return;
- }
- geometryDirty = true;
- xeogl.scheduleTask(function () {
- self._setPositionsFromOBB(self._attached.target.obb);
- geometryDirty = false;
- });
- }
- },
- onAttached: function () {
- self._setPositionsFromOBB(self._attached.target.obb);
- }
+import {core} from "./../core.js";
+import {utils} from '../utils.js';
+import {tasks} from '../tasks.js';
+import {Geometry} from './geometry.js';
+
+const type = "xeogl.OBBGeometry";
+
+class OBBGeometry extends Geometry {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return type;
+ }
+
+ init(cfg) {
+ super(owner, utils.apply(cfg, {
+ combined: true,
+ quantized: false, // Quantized geometry is immutable
+ primitive: cfg.primitive || "lines",
+ positions: cfg.positions || [1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0,
+ 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0],
+ indices: [0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7]
+ }));
+ if (cfg.target) {
+ this.target = cfg.target;
+ } else if (cfg.targetOBB) {
+ this.targetOBB = cfg.targetOBB;
+ }
+ }
+
+ /**
+ A component whose OBB we'll dynamically fit this AABBGeometry to.
+
+ This property effectively replaces the {{#crossLink "OBBGeometry/targetOBB:property"}}{{/crossLink}} property.
+
+ @property target
+ @type Component
+ */
+ set target(value) {
+ let geometryDirty = false;
+ const self = this;
+ this._attach({
+ name: "target",
+ type: "xeogl.Component",
+ component: value,
+ sceneDefault: false,
+ on: {
+ boundary: function () {
+ if (geometryDirty) {
+ return;
+ }
+ geometryDirty = true;
+ tasks.scheduleTask(function () {
+ self._setPositionsFromOBB(self._attached.target.obb);
+ geometryDirty = false;
});
- },
-
- get: function () {
- return this._attached.target;
}
},
+ onAttached: function () {
+ self._setPositionsFromOBB(self._attached.target.obb);
+ }
+ });
+ }
- /**
- Sets this OBBGeometry to an mesh-oriented bounding box (OBB), given as a 32-element Float32Array
- containing homogeneous coordinates for the eight corner vertices, ie. each having elements [x,y,z,w].
-
- This property effectively replaces the {{#crossLink "OBBGeometry/boundary:property"}}{{/crossLink}} property, causing it to become null.
-
- @property targetOBB
- @type Float32Array
- */
- targetOBB: {
+ get target() {
+ return this._attached.target;
+ }
- set: function (value) {
+ /**
+ Sets this OBBGeometry to an mesh-oriented bounding box (OBB), given as a 32-element Float32Array
+ containing homogeneous coordinates for the eight corner vertices, ie. each having elements [x,y,z,w].
- if (!value) {
- return;
- }
+ This property effectively replaces the {{#crossLink "OBBGeometry/boundary:property"}}{{/crossLink}} property, causing it to become null.
- if (this._attached.target) {
- this.target = null;
- }
-
- this._setPositionsFromOBB(value);
- }
- }
- },
-
- _setPositionsFromOBB: function (obb) {
- this.positions = [
- obb[0], obb[1], obb[2],
- obb[4], obb[5], obb[6],
- obb[8], obb[9], obb[10],
- obb[12], obb[13], obb[14],
- obb[16], obb[17], obb[18],
- obb[20], obb[21], obb[22],
- obb[24], obb[25], obb[26],
- obb[28], obb[29], obb[30]
- ];
+ @property targetOBB
+ @type Float32Array
+ */
+ set targetOBB(value) {
+ if (!value) {
+ return;
+ }
+ if (this._attached.target) {
+ this.target = null;
}
- });
-})();
+ this._setPositionsFromOBB(value);
+ }
+
+ _setPositionsFromOBB(obb) {
+ this.positions = [
+ obb[0], obb[1], obb[2],
+ obb[4], obb[5], obb[6],
+ obb[8], obb[9], obb[10],
+ obb[12], obb[13], obb[14],
+ obb[16], obb[17], obb[18],
+ obb[20], obb[21], obb[22],
+ obb[24], obb[25], obb[26],
+ obb[28], obb[29], obb[30]
+ ];
+ }
+}
+
+export{OBBGeometry};
diff --git a/src/geometry/pathGeometry.js b/src/geometry/pathGeometry.js
index 93b1bace8..50da921bc 100644
--- a/src/geometry/pathGeometry.js
+++ b/src/geometry/pathGeometry.js
@@ -53,7 +53,10 @@
@submodule geometry
@extends Geometry
*/
-xeogl.PathGeometry = xeogl.Geometry.extend({
+
+import {Geometry} from './geometry.js';
+
+const PathGeometry = Geometry.extend({
type: "xeogl.PathGeometry",
@@ -166,4 +169,6 @@ xeogl.PathGeometry = xeogl.Geometry.extend({
}
}
}
-});
\ No newline at end of file
+});
+
+export{PathGeometry};
\ No newline at end of file
diff --git a/src/geometry/planeGeometry.js b/src/geometry/planeGeometry.js
index 6334f0de2..b7c4a47cd 100644
--- a/src/geometry/planeGeometry.js
+++ b/src/geometry/planeGeometry.js
@@ -10,7 +10,7 @@
{{#crossLink "PlaneGeometry/zSegments:property"}}{{/crossLink}} properties.
* Dynamically switch its primitive type between ````"points"````, ````"lines"```` and ````"triangles"```` at any time by
updating its {{#crossLink "Geometry/primitive:property"}}{{/crossLink}} property.
-
+
## Examples
* [Textured PlaneGeometry](../../examples/#geometry_primitives_plane)
@@ -44,8 +44,7 @@
@module xeogl
@submodule geometry
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}} - creates this PlaneGeometry in the default
- {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
+ @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.
@@ -58,132 +57,145 @@
@param [cfg.zSegments=1] {Number} Number of segments on the Z-axis.
@extends Geometry
*/
-(function () {
+import {core} from "./../core.js";
+import {utils} from '../utils.js';
+import {Geometry} from './geometry.js';
- "use strict";
+const type = "xeogl.PlaneGeometry";
- xeogl.PlaneGeometry = xeogl.Geometry.extend({
+class PlaneGeometry extends Geometry {
- type: "xeogl.PlaneGeometry",
+ /**
+ JavaScript class name for this Component.
- _init: function (cfg) {
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
- let xSize = cfg.xSize || 1;
- if (xSize < 0) {
- this.error("negative xSize not allowed - will invert");
- xSize *= -1;
- }
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return type;
+ }
- let zSize = cfg.zSize || 1;
- if (zSize < 0) {
- this.error("negative zSize not allowed - will invert");
- zSize *= -1;
- }
+ constructor(owner = null, cfg = {}) {
- let xSegments = cfg.xSegments || 1;
- if (xSegments < 0) {
- this.error("negative xSegments not allowed - will invert");
- xSegments *= -1;
- }
- if (xSegments < 1) {
- xSegments = 1;
- }
+ let xSize = cfg.xSize || 1;
+ if (xSize < 0) {
+ this.error("negative xSize not allowed - will invert");
+ xSize *= -1;
+ }
- let zSegments = cfg.xSegments || 1;
- if (zSegments < 0) {
- this.error("negative zSegments not allowed - will invert");
- zSegments *= -1;
- }
- if (zSegments < 1) {
- zSegments = 1;
- }
+ let zSize = cfg.zSize || 1;
+ if (zSize < 0) {
+ this.error("negative zSize not allowed - will invert");
+ zSize *= -1;
+ }
- const center = cfg.center;
- const centerX = center ? center[0] : 0;
- const centerY = center ? center[1] : 0;
- const centerZ = center ? center[2] : 0;
+ let xSegments = cfg.xSegments || 1;
+ if (xSegments < 0) {
+ this.error("negative xSegments not allowed - will invert");
+ xSegments *= -1;
+ }
+ if (xSegments < 1) {
+ xSegments = 1;
+ }
+
+ let zSegments = cfg.xSegments || 1;
+ if (zSegments < 0) {
+ this.error("negative zSegments not allowed - will invert");
+ zSegments *= -1;
+ }
+ if (zSegments < 1) {
+ zSegments = 1;
+ }
- const halfWidth = xSize / 2;
- const halfHeight = zSize / 2;
+ const center = cfg.center;
+ const centerX = center ? center[0] : 0;
+ const centerY = center ? center[1] : 0;
+ const centerZ = center ? center[2] : 0;
- const planeX = Math.floor(xSegments) || 1;
- const planeZ = Math.floor(zSegments) || 1;
+ const halfWidth = xSize / 2;
+ const halfHeight = zSize / 2;
- const planeX1 = planeX + 1;
- const planeZ1 = planeZ + 1;
+ const planeX = Math.floor(xSegments) || 1;
+ const planeZ = Math.floor(zSegments) || 1;
- const segmentWidth = xSize / planeX;
- const segmentHeight = zSize / planeZ;
+ const planeX1 = planeX + 1;
+ const planeZ1 = planeZ + 1;
- const positions = new Float32Array(planeX1 * planeZ1 * 3);
- const normals = new Float32Array(planeX1 * planeZ1 * 3);
- const uvs = new Float32Array(planeX1 * planeZ1 * 2);
+ const segmentWidth = xSize / planeX;
+ const segmentHeight = zSize / planeZ;
- let offset = 0;
- let offset2 = 0;
+ const positions = new Float32Array(planeX1 * planeZ1 * 3);
+ const normals = new Float32Array(planeX1 * planeZ1 * 3);
+ const uvs = new Float32Array(planeX1 * planeZ1 * 2);
- let iz;
- let ix;
- let x;
- let a;
- let b;
- let c;
- let d;
+ let offset = 0;
+ let offset2 = 0;
- for (iz = 0; iz < planeZ1; iz++) {
+ let iz;
+ let ix;
+ let x;
+ let a;
+ let b;
+ let c;
+ let d;
- const z = iz * segmentHeight - halfHeight;
+ for (iz = 0; iz < planeZ1; iz++) {
- for (ix = 0; ix < planeX1; ix++) {
+ const z = iz * segmentHeight - halfHeight;
- x = ix * segmentWidth - halfWidth;
+ for (ix = 0; ix < planeX1; ix++) {
- positions[offset] = x + centerX;
- positions[offset + 1] = centerY;
- positions[offset + 2] = -z + centerZ;
+ x = ix * segmentWidth - halfWidth;
- normals[offset + 2] = -1;
+ positions[offset] = x + centerX;
+ positions[offset + 1] = centerY;
+ positions[offset + 2] = -z + centerZ;
- uvs[offset2] = (planeX - ix) / planeX;
- uvs[offset2 + 1] = ( (planeZ - iz) / planeZ );
+ normals[offset + 2] = -1;
- offset += 3;
- offset2 += 2;
- }
+ uvs[offset2] = (planeX - ix) / planeX;
+ uvs[offset2 + 1] = ( (planeZ - iz) / planeZ );
+
+ offset += 3;
+ offset2 += 2;
}
+ }
- offset = 0;
+ offset = 0;
- const indices = new ( ( positions.length / 3 ) > 65535 ? Uint32Array : Uint16Array )(planeX * planeZ * 6);
+ const indices = new ( ( positions.length / 3 ) > 65535 ? Uint32Array : Uint16Array )(planeX * planeZ * 6);
- for (iz = 0; iz < planeZ; iz++) {
+ for (iz = 0; iz < planeZ; iz++) {
- for (ix = 0; ix < planeX; ix++) {
+ for (ix = 0; ix < planeX; ix++) {
- a = ix + planeX1 * iz;
- b = ix + planeX1 * ( iz + 1 );
- c = ( ix + 1 ) + planeX1 * ( iz + 1 );
- d = ( ix + 1 ) + planeX1 * iz;
+ a = ix + planeX1 * iz;
+ b = ix + planeX1 * ( iz + 1 );
+ c = ( ix + 1 ) + planeX1 * ( iz + 1 );
+ d = ( ix + 1 ) + planeX1 * iz;
- indices[offset] = d;
- indices[offset + 1] = b;
- indices[offset + 2] = a;
+ indices[offset] = d;
+ indices[offset + 1] = b;
+ indices[offset + 2] = a;
- indices[offset + 3] = d;
- indices[offset + 4] = c;
- indices[offset + 5] = b;
+ indices[offset + 3] = d;
+ indices[offset + 4] = c;
+ indices[offset + 5] = b;
- offset += 6;
- }
+ offset += 6;
}
-
- this._super(xeogl._apply(cfg, {
- positions: positions,
- normals: normals,
- uv: uvs,
- indices: indices
- }));
}
- });
-})();
+ super(owner, utils.apply(cfg, {
+ positions: positions,
+ normals: normals,
+ uv: uvs,
+ indices: indices
+ }));
+ }
+}
+
+export {PlaneGeometry};
diff --git a/src/geometry/sceneVertexBufs.js b/src/geometry/sceneVertexBufs.js
index 9b50ecff6..e877767f1 100644
--- a/src/geometry/sceneVertexBufs.js
+++ b/src/geometry/sceneVertexBufs.js
@@ -1,333 +1,329 @@
-(function () {
-
- const CHUNK_LEN = bigIndicesSupported ? (Number.MAX_SAFE_INTEGER / 6) : (64000 * 4); // RGBA is largest item
- const memoryStats = xeogl.stats.memory;
- var bigIndicesSupported = xeogl.WEBGL_INFO.SUPPORTED_EXTENSIONS["OES_element_index_uint"];
- const IndexArrayType = bigIndicesSupported ? Uint32Array : Uint16Array;
- const nullVertexBufs = new xeogl.renderer.State({});
-
- xeogl.SceneVertexBufs = function (scene, hasPositions, hasNormals, hasColors, hasUVs, quantized) {
-
- const gl = scene.canvas.gl;
- let contextLost = false;
- const geometries = {};
- const geometryIndicesOffsets = {};
- const newGeometries = [];
- let geometryVertexBufs = {};
- let needRebuild = false;
- let needAppend = false;
- let positions = [];
- let normals = [];
- let colors = [];
- let uv = [];
- let vertexBufs = null;
-
- this.addGeometry = function (geometry) {
- if (!geometry.positions || !geometry.indices) {
- scene.warn("Ignoring geometry with no positions or indices: " + geometry.id);
- return;
- }
- geometries[geometry.id] = geometry;
- geometryIndicesOffsets[geometry.id] = 0; // Will initialize below
- newGeometries.push(geometry);
- needAppend = true;
- };
-
- this.getIndicesOffset = function (geometry) {
- if (needRebuild || needAppend) {
- build();
- }
- return geometryIndicesOffsets[geometry.id];
- };
+import {State} from '../renderer/state.js';
+import {ArrayBuffer} from '../renderer/arrayBuffer.js';
+import {stats} from './../stats.js';
+import {WEBGL_INFO} from './../webglInfo.js';
+
+const CHUNK_LEN = bigIndicesSupported ? (Number.MAX_SAFE_INTEGER / 6) : (64000 * 4); // RGBA is largest item
+const memoryStats = stats.memory;
+var bigIndicesSupported = WEBGL_INFO.SUPPORTED_EXTENSIONS["OES_element_index_uint"];
+const IndexArrayType = bigIndicesSupported ? Uint32Array : Uint16Array;
+const nullVertexBufs = new State({});
+
+class SceneVertexBufs {
+
+ constructor(scene, hasPositions, hasNormals, hasColors, hasUVs, quantized) {
+
+ this.scene = scene;
+ this.gl = scene.canvas.gl;
+ this.contextLost = false;
+ this.geometries = {};
+ this.geometryIndicesOffsets = {};
+ this.newGeometries = [];
+ this.geometryVertexBufs = {};
+ this.needRebuild = false;
+ this.needAppend = false;
+ this.positions = hasPositions ? [] : null;
+ this.normals = hasNormals ? [] : null;
+ this.colors = hasColors ? [] : null;
+ this.uv = hasUVs ? [] : null;
+ this.quantized = quantized;
+ this.vertexBufs = null;
+ }
+
+ addGeometry(geometry) {
+ if (!geometry.positions || !geometry.indices) {
+ this.scene.warn(`Ignoring geometry with no positions or indices: ${geometry.id}`);
+ return;
+ }
+ this.geometries[geometry.id] = geometry;
+ this.geometryIndicesOffsets[geometry.id] = 0; // Will initialize below
+ this.newGeometries.push(geometry);
+ this.needAppend = true;
+ }
+
+ getIndicesOffset(geometry) {
+ if (this.needRebuild || this.needAppend) {
+ this.build();
+ }
+ return this.geometryIndicesOffsets[geometry.id];
+ }
- this.getVertexBufs = function (geometry) {
- if (!geometries[geometry.id]) {
- return nullVertexBufs;
- }
- if (needRebuild || needAppend) {
- build();
- }
- return geometryVertexBufs[geometry.id];
- };
+ getVertexBufs(geometry) {
+ if (!this.geometries[geometry.id]) {
+ return nullVertexBufs;
+ }
+ if (this.needRebuild || this.needAppend) {
+ this.build();
+ }
+ return this.geometryVertexBufs[geometry.id];
+ }
- this.setPositions = function (geometry) {
- const vertexBufs = geometryVertexBufs[geometry.id];
- if (!vertexBufs) {
- return;
- }
- if (!geometry.positions) {
- return;
- }
- const positionsBuf = vertexBufs.positionsBuf;
- if (!positionsBuf) {
- return;
- }
- positionsBuf.setData(geometry.positions, geometryIndicesOffsets[geometry.id] * 3);
- };
+ setPositions(geometry) {
+ const vertexBufs = this.geometryVertexBufs[geometry.id];
+ if (!vertexBufs) {
+ return;
+ }
+ if (!geometry.positions) {
+ return;
+ }
+ const positionsBuf = vertexBufs.positionsBuf;
+ if (!positionsBuf) {
+ return;
+ }
+ positionsBuf.setData(geometry.positions, this.geometryIndicesOffsets[geometry.id] * 3);
+ }
- this.setNormals = function (geometry) {
- const vertexBufs = geometryVertexBufs[geometry.id];
- if (!vertexBufs) {
- return;
- }
- if (!geometry.normals) {
- return;
- }
- const normalsBuf = vertexBufs.normalsBuf;
- if (!normalsBuf) {
- return;
- }
- normalsBuf.setData(geometry.normals, geometryIndicesOffsets[geometry.id] * 3);
- };
+ setNormals(geometry) {
+ const vertexBufs = this.geometryVertexBufs[geometry.id];
+ if (!vertexBufs) {
+ return;
+ }
+ if (!geometry.normals) {
+ return;
+ }
+ const normalsBuf = vertexBufs.normalsBuf;
+ if (!normalsBuf) {
+ return;
+ }
+ normalsBuf.setData(geometry.normals, this.geometryIndicesOffsets[geometry.id] * 3);
+ }
- this.setUVs = function (geometry) {
- const vertexBufs = geometryVertexBufs[geometry.id];
- if (!vertexBufs) {
- return;
- }
- if (!geometry.uv) {
- return;
- }
- const uvBuf = vertexBufs.uvBuf;
- if (!uvBuf) {
- return;
- }
- uvBuf.setData(geometry.uv, geometryIndicesOffsets[geometry.id] * 2);
- };
+ setUVs(geometry) {
+ const vertexBufs = this.geometryVertexBufs[geometry.id];
+ if (!vertexBufs) {
+ return;
+ }
+ if (!geometry.uv) {
+ return;
+ }
+ const uvBuf = vertexBufs.uvBuf;
+ if (!uvBuf) {
+ return;
+ }
+ uvBuf.setData(geometry.uv, this.geometryIndicesOffsets[geometry.id] * 2);
+ }
- this.setColors = function (geometry) {
- const vertexBufs = geometryVertexBufs[geometry.id];
- if (!vertexBufs) {
- return;
- }
- if (!geometry.color) {
- return;
- }
- const colorsBuf = vertexBufs.colorsBuf;
- if (!colorsBuf) {
- return;
- }
- colorsBuf.setData(geometry.colors, geometryIndicesOffsets[geometry.id] * 4);
- };
+ setColors(geometry) {
+ const vertexBufs = this.geometryVertexBufs[geometry.id];
+ if (!vertexBufs) {
+ return;
+ }
+ if (!geometry.color) {
+ return;
+ }
+ const colorsBuf = vertexBufs.colorsBuf;
+ if (!colorsBuf) {
+ return;
+ }
+ colorsBuf.setData(geometry.colors, this.geometryIndicesOffsets[geometry.id] * 4);
+ }
- this.removeGeometry = function (geometry) {
- const id = geometry.id;
- if (!geometries[id]) {
- return;
- }
- delete geometries[id];
- delete geometryIndicesOffsets[id];
- if (geometry.indicesBufCombined) {
- geometry.indicesBufCombined.destroy();
- }
- needRebuild = true;
- };
-
- this.webglContextLost = function () {
- contextLost = true;
- };
-
- this.webglContextRestored = function () {
- if (contextLost) {
- for (const id in geometries) {
- if (geometries.hasOwnProperty(id)) {
- geometries[id].indicesBufCombined = null;
- }
+ removeGeometry(geometry) {
+ const id = geometry.id;
+ if (!this.geometries[id]) {
+ return;
+ }
+ delete this.geometries[id];
+ delete this.geometryIndicesOffsets[id];
+ if (geometry.indicesBufCombined) {
+ geometry.indicesBufCombined.destroy();
+ }
+ this.needRebuild = true;
+ }
+
+ webglContextLost() {
+ this.contextLost = true;
+ }
+
+ webglContextRestored() {
+ if (this.contextLost) {
+ for (const id in this.geometries) {
+ if (this.geometries.hasOwnProperty(id)) {
+ this.geometries[id].indicesBufCombined = null;
}
- build();
- contextLost = false;
}
- };
+ this.build();
+ this.contextLost = false;
+ }
+ }
- function build() {
+ build() {
- geometryVertexBufs = {};
+ this.geometryVertexBufs = {};
- let id;
- let geometry;
- let indicesOffset = 0;
+ let id;
+ let geometry;
+ let indicesOffset = 0;
- vertexBufs = null;
+ this.vertexBufs = null;
- let lenPositions = 0;
- let lenNormals = 0;
- let lenUVs = 0;
- let lenColors = 0;
+ let lenPositions = 0;
+ let lenNormals = 0;
+ let lenUVs = 0;
+ let lenColors = 0;
- for (id in geometries) {
- if (geometries.hasOwnProperty(id)) {
- geometry = geometries[id];
- if (hasPositions) {
- lenPositions += geometry.positions.length;
- }
- if (hasNormals) {
- lenNormals += geometry.normals.length;
- }
- if (hasUVs) {
- lenUVs += geometry.uv.length;
- }
- if (hasColors) {
- lenColors += geometry.uv.length;
- }
+ for (id in this.geometries) {
+ if (this.geometries.hasOwnProperty(id)) {
+ geometry = this.geometries[id];
+ if (this.positions) {
+ lenPositions += geometry.positions.length;
+ }
+ if (this.normals) {
+ lenNormals += geometry.normals.length;
+ }
+ if (this.uv) {
+ lenUVs += geometry.uv.length;
+ }
+ if (this.colors) {
+ lenColors += geometry.uv.length;
}
}
+ }
- // if (hasPositions) {
- // positions = quantized ? new Uint16Array(lenPositions) : new Float32Array(lenPositions);
- // }
- // if (hasNormals) {
- // normals = quantized ? new Uint16Array(lenNormals) : new Float32Array(lenNormals);
- // }
- // if (hasUVs) {
- // uv = quantized ? new Uint16Array(lenUVs) : new Float32Array(lenUVs);
- // }
- // if (hasColors) {
- // colors = quantized ? new Uint16Array(lenColors) : new Float32Array(lenColors);
- // }
-
- for (id in geometries) {
- if (geometries.hasOwnProperty(id)) {
-
- geometry = geometries[id];
-
- const needNew = (!vertexBufs) || (positions.length + geometry.positions.length > CHUNK_LEN);
-
- if (needNew) {
- if (vertexBufs) {
- createBufs(vertexBufs);
- }
- vertexBufs = new xeogl.renderer.State({
- positionsBuf: null,
- normalsBuf: null,
- uvBuf: null,
- colorsBuf: null,
- quantized: quantized
- });
- indicesOffset = 0;
+ // if (this.positions) {
+ // positions = this.quantized ? new Uint16Array(lenPositions) : new Float32Array(lenPositions);
+ // }
+ // if (this.normals) {
+ // normals = this.quantized ? new Uint16Array(lenNormals) : new Float32Array(lenNormals);
+ // }
+ // if (this.uv) {
+ // uv = this.quantized ? new Uint16Array(lenUVs) : new Float32Array(lenUVs);
+ // }
+ // if (this.colors) {
+ // colors = this.quantized ? new Uint16Array(lenColors) : new Float32Array(lenColors);
+ // }
+
+ for (id in this.geometries) {
+ if (this.geometries.hasOwnProperty(id)) {
+
+ geometry = this.geometries[id];
+
+ const needNew = (!this.vertexBufs) || (this.positions.length + geometry.positions.length > CHUNK_LEN);
+
+ if (needNew) {
+ if (this.vertexBufs) {
+ this.createBufs(this.vertexBufs);
}
+ this.vertexBufs = new State({
+ positionsBuf: null,
+ normalsBuf: null,
+ uvBuf: null,
+ colorsBuf: null,
+ quantized: this.quantized
+ });
+ indicesOffset = 0;
+ }
- geometryVertexBufs[id] = vertexBufs;
+ this.geometryVertexBufs[id] = this.vertexBufs;
- if (hasPositions) {
- for (var i = 0, len = geometry.positions.length; i < len; i++) {
- positions.push(geometry.positions[i]);
- }
+ if (this.positions) {
+ for (var i = 0, len = geometry.positions.length; i < len; i++) {
+ this.positions.push(geometry.positions[i]);
}
+ }
- if (hasNormals) {
- for (var i = 0, len = geometry.normals.length; i < len; i++) {
- normals.push(geometry.normals[i]);
- }
+ if (this.normals) {
+ for (var i = 0, len = geometry.normals.length; i < len; i++) {
+ this.normals.push(geometry.normals[i]);
}
+ }
- if (hasColors) {
- for (var i = 0, len = geometry.colors.length; i < len; i++) {
- colors.push(geometry.colors[i]);
- }
+ if (this.colors) {
+ for (var i = 0, len = geometry.colors.length; i < len; i++) {
+ this.colors.push(geometry.colors[i]);
}
+ }
- if (hasUVs) {
- for (var i = 0, len = geometry.uv.length; i < len; i++) {
- uv.push(geometry.uv[i]);
- }
+ if (this.uv) {
+ for (var i = 0, len = geometry.uv.length; i < len; i++) {
+ this.uv.push(geometry.uv[i]);
}
+ }
- // Adjust geometry indices
+ // Adjust geometry indices
- geometryIndicesOffsets[id] = indicesOffset;
+ this.geometryIndicesOffsets[id] = indicesOffset;
- let indices;
+ let indices;
- if (indicesOffset) {
- indices = new (bigIndicesSupported ? Uint32Array : Uint16Array)(geometry.indices);
- for (var i = 0, len = indices.length; i < len; i++) {
- indices[i] += indicesOffset;
- if (indices[i] > (CHUNK_LEN / 3)) {
- console.error("out of range: " + indices[i])
- }
+ if (indicesOffset) {
+ indices = new (bigIndicesSupported ? Uint32Array : Uint16Array)(geometry.indices);
+ for (var i = 0, len = indices.length; i < len; i++) {
+ indices[i] += indicesOffset;
+ if (indices[i] > (CHUNK_LEN / 3)) {
+ console.error(`out of range: ${indices[i]}`)
}
- } else {
- indices = geometry.indices;
}
+ } else {
+ indices = geometry.indices;
+ }
- // Update indices buffer, lazy-create first if necessary
-
- if (!geometry.indicesBufCombined) {
- geometry.indicesBufCombined = new xeogl.renderer.ArrayBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, indices, indices.length, 1, gl.STATIC_DRAW);
- } else {
- geometry.indicesBufCombined.setData(indices);
- }
+ // Update indices buffer, lazy-create first if necessary
- indicesOffset += geometry.positions.length / 3;
+ if (!geometry.indicesBufCombined) {
+ geometry.indicesBufCombined = new ArrayBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, indices, indices.length, 1, gl.STATIC_DRAW);
+ } else {
+ geometry.indicesBufCombined.setData(indices);
}
- }
- if (vertexBufs) {
- createBufs(vertexBufs);
+ indicesOffset += geometry.positions.length / 3;
}
+ }
- needRebuild = false;
- needAppend = false;
+ if (this.vertexBufs) {
+ this.createBufs(this.vertexBufs);
}
- function createBufs(vertexBufs) {
- const gl = scene.canvas.gl;
- let array;
- if (hasPositions) {
- array = quantized ? new Uint16Array(positions) : new Float32Array(positions);
- vertexBufs.positionsBuf = new xeogl.renderer.ArrayBuffer(gl, gl.ARRAY_BUFFER, array, array.length, 3, gl.STATIC_DRAW);
- memoryStats.positions += vertexBufs.positionsBuf.numItems;
- positions = [];
- }
- if (hasNormals) {
- array = quantized ? new Int8Array(normals) : new Float32Array(normals);
- vertexBufs.normalsBuf = new xeogl.renderer.ArrayBuffer(gl, gl.ARRAY_BUFFER, array, array.length, 3, gl.STATIC_DRAW);
- memoryStats.normals += vertexBufs.normalsBuf.numItems;
- normals = [];
- }
- if (hasColors) {
- array = new Float32Array(colors);
- vertexBufs.colorsBuf = new xeogl.renderer.ArrayBuffer(gl, gl.ARRAY_BUFFER, array, array.length, 4, gl.STATIC_DRAW);
- memoryStats.colors += vertexBufs.colorsBuf.numItems;
- colors = [];
- }
- if (hasUVs) {
- array = quantized ? new Uint16Array(uv) : new Float32Array(uv);
- vertexBufs.uvBuf = new xeogl.renderer.ArrayBuffer(gl, gl.ARRAY_BUFFER, array, array.length, 2, gl.STATIC_DRAW);
- memoryStats.uvs += vertexBufs.uvBuf.numItems;
- uv = [];
- }
+ this.needRebuild = false;
+ this.needAppend = false;
+ }
+
+ createBufs(vertexBufs) {
+ const gl = this.scene.canvas.gl;
+ let array;
+ if (this.positions) {
+ array = this.quantized ? new Uint16Array(this.positions) : new Float32Array(this.positions);
+ vertexBufs.positionsBuf = new ArrayBuffer(gl, gl.ARRAY_BUFFER, array, array.length, 3, gl.STATIC_DRAW);
+ memoryStats.positions += vertexBufs.positionsBuf.numItems;
+ this.positions = [];
+ }
+ if (this.normals) {
+ array = this.quantized ? new Int8Array(this.normals) : new Float32Array(this.normals);
+ vertexBufs.normalsBuf = new ArrayBuffer(gl, gl.ARRAY_BUFFER, array, array.length, 3, gl.STATIC_DRAW);
+ memoryStats.normals += vertexBufs.normalsBuf.numItems;
+ this.normals = [];
}
- }; // SceneVertexBufs
-
- xeogl.SceneVertexBufs.get = function (scene, geometry) {
- const hasPositions = !!geometry.positions;
- const quantized = !!geometry.quantized;
- const hasNormals = !!geometry.normals;
- const hasColors = !!geometry.colors;
- const hasUVs = !!geometry.uv;
- const hash = ([
- scene.id,
- hasPositions ? "p" : "",
- quantized ? "c" : "",
- hasNormals ? "n" : "",
- hasColors ? "c" : "",
- hasUVs ? "u" : ""
- ]).join(";");
- if (!scene._sceneVertexBufs) {
- scene._sceneVertexBufs = {};
+ if (this.colors) {
+ array = new Float32Array(this.colors);
+ vertexBufs.colorsBuf = new ArrayBuffer(gl, gl.ARRAY_BUFFER, array, array.length, 4, gl.STATIC_DRAW);
+ memoryStats.colors += vertexBufs.colorsBuf.numItems;
+ this.colors = [];
}
- let sceneVertexBufs = scene._sceneVertexBufs[hash];
- if (!sceneVertexBufs) {
- sceneVertexBufs = new xeogl.SceneVertexBufs(
- scene,
- hasPositions,
- hasNormals,
- hasColors,
- hasUVs,
- quantized);
- scene._sceneVertexBufs[hash] = sceneVertexBufs;
+ if (this.uv) {
+ array = this.quantized ? new Uint16Array(this.uv) : new Float32Array(this.uv);
+ vertexBufs.uvBuf = new ArrayBuffer(gl, gl.ARRAY_BUFFER, array, array.length, 2, gl.STATIC_DRAW);
+ memoryStats.uvs += vertexBufs.uvBuf.numItems;
+ this.uv = [];
}
- return sceneVertexBufs;
- };
-})();
\ No newline at end of file
+ }
+}
+
+const getSceneVertexBufs = (scene, geometry) => {
+ const hasPositions = !!geometry.positions;
+ const quantized = !!geometry.quantized;
+ const hasNormals = !!geometry.normals;
+ const hasColors = !!geometry.colors;
+ const hasUVs = !!geometry.uv;
+ const hash = ([scene.id, hasPositions ? "p" : "", quantized ? "c" : "", hasNormals ? "n" : "", hasColors ? "c" : "", hasUVs ? "u" : ""]).join(";");
+ if (!scene._sceneVertexBufs) {
+ scene._sceneVertexBufs = {};
+ }
+ let sceneVertexBufs = scene._sceneVertexBufs[hash];
+ if (!sceneVertexBufs) {
+ sceneVertexBufs = new SceneVertexBufs(scene, hasPositions, hasNormals, hasColors, hasUVs, quantized);
+ scene._sceneVertexBufs[hash] = sceneVertexBufs;
+ }
+ return sceneVertexBufs;
+};
+
+export {getSceneVertexBufs};
\ No newline at end of file
diff --git a/src/geometry/sphereGeometry.js b/src/geometry/sphereGeometry.js
index e9243d8d6..f4a45b888 100644
--- a/src/geometry/sphereGeometry.js
+++ b/src/geometry/sphereGeometry.js
@@ -34,8 +34,7 @@
@module xeogl
@submodule geometry
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}} - creates this SphereGeometry in the default
- {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
+ @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.
@@ -48,127 +47,140 @@
@param [cfg.lod=1] {Number} Level-of-detail, in range [0..1].
@extends Geometry
*/
-(function () {
+import {core} from "./../core.js";
+import {utils} from '../utils.js';
+import {Geometry} from './geometry.js';
- "use strict";
+const type = "xeogl.SphereGeometry";
- xeogl.SphereGeometry = xeogl.Geometry.extend({
+class SphereGeometry extends Geometry {
- type: "xeogl.SphereGeometry",
+ /**
+ JavaScript class name for this Component.
- _init: function (cfg) {
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
- const lod = cfg.lod || 1;
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return type;
+ }
- const centerX = cfg.center ? cfg.center[0] : 0;
- const centerY = cfg.center ? cfg.center[1] : 0;
- const centerZ = cfg.center ? cfg.center[2] : 0;
+ constructor(owner = nul, cfg) {
- let radius = cfg.radius || 1;
- if (radius < 0) {
- this.warn("negative radius not allowed - will invert");
- radius *= -1;
- }
+ const lod = cfg.lod || 1;
- let heightSegments = cfg.heightSegments || 18;
- if (heightSegments < 0) {
- this.warn("negative heightSegments not allowed - will invert");
- heightSegments *= -1;
- }
- heightSegments = Math.floor(lod * heightSegments);
- if (heightSegments < 18) {
- heightSegments = 18;
- }
+ const centerX = cfg.center ? cfg.center[0] : 0;
+ const centerY = cfg.center ? cfg.center[1] : 0;
+ const centerZ = cfg.center ? cfg.center[2] : 0;
- let widthSegments = cfg.widthSegments || 18;
- if (widthSegments < 0) {
- this.warn("negative widthSegments not allowed - will invert");
- widthSegments *= -1;
- }
- widthSegments = Math.floor(lod * widthSegments);
- if (widthSegments < 18) {
- widthSegments = 18;
- }
+ let radius = cfg.radius || 1;
+ if (radius < 0) {
+ this.warn("negative radius not allowed - will invert");
+ radius *= -1;
+ }
- const positions = [];
- const normals = [];
- const uvs = [];
- const indices = [];
+ let heightSegments = cfg.heightSegments || 18;
+ if (heightSegments < 0) {
+ this.warn("negative heightSegments not allowed - will invert");
+ heightSegments *= -1;
+ }
+ heightSegments = Math.floor(lod * heightSegments);
+ if (heightSegments < 18) {
+ heightSegments = 18;
+ }
- let i;
- let j;
+ let widthSegments = cfg.widthSegments || 18;
+ if (widthSegments < 0) {
+ this.warn("negative widthSegments not allowed - will invert");
+ widthSegments *= -1;
+ }
+ widthSegments = Math.floor(lod * widthSegments);
+ if (widthSegments < 18) {
+ widthSegments = 18;
+ }
+
+ const positions = [];
+ const normals = [];
+ const uvs = [];
+ const indices = [];
+
+ let i;
+ let j;
- let theta;
- let sinTheta;
- let cosTheta;
+ let theta;
+ let sinTheta;
+ let cosTheta;
- let phi;
- let sinPhi;
- let cosPhi;
+ let phi;
+ let sinPhi;
+ let cosPhi;
- let x;
- let y;
- let z;
+ let x;
+ let y;
+ let z;
- let u;
- let v;
+ let u;
+ let v;
- let first;
- let second;
+ let first;
+ let second;
- for (i = 0; i <= heightSegments; i++) {
+ for (i = 0; i <= heightSegments; i++) {
- theta = i * Math.PI / heightSegments;
- sinTheta = Math.sin(theta);
- cosTheta = Math.cos(theta);
+ theta = i * Math.PI / heightSegments;
+ sinTheta = Math.sin(theta);
+ cosTheta = Math.cos(theta);
- for (j = 0; j <= widthSegments; j++) {
+ for (j = 0; j <= widthSegments; j++) {
- phi = j * 2 * Math.PI / widthSegments;
- sinPhi = Math.sin(phi);
- cosPhi = Math.cos(phi);
+ phi = j * 2 * Math.PI / widthSegments;
+ sinPhi = Math.sin(phi);
+ cosPhi = Math.cos(phi);
- x = cosPhi * sinTheta;
- y = cosTheta;
- z = sinPhi * sinTheta;
- u = 1.0 - j / widthSegments;
- v = i / heightSegments;
+ x = cosPhi * sinTheta;
+ y = cosTheta;
+ z = sinPhi * sinTheta;
+ u = 1.0 - j / widthSegments;
+ v = i / heightSegments;
- normals.push(x);
- normals.push(y);
- normals.push(z);
+ normals.push(x);
+ normals.push(y);
+ normals.push(z);
- uvs.push(u);
- uvs.push(v);
+ uvs.push(u);
+ uvs.push(v);
- positions.push(centerX + radius * x);
- positions.push(centerY + radius * y);
- positions.push(centerZ + radius * z);
- }
+ positions.push(centerX + radius * x);
+ positions.push(centerY + radius * y);
+ positions.push(centerZ + radius * z);
}
+ }
- for (i = 0; i < heightSegments; i++) {
- for (j = 0; j < widthSegments; j++) {
+ for (i = 0; i < heightSegments; i++) {
+ for (j = 0; j < widthSegments; j++) {
- first = (i * (widthSegments + 1)) + j;
- second = first + widthSegments + 1;
+ first = (i * (widthSegments + 1)) + j;
+ second = first + widthSegments + 1;
- indices.push(first + 1);
- indices.push(second + 1);
- indices.push(second);
- indices.push(first + 1);
- indices.push(second);
- indices.push(first);
- }
+ indices.push(first + 1);
+ indices.push(second + 1);
+ indices.push(second);
+ indices.push(first + 1);
+ indices.push(second);
+ indices.push(first);
}
-
- this._super(xeogl._apply(cfg, {
- positions: positions,
- normals: normals,
- uv: uvs,
- indices: indices
- }));
}
- });
-})();
+ super(owner, utils.apply(cfg, {
+ positions: positions,
+ normals: normals,
+ uv: uvs,
+ indices: indices
+ }));
+ }
+}
+
+export {SphereGeometry};
diff --git a/src/geometry/torusGeometry.js b/src/geometry/torusGeometry.js
index bb124d079..e2d2da9f9 100644
--- a/src/geometry/torusGeometry.js
+++ b/src/geometry/torusGeometry.js
@@ -46,8 +46,7 @@
@module xeogl
@submodule geometry
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}} - creates this TorusGeometry in the default
- {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
+ @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.
@@ -61,134 +60,147 @@
@param [cfg.arc=Math.PI / 2.0] {Number} The length of the TorusGeometry's arc in radians, where Math.PI*2 is a closed torus.
@extends Geometry
*/
-(function () {
+import {core} from "./../core.js";
+import {Geometry} from './geometry.js';
+import {math} from '../math/math.js';
- "use strict";
+const type = "xeogl.TorusGeometry";
- xeogl.TorusGeometry = xeogl.Geometry.extend({
+class TorusGeometry extends Geometry {
- type: "xeogl.TorusGeometry",
+ /**
+ JavaScript class name for this Component.
- _init: function (cfg) {
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
- let radius = cfg.radius || 1;
- if (radius < 0) {
- this.error("negative radius not allowed - will invert");
- radius *= -1;
- }
- radius *= 0.5;
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return type;
+ }
- let tube = cfg.tube || 0.3;
- if (tube < 0) {
- this.error("negative tube not allowed - will invert");
- tube *= -1;
- }
+ init(cfg) {
- let radialSegments = cfg.radialSegments || 32;
- if (radialSegments < 0) {
- this.error("negative radialSegments not allowed - will invert");
- radialSegments *= -1;
- }
- if (radialSegments < 4) {
- radialSegments = 4;
- }
+ let radius = cfg.radius || 1;
+ if (radius < 0) {
+ this.error("negative radius not allowed - will invert");
+ radius *= -1;
+ }
+ radius *= 0.5;
- let tubeSegments = cfg.tubeSegments || 24;
- if (tubeSegments < 0) {
- this.error("negative tubeSegments not allowed - will invert");
- tubeSegments *= -1;
- }
- if (tubeSegments < 4) {
- tubeSegments = 4;
- }
+ let tube = cfg.tube || 0.3;
+ if (tube < 0) {
+ this.error("negative tube not allowed - will invert");
+ tube *= -1;
+ }
- let arc = cfg.arc || Math.PI * 2;
- if (arc < 0) {
- this.warn("negative arc not allowed - will invert");
- arc *= -1;
- }
- if (arc > 360) {
- arc = 360;
- }
+ let radialSegments = cfg.radialSegments || 32;
+ if (radialSegments < 0) {
+ this.error("negative radialSegments not allowed - will invert");
+ radialSegments *= -1;
+ }
+ if (radialSegments < 4) {
+ radialSegments = 4;
+ }
+
+ let tubeSegments = cfg.tubeSegments || 24;
+ if (tubeSegments < 0) {
+ this.error("negative tubeSegments not allowed - will invert");
+ tubeSegments *= -1;
+ }
+ if (tubeSegments < 4) {
+ tubeSegments = 4;
+ }
+
+ let arc = cfg.arc || Math.PI * 2;
+ if (arc < 0) {
+ this.warn("negative arc not allowed - will invert");
+ arc *= -1;
+ }
+ if (arc > 360) {
+ arc = 360;
+ }
- const center = cfg.center;
- let centerX = center ? center[0] : 0;
- let centerY = center ? center[1] : 0;
- const centerZ = center ? center[2] : 0;
+ const center = cfg.center;
+ let centerX = center ? center[0] : 0;
+ let centerY = center ? center[1] : 0;
+ const centerZ = center ? center[2] : 0;
- const positions = [];
- const normals = [];
- const uvs = [];
- const indices = [];
+ const positions = [];
+ const normals = [];
+ const uvs = [];
+ const indices = [];
- let u;
- let v;
- let x;
- let y;
- let z;
- let vec;
+ let u;
+ let v;
+ let x;
+ let y;
+ let z;
+ let vec;
- let i;
- let j;
+ let i;
+ let j;
- for (j = 0; j <= tubeSegments; j++) {
- for (i = 0; i <= radialSegments; i++) {
+ for (j = 0; j <= tubeSegments; j++) {
+ for (i = 0; i <= radialSegments; i++) {
- u = i / radialSegments * arc;
- v = 0.785398 + (j / tubeSegments * Math.PI * 2);
+ u = i / radialSegments * arc;
+ v = 0.785398 + (j / tubeSegments * Math.PI * 2);
- centerX = radius * Math.cos(u);
- centerY = radius * Math.sin(u);
+ centerX = radius * Math.cos(u);
+ centerY = radius * Math.sin(u);
- x = (radius + tube * Math.cos(v) ) * Math.cos(u);
- y = (radius + tube * Math.cos(v) ) * Math.sin(u);
- z = tube * Math.sin(v);
+ x = (radius + tube * Math.cos(v) ) * Math.cos(u);
+ y = (radius + tube * Math.cos(v) ) * Math.sin(u);
+ z = tube * Math.sin(v);
- positions.push(x + centerX);
- positions.push(y + centerY);
- positions.push(z + centerZ);
+ positions.push(x + centerX);
+ positions.push(y + centerY);
+ positions.push(z + centerZ);
- uvs.push(1 - (i / radialSegments));
- uvs.push((j / tubeSegments));
+ uvs.push(1 - (i / radialSegments));
+ uvs.push((j / tubeSegments));
- vec = xeogl.math.normalizeVec3(xeogl.math.subVec3([x, y, z], [centerX, centerY, centerZ], []), []);
+ vec = math.normalizeVec3(math.subVec3([x, y, z], [centerX, centerY, centerZ], []), []);
- normals.push(vec[0]);
- normals.push(vec[1]);
- normals.push(vec[2]);
- }
+ normals.push(vec[0]);
+ normals.push(vec[1]);
+ normals.push(vec[2]);
}
+ }
- let a;
- let b;
- let c;
- let d;
+ let a;
+ let b;
+ let c;
+ let d;
- for (j = 1; j <= tubeSegments; j++) {
- for (i = 1; i <= radialSegments; i++) {
+ for (j = 1; j <= tubeSegments; j++) {
+ for (i = 1; i <= radialSegments; i++) {
- a = ( radialSegments + 1 ) * j + i - 1;
- b = ( radialSegments + 1 ) * ( j - 1 ) + i - 1;
- c = ( radialSegments + 1 ) * ( j - 1 ) + i;
- d = ( radialSegments + 1 ) * j + i;
+ a = ( radialSegments + 1 ) * j + i - 1;
+ b = ( radialSegments + 1 ) * ( j - 1 ) + i - 1;
+ c = ( radialSegments + 1 ) * ( j - 1 ) + i;
+ d = ( radialSegments + 1 ) * j + i;
- indices.push(a);
- indices.push(b);
- indices.push(c);
+ indices.push(a);
+ indices.push(b);
+ indices.push(c);
- indices.push(c);
- indices.push(d);
- indices.push(a);
- }
+ indices.push(c);
+ indices.push(d);
+ indices.push(a);
}
-
- this._super(xeogl._apply(cfg, {
- positions: positions,
- normals: normals,
- uv: uvs,
- indices: indices
- }));
}
- });
-})();
+ super(owner, utils.apply(cfg, {
+ positions: positions,
+ normals: normals,
+ uv: uvs,
+ indices: indices
+ }));
+ }
+}
+
+export {TorusGeometry};
diff --git a/src/input/input.js b/src/input/input.js
index 1cddaed80..50ffc498e 100644
--- a/src/input/input.js
+++ b/src/input/input.js
@@ -89,535 +89,835 @@
@submodule input
@extends Component
*/
-(function () {
+import {core} from "./../core.js";
+import {Component} from '../component.js';
+import {math} from '../math/math.js';
- "use strict";
+const type = "xeogl.Input";
- xeogl.Input = xeogl.Component.extend({
+class Input extends Component {
- type: "xeogl.Input",
+ /**
+ JavaScript class name for this Component.
- serializable: false,
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
- _init: function (cfg) {
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return type;
+ }
- const self = this;
+ constructor(owner=null, cfg) {
- this._element = cfg.element;
+ super.init(cfg);
- // True when ALT down
- this.altDown = false;
+ const self = this;
- /** True whenever CTRL is down
- *
- * @type {boolean}
- */
- this.ctrlDown = false;
+ // Key codes
- /** True whenever left mouse button is down
- *
- * @type {boolean}
- */
- this.mouseDownLeft = false;
+ /**
+ * Code for the BACKSPACE key.
+ * @property KEY_BACKSPACE
+ * @final
+ * @type Number
+ */
+ // Key codes
- /** True whenever middle mouse button is down
- *
- * @type {boolean}
- */
- this.mouseDownMiddle = false;
+ /**
+ * Code for the BACKSPACE key.
+ * @property KEY_BACKSPACE
+ * @final
+ * @type Number
+ */
+ this.KEY_BACKSPACE = 8;
- /** True whenever right mouse button is down
- *
- * @type {boolean}
- */
- this.mouseDownRight = false;
+ /**
+ * Code for the TAB key.
+ * @property KEY_TAB
+ * @final
+ * @type Number
+ */
+ this.KEY_TAB = 9;
- /** Flag for each key that's down
- *
- * @type {boolean}
- */
- this.keyDown = [];
+ /**
+ * Code for the ENTER key.
+ * @property KEY_ENTER
+ * @final
+ * @type Number
+ */
+ this.KEY_ENTER = 13;
- /** True while input enabled
- *
- * @type {boolean}
- */
- this.enabled = true;
+ /**
+ * Code for the SHIFT key.
+ * @property KEY_SHIFT
+ * @final
+ * @type Number
+ */
+ this.KEY_SHIFT = 16;
- /** True while mouse is over the parent {{#crossLink "Scene"}}Scene's{{/crossLink}} {{#crossLink "Canvas"}}Canvas{{/crossLink}}
- *
- * @type {boolean}
- */
- this.mouseover = false;
+ /**
+ * Code for the CTRL key.
+ * @property KEY_CTRL
+ * @final
+ * @type Number
+ */
+ this.KEY_CTRL = 17;
- // Capture input events and publish them on this component
+ /**
+ * Code for the ALT key.
+ * @property KEY_ALT
+ * @final
+ * @type Number
+ */
+ this.KEY_ALT = 18;
- document.addEventListener("keydown", this._keyDownListener = function (e) {
+ /**
+ * Code for the PAUSE_BREAK key.
+ * @property KEY_PAUSE_BREAK
+ * @final
+ * @type Number
+ */
+ this.KEY_PAUSE_BREAK = 19;
- if (!self.enabled) {
- return;
- }
+ /**
+ * Code for the CAPS_LOCK key.
+ * @property KEY_CAPS_LOCK
+ * @final
+ * @type Number
+ */
+ this.KEY_CAPS_LOCK = 20;
- if (e.target.tagName !== "INPUT" && e.target.tagName !== "TEXTAREA") {
+ /**
+ * Code for the ESCAPE key.
+ * @property KEY_ESCAPE
+ * @final
+ * @type Number
+ */
+ this.KEY_ESCAPE = 27;
- if (e.ctrlKey) {
- self.ctrlDown = true;
+ /**
+ * Code for the PAGE_UP key.
+ * @property KEY_PAGE_UP
+ * @final
+ * @type Number
+ */
+ this.KEY_PAGE_UP = 33;
- } else if (e.altKey) {
- self.altDown = true;
+ /**
+ * Code for the PAGE_DOWN key.
+ * @property KEY_PAGE_DOWN
+ * @final
+ * @type Number
+ */
+ this.KEY_PAGE_DOWN = 34;
- } else {
- self.keyDown[e.keyCode] = true;
+ /**
+ * Code for the END key.
+ * @property KEY_END
+ * @final
+ * @type Number
+ */
+ this.KEY_END = 35;
- /**
- * Fired whenever a key is pressed while the parent
- * {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Canvas"}}Canvas{{/crossLink}} has input focus.
- * @event keydown
- * @param value {Number} The key code, for example {{#crossLink "Input/KEY_LEFT_ARROW:property"}}{{/crossLink}},
- */
- self.fire("keydown", e.keyCode, true);
- }
- }
+ /**
+ * Code for the HOME key.
+ * @property KEY_HOME
+ * @final
+ * @type Number
+ */
+ this.KEY_HOME = 36;
- if (self.mouseover) {
- e.preventDefault();
- }
+ /**
+ * Code for the LEFT_ARROW key.
+ * @property KEY_LEFT_ARROW
+ * @final
+ * @type Number
+ */
+ this.KEY_LEFT_ARROW = 37;
- }, true);
+ /**
+ * Code for the UP_ARROW key.
+ * @property KEY_UP_ARROW
+ * @final
+ * @type Number
+ */
+ this.KEY_UP_ARROW = 38;
- document.addEventListener("keyup", this._keyUpListener = function (e) {
+ /**
+ * Code for the RIGHT_ARROW key.
+ * @property KEY_RIGHT_ARROW
+ * @final
+ * @type Number
+ */
+ this.KEY_RIGHT_ARROW = 39;
- if (!self.enabled) {
- return;
- }
+ /**
+ * Code for the DOWN_ARROW key.
+ * @property KEY_DOWN_ARROW
+ * @final
+ * @type Number
+ */
+ this.KEY_DOWN_ARROW = 40;
- if (e.target.tagName !== "INPUT" && e.target.tagName !== "TEXTAREA") {
+ /**
+ * Code for the INSERT key.
+ * @property KEY_INSERT
+ * @final
+ * @type Number
+ */
+ this.KEY_INSERT = 45;
- if (e.ctrlKey) {
- self.ctrlDown = false;
+ /**
+ * Code for the DELETE key.
+ * @property KEY_DELETE
+ * @final
+ * @type Number
+ */
+ this.KEY_DELETE = 46;
- } else if (e.altKey) {
- self.altDown = false;
+ /**
+ * Code for the 0 key.
+ * @property KEY_NUM_0
+ * @final
+ * @type Number
+ */
+ this.KEY_NUM_0 = 48;
- } else {
- self.keyDown[e.keyCode] = false;
+ /**
+ * Code for the 1 key.
+ * @property KEY_NUM_1
+ * @final
+ * @type Number
+ */
+ this.KEY_NUM_1 = 49;
- /**
- * Fired whenever a key is released while the parent
- * {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Canvas"}}Canvas{{/crossLink}} has input focus.
- * @event keyup
- * @param value {Number} The key code, for example {{#crossLink "Input/KEY_LEFT_ARROW:property"}}{{/crossLink}},
- */
- self.fire("keyup", e.keyCode, true);
- }
- }
- });
+ /**
+ * Code for the 2 key.
+ * @property KEY_NUM_2
+ * @final
+ * @type Number
+ */
+ this.KEY_NUM_2 = 50;
- cfg.element.addEventListener("mouseenter", this._mouseEnterListener = function (e) {
+ /**
+ * Code for the 3 key.
+ * @property KEY_NUM_3
+ * @final
+ * @type Number
+ */
+ this.KEY_NUM_3 = 51;
- if (!self.enabled) {
- return;
- }
+ /**
+ * Code for the 4 key.
+ * @property KEY_NUM_4
+ * @final
+ * @type Number
+ */
+ this.KEY_NUM_4 = 52;
- self.mouseover = true;
+ /**
+ * Code for the 5 key.
+ * @property KEY_NUM_5
+ * @final
+ * @type Number
+ */
+ this.KEY_NUM_5 = 53;
- const coords = self._getClickCoordsWithinElement(e);
+ /**
+ * Code for the 6 key.
+ * @property KEY_NUM_6
+ * @final
+ * @type Number
+ */
+ this.KEY_NUM_6 = 54;
- /**
- * Fired whenever the mouse is moved into of the parent
- * {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Canvas"}}Canvas{{/crossLink}}.
- * @event mouseenter
- * @param value {[Number, Number]} The mouse coordinates within the {{#crossLink "Canvas"}}Canvas{{/crossLink}},
- */
- self.fire("mouseenter", coords, true);
- });
+ /**
+ * Code for the 7 key.
+ * @property KEY_NUM_7
+ * @final
+ * @type Number
+ */
+ this.KEY_NUM_7 = 55;
- cfg.element.addEventListener("mouseleave", this._mouseLeaveListener = function (e) {
+ /**
+ * Code for the 8 key.
+ * @property KEY_NUM_8
+ * @final
+ * @type Number
+ */
+ this.KEY_NUM_8 = 56;
- if (!self.enabled) {
- return;
- }
+ /**
+ * Code for the 9 key.
+ * @property KEY_NUM_9
+ * @final
+ * @type Number
+ */
+ this.KEY_NUM_9 = 57;
+
+ /**
+ * Code for the A key.
+ * @property KEY_A
+ * @final
+ * @type Number
+ */
+ this.KEY_A = 65;
+
+ /**
+ * Code for the B key.
+ * @property KEY_B
+ * @final
+ * @type Number
+ */
+ this.KEY_B = 66;
- self.mouseover = false;
+ /**
+ * Code for the C key.
+ * @property KEY_C
+ * @final
+ * @type Number
+ */
+ this.KEY_C = 67;
- const coords = self._getClickCoordsWithinElement(e);
+ /**
+ * Code for the D key.
+ * @property KEY_D
+ * @final
+ * @type Number
+ */
+ this.KEY_D = 68;
- /**
- * Fired whenever the mouse is moved out of the parent
- * {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Canvas"}}Canvas{{/crossLink}}.
- * @event mouseleave
- * @param value {[Number, Number]} The mouse coordinates within the {{#crossLink "Canvas"}}Canvas{{/crossLink}},
- */
- self.fire("mouseleave", coords, true);
- });
+ /**
+ * Code for the E key.
+ * @property KEY_E
+ * @final
+ * @type Number
+ */
+ this.KEY_E = 69;
+
+ /**
+ * Code for the F key.
+ * @property KEY_F
+ * @final
+ * @type Number
+ */
+ this.KEY_F = 70;
+
+ /**
+ * Code for the G key.
+ * @property KEY_G
+ * @final
+ * @type Number
+ */
+ this.KEY_G = 71;
+
+ /**
+ * Code for the H key.
+ * @property KEY_H
+ * @final
+ * @type Number
+ */
+ this.KEY_H = 72;
+
+ /**
+ * Code for the I key.
+ * @property KEY_I
+ * @final
+ * @type Number
+ */
+ this.KEY_I = 73;
+
+ /**
+ * Code for the J key.
+ * @property KEY_J
+ * @final
+ * @type Number
+ */
+ this.KEY_J = 74;
+
+ /**
+ * Code for the K key.
+ * @property KEY_K
+ * @final
+ * @type Number
+ */
+ this.KEY_K = 75;
+
+ /**
+ * Code for the L key.
+ * @property KEY_L
+ * @final
+ * @type Number
+ */
+ this.KEY_L = 76;
+
+ /**
+ * Code for the M key.
+ * @property KEY_M
+ * @final
+ * @type Number
+ */
+ this.KEY_M = 77;
+
+ /**
+ * Code for the N key.
+ * @property KEY_N
+ * @final
+ * @type Number
+ */
+ this.KEY_N = 78;
+
+ /**
+ * Code for the O key.
+ * @property KEY_O
+ * @final
+ * @type Number
+ */
+ this.KEY_O = 79;
+
+ /**
+ * Code for the P key.
+ * @property KEY_P
+ * @final
+ * @type Number
+ */
+ this.KEY_P = 80;
+ /**
+ * Code for the Q key.
+ * @property KEY_Q
+ * @final
+ * @type Number
+ */
+ this.KEY_Q = 81;
- cfg.element.addEventListener("mousedown", this._mouseDownListener = function (e) {
+ /**
+ * Code for the R key.
+ * @property KEY_R
+ * @final
+ * @type Number
+ */
+ this.KEY_R = 82;
- if (!self.enabled) {
- return;
- }
+ /**
+ * Code for the S key.
+ * @property KEY_S
+ * @final
+ * @type Number
+ */
+ this.KEY_S = 83;
- switch (e.which) {
+ /**
+ * Code for the T key.
+ * @property KEY_T
+ * @final
+ * @type Number
+ */
+ this.KEY_T = 84;
- case 1:// Left button
- self.mouseDownLeft = true;
- break;
+ /**
+ * Code for the U key.
+ * @property KEY_U
+ * @final
+ * @type Number
+ */
+ this.KEY_U = 85;
- case 2:// Middle/both buttons
- self.mouseDownMiddle = true;
- break;
+ /**
+ * Code for the V key.
+ * @property KEY_V
+ * @final
+ * @type Number
+ */
+ this.KEY_V = 86;
- case 3:// Right button
- self.mouseDownRight = true;
- break;
+ /**
+ * Code for the W key.
+ * @property KEY_W
+ * @final
+ * @type Number
+ */
+ this.KEY_W = 87;
- default:
- break;
- }
+ /**
+ * Code for the X key.
+ * @property KEY_X
+ * @final
+ * @type Number
+ */
+ this.KEY_X = 88;
- const coords = self._getClickCoordsWithinElement(e);
+ /**
+ * Code for the Y key.
+ * @property KEY_Y
+ * @final
+ * @type Number
+ */
+ this.KEY_Y = 89;
- cfg.element.focus();
+ /**
+ * Code for the Z key.
+ * @property KEY_Z
+ * @final
+ * @type Number
+ */
+ this.KEY_Z = 90;
- /**
- * Fired whenever the mouse is pressed over the parent
- * {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Canvas"}}Canvas{{/crossLink}}.
- * @event mousedown
- * @param value {[Number, Number]} The mouse coordinates within the {{#crossLink "Canvas"}}Canvas{{/crossLink}},
- */
- self.fire("mousedown", coords, true);
+ /**
+ * Code for the LEFT_WINDOW key.
+ * @property KEY_LEFT_WINDOW
+ * @final
+ * @type Number
+ */
+ this.KEY_LEFT_WINDOW = 91;
- if (self.mouseover) {
- e.preventDefault();
- }
- });
+ /**
+ * Code for the RIGHT_WINDOW key.
+ * @property KEY_RIGHT_WINDOW
+ * @final
+ * @type Number
+ */
+ this.KEY_RIGHT_WINDOW = 92;
- document.addEventListener("mouseup", this._mouseUpListener = function (e) {
+ /**
+ * Code for the SELECT key.
+ * @property KEY_SELECT
+ * @final
+ * @type Number
+ */
+ this.KEY_SELECT_KEY = 93;
- if (!self.enabled) {
- return;
- }
+ /**
+ * Code for the number pad 0 key.
+ * @property KEY_NUMPAD_0
+ * @final
+ * @type Number
+ */
+ this.KEY_NUMPAD_0 = 96;
- switch (e.which) {
+ /**
+ * Code for the number pad 1 key.
+ * @property KEY_NUMPAD_1
+ * @final
+ * @type Number
+ */
+ this.KEY_NUMPAD_1 = 97;
- case 1:// Left button
- self.mouseDownLeft = false;
- break;
+ /**
+ * Code for the number pad 2 key.
+ * @property KEY_NUMPAD 2
+ * @final
+ * @type Number
+ */
+ this.KEY_NUMPAD_2 = 98;
- case 2:// Middle/both buttons
- self.mouseDownMiddle = false;
- break;
+ /**
+ * Code for the number pad 3 key.
+ * @property KEY_NUMPAD_3
+ * @final
+ * @type Number
+ */
+ this.KEY_NUMPAD_3 = 99;
- case 3:// Right button
- self.mouseDownRight = false;
- break;
+ /**
+ * Code for the number pad 4 key.
+ * @property KEY_NUMPAD_4
+ * @final
+ * @type Number
+ */
+ this.KEY_NUMPAD_4 = 100;
- default:
- break;
- }
+ /**
+ * Code for the number pad 5 key.
+ * @property KEY_NUMPAD_5
+ * @final
+ * @type Number
+ */
+ this.KEY_NUMPAD_5 = 101;
- const coords = self._getClickCoordsWithinElement(e);
+ /**
+ * Code for the number pad 6 key.
+ * @property KEY_NUMPAD_6
+ * @final
+ * @type Number
+ */
+ this.KEY_NUMPAD_6 = 102;
- /**
- * Fired whenever the mouse is released over the parent
- * {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Canvas"}}Canvas{{/crossLink}}.
- * @event mouseup
- * @param value {[Number, Number]} The mouse coordinates within the {{#crossLink "Canvas"}}Canvas{{/crossLink}},
- */
- self.fire("mouseup", coords, true);
+ /**
+ * Code for the number pad 7 key.
+ * @property KEY_NUMPAD_7
+ * @final
+ * @type Number
+ */
+ this.KEY_NUMPAD_7 = 103;
- if (self.mouseover) {
- e.preventDefault();
- }
- }, true);
+ /**
+ * Code for the number pad 8 key.
+ * @property KEY_NUMPAD_8
+ * @final
+ * @type Number
+ */
+ this.KEY_NUMPAD_8 = 104;
- document.addEventListener("dblclick", this._dblClickListener = function (e) {
+ /**
+ * Code for the number pad 9 key.
+ * @property KEY_NUMPAD_9
+ * @final
+ * @type Number
+ */
+ this.KEY_NUMPAD_9 = 105;
- if (!self.enabled) {
- return;
- }
+ /**
+ * Code for the MULTIPLY key.
+ * @property KEY_MULTIPLY
+ * @final
+ * @type Number
+ */
+ this.KEY_MULTIPLY = 106;
- switch (e.which) {
+ /**
+ * Code for the ADD key.
+ * @property KEY_ADD
+ * @final
+ * @type Number
+ */
+ this.KEY_ADD = 107;
- case 1:// Left button
- self.mouseDownLeft = false;
- self.mouseDownRight = false;
- break;
+ /**
+ * Code for the SUBTRACT key.
+ * @property KEY_SUBTRACT
+ * @final
+ * @type Number
+ */
+ this.KEY_SUBTRACT = 109;
- case 2:// Middle/both buttons
- self.mouseDownMiddle = false;
- break;
+ /**
+ * Code for the DECIMAL POINT key.
+ * @property KEY_DECIMAL_POINT
+ * @final
+ * @type Number
+ */
+ this.KEY_DECIMAL_POINT = 110;
- case 3:// Right button
- self.mouseDownLeft = false;
- self.mouseDownRight = false;
- break;
+ /**
+ * Code for the DIVIDE key.
+ * @property KEY_DIVIDE
+ * @final
+ * @type Number
+ */
+ this.KEY_DIVIDE = 111;
- default:
- break;
- }
+ /**
+ * Code for the F1 key.
+ * @property KEY_F1
+ * @final
+ * @type Number
+ */
+ this.KEY_F1 = 112;
- const coords = self._getClickCoordsWithinElement(e);
+ /**
+ * Code for the F2 key.
+ * @property KEY_F2
+ * @final
+ * @type Number
+ */
+ this.KEY_F2 = 113;
- /**
- * Fired whenever the mouse is double-clicked over the parent
- * {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Canvas"}}Canvas{{/crossLink}}.
- * @event dblclick
- * @param value {[Number, Number]} The mouse coordinates within the {{#crossLink "Canvas"}}Canvas{{/crossLink}},
- */
- self.fire("dblclick", coords, true);
+ /**
+ * Code for the F3 key.
+ * @property KEY_F3
+ * @final
+ * @type Number
+ */
+ this.KEY_F3 = 114;
- if (self.mouseover) {
- e.preventDefault();
- }
- });
+ /**
+ * Code for the F4 key.
+ * @property KEY_F4
+ * @final
+ * @type Number
+ */
+ this.KEY_F4 = 115;
- cfg.element.addEventListener("mousemove", this._mouseMoveListener = function (e) {
+ /**
+ * Code for the F5 key.
+ * @property KEY_F5
+ * @final
+ * @type Number
+ */
+ this.KEY_F5 = 116;
- if (!self.enabled) {
- return;
- }
+ /**
+ * Code for the F6 key.
+ * @property KEY_F6
+ * @final
+ * @type Number
+ */
+ this.KEY_F6 = 117;
- const coords = self._getClickCoordsWithinElement(e);
+ /**
+ * Code for the F7 key.
+ * @property KEY_F7
+ * @final
+ * @type Number
+ */
+ this.KEY_F7 = 118;
- /**
- * Fired whenever the mouse is moved over the parent
- * {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Canvas"}}Canvas{{/crossLink}}.
- * @event mousedown
- * @param value {[Number, Number]} The mouse coordinates within the {{#crossLink "Canvas"}}Canvas{{/crossLink}},
- */
- self.fire("mousemove", coords, true);
+ /**
+ * Code for the F8 key.
+ * @property KEY_F8
+ * @final
+ * @type Number
+ */
+ this.KEY_F8 = 119;
- if (self.mouseover) {
- e.preventDefault();
- }
- });
+ /**
+ * Code for the F9 key.
+ * @property KEY_F9
+ * @final
+ * @type Number
+ */
+ this.KEY_F9 = 120;
- cfg.element.addEventListener("wheel", this._mouseWheelListener = function (e, d) {
+ /**
+ * Code for the F10 key.
+ * @property KEY_F10
+ * @final
+ * @type Number
+ */
+ this.KEY_F10 = 121;
- if (!self.enabled) {
- return;
- }
+ /**
+ * Code for the F11 key.
+ * @property KEY_F11
+ * @final
+ * @type Number
+ */
+ this.KEY_F11 = 122;
- const delta = Math.max(-1, Math.min(1, -e.deltaY * 40));
+ /**
+ * Code for the F12 key.
+ * @property KEY_F12
+ * @final
+ * @type Number
+ */
+ this.KEY_F12 = 123;
- /**
- * Fired whenever the mouse wheel is moved over the parent
- * {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Canvas"}}Canvas{{/crossLink}}.
- * @event mousewheel
- * @param delta {Number} The mouse wheel delta,
- */
- self.fire("mousewheel", delta, true);
- }, {passive: true});
+ /**
+ * Code for the NUM_LOCK key.
+ * @property KEY_NUM_LOCK
+ * @final
+ * @type Number
+ */
+ this.KEY_NUM_LOCK = 144;
- // mouseclicked
+ /**
+ * Code for the SCROLL_LOCK key.
+ * @property KEY_SCROLL_LOCK
+ * @final
+ * @type Number
+ */
+ this.KEY_SCROLL_LOCK = 145;
- (function () {
+ /**
+ * Code for the SEMI_COLON key.
+ * @property KEY_SEMI_COLON
+ * @final
+ * @type Number
+ */
+ this.KEY_SEMI_COLON = 186;
- let downX;
- let downY;
+ /**
+ * Code for the EQUAL_SIGN key.
+ * @property KEY_EQUAL_SIGN
+ * @final
+ * @type Number
+ */
+ this.KEY_EQUAL_SIGN = 187;
- // Tolerance between down and up positions for a mouse click
- const tolerance = 2;
+ /**
+ * Code for the COMMA key.
+ * @property KEY_COMMA
+ * @final
+ * @type Number
+ */
+ this.KEY_COMMA = 188;
- self.on("mousedown", function (params) {
- downX = params[0];
- downY = params[1];
- });
+ /**
+ * Code for the DASH key.
+ * @property KEY_DASH
+ * @final
+ * @type Number
+ */
+ this.KEY_DASH = 189;
- self.on("mouseup", function (params) {
+ /**
+ * Code for the PERIOD key.
+ * @property KEY_PERIOD
+ * @final
+ * @type Number
+ */
+ this.KEY_PERIOD = 190;
- if (downX >= (params[0] - tolerance) &&
- downX <= (params[0] + tolerance) &&
- downY >= (params[1] - tolerance) &&
- downY <= (params[1] + tolerance)) {
+ /**
+ * Code for the FORWARD_SLASH key.
+ * @property KEY_FORWARD_SLASH
+ * @final
+ * @type Number
+ */
+ this.KEY_FORWARD_SLASH = 191;
- /**
- * Fired whenever the mouse is clicked over the parent
- * {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Canvas"}}Canvas{{/crossLink}}.
- * @event mouseclicked
- * @param value {[Number, Number]} The mouse coordinates within the {{#crossLink "Canvas"}}Canvas{{/crossLink}},
- */
- self.fire("mouseclicked", params, true);
- }
- });
- })();
-
-
- // VR
-
- (function () {
-
- const orientationAngleLookup = {
- 'landscape-primary': 90,
- 'landscape-secondary': -90,
- 'portrait-secondary': 180,
- 'portrait-primary': 0
- };
-
- let orientation;
- let orientationAngle;
- const acceleration = xeogl.math.vec3();
- const accelerationIncludingGravity = xeogl.math.vec3();
-
- const orientationChangeEvent = {
- orientation: null,
- orientationAngle: 0
- };
-
- const deviceMotionEvent = {
- orientationAngle: 0,
- acceleration: null,
- accelerationIncludingGravity: accelerationIncludingGravity,
- rotationRate: xeogl.math.vec3(),
- interval: 0
- };
-
- const deviceOrientationEvent = {
- alpha: 0,
- beta: 0,
- gamma: 0,
- absolute: false
- };
-
- if (window.OrientationChangeEvent) {
- window.addEventListener('orientationchange', self._orientationchangedListener = function () {
-
- orientation = window.screen.orientation || window.screen.mozOrientation || window.msOrientation || null;
- orientationAngle = orientation ? (orientationAngleLookup[orientation] || 0) : 0;
-
- orientationChangeEvent.orientation = orientation;
- orientationChangeEvent.orientationAngle = orientationAngle;
-
- /**
- * Fired when the orientation of the device has changed.
- *
- * @event orientationchange
- * @param orientation The orientation: "landscape-primary", "landscape-secondary", "portrait-secondary" or "portrait-primary"
- * @param orientationAngle The orientation angle in degrees: 90 for landscape-primary, -90 for landscape-secondary, 180 for portrait-secondary or 0 for portrait-primary.
- */
- self.fire("orientationchange", orientationChangeEvent);
- },
- false);
- }
+ /**
+ * Code for the GRAVE_ACCENT key.
+ * @property KEY_GRAVE_ACCENT
+ * @final
+ * @type Number
+ */
+ this.KEY_GRAVE_ACCENT = 192;
- if (window.DeviceMotionEvent) {
- window.addEventListener('devicemotion', self._deviceMotionListener = function (e) {
-
- deviceMotionEvent.interval = e.interval;
- deviceMotionEvent.orientationAngle = orientationAngle;
-
- const accel = e.acceleration;
-
- if (accel) {
- acceleration[0] = accel.x;
- acceleration[1] = accel.y;
- acceleration[2] = accel.z;
- deviceMotionEvent.acceleration = acceleration;
- } else {
- deviceMotionEvent.acceleration = null;
- }
-
- const accelGrav = e.accelerationIncludingGravity;
-
- if (accelGrav) {
- accelerationIncludingGravity[0] = accelGrav.x;
- accelerationIncludingGravity[1] = accelGrav.y;
- accelerationIncludingGravity[2] = accelGrav.z;
- deviceMotionEvent.accelerationIncludingGravity = accelerationIncludingGravity;
- } else {
- deviceMotionEvent.accelerationIncludingGravity = null;
- }
-
- deviceMotionEvent.rotationRate = e.rotationRate;
-
- /**
- * Fires on a regular interval and returns data about the rotation
- * (in degrees per second) and acceleration (in meters per second squared) of the device, at that moment in
- * time. Some devices do not have the hardware to exclude the effect of gravity.
- *
- * @event devicemotion
- * @param Float32Array acceleration The acceleration of the device, in meters per second squared, as a 3-element vector. This value has taken into account the effect of gravity and removed it from the figures. This value may not exist if the hardware doesn't know how to remove gravity from the acceleration data.
- * @param Float32Array accelerationIncludingGravity The acceleration of the device, in meters per second squared, as a 3-element vector. This value includes the effect of gravity, and may be the only value available on devices that don't have a gyroscope to allow them to properly remove gravity from the data.
- * @param, Number interval The interval, in milliseconds, at which this event is fired. The next event will be fired in approximately this amount of time.
- * @param Float32Array rotationRate The rates of rotation of the device about each axis, in degrees per second.
- */
- self.fire("devicemotion", deviceMotionEvent);
- },
- false);
- }
+ /**
+ * Code for the OPEN_BRACKET key.
+ * @property KEY_OPEN_BRACKET
+ * @final
+ * @type Number
+ */
+ this.KEY_OPEN_BRACKET = 219;
- if (window.DeviceOrientationEvent) {
- window.addEventListener("deviceorientation", self._deviceOrientListener = function (e) {
-
- deviceOrientationEvent.gamma = e.gamma;
- deviceOrientationEvent.beta = e.beta;
- deviceOrientationEvent.alpha = e.alpha;
- deviceOrientationEvent.absolute = e.absolute;
-
- /**
- * Fired when fresh data is available from an orientation sensor about the current orientation
- * of the device as compared to the Earth coordinate frame. This data is gathered from a
- * magnetometer inside the device. See
- * Orientation and motion data explained for more info.
- *
- * @event deviceorientation
- * @param Number alpha The current orientation of the device around the Z axis in degrees; that is, how far the device is rotated around a line perpendicular to the device.
- * @param Number beta The current orientation of the device around the X axis in degrees; that is, how far the device is tipped forward or backward.
- * @param Number gamma The current orientation of the device around the Y axis in degrees; that is, how far the device is turned left or right.
- * @param Boolean absolute This value is true if the orientation is provided as a difference between the device coordinate frame and the Earth coordinate frame; if the device can't detect the Earth coordinate frame, this value is false.
- */
- self.fire("deviceorientation", deviceOrientationEvent);
- },
- false);
- }
- })();
- },
-
- _getClickCoordsWithinElement: function (event) {
- const coords = [0, 0];
- if (!event) {
- event = window.event;
- coords.x = event.x;
- coords.y = event.y;
- }
- else {
- let element = event.target;
- let totalOffsetLeft = 0;
- let totalOffsetTop = 0;
-
- while (element.offsetParent) {
- totalOffsetLeft += element.offsetLeft;
- totalOffsetTop += element.offsetTop;
- element = element.offsetParent;
- }
- coords[0] = event.pageX - totalOffsetLeft;
- coords[1] = event.pageY - totalOffsetTop;
- }
- return coords;
- },
+ /**
+ * Code for the BACK_SLASH key.
+ * @property KEY_BACK_SLASH
+ * @final
+ * @type Number
+ */
+ this.KEY_BACK_SLASH = 220;
/**
- * Enable or disable all input handlers
- *
- * @param enable
+ * Code for the CLOSE_BRACKET key.
+ * @property KEY_CLOSE_BRACKET
+ * @final
+ * @type Number
*/
- setEnabled: function (enable) {
- if (this.enabled !== enable) {
- this.fire("enabled", this.enabled = enable);
- }
- },
+ this.KEY_CLOSE_BRACKET = 221;
- // Key codes
+ /**
+ * Code for the SINGLE_QUOTE key.
+ * @property KEY_SINGLE_QUOTE
+ * @final
+ * @type Number
+ */
+ this.KEY_SINGLE_QUOTE = 222;
/**
- * Code for the BACKSPACE key.
- * @property KEY_BACKSPACE
+ * Code for the SPACE key.
+ * @property KEY_SPACE
* @final
* @type Number
*/
- KEY_BACKSPACE: 8,
+ this.KEY_SPACE = 32;
+ this.KEY_BACKSPACE = 8;
/**
* Code for the TAB key.
@@ -625,7 +925,7 @@
* @final
* @type Number
*/
- KEY_TAB: 9,
+ this.KEY_TAB = 9;
/**
* Code for the ENTER key.
@@ -633,7 +933,7 @@
* @final
* @type Number
*/
- KEY_ENTER: 13,
+ this.KEY_ENTER = 13;
/**
* Code for the SHIFT key.
@@ -641,7 +941,7 @@
* @final
* @type Number
*/
- KEY_SHIFT: 16,
+ this.KEY_SHIFT = 16;
/**
* Code for the CTRL key.
@@ -649,7 +949,7 @@
* @final
* @type Number
*/
- KEY_CTRL: 17,
+ this.KEY_CTRL = 17;
/**
* Code for the ALT key.
@@ -657,7 +957,7 @@
* @final
* @type Number
*/
- KEY_ALT: 18,
+ this.KEY_ALT = 18;
/**
* Code for the PAUSE_BREAK key.
@@ -665,7 +965,7 @@
* @final
* @type Number
*/
- KEY_PAUSE_BREAK: 19,
+ this.KEY_PAUSE_BREAK = 19;
/**
* Code for the CAPS_LOCK key.
@@ -673,7 +973,7 @@
* @final
* @type Number
*/
- KEY_CAPS_LOCK: 20,
+ this.KEY_CAPS_LOCK = 20;
/**
* Code for the ESCAPE key.
@@ -681,7 +981,7 @@
* @final
* @type Number
*/
- KEY_ESCAPE: 27,
+ this.KEY_ESCAPE = 27;
/**
* Code for the PAGE_UP key.
@@ -689,7 +989,7 @@
* @final
* @type Number
*/
- KEY_PAGE_UP: 33,
+ this.KEY_PAGE_UP = 33;
/**
* Code for the PAGE_DOWN key.
@@ -697,7 +997,7 @@
* @final
* @type Number
*/
- KEY_PAGE_DOWN: 34,
+ this.KEY_PAGE_DOWN = 34;
/**
* Code for the END key.
@@ -705,7 +1005,7 @@
* @final
* @type Number
*/
- KEY_END: 35,
+ this.KEY_END = 35;
/**
* Code for the HOME key.
@@ -713,7 +1013,7 @@
* @final
* @type Number
*/
- KEY_HOME: 36,
+ this.KEY_HOME = 36;
/**
* Code for the LEFT_ARROW key.
@@ -721,7 +1021,7 @@
* @final
* @type Number
*/
- KEY_LEFT_ARROW: 37,
+ this.KEY_LEFT_ARROW = 37;
/**
* Code for the UP_ARROW key.
@@ -729,7 +1029,7 @@
* @final
* @type Number
*/
- KEY_UP_ARROW: 38,
+ this.KEY_UP_ARROW = 38;
/**
* Code for the RIGHT_ARROW key.
@@ -737,7 +1037,7 @@
* @final
* @type Number
*/
- KEY_RIGHT_ARROW: 39,
+ this.KEY_RIGHT_ARROW = 39;
/**
* Code for the DOWN_ARROW key.
@@ -745,7 +1045,7 @@
* @final
* @type Number
*/
- KEY_DOWN_ARROW: 40,
+ this.KEY_DOWN_ARROW = 40;
/**
* Code for the INSERT key.
@@ -753,7 +1053,7 @@
* @final
* @type Number
*/
- KEY_INSERT: 45,
+ this.KEY_INSERT = 45;
/**
* Code for the DELETE key.
@@ -761,7 +1061,7 @@
* @final
* @type Number
*/
- KEY_DELETE: 46,
+ this.KEY_DELETE = 46;
/**
* Code for the 0 key.
@@ -769,7 +1069,7 @@
* @final
* @type Number
*/
- KEY_NUM_0: 48,
+ this.KEY_NUM_0 = 48;
/**
* Code for the 1 key.
@@ -777,7 +1077,7 @@
* @final
* @type Number
*/
- KEY_NUM_1: 49,
+ this.KEY_NUM_1 = 49;
/**
* Code for the 2 key.
@@ -785,7 +1085,7 @@
* @final
* @type Number
*/
- KEY_NUM_2: 50,
+ this.KEY_NUM_2 = 50;
/**
* Code for the 3 key.
@@ -793,7 +1093,7 @@
* @final
* @type Number
*/
- KEY_NUM_3: 51,
+ this.KEY_NUM_3 = 51;
/**
* Code for the 4 key.
@@ -801,7 +1101,7 @@
* @final
* @type Number
*/
- KEY_NUM_4: 52,
+ this.KEY_NUM_4 = 52;
/**
* Code for the 5 key.
@@ -809,7 +1109,7 @@
* @final
* @type Number
*/
- KEY_NUM_5: 53,
+ this.KEY_NUM_5 = 53;
/**
* Code for the 6 key.
@@ -817,7 +1117,7 @@
* @final
* @type Number
*/
- KEY_NUM_6: 54,
+ this.KEY_NUM_6 = 54;
/**
* Code for the 7 key.
@@ -825,7 +1125,7 @@
* @final
* @type Number
*/
- KEY_NUM_7: 55,
+ this.KEY_NUM_7 = 55;
/**
* Code for the 8 key.
@@ -833,7 +1133,7 @@
* @final
* @type Number
*/
- KEY_NUM_8: 56,
+ this.KEY_NUM_8 = 56;
/**
* Code for the 9 key.
@@ -841,7 +1141,7 @@
* @final
* @type Number
*/
- KEY_NUM_9: 57,
+ this.KEY_NUM_9 = 57;
/**
* Code for the A key.
@@ -849,7 +1149,7 @@
* @final
* @type Number
*/
- KEY_A: 65,
+ this.KEY_A = 65;
/**
* Code for the B key.
@@ -857,7 +1157,7 @@
* @final
* @type Number
*/
- KEY_B: 66,
+ this.KEY_B = 66;
/**
* Code for the C key.
@@ -865,7 +1165,7 @@
* @final
* @type Number
*/
- KEY_C: 67,
+ this.KEY_C = 67;
/**
* Code for the D key.
@@ -873,7 +1173,7 @@
* @final
* @type Number
*/
- KEY_D: 68,
+ this.KEY_D = 68;
/**
* Code for the E key.
@@ -881,7 +1181,7 @@
* @final
* @type Number
*/
- KEY_E: 69,
+ this.KEY_E = 69;
/**
* Code for the F key.
@@ -889,7 +1189,7 @@
* @final
* @type Number
*/
- KEY_F: 70,
+ this.KEY_F = 70;
/**
* Code for the G key.
@@ -897,7 +1197,7 @@
* @final
* @type Number
*/
- KEY_G: 71,
+ this.KEY_G = 71;
/**
* Code for the H key.
@@ -905,7 +1205,7 @@
* @final
* @type Number
*/
- KEY_H: 72,
+ this.KEY_H = 72;
/**
* Code for the I key.
@@ -913,7 +1213,7 @@
* @final
* @type Number
*/
- KEY_I: 73,
+ this.KEY_I = 73;
/**
* Code for the J key.
@@ -921,7 +1221,7 @@
* @final
* @type Number
*/
- KEY_J: 74,
+ this.KEY_J = 74;
/**
* Code for the K key.
@@ -929,7 +1229,7 @@
* @final
* @type Number
*/
- KEY_K: 75,
+ this.KEY_K = 75;
/**
* Code for the L key.
@@ -937,7 +1237,7 @@
* @final
* @type Number
*/
- KEY_L: 76,
+ this.KEY_L = 76;
/**
* Code for the M key.
@@ -945,7 +1245,7 @@
* @final
* @type Number
*/
- KEY_M: 77,
+ this.KEY_M = 77;
/**
* Code for the N key.
@@ -953,7 +1253,7 @@
* @final
* @type Number
*/
- KEY_N: 78,
+ this.KEY_N = 78;
/**
* Code for the O key.
@@ -961,7 +1261,7 @@
* @final
* @type Number
*/
- KEY_O: 79,
+ this.KEY_O = 79;
/**
* Code for the P key.
@@ -969,7 +1269,7 @@
* @final
* @type Number
*/
- KEY_P: 80,
+ this.KEY_P = 80;
/**
* Code for the Q key.
@@ -977,7 +1277,7 @@
* @final
* @type Number
*/
- KEY_Q: 81,
+ this.KEY_Q = 81;
/**
* Code for the R key.
@@ -985,7 +1285,7 @@
* @final
* @type Number
*/
- KEY_R: 82,
+ this.KEY_R = 82;
/**
* Code for the S key.
@@ -993,7 +1293,7 @@
* @final
* @type Number
*/
- KEY_S: 83,
+ this.KEY_S = 83;
/**
* Code for the T key.
@@ -1001,7 +1301,7 @@
* @final
* @type Number
*/
- KEY_T: 84,
+ this.KEY_T = 84;
/**
* Code for the U key.
@@ -1009,7 +1309,7 @@
* @final
* @type Number
*/
- KEY_U: 85,
+ this.KEY_U = 85;
/**
* Code for the V key.
@@ -1017,7 +1317,7 @@
* @final
* @type Number
*/
- KEY_V: 86,
+ this.KEY_V = 86;
/**
* Code for the W key.
@@ -1025,7 +1325,7 @@
* @final
* @type Number
*/
- KEY_W: 87,
+ this.KEY_W = 87;
/**
* Code for the X key.
@@ -1033,7 +1333,7 @@
* @final
* @type Number
*/
- KEY_X: 88,
+ this.KEY_X = 88;
/**
* Code for the Y key.
@@ -1041,7 +1341,7 @@
* @final
* @type Number
*/
- KEY_Y: 89,
+ this.KEY_Y = 89;
/**
* Code for the Z key.
@@ -1049,7 +1349,7 @@
* @final
* @type Number
*/
- KEY_Z: 90,
+ this.KEY_Z = 90;
/**
* Code for the LEFT_WINDOW key.
@@ -1057,7 +1357,7 @@
* @final
* @type Number
*/
- KEY_LEFT_WINDOW: 91,
+ this.KEY_LEFT_WINDOW = 91;
/**
* Code for the RIGHT_WINDOW key.
@@ -1065,7 +1365,7 @@
* @final
* @type Number
*/
- KEY_RIGHT_WINDOW: 92,
+ this.KEY_RIGHT_WINDOW = 92;
/**
* Code for the SELECT key.
@@ -1073,7 +1373,7 @@
* @final
* @type Number
*/
- KEY_SELECT_KEY: 93,
+ this.KEY_SELECT_KEY = 93;
/**
* Code for the number pad 0 key.
@@ -1081,7 +1381,7 @@
* @final
* @type Number
*/
- KEY_NUMPAD_0: 96,
+ this.KEY_NUMPAD_0 = 96;
/**
* Code for the number pad 1 key.
@@ -1089,7 +1389,7 @@
* @final
* @type Number
*/
- KEY_NUMPAD_1: 97,
+ this.KEY_NUMPAD_1 = 97;
/**
* Code for the number pad 2 key.
@@ -1097,7 +1397,7 @@
* @final
* @type Number
*/
- KEY_NUMPAD_2: 98,
+ this.KEY_NUMPAD_2 = 98;
/**
* Code for the number pad 3 key.
@@ -1105,7 +1405,7 @@
* @final
* @type Number
*/
- KEY_NUMPAD_3: 99,
+ this.KEY_NUMPAD_3 = 99;
/**
* Code for the number pad 4 key.
@@ -1113,7 +1413,7 @@
* @final
* @type Number
*/
- KEY_NUMPAD_4: 100,
+ this.KEY_NUMPAD_4 = 100;
/**
* Code for the number pad 5 key.
@@ -1121,7 +1421,7 @@
* @final
* @type Number
*/
- KEY_NUMPAD_5: 101,
+ this.KEY_NUMPAD_5 = 101;
/**
* Code for the number pad 6 key.
@@ -1129,7 +1429,7 @@
* @final
* @type Number
*/
- KEY_NUMPAD_6: 102,
+ this.KEY_NUMPAD_6 = 102;
/**
* Code for the number pad 7 key.
@@ -1137,7 +1437,7 @@
* @final
* @type Number
*/
- KEY_NUMPAD_7: 103,
+ this.KEY_NUMPAD_7 = 103;
/**
* Code for the number pad 8 key.
@@ -1145,7 +1445,7 @@
* @final
* @type Number
*/
- KEY_NUMPAD_8: 104,
+ this.KEY_NUMPAD_8 = 104;
/**
* Code for the number pad 9 key.
@@ -1153,7 +1453,7 @@
* @final
* @type Number
*/
- KEY_NUMPAD_9: 105,
+ this.KEY_NUMPAD_9 = 105;
/**
* Code for the MULTIPLY key.
@@ -1161,7 +1461,7 @@
* @final
* @type Number
*/
- KEY_MULTIPLY: 106,
+ this.KEY_MULTIPLY = 106;
/**
* Code for the ADD key.
@@ -1169,7 +1469,7 @@
* @final
* @type Number
*/
- KEY_ADD: 107,
+ this.KEY_ADD = 107;
/**
* Code for the SUBTRACT key.
@@ -1177,7 +1477,7 @@
* @final
* @type Number
*/
- KEY_SUBTRACT: 109,
+ this.KEY_SUBTRACT = 109;
/**
* Code for the DECIMAL POINT key.
@@ -1185,7 +1485,7 @@
* @final
* @type Number
*/
- KEY_DECIMAL_POINT: 110,
+ this.KEY_DECIMAL_POINT = 110;
/**
* Code for the DIVIDE key.
@@ -1193,7 +1493,7 @@
* @final
* @type Number
*/
- KEY_DIVIDE: 111,
+ this.KEY_DIVIDE = 111;
/**
* Code for the F1 key.
@@ -1201,7 +1501,7 @@
* @final
* @type Number
*/
- KEY_F1: 112,
+ this.KEY_F1 = 112;
/**
* Code for the F2 key.
@@ -1209,7 +1509,7 @@
* @final
* @type Number
*/
- KEY_F2: 113,
+ this.KEY_F2 = 113;
/**
* Code for the F3 key.
@@ -1217,7 +1517,7 @@
* @final
* @type Number
*/
- KEY_F3: 114,
+ this.KEY_F3 = 114;
/**
* Code for the F4 key.
@@ -1225,7 +1525,7 @@
* @final
* @type Number
*/
- KEY_F4: 115,
+ this.KEY_F4 = 115;
/**
* Code for the F5 key.
@@ -1233,7 +1533,7 @@
* @final
* @type Number
*/
- KEY_F5: 116,
+ this.KEY_F5 = 116;
/**
* Code for the F6 key.
@@ -1241,7 +1541,7 @@
* @final
* @type Number
*/
- KEY_F6: 117,
+ this.KEY_F6 = 117;
/**
* Code for the F7 key.
@@ -1249,7 +1549,7 @@
* @final
* @type Number
*/
- KEY_F7: 118,
+ this.KEY_F7 = 118;
/**
* Code for the F8 key.
@@ -1257,7 +1557,7 @@
* @final
* @type Number
*/
- KEY_F8: 119,
+ this.KEY_F8 = 119;
/**
* Code for the F9 key.
@@ -1265,7 +1565,7 @@
* @final
* @type Number
*/
- KEY_F9: 120,
+ this.KEY_F9 = 120;
/**
* Code for the F10 key.
@@ -1273,7 +1573,7 @@
* @final
* @type Number
*/
- KEY_F10: 121,
+ this.KEY_F10 = 121;
/**
* Code for the F11 key.
@@ -1281,7 +1581,7 @@
* @final
* @type Number
*/
- KEY_F11: 122,
+ this.KEY_F11 = 122;
/**
* Code for the F12 key.
@@ -1289,7 +1589,7 @@
* @final
* @type Number
*/
- KEY_F12: 123,
+ this.KEY_F12 = 123;
/**
* Code for the NUM_LOCK key.
@@ -1297,7 +1597,7 @@
* @final
* @type Number
*/
- KEY_NUM_LOCK: 144,
+ this.KEY_NUM_LOCK = 144;
/**
* Code for the SCROLL_LOCK key.
@@ -1305,7 +1605,7 @@
* @final
* @type Number
*/
- KEY_SCROLL_LOCK: 145,
+ this.KEY_SCROLL_LOCK = 145;
/**
* Code for the SEMI_COLON key.
@@ -1313,7 +1613,7 @@
* @final
* @type Number
*/
- KEY_SEMI_COLON: 186,
+ this.KEY_SEMI_COLON = 186;
/**
* Code for the EQUAL_SIGN key.
@@ -1321,7 +1621,7 @@
* @final
* @type Number
*/
- KEY_EQUAL_SIGN: 187,
+ this.KEY_EQUAL_SIGN = 187;
/**
* Code for the COMMA key.
@@ -1329,7 +1629,7 @@
* @final
* @type Number
*/
- KEY_COMMA: 188,
+ this.KEY_COMMA = 188;
/**
* Code for the DASH key.
@@ -1337,7 +1637,7 @@
* @final
* @type Number
*/
- KEY_DASH: 189,
+ this.KEY_DASH = 189;
/**
* Code for the PERIOD key.
@@ -1345,7 +1645,7 @@
* @final
* @type Number
*/
- KEY_PERIOD: 190,
+ this.KEY_PERIOD = 190;
/**
* Code for the FORWARD_SLASH key.
@@ -1353,7 +1653,7 @@
* @final
* @type Number
*/
- KEY_FORWARD_SLASH: 191,
+ this.KEY_FORWARD_SLASH = 191;
/**
* Code for the GRAVE_ACCENT key.
@@ -1361,7 +1661,7 @@
* @final
* @type Number
*/
- KEY_GRAVE_ACCENT: 192,
+ this.KEY_GRAVE_ACCENT = 192;
/**
* Code for the OPEN_BRACKET key.
@@ -1369,7 +1669,7 @@
* @final
* @type Number
*/
- KEY_OPEN_BRACKET: 219,
+ this.KEY_OPEN_BRACKET = 219;
/**
* Code for the BACK_SLASH key.
@@ -1377,7 +1677,7 @@
* @final
* @type Number
*/
- KEY_BACK_SLASH: 220,
+ this.KEY_BACK_SLASH = 220;
/**
* Code for the CLOSE_BRACKET key.
@@ -1385,7 +1685,7 @@
* @final
* @type Number
*/
- KEY_CLOSE_BRACKET: 221,
+ this.KEY_CLOSE_BRACKET = 221;
/**
* Code for the SINGLE_QUOTE key.
@@ -1393,7 +1693,7 @@
* @final
* @type Number
*/
- KEY_SINGLE_QUOTE: 222,
+ this.KEY_SINGLE_QUOTE = 222;
/**
* Code for the SPACE key.
@@ -1401,30 +1701,536 @@
* @final
* @type Number
*/
- KEY_SPACE: 32,
+ this.KEY_SPACE = 32;
+
+ this._element = cfg.element;
+
+ // True when ALT down
+ this.altDown = false;
+
+ /** True whenever CTRL is down
+ *
+ * @type {boolean}
+ */
+ this.ctrlDown = false;
+
+ /** True whenever left mouse button is down
+ *
+ * @type {boolean}
+ */
+ this.mouseDownLeft = false;
+
+ /** True whenever middle mouse button is down
+ *
+ * @type {boolean}
+ */
+ this.mouseDownMiddle = false;
+
+ /** True whenever right mouse button is down
+ *
+ * @type {boolean}
+ */
+ this.mouseDownRight = false;
+
+ /** Flag for each key that's down
+ *
+ * @type {boolean}
+ */
+ this.keyDown = [];
+
+ /** True while input enabled
+ *
+ * @type {boolean}
+ */
+ this.enabled = true;
+
+ /** True while mouse is over the parent {{#crossLink "Scene"}}Scene's{{/crossLink}} {{#crossLink "Canvas"}}Canvas{{/crossLink}}
+ *
+ * @type {boolean}
+ */
+ this.mouseover = false;
+
+ // Capture input events and publish them on this component
+
+ document.addEventListener("keydown", this._keyDownListener = function (e) {
+
+ if (!self.enabled) {
+ return;
+ }
+
+ if (e.target.tagName !== "INPUT" && e.target.tagName !== "TEXTAREA") {
+
+ if (e.ctrlKey) {
+ self.ctrlDown = true;
+
+ } else if (e.altKey) {
+ self.altDown = true;
+
+ } else {
+ self.keyDown[e.keyCode] = true;
+
+ /**
+ * Fired whenever a key is pressed while the parent
+ * {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Canvas"}}Canvas{{/crossLink}} has input focus.
+ * @event keydown
+ * @param value {Number} The key code, for example {{#crossLink "Input/KEY_LEFT_ARROW:property"}}{{/crossLink}},
+ */
+ self.fire("keydown", e.keyCode, true);
+ }
+ }
+
+ if (self.mouseover) {
+ e.preventDefault();
+ }
+
+ }, true);
+
+ document.addEventListener("keyup", this._keyUpListener = function (e) {
+
+ if (!self.enabled) {
+ return;
+ }
+
+ if (e.target.tagName !== "INPUT" && e.target.tagName !== "TEXTAREA") {
+
+ if (e.ctrlKey) {
+ self.ctrlDown = false;
+
+ } else if (e.altKey) {
+ self.altDown = false;
+
+ } else {
+ self.keyDown[e.keyCode] = false;
+
+ /**
+ * Fired whenever a key is released while the parent
+ * {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Canvas"}}Canvas{{/crossLink}} has input focus.
+ * @event keyup
+ * @param value {Number} The key code, for example {{#crossLink "Input/KEY_LEFT_ARROW:property"}}{{/crossLink}},
+ */
+ self.fire("keyup", e.keyCode, true);
+ }
+ }
+ });
+
+ cfg.element.addEventListener("mouseenter", this._mouseEnterListener = function (e) {
+
+ if (!self.enabled) {
+ return;
+ }
+
+ self.mouseover = true;
+
+ const coords = self._getClickCoordsWithinElement(e);
+
+ /**
+ * Fired whenever the mouse is moved into of the parent
+ * {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Canvas"}}Canvas{{/crossLink}}.
+ * @event mouseenter
+ * @param value {[Number, Number]} The mouse coordinates within the {{#crossLink "Canvas"}}Canvas{{/crossLink}},
+ */
+ self.fire("mouseenter", coords, true);
+ });
+
+ cfg.element.addEventListener("mouseleave", this._mouseLeaveListener = function (e) {
+
+ if (!self.enabled) {
+ return;
+ }
+
+ self.mouseover = false;
+
+ const coords = self._getClickCoordsWithinElement(e);
+
+ /**
+ * Fired whenever the mouse is moved out of the parent
+ * {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Canvas"}}Canvas{{/crossLink}}.
+ * @event mouseleave
+ * @param value {[Number, Number]} The mouse coordinates within the {{#crossLink "Canvas"}}Canvas{{/crossLink}},
+ */
+ self.fire("mouseleave", coords, true);
+ });
+
+
+ cfg.element.addEventListener("mousedown", this._mouseDownListener = function (e) {
+
+ if (!self.enabled) {
+ return;
+ }
+
+ switch (e.which) {
+
+ case 1:// Left button
+ self.mouseDownLeft = true;
+ break;
+
+ case 2:// Middle/both buttons
+ self.mouseDownMiddle = true;
+ break;
+
+ case 3:// Right button
+ self.mouseDownRight = true;
+ break;
+
+ default:
+ break;
+ }
+
+ const coords = self._getClickCoordsWithinElement(e);
+
+ cfg.element.focus();
+
+ /**
+ * Fired whenever the mouse is pressed over the parent
+ * {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Canvas"}}Canvas{{/crossLink}}.
+ * @event mousedown
+ * @param value {[Number, Number]} The mouse coordinates within the {{#crossLink "Canvas"}}Canvas{{/crossLink}},
+ */
+ self.fire("mousedown", coords, true);
+
+ if (self.mouseover) {
+ e.preventDefault();
+ }
+ });
+
+ document.addEventListener("mouseup", this._mouseUpListener = function (e) {
+
+ if (!self.enabled) {
+ return;
+ }
+
+ switch (e.which) {
+
+ case 1:// Left button
+ self.mouseDownLeft = false;
+ break;
+
+ case 2:// Middle/both buttons
+ self.mouseDownMiddle = false;
+ break;
+
+ case 3:// Right button
+ self.mouseDownRight = false;
+ break;
+
+ default:
+ break;
+ }
+
+ const coords = self._getClickCoordsWithinElement(e);
+
+ /**
+ * Fired whenever the mouse is released over the parent
+ * {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Canvas"}}Canvas{{/crossLink}}.
+ * @event mouseup
+ * @param value {[Number, Number]} The mouse coordinates within the {{#crossLink "Canvas"}}Canvas{{/crossLink}},
+ */
+ self.fire("mouseup", coords, true);
+
+ if (self.mouseover) {
+ e.preventDefault();
+ }
+ }, true);
+
+ document.addEventListener("dblclick", this._dblClickListener = function (e) {
+
+ if (!self.enabled) {
+ return;
+ }
+
+ switch (e.which) {
+
+ case 1:// Left button
+ self.mouseDownLeft = false;
+ self.mouseDownRight = false;
+ break;
+
+ case 2:// Middle/both buttons
+ self.mouseDownMiddle = false;
+ break;
+
+ case 3:// Right button
+ self.mouseDownLeft = false;
+ self.mouseDownRight = false;
+ break;
+
+ default:
+ break;
+ }
+
+ const coords = self._getClickCoordsWithinElement(e);
+
+ /**
+ * Fired whenever the mouse is double-clicked over the parent
+ * {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Canvas"}}Canvas{{/crossLink}}.
+ * @event dblclick
+ * @param value {[Number, Number]} The mouse coordinates within the {{#crossLink "Canvas"}}Canvas{{/crossLink}},
+ */
+ self.fire("dblclick", coords, true);
+
+ if (self.mouseover) {
+ e.preventDefault();
+ }
+ });
+
+ cfg.element.addEventListener("mousemove", this._mouseMoveListener = function (e) {
+
+ if (!self.enabled) {
+ return;
+ }
+
+ const coords = self._getClickCoordsWithinElement(e);
+
+ /**
+ * Fired whenever the mouse is moved over the parent
+ * {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Canvas"}}Canvas{{/crossLink}}.
+ * @event mousedown
+ * @param value {[Number, Number]} The mouse coordinates within the {{#crossLink "Canvas"}}Canvas{{/crossLink}},
+ */
+ self.fire("mousemove", coords, true);
+
+ if (self.mouseover) {
+ e.preventDefault();
+ }
+ });
+
+ cfg.element.addEventListener("wheel", this._mouseWheelListener = function (e, d) {
+
+ if (!self.enabled) {
+ return;
+ }
+
+ const delta = Math.max(-1, Math.min(1, -e.deltaY * 40));
+
+ /**
+ * Fired whenever the mouse wheel is moved over the parent
+ * {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Canvas"}}Canvas{{/crossLink}}.
+ * @event mousewheel
+ * @param delta {Number} The mouse wheel delta,
+ */
+ self.fire("mousewheel", delta, true);
+ }, {passive: true});
+
+ // mouseclicked
+
+ (function () {
+
+ let downX;
+ let downY;
+
+ // Tolerance between down and up positions for a mouse click
+ const tolerance = 2;
+
+ self.on("mousedown", function (params) {
+ downX = params[0];
+ downY = params[1];
+ });
+
+ self.on("mouseup", function (params) {
+
+ if (downX >= (params[0] - tolerance) &&
+ downX <= (params[0] + tolerance) &&
+ downY >= (params[1] - tolerance) &&
+ downY <= (params[1] + tolerance)) {
+
+ /**
+ * Fired whenever the mouse is clicked over the parent
+ * {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Canvas"}}Canvas{{/crossLink}}.
+ * @event mouseclicked
+ * @param value {[Number, Number]} The mouse coordinates within the {{#crossLink "Canvas"}}Canvas{{/crossLink}},
+ */
+ self.fire("mouseclicked", params, true);
+ }
+ });
+ })();
+
+ // VR
+
+ (function () {
+
+ const orientationAngleLookup = {
+ 'landscape-primary': 90,
+ 'landscape-secondary': -90,
+ 'portrait-secondary': 180,
+ 'portrait-primary': 0
+ };
+
+ let orientation;
+ let orientationAngle;
+ const acceleration = math.vec3();
+ const accelerationIncludingGravity = math.vec3();
+
+ const orientationChangeEvent = {
+ orientation: null,
+ orientationAngle: 0
+ };
+
+ const deviceMotionEvent = {
+ orientationAngle: 0,
+ acceleration: null,
+ accelerationIncludingGravity: accelerationIncludingGravity,
+ rotationRate: math.vec3(),
+ interval: 0
+ };
+
+ const deviceOrientationEvent = {
+ alpha: 0,
+ beta: 0,
+ gamma: 0,
+ absolute: false
+ };
- _destroy: function () {
- // Prevent memory leak when destroying canvas/WebGL context
- document.removeEventListener("keydown", this._keyDownListener);
- document.removeEventListener("keyup", this._keyUpListener);
- this._element.removeEventListener("mouseenter", this._mouseEnterListener);
- this._element.removeEventListener("mouseleave", this._mouseLeaveListener);
- this._element.removeEventListener("mousedown", this._mouseDownListener);
- document.removeEventListener("mouseup", this._mouseDownListener);
- document.removeEventListener("dblclick", this._dblClickListener);
- this._element.removeEventListener("mousemove", this._mouseMoveListener);
- this._element.removeEventListener("wheel", this._mouseWheelListener);
if (window.OrientationChangeEvent) {
- window.removeEventListener('orientationchange', this._orientationchangedListener);
+ window.addEventListener('orientationchange', self._orientationchangedListener = function () {
+
+ orientation = window.screen.orientation || window.screen.mozOrientation || window.msOrientation || null;
+ orientationAngle = orientation ? (orientationAngleLookup[orientation] || 0) : 0;
+
+ orientationChangeEvent.orientation = orientation;
+ orientationChangeEvent.orientationAngle = orientationAngle;
+
+ /**
+ * Fired when the orientation of the device has changed.
+ *
+ * @event orientationchange
+ * @param orientation The orientation: "landscape-primary", "landscape-secondary", "portrait-secondary" or "portrait-primary"
+ * @param orientationAngle The orientation angle in degrees: 90 for landscape-primary, -90 for landscape-secondary, 180 for portrait-secondary or 0 for portrait-primary.
+ */
+ self.fire("orientationchange", orientationChangeEvent);
+ },
+ false);
}
+
if (window.DeviceMotionEvent) {
- window.removeEventListener('devicemotion', this._deviceMotionListener);
+ window.addEventListener('devicemotion', self._deviceMotionListener = function (e) {
+
+ deviceMotionEvent.interval = e.interval;
+ deviceMotionEvent.orientationAngle = orientationAngle;
+
+ const accel = e.acceleration;
+
+ if (accel) {
+ acceleration[0] = accel.x;
+ acceleration[1] = accel.y;
+ acceleration[2] = accel.z;
+ deviceMotionEvent.acceleration = acceleration;
+ } else {
+ deviceMotionEvent.acceleration = null;
+ }
+
+ const accelGrav = e.accelerationIncludingGravity;
+
+ if (accelGrav) {
+ accelerationIncludingGravity[0] = accelGrav.x;
+ accelerationIncludingGravity[1] = accelGrav.y;
+ accelerationIncludingGravity[2] = accelGrav.z;
+ deviceMotionEvent.accelerationIncludingGravity = accelerationIncludingGravity;
+ } else {
+ deviceMotionEvent.accelerationIncludingGravity = null;
+ }
+
+ deviceMotionEvent.rotationRate = e.rotationRate;
+
+ /**
+ * Fires on a regular interval and returns data about the rotation
+ * (in degrees per second) and acceleration (in meters per second squared) of the device, at that moment in
+ * time. Some devices do not have the hardware to exclude the effect of gravity.
+ *
+ * @event devicemotion
+ * @param Float32Array acceleration The acceleration of the device, in meters per second squared, as a 3-element vector. This value has taken into account the effect of gravity and removed it from the figures. This value may not exist if the hardware doesn't know how to remove gravity from the acceleration data.
+ * @param Float32Array accelerationIncludingGravity The acceleration of the device, in meters per second squared, as a 3-element vector. This value includes the effect of gravity, and may be the only value available on devices that don't have a gyroscope to allow them to properly remove gravity from the data.
+ * @param, Number interval The interval, in milliseconds, at which this event is fired. The next event will be fired in approximately this amount of time.
+ * @param Float32Array rotationRate The rates of rotation of the device about each axis, in degrees per second.
+ */
+ self.fire("devicemotion", deviceMotionEvent);
+ },
+ false);
}
+
if (window.DeviceOrientationEvent) {
- window.addEventListener("deviceorientation", this._deviceOrientListener);
+ window.addEventListener("deviceorientation", self._deviceOrientListener = function (e) {
+
+ deviceOrientationEvent.gamma = e.gamma;
+ deviceOrientationEvent.beta = e.beta;
+ deviceOrientationEvent.alpha = e.alpha;
+ deviceOrientationEvent.absolute = e.absolute;
+
+ /**
+ * Fired when fresh data is available from an orientation sensor about the current orientation
+ * of the device as compared to the Earth coordinate frame. This data is gathered from a
+ * magnetometer inside the device. See
+ * Orientation and motion data explained for more info.
+ *
+ * @event deviceorientation
+ * @param Number alpha The current orientation of the device around the Z axis in degrees; that is, how far the device is rotated around a line perpendicular to the device.
+ * @param Number beta The current orientation of the device around the X axis in degrees; that is, how far the device is tipped forward or backward.
+ * @param Number gamma The current orientation of the device around the Y axis in degrees; that is, how far the device is turned left or right.
+ * @param Boolean absolute This value is true if the orientation is provided as a difference between the device coordinate frame and the Earth coordinate frame; if the device can't detect the Earth coordinate frame, this value is false.
+ */
+ self.fire("deviceorientation", deviceOrientationEvent);
+ },
+ false);
+ }
+ })();
+ }
+
+ _getClickCoordsWithinElement(event) {
+ const coords = [0, 0];
+ if (!event) {
+ event = window.event;
+ coords.x = event.x;
+ coords.y = event.y;
+ }
+ else {
+ let element = event.target;
+ let totalOffsetLeft = 0;
+ let totalOffsetTop = 0;
+
+ while (element.offsetParent) {
+ totalOffsetLeft += element.offsetLeft;
+ totalOffsetTop += element.offsetTop;
+ element = element.offsetParent;
}
+ coords[0] = event.pageX - totalOffsetLeft;
+ coords[1] = event.pageY - totalOffsetTop;
+ }
+ return coords;
+ }
+
+ /**
+ * Enable or disable all input handlers
+ *
+ * @param enable
+ */
+ setEnabled(enable) {
+ if (this.enabled !== enable) {
+ this.fire("enabled", this.enabled = enable);
+ }
+ }
+
+ destroy() {
+ super.destroy();
+ // Prevent memory leak when destroying canvas/WebGL context
+ document.removeEventListener("keydown", this._keyDownListener);
+ document.removeEventListener("keyup", this._keyUpListener);
+ this._element.removeEventListener("mouseenter", this._mouseEnterListener);
+ this._element.removeEventListener("mouseleave", this._mouseLeaveListener);
+ this._element.removeEventListener("mousedown", this._mouseDownListener);
+ document.removeEventListener("mouseup", this._mouseDownListener);
+ document.removeEventListener("dblclick", this._dblClickListener);
+ this._element.removeEventListener("mousemove", this._mouseMoveListener);
+ this._element.removeEventListener("wheel", this._mouseWheelListener);
+ if (window.OrientationChangeEvent) {
+ window.removeEventListener('orientationchange', this._orientationchangedListener);
+ }
+ if (window.DeviceMotionEvent) {
+ window.removeEventListener('devicemotion', this._deviceMotionListener);
+ }
+ if (window.DeviceOrientationEvent) {
+ window.addEventListener("deviceorientation", this._deviceOrientListener);
}
- });
+ }
+}
-})();
+export{Input};
diff --git a/src/lighting/ambientLight.js b/src/lighting/ambientLight.js
index b7f122de1..0ac04c369 100644
--- a/src/lighting/ambientLight.js
+++ b/src/lighting/ambientLight.js
@@ -50,8 +50,7 @@
@module xeogl
@submodule lighting
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}}, creates this AmbientLight within the
- default {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted
+ @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] {*} AmbientLight configuration
@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 AmbientLight.
@@ -59,65 +58,74 @@
@param [cfg.intensity=[1.0]] {Number} The intensity of this AmbientLight, as a factor in range ````[0..1]````.
@extends Component
*/
-(function () {
-
- "use strict";
-
- xeogl.AmbientLight = xeogl.Component.extend({
-
- type: "xeogl.AmbientLight",
-
- _init: function (cfg) {
- this._state = {
- type: "ambient",
- color: xeogl.math.vec3([0.7, 0.7, 0.7]),
- intensity: 1.0
- };
- this.color = cfg.color;
- this.intensity = cfg.intensity;
- this.scene._lightCreated(this);
- },
-
- _props: {
-
- /**
- The color of this AmbientLight.
-
- @property color
- @default [0.7, 0.7, 0.8]
- @type Float32Array
- */
- color: {
- set: function (value) {
- this._state.color.set(value || [ 0.7, 0.7, 0.8 ]);
- this._renderer.setImageForceDirty();
- },
- get: function () {
- return this._state.color;
- }
- },
-
- /**
- The intensity of this AmbientLight.
-
- @property intensity
- @default 1.0
- @type Number
- */
- intensity: {
- set: function (value) {
- this._state.intensity = value !== undefined ? value : 1.0;
- this._renderer.setImageForceDirty();
- },
- get: function () {
- return this._state.intensity;
- }
- }
- },
-
- _destroy: function () {
- this.scene._lightDestroyed(this);
- }
- });
-
-})();
+import {math} from '../math/math.js';
+import {Component} from '../component.js';
+
+const type = "AmbientLight";
+
+class AmbientLight extends Component {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return type;
+ }
+
+ init(cfg) {
+ super.init(cfg);
+ this._state = {
+ type: "ambient",
+ color: math.vec3([0.7, 0.7, 0.7]),
+ intensity: 1.0
+ };
+ this.color = cfg.color;
+ this.intensity = cfg.intensity;
+ this.scene._lightCreated(this);
+ }
+
+ /**
+ The color of this AmbientLight.
+
+ @property color
+ @default [0.7, 0.7, 0.8]
+ @type Float32Array
+ */
+ set color(value) {
+ this._state.color.set(value || [0.7, 0.7, 0.8]);
+ this._renderer.setImageForceDirty();
+ }
+
+ get color() {
+ return this._state.color;
+ }
+
+ /**
+ The intensity of this AmbientLight.
+
+ @property intensity
+ @default 1.0
+ @type Number
+ */
+ set intensity(value) {
+ this._state.intensity = value !== undefined ? value : 1.0;
+ this._renderer.setImageForceDirty();
+ }
+
+ get intensity() {
+ return this._state.intensity;
+ }
+
+ destroy() {
+ super.destroy();
+ this._state.destroy();
+ }
+}
+
+export {AmbientLight};
diff --git a/src/lighting/cubeTexture.js b/src/lighting/cubeTexture.js
index 60b0f8cc1..9a1a81e4b 100644
--- a/src/lighting/cubeTexture.js
+++ b/src/lighting/cubeTexture.js
@@ -9,8 +9,7 @@
@module xeogl
@submodule lighting
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}} - creates this CubeTexture in the default
- {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
+ @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 for this CubeTexture, unique among all components in the parent scene, generated automatically when omitted.
@param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to this CubeTexture.
@@ -19,112 +18,155 @@
@param [cfg.encoding="linear"] {String} Encoding format. See the {{#crossLink "CubeTexture/encoding:property"}}{{/crossLink}} property for more info.
@extends Component
*/
-(function () {
-
- "use strict";
-
- xeogl.CubeTexture = xeogl.Component.extend({
-
- type: "xeogl.CubeTexture",
-
- _init: function (cfg) {
-
- const gl = this.scene.canvas.gl;
-
- this._state = new xeogl.renderer.State({
- texture: new xeogl.renderer.Texture2D(gl, gl.TEXTURE_CUBE_MAP),
- flipY: this._checkFlipY(cfg.minFilter),
- encoding: this._checkEncoding(cfg.encoding),
- minFilter: "linearMipmapLinear",
- magFilter: "linear",
- wrapS: "clampToEdge",
- wrapT: "clampToEdge",
- mipmaps: true
- });
-
- this._src = cfg.src;
- this._images = [];
-
- this._loadSrc(cfg.src);
-
- xeogl.stats.memory.textures++;
- },
-
- _checkFlipY: function (value) {
- return !!value;
- },
-
- _checkEncoding: function (value) {
- value = value || "linear";
- if (value !== "linear" && value !== "sRGB" && value !== "gamma") {
- this.error("Unsupported value for 'encoding': '" + value + "' - supported values are 'linear', 'sRGB', 'gamma'. Defaulting to 'linear'.");
- value = "linear";
- }
- return value;
- },
-
- _webglContextRestored: function () {
- const gl = this.scene.canvas.gl;
- this._state.texture = null;
- // if (this._images.length > 0) {
- // this._state.texture = new xeogl.renderer.Texture2D(gl, gl.TEXTURE_CUBE_MAP);
- // this._state.texture.setImage(this._images, this._state);
- // this._state.texture.setProps(this._state);
- // } else
- if (this._src) {
- this._loadSrc(this._src);
- }
- },
-
- _loadSrc: function (src) {
- const self = this;
- const gl = this.scene.canvas.gl;
- this._images = [];
- let loadFailed = false;
- let numLoaded = 0;
- for (let i = 0; i < src.length; i++) {
- const image = new Image();
- image.onload = (function () {
- let _image = image;
- const index = i;
- return function () {
- if (loadFailed) {
- return;
- }
- _image = xeogl.renderer.ensureImageSizePowerOfTwo(_image);
- self._images[index] = _image;
- numLoaded++;
- if (numLoaded === 6) {
- let texture = self._state.texture;
- if (!texture) {
- texture = new xeogl.renderer.Texture2D(gl, gl.TEXTURE_CUBE_MAP);
- self._state.texture = texture;
- }
- texture.setImage(self._images, self._state);
- texture.setProps(self._state);
- /**
- * Fired whenever this CubeTexture has loaded the
- * image files that its {{#crossLink "CubeTexture/src:property"}}{{/crossLink}} property currently points to.
- * @event loaded
- * @param value {HTML Image} The value of the {{#crossLink "CubeTexture/src:property"}}{{/crossLink}} property
- */
- self.fire("loaded", self._src);
+
+import {Component} from '../component.js';
+import {State} from '../renderer/state.js';
+import {Texture2D} from '../renderer/texture2d.js';
+import {stats} from './../stats.js';
+
+function ensureImageSizePowerOfTwo(image) {
+ if (!isPowerOfTwo(image.width) || !isPowerOfTwo(image.height)) {
+ const canvas = document.createElement("canvas");
+ canvas.width = nextHighestPowerOfTwo(image.width);
+ canvas.height = nextHighestPowerOfTwo(image.height);
+ const ctx = canvas.getContext("2d");
+ ctx.drawImage(image,
+ 0, 0, image.width, image.height,
+ 0, 0, canvas.width, canvas.height);
+ image = canvas;
+ }
+ return image;
+}
+
+function isPowerOfTwo(x) {
+ return (x & (x - 1)) === 0;
+}
+
+function nextHighestPowerOfTwo(x) {
+ --x;
+ for (let i = 1; i < 32; i <<= 1) {
+ x = x | x >> i;
+ }
+ return x + 1;
+}
+
+class CubeTexture extends Component{
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return "xeogl.CubeTexture";
+ }
+
+ init(cfg) {
+
+ super.init(cfg);
+
+ const gl = this.scene.canvas.gl;
+
+ this._state = new State({
+ texture: new Texture2D(gl, gl.TEXTURE_CUBE_MAP),
+ flipY: this._checkFlipY(cfg.minFilter),
+ encoding: this._checkEncoding(cfg.encoding),
+ minFilter: "linearMipmapLinear",
+ magFilter: "linear",
+ wrapS: "clampToEdge",
+ wrapT: "clampToEdge",
+ mipmaps: true
+ });
+
+ this._src = cfg.src;
+ this._images = [];
+
+ this._loadSrc(cfg.src);
+
+ stats.memory.textures++;
+ }
+
+ _checkFlipY(value) {
+ return !!value;
+ }
+
+ _checkEncoding (value) {
+ value = value || "linear";
+ if (value !== "linear" && value !== "sRGB" && value !== "gamma") {
+ this.error("Unsupported value for 'encoding': '" + value + "' - supported values are 'linear', 'sRGB', 'gamma'. Defaulting to 'linear'.");
+ value = "linear";
+ }
+ return value;
+ }
+
+ _webglContextRestored () {
+ const gl = this.scene.canvas.gl;
+ this._state.texture = null;
+ // if (this._images.length > 0) {
+ // this._state.texture = new xeogl.renderer.Texture2D(gl, gl.TEXTURE_CUBE_MAP);
+ // this._state.texture.setImage(this._images, this._state);
+ // this._state.texture.setProps(this._state);
+ // } else
+ if (this._src) {
+ this._loadSrc(this._src);
+ }
+ }
+
+ _loadSrc (src) {
+ const self = this;
+ const gl = this.scene.canvas.gl;
+ this._images = [];
+ let loadFailed = false;
+ let numLoaded = 0;
+ for (let i = 0; i < src.length; i++) {
+ const image = new Image();
+ image.onload = (function () {
+ let _image = image;
+ const index = i;
+ return function () {
+ if (loadFailed) {
+ return;
+ }
+ _image = ensureImageSizePowerOfTwo(_image);
+ self._images[index] = _image;
+ numLoaded++;
+ if (numLoaded === 6) {
+ let texture = self._state.texture;
+ if (!texture) {
+ texture = new Texture2D(gl, gl.TEXTURE_CUBE_MAP);
+ self._state.texture = texture;
}
- };
- })();
- image.onerror = function () {
- loadFailed = true;
+ texture.setImage(self._images, self._state);
+ texture.setProps(self._state);
+ /**
+ * Fired whenever this CubeTexture has loaded the
+ * image files that its {{#crossLink "CubeTexture/src:property"}}{{/crossLink}} property currently points to.
+ * @event loaded
+ * @param value {HTML Image} The value of the {{#crossLink "CubeTexture/src:property"}}{{/crossLink}} property
+ */
+ self.fire("loaded", self._src);
+ }
};
- image.src = src[i];
- }
- },
-
- _destroy: function () {
- if (this._state.texture) {
- this._state.texture.destroy();
- }
- xeogl.stats.memory.textures--;
+ })();
+ image.onerror = function () {
+ loadFailed = true;
+ };
+ image.src = src[i];
+ }
+ }
+
+ destroy() {
+ super.destroy();
+ if (this._state.texture) {
+ this._state.texture.destroy();
}
- });
+ stats.memory.textures--;
+ this._state.destroy();
+ }
+}
-})();
+export {CubeTexture};
\ No newline at end of file
diff --git a/src/lighting/dirLight.js b/src/lighting/dirLight.js
index 15966311a..5a0dc0f97 100644
--- a/src/lighting/dirLight.js
+++ b/src/lighting/dirLight.js
@@ -61,8 +61,7 @@
@module xeogl
@submodule lighting
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}}, creates this DirLight within the
- default {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted
+ @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] {*} The DirLight configuration
@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 DirLight.
@@ -74,164 +73,170 @@
@param [cfg.shadow=false] {Boolean} Flag which indicates if this DirLight casts a shadow.
@extends Component
*/
-(function () {
-
- "use strict";
-
- const math = xeogl.math;
-
- xeogl.DirLight = xeogl.Component.extend({
-
- type: "xeogl.DirLight",
-
- _init: function (cfg) {
-
- const self = this;
-
- this._shadowRenderBuf = null;
- this._shadowViewMatrix = null;
- this._shadowProjMatrix = null;
- this._shadowViewMatrixDirty = true;
- this._shadowProjMatrixDirty = true;
-
- this._state = new xeogl.renderer.State({
- type: "dir",
- dir: xeogl.math.vec3([1.0, 1.0, 1.0]),
- color: xeogl.math.vec3([0.7, 0.7, 0.8]),
- intensity: 1.0,
- space: cfg.space || "view",
- shadow: false,
- shadowDirty: true,
-
- getShadowViewMatrix: (function () {
- const look = math.vec3();
- const up = math.vec3([0, 1, 0]);
- return function () {
- if (self._shadowViewMatrixDirty) {
- if (!self._shadowViewMatrix) {
- self._shadowViewMatrix = math.identityMat4();
- }
- const dir = self._state.dir;
- math.lookAtMat4v([-dir[0], -dir[1], -dir[2]], [0, 0, 0], up, self._shadowViewMatrix);
- self._shadowViewMatrixDirty = false;
+import {Component} from '../component.js';
+import {State} from '../renderer/state.js';
+import {RenderBuffer} from '../renderer/renderBuffer.js';
+import {math} from '../math/math.js';
+
+class DirLight extends Component {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return "xeogl.DirLight";
+ }
+
+ init(cfg) {
+
+ super.init(cfg);
+
+ const self = this;
+
+ this._shadowRenderBuf = null;
+ this._shadowViewMatrix = null;
+ this._shadowProjMatrix = null;
+ this._shadowViewMatrixDirty = true;
+ this._shadowProjMatrixDirty = true;
+
+ this._state = new State({
+ type: "dir",
+ dir: math.vec3([1.0, 1.0, 1.0]),
+ color: math.vec3([0.7, 0.7, 0.8]),
+ intensity: 1.0,
+ space: cfg.space || "view",
+ shadow: false,
+ shadowDirty: true,
+
+ getShadowViewMatrix: (function () {
+ const look = math.vec3();
+ const up = math.vec3([0, 1, 0]);
+ return function () {
+ if (self._shadowViewMatrixDirty) {
+ if (!self._shadowViewMatrix) {
+ self._shadowViewMatrix = math.identityMat4();
}
- return self._shadowViewMatrix;
- };
- })(),
-
- getShadowProjMatrix: function () {
- if (self._shadowProjMatrixDirty) { // TODO: Set when canvas resizes
- if (!self._shadowProjMatrix) {
- self._shadowProjMatrix = math.identityMat4();
- }
- xeogl.math.orthoMat4c(-10, 10, -10, 10, 0, 1000.0, self._shadowProjMatrix);
- self._shadowProjMatrixDirty = false;
+ const dir = self._state.dir;
+ math.lookAtMat4v([-dir[0], -dir[1], -dir[2]], [0, 0, 0], up, self._shadowViewMatrix);
+ self._shadowViewMatrixDirty = false;
}
- return self._shadowProjMatrix;
- },
-
- getShadowRenderBuf: function () {
- if (!self._shadowRenderBuf) {
- self._shadowRenderBuf = new xeogl.renderer.RenderBuffer(self.scene.canvas.canvas, self.scene.canvas.gl);
+ return self._shadowViewMatrix;
+ };
+ })(),
+
+ getShadowProjMatrix: function () {
+ if (self._shadowProjMatrixDirty) { // TODO: Set when canvas resizes
+ if (!self._shadowProjMatrix) {
+ self._shadowProjMatrix = math.identityMat4();
}
- return self._shadowRenderBuf;
- }
- });
-
- this.dir = cfg.dir;
- this.color = cfg.color;
- this.intensity = cfg.intensity;
- this.shadow = cfg.shadow;
- this.scene._lightCreated(this);
- },
-
- _props: {
-
- /**
- The direction in which the light is shining.
-
- @property dir
- @default [1.0, 1.0, 1.0]
- @type Float32Array
- */
- dir: {
- set: function (value) {
- this._state.dir.set(value || [1.0, 1.0, 1.0]);
- this._shadowViewMatrixDirty = true;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.dir;
- }
- },
-
- /**
- The color of this DirLight.
-
- @property color
- @default [0.7, 0.7, 0.8]
- @type Float32Array
- */
- color: {
- set: function (value) {
- this._state.color.set(value || [0.7, 0.7, 0.8]);
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.color;
- }
- },
-
- /**
- The intensity of this DirLight.
-
- Fires a {{#crossLink "DirLight/intensity:event"}}{{/crossLink}} event on change.
-
- @property intensity
- @default 1.0
- @type Number
- */
- intensity: {
- set: function (value) {
- value = value !== undefined ? value : 1.0;
- this._state.intensity = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.intensity;
+ math.orthoMat4c(-10, 10, -10, 10, 0, 1000.0, self._shadowProjMatrix);
+ self._shadowProjMatrixDirty = false;
}
+ return self._shadowProjMatrix;
},
- /**
- Flag which indicates if this DirLight casts a shadow.
-
- @property shadow
- @default false
- @type Boolean
- */
- shadow: {
- set: function (value) {
- value = !!value;
- if (this._state.shadow === value) {
- return;
- }
- this._state.shadow = value;
- this._shadowViewMatrixDirty = true;
- this._renderer.imageDirty();
- },
-
- get: function () {
- return this._state.shadow;
+ getShadowRenderBuf: function () {
+ if (!self._shadowRenderBuf) {
+ self._shadowRenderBuf = new RenderBuffer(self.scene.canvas.canvas, self.scene.canvas.gl);
}
+ return self._shadowRenderBuf;
}
- },
-
- _destroy: function () {
- if (this._shadowRenderBuf) {
- this._shadowRenderBuf.destroy();
- }
- this.scene._lightDestroyed(this);
+ });
+
+ this.dir = cfg.dir;
+ this.color = cfg.color;
+ this.intensity = cfg.intensity;
+ this.shadow = cfg.shadow;
+ this.scene._lightCreated(this);
+ }
+
+ /**
+ The direction in which the light is shining.
+
+ @property dir
+ @default [1.0, 1.0, 1.0]
+ @type Float32Array
+ */
+ set dir(value) {
+ this._state.dir.set(value || [1.0, 1.0, 1.0]);
+ this._shadowViewMatrixDirty = true;
+ this._renderer.imageDirty();
+ }
+
+ get dir() {
+ return this._state.dir;
+ }
+
+ /**
+ The color of this DirLight.
+
+ @property color
+ @default [0.7, 0.7, 0.8]
+ @type Float32Array
+ */
+ set color(value) {
+ this._state.color.set(value || [0.7, 0.7, 0.8]);
+ this._renderer.imageDirty();
+ }
+
+ get color() {
+ return this._state.color;
+ }
+
+ /**
+ The intensity of this DirLight.
+
+ Fires a {{#crossLink "DirLight/intensity:event"}}{{/crossLink}} event on change.
+
+ @property intensity
+ @default 1.0
+ @type Number
+ */
+ set intensity(value) {
+ value = value !== undefined ? value : 1.0;
+ this._state.intensity = value;
+ this._renderer.imageDirty();
+ }
+
+ get intensity() {
+ return this._state.intensity;
+ }
+
+ /**
+ Flag which indicates if this DirLight casts a shadow.
+
+ @property shadow
+ @default false
+ @type Boolean
+ */
+ set shadow(value) {
+ value = !!value;
+ if (this._state.shadow === value) {
+ return;
+ }
+ this._state.shadow = value;
+ this._shadowViewMatrixDirty = true;
+ this._renderer.imageDirty();
+ }
+
+ get shadow() {
+ return this._state.shadow;
+ }
+
+ destroy() {
+ super.destroy();
+ this._state.destroy();
+ if (this._shadowRenderBuf) {
+ this._shadowRenderBuf.destroy();
}
- });
+ this.scene._lightDestroyed(this);
+ }
+}
-})();
+export {DirLight};
diff --git a/src/lighting/lightMap.js b/src/lighting/lightMap.js
index 3b080b9ce..9826a8591 100644
--- a/src/lighting/lightMap.js
+++ b/src/lighting/lightMap.js
@@ -20,8 +20,7 @@
@module xeogl
@submodule lighting
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}} - creates this LightMap in the default
- {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
+ @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 for this LightMap, unique among all components in the parent scene, generated automatically when omitted.
@param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to this LightMap.
@@ -30,21 +29,34 @@
@param [cfg.encoding="linear"] {String} Encoding format. See the {{#crossLink "LightMap/encoding:property"}}{{/crossLink}} property for more info.
@extends Component
*/
-(function () {
- "use strict";
+import {core} from "./../core.js";
+import {CubeTexture} from './cubeTexture.js';
- xeogl.LightMap = xeogl.CubeTexture.extend({
- type: "xeogl.LightMap",
- _init: function (cfg) {
- this._super(cfg);
- this.scene._lightMapCreated(this);
- },
+class LightMap extends CubeTexture{
- _destroy: function () {
- this._super();
- this.scene._lightMapDestroyed(this);
- }
- });
+ /**
+ JavaScript class name for this Component.
-})();
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return "xeogl.LightMap";
+ }
+
+ init(cfg) {
+ super.init(cfg);
+ this.scene._lightMapCreated(this);
+ }
+
+ destroy() {
+ super.destroy();
+ this.scene._lightMapDestroyed(this);
+ }
+}
+
+export{LightMap};
diff --git a/src/lighting/pointLight.js b/src/lighting/pointLight.js
index 7a76c6610..e5862c577 100644
--- a/src/lighting/pointLight.js
+++ b/src/lighting/pointLight.js
@@ -70,8 +70,7 @@
@submodule lighting
@constructor
@extends Component
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}}, creates this PointLight within the
- default {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted
+ @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] {*} The PointLight configuration
@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 PointLight.
@@ -84,219 +83,223 @@
@param [cfg.space="view"] {String} The coordinate system this PointLight is defined in - "view" or "world".
@param [cfg.shadow=false] {Boolean} Flag which indicates if this PointLight casts a shadow.
*/
-(function () {
-
- "use strict";
-
- const math = xeogl.math;
-
- xeogl.PointLight = xeogl.Component.extend({
-
- type: "xeogl.PointLight",
-
- _init: function (cfg) {
-
- const self = this;
-
- this._shadowRenderBuf = null;
- this._shadowViewMatrix = null;
- this._shadowProjMatrix = null;
- this._shadowViewMatrixDirty = true;
- this._shadowProjMatrixDirty = true;
-
- this._state = new xeogl.renderer.State({
- type: "point",
- pos: xeogl.math.vec3([1.0, 1.0, 1.0]),
- color: xeogl.math.vec3([0.7, 0.7, 0.8]),
- intensity: 1.0, attenuation: [0.0, 0.0, 0.0],
- space: cfg.space || "view",
- shadow: false,
- shadowDirty: true,
-
- getShadowViewMatrix: (function () {
- const look = math.vec3([0, 0, 0]);
- const up = math.vec3([0, 1, 0]);
- return function () {
- if (self._shadowViewMatrixDirty) {
- if (!self._shadowViewMatrix) {
- self._shadowViewMatrix = math.identityMat4();
- }
- math.lookAtMat4v(self._state.pos, look, up, self._shadowViewMatrix);
- self._shadowViewMatrixDirty = false;
+import {Component} from '../component.js';
+import {State} from '../renderer/state.js';
+import {RenderBuffer} from '../renderer/renderBuffer.js';
+import {math} from '../math/math.js';
+
+class PointLight extends Component {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return "xeogl.PointLight";
+ }
+
+ init(cfg) {
+
+ super.init(cfg);
+
+ const self = this;
+
+ this._shadowRenderBuf = null;
+ this._shadowViewMatrix = null;
+ this._shadowProjMatrix = null;
+ this._shadowViewMatrixDirty = true;
+ this._shadowProjMatrixDirty = true;
+
+ this._state = new State({
+ type: "point",
+ pos: math.vec3([1.0, 1.0, 1.0]),
+ color: math.vec3([0.7, 0.7, 0.8]),
+ intensity: 1.0, attenuation: [0.0, 0.0, 0.0],
+ space: cfg.space || "view",
+ shadow: false,
+ shadowDirty: true,
+
+ getShadowViewMatrix: (function () {
+ const look = math.vec3([0, 0, 0]);
+ const up = math.vec3([0, 1, 0]);
+ return function () {
+ if (self._shadowViewMatrixDirty) {
+ if (!self._shadowViewMatrix) {
+ self._shadowViewMatrix = math.identityMat4();
}
- return self._shadowViewMatrix;
- };
- })(),
-
- getShadowProjMatrix: function () {
- if (self._shadowProjMatrixDirty) { // TODO: Set when canvas resizes
- if (!self._shadowProjMatrix) {
- self._shadowProjMatrix = math.identityMat4();
- }
- const canvas = self.scene.canvas.canvas;
- math.perspectiveMat4(70 * (Math.PI / 180.0), canvas.clientWidth / canvas.clientHeight, 0.1, 500.0, self._shadowProjMatrix);
- self._shadowProjMatrixDirty = false;
+ math.lookAtMat4v(self._state.pos, look, up, self._shadowViewMatrix);
+ self._shadowViewMatrixDirty = false;
}
- return self._shadowProjMatrix;
- },
-
- getShadowRenderBuf: function () {
- if (!self._shadowRenderBuf) {
- self._shadowRenderBuf = new xeogl.renderer.RenderBuffer(self.scene.canvas.canvas, self.scene.canvas.gl);
+ return self._shadowViewMatrix;
+ };
+ })(),
+
+ getShadowProjMatrix: function () {
+ if (self._shadowProjMatrixDirty) { // TODO: Set when canvas resizes
+ if (!self._shadowProjMatrix) {
+ self._shadowProjMatrix = math.identityMat4();
}
- return self._shadowRenderBuf;
- }
- });
-
- this.pos = cfg.pos;
- this.color = cfg.color;
- this.intensity = cfg.intensity;
- this.constantAttenuation = cfg.constantAttenuation;
- this.linearAttenuation = cfg.linearAttenuation;
- this.quadraticAttenuation = cfg.quadraticAttenuation;
- this.shadow = cfg.shadow;
-
- this.scene._lightCreated(this);
- },
-
- _props: {
-
- /**
- The position of this PointLight.
-
- This will be either World- or View-space, depending on the value of {{#crossLink "PointLight/space:property"}}{{/crossLink}}.
-
- @property pos
- @default [1.0, 1.0, 1.0]
- @type Array(Number)
- */
- pos: {
- set: function (value) {
- this._state.pos.set(value || [1.0, 1.0, 1.0]);
- this._shadowViewMatrixDirty = true;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.pos;
- }
- },
-
- /**
- The color of this PointLight.
-
- @property color
- @default [0.7, 0.7, 0.8]
- @type Float32Array
- */
- color: {
- set: function (value) {
- this._state.color.set(value || [0.7, 0.7, 0.8]);
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.color;
+ const canvas = self.scene.canvas.canvas;
+ math.perspectiveMat4(70 * (Math.PI / 180.0), canvas.clientWidth / canvas.clientHeight, 0.1, 500.0, self._shadowProjMatrix);
+ self._shadowProjMatrixDirty = false;
}
+ return self._shadowProjMatrix;
},
- /**
- The intensity of this PointLight.
-
- @property intensity
- @default 1.0
- @type Number
- */
- intensity: {
- set: function (value) {
- value = value !== undefined ? value : 1.0;
- this._state.intensity = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.intensity;
- }
- },
-
- /**
- The constant attenuation factor for this PointLight.
-
- @property constantAttenuation
- @default 0
- @type Number
- */
- constantAttenuation: {
- set: function (value) {
- this._state.attenuation[0] = value || 0.0;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.attenuation[0];
- }
- },
-
- /**
- The linear attenuation factor for this PointLight.
-
- @property linearAttenuation
- @default 0
- @type Number
- */
- linearAttenuation: {
- set: function (value) {
- this._state.attenuation[1] = value || 0.0;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.attenuation[1];
- }
- },
-
- /**
- The quadratic attenuation factor for this Pointlight.
-
- @property quadraticAttenuation
- @default 0
- @type Number
- */
- quadraticAttenuation: {
- set: function (value) {
- this._state.attenuation[2] = value || 0.0;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.attenuation[2];
- }
- },
-
- /**
- Flag which indicates if this PointLight casts a shadow.
-
- @property shadow
- @default false
- @type Boolean
- */
- shadow: {
- set: function (value) {
- value = !!value;
- if (this._state.shadow === value) {
- return;
- }
- this._state.shadow = value;
- this._shadowViewMatrixDirty = true;
- this._renderer.imageDirty();
- },
-
- get: function () {
- return this._state.shadow;
+ getShadowRenderBuf: function () {
+ if (!self._shadowRenderBuf) {
+ self._shadowRenderBuf = new RenderBuffer(self.scene.canvas.canvas, self.scene.canvas.gl);
}
+ return self._shadowRenderBuf;
}
- },
-
- _destroy: function () {
- if (this._shadowRenderBuf) {
- this._shadowRenderBuf.destroy();
- }
- this.scene._lightDestroyed(this);
+ });
+
+ this.pos = cfg.pos;
+ this.color = cfg.color;
+ this.intensity = cfg.intensity;
+ this.constantAttenuation = cfg.constantAttenuation;
+ this.linearAttenuation = cfg.linearAttenuation;
+ this.quadraticAttenuation = cfg.quadraticAttenuation;
+ this.shadow = cfg.shadow;
+
+ this.scene._lightCreated(this);
+ }
+
+
+ /**
+ The position of this PointLight.
+
+ This will be either World- or View-space, depending on the value of {{#crossLink "PointLight/space:property"}}{{/crossLink}}.
+
+ @property pos
+ @default [1.0, 1.0, 1.0]
+ @type Array(Number)
+ */
+ set pos(value) {
+ this._state.pos.set(value || [1.0, 1.0, 1.0]);
+ this._shadowViewMatrixDirty = true;
+ this._renderer.imageDirty();
+ }
+
+ get pos() {
+ return this._state.pos;
+ }
+
+ /**
+ The color of this PointLight.
+
+ @property color
+ @default [0.7, 0.7, 0.8]
+ @type Float32Array
+ */
+ set color(value) {
+ this._state.color.set(value || [0.7, 0.7, 0.8]);
+ this._renderer.imageDirty();
+ }
+
+ get color() {
+ return this._state.color;
+ }
+
+ /**
+ The intensity of this PointLight.
+
+ @property intensity
+ @default 1.0
+ @type Number
+ */
+ set intensity(value) {
+ value = value !== undefined ? value : 1.0;
+ this._state.intensity = value;
+ this._renderer.imageDirty();
+ }
+
+ get intensity() {
+ return this._state.intensity;
+ }
+
+ /**
+ The constant attenuation factor for this PointLight.
+
+ @property constantAttenuation
+ @default 0
+ @type Number
+ */
+ set constantAttenuation(value) {
+ this._state.attenuation[0] = value || 0.0;
+ this._renderer.imageDirty();
+ }
+
+ get constantAttenuation() {
+ return this._state.attenuation[0];
+ }
+
+ /**
+ The linear attenuation factor for this PointLight.
+
+ @property linearAttenuation
+ @default 0
+ @type Number
+ */
+ set linearAttenuation(value) {
+ this._state.attenuation[1] = value || 0.0;
+ this._renderer.imageDirty();
+ }
+
+ get linearAttenuation() {
+ return this._state.attenuation[1];
+ }
+
+ /**
+ The quadratic attenuation factor for this Pointlight.
+
+ @property quadraticAttenuation
+ @default 0
+ @type Number
+ */
+ set quadraticAttenuation(value) {
+ this._state.attenuation[2] = value || 0.0;
+ this._renderer.imageDirty();
+ }
+
+ get quadraticAttenuation() {
+ return this._state.attenuation[2];
+ }
+
+ /**
+ Flag which indicates if this PointLight casts a shadow.
+
+ @property shadow
+ @default false
+ @type Boolean
+ */
+ set shadow(value) {
+ value = !!value;
+ if (this._state.shadow === value) {
+ return;
}
- });
+ this._state.shadow = value;
+ this._shadowViewMatrixDirty = true;
+ this._renderer.imageDirty();
+ }
+
+ get shadow() {
+ return this._state.shadow;
+ }
+
+ destroy() {
+ super.destroy();
+ this._state.destroy();
+ if (this._shadowRenderBuf) {
+ this._shadowRenderBuf.destroy();
+ }
+ this.scene._lightDestroyed(this);
+ }
+}
-})();
+export {PointLight};
diff --git a/src/lighting/reflectionMap.js b/src/lighting/reflectionMap.js
index aafb16554..59b9b9003 100644
--- a/src/lighting/reflectionMap.js
+++ b/src/lighting/reflectionMap.js
@@ -20,8 +20,7 @@
@module xeogl
@submodule lighting
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}} - creates this ReflectionMap in the default
- {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
+ @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 for this ReflectionMap, unique among all components in the parent scene, generated automatically when omitted.
@param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to this ReflectionMap.
@@ -30,21 +29,34 @@
@param [cfg.encoding="linear"] {String} Encoding format. See the {{#crossLink "ReflectionMap/encoding:property"}}{{/crossLink}} property for more info.
@extends Component
*/
-(function () {
-
- "use strict";
-
- xeogl.ReflectionMap = xeogl.CubeTexture.extend({
- type: "xeogl.ReflectionMap",
- _init: function (cfg) {
- this._super(cfg);
- this.scene._lightsState.addReflectionMap(this._state);
- this.scene._reflectionMapCreated(this);
- },
-
- _destroy: function () {
- this._super();
- this.scene._reflectionMapDestroyed(this);
- }
- });
-})();
+import {core} from "./../core.js";
+import {CubeTexture} from './cubeTexture.js';
+
+class ReflectionMap extends CubeTexture {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return "xeogl.ReflectionMap";
+ }
+
+ init(cfg) {
+ super.init(cfg);
+ this.scene._lightsState.addReflectionMap(this._state);
+ this.scene._reflectionMapCreated(this);
+ }
+
+ destroy() {
+ super.destroy();
+ this.scene._reflectionMapDestroyed(this);
+ }
+}
+
+export{ReflectionMap};
diff --git a/src/lighting/shadow.js b/src/lighting/shadow.js
index ee7722f61..8a7b8a4ce 100644
--- a/src/lighting/shadow.js
+++ b/src/lighting/shadow.js
@@ -53,99 +53,103 @@
@submodule lighting
@constructor
@extends Component
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}}, creates this Shadow within the
- default {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted
+ @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] {*} The Shadow configuration
@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 Shadow.
@param [cfg.resolution=[1000,1000]] {Uint16Array} Resolution of the texture map for this Shadow.
@param [cfg.intensity=1.0] {Number} Intensity of this Shadow.
*/
-(function () {
+import {Component} from '../component.js';
+import {math} from '../math/math.js';
- "use strict";
+class Shadow extends Component {
- xeogl.Shadow = xeogl.Component.extend({
+ /**
+ JavaScript class name for this Component.
- type: "xeogl.Shadow",
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
- _init: function (cfg) {
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return "xeogl.Shadow";
+ }
- this._state = {
- resolution: xeogl.math.vec3([1000, 1000]),
- intensity: 1.0
- };
+ init(cfg) {
+ super.init(cfg);
+ this._state = {
+ resolution: math.vec3([1000, 1000]),
+ intensity: 1.0
+ };
+ this.resolution = cfg.resolution;
+ this.intensity = cfg.intensity;
+ }
- this.resolution = cfg.resolution;
- this.intensity = cfg.intensity;
- },
+ /**
+ The resolution of the texture map for this Shadow.
- _props: {
+ This will be either World- or View-space, depending on the value of {{#crossLink "Shadow/space:property"}}{{/crossLink}}.
- /**
- The resolution of the texture map for this Shadow.
+ Fires a {{#crossLink "Shadow/resolution:event"}}{{/crossLink}} event on change.
- This will be either World- or View-space, depending on the value of {{#crossLink "Shadow/space:property"}}{{/crossLink}}.
+ @property resolution
+ @default [1000, 1000]
+ @type Uint16Array
+ */
+ set resolution(value) {
- Fires a {{#crossLink "Shadow/resolution:event"}}{{/crossLink}} event on change.
+ this._state.resolution.set(value || [1000.0, 1000.0]);
- @property resolution
- @default [1000, 1000]
- @type Uint16Array
- */
- resolution: {
+ this._renderer.imageDirty();
- set: function (value) {
+ /**
+ Fired whenever this Shadow's {{#crossLink "Shadow/resolution:property"}}{{/crossLink}} property changes.
+ @event resolution
+ @param value The property's new value
+ */
+ this.fire("resolution", this._state.resolution);
+ }
- this._state.resolution.set(value || [1000.0, 1000.0]);
+ get resolution() {
+ return this._state.resolution;
+ }
- this._renderer.imageDirty();
+ /**
+ The intensity of this Shadow.
- /**
- Fired whenever this Shadow's {{#crossLink "Shadow/resolution:property"}}{{/crossLink}} property changes.
- @event resolution
- @param value The property's new value
- */
- this.fire("resolution", this._state.resolution);
- },
+ Fires a {{#crossLink "Shadow/intensity:event"}}{{/crossLink}} event on change.
- get: function () {
- return this._state.resolution;
- }
- },
-
- /**
- The intensity of this Shadow.
+ @property intensity
+ @default 1.0
+ @type Number
+ */
+ set intensity(value) {
- Fires a {{#crossLink "Shadow/intensity:event"}}{{/crossLink}} event on change.
+ value = value !== undefined ? value : 1.0;
- @property intensity
- @default 1.0
- @type Number
- */
- intensity: {
+ this._state.intensity = value;
- set: function (value) {
+ this._renderer.imageDirty();
- value = value !== undefined ? value : 1.0;
+ /**
+ * Fired whenever this Shadow's {{#crossLink "Shadow/intensity:property"}}{{/crossLink}} property changes.
+ * @event intensity
+ * @param value The property's new value
+ */
+ this.fire("intensity", this._state.intensity);
+ }
- this._state.intensity = value;
+ get intensity() {
+ return this._state.intensity;
+ }
- this._renderer.imageDirty();
+ destroy() {
+ super.destroy();
+ //this._state.destroy();
+ }
+}
- /**
- * Fired whenever this Shadow's {{#crossLink "Shadow/intensity:property"}}{{/crossLink}} property changes.
- * @event intensity
- * @param value The property's new value
- */
- this.fire("intensity", this._state.intensity);
- },
-
- get: function () {
- return this._state.intensity;
- }
- }
- }
- });
-
-})();
+export{Shadow};
diff --git a/src/lighting/spotLight.js b/src/lighting/spotLight.js
index aa27f70f6..98155df39 100644
--- a/src/lighting/spotLight.js
+++ b/src/lighting/spotLight.js
@@ -67,8 +67,7 @@
@submodule lighting
@constructor
@extends Component
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}}, creates this SpotLight within the
- default {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted
+ @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] {*} The SpotLight configuration
@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 SpotLight.
@@ -82,242 +81,247 @@
@param [cfg.space="view"] {String} The coordinate system this SpotLight is defined in - "view" or "world".
@param [cfg.shadow=false] {Boolean} Flag which indicates if this SpotLight casts a shadow.
*/
-(function () {
-
- "use strict";
-
- const math = xeogl.math;
-
- xeogl.SpotLight = xeogl.Component.extend({
-
- type: "xeogl.SpotLight",
-
- _init: function (cfg) {
-
- const self = this;
-
- this._shadowRenderBuf = null;
- this._shadowViewMatrix = null;
- this._shadowProjMatrix = null;
- this._shadowViewMatrixDirty = true;
- this._shadowProjMatrixDirty = true;
-
- this._state = new xeogl.renderer.State({
- type: "spot",
- pos: math.vec3([1.0, 1.0, 1.0]),
- dir: math.vec3([0.0, -1.0, 0.0]),
- color: math.vec3([0.7, 0.7, 0.8]),
- intensity: 1.0,
- attenuation: [0.0, 0.0, 0.0],
- space: cfg.space || "view",
- shadow: false,
- shadowDirty: true,
-
- getShadowViewMatrix: (function () {
- const look = math.vec3();
- const up = math.vec3([0, 1, 0]);
- return function () {
- if (self._shadowViewMatrixDirty) {
- if (!self._shadowViewMatrix) {
- self._shadowViewMatrix = math.identityMat4();
- }
- math.addVec3(self._state.pos, self._state.dir, look);
- math.lookAtMat4v(self._state.pos, look, up, self._shadowViewMatrix);
- self._shadowViewMatrixDirty = false;
- }
- return self._shadowViewMatrix;
- };
- })(),
-
- getShadowProjMatrix: function () {
- if (self._shadowProjMatrixDirty) { // TODO: Set when canvas resizes
- if (!self._shadowProjMatrix) {
- self._shadowProjMatrix = math.identityMat4();
+
+import {Component} from '../component.js';
+import {State} from '../renderer/state.js';
+import {RenderBuffer} from '../renderer/renderBuffer.js';
+import {math} from '../math/math.js';
+
+class SpotLight extends Component {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return "xeogl.SpotLight";
+ }
+
+ init(cfg) {
+
+ super.init(cfg);
+
+ const self = this;
+
+ this._shadowRenderBuf = null;
+ this._shadowViewMatrix = null;
+ this._shadowProjMatrix = null;
+ this._shadowViewMatrixDirty = true;
+ this._shadowProjMatrixDirty = true;
+
+ this._state = new State({
+ type: "spot",
+ pos: math.vec3([1.0, 1.0, 1.0]),
+ dir: math.vec3([0.0, -1.0, 0.0]),
+ color: math.vec3([0.7, 0.7, 0.8]),
+ intensity: 1.0,
+ attenuation: [0.0, 0.0, 0.0],
+ space: cfg.space || "view",
+ shadow: false,
+ shadowDirty: true,
+
+ getShadowViewMatrix: (function () {
+ const look = math.vec3();
+ const up = math.vec3([0, 1, 0]);
+ return function () {
+ if (self._shadowViewMatrixDirty) {
+ if (!self._shadowViewMatrix) {
+ self._shadowViewMatrix = math.identityMat4();
}
- const canvas = self.scene.canvas.canvas;
- math.perspectiveMat4(60 * (Math.PI / 180.0), canvas.clientWidth / canvas.clientHeight, 0.1, 400.0, self._shadowProjMatrix);
- self._shadowProjMatrixDirty = false;
+ math.addVec3(self._state.pos, self._state.dir, look);
+ math.lookAtMat4v(self._state.pos, look, up, self._shadowViewMatrix);
+ self._shadowViewMatrixDirty = false;
}
- return self._shadowProjMatrix;
- },
-
- getShadowRenderBuf: function () {
- if (!self._shadowRenderBuf) {
- self._shadowRenderBuf = new xeogl.renderer.RenderBuffer(self.scene.canvas.canvas, self.scene.canvas.gl);
+ return self._shadowViewMatrix;
+ };
+ })(),
+
+ getShadowProjMatrix: function () {
+ if (self._shadowProjMatrixDirty) { // TODO: Set when canvas resizes
+ if (!self._shadowProjMatrix) {
+ self._shadowProjMatrix = math.identityMat4();
}
- return self._shadowRenderBuf;
- }
- });
-
- this.pos = cfg.pos;
- this.color = cfg.color;
- this.intensity = cfg.intensity;
- this.constantAttenuation = cfg.constantAttenuation;
- this.linearAttenuation = cfg.linearAttenuation;
- this.quadraticAttenuation = cfg.quadraticAttenuation;
- this.shadow = cfg.shadow;
- this.scene._lightCreated(this);
- },
-
- _props: {
-
- /**
- The position of this SpotLight.
-
- This will be either World- or View-space, depending on the value of {{#crossLink "SpotLight/space:property"}}{{/crossLink}}.
-
- @property pos
- @default [1.0, 1.0, 1.0]
- @type Array(Number)
- */
- pos: {
- set: function (value) {
- this._state.pos.set(value || [1.0, 1.0, 1.0]);
- this._shadowViewMatrixDirty = true;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.pos;
- }
- },
-
- /**
- The direction in which the light is shining.
-
- @property dir
- @default [1.0, 1.0, 1.0]
- @type Float32Array
- */
- dir: {
- set: function (value) {
- this._state.dir.set(value || [1.0, 1.0, 1.0]);
- this._shadowViewMatrixDirty = true;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.dir;
+ const canvas = self.scene.canvas.canvas;
+ math.perspectiveMat4(60 * (Math.PI / 180.0), canvas.clientWidth / canvas.clientHeight, 0.1, 400.0, self._shadowProjMatrix);
+ self._shadowProjMatrixDirty = false;
}
+ return self._shadowProjMatrix;
},
- /**
- The color of this SpotLight.
-
- @property color
- @default [0.7, 0.7, 0.8]
- @type Float32Array
- */
- color: {
- set: function (value) {
- this._state.color.set(value || [0.7, 0.7, 0.8]);
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.color;
+ getShadowRenderBuf: function () {
+ if (!self._shadowRenderBuf) {
+ self._shadowRenderBuf = new RenderBuffer(self.scene.canvas.canvas, self.scene.canvas.gl);
}
- },
-
- /**
- The intensity of this SpotLight.
-
- Fires a {{#crossLink "SpotLight/intensity:event"}}{{/crossLink}} event on change.
-
- @property intensity
- @default 1.0
- @type Number
- */
- intensity: {
- set: function (value) {
- value = value !== undefined ? value : 1.0;
- this._state.intensity = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.intensity;
- }
- },
-
- /**
- The constant attenuation factor for this SpotLight.
-
- @property constantAttenuation
- @default 0
- @type Number
- */
- constantAttenuation: {
- set: function (value) {
- this._state.attenuation[0] = value || 0.0;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.attenuation[0];
- }
- },
-
- /**
- The linear attenuation factor for this SpotLight.
-
- @property linearAttenuation
- @default 0
- @type Number
- */
- linearAttenuation: {
- set: function (value) {
- this._state.attenuation[1] = value || 0.0;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.attenuation[1];
- }
- },
-
- /**
- The quadratic attenuation factor for this SpotLight.
-
- @property quadraticAttenuation
- @default 0
- @type Number
- */
- quadraticAttenuation: {
- set: function (value) {
- this._state.attenuation[2] = value || 0.0;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.attenuation[2];
- }
- },
-
- /**
- Flag which indicates if this SpotLight casts a shadow.
-
- @property shadow
- @default false
- @type Boolean
- */
- shadow: {
- set: function (value) {
- value = !!value;
- if (this._state.shadow === value) {
- return;
- }
- this._state.shadow = value;
- this._shadowViewMatrixDirty = true;
- this._renderer.imageDirty();
- this.fire("dirty", true);
- },
- get: function () {
- return this._state.shadow;
- }
- }
- },
-
- _destroy: function () {
-// this.scene.canvas.off(this._webglContextRestored);
- if (this._shadowRenderBuf) {
- this._shadowRenderBuf.destroy();
+ return self._shadowRenderBuf;
}
- this.scene._lightDestroyed(this);
+ });
+
+ this.pos = cfg.pos;
+ this.color = cfg.color;
+ this.intensity = cfg.intensity;
+ this.constantAttenuation = cfg.constantAttenuation;
+ this.linearAttenuation = cfg.linearAttenuation;
+ this.quadraticAttenuation = cfg.quadraticAttenuation;
+ this.shadow = cfg.shadow;
+ this.scene._lightCreated(this);
+ }
+
+
+ /**
+ The position of this SpotLight.
+
+ This will be either World- or View-space, depending on the value of {{#crossLink "SpotLight/space:property"}}{{/crossLink}}.
+
+ @property pos
+ @default [1.0, 1.0, 1.0]
+ @type Array(Number)
+ */
+ set pos(value) {
+ this._state.pos.set(value || [1.0, 1.0, 1.0]);
+ this._shadowViewMatrixDirty = true;
+ this._renderer.imageDirty();
+ }
+
+ get pos() {
+ return this._state.pos;
+ }
+
+ /**
+ The direction in which the light is shining.
+
+ @property dir
+ @default [1.0, 1.0, 1.0]
+ @type Float32Array
+ */
+ set dir(value) {
+ this._state.dir.set(value || [1.0, 1.0, 1.0]);
+ this._shadowViewMatrixDirty = true;
+ this._renderer.imageDirty();
+ }
+
+ get dir() {
+ return this._state.dir;
+ }
+
+ /**
+ The color of this SpotLight.
+
+ @property color
+ @default [0.7, 0.7, 0.8]
+ @type Float32Array
+ */
+ set color(value) {
+ this._state.color.set(value || [0.7, 0.7, 0.8]);
+ this._renderer.imageDirty();
+ }
+
+ get color() {
+ return this._state.color;
+ }
+
+ /**
+ The intensity of this SpotLight.
+
+ Fires a {{#crossLink "SpotLight/intensity:event"}}{{/crossLink}} event on change.
+
+ @property intensity
+ @default 1.0
+ @type Number
+ */
+ set intensity(value) {
+ value = value !== undefined ? value : 1.0;
+ this._state.intensity = value;
+ this._renderer.imageDirty();
+ }
+
+ get intensity() {
+ return this._state.intensity;
+ }
+
+ /**
+ The constant attenuation factor for this SpotLight.
+
+ @property constantAttenuation
+ @default 0
+ @type Number
+ */
+ set constantAttenuation(value) {
+ this._state.attenuation[0] = value || 0.0;
+ this._renderer.imageDirty();
+ }
+
+ get constantAttenuation() {
+ return this._state.attenuation[0];
+ }
+
+ /**
+ The linear attenuation factor for this SpotLight.
+
+ @property linearAttenuation
+ @default 0
+ @type Number
+ */
+ set linearAttenuation(value) {
+ this._state.attenuation[1] = value || 0.0;
+ this._renderer.imageDirty();
+ }
+
+ get linearAttenuation() {
+ return this._state.attenuation[1];
+ }
+
+ /**
+ The quadratic attenuation factor for this SpotLight.
+
+ @property quadraticAttenuation
+ @default 0
+ @type Number
+ */
+ set quadraticAttenuation(value) {
+ this._state.attenuation[2] = value || 0.0;
+ this._renderer.imageDirty();
+ }
+
+ get quadraticAttenuation() {
+ return this._state.attenuation[2];
+ }
+
+ /**
+ Flag which indicates if this SpotLight casts a shadow.
+
+ @property shadow
+ @default false
+ @type Boolean
+ */
+ set shadow(value) {
+ value = !!value;
+ if (this._state.shadow === value) {
+ return;
+ }
+ this._state.shadow = value;
+ this._shadowViewMatrixDirty = true;
+ this._renderer.imageDirty();
+ this.fire("dirty", true);
+ }
+
+ get shadow() {
+ return this._state.shadow;
+ }
+
+ destroy() {
+ super.destroy();
+ this._state.destroy();
+ if (this._shadowRenderBuf) {
+ this._shadowRenderBuf.destroy();
}
- });
+ this.scene._lightDestroyed(this);
+
+ }
+}
-})();
+export{SpotLight};
diff --git a/src/materials/edgeMaterial.js b/src/materials/edgeMaterial.js
index 7e0c323e1..5137a2326 100644
--- a/src/materials/edgeMaterial.js
+++ b/src/materials/edgeMaterial.js
@@ -173,8 +173,7 @@
@submodule materials
@constructor
@extends Material
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}}, creates this EdgeMaterial within the
- default {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted
+ @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] {*} The EdgeMaterial configuration
@param [cfg.id] {String} Optional ID, unique among all components in the parent {{#crossLink "Scene"}}Scene{{/crossLink}}, generated automatically when omitted.
@param [cfg.meta=null] {String:Object} Metadata to attach to this EdgeMaterial.
@@ -185,190 +184,201 @@
@param [cfg.preset] {String} Selects a preset EdgeMaterial configuration - see {{#crossLink "EdgeMaterial/preset:method"}}EdgeMaterial#preset(){{/crossLink}}.
*/
-(function () {
- "use strict";
+import {Material} from './material.js';
+import {State} from '../renderer/state.js';
- xeogl.EdgeMaterial = xeogl.Material.extend({
+const PRESETS = {
+ "default": {
+ edgeColor: [0.0, 0.0, 0.0],
+ edgeAlpha: 1.0,
+ edgeWidth: 1
+ },
+ "defaultWhiteBG": {
+ edgeColor: [0.2, 0.2, 0.2],
+ edgeAlpha: 1.0,
+ edgeWidth: 1
+ },
+ "defaultLightBG": {
+ edgeColor: [0.2, 0.2, 0.2],
+ edgeAlpha: 1.0,
+ edgeWidth: 1
+ },
+ "defaultDarkBG": {
+ edgeColor: [0.5, 0.5, 0.5],
+ edgeAlpha: 1.0,
+ edgeWidth: 1
+ }
+};
- type: "xeogl.EdgeMaterial",
+class EdgeMaterial extends Material {
- _init: function (cfg) {
+ /**
+ Available EdgeMaterial presets.
- this._super(cfg);
+ @property presets
+ @type {Object}
+ @static
+ */
+ static get presets() {
+ return PRESETS;
+ };
- this._state = new xeogl.renderer.State({
- type: "EdgeMaterial",
- edgeColor: null,
- edgeAlpha: null,
- edgeWidth: null
- });
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return "xeogl.EdgeMaterial";
+ }
- this._preset = "default";
+ init(cfg) {
- if (cfg.preset) {
- this.preset = cfg.preset;
- if (cfg.edgeColor) this.edgeColor = cfg.edgeColor;
- if (cfg.edgeAlpha !== undefined) this.edgeAlpha = cfg.edgeAlpha;
- if (cfg.edgeWidth !== undefined) this.edgeWidth = cfg.edgeWidth;
- } else {
+ super.init(cfg);
+
+ this._state = new State({
+ type: "EdgeMaterial",
+ edgeColor: null,
+ edgeAlpha: null,
+ edgeWidth: null
+ });
+
+ this._preset = "default";
+
+ if (cfg.preset) { // Apply preset then override with configs where provided
+ this.preset = cfg.preset;
+ if (cfg.edgeColor) {
this.edgeColor = cfg.edgeColor;
+ }
+ if (cfg.edgeAlpha !== undefined) {
this.edgeAlpha = cfg.edgeAlpha;
+ }
+ if (cfg.edgeWidth !== undefined) {
this.edgeWidth = cfg.edgeWidth;
}
- },
+ } else {
+ this.edgeColor = cfg.edgeColor;
+ this.edgeAlpha = cfg.edgeAlpha;
+ this.edgeWidth = cfg.edgeWidth;
+ }
+ }
- _props: {
-
- /**
- RGB color of ghost edges.
-
- @property edgeColor
- @default [0.2, 0.2, 0.2]
- @type Float32Array
- */
- edgeColor: {
- set: function (value) {
- let edgeColor = this._state.edgeColor;
- if (!edgeColor) {
- edgeColor = this._state.edgeColor = new Float32Array(3);
- } else if (value && edgeColor[0] === value[0] && edgeColor[1] === value[1] && edgeColor[2] === value[2]) {
- return;
- }
- if (value) {
- edgeColor[0] = value[0];
- edgeColor[1] = value[1];
- edgeColor[2] = value[2];
- } else {
- edgeColor[0] = 0.2;
- edgeColor[1] = 0.2;
- edgeColor[2] = 0.2;
- }
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.edgeColor;
- }
- },
-
- /**
- Transparency of ghost edges.
-
- A value of 0.0 indicates fully transparent, 1.0 is fully opaque.
-
- @property edgeAlpha
- @default 1.0
- @type Number
- */
- edgeAlpha: {
- set: function (value) {
- value = (value !== undefined && value !== null) ? value : 1.0;
- if (this._state.edgeAlpha === value) {
- return;
- }
- this._state.edgeAlpha = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.edgeAlpha;
- }
- },
-
- /**
- Width of ghost edges, in pixels.
-
- @property edgeWidth
- @default 1.0
- @type Number
- */
- edgeWidth: {
- set: function (value) {
- this._state.edgeWidth = value || 1.0;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.edgeWidth;
- }
- },
-
- /**
- Selects a preset EdgeMaterial configuration.
-
- Available presets are:
-
- * "default" - grey wireframe with translucent fill, for light backgrounds.
- * "defaultLightBG" - grey wireframe with grey translucent fill, for light backgrounds.
- * "defaultDarkBG" - grey wireframe with grey translucent fill, for dark backgrounds.
- * "vectorscope" - green wireframe with glowing vertices and black translucent fill.
- * "battlezone" - green wireframe with black opaque fill, giving a solid hidden-lines-removed effect.
- * "sepia" - light red-grey wireframe with light sepia translucent fill - easy on the eyes.
- * "gamegrid" - light blue wireframe with dark blue translucent fill - reminiscent of Tron.
- * "yellowHighlight" - light yellow translucent fill - highlights while allowing underlying detail to show through.
-
- @property preset
- @default "default"
- @type String
- */
- preset: {
- set: function (value) {
- value = value || "default";
- if (this._preset === value) {
- return;
- }
- const preset = xeogl.EdgeMaterial.presets[value];
- if (!preset) {
- this.error("unsupported preset: '" + value + "' - supported values are " + Object.keys(xeogl.EdgeMaterial.presets).join(", "));
- return;
- }
- this.edgeColor = preset.edgeColor;
- this.edgeAlpha = preset.edgeAlpha;
- this.edgeWidth = preset.edgeWidth;
- this._preset = value;
- },
- get: function () {
- return this._preset;
- }
- }
- },
- _destroy: function () {
- this._super();
- this._state.destroy();
+ /**
+ RGB color of ghost edges.
+
+ @property edgeColor
+ @default [0.2, 0.2, 0.2]
+ @type Float32Array
+ */
+ set edgeColor(value) {
+ let edgeColor = this._state.edgeColor;
+ if (!edgeColor) {
+ edgeColor = this._state.edgeColor = new Float32Array(3);
+ } else if (value && edgeColor[0] === value[0] && edgeColor[1] === value[1] && edgeColor[2] === value[2]) {
+ return;
}
- });
+ if (value) {
+ edgeColor[0] = value[0];
+ edgeColor[1] = value[1];
+ edgeColor[2] = value[2];
+ } else {
+ edgeColor[0] = 0.2;
+ edgeColor[1] = 0.2;
+ edgeColor[2] = 0.2;
+ }
+ this._renderer.imageDirty();
+ }
+
+ get edgeColor() {
+ return this._state.edgeColor;
+ }
/**
- Available EdgeMaterial presets.
+ Transparency of ghost edges.
- @property presets
- @type {Object}
- @static
+ A value of 0.0 indicates fully transparent, 1.0 is fully opaque.
+
+ @property edgeAlpha
+ @default 1.0
+ @type Number
*/
- xeogl.EdgeMaterial.presets = {
+ set edgeAlpha(value) {
+ value = (value !== undefined && value !== null) ? value : 1.0;
+ if (this._state.edgeAlpha === value) {
+ return;
+ }
+ this._state.edgeAlpha = value;
+ this._renderer.imageDirty();
+ }
- "default": {
- edgeColor: [0.0, 0.0, 0.0],
- edgeAlpha: 1.0,
- edgeWidth: 1
- },
+ get edgeAlpha() {
+ return this._state.edgeAlpha;
+ }
- "defaultWhiteBG": {
- edgeColor: [0.2, 0.2, 0.2],
- edgeAlpha: 1.0,
- edgeWidth: 1
- },
+ /**
+ Width of ghost edges, in pixels.
- "defaultLightBG": {
- edgeColor: [0.2, 0.2, 0.2],
- edgeAlpha: 1.0,
- edgeWidth: 1
- },
+ @property edgeWidth
+ @default 1.0
+ @type Number
+ */
+ set edgeWidth(value) {
+ this._state.edgeWidth = value || 1.0;
+ this._renderer.imageDirty();
+ }
- "defaultDarkBG": {
- edgeColor: [0.5, 0.5, 0.5],
- edgeAlpha: 1.0,
- edgeWidth: 1
- }
- };
+ get edgeWidth() {
+ return this._state.edgeWidth;
+ }
-})();
\ No newline at end of file
+ /**
+ Selects a preset EdgeMaterial configuration.
+
+ Available presets are:
+
+ * "default" - grey wireframe with translucent fill, for light backgrounds.
+ * "defaultLightBG" - grey wireframe with grey translucent fill, for light backgrounds.
+ * "defaultDarkBG" - grey wireframe with grey translucent fill, for dark backgrounds.
+ * "vectorscope" - green wireframe with glowing vertices and black translucent fill.
+ * "battlezone" - green wireframe with black opaque fill, giving a solid hidden-lines-removed effect.
+ * "sepia" - light red-grey wireframe with light sepia translucent fill - easy on the eyes.
+ * "gamegrid" - light blue wireframe with dark blue translucent fill - reminiscent of Tron.
+ * "yellowHighlight" - light yellow translucent fill - highlights while allowing underlying detail to show through.
+
+ @property preset
+ @default "default"
+ @type String
+ */
+ set preset(value) {
+ value = value || "default";
+ if (this._preset === value) {
+ return;
+ }
+ const preset = PRESETS[value];
+ if (!preset) {
+ this.error("unsupported preset: '" + value + "' - supported values are " + Object.keys(PRESETS).join(", "));
+ return;
+ }
+ this.edgeColor = preset.edgeColor;
+ this.edgeAlpha = preset.edgeAlpha;
+ this.edgeWidth = preset.edgeWidth;
+ this._preset = value;
+ }
+
+ get preset() {
+ return this._preset;
+ }
+
+ destroy() {
+ super.destroy();
+ this._state.destroy();
+ }
+}
+
+export {EdgeMaterial};
\ No newline at end of file
diff --git a/src/materials/emphasisMaterial.js b/src/materials/emphasisMaterial.js
index e56219df5..8acf040d9 100644
--- a/src/materials/emphasisMaterial.js
+++ b/src/materials/emphasisMaterial.js
@@ -209,8 +209,7 @@
@submodule materials
@constructor
@extends Material
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}}, creates this EmphasisMaterial within the
- default {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted
+ @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] {*} The EmphasisMaterial configuration
@param [cfg.id] {String} Optional ID, unique among all components in the parent {{#crossLink "Scene"}}Scene{{/crossLink}}, generated automatically when omitted.
@param [cfg.meta=null] {String:Object} Metadata to attach to this EmphasisMaterial.
@@ -228,584 +227,592 @@
@param [cfg.backfaces=false] {Boolean} Whether to render {{#crossLink "Geometry"}}Geometry{{/crossLink}} backfaces.
@param [cfg.preset] {String} Selects a preset EmphasisMaterial configuration - see {{#crossLink "EmphasisMaterial/preset:method"}}EmphasisMaterial#preset(){{/crossLink}}.
*/
-(function () {
-
- "use strict";
-
- xeogl.EmphasisMaterial = xeogl.Material.extend({
-
- type: "xeogl.EmphasisMaterial",
-
- _init: function (cfg) {
-
- this._super(cfg);
-
- this._state = new xeogl.renderer.State({
- type: "EmphasisMaterial",
- edges: null,
- edgeColor: null,
- edgeAlpha: null,
- edgeWidth: null,
- vertices: null,
- vertexColor: null,
- vertexAlpha: null,
- vertexSize: null,
- fill: null,
- fillColor: null,
- fillAlpha: null,
- backfaces: true
- });
-
- this._preset = "default";
-
- if (cfg.preset) {
-
- this.preset = cfg.preset;
-
- if (cfg.edges !== undefined) this.edges = cfg.edges;
- if (cfg.edgeColor) this.edgeColor = cfg.edgeColor;
- if (cfg.edgeAlpha !== undefined) this.edgeAlpha = cfg.edgeAlpha;
- if (cfg.edgeWidth !== undefined) this.edgeWidth = cfg.edgeWidth;
- if (cfg.vertices !== undefined) this.vertices = cfg.vertices;
- if (cfg.vertexColor) this.vertexColor = cfg.vertexColor;
- if (cfg.vertexAlpha !== undefined) this.vertexAlpha = cfg.vertexAlpha;
- if (cfg.vertexSize) this.vertexSize = cfg.vertexSize;
- if (cfg.fill !== undefined) this.fill = cfg.fill;
- if (cfg.fillColor) this.fillColor = cfg.fillColor;
- if (cfg.fillAlpha !== undefined) this.fillAlpha = cfg.fillAlpha;
- if (cfg.backfaces !== undefined) this.backfaces = cfg.backfaces;
- } else {
+
+import {Material} from './material.js';
+import {State} from '../renderer/state.js';
+
+const PRESETS = {
+ "default": {
+ edges: true,
+ edgeColor: [0.2, 0.2, 0.2],
+ edgeAlpha: 0.5,
+ edgeWidth: 1,
+ vertices: false,
+ vertexColor: [0.4, 0.4, 0.4],
+ vertexAlpha: 0.7,
+ vertexSize: 4.0,
+ fill: true,
+ fillColor: [0.4, 0.4, 0.4],
+ fillAlpha: 0.2
+ },
+ "defaultWhiteBG": {
+ edgeColor: [0.2, 0.2, 0.2],
+ edgeAlpha: 1.0,
+ edgeWidth: 1,
+ vertices: false,
+ vertexColor: [0.4, 0.4, 0.4],
+ vertexAlpha: 0.7,
+ vertexSize: 4.0,
+ fill: true,
+ fillColor: [1, 1, 1],
+ fillAlpha: 0.6
+ },
+ "defaultLightBG": {
+ edges: true,
+ edgeColor: [0.2, 0.2, 0.2],
+ edgeAlpha: 0.5,
+ edgeWidth: 1,
+ vertices: false,
+ vertexColor: [0.4, 0.4, 0.4],
+ vertexAlpha: 0.7,
+ vertexSize: 4.0,
+ fill: true,
+ fillColor: [0.4, 0.4, 0.4],
+ fillAlpha: 0.2
+ },
+ "defaultDarkBG": {
+ edges: true,
+ edgeColor: [0.5, 0.5, 0.5],
+ edgeAlpha: 0.5,
+ edgeWidth: 1,
+ vertices: false,
+ vertexColor: [0.4, 0.4, 0.4],
+ vertexAlpha: 0.7,
+ vertexSize: 4.0,
+ fill: true,
+ fillColor: [0.4, 0.4, 0.4],
+ fillAlpha: 0.2
+ },
+ "phosphorous": {
+ edges: true,
+ edgeColor: [0.9, 0.9, 0.9],
+ edgeAlpha: 0.5,
+ edgeWidth: 2,
+ vertices: false,
+ vertexColor: [0.4, 0.4, 0.4],
+ vertexAlpha: 0.7,
+ vertexSize: 1.0,
+ fill: true,
+ fillColor: [0.0, 0.0, 0.0],
+ fillAlpha: 0.4
+ },
+ "sunset": {
+ edges: true,
+ edgeColor: [0.9, 0.9, 0.9],
+ edgeAlpha: 0.5,
+ edgeWidth: 1,
+ vertices: false,
+ vertexColor: [0.4, 0.4, 0.4],
+ vertexAlpha: 0.7,
+ vertexSize: 1.0,
+ fill: true,
+ fillColor: [0.9, 0.9, 0.6],
+ fillAlpha: 0.2
+ },
+ "vectorscope": {
+ edges: true,
+ edgeColor: [0.2, 1.0, 0.2],
+ edgeAlpha: 1,
+ edgeWidth: 2,
+ vertices: true,
+ vertexColor: [0.7, 1.0, 0.7],
+ vertexAlpha: 0.9,
+ vertexSize: 8.0,
+ fill: true,
+ fillColor: [0.0, 0.0, 0.0],
+ fillAlpha: 0.7
+ },
+ "battlezone": {
+ edges: true,
+ edgeColor: [0.2, 1.0, 0.2],
+ edgeAlpha: 1,
+ edgeWidth: 3,
+ vertices: false,
+ vertexColor: [0.8, 1.0, 0.8],
+ vertexAlpha: 0.9,
+ vertexSize: 8.0,
+ fill: true,
+ fillColor: [0.0, 0.0, 0.0],
+ fillAlpha: 1.0
+ },
+ "sepia": {
+ edges: true,
+ edgeColor: [0.529411792755127, 0.4577854573726654, 0.4100345969200134],
+ edgeAlpha: 1.0,
+ edgeWidth: 1,
+ vertices: false,
+ vertexColor: [0.7, 1.0, 0.7],
+ vertexAlpha: 0.9,
+ vertexSize: 4.0,
+ fill: true,
+ fillColor: [0.970588207244873, 0.7965892553329468, 0.6660899519920349],
+ fillAlpha: 0.4
+ },
+ "yellowHighlight": {
+ edges: true,
+ edgeColor: [0.529411792755127, 0.4577854573726654, 0.4100345969200134],
+ edgeAlpha: 1.0,
+ edgeWidth: 1,
+ vertices: false,
+ vertexColor: [0.7, 1.0, 0.7],
+ vertexAlpha: 0.9,
+ vertexSize: 4.0,
+ fill: true,
+ fillColor: [1.0, 1.0, 0.0],
+ fillAlpha: 0.5
+ },
+ "greenSelected": {
+ edges: true,
+ edgeColor: [0.4577854573726654, 0.529411792755127, 0.4100345969200134],
+ edgeAlpha: 1.0,
+ edgeWidth: 1,
+ vertices: false,
+ vertexColor: [0.7, 1.0, 0.7],
+ vertexAlpha: 0.9,
+ vertexSize: 4.0,
+ fill: true,
+ fillColor: [0.0, 1.0, 0.0],
+ fillAlpha: 0.5
+ },
+ "gamegrid": {
+ edges: true,
+ edgeColor: [0.4, 0.4, 1.6],
+ edgeAlpha: 0.8,
+ edgeWidth: 3,
+ vertices: false,
+ vertexColor: [0.7, 1.0, 0.7],
+ vertexAlpha: 0.9,
+ vertexSize: 4.0,
+ fill: true,
+ fillColor: [0.2, 0.2, 0.7],
+ fillAlpha: 0.9
+ }
+};
+
+class EmphasisMaterial extends Material {
+
+ /**
+ Available EmphasisMaterial presets.
+
+ @property presets
+ @type {Object}
+ @static
+ */
+ static get presets() {
+ return PRESETS;
+ };
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return "xeogl.EmphasisMaterial";
+ }
+
+ init(cfg) {
+
+ super.init(cfg);
+
+ this._state = new State({
+ type: "EmphasisMaterial",
+ edges: null,
+ edgeColor: null,
+ edgeAlpha: null,
+ edgeWidth: null,
+ vertices: null,
+ vertexColor: null,
+ vertexAlpha: null,
+ vertexSize: null,
+ fill: null,
+ fillColor: null,
+ fillAlpha: null,
+ backfaces: true
+ });
+
+ this._preset = "default";
+
+ if (cfg.preset) { // Apply preset then override with configs where provided
+ this.preset = cfg.preset;
+ if (cfg.edges !== undefined) {
this.edges = cfg.edges;
+ }
+ if (cfg.edgeColor) {
this.edgeColor = cfg.edgeColor;
+ }
+ if (cfg.edgeAlpha !== undefined) {
this.edgeAlpha = cfg.edgeAlpha;
+ }
+ if (cfg.edgeWidth !== undefined) {
this.edgeWidth = cfg.edgeWidth;
+ }
+ if (cfg.vertices !== undefined) {
this.vertices = cfg.vertices;
+ }
+ if (cfg.vertexColor) {
this.vertexColor = cfg.vertexColor;
+ }
+ if (cfg.vertexAlpha !== undefined) {
this.vertexAlpha = cfg.vertexAlpha;
+ }
+ if (cfg.vertexSize) {
this.vertexSize = cfg.vertexSize;
+ }
+ if (cfg.fill !== undefined) {
this.fill = cfg.fill;
+ }
+ if (cfg.fillColor) {
this.fillColor = cfg.fillColor;
+ }
+ if (cfg.fillAlpha !== undefined) {
this.fillAlpha = cfg.fillAlpha;
+ }
+ if (cfg.backfaces !== undefined) {
this.backfaces = cfg.backfaces;
}
- },
+ } else {
+ this.edges = cfg.edges;
+ this.edgeColor = cfg.edgeColor;
+ this.edgeAlpha = cfg.edgeAlpha;
+ this.edgeWidth = cfg.edgeWidth;
+ this.vertices = cfg.vertices;
+ this.vertexColor = cfg.vertexColor;
+ this.vertexAlpha = cfg.vertexAlpha;
+ this.vertexSize = cfg.vertexSize;
+ this.fill = cfg.fill;
+ this.fillColor = cfg.fillColor;
+ this.fillAlpha = cfg.fillAlpha;
+ this.backfaces = cfg.backfaces;
+ }
+ }
- _props: {
-
- /**
- Indicates whether or not ghost edges are visible.
-
- @property edges
- @default true
- @type Boolean
- */
- edges: {
- set: function (value) {
- value = value !== false;
- if (this._state.edges === value) {
- return;
- }
- this._state.edges = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.edges;
- }
- },
-
- /**
- RGB color of ghost edges.
-
- @property edgeColor
- @default [0.2, 0.2, 0.2]
- @type Float32Array
- */
- edgeColor: {
- set: function (value) {
- let edgeColor = this._state.edgeColor;
- if (!edgeColor) {
- edgeColor = this._state.edgeColor = new Float32Array(3);
- } else if (value && edgeColor[0] === value[0] && edgeColor[1] === value[1] && edgeColor[2] === value[2]) {
- return;
- }
- if (value) {
- edgeColor[0] = value[0];
- edgeColor[1] = value[1];
- edgeColor[2] = value[2];
- } else {
- edgeColor[0] = 0.2;
- edgeColor[1] = 0.2;
- edgeColor[2] = 0.2;
- }
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.edgeColor;
- }
- },
-
- /**
- Transparency of ghost edges.
-
- A value of 0.0 indicates fully transparent, 1.0 is fully opaque.
-
- @property edgeAlpha
- @default 0.5
- @type Number
- */
- edgeAlpha: {
- set: function (value) {
- value = (value !== undefined && value !== null) ? value : 0.5;
- if (this._state.edgeAlpha === value) {
- return;
- }
- this._state.edgeAlpha = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.edgeAlpha;
- }
- },
-
- /**
- Width of ghost edges, in pixels.
-
- @property edgeWidth
- @default 1.0
- @type Number
- */
- edgeWidth: {
- set: function (value) {
- this._state.edgeWidth = value || 1.0;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.edgeWidth;
- }
- },
-
- /**
- Indicates whether or not ghost vertices are visible.
-
- @property vertices
- @default false
- @type Boolean
- */
- vertices: {
- set: function (value) {
- value = !!value;
- if (this._state.vertices === value) {
- return;
- }
- this._state.vertices = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.vertices;
- }
- },
-
- /**
- Color of ghost vertices.
-
- @property vertexColor
- @default [0.4,0.4,0.4]
- @type Float32Array
- */
- vertexColor: {
- set: function (value) {
- let vertexColor = this._state.vertexColor;
- if (!vertexColor) {
- vertexColor = this._state.vertexColor = new Float32Array(3);
- } else if (value && vertexColor[0] === value[0] && vertexColor[1] === value[1] && vertexColor[2] === value[2]) {
- return;
- }
- if (value) {
- vertexColor[0] = value[0];
- vertexColor[1] = value[1];
- vertexColor[2] = value[2];
- } else {
- vertexColor[0] = 0.4;
- vertexColor[1] = 0.4;
- vertexColor[2] = 0.4;
- }
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.vertexColor;
- }
- },
-
- /**
- Transparency of ghost vertices.
-
- A value of 0.0 indicates fully transparent, 1.0 is fully opaque.
-
- @property vertexAlpha
- @default 0.7
- @type Number
- */
- vertexAlpha: {
- set: function (value) {
- value = (value !== undefined && value !== null) ? value : 0.7;
- if (this._state.vertexAlpha === value) {
- return;
- }
- this._state.vertexAlpha = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.vertexAlpha;
- }
- },
-
- /**
- Pixel size of ghost vertices.
-
- @property vertexSize
- @default 4.0
- @type Number
- */
- vertexSize: {
- set: function (value) {
- this._state.vertexSize = value || 4.0;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.vertexSize;
- }
- },
-
- /**
- Indicates whether or not ghost surfaces are filled with color.
-
- @property fill
- @default true
- @type Boolean
- */
- fill: {
- set: function (value) {
- value = value !== false;
- if (this._state.fill === value) {
- return;
- }
- this._state.fill = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.fill;
- }
- },
-
- /**
- RGB color of filled ghost faces.
-
- @property fillColor
- @default [0.4, 0.4, 0.4]
- @type Float32Array
- */
- fillColor: {
- set: function (value) {
- let fillColor = this._state.fillColor;
- if (!fillColor) {
- fillColor = this._state.fillColor = new Float32Array(3);
- } else if (value && fillColor[0] === value[0] && fillColor[1] === value[1] && fillColor[2] === value[2]) {
- return;
- }
- if (value) {
- fillColor[0] = value[0];
- fillColor[1] = value[1];
- fillColor[2] = value[2];
- } else {
- fillColor[0] = 0.4;
- fillColor[1] = 0.4;
- fillColor[2] = 0.4;
- }
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.fillColor;
- }
- },
-
- /**
- Transparency of filled ghost faces.
-
- A value of 0.0 indicates fully transparent, 1.0 is fully opaque.
-
- @property fillAlpha
- @default 0.2
- @type Number
- */
- fillAlpha: {
- set: function (value) {
- value = (value !== undefined && value !== null) ? value : 0.2;
- if (this._state.fillAlpha === value) {
- return;
- }
- this._state.fillAlpha = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.fillAlpha;
- }
- },
-
- /**
- Whether backfaces are visible on attached {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
-
- The backfaces will belong to {{#crossLink "Geometry"}}{{/crossLink}} components that are also attached to
- the {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
-
- @property backfaces
- @default false
- @type Boolean
- */
- backfaces: {
- set: function (value) {
- value = !!value;
- if (this._state.backfaces === value) {
- return;
- }
- this._state.backfaces = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.backfaces;
- }
- },
-
- /**
- Selects a preset EmphasisMaterial configuration.
-
- Available presets are:
-
- * "default" - grey wireframe with translucent fill, for light backgrounds.
- * "defaultLightBG" - grey wireframe with grey translucent fill, for light backgrounds.
- * "defaultDarkBG" - grey wireframe with grey translucent fill, for dark backgrounds.
- * "vectorscope" - green wireframe with glowing vertices and black translucent fill.
- * "battlezone" - green wireframe with black opaque fill, giving a solid hidden-lines-removed effect.
- * "sepia" - light red-grey wireframe with light sepia translucent fill - easy on the eyes.
- * "gamegrid" - light blue wireframe with dark blue translucent fill - reminiscent of Tron.
- * "yellowHighlight" - light yellow translucent fill - highlights while allowing underlying detail to show through.
-
- @property preset
- @default "default"
- @type String
- */
- preset: {
- set: function (value) {
- value = value || "default";
- if (this._preset === value) {
- return;
- }
- const preset = xeogl.EmphasisMaterial.presets[value];
- if (!preset) {
- this.error("unsupported preset: '" + value + "' - supported values are " + Object.keys(xeogl.EmphasisMaterial.presets).join(", "));
- return;
- }
- this.edges = preset.edges;
- this.edgeColor = preset.edgeColor;
- this.edgeAlpha = preset.edgeAlpha;
- this.edgeWidth = preset.edgeWidth;
- this.vertices = preset.vertices;
- this.vertexColor = preset.vertexColor;
- this.vertexAlpha = preset.vertexAlpha;
- this.vertexSize = preset.vertexSize;
- this.fill = preset.fill;
- this.fillColor = preset.fillColor;
- this.fillAlpha = preset.fillAlpha;
- this._preset = value;
- },
- get: function () {
- return this._preset;
- }
- }
- },
- _destroy: function () {
- this._super();
- this._state.destroy();
+ /**
+ Indicates whether or not ghost edges are visible.
+
+ @property edges
+ @default true
+ @type Boolean
+ */
+ set edges(value) {
+ value = value !== false;
+ if (this._state.edges === value) {
+ return;
}
- });
+ this._state.edges = value;
+ this._renderer.imageDirty();
+ }
+
+ get edges() {
+ return this._state.edges;
+ }
/**
- Available EmphasisMaterial presets.
+ RGB color of ghost edges.
- @property presets
- @type {Object}
- @static
+ @property edgeColor
+ @default [0.2, 0.2, 0.2]
+ @type Float32Array
*/
- xeogl.EmphasisMaterial.presets = {
+ set edgeColor(value) {
+ let edgeColor = this._state.edgeColor;
+ if (!edgeColor) {
+ edgeColor = this._state.edgeColor = new Float32Array(3);
+ } else if (value && edgeColor[0] === value[0] && edgeColor[1] === value[1] && edgeColor[2] === value[2]) {
+ return;
+ }
+ if (value) {
+ edgeColor[0] = value[0];
+ edgeColor[1] = value[1];
+ edgeColor[2] = value[2];
+ } else {
+ edgeColor[0] = 0.2;
+ edgeColor[1] = 0.2;
+ edgeColor[2] = 0.2;
+ }
+ this._renderer.imageDirty();
+ }
- "default": {
- edges: true,
- edgeColor: [0.2, 0.2, 0.2],
- edgeAlpha: 0.5,
- edgeWidth: 1,
- vertices: false,
- vertexColor: [0.4, 0.4, 0.4],
- vertexAlpha: 0.7,
- vertexSize: 4.0,
- fill: true,
- fillColor: [0.4, 0.4, 0.4],
- fillAlpha: 0.2
- },
+ get edgeColor() {
+ return this._state.edgeColor;
+ }
- "defaultWhiteBG": {
- edgeColor: [0.2, 0.2, 0.2],
- edgeAlpha: 1.0,
- edgeWidth: 1,
- vertices: false,
- vertexColor: [0.4, 0.4, 0.4],
- vertexAlpha: 0.7,
- vertexSize: 4.0,
- fill: true,
- fillColor: [1, 1, 1],
- fillAlpha: 0.6
- },
+ /**
+ Transparency of ghost edges.
- "defaultLightBG": {
- edges: true,
- edgeColor: [0.2, 0.2, 0.2],
- edgeAlpha: 0.5,
- edgeWidth: 1,
- vertices: false,
- vertexColor: [0.4, 0.4, 0.4],
- vertexAlpha: 0.7,
- vertexSize: 4.0,
- fill: true,
- fillColor: [0.4, 0.4, 0.4],
- fillAlpha: 0.2
- },
+ A value of 0.0 indicates fully transparent, 1.0 is fully opaque.
- "defaultDarkBG": {
- edges: true,
- edgeColor: [0.5, 0.5, 0.5],
- edgeAlpha: 0.5,
- edgeWidth: 1,
- vertices: false,
- vertexColor: [0.4, 0.4, 0.4],
- vertexAlpha: 0.7,
- vertexSize: 4.0,
- fill: true,
- fillColor: [0.4, 0.4, 0.4],
- fillAlpha: 0.2
- },
+ @property edgeAlpha
+ @default 0.5
+ @type Number
+ */
+ set edgeAlpha(value) {
+ value = (value !== undefined && value !== null) ? value : 0.5;
+ if (this._state.edgeAlpha === value) {
+ return;
+ }
+ this._state.edgeAlpha = value;
+ this._renderer.imageDirty();
+ }
- "phosphorous": {
- edges: true,
- edgeColor: [0.9, 0.9, 0.9],
- edgeAlpha: 0.5,
- edgeWidth: 2,
- vertices: false,
- vertexColor: [0.4, 0.4, 0.4],
- vertexAlpha: 0.7,
- vertexSize: 1.0,
- fill: true,
- fillColor: [0.0, 0.0, 0.0],
- fillAlpha: 0.4
- },
+ get edgeAlpha() {
+ return this._state.edgeAlpha;
+ }
- "sunset": {
- edges: true,
- edgeColor: [0.9, 0.9, 0.9],
- edgeAlpha: 0.5,
- edgeWidth: 1,
- vertices: false,
- vertexColor: [0.4, 0.4, 0.4],
- vertexAlpha: 0.7,
- vertexSize: 1.0,
- fill: true,
- fillColor: [0.9, 0.9, 0.6],
- fillAlpha: 0.2
- },
+ /**
+ Width of ghost edges, in pixels.
- "vectorscope": {
- edges: true,
- edgeColor: [0.2, 1.0, 0.2],
- edgeAlpha: 1,
- edgeWidth: 2,
- vertices: true,
- vertexColor: [0.7, 1.0, 0.7],
- vertexAlpha: 0.9,
- vertexSize: 8.0,
- fill: true,
- fillColor: [0.0, 0.0, 0.0],
- fillAlpha: 0.7
- },
+ @property edgeWidth
+ @default 1.0
+ @type Number
+ */
+ set edgeWidth(value) {
+ this._state.edgeWidth = value || 1.0;
+ this._renderer.imageDirty();
+ }
- "battlezone": {
- edges: true,
- edgeColor: [0.2, 1.0, 0.2],
- edgeAlpha: 1,
- edgeWidth: 3,
- vertices: false,
- vertexColor: [0.8, 1.0, 0.8],
- vertexAlpha: 0.9,
- vertexSize: 8.0,
- fill: true,
- fillColor: [0.0, 0.0, 0.0],
- fillAlpha: 1.0
- },
+ get edgeWidth() {
+ return this._state.edgeWidth;
+ }
- "sepia": {
- edges: true,
- edgeColor: [0.529411792755127, 0.4577854573726654, 0.4100345969200134],
- edgeAlpha: 1.0,
- edgeWidth: 1,
- vertices: false,
- vertexColor: [0.7, 1.0, 0.7],
- vertexAlpha: 0.9,
- vertexSize: 4.0,
- fill: true,
- fillColor: [0.970588207244873, 0.7965892553329468, 0.6660899519920349],
- fillAlpha: 0.4
- },
+ /**
+ Indicates whether or not ghost vertices are visible.
- "yellowHighlight": {
- edges: true,
- edgeColor: [0.529411792755127, 0.4577854573726654, 0.4100345969200134],
- edgeAlpha: 1.0,
- edgeWidth: 1,
- vertices: false,
- vertexColor: [0.7, 1.0, 0.7],
- vertexAlpha: 0.9,
- vertexSize: 4.0,
- fill: true,
- fillColor: [1.0, 1.0, 0.0],
- fillAlpha: 0.5
- },
+ @property vertices
+ @default false
+ @type Boolean
+ */
+ set vertices(value) {
+ value = !!value;
+ if (this._state.vertices === value) {
+ return;
+ }
+ this._state.vertices = value;
+ this._renderer.imageDirty();
+ }
- "greenSelected": {
- edges: true,
- edgeColor: [0.4577854573726654, 0.529411792755127, 0.4100345969200134],
- edgeAlpha: 1.0,
- edgeWidth: 1,
- vertices: false,
- vertexColor: [0.7, 1.0, 0.7],
- vertexAlpha: 0.9,
- vertexSize: 4.0,
- fill: true,
- fillColor: [0.0, 1.0, 0.0],
- fillAlpha: 0.5
- },
+ get vertices() {
+ return this._state.vertices;
+ }
- "gamegrid": {
- edges: true,
- edgeColor: [0.4, 0.4, 1.6],
- edgeAlpha: 0.8,
- edgeWidth: 3,
- vertices: false,
- vertexColor: [0.7, 1.0, 0.7],
- vertexAlpha: 0.9,
- vertexSize: 4.0,
- fill: true,
- fillColor: [0.2, 0.2, 0.7],
- fillAlpha: 0.9
+ /**
+ Color of ghost vertices.
+
+ @property vertexColor
+ @default [0.4,0.4,0.4]
+ @type Float32Array
+ */
+ set vertexColor(value) {
+ let vertexColor = this._state.vertexColor;
+ if (!vertexColor) {
+ vertexColor = this._state.vertexColor = new Float32Array(3);
+ } else if (value && vertexColor[0] === value[0] && vertexColor[1] === value[1] && vertexColor[2] === value[2]) {
+ return;
}
- };
+ if (value) {
+ vertexColor[0] = value[0];
+ vertexColor[1] = value[1];
+ vertexColor[2] = value[2];
+ } else {
+ vertexColor[0] = 0.4;
+ vertexColor[1] = 0.4;
+ vertexColor[2] = 0.4;
+ }
+ this._renderer.imageDirty();
+ }
+
+ get vertexColor() {
+ return this._state.vertexColor;
+ }
+
+ /**
+ Transparency of ghost vertices.
+
+ A value of 0.0 indicates fully transparent, 1.0 is fully opaque.
+
+ @property vertexAlpha
+ @default 0.7
+ @type Number
+ */
+ set vertexAlpha(value) {
+ value = (value !== undefined && value !== null) ? value : 0.7;
+ if (this._state.vertexAlpha === value) {
+ return;
+ }
+ this._state.vertexAlpha = value;
+ this._renderer.imageDirty();
+ }
- xeogl.GhostMaterial = xeogl.EmphasisMaterial; // Backward compatibility
+ get vertexAlpha() {
+ return this._state.vertexAlpha;
+ }
+
+ /**
+ Pixel size of ghost vertices.
+
+ @property vertexSize
+ @default 4.0
+ @type Number
+ */
+ set vertexSize(value) {
+ this._state.vertexSize = value || 4.0;
+ this._renderer.imageDirty();
+ }
+
+ get vertexSize() {
+ return this._state.vertexSize;
+ }
+
+ /**
+ Indicates whether or not ghost surfaces are filled with color.
-})();
\ No newline at end of file
+ @property fill
+ @default true
+ @type Boolean
+ */
+ set fill(value) {
+ value = value !== false;
+ if (this._state.fill === value) {
+ return;
+ }
+ this._state.fill = value;
+ this._renderer.imageDirty();
+ }
+
+ get fill() {
+ return this._state.fill;
+ }
+
+ /**
+ RGB color of filled ghost faces.
+
+ @property fillColor
+ @default [0.4, 0.4, 0.4]
+ @type Float32Array
+ */
+ set fillColor(value) {
+ let fillColor = this._state.fillColor;
+ if (!fillColor) {
+ fillColor = this._state.fillColor = new Float32Array(3);
+ } else if (value && fillColor[0] === value[0] && fillColor[1] === value[1] && fillColor[2] === value[2]) {
+ return;
+ }
+ if (value) {
+ fillColor[0] = value[0];
+ fillColor[1] = value[1];
+ fillColor[2] = value[2];
+ } else {
+ fillColor[0] = 0.4;
+ fillColor[1] = 0.4;
+ fillColor[2] = 0.4;
+ }
+ this._renderer.imageDirty();
+ }
+
+ get fillColor() {
+ return this._state.fillColor;
+ }
+
+ /**
+ Transparency of filled ghost faces.
+
+ A value of 0.0 indicates fully transparent, 1.0 is fully opaque.
+
+ @property fillAlpha
+ @default 0.2
+ @type Number
+ */
+ set fillAlpha(value) {
+ value = (value !== undefined && value !== null) ? value : 0.2;
+ if (this._state.fillAlpha === value) {
+ return;
+ }
+ this._state.fillAlpha = value;
+ this._renderer.imageDirty();
+ }
+
+ get fillAlpha() {
+ return this._state.fillAlpha;
+ }
+
+ /**
+ Whether backfaces are visible on attached {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
+
+ The backfaces will belong to {{#crossLink "Geometry"}}{{/crossLink}} components that are also attached to
+ the {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
+
+ @property backfaces
+ @default false
+ @type Boolean
+ */
+ set backfaces(value) {
+ value = !!value;
+ if (this._state.backfaces === value) {
+ return;
+ }
+ this._state.backfaces = value;
+ this._renderer.imageDirty();
+ }
+
+ get backfaces() {
+ return this._state.backfaces;
+ }
+
+ /**
+ Selects a preset EmphasisMaterial configuration.
+
+ Available presets are:
+
+ * "default" - grey wireframe with translucent fill, for light backgrounds.
+ * "defaultLightBG" - grey wireframe with grey translucent fill, for light backgrounds.
+ * "defaultDarkBG" - grey wireframe with grey translucent fill, for dark backgrounds.
+ * "vectorscope" - green wireframe with glowing vertices and black translucent fill.
+ * "battlezone" - green wireframe with black opaque fill, giving a solid hidden-lines-removed effect.
+ * "sepia" - light red-grey wireframe with light sepia translucent fill - easy on the eyes.
+ * "gamegrid" - light blue wireframe with dark blue translucent fill - reminiscent of Tron.
+ * "yellowHighlight" - light yellow translucent fill - highlights while allowing underlying detail to show through.
+
+ @property preset
+ @default "default"
+ @type String
+ */
+ set preset(value) {
+ value = value || "default";
+ if (this._preset === value) {
+ return;
+ }
+ const preset = PRESETS[value];
+ if (!preset) {
+ this.error("unsupported preset: '" + value + "' - supported values are " + Object.keys(PRESETS).join(", "));
+ return;
+ }
+ this.edges = preset.edges;
+ this.edgeColor = preset.edgeColor;
+ this.edgeAlpha = preset.edgeAlpha;
+ this.edgeWidth = preset.edgeWidth;
+ this.vertices = preset.vertices;
+ this.vertexColor = preset.vertexColor;
+ this.vertexAlpha = preset.vertexAlpha;
+ this.vertexSize = preset.vertexSize;
+ this.fill = preset.fill;
+ this.fillColor = preset.fillColor;
+ this.fillAlpha = preset.fillAlpha;
+ this._preset = value;
+ }
+
+ get preset() {
+ return this._preset;
+ }
+
+ destroy() {
+ super.destroy();
+ this._state.destroy();
+ }
+}
+
+export {EmphasisMaterial};
\ No newline at end of file
diff --git a/src/materials/fresnel.js b/src/materials/fresnel.js
index 26f058215..cdd594a00 100644
--- a/src/materials/fresnel.js
+++ b/src/materials/fresnel.js
@@ -44,8 +44,7 @@
@module xeogl
@submodule materials
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}} - creates this Geometry in the default
- {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
+ @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 scene, generated automatically when omitted.
@param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to this Fresnel.
@@ -56,122 +55,129 @@
@param [cfg.power=0] {Number} The power.
@extends Component
*/
-(function () {
-
- "use strict";
-
- xeogl.Fresnel = xeogl.Component.extend({
-
- type: "xeogl.Fresnel",
-
- _init: function (cfg) {
-
- this._state = new xeogl.renderer.State({
- edgeColor: xeogl.math.vec3([0, 0, 0]),
- centerColor: xeogl.math.vec3([1, 1, 1]),
- edgeBias: 0,
- centerBias: 1,
- power: 1
- });
-
- this.edgeColor = cfg.edgeColor;
- this.centerColor = cfg.centerColor;
- this.edgeBias = cfg.edgeBias;
- this.centerBias = cfg.centerBias;
- this.power = cfg.power;
- },
-
- _props: {
-
- /**
- This Fresnel's edge color.
-
- @property edgeColor
- @default [0.0, 0.0, 0.0]
- @type Float32Array
- */
- edgeColor: {
- set: function (value) {
- this._state.edgeColor.set(value || [0.0, 0.0, 0.0]);
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.edgeColor;
- }
- },
-
- /**
- This Fresnel's center color.
-
- @property centerColor
- @default [1.0, 1.0, 1.0]
- @type Float32Array
- */
- centerColor: {
- set: function (value) {
- this._state.centerColor.set(value || [1.0, 1.0, 1.0]);
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.centerColor;
- }
- },
-
- /**
- * Indicates this Fresnel's edge bias.
- *
- * @property edgeBias
- * @default 0
- * @type Number
- */
- edgeBias: {
- set: function (value) {
- this._state.edgeBias = value || 0;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.edgeBias;
- }
- },
-
- /**
- * Indicates this Fresnel's center bias.
- *
- * @property centerBias
- * @default 1
- * @type Number
- */
- centerBias: {
- set: function (value) {
- this._state.centerBias = (value !== undefined && value !== null) ? value : 1;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.centerBias;
- }
- },
-
- /**
- * Indicates this Fresnel's power.
- *
- * @property power
- * @default 1
- * @type Number
- */
- power: {
- set: function (value) {
- this._state.power = (value !== undefined && value !== null) ? value : 1;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.power;
- }
- }
- },
-
- _destroy: function () {
- this._state.destroy();
- }
- });
-
-})();
+
+import {Component} from '../component.js';
+import {State} from '../renderer/state.js';
+import {math} from '../math/math.js';
+
+class Fresnel extends Component {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return "xeogl.Fresnel";
+ }
+
+ constructor(owner = null, cfg = {}) {
+
+ super.init(cfg);
+
+ this._state = new State({
+ edgeColor: math.vec3([0, 0, 0]),
+ centerColor: math.vec3([1, 1, 1]),
+ edgeBias: 0,
+ centerBias: 1,
+ power: 1
+ });
+
+ this.edgeColor = cfg.edgeColor;
+ this.centerColor = cfg.centerColor;
+ this.edgeBias = cfg.edgeBias;
+ this.centerBias = cfg.centerBias;
+ this.power = cfg.power;
+ }
+
+ /**
+ This Fresnel's edge color.
+
+ @property edgeColor
+ @default [0.0, 0.0, 0.0]
+ @type Float32Array
+ */
+ set edgeColor(value) {
+ this._state.edgeColor.set(value || [0.0, 0.0, 0.0]);
+ this._renderer.imageDirty();
+ }
+
+ get edgeColor() {
+ return this._state.edgeColor;
+ }
+
+ /**
+ This Fresnel's center color.
+
+ @property centerColor
+ @default [1.0, 1.0, 1.0]
+ @type Float32Array
+ */
+ set centerColor(value) {
+ this._state.centerColor.set(value || [1.0, 1.0, 1.0]);
+ this._renderer.imageDirty();
+ }
+
+ get centerColor() {
+ return this._state.centerColor;
+ }
+
+ /**
+ * Indicates this Fresnel's edge bias.
+ *
+ * @property edgeBias
+ * @default 0
+ * @type Number
+ */
+ set edgeBias(value) {
+ this._state.edgeBias = value || 0;
+ this._renderer.imageDirty();
+ }
+
+ get edgeBias() {
+ return this._state.edgeBias;
+ }
+
+ /**
+ * Indicates this Fresnel's center bias.
+ *
+ * @property centerBias
+ * @default 1
+ * @type Number
+ */
+ set centerBias(value) {
+ this._state.centerBias = (value !== undefined && value !== null) ? value : 1;
+ this._renderer.imageDirty();
+ }
+
+ get centerBias() {
+ return this._state.centerBias;
+ }
+
+ /**
+ * Indicates this Fresnel's power.
+ *
+ * @property power
+ * @default 1
+ * @type Number
+ */
+ set power(value) {
+ this._state.power = (value !== undefined && value !== null) ? value : 1;
+ this._renderer.imageDirty();
+ }
+
+ get power() {
+ return this._state.power;
+ }
+
+ destroy() {
+ super.destroy();
+ this._state.destroy();
+ }
+}
+
+export{Fresnel};
\ No newline at end of file
diff --git a/src/materials/lambertMaterial.js b/src/materials/lambertMaterial.js
index 3f2adf336..636cb79e2 100644
--- a/src/materials/lambertMaterial.js
+++ b/src/materials/lambertMaterial.js
@@ -5,7 +5,7 @@
## Examples
-TODO
+ TODO
## Overview
@@ -49,8 +49,7 @@ TODO
@submodule materials
@constructor
@extends Material
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}}, creates this LambertMaterial within the
- default {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted
+ @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] {*} The LambertMaterial configuration
@param [cfg.id] {String} Optional ID, unique among all components in the parent {{#crossLink "Scene"}}Scene{{/crossLink}}, generated automatically when omitted.
@param [cfg.meta=null] {String:Object} Metadata to attach to this LambertMaterial.
@@ -64,252 +63,258 @@ TODO
@param [cfg.backfaces=false] {Boolean} Whether to render {{#crossLink "Geometry"}}Geometry{{/crossLink}} backfaces.
@param [cfg.frontface="ccw"] {Boolean} The winding order for {{#crossLink "Geometry"}}Geometry{{/crossLink}} front faces - "cw" for clockwise, or "ccw" for counter-clockwise.
*/
-(function () {
-
- "use strict";
-
- xeogl.LambertMaterial = xeogl.Material.extend({
-
- type: "xeogl.LambertMaterial",
-
- _init: function (cfg) {
-
- this._super(cfg);
-
- this._state = new xeogl.renderer.State({
- type: "LambertMaterial",
- ambient: xeogl.math.vec3([1.0, 1.0, 1.0]),
- color: xeogl.math.vec3([1.0, 1.0, 1.0]),
- emissive: xeogl.math.vec3([0.0, 0.0, 0.0]),
- alpha: null,
- alphaMode: 0, // 2 ("blend") when transparent, so renderer knows when to add to transparency bin
- lineWidth: null,
- pointSize: null,
- backfaces: null,
- frontface: null, // Boolean for speed; true == "ccw", false == "cw"
- hash: "/lam;"
- });
-
- this.ambient = cfg.ambient;
- this.color = cfg.color;
- this.emissive = cfg.emissive;
- this.alpha = cfg.alpha;
- this.lineWidth = cfg.lineWidth;
- this.pointSize = cfg.pointSize;
- this.backfaces = cfg.backfaces;
- this.frontface = cfg.frontface;
- },
-
- _props: {
-
- /**
- The LambertMaterial's ambient color.
-
- @property ambient
- @default [0.3, 0.3, 0.3]
- @type Float32Array
- */
- ambient: {
- set: function (value) {
- let ambient = this._state.ambient;
- if (!ambient) {
- ambient = this._state.ambient = new Float32Array(3);
- } else if (value && ambient[0] === value[0] && ambient[1] === value[1] && ambient[2] === value[2]) {
- return;
- }
- if (value) {
- ambient[0] = value[0];
- ambient[1] = value[1];
- ambient[2] = value[2];
- } else {
- ambient[0] = .2;
- ambient[1] = .2;
- ambient[2] = .2;
- }
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.ambient;
- }
- },
-
- /**
- The LambertMaterial's diffuse color.
-
- @property color
- @default [1.0, 1.0, 1.0]
- @type Float32Array
- */
- color: {
- set: function (value) {
- let color = this._state.color;
- if (!color) {
- color = this._state.color = new Float32Array(3);
- } else if (value && color[0] === value[0] && color[1] === value[1] && color[2] === value[2]) {
- return;
- }
- if (value) {
- color[0] = value[0];
- color[1] = value[1];
- color[2] = value[2];
- } else {
- color[0] = 1;
- color[1] = 1;
- color[2] = 1;
- }
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.color;
- }
- },
-
- /**
- The LambertMaterial's emissive color.
-
- @property emissive
- @default [0.0, 0.0, 0.0]
- @type Float32Array
- */
- emissive: {
- set: function (value) {
- let emissive = this._state.emissive;
- if (!emissive) {
- emissive = this._state.emissive = new Float32Array(3);
- } else if (value && emissive[0] === value[0] && emissive[1] === value[1] && emissive[2] === value[2]) {
- return;
- }
- if (value) {
- emissive[0] = value[0];
- emissive[1] = value[1];
- emissive[2] = value[2];
- } else {
- emissive[0] = 0;
- emissive[1] = 0;
- emissive[2] = 0;
- }
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.emissive;
- }
- },
-
- /**
- Factor in the range [0..1] indicating how transparent the LambertMaterial is.
-
- A value of 0.0 indicates fully transparent, 1.0 is fully opaque.
-
- @property alpha
- @default 1.0
- @type Number
- */
- alpha: {
- set: function (value) {
- value = (value !== undefined && value !== null) ? value : 1.0;
- if (this._state.alpha === value) {
- return;
- }
- this._state.alpha = value;
- this._state.alphaMode = value < 1.0 ? 2 /* blend */ : 0 /* opaque */
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.alpha;
- }
- },
-
- /**
- The LambertMaterial's line width.
-
- @property lineWidth
- @default 1.0
- @type Number
- */
- lineWidth: {
- set: function (value) {
- this._state.lineWidth = value || 1.0;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.lineWidth;
- }
- },
-
- /**
- The LambertMaterial's point size.
-
- @property pointSize
- @default 1.0
- @type Number
- */
- pointSize: {
- set: function (value) {
- this._state.pointSize = value || 1.0;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.pointSize;
- }
- },
-
- /**
- Whether backfaces are visible on attached {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
-
- The backfaces will belong to {{#crossLink "Geometry"}}{{/crossLink}} components that are also attached to
- the {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
-
- @property backfaces
- @default false
- @type Boolean
- */
- backfaces: {
- set: function (value) {
- value = !!value;
- if (this._state.backfaces === value) {
- return;
- }
- this._state.backfaces = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.backfaces;
- }
- },
-
- /**
- Indicates the winding direction of front faces on attached {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
-
- The faces will belong to {{#crossLink "Geometry"}}{{/crossLink}} components that are also attached to
- the {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
-
- @property frontface
- @default "ccw"
- @type String
- */
- frontface: {
- set: function (value) {
- value = value !== "cw";
- if (this._state.frontface === value) {
- return;
- }
- this._state.frontface = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.frontface ? "ccw" : "cw";
- }
- }
- },
-
- _getState: function () {
- return this._state;
- },
-
- _destroy: function () {
- this._super();
- this._state.destroy();
+
+import {core} from "./../core.js";
+import {Material} from './material.js';
+import {State} from '../renderer/state.js';
+import {math} from '../math/math.js';
+
+class LambertMaterial extends Material {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return "xeogl.LambertMaterial";
+ }
+
+ init(cfg) {
+
+ super.init(cfg);
+
+ this._state = new State({
+ type: "LambertMaterial",
+ ambient: math.vec3([1.0, 1.0, 1.0]),
+ color: math.vec3([1.0, 1.0, 1.0]),
+ emissive: math.vec3([0.0, 0.0, 0.0]),
+ alpha: null,
+ alphaMode: 0, // 2 ("blend") when transparent, so renderer knows when to add to transparency bin
+ lineWidth: null,
+ pointSize: null,
+ backfaces: null,
+ frontface: null, // Boolean for speed; true == "ccw", false == "cw"
+ hash: "/lam;"
+ });
+
+ this.ambient = cfg.ambient;
+ this.color = cfg.color;
+ this.emissive = cfg.emissive;
+ this.alpha = cfg.alpha;
+ this.lineWidth = cfg.lineWidth;
+ this.pointSize = cfg.pointSize;
+ this.backfaces = cfg.backfaces;
+ this.frontface = cfg.frontface;
+ }
+
+ /**
+ The LambertMaterial's ambient color.
+
+ @property ambient
+ @default [0.3, 0.3, 0.3]
+ @type Float32Array
+ */
+
+ set ambient(value) {
+ let ambient = this._state.ambient;
+ if (!ambient) {
+ ambient = this._state.ambient = new Float32Array(3);
+ } else if (value && ambient[0] === value[0] && ambient[1] === value[1] && ambient[2] === value[2]) {
+ return;
+ }
+ if (value) {
+ ambient[0] = value[0];
+ ambient[1] = value[1];
+ ambient[2] = value[2];
+ } else {
+ ambient[0] = .2;
+ ambient[1] = .2;
+ ambient[2] = .2;
+ }
+ this._renderer.imageDirty();
+ }
+
+ get ambient() {
+ return this._state.ambient;
+ }
+
+ /**
+ The LambertMaterial's diffuse color.
+
+ @property color
+ @default [1.0, 1.0, 1.0]
+ @type Float32Array
+ */
+ set color(value) {
+ let color = this._state.color;
+ if (!color) {
+ color = this._state.color = new Float32Array(3);
+ } else if (value && color[0] === value[0] && color[1] === value[1] && color[2] === value[2]) {
+ return;
+ }
+ if (value) {
+ color[0] = value[0];
+ color[1] = value[1];
+ color[2] = value[2];
+ } else {
+ color[0] = 1;
+ color[1] = 1;
+ color[2] = 1;
+ }
+ this._renderer.imageDirty();
+ }
+
+ get color() {
+ return this._state.color;
+ }
+
+ /**
+ The LambertMaterial's emissive color.
+
+ @property emissive
+ @default [0.0, 0.0, 0.0]
+ @type Float32Array
+ */
+ set emissive(value) {
+ let emissive = this._state.emissive;
+ if (!emissive) {
+ emissive = this._state.emissive = new Float32Array(3);
+ } else if (value && emissive[0] === value[0] && emissive[1] === value[1] && emissive[2] === value[2]) {
+ return;
}
- });
+ if (value) {
+ emissive[0] = value[0];
+ emissive[1] = value[1];
+ emissive[2] = value[2];
+ } else {
+ emissive[0] = 0;
+ emissive[1] = 0;
+ emissive[2] = 0;
+ }
+ this._renderer.imageDirty();
+ }
+
+ get emissive() {
+ return this._state.emissive;
+ }
+
+ /**
+ Factor in the range [0..1] indicating how transparent the LambertMaterial is.
+
+ A value of 0.0 indicates fully transparent, 1.0 is fully opaque.
+
+ @property alpha
+ @default 1.0
+ @type Number
+ */
+
+ set alpha(value) {
+ value = (value !== undefined && value !== null) ? value : 1.0;
+ if (this._state.alpha === value) {
+ return;
+ }
+ this._state.alpha = value;
+ this._state.alphaMode = value < 1.0 ? 2 /* blend */ : 0
+ /* opaque */
+ this._renderer.imageDirty();
+ }
+
+ get alpha() {
+ return this._state.alpha;
+ }
+
+ /**
+ The LambertMaterial's line width.
+
+ @property lineWidth
+ @default 1.0
+ @type Number
+ */
+
+ set lineWidth(value) {
+ this._state.lineWidth = value || 1.0;
+ this._renderer.imageDirty();
+ }
+
+ get lineWidth() {
+ return this._state.lineWidth;
+ }
+
+ /**
+ The LambertMaterial's point size.
+
+ @property pointSize
+ @default 1.0
+ @type Number
+ */
+ set pointSize(value) {
+ this._state.pointSize = value || 1.0;
+ this._renderer.imageDirty();
+ }
+
+ get pointSize() {
+ return this._state.pointSize;
+ }
+
+ /**
+ Whether backfaces are visible on attached {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
+
+ The backfaces will belong to {{#crossLink "Geometry"}}{{/crossLink}} components that are also attached to
+ the {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
+
+ @property backfaces
+ @default false
+ @type Boolean
+ */
+ set backfaces(value) {
+ value = !!value;
+ if (this._state.backfaces === value) {
+ return;
+ }
+ this._state.backfaces = value;
+ this._renderer.imageDirty();
+ }
+
+ get backfaces() {
+ return this._state.backfaces;
+ }
+
+ /**
+ Indicates the winding direction of front faces on attached {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
+
+ The faces will belong to {{#crossLink "Geometry"}}{{/crossLink}} components that are also attached to
+ the {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
+
+ @property frontface
+ @default "ccw"
+ @type String
+ */
+ set frontface(value) {
+ value = value !== "cw";
+ if (this._state.frontface === value) {
+ return;
+ }
+ this._state.frontface = value;
+ this._renderer.imageDirty();
+ }
+
+ get frontface() {
+ return this._state.frontface ? "ccw" : "cw";
+ }
+
+ _getState() {
+ return this._state;
+ }
+
+ destroy() {
+ super.destroy();
+ this._state.destroy();
+ }
+}
-})();
\ No newline at end of file
+export{LambertMaterial};
\ No newline at end of file
diff --git a/src/materials/material.js b/src/materials/material.js
index f2d51e9df..510e6a6a3 100644
--- a/src/materials/material.js
+++ b/src/materials/material.js
@@ -21,21 +21,34 @@
@constructor
@extends Component
*/
-(function () {
+import {core} from "./../core.js";
+import {Component} from '../component.js';
+import {stats} from './../stats.js';
- "use strict";
+class Material extends Component{
- xeogl.Material = xeogl.Component.extend({
+ /**
+ JavaScript class name for this Component.
- type: "xeogl.Material",
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
- _init: function () {
- xeogl.stats.memory.materials++;
- },
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return "xeogl.Material";
+ }
- _destroy: function() {
- xeogl.stats.memory.materials--;
- }
- });
+ init(cfg) {
+ super.init(cfg);
+ stats.memory.materials++;
+ }
-})();
+ destroy() {
+ super.destroy();
+ stats.memory.materials--;
+ }
+}
+
+export{Material};
diff --git a/src/materials/metallicMaterial.js b/src/materials/metallicMaterial.js
index 0ed0ad6fa..0047a7273 100644
--- a/src/materials/metallicMaterial.js
+++ b/src/materials/metallicMaterial.js
@@ -219,8 +219,7 @@
@constructor
@extends Material
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}}, creates this MetallicMaterial within the
- default {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
+ @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] {*} The MetallicMaterial configuration.
@@ -297,591 +296,575 @@
@param [cfg.pointSize=1] {Number} Scalar that controls the size of points for {{#crossLink "Geometry"}}{{/crossLink}} with {{#crossLink "Geometry/primitive:property"}}{{/crossLink}} set to "points".
*/
-(function () {
-
- "use strict";
-
- xeogl.MetallicMaterial = xeogl.Material.extend({
-
- type: "xeogl.MetallicMaterial",
-
- _init: function (cfg) {
-
- this._super(cfg);
-
- this._state = new xeogl.renderer.State({
- type: "MetallicMaterial",
- baseColor: xeogl.math.vec4([1.0, 1.0, 1.0]),
- emissive: xeogl.math.vec4([0.0, 0.0, 0.0]),
- metallic: null,
- roughness: null,
- specularF0: null,
- alpha: null,
- alphaMode: null, // "opaque"
- alphaCutoff: null,
- lineWidth: null,
- pointSize: null,
- backfaces: null,
- frontface: null, // Boolean for speed; true == "ccw", false == "cw"
- hash: null
- });
-
- this.baseColor = cfg.baseColor;
- this.metallic = cfg.metallic;
- this.roughness = cfg.roughness;
- this.specularF0 = cfg.specularF0;
- this.emissive = cfg.emissive;
- this.alpha = cfg.alpha;
-
- if (cfg.baseColorMap) {
- this._baseColorMap = this._checkComponent("xeogl.Texture", cfg.baseColorMap);
- }
- if (cfg.metallicMap) {
- this._metallicMap = this._checkComponent("xeogl.Texture", cfg.metallicMap);
- }
- if (cfg.roughnessMap) {
- this._roughnessMap = this._checkComponent("xeogl.Texture", cfg.roughnessMap);
- }
- if (cfg.metallicRoughnessMap) {
- this._metallicRoughnessMap = this._checkComponent("xeogl.Texture", cfg.metallicRoughnessMap);
- }
- if (cfg.emissiveMap) {
- this._emissiveMap = this._checkComponent("xeogl.Texture", cfg.emissiveMap);
- }
- if (cfg.occlusionMap) {
- this._occlusionMap = this._checkComponent("xeogl.Texture", cfg.occlusionMap);
- }
- if (cfg.alphaMap) {
- this._alphaMap = this._checkComponent("xeogl.Texture", cfg.alphaMap);
- }
- if (cfg.normalMap) {
- this._normalMap = this._checkComponent("xeogl.Texture", cfg.normalMap);
- }
+import {core} from "./../core.js";
+import {Material} from './material.js';
+import {State} from '../renderer/state.js';
+import {math} from '../math/math.js';
+
+const modes = {"opaque": 0, "mask": 1, "blend": 2};
+const modeNames = ["opaque", "mask", "blend"];
+
+class MetallicMaterial extends Material {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return "xeogl.MetallicMaterial";
+ }
+
+ init(cfg) {
+
+ super.init(cfg);
+
+ this._state = new State({
+ type: "MetallicMaterial",
+ baseColor: math.vec4([1.0, 1.0, 1.0]),
+ emissive: math.vec4([0.0, 0.0, 0.0]),
+ metallic: null,
+ roughness: null,
+ specularF0: null,
+ alpha: null,
+ alphaMode: null, // "opaque"
+ alphaCutoff: null,
+ lineWidth: null,
+ pointSize: null,
+ backfaces: null,
+ frontface: null, // Boolean for speed; true == "ccw", false == "cw"
+ hash: null
+ });
+
+ this.baseColor = cfg.baseColor;
+ this.metallic = cfg.metallic;
+ this.roughness = cfg.roughness;
+ this.specularF0 = cfg.specularF0;
+ this.emissive = cfg.emissive;
+ this.alpha = cfg.alpha;
+
+ if (cfg.baseColorMap) {
+ this._baseColorMap = this._checkComponent("xeogl.Texture", cfg.baseColorMap);
+ }
+ if (cfg.metallicMap) {
+ this._metallicMap = this._checkComponent("xeogl.Texture", cfg.metallicMap);
- this.alphaMode = cfg.alphaMode;
- this.alphaCutoff = cfg.alphaCutoff;
- this.backfaces = cfg.backfaces;
- this.frontface = cfg.frontface;
- this.lineWidth = cfg.lineWidth;
- this.pointSize = cfg.pointSize;
-
- this._makeHash();
- },
-
- _makeHash: function () {
- const state = this._state;
- const hash = ["/met"];
- if (this._baseColorMap) {
- hash.push("/bm");
- if (this._baseColorMap._state.hasMatrix) {
- hash.push("/mat");
- }
- hash.push("/" + this._baseColorMap._state.encoding);
- }
- if (this._metallicMap) {
- hash.push("/mm");
- if (this._metallicMap._state.hasMatrix) {
- hash.push("/mat");
- }
+ }
+ if (cfg.roughnessMap) {
+ this._roughnessMap = this._checkComponent("xeogl.Texture", cfg.roughnessMap);
+ }
+ if (cfg.metallicRoughnessMap) {
+ this._metallicRoughnessMap = this._checkComponent("xeogl.Texture", cfg.metallicRoughnessMap);
+ }
+ if (cfg.emissiveMap) {
+ this._emissiveMap = this._checkComponent("xeogl.Texture", cfg.emissiveMap);
+ }
+ if (cfg.occlusionMap) {
+ this._occlusionMap = this._checkComponent("xeogl.Texture", cfg.occlusionMap);
+ }
+ if (cfg.alphaMap) {
+ this._alphaMap = this._checkComponent("xeogl.Texture", cfg.alphaMap);
+ }
+ if (cfg.normalMap) {
+ this._normalMap = this._checkComponent("xeogl.Texture", cfg.normalMap);
+ }
+
+ this.alphaMode = cfg.alphaMode;
+ this.alphaCutoff = cfg.alphaCutoff;
+ this.backfaces = cfg.backfaces;
+ this.frontface = cfg.frontface;
+ this.lineWidth = cfg.lineWidth;
+ this.pointSize = cfg.pointSize;
+
+ this._makeHash();
+ }
+
+ _makeHash() {
+ const state = this._state;
+ const hash = ["/met"];
+ if (this._baseColorMap) {
+ hash.push("/bm");
+ if (this._baseColorMap._state.hasMatrix) {
+ hash.push("/mat");
}
- if (this._roughnessMap) {
- hash.push("/rm");
- if (this._roughnessMap._state.hasMatrix) {
- hash.push("/mat");
- }
+ hash.push("/" + this._baseColorMap._state.encoding);
+ }
+ if (this._metallicMap) {
+ hash.push("/mm");
+ if (this._metallicMap._state.hasMatrix) {
+ hash.push("/mat");
}
- if (this._metallicRoughnessMap) {
- hash.push("/mrm");
- if (this._metallicRoughnessMap._state.hasMatrix) {
- hash.push("/mat");
- }
+ }
+ if (this._roughnessMap) {
+ hash.push("/rm");
+ if (this._roughnessMap._state.hasMatrix) {
+ hash.push("/mat");
}
- if (this._emissiveMap) {
- hash.push("/em");
- if (this._emissiveMap._state.hasMatrix) {
- hash.push("/mat");
- }
+ }
+ if (this._metallicRoughnessMap) {
+ hash.push("/mrm");
+ if (this._metallicRoughnessMap._state.hasMatrix) {
+ hash.push("/mat");
}
- if (this._occlusionMap) {
- hash.push("/ocm");
- if (this._occlusionMap._state.hasMatrix) {
- hash.push("/mat");
- }
+ }
+ if (this._emissiveMap) {
+ hash.push("/em");
+ if (this._emissiveMap._state.hasMatrix) {
+ hash.push("/mat");
}
- if (this._alphaMap) {
- hash.push("/am");
- if (this._alphaMap._state.hasMatrix) {
- hash.push("/mat");
- }
+ }
+ if (this._occlusionMap) {
+ hash.push("/ocm");
+ if (this._occlusionMap._state.hasMatrix) {
+ hash.push("/mat");
}
- if (this._normalMap) {
- hash.push("/nm");
- if (this._normalMap._state.hasMatrix) {
- hash.push("/mat");
- }
+ }
+ if (this._alphaMap) {
+ hash.push("/am");
+ if (this._alphaMap._state.hasMatrix) {
+ hash.push("/mat");
}
- hash.push(";");
- state.hash = hash.join("");
- },
-
- _props: {
-
- /**
- RGB diffuse color.
-
- Multiplies by the RGB components of {{#crossLink "MetallicMaterial/baseColorMap:property"}}{{/crossLink}}.
-
- @property baseColor
- @default [1.0, 1.0, 1.0]
- @type Float32Array
- */
- baseColor: {
- set: function (value) {
- let baseColor = this._state.baseColor;
- if (!baseColor) {
- baseColor = this._state.baseColor = new Float32Array(3);
- } else if (value && baseColor[0] === value[0] && baseColor[1] === value[1] && baseColor[2] === value[2]) {
- return;
- }
- if (value) {
- baseColor[0] = value[0];
- baseColor[1] = value[1];
- baseColor[2] = value[2];
- } else {
- baseColor[0] = 1;
- baseColor[1] = 1;
- baseColor[2] = 1;
- }
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.baseColor;
- }
- },
-
- /**
- RGB {{#crossLink "Texture"}}{{/crossLink}} containing the diffuse color of this MetallicMaterial, with optional *A* component for alpha.
-
- The RGB components multiply by the {{#crossLink "MetallicMaterial/baseColor:property"}}{{/crossLink}} property,
- while the *A* component, if present, multiplies by the {{#crossLink "MetallicMaterial/alpha:property"}}{{/crossLink}} property.
-
- @property baseColorMap
- @default undefined
- @type {Texture}
- @final
- */
- baseColorMap: {
- get: function () {
- return this._baseColorMap;
- }
- },
-
- /**
- Factor in the range [0..1] indicating how metallic this MetallicMaterial is.
-
- 1 is metal, 0 is non-metal.
-
- Multiplies by the *R* component of {{#crossLink "MetallicMaterial/metallicMap:property"}}{{/crossLink}}
- and the *A* component of {{#crossLink "MetallicMaterial/metalRoughnessMap:property"}}{{/crossLink}}.
-
- @property metallic
- @default 1.0
- @type Number
- */
- metallic: {
- set: function (value) {
- value = (value !== undefined && value !== null) ? value : 1.0;
- if (this._state.metallic === value) {
- return;
- }
- this._state.metallic = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.metallic;
- }
- },
-
- /**
- RGB {{#crossLink "Texture"}}{{/crossLink}} containing this MetallicMaterial's metallic factor in its *R* component.
-
- The *R* component multiplies by the {{#crossLink "MetallicMaterial/metallic:property"}}{{/crossLink}} property.
-
- @property metallicMap
- @default undefined
- @type {Texture}
- @final
- */
- metallicMap: {
- get: function () {
- return this._attached.metallicMap;
- }
- },
-
- /**
- Factor in the range [0..1] indicating the roughness of this MetallicMaterial.
-
- 0 is fully smooth, 1 is fully rough.
-
- Multiplies by the *R* component of {{#crossLink "MetallicMaterial/roughnessMap:property"}}{{/crossLink}}.
-
- @property roughness
- @default 1.0
- @type Number
- */
- roughness: {
- set: function (value) {
- value = (value !== undefined && value !== null) ? value : 1.0;
- if (this._state.roughness === value) {
- return;
- }
- this._state.roughness = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.roughness;
- }
- },
-
- /**
- RGB {{#crossLink "Texture"}}{{/crossLink}} containing this MetallicMaterial's roughness factor in its *R* component.
-
- The *R* component multiplies by the {{#crossLink "MetallicMaterial/roughness:property"}}{{/crossLink}} property.
-
- Must be within the same {{#crossLink "Scene"}}Scene{{/crossLink}} as this MetallicMaterial.
-
- @property roughnessMap
- @default undefined
- @type {Texture}
- @final
- */
- roughnessMap: {
- get: function () {
- return this._attached.roughnessMap;
- }
- },
-
- /**
- RGB {{#crossLink "Texture"}}{{/crossLink}} containing this MetallicMaterial's metalness in its *R* component and roughness in its *G* component.
-
- Its *B* component multiplies by the {{#crossLink "MetallicMaterial/metallic:property"}}{{/crossLink}} property, while
- its *G* component multiplies by the {{#crossLink "MetallicMaterial/roughness:property"}}{{/crossLink}} property.
-
- Must be within the same {{#crossLink "Scene"}}Scene{{/crossLink}} as this MetallicMaterial.
-
- @property metallicRoughnessMap
- @default undefined
- @type {Texture}
- @final
- */
- metallicRoughnessMap: {
- get: function () {
- return this._attached.metallicRoughnessMap;
- }
- },
-
- /**
- Factor in the range [0..1] indicating specular Fresnel value.
-
- @property specularF0
- @default 0.0
- @type Number
- */
- specularF0: {
- set: function (value) {
- value = (value !== undefined && value !== null) ? value : 0.0;
- if (this._state.specularF0 === value) {
- return;
- }
- this._state.specularF0 = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.specularF0;
- }
- },
-
- /**
- RGB emissive color.
-
- Multiplies by {{#crossLink "MetallicMaterial/emissiveMap:property"}}{{/crossLink}}.
-
- @property emissive
- @default [0.0, 0.0, 0.0]
- @type Float32Array
- */
- emissive: {
- set: function (value) {
- let emissive = this._state.emissive;
- if (!emissive) {
- emissive = this._state.emissive = new Float32Array(3);
- } else if (value && emissive[0] === value[0] && emissive[1] === value[1] && emissive[2] === value[2]) {
- return;
- }
- if (value) {
- emissive[0] = value[0];
- emissive[1] = value[1];
- emissive[2] = value[2];
- } else {
- emissive[0] = 0;
- emissive[1] = 0;
- emissive[2] = 0;
- }
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.emissive;
- }
- },
-
- /**
- RGB emissive map.
-
- Multiplies by {{#crossLink "MetallicMaterial/emissive:property"}}{{/crossLink}}.
-
- @property emissiveMap
- @default undefined
- @type {Texture}
- @final
- */
- emissiveMap: {
- get: function () {
- return this._attached.emissiveMap;
- }
- },
-
- /**
- RGB ambient occlusion map.
-
- Within objectRenderers, multiplies by the specular and diffuse light reflected by surfaces.
-
- @property occlusionMap
- @default undefined
- @type {Texture}
- @final
- */
- occlusionMap: {
- get: function () {
- return this._attached.occlusionMap;
- }
- },
-
- /**
- Factor in the range [0..1] indicating the alpha value.
-
- Multiplies by the *R* component of {{#crossLink "MetallicMaterial/alphaMap:property"}}{{/crossLink}} and
- the *A* component, if present, of {{#crossLink "MetallicMaterial/baseColorMap:property"}}{{/crossLink}}.
-
- The value of {{#crossLink "MetallicMaterial/alphaMode:property"}}{{/crossLink}} indicates how alpha is
- interpreted when rendering.
-
- @property alpha
- @default 1.0
- @type Number
- */
- alpha: {
- set: function (value) {
- value = (value !== undefined && value !== null) ? value : 1.0;
- if (this._state.alpha === value) {
- return;
- }
- this._state.alpha = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.alpha;
- }
- },
-
- /**
- RGB {{#crossLink "Texture"}}{{/crossLink}} containing this MetallicMaterial's alpha in its *R* component.
-
- The *R* component multiplies by the {{#crossLink "MetallicMaterial/alpha:property"}}{{/crossLink}} property.
-
- @property alphaMap
- @default undefined
- @type {Texture}
- @final
- */
- alphaMap: {
- get: function () {
- return this._attached.alphaMap;
- }
- },
-
- /**
- RGB tangent-space normal map {{#crossLink "Texture"}}{{/crossLink}}.
-
- Must be within the same {{#crossLink "Scene"}}Scene{{/crossLink}} as this MetallicMaterial.
-
- @property normalMap
- @default undefined
- @type {Texture}
- @final
- */
- normalMap: {
- get: function () {
- return this._attached.normalMap;
- }
- },
-
- /**
- The alpha rendering mode.
-
- This specifies how alpha is interpreted. Alpha is the combined result of the
- {{#crossLink "MetallicMaterial/alpha:property"}}{{/crossLink}} and
- {{#crossLink "MetallicMaterial/alphaMap:property"}}{{/crossLink}} properties.
-
- * "opaque" - The alpha value is ignored and the rendered output is fully opaque.
- * "mask" - The rendered output is either fully opaque or fully transparent depending on the alpha and {{#crossLink "MetallicMaterial/alphaCutoff:property"}}{{/crossLink}}.
- * "blend" - The alpha value is used to composite the source and destination areas. The rendered output is combined with the background using the normal painting operation (i.e. the Porter and Duff over operator).
-
- @property alphaMode
- @default "opaque"
- @type {String}
- */
- alphaMode: (function () {
- const modes = {"opaque": 0, "mask": 1, "blend": 2};
- const modeNames = ["opaque", "mask", "blend"];
- return {
- set: function (alphaMode) {
- alphaMode = alphaMode || "opaque";
- let value = modes[alphaMode];
- if (value === undefined) {
- this.error("Unsupported value for 'alphaMode': " + alphaMode + " defaulting to 'opaque'");
- value = "opaque";
- }
- if (this._state.alphaMode === value) {
- return;
- }
- this._state.alphaMode = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return modeNames[this._state.alphaMode];
- }
- };
- })(),
-
- /**
- The alpha cutoff value.
-
- Specifies the cutoff threshold when {{#crossLink "MetallicMaterial/alphaMode:property"}}{{/crossLink}}
- equals "mask". If the alpha is greater than or equal to this value then it is rendered as fully
- opaque, otherwise, it is rendered as fully transparent. A value greater than 1.0 will render the entire
- material as fully transparent. This value is ignored for other modes.
-
- Alpha is the combined result of the
- {{#crossLink "MetallicMaterial/alpha:property"}}{{/crossLink}} and
- {{#crossLink "MetallicMaterial/alphaMap:property"}}{{/crossLink}} properties.
-
- @property alphaCutoff
- @default 0.5
- @type {Number}
- */
- alphaCutoff: {
- set: function (alphaCutoff) {
- if (alphaCutoff === null || alphaCutoff === undefined) {
- alphaCutoff = 0.5;
- }
- if (this._state.alphaCutoff === alphaCutoff) {
- return;
- }
- this._state.alphaCutoff = alphaCutoff;
- },
- get: function () {
- return this._state.alphaCutoff;
- }
- },
-
- /**
- Whether backfaces are visible on attached {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
-
- The backfaces will belong to {{#crossLink "Geometry"}}{{/crossLink}} compoents that are also attached to
- the {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
-
- @property backfaces
- @default false
- @type Boolean
- */
- backfaces: {
- set: function (value) {
- value = !!value;
- if (this._state.backfaces === value) {
- return;
- }
- this._state.backfaces = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.backfaces;
- }
- },
-
- /**
- Indicates the winding direction of front faces on attached {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
-
- The faces will belong to {{#crossLink "Geometry"}}{{/crossLink}} components that are also attached to
- the {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
-
- @property frontface
- @default "ccw"
- @type String
- */
- frontface: {
- set: function (value) {
- value = value !== "cw";
- if (this._state.frontface === value) {
- return;
- }
- this._state.frontface = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.frontface ? "ccw" : "cw";
- }
- },
-
- /**
- The MetallicMaterial's line width.
-
- @property lineWidth
- @default 1.0
- @type Number
- */
- lineWidth: {
- set: function (value) {
- this._state.lineWidth = value || 1.0;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.lineWidth;
- }
- },
-
- /**
- The MetallicMaterial's point size.
-
- @property pointSize
- @default 1.0
- @type Number
- */
- pointSize: {
- set: function (value) {
- this._state.pointSize = value || 1.0;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.pointSize;
- }
+ }
+ if (this._normalMap) {
+ hash.push("/nm");
+ if (this._normalMap._state.hasMatrix) {
+ hash.push("/mat");
}
- },
-
- _destroy: function () {
- this._super();
- this._state.destroy();
}
- });
+ hash.push(";");
+ state.hash = hash.join("");
+ }
+
+
+ /**
+ RGB diffuse color.
+
+ Multiplies by the RGB components of {{#crossLink "MetallicMaterial/baseColorMap:property"}}{{/crossLink}}.
+
+ @property baseColor
+ @default [1.0, 1.0, 1.0]
+ @type Float32Array
+ */
+ set baseColor(value) {
+ let baseColor = this._state.baseColor;
+ if (!baseColor) {
+ baseColor = this._state.baseColor = new Float32Array(3);
+ } else if (value && baseColor[0] === value[0] && baseColor[1] === value[1] && baseColor[2] === value[2]) {
+ return;
+ }
+ if (value) {
+ baseColor[0] = value[0];
+ baseColor[1] = value[1];
+ baseColor[2] = value[2];
+ } else {
+ baseColor[0] = 1;
+ baseColor[1] = 1;
+ baseColor[2] = 1;
+ }
+ this._renderer.imageDirty();
+ }
+
+ get baseColor() {
+ return this._state.baseColor;
+ }
+
+
+ /**
+ RGB {{#crossLink "Texture"}}{{/crossLink}} containing the diffuse color of this MetallicMaterial, with optional *A* component for alpha.
+
+ The RGB components multiply by the {{#crossLink "MetallicMaterial/baseColor:property"}}{{/crossLink}} property,
+ while the *A* component, if present, multiplies by the {{#crossLink "MetallicMaterial/alpha:property"}}{{/crossLink}} property.
+
+ @property baseColorMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get baseColorMap() {
+ return this._baseColorMap;
+ }
+
+ /**
+ Factor in the range [0..1] indicating how metallic this MetallicMaterial is.
+
+ 1 is metal, 0 is non-metal.
+
+ Multiplies by the *R* component of {{#crossLink "MetallicMaterial/metallicMap:property"}}{{/crossLink}}
+ and the *A* component of {{#crossLink "MetallicMaterial/metalRoughnessMap:property"}}{{/crossLink}}.
-})();
\ No newline at end of file
+ @property metallic
+ @default 1.0
+ @type Number
+ */
+ set metallic(value) {
+ value = (value !== undefined && value !== null) ? value : 1.0;
+ if (this._state.metallic === value) {
+ return;
+ }
+ this._state.metallic = value;
+ this._renderer.imageDirty();
+ }
+
+ get metallic() {
+ return this._state.metallic;
+ }
+
+ /**
+ RGB {{#crossLink "Texture"}}{{/crossLink}} containing this MetallicMaterial's metallic factor in its *R* component.
+
+ The *R* component multiplies by the {{#crossLink "MetallicMaterial/metallic:property"}}{{/crossLink}} property.
+
+ @property metallicMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get metallicMap() {
+ return this._attached.metallicMap;
+ }
+
+ /**
+ Factor in the range [0..1] indicating the roughness of this MetallicMaterial.
+
+ 0 is fully smooth, 1 is fully rough.
+
+ Multiplies by the *R* component of {{#crossLink "MetallicMaterial/roughnessMap:property"}}{{/crossLink}}.
+
+ @property roughness
+ @default 1.0
+ @type Number
+ */
+ set roughness(value) {
+ value = (value !== undefined && value !== null) ? value : 1.0;
+ if (this._state.roughness === value) {
+ return;
+ }
+ this._state.roughness = value;
+ this._renderer.imageDirty();
+ }
+
+ get roughness() {
+ return this._state.roughness;
+ }
+
+ /**
+ RGB {{#crossLink "Texture"}}{{/crossLink}} containing this MetallicMaterial's roughness factor in its *R* component.
+
+ The *R* component multiplies by the {{#crossLink "MetallicMaterial/roughness:property"}}{{/crossLink}} property.
+
+ Must be within the same {{#crossLink "Scene"}}Scene{{/crossLink}} as this MetallicMaterial.
+
+ @property roughnessMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get roughnessMap() {
+ return this._attached.roughnessMap;
+ }
+
+ /**
+ RGB {{#crossLink "Texture"}}{{/crossLink}} containing this MetallicMaterial's metalness in its *R* component and roughness in its *G* component.
+
+ Its *B* component multiplies by the {{#crossLink "MetallicMaterial/metallic:property"}}{{/crossLink}} property, while
+ its *G* component multiplies by the {{#crossLink "MetallicMaterial/roughness:property"}}{{/crossLink}} property.
+
+ Must be within the same {{#crossLink "Scene"}}Scene{{/crossLink}} as this MetallicMaterial.
+
+ @property metallicRoughnessMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get metallicRoughnessMap() {
+ return this._attached.metallicRoughnessMap;
+ }
+
+ /**
+ Factor in the range [0..1] indicating specular Fresnel value.
+
+ @property specularF0
+ @default 0.0
+ @type Number
+ */
+ set specularF0(value) {
+ value = (value !== undefined && value !== null) ? value : 0.0;
+ if (this._state.specularF0 === value) {
+ return;
+ }
+ this._state.specularF0 = value;
+ this._renderer.imageDirty();
+ }
+
+ get specularF0() {
+ return this._state.specularF0;
+ }
+
+ /**
+ RGB emissive color.
+
+ Multiplies by {{#crossLink "MetallicMaterial/emissiveMap:property"}}{{/crossLink}}.
+
+ @property emissive
+ @default [0.0, 0.0, 0.0]
+ @type Float32Array
+ */
+ set emissive(value) {
+ let emissive = this._state.emissive;
+ if (!emissive) {
+ emissive = this._state.emissive = new Float32Array(3);
+ } else if (value && emissive[0] === value[0] && emissive[1] === value[1] && emissive[2] === value[2]) {
+ return;
+ }
+ if (value) {
+ emissive[0] = value[0];
+ emissive[1] = value[1];
+ emissive[2] = value[2];
+ } else {
+ emissive[0] = 0;
+ emissive[1] = 0;
+ emissive[2] = 0;
+ }
+ this._renderer.imageDirty();
+ }
+
+ get emissive() {
+ return this._state.emissive;
+ }
+
+ /**
+ RGB emissive map.
+
+ Multiplies by {{#crossLink "MetallicMaterial/emissive:property"}}{{/crossLink}}.
+
+ @property emissiveMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get emissiveMap() {
+ return this._attached.emissiveMap;
+ }
+
+ /**
+ RGB ambient occlusion map.
+
+ Within objectRenderers, multiplies by the specular and diffuse light reflected by surfaces.
+
+ @property occlusionMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get occlusionMap() {
+ return this._attached.occlusionMap;
+ }
+
+ /**
+ Factor in the range [0..1] indicating the alpha value.
+
+ Multiplies by the *R* component of {{#crossLink "MetallicMaterial/alphaMap:property"}}{{/crossLink}} and
+ the *A* component, if present, of {{#crossLink "MetallicMaterial/baseColorMap:property"}}{{/crossLink}}.
+
+ The value of {{#crossLink "MetallicMaterial/alphaMode:property"}}{{/crossLink}} indicates how alpha is
+ interpreted when rendering.
+
+ @property alpha
+ @default 1.0
+ @type Number
+ */
+ set alpha(value) {
+ value = (value !== undefined && value !== null) ? value : 1.0;
+ if (this._state.alpha === value) {
+ return;
+ }
+ this._state.alpha = value;
+ this._renderer.imageDirty();
+ }
+
+ get alpha() {
+ return this._state.alpha;
+ }
+
+ /**
+ RGB {{#crossLink "Texture"}}{{/crossLink}} containing this MetallicMaterial's alpha in its *R* component.
+
+ The *R* component multiplies by the {{#crossLink "MetallicMaterial/alpha:property"}}{{/crossLink}} property.
+
+ @property alphaMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get alphaMap() {
+ return this._attached.alphaMap;
+ }
+
+ /**
+ RGB tangent-space normal map {{#crossLink "Texture"}}{{/crossLink}}.
+
+ Must be within the same {{#crossLink "Scene"}}Scene{{/crossLink}} as this MetallicMaterial.
+
+ @property normalMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get normalMap() {
+ return this._attached.normalMap;
+ }
+
+ /**
+ The alpha rendering mode.
+
+ This specifies how alpha is interpreted. Alpha is the combined result of the
+ {{#crossLink "MetallicMaterial/alpha:property"}}{{/crossLink}} and
+ {{#crossLink "MetallicMaterial/alphaMap:property"}}{{/crossLink}} properties.
+
+ * "opaque" - The alpha value is ignored and the rendered output is fully opaque.
+ * "mask" - The rendered output is either fully opaque or fully transparent depending on the alpha and {{#crossLink "MetallicMaterial/alphaCutoff:property"}}{{/crossLink}}.
+ * "blend" - The alpha value is used to composite the source and destination areas. The rendered output is combined with the background using the normal painting operation (i.e. the Porter and Duff over operator).
+
+ @property alphaMode
+ @default "opaque"
+ @type {String}
+ */
+
+ set alphaMode(alphaMode) {
+ alphaMode = alphaMode || "opaque";
+ let value = modes[alphaMode];
+ if (value === undefined) {
+ this.error("Unsupported value for 'alphaMode': " + alphaMode + " defaulting to 'opaque'");
+ value = "opaque";
+ }
+ if (this._state.alphaMode === value) {
+ return;
+ }
+ this._state.alphaMode = value;
+ this._renderer.imageDirty();
+ }
+
+ get alphaMode() {
+ return modeNames[this._state.alphaMode];
+ }
+
+ /**
+ The alpha cutoff value.
+
+ Specifies the cutoff threshold when {{#crossLink "MetallicMaterial/alphaMode:property"}}{{/crossLink}}
+ equals "mask". If the alpha is greater than or equal to this value then it is rendered as fully
+ opaque, otherwise, it is rendered as fully transparent. A value greater than 1.0 will render the entire
+ material as fully transparent. This value is ignored for other modes.
+
+ Alpha is the combined result of the
+ {{#crossLink "MetallicMaterial/alpha:property"}}{{/crossLink}} and
+ {{#crossLink "MetallicMaterial/alphaMap:property"}}{{/crossLink}} properties.
+
+ @property alphaCutoff
+ @default 0.5
+ @type {Number}
+ */
+ set alphaCutoff(alphaCutoff) {
+ if (alphaCutoff === null || alphaCutoff === undefined) {
+ alphaCutoff = 0.5;
+ }
+ if (this._state.alphaCutoff === alphaCutoff) {
+ return;
+ }
+ this._state.alphaCutoff = alphaCutoff;
+ }
+
+ get alphaCutoff() {
+ return this._state.alphaCutoff;
+ }
+
+ /**
+ Whether backfaces are visible on attached {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
+
+ The backfaces will belong to {{#crossLink "Geometry"}}{{/crossLink}} compoents that are also attached to
+ the {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
+
+ @property backfaces
+ @default false
+ @type Boolean
+ */
+ set backfaces(value) {
+ value = !!value;
+ if (this._state.backfaces === value) {
+ return;
+ }
+ this._state.backfaces = value;
+ this._renderer.imageDirty();
+ }
+
+ get backfaces() {
+ return this._state.backfaces;
+ }
+
+ /**
+ Indicates the winding direction of front faces on attached {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
+
+ The faces will belong to {{#crossLink "Geometry"}}{{/crossLink}} components that are also attached to
+ the {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
+
+ @property frontface
+ @default "ccw"
+ @type String
+ */
+ set frontface(value) {
+ value = value !== "cw";
+ if (this._state.frontface === value) {
+ return;
+ }
+ this._state.frontface = value;
+ this._renderer.imageDirty();
+ }
+
+ get frontface() {
+ return this._state.frontface ? "ccw" : "cw";
+ }
+
+ /**
+ The MetallicMaterial's line width.
+
+ @property lineWidth
+ @default 1.0
+ @type Number
+ */
+ set lineWidth(value) {
+ this._state.lineWidth = value || 1.0;
+ this._renderer.imageDirty();
+ }
+
+ get lineWidth() {
+ return this._state.lineWidth;
+ }
+
+ /**
+ The MetallicMaterial's point size.
+
+ @property pointSize
+ @default 1.0
+ @type Number
+ */
+ set pointSize(value) {
+ this._state.pointSize = value || 1.0;
+ this._renderer.imageDirty();
+ }
+
+ get pointSize() {
+ return this._state.pointSize;
+ }
+
+ destroy() {
+ super.destroy();
+ this._state.destroy();
+ }
+}
+
+export{MetallicMaterial};
\ No newline at end of file
diff --git a/src/materials/outlineMaterial.js b/src/materials/outlineMaterial.js
index aae1e616e..dbead040d 100644
--- a/src/materials/outlineMaterial.js
+++ b/src/materials/outlineMaterial.js
@@ -9,8 +9,7 @@
@submodule materials
@constructor
@extends Material
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}}, creates this OutlineMaterial within the
- default {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted
+ @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] {*} The OutlineMaterial configuration
@param [cfg.id] {String} Optional ID, unique among all components in the parent {{#crossLink "Scene"}}Scene{{/crossLink}}, generated automatically when omitted.
@param [cfg.meta=null] {String:Object} Metadata to attach to this OutlineMaterial.
@@ -18,105 +17,112 @@
@param [cfg.alpha=1.0] {Number} Outline opacity. A value of 0.0 indicates fully transparent, 1.0 is fully opaque.
@param [cfg.width=4] {Number} Outline width, in pixels.
*/
-(function () {
-
- "use strict";
-
- xeogl.OutlineMaterial = xeogl.Material.extend({
-
- type: "xeogl.OutlineMaterial",
-
- _init: function (cfg) {
- this._super(cfg);
- this._state = new xeogl.renderer.State({
- type: "OutlineMaterial",
- color: null,
- alpha: null,
- width: null
- });
- this.color = cfg.color;
- this.alpha = cfg.alpha;
- this.width = cfg.width;
- },
-
- _props: {
-
- /**
- RGB outline color.
-
- @property color
- @default [1.0, 0.2, 0.2]
- @type Float32Array
- */
- color: {
- set: function (value) {
- let color = this._state.color;
- if (!color) {
- color = this._state.color = new Float32Array(3);
- } else if (value && color[0] === value[0] && color[1] === value[1] && color[2] === value[2]) {
- return;
- }
- if (value) {
- color[0] = value[0];
- color[1] = value[1];
- color[2] = value[2];
- } else {
- color[0] = 1.0;
- color[1] = 0.2;
- color[2] = 0.2;
- }
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.color;
- }
- },
-
- /**
- Outline transparency.
-
- A value of 0.0 indicates fully transparent, 1.0 is fully opaque.
-
- @property alpha
- @default 1.0
- @type Number
- */
- alpha: {
- set: function (value) {
- value = (value !== undefined && value !== null) ? value : 1.0;
- if (this._state.alpha === value) {
- return;
- }
- this._state.alpha = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.alpha;
- }
- },
-
- /**
- Outline width in pixels.
-
- @property width
- @default 4.0
- @type Number
- */
- width: {
- set: function (value) {
- this._state.width = value || 4.0;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.width;
- }
- }
- },
-
- _destroy: function () {
- this._super();
- this._state.destroy();
+import {core} from "./../core.js";
+import {Material} from './material.js';
+import {State} from '../renderer/state.js';
+
+class OutlineMaterial extends Material {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return "xeogl.OutlineMaterial";
+ }
+
+ init(cfg) {
+
+ super.init(cfg);
+
+ this._state = new State({
+ type: "OutlineMaterial",
+ color: null,
+ alpha: null,
+ width: null
+ });
+ this.color = cfg.color;
+ this.alpha = cfg.alpha;
+ this.width = cfg.width;
+ }
+
+ /**
+ RGB outline color.
+
+ @property color
+ @default [1.0, 0.2, 0.2]
+ @type Float32Array
+ */
+ set color(value) {
+ let color = this._state.color;
+ if (!color) {
+ color = this._state.color = new Float32Array(3);
+ } else if (value && color[0] === value[0] && color[1] === value[1] && color[2] === value[2]) {
+ return;
+ }
+ if (value) {
+ color[0] = value[0];
+ color[1] = value[1];
+ color[2] = value[2];
+ } else {
+ color[0] = 1.0;
+ color[1] = 0.2;
+ color[2] = 0.2;
}
- });
+ this._renderer.imageDirty();
+ }
+
+ get color() {
+ return this._state.color;
+ }
+
+ /**
+ Outline transparency.
+
+ A value of 0.0 indicates fully transparent, 1.0 is fully opaque.
+
+ @property alpha
+ @default 1.0
+ @type Number
+ */
+ set alpha(value) {
+ value = (value !== undefined && value !== null) ? value : 1.0;
+ if (this._state.alpha === value) {
+ return;
+ }
+ this._state.alpha = value;
+ this._renderer.imageDirty();
+ }
+
+ get alpha() {
+ return this._state.alpha;
+ }
+
+ /**
+ Outline width in pixels.
+
+ @property width
+ @default 4.0
+ @type Number
+ */
+ set width(value) {
+ this._state.width = value || 4.0;
+ this._renderer.imageDirty();
+ }
+
+ get width() {
+ return this._state.width;
+ }
+
+ destroy() {
+ super.destroy();
+ this._state.destroy();
+ }
+}
-})();
\ No newline at end of file
+export{OutlineMaterial};
\ No newline at end of file
diff --git a/src/materials/phongMaterial.js b/src/materials/phongMaterial.js
index 8355344e8..789771242 100644
--- a/src/materials/phongMaterial.js
+++ b/src/materials/phongMaterial.js
@@ -124,8 +124,7 @@
@submodule materials
@constructor
@extends Material
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}}, creates this PhongMaterial within the
- default {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted
+ @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] {*} The PhongMaterial configuration
@param [cfg.id] {String} Optional ID, unique among all components in the parent {{#crossLink "Scene"}}Scene{{/crossLink}}, generated automatically when omitted.
@param [cfg.meta=null] {String:Object} Metadata to attach to this PhongMaterial.
@@ -157,727 +156,698 @@
@param [cfg.backfaces=false] {Boolean} Whether to render {{#crossLink "Geometry"}}Geometry{{/crossLink}} backfaces.
@param [cfg.frontface="ccw"] {Boolean} The winding order for {{#crossLink "Geometry"}}Geometry{{/crossLink}} front faces - "cw" for clockwise, or "ccw" for counter-clockwise.
*/
-(function () {
-
- "use strict";
-
- xeogl.PhongMaterial = xeogl.Material.extend({
-
- type: "xeogl.PhongMaterial",
-
- _init: function (cfg) {
-
- this._super(cfg);
-
- this._state = new xeogl.renderer.State({
- type: "PhongMaterial",
- ambient: xeogl.math.vec3([1.0, 1.0, 1.0]),
- diffuse: xeogl.math.vec3([1.0, 1.0, 1.0]),
- specular: xeogl.math.vec3([1.0, 1.0, 1.0]),
- emissive: xeogl.math.vec3([0.0, 0.0, 0.0]),
- alpha: null,
- shininess: null,
- reflectivity: null,
- alphaMode: null,
- alphaCutoff: null,
- lineWidth: null,
- pointSize: null,
- backfaces: null,
- frontface: null, // Boolean for speed; true == "ccw", false == "cw"
- hash: null
- });
-
- this.ambient = cfg.ambient;
- this.diffuse = cfg.diffuse;
- this.specular = cfg.specular;
- this.emissive = cfg.emissive;
- this.alpha = cfg.alpha;
- this.shininess = cfg.shininess;
- this.reflectivity = cfg.reflectivity;
- this.lineWidth = cfg.lineWidth;
- this.pointSize = cfg.pointSize;
-
- if (cfg.ambientMap) {
- this._ambientMap = this._checkComponent("xeogl.Texture", cfg.ambientMap);
- }
- if (cfg.diffuseMap) {
- this._diffuseMap = this._checkComponent("xeogl.Texture", cfg.diffuseMap);
- }
- if (cfg.specularMap) {
- this._specularMap = this._checkComponent("xeogl.Texture", cfg.specularMap);
- }
- if (cfg.emissiveMap) {
- this._emissiveMap = this._checkComponent("xeogl.Texture", cfg.emissiveMap);
- }
- if (cfg.alphaMap) {
- this._alphaMap = this._checkComponent("xeogl.Texture", cfg.alphaMap);
- }
- if (cfg.reflectivityMap) {
- this._reflectivityMap = this._checkComponent("xeogl.Texture", cfg.reflectivityMap);
- }
- if (cfg.normalMap) {
- this._normalMap = this._checkComponent("xeogl.Texture", cfg.normalMap);
- }
- if (cfg.occlusionMap) {
- this._occlusionMap = this._checkComponent("xeogl.Texture", cfg.occlusionMap);
- }
- if (cfg.diffuseFresnel) {
- this._diffuseFresnel = this._checkComponent("xeogl.Fresnel", cfg.diffuseFresnel);
- }
- if (cfg.specularFresnel) {
- this._specularFresnel = this._checkComponent("xeogl.Fresnel", cfg.specularFresnel);
- }
- if (cfg.emissiveFresnel) {
- this._emissiveFresnel = this._checkComponent("xeogl.Fresnel", cfg.emissiveFresnel);
- }
- if (cfg.alphaFresnel) {
- this._alphaFresnel = this._checkComponent("xeogl.Fresnel", cfg.alphaFresnel);
- }
- if (cfg.reflectivityFresnel) {
- this._reflectivityFresnel = this._checkComponent("xeogl.Fresnel", cfg.reflectivityFresnel);
- }
+import {core} from "./../core.js";
+import {Material} from './material.js';
+import {State} from '../renderer/state.js';
+import {math} from '../math/math.js';
+
+const alphaModes = {"opaque": 0, "mask": 1, "blend": 2};
+const alphaModeNames = ["opaque", "mask", "blend"];
+
+class PhongMaterial extends Material {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return "xeogl.PhongMaterial";
+ }
+
+ init(cfg) {
+
+ super.init(cfg);
+
+ this._state = new State({
+ type: "PhongMaterial",
+ ambient: math.vec3([1.0, 1.0, 1.0]),
+ diffuse: math.vec3([1.0, 1.0, 1.0]),
+ specular: math.vec3([1.0, 1.0, 1.0]),
+ emissive: math.vec3([0.0, 0.0, 0.0]),
+ alpha: null,
+ shininess: null,
+ reflectivity: null,
+ alphaMode: null,
+ alphaCutoff: null,
+ lineWidth: null,
+ pointSize: null,
+ backfaces: null,
+ frontface: null, // Boolean for speed; true == "ccw", false == "cw"
+ hash: null
+ });
+
+ this.ambient = cfg.ambient;
+ this.diffuse = cfg.diffuse;
+ this.specular = cfg.specular;
+ this.emissive = cfg.emissive;
+ this.alpha = cfg.alpha;
+ this.shininess = cfg.shininess;
+ this.reflectivity = cfg.reflectivity;
+ this.lineWidth = cfg.lineWidth;
+ this.pointSize = cfg.pointSize;
+
+ if (cfg.ambientMap) {
+ this._ambientMap = this._checkComponent("xeogl.Texture", cfg.ambientMap);
+ }
+ if (cfg.diffuseMap) {
+ this._diffuseMap = this._checkComponent("xeogl.Texture", cfg.diffuseMap);
+ }
+ if (cfg.specularMap) {
+ this._specularMap = this._checkComponent("xeogl.Texture", cfg.specularMap);
+ }
+ if (cfg.emissiveMap) {
+ this._emissiveMap = this._checkComponent("xeogl.Texture", cfg.emissiveMap);
+ }
+ if (cfg.alphaMap) {
+ this._alphaMap = this._checkComponent("xeogl.Texture", cfg.alphaMap);
+ }
+ if (cfg.reflectivityMap) {
+ this._reflectivityMap = this._checkComponent("xeogl.Texture", cfg.reflectivityMap);
+ }
+ if (cfg.normalMap) {
+ this._normalMap = this._checkComponent("xeogl.Texture", cfg.normalMap);
+ }
+ if (cfg.occlusionMap) {
+ this._occlusionMap = this._checkComponent("xeogl.Texture", cfg.occlusionMap);
+ }
+ if (cfg.diffuseFresnel) {
+ this._diffuseFresnel = this._checkComponent("xeogl.Fresnel", cfg.diffuseFresnel);
+ }
+ if (cfg.specularFresnel) {
+ this._specularFresnel = this._checkComponent("xeogl.Fresnel", cfg.specularFresnel);
+ }
+ if (cfg.emissiveFresnel) {
+ this._emissiveFresnel = this._checkComponent("xeogl.Fresnel", cfg.emissiveFresnel);
+ }
+ if (cfg.alphaFresnel) {
+ this._alphaFresnel = this._checkComponent("xeogl.Fresnel", cfg.alphaFresnel);
+ }
+ if (cfg.reflectivityFresnel) {
+ this._reflectivityFresnel = this._checkComponent("xeogl.Fresnel", cfg.reflectivityFresnel);
+ }
- this.alphaMode = cfg.alphaMode;
- this.alphaCutoff = cfg.alphaCutoff;
- this.backfaces = cfg.backfaces;
- this.frontface = cfg.frontface;
-
- this._makeHash();
- },
-
- _makeHash: function () {
- const state = this._state;
- const hash = ["/p"]; // 'P' for Phong
- if (this._normalMap) {
- hash.push("/nm");
- if (this._normalMap.hasMatrix) {
- hash.push("/mat");
- }
- }
- if (this._ambientMap) {
- hash.push("/am");
- if (this._ambientMap.hasMatrix) {
- hash.push("/mat");
- }
- hash.push("/" + this._ambientMap.encoding);
- }
- if (this._diffuseMap) {
- hash.push("/dm");
- if (this._diffuseMap.hasMatrix) {
- hash.push("/mat");
- }
- hash.push("/" + this._diffuseMap.encoding);
- }
- if (this._specularMap) {
- hash.push("/sm");
- if (this._specularMap.hasMatrix) {
- hash.push("/mat");
- }
+ this.alphaMode = cfg.alphaMode;
+ this.alphaCutoff = cfg.alphaCutoff;
+ this.backfaces = cfg.backfaces;
+ this.frontface = cfg.frontface;
+
+ this._makeHash();
+ }
+
+ _makeHash() {
+ const state = this._state;
+ const hash = ["/p"]; // 'P' for Phong
+ if (this._normalMap) {
+ hash.push("/nm");
+ if (this._normalMap.hasMatrix) {
+ hash.push("/mat");
}
- if (this._emissiveMap) {
- hash.push("/em");
- if (this._emissiveMap.hasMatrix) {
- hash.push("/mat");
- }
- hash.push("/" + this._emissiveMap.encoding);
- }
- if (this._alphaMap) {
- hash.push("/opm");
- if (this._alphaMap.hasMatrix) {
- hash.push("/mat");
- }
- }
- if (this._reflectivityMap) {
- hash.push("/rm");
- if (this._reflectivityMap.hasMatrix) {
- hash.push("/mat");
- }
- }
- if (this._occlusionMap) {
- hash.push("/ocm");
- if (this._occlusionMap.hasMatrix) {
- hash.push("/mat");
- }
+ }
+ if (this._ambientMap) {
+ hash.push("/am");
+ if (this._ambientMap.hasMatrix) {
+ hash.push("/mat");
}
- if (this._diffuseFresnel) {
- hash.push("/df");
+ hash.push("/" + this._ambientMap.encoding);
+ }
+ if (this._diffuseMap) {
+ hash.push("/dm");
+ if (this._diffuseMap.hasMatrix) {
+ hash.push("/mat");
}
- if (this._specularFresnel) {
- hash.push("/sf");
+ hash.push("/" + this._diffuseMap.encoding);
+ }
+ if (this._specularMap) {
+ hash.push("/sm");
+ if (this._specularMap.hasMatrix) {
+ hash.push("/mat");
}
- if (this._emissiveFresnel) {
- hash.push("/ef");
+ }
+ if (this._emissiveMap) {
+ hash.push("/em");
+ if (this._emissiveMap.hasMatrix) {
+ hash.push("/mat");
}
- if (this._alphaFresnel) {
- hash.push("/of");
+ hash.push("/" + this._emissiveMap.encoding);
+ }
+ if (this._alphaMap) {
+ hash.push("/opm");
+ if (this._alphaMap.hasMatrix) {
+ hash.push("/mat");
}
- if (this._reflectivityFresnel) {
- hash.push("/rf");
+ }
+ if (this._reflectivityMap) {
+ hash.push("/rm");
+ if (this._reflectivityMap.hasMatrix) {
+ hash.push("/mat");
}
- hash.push(";");
- state.hash = hash.join("");
- },
-
- _props: {
-
- /**
- The PhongMaterial's ambient color.
-
- @property ambient
- @default [0.3, 0.3, 0.3]
- @type Float32Array
- */
- ambient: {
- set: function (value) {
- let ambient = this._state.ambient;
- if (!ambient) {
- ambient = this._state.ambient = new Float32Array(3);
- } else if (value && ambient[0] === value[0] && ambient[1] === value[1] && ambient[2] === value[2]) {
- return;
- }
- if (value) {
- ambient[0] = value[0];
- ambient[1] = value[1];
- ambient[2] = value[2];
- } else {
- ambient[0] = .2;
- ambient[1] = .2;
- ambient[2] = .2;
- }
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.ambient;
- }
- },
-
- /**
- The PhongMaterial's diffuse color.
-
- Multiplies by {{#crossLink "PhongMaterial/diffuseMap:property"}}{{/crossLink}}.
-
- @property diffuse
- @default [1.0, 1.0, 1.0]
- @type Float32Array
- */
- diffuse: {
- set: function (value) {
- let diffuse = this._state.diffuse;
- if (!diffuse) {
- diffuse = this._state.diffuse = new Float32Array(3);
- } else if (value && diffuse[0] === value[0] && diffuse[1] === value[1] && diffuse[2] === value[2]) {
- return;
- }
- if (value) {
- diffuse[0] = value[0];
- diffuse[1] = value[1];
- diffuse[2] = value[2];
- } else {
- diffuse[0] = 1;
- diffuse[1] = 1;
- diffuse[2] = 1;
- }
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.diffuse;
- }
- },
-
- /**
- The material's specular color.
-
- Multiplies by {{#crossLink "PhongMaterial/specularMap:property"}}{{/crossLink}}.
-
- @property specular
- @default [1.0, 1.0, 1.0]
- @type Float32Array
- */
- specular: {
- set: function (value) {
- let specular = this._state.specular;
- if (!specular) {
- specular = this._state.specular = new Float32Array(3);
- } else if (value && specular[0] === value[0] && specular[1] === value[1] && specular[2] === value[2]) {
- return;
- }
- if (value) {
- specular[0] = value[0];
- specular[1] = value[1];
- specular[2] = value[2];
- } else {
- specular[0] = 1;
- specular[1] = 1;
- specular[2] = 1;
- }
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.specular;
- }
- },
-
- /**
- The PhongMaterial's emissive color.
-
- Multiplies by {{#crossLink "PhongMaterial/emissiveMap:property"}}{{/crossLink}}.
-
- @property emissive
- @default [0.0, 0.0, 0.0]
- @type Float32Array
- */
- emissive: {
- set: function (value) {
- let emissive = this._state.emissive;
- if (!emissive) {
- emissive = this._state.emissive = new Float32Array(3);
- } else if (value && emissive[0] === value[0] && emissive[1] === value[1] && emissive[2] === value[2]) {
- return;
- }
- if (value) {
- emissive[0] = value[0];
- emissive[1] = value[1];
- emissive[2] = value[2];
- } else {
- emissive[0] = 0;
- emissive[1] = 0;
- emissive[2] = 0;
- }
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.emissive;
- }
- },
-
- /**
- Factor in the range [0..1] indicating how transparent the PhongMaterial is.
-
- A value of 0.0 indicates fully transparent, 1.0 is fully opaque.
-
- Multiplies by {{#crossLink "PhongMaterial/alphaMap:property"}}{{/crossLink}}.
-
- @property alpha
- @default 1.0
- @type Number
- */
- alpha: {
- set: function (value) {
- value = (value !== undefined && value !== null) ? value : 1.0;
- if (this._state.alpha === value) {
- return;
- }
- this._state.alpha = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.alpha;
- }
- },
-
- /**
- A factor in range [0..128] that determines the size and sharpness of the specular highlights create by this PhongMaterial.
-
- Larger values produce smaller, sharper highlights. A value of 0.0 gives very large highlights that are almost never
- desirable. Try values close to 10 for a larger, fuzzier highlight and values of 100 or more for a small, sharp
- highlight.
-
- @property shininess
- @default 80.0
- @type Number
- */
- shininess: {
- set: function (value) {
- this._state.shininess = value !== undefined ? value : 80;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.shininess;
- }
- },
-
- /**
- The PhongMaterial's line width.
-
- @property lineWidth
- @default 1.0
- @type Number
- */
- lineWidth: {
- set: function (value) {
- this._state.lineWidth = value || 1.0;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.lineWidth;
- }
- },
-
- /**
- The PhongMaterial's point size.
-
- @property pointSize
- @default 1.0
- @type Number
- */
- pointSize: {
- set: function (value) {
- this._state.pointSize = value || 1.0;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.pointSize;
- }
- },
-
- /**
- Scalar in range 0-1 that controls how much {{#crossLink "CubeMap"}}CubeMap{{/crossLink}} is reflected by this PhongMaterial.
-
- The surface will be non-reflective when this is 0, and completely mirror-like when it is 1.0.
-
- Multiplies by {{#crossLink "PhongMaterial/reflectivityMap:property"}}{{/crossLink}}.
-
- @property reflectivity
- @default 1.0
- @type Number
- */
- reflectivity: {
- set: function (value) {
- this._state.reflectivity = value !== undefined ? value : 1.0;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.reflectivity;
- }
- },
-
- /**
- Normal map.
-
- @property normalMap
- @default undefined
- @type {Texture}
- @final
- */
- normalMap: {
- get: function () {
- return this._normalMap;
- }
- },
-
- /**
- Ambient map.
-
- Multiplies by {{#crossLink "PhongMaterial/ambient:property"}}{{/crossLink}}.
-
- @property ambientMap
- @default undefined
- @type {Texture}
- @final
- */
- ambientMap: {
- get: function () {
- return this._ambientMap;
- }
- },
-
- /**
- Diffuse map.
-
- Multiplies by {{#crossLink "PhongMaterial/diffuse:property"}}{{/crossLink}}.
-
- @property diffuseMap
- @default undefined
- @type {Texture}
- @final
- */
- diffuseMap: {
- get: function () {
- return this._diffuseMap;
- }
- },
-
- /**
- Specular map.
-
- Multiplies by {{#crossLink "PhongMaterial/specular:property"}}{{/crossLink}}.
-
- @property specularMap
- @default undefined
- @type {Texture}
- @final
- */
- specularMap: {
- get: function () {
- return this._specularMap;
- }
- },
-
- /**
- Emissive map.
-
- Multiplies by {{#crossLink "PhongMaterial/emissive:property"}}{{/crossLink}}.
-
- @property emissiveMap
- @default undefined
- @type {Texture}
- @final
- */
- emissiveMap: {
- get: function () {
- return this._emissiveMap;
- }
- },
-
- /**
- Alpha map.
-
- Multiplies by {{#crossLink "PhongMaterial/alpha:property"}}{{/crossLink}}.
-
- @property alphaMap
- @default undefined
- @type {Texture}
- @final
- */
- alphaMap: {
- get: function () {
- return this._alphaMap;
- }
- },
-
- /**
- Reflectivity map.
-
- Multiplies by {{#crossLink "PhongMaterial/reflectivity:property"}}{{/crossLink}}.
-
- @property reflectivityMap
- @default undefined
- @type {Texture}
- @final
- */
- reflectivityMap: {
- get: function () {
- return this._reflectivityMap;
- }
- },
-
- /**
-
- Occlusion map.
-
- @property occlusionMap
- @default undefined
- @type {Texture}
- @final
- */
- occlusionMap: {
- get: function () {
- return this._occlusionMap;
- }
- },
-
- /**
- Diffuse Fresnel.
-
- Applies to {{#crossLink "PhongMaterial/diffuseFresnel:property"}}{{/crossLink}}.
-
- @property diffuseFresnel
- @default undefined
- @type {Fresnel}
- @final
- */
- diffuseFresnel: {
- get: function () {
- return this._diffuseFresnel;
- }
- },
-
- /**
- Specular Fresnel.
-
- Applies to {{#crossLink "PhongMaterial/specular:property"}}{{/crossLink}}.
-
- @property specularFresnel
- @default undefined
- @type {Fresnel}
- @final
- */
- specularFresnel: {
- get: function () {
- return this._specularFresnel;
- }
- },
-
- /**
- Emissive Fresnel.
-
- Applies to {{#crossLink "PhongMaterial/emissive:property"}}{{/crossLink}}.
-
- @property emissiveFresnel
- @default undefined
- @type {Fresnel}
- @final
- */
- emissiveFresnel: {
- get: function () {
- return this._emissiveFresnel;
- }
- },
-
- /**
- Alpha Fresnel.
-
- Applies to {{#crossLink "PhongMaterial/alpha:property"}}{{/crossLink}}.
-
- @property alphaFresnel
- @default undefined
- @type {Fresnel}
- @final
- */
- alphaFresnel: {
- get: function () {
- return this._alphaFresnel;
- }
- },
-
- /**
- Reflectivity Fresnel.
-
- Applies to {{#crossLink "PhongMaterial/reflectivity:property"}}{{/crossLink}}.
-
- @property reflectivityFresnel
- @default undefined
- @type {Fresnel}
- @final
- */
- reflectivityFresnel: {
- get: function () {
- return this._reflectivityFresnel;
- }
- },
-
- /**
- The alpha rendering mode.
-
- This governs how alpha is treated. Alpha is the combined result of the
- {{#crossLink "PhongMaterial/alpha:property"}}{{/crossLink}} and
- {{#crossLink "PhongMaterial/alphaMap:property"}}{{/crossLink}} properties.
-
- * "opaque" - The alpha value is ignored and the rendered output is fully opaque.
- * "mask" - The rendered output is either fully opaque or fully transparent depending on the alpha value and the specified alpha cutoff value.
- * "blend" - The alpha value is used to composite the source and destination areas. The rendered output is combined with the background using the normal painting operation (i.e. the Porter and Duff over operator).
-
- @property alphaMode
- @default "opaque"
- @type {String}
- */
- alphaMode: (function () {
- const modes = {"opaque": 0, "mask": 1, "blend": 2};
- const modeNames = ["opaque", "mask", "blend"];
- return {
- set: function (alphaMode) {
- alphaMode = alphaMode || "opaque";
- let value = modes[alphaMode];
- if (value === undefined) {
- this.error("Unsupported value for 'alphaMode': " + alphaMode + " - defaulting to 'opaque'");
- value = "opaque";
- }
- if (this._state.alphaMode === value) {
- return;
- }
- this._state.alphaMode = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return modeNames[this._state.alphaMode];
- }
- };
- })(),
-
- /**
- The alpha cutoff value.
-
- Specifies the cutoff threshold when {{#crossLink "PhongMaterial/alphaMode:property"}}{{/crossLink}}
- equals "mask". If the alpha is greater than or equal to this value then it is rendered as fully
- opaque, otherwise, it is rendered as fully transparent. A value greater than 1.0 will render the entire
- material as fully transparent. This value is ignored for other modes.
-
- Alpha is the combined result of the
- {{#crossLink "PhongMaterial/alpha:property"}}{{/crossLink}} and
- {{#crossLink "PhongMaterial/alphaMap:property"}}{{/crossLink}} properties.
-
- @property alphaCutoff
- @default 0.5
- @type {Number}
- */
- alphaCutoff: {
- set: function (alphaCutoff) {
- if (alphaCutoff === null || alphaCutoff === undefined) {
- alphaCutoff = 0.5;
- }
- if (this._state.alphaCutoff === alphaCutoff) {
- return;
- }
- this._state.alphaCutoff = alphaCutoff;
- },
- get: function () {
- return this._state.alphaCutoff;
- }
- },
-
- /**
- Whether backfaces are visible on attached {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
-
- The backfaces will belong to {{#crossLink "Geometry"}}{{/crossLink}} compoents that are also attached to
- the {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
-
- @property backfaces
- @default false
- @type Boolean
- */
- backfaces: {
- set: function (value) {
- value = !!value;
- if (this._state.backfaces === value) {
- return;
- }
- this._state.backfaces = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.backfaces;
- }
- },
-
- /**
- Indicates the winding direction of front faces on attached {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
-
- The faces will belong to {{#crossLink "Geometry"}}{{/crossLink}} components that are also attached to
- the {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
-
- @property frontface
- @default "ccw"
- @type String
- */
- frontface: {
- set: function (value) {
- value = value !== "cw";
- if (this._state.frontface === value) {
- return;
- }
- this._state.frontface = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.frontface ? "ccw" : "cw";
- }
+ }
+ if (this._occlusionMap) {
+ hash.push("/ocm");
+ if (this._occlusionMap.hasMatrix) {
+ hash.push("/mat");
}
- },
+ }
+ if (this._diffuseFresnel) {
+ hash.push("/df");
+ }
+ if (this._specularFresnel) {
+ hash.push("/sf");
+ }
+ if (this._emissiveFresnel) {
+ hash.push("/ef");
+ }
+ if (this._alphaFresnel) {
+ hash.push("/of");
+ }
+ if (this._reflectivityFresnel) {
+ hash.push("/rf");
+ }
+ hash.push(";");
+ state.hash = hash.join("");
+ }
+
+ /**
+ The PhongMaterial's ambient color.
+
+ @property ambient
+ @default [0.3, 0.3, 0.3]
+ @type Float32Array
+ */
+ set ambient(value) {
+ let ambient = this._state.ambient;
+ if (!ambient) {
+ ambient = this._state.ambient = new Float32Array(3);
+ } else if (value && ambient[0] === value[0] && ambient[1] === value[1] && ambient[2] === value[2]) {
+ return;
+ }
+ if (value) {
+ ambient[0] = value[0];
+ ambient[1] = value[1];
+ ambient[2] = value[2];
+ } else {
+ ambient[0] = .2;
+ ambient[1] = .2;
+ ambient[2] = .2;
+ }
+ this._renderer.imageDirty();
+ }
+
+ get ambient() {
+ return this._state.ambient;
+ }
+
+ /**
+ The PhongMaterial's diffuse color.
+
+ Multiplies by {{#crossLink "PhongMaterial/diffuseMap:property"}}{{/crossLink}}.
+
+ @property diffuse
+ @default [1.0, 1.0, 1.0]
+ @type Float32Array
+ */
+ set diffuse(value) {
+ let diffuse = this._state.diffuse;
+ if (!diffuse) {
+ diffuse = this._state.diffuse = new Float32Array(3);
+ } else if (value && diffuse[0] === value[0] && diffuse[1] === value[1] && diffuse[2] === value[2]) {
+ return;
+ }
+ if (value) {
+ diffuse[0] = value[0];
+ diffuse[1] = value[1];
+ diffuse[2] = value[2];
+ } else {
+ diffuse[0] = 1;
+ diffuse[1] = 1;
+ diffuse[2] = 1;
+ }
+ this._renderer.imageDirty();
+ }
+
+ get diffuse() {
+ return this._state.diffuse;
+ }
+
+ /**
+ The material's specular color.
+
+ Multiplies by {{#crossLink "PhongMaterial/specularMap:property"}}{{/crossLink}}.
+
+ @property specular
+ @default [1.0, 1.0, 1.0]
+ @type Float32Array
+ */
+ set specular(value) {
+ let specular = this._state.specular;
+ if (!specular) {
+ specular = this._state.specular = new Float32Array(3);
+ } else if (value && specular[0] === value[0] && specular[1] === value[1] && specular[2] === value[2]) {
+ return;
+ }
+ if (value) {
+ specular[0] = value[0];
+ specular[1] = value[1];
+ specular[2] = value[2];
+ } else {
+ specular[0] = 1;
+ specular[1] = 1;
+ specular[2] = 1;
+ }
+ this._renderer.imageDirty();
+ }
+
+ get specular() {
+ return this._state.specular;
+ }
+
+ /**
+ The PhongMaterial's emissive color.
+
+ Multiplies by {{#crossLink "PhongMaterial/emissiveMap:property"}}{{/crossLink}}.
+
+ @property emissive
+ @default [0.0, 0.0, 0.0]
+ @type Float32Array
+ */
+ set emissive(value) {
+ let emissive = this._state.emissive;
+ if (!emissive) {
+ emissive = this._state.emissive = new Float32Array(3);
+ } else if (value && emissive[0] === value[0] && emissive[1] === value[1] && emissive[2] === value[2]) {
+ return;
+ }
+ if (value) {
+ emissive[0] = value[0];
+ emissive[1] = value[1];
+ emissive[2] = value[2];
+ } else {
+ emissive[0] = 0;
+ emissive[1] = 0;
+ emissive[2] = 0;
+ }
+ this._renderer.imageDirty();
+ }
+
+ get emissive() {
+ return this._state.emissive;
+ }
- _destroy: function () {
- this._super();
- this._state.destroy();
+ /**
+ Factor in the range [0..1] indicating how transparent the PhongMaterial is.
+
+ A value of 0.0 indicates fully transparent, 1.0 is fully opaque.
+
+ Multiplies by {{#crossLink "PhongMaterial/alphaMap:property"}}{{/crossLink}}.
+
+ @property alpha
+ @default 1.0
+ @type Number
+ */
+ set alpha(value) {
+ value = (value !== undefined && value !== null) ? value : 1.0;
+ if (this._state.alpha === value) {
+ return;
}
- });
+ this._state.alpha = value;
+ this._renderer.imageDirty();
+ }
+
+ get alpha() {
+ return this._state.alpha;
+ }
+
+ /**
+ A factor in range [0..128] that determines the size and sharpness of the specular highlights create by this PhongMaterial.
+
+ Larger values produce smaller, sharper highlights. A value of 0.0 gives very large highlights that are almost never
+ desirable. Try values close to 10 for a larger, fuzzier highlight and values of 100 or more for a small, sharp
+ highlight.
+
+ @property shininess
+ @default 80.0
+ @type Number
+ */
+ set shininess(value) {
+ this._state.shininess = value !== undefined ? value : 80;
+ this._renderer.imageDirty();
+ }
+
+ get shininess() {
+ return this._state.shininess;
+ }
+
+ /**
+ The PhongMaterial's line width.
+
+ @property lineWidth
+ @default 1.0
+ @type Number
+ */
+ set lineWidth(value) {
+ this._state.lineWidth = value || 1.0;
+ this._renderer.imageDirty();
+ }
+
+ get lineWidth() {
+ return this._state.lineWidth;
+ }
+
+ /**
+ The PhongMaterial's point size.
+
+ @property pointSize
+ @default 1.0
+ @type Number
+ */
+ set pointSize(value) {
+ this._state.pointSize = value || 1.0;
+ this._renderer.imageDirty();
+ }
+
+ get pointSize() {
+ return this._state.pointSize;
+ }
+
+ /**
+ Scalar in range 0-1 that controls how much {{#crossLink "CubeMap"}}CubeMap{{/crossLink}} is reflected by this PhongMaterial.
+
+ The surface will be non-reflective when this is 0, and completely mirror-like when it is 1.0.
+
+ Multiplies by {{#crossLink "PhongMaterial/reflectivityMap:property"}}{{/crossLink}}.
+
+ @property reflectivity
+ @default 1.0
+ @type Number
+ */
+ set reflectivity(value) {
+ this._state.reflectivity = value !== undefined ? value : 1.0;
+ this._renderer.imageDirty();
+ }
+
+ get reflectivity() {
+ return this._state.reflectivity;
+ }
+
+ /**
+ Normal map.
+
+ @property normalMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get normalMap() {
+ return this._normalMap;
+ }
+
+ /**
+ Ambient map.
+
+ Multiplies by {{#crossLink "PhongMaterial/ambient:property"}}{{/crossLink}}.
+
+ @property ambientMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get ambientMap() {
+ return this._ambientMap;
+ }
+
+ /**
+ Diffuse map.
+
+ Multiplies by {{#crossLink "PhongMaterial/diffuse:property"}}{{/crossLink}}.
+
+ @property diffuseMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get diffuseMap() {
+ return this._diffuseMap;
+ }
+
+ /**
+ Specular map.
+
+ Multiplies by {{#crossLink "PhongMaterial/specular:property"}}{{/crossLink}}.
+
+ @property specularMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+
+ get specularMap() {
+ return this._specularMap;
+ }
+
+ /**
+ Emissive map.
+
+ Multiplies by {{#crossLink "PhongMaterial/emissive:property"}}{{/crossLink}}.
+
+ @property emissiveMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get emissiveMap() {
+ return this._emissiveMap;
+ }
+
+ /**
+ Alpha map.
+
+ Multiplies by {{#crossLink "PhongMaterial/alpha:property"}}{{/crossLink}}.
+
+ @property alphaMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get alphaMap() {
+ return this._alphaMap;
+ }
+
+ /**
+ Reflectivity map.
+
+ Multiplies by {{#crossLink "PhongMaterial/reflectivity:property"}}{{/crossLink}}.
+
+ @property reflectivityMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get reflectivityMap() {
+ return this._reflectivityMap;
+ }
+
+ /**
+
+ Occlusion map.
+
+ @property occlusionMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get occlusionMap() {
+ return this._occlusionMap;
+ }
+
+ /**
+ Diffuse Fresnel.
+
+ Applies to {{#crossLink "PhongMaterial/diffuseFresnel:property"}}{{/crossLink}}.
+
+ @property diffuseFresnel
+ @default undefined
+ @type {Fresnel}
+ @final
+ */
+ get diffuseFresnel() {
+ return this._diffuseFresnel;
+ }
+
+ /**
+ Specular Fresnel.
+
+ Applies to {{#crossLink "PhongMaterial/specular:property"}}{{/crossLink}}.
+
+ @property specularFresnel
+ @default undefined
+ @type {Fresnel}
+ @final
+ */
+ get specularFresnel() {
+ return this._specularFresnel;
+ }
+
+ /**
+ Emissive Fresnel.
+
+ Applies to {{#crossLink "PhongMaterial/emissive:property"}}{{/crossLink}}.
+
+ @property emissiveFresnel
+ @default undefined
+ @type {Fresnel}
+ @final
+ */
+ get emissiveFresnel() {
+ return this._emissiveFresnel;
+ }
+
+ /**
+ Alpha Fresnel.
+
+ Applies to {{#crossLink "PhongMaterial/alpha:property"}}{{/crossLink}}.
+
+ @property alphaFresnel
+ @default undefined
+ @type {Fresnel}
+ @final
+ */
+ get alphaFresnel() {
+ return this._alphaFresnel;
+ }
+
+ /**
+ Reflectivity Fresnel.
+
+ Applies to {{#crossLink "PhongMaterial/reflectivity:property"}}{{/crossLink}}.
+
+ @property reflectivityFresnel
+ @default undefined
+ @type {Fresnel}
+ @final
+ */
+ get reflectivityFresnel() {
+ return this._reflectivityFresnel;
+ }
+
+ /**
+ The alpha rendering mode.
+
+ This governs how alpha is treated. Alpha is the combined result of the
+ {{#crossLink "PhongMaterial/alpha:property"}}{{/crossLink}} and
+ {{#crossLink "PhongMaterial/alphaMap:property"}}{{/crossLink}} properties.
+
+ * "opaque" - The alpha value is ignored and the rendered output is fully opaque.
+ * "mask" - The rendered output is either fully opaque or fully transparent depending on the alpha value and the specified alpha cutoff value.
+ * "blend" - The alpha value is used to composite the source and destination areas. The rendered output is combined with the background using the normal painting operation (i.e. the Porter and Duff over operator).
+
+ @property alphaMode
+ @default "opaque"
+ @type {String}
+ */
+
+ set alphaMode(alphaMode) {
+ alphaMode = alphaMode || "opaque";
+ let value = alphaModes[alphaMode];
+ if (value === undefined) {
+ this.error("Unsupported value for 'alphaMode': " + alphaMode + " - defaulting to 'opaque'");
+ value = "opaque";
+ }
+ if (this._state.alphaMode === value) {
+ return;
+ }
+ this._state.alphaMode = value;
+ this._renderer.imageDirty();
+ }
+
+ get alphaMode() {
+ return alphaModeNames[this._state.alphaMode];
+ }
+
+ /**
+ The alpha cutoff value.
+
+ Specifies the cutoff threshold when {{#crossLink "PhongMaterial/alphaMode:property"}}{{/crossLink}}
+ equals "mask". If the alpha is greater than or equal to this value then it is rendered as fully
+ opaque, otherwise, it is rendered as fully transparent. A value greater than 1.0 will render the entire
+ material as fully transparent. This value is ignored for other modes.
+
+ Alpha is the combined result of the
+ {{#crossLink "PhongMaterial/alpha:property"}}{{/crossLink}} and
+ {{#crossLink "PhongMaterial/alphaMap:property"}}{{/crossLink}} properties.
+
+ @property alphaCutoff
+ @default 0.5
+ @type {Number}
+ */
+ set alphaCutoff(alphaCutoff) {
+ if (alphaCutoff === null || alphaCutoff === undefined) {
+ alphaCutoff = 0.5;
+ }
+ if (this._state.alphaCutoff === alphaCutoff) {
+ return;
+ }
+ this._state.alphaCutoff = alphaCutoff;
+ }
+
+ get alphaCutoff() {
+ return this._state.alphaCutoff;
+ }
+
+ /**
+ Whether backfaces are visible on attached {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
+
+ The backfaces will belong to {{#crossLink "Geometry"}}{{/crossLink}} compoents that are also attached to
+ the {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
+
+ @property backfaces
+ @default false
+ @type Boolean
+ */
+ set backfaces(value) {
+ value = !!value;
+ if (this._state.backfaces === value) {
+ return;
+ }
+ this._state.backfaces = value;
+ this._renderer.imageDirty();
+ }
+
+ get backfaces() {
+ return this._state.backfaces;
+ }
+
+ /**
+ Indicates the winding direction of front faces on attached {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
+
+ The faces will belong to {{#crossLink "Geometry"}}{{/crossLink}} components that are also attached to
+ the {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
+
+ @property frontface
+ @default "ccw"
+ @type String
+ */
+ set frontface(value) {
+ value = value !== "cw";
+ if (this._state.frontface === value) {
+ return;
+ }
+ this._state.frontface = value;
+ this._renderer.imageDirty();
+ }
+
+ get frontface() {
+ return this._state.frontface ? "ccw" : "cw";
+ }
+
+ destroy() {
+ super.destroy();
+ this._state.destroy();
+ }
+}
-})();
\ No newline at end of file
+export{PhongMaterial};
\ No newline at end of file
diff --git a/src/materials/specularMaterial.js b/src/materials/specularMaterial.js
index 75649e008..024b03cff 100644
--- a/src/materials/specularMaterial.js
+++ b/src/materials/specularMaterial.js
@@ -217,8 +217,7 @@
@constructor
@extends Material
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}}, creates this SpecularMaterial within the
- default {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted
+ @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] {*} The SpecularMaterial configuration
@@ -290,597 +289,575 @@
@param [cfg.pointSize=1] {Number} Scalar that controls the size of points for {{#crossLink "Geometry"}}{{/crossLink}} with {{#crossLink "Geometry/primitive:property"}}{{/crossLink}} set to "points".
*/
-(function () {
-
- "use strict";
-
- xeogl.SpecularMaterial = xeogl.Material.extend({
-
- type: "xeogl.SpecularMaterial",
-
- _init: function (cfg) {
-
- this._super(cfg);
-
- this._state = new xeogl.renderer.State({
- type: "SpecularMaterial",
- diffuse: xeogl.math.vec3([1.0, 1.0, 1.0]),
- emissive: xeogl.math.vec3([0.0, 0.0, 0.0]),
- specular: xeogl.math.vec3([1.0, 1.0, 1.0]),
- glossiness: null,
- specularF0: null,
- alpha: null,
- alphaMode: null,
- alphaCutoff: null,
- lineWidth: null,
- pointSize: null,
- backfaces: null,
- frontface: null, // Boolean for speed; true == "ccw", false == "cw"
- hash: null
- });
-
- this.diffuse = cfg.diffuse;
- this.specular = cfg.specular;
- this.glossiness = cfg.glossiness;
- this.specularF0 = cfg.specularF0;
- this.emissive = cfg.emissive;
- this.alpha = cfg.alpha;
-
- if (cfg.diffuseMap) {
- this._diffuseMap = this._checkComponent("xeogl.Texture", cfg.diffuseMap);
- }
- if (cfg.emissiveMap) {
- this._emissiveMap = this._checkComponent("xeogl.Texture", cfg.emissiveMap);
- }
- if (cfg.specularMap) {
- this._specularMap = this._checkComponent("xeogl.Texture", cfg.specularMap);
- }
- if (cfg.glossinessMap) {
- this._glossinessMap = this._checkComponent("xeogl.Texture", cfg.glossinessMap);
- }
- if (cfg.specularGlossinessMap) {
- this._specularGlossinessMap = this._checkComponent("xeogl.Texture", cfg.specularGlossinessMap);
- }
- if (cfg.occlusionMap) {
- this._occlusionMap = this._checkComponent("xeogl.Texture", cfg.occlusionMap);
- }
- if (cfg.alphaMap) {
- this._alphaMap = this._checkComponent("xeogl.Texture", cfg.alphaMap);
- }
- if (cfg.normalMap) {
- this._normalMap = this._checkComponent("xeogl.Texture", cfg.normalMap);
- }
+import {core} from "./../core.js";
+import {Material} from './material.js';
+import {State} from '../renderer/state.js';
+import {math} from '../math/math.js';
- this.alphaMode = cfg.alphaMode;
- this.alphaCutoff = cfg.alphaCutoff;
- this.backfaces = cfg.backfaces;
- this.frontface = cfg.frontface;
+const alphaModes = {"opaque": 0, "mask": 1, "blend": 2};
+const alphaModeNames = ["opaque", "mask", "blend"];
- this.lineWidth = cfg.lineWidth;
- this.pointSize = cfg.pointSize;
+class SpecularMaterial extends Material {
- this._makeHash();
- },
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return "xeogl.SpecularMaterial";
+ }
+
+ init(cfg) {
+
+ super.init(cfg);
+
+ this._state = new State({
+ type: "SpecularMaterial",
+ diffuse: math.vec3([1.0, 1.0, 1.0]),
+ emissive: math.vec3([0.0, 0.0, 0.0]),
+ specular: math.vec3([1.0, 1.0, 1.0]),
+ glossiness: null,
+ specularF0: null,
+ alpha: null,
+ alphaMode: null,
+ alphaCutoff: null,
+ lineWidth: null,
+ pointSize: null,
+ backfaces: null,
+ frontface: null, // Boolean for speed; true == "ccw", false == "cw"
+ hash: null
+ });
+
+ this.diffuse = cfg.diffuse;
+ this.specular = cfg.specular;
+ this.glossiness = cfg.glossiness;
+ this.specularF0 = cfg.specularF0;
+ this.emissive = cfg.emissive;
+ this.alpha = cfg.alpha;
+
+ if (cfg.diffuseMap) {
+ this._diffuseMap = this._checkComponent("xeogl.Texture", cfg.diffuseMap);
+ }
+ if (cfg.emissiveMap) {
+ this._emissiveMap = this._checkComponent("xeogl.Texture", cfg.emissiveMap);
+ }
+ if (cfg.specularMap) {
+ this._specularMap = this._checkComponent("xeogl.Texture", cfg.specularMap);
+ }
+ if (cfg.glossinessMap) {
+ this._glossinessMap = this._checkComponent("xeogl.Texture", cfg.glossinessMap);
+ }
+ if (cfg.specularGlossinessMap) {
+ this._specularGlossinessMap = this._checkComponent("xeogl.Texture", cfg.specularGlossinessMap);
+ }
+ if (cfg.occlusionMap) {
+ this._occlusionMap = this._checkComponent("xeogl.Texture", cfg.occlusionMap);
+ }
+ if (cfg.alphaMap) {
+ this._alphaMap = this._checkComponent("xeogl.Texture", cfg.alphaMap);
+ }
+ if (cfg.normalMap) {
+ this._normalMap = this._checkComponent("xeogl.Texture", cfg.normalMap);
+ }
+
+ this.alphaMode = cfg.alphaMode;
+ this.alphaCutoff = cfg.alphaCutoff;
+ this.backfaces = cfg.backfaces;
+ this.frontface = cfg.frontface;
+
+ this.lineWidth = cfg.lineWidth;
+ this.pointSize = cfg.pointSize;
- _makeHash: function () {
- const state = this._state;
- const hash = ["/spe"];
- if (this._diffuseMap) {
- hash.push("/dm");
- if (this._diffuseMap.hasMatrix) {
- hash.push("/mat");
- }
- hash.push("/" + this._diffuseMap.encoding);
+ this._makeHash();
+ }
+
+ _makeHash() {
+ const state = this._state;
+ const hash = ["/spe"];
+ if (this._diffuseMap) {
+ hash.push("/dm");
+ if (this._diffuseMap.hasMatrix) {
+ hash.push("/mat");
}
- if (this._emissiveMap) {
- hash.push("/em");
- if (this._emissiveMap.hasMatrix) {
- hash.push("/mat");
- }
+ hash.push("/" + this._diffuseMap.encoding);
+ }
+ if (this._emissiveMap) {
+ hash.push("/em");
+ if (this._emissiveMap.hasMatrix) {
+ hash.push("/mat");
}
- if (this._glossinessMap) {
- hash.push("/gm");
- if (this._glossinessMap.hasMatrix) {
- hash.push("/mat");
- }
+ }
+ if (this._glossinessMap) {
+ hash.push("/gm");
+ if (this._glossinessMap.hasMatrix) {
+ hash.push("/mat");
}
- if (this._specularMap) {
- hash.push("/sm");
- if (this._specularMap.hasMatrix) {
- hash.push("/mat");
- }
+ }
+ if (this._specularMap) {
+ hash.push("/sm");
+ if (this._specularMap.hasMatrix) {
+ hash.push("/mat");
}
- if (this._specularGlossinessMap) {
- hash.push("/sgm");
- if (this._specularGlossinessMap.hasMatrix) {
- hash.push("/mat");
- }
+ }
+ if (this._specularGlossinessMap) {
+ hash.push("/sgm");
+ if (this._specularGlossinessMap.hasMatrix) {
+ hash.push("/mat");
}
- if (this._occlusionMap) {
- hash.push("/ocm");
- if (this._occlusionMap.hasMatrix) {
- hash.push("/mat");
- }
+ }
+ if (this._occlusionMap) {
+ hash.push("/ocm");
+ if (this._occlusionMap.hasMatrix) {
+ hash.push("/mat");
}
- if (this._normalMap) {
- hash.push("/nm");
- if (this._normalMap.hasMatrix) {
- hash.push("/mat");
- }
+ }
+ if (this._normalMap) {
+ hash.push("/nm");
+ if (this._normalMap.hasMatrix) {
+ hash.push("/mat");
}
- if (this._alphaMap) {
- hash.push("/opm");
- if (this._alphaMap.hasMatrix) {
- hash.push("/mat");
- }
+ }
+ if (this._alphaMap) {
+ hash.push("/opm");
+ if (this._alphaMap.hasMatrix) {
+ hash.push("/mat");
}
- hash.push(";");
- state.hash = hash.join("");
- },
+ }
+ hash.push(";");
+ state.hash = hash.join("");
+ }
- _props: {
-
-
- /**
- RGB diffuse color of this SpecularMaterial.
-
- Multiplies by the *RGB* components of {{#crossLink "SpecularMaterial/diffuseMap:property"}}{{/crossLink}}.
-
- @property diffuse
- @default [1.0, 1.0, 1.0]
- @type Float32Array
- */
- diffuse: {
- set: function (value) {
- let diffuse = this._state.diffuse;
- if (!diffuse) {
- diffuse = this._state.diffuse = new Float32Array(3);
- } else if (value && diffuse[0] === value[0] && diffuse[1] === value[1] && diffuse[2] === value[2]) {
- return;
- }
- if (value) {
- diffuse[0] = value[0];
- diffuse[1] = value[1];
- diffuse[2] = value[2];
- } else {
- diffuse[0] = 1;
- diffuse[1] = 1;
- diffuse[2] = 1;
- }
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.diffuse;
- }
- },
-
- /**
- RGB {{#crossLink "Texture"}}{{/crossLink}} containing the diffuse color of this SpecularMaterial, with optional *A* component for alpha.
-
- The *RGB* components multiply by the {{#crossLink "SpecularMaterial/diffuse:property"}}{{/crossLink}} property,
- while the *A* component, if present, multiplies by the {{#crossLink "SpecularMaterial/alpha:property"}}{{/crossLink}} property.
-
- @property diffuseMap
- @default undefined
- @type {Texture}
- @final
- */
- diffuseMap: {
- get: function () {
- return this._diffuseMap;
- }
- },
-
- /**
- RGB specular color of this SpecularMaterial.
-
- Multiplies by the {{#crossLink "SpecularMaterial/specularMap:property"}}{{/crossLink}}
- and the *A* component of {{#crossLink "SpecularMaterial/specularGlossinessMap:property"}}{{/crossLink}}.
-
- @property specular
- @default [1.0, 1.0, 1.0]
- @type Float32Array
- */
- specular: {
- set: function (value) {
- let specular = this._state.specular;
- if (!specular) {
- specular = this._state.specular = new Float32Array(3);
- } else if (value && specular[0] === value[0] && specular[1] === value[1] && specular[2] === value[2]) {
- return;
- }
- if (value) {
- specular[0] = value[0];
- specular[1] = value[1];
- specular[2] = value[2];
- } else {
- specular[0] = 1;
- specular[1] = 1;
- specular[2] = 1;
- }
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.specular;
- }
- },
-
- /**
- RGB texture containing the specular color of this SpecularMaterial.
-
- Multiplies by the {{#crossLink "SpecularMaterial/specular:property"}}{{/crossLink}} property.
-
- @property specularMap
- @default undefined
- @type {Texture}
- @final
- */
- specularMap: {
- get: function () {
- return this._specularMap;
- }
- },
-
- /**
- RGBA texture containing this SpecularMaterial's specular color in its *RGB* components and glossiness in its *A* component.
-
- The *RGB* components multiply by the {{#crossLink "SpecularMaterial/specular:property"}}{{/crossLink}} property, while
- the *A* component multiplies by the {{#crossLink "SpecularMaterial/glossiness:property"}}{{/crossLink}} property.
-
- @property specularGlossinessMap
- @default undefined
- @type {Texture}
- @final
- */
- specularGlossinessMap: {
- get: function () {
- return this._specularGlossinessMap;
- }
- },
-
- /**
- Factor in the range [0..1] indicating how glossy this SpecularMaterial is.
-
- 0 is no glossiness, 1 is full glossiness.
-
- Multiplies by the *R* component of {{#crossLink "SpecularMaterial/glossinessMap:property"}}{{/crossLink}}
- and the *A* component of {{#crossLink "SpecularMaterial/specularGlossinessMap:property"}}{{/crossLink}}.
-
- @property glossiness
- @default 1.0
- @type Number
- */
- glossiness: {
- set: function (value) {
- value = (value !== undefined && value !== null) ? value : 1.0;
- if (this._state.glossiness === value) {
- return;
- }
- this._state.glossiness = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.glossiness;
- }
- },
-
- /**
- RGB texture containing this SpecularMaterial's glossiness in its *R* component.
-
- The *R* component multiplies by the {{#crossLink "SpecularMaterial/glossiness:property"}}{{/crossLink}} property.
-
- Must be within the same {{#crossLink "Scene"}}Scene{{/crossLink}} as this SpecularMaterial.
-
- @property glossinessMap
- @default undefined
- @type {Texture}
- @final
- */
- glossinessMap: {
- get: function () {
- return this._glossinessMap;
- }
- },
-
- /**
- Factor in the range [0..1] indicating amount of specular Fresnel.
-
- @property specularF0
- @default 0.0
- @type Number
- */
- specularF0: {
- set: function (value) {
- value = (value !== undefined && value !== null) ? value : 0.0;
- if (this._state.specularF0 === value) {
- return;
- }
- this._state.specularF0 = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.specularF0;
- }
- },
-
- /**
- RGB emissive color of this SpecularMaterial.
-
- Multiplies by {{#crossLink "SpecularMaterial/emissiveMap:property"}}{{/crossLink}}.
-
- @property emissive
- @default [0.0, 0.0, 0.0]
- @type Float32Array
- */
- emissive: {
- set: function (value) {
- let emissive = this._state.emissive;
- if (!emissive) {
- emissive = this._state.emissive = new Float32Array(3);
- } else if (value && emissive[0] === value[0] && emissive[1] === value[1] && emissive[2] === value[2]) {
- return;
- }
- if (value) {
- emissive[0] = value[0];
- emissive[1] = value[1];
- emissive[2] = value[2];
- } else {
- emissive[0] = 0;
- emissive[1] = 0;
- emissive[2] = 0;
- }
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.emissive;
- }
- },
-
- /**
- RGB texture containing the emissive color of this SpecularMaterial.
-
- Multiplies by the {{#crossLink "SpecularMaterial/emissive:property"}}{{/crossLink}} property.
-
- @property emissiveMap
- @default undefined
- @type {Texture}
- @final
- */
- emissiveMap: {
- get: function () {
- return this._emissiveMap;
- }
- },
-
- /**
- Factor in the range [0..1] indicating how transparent this SpecularMaterial is.
-
- A value of 0.0 is fully transparent, while 1.0 is fully opaque.
-
- Multiplies by the *R* component of {{#crossLink "SpecularMaterial/alphaMap:property"}}{{/crossLink}} and
- the *A* component, if present, of {{#crossLink "SpecularMaterial/diffuseMap:property"}}{{/crossLink}}.
-
- @property alpha
- @default 1.0
- @type Number
- */
- alpha: {
- set: function (value) {
- value = (value !== undefined && value !== null) ? value : 1.0;
- if (this._state.alpha === value) {
- return;
- }
- this._state.alpha = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.alpha;
- }
- },
-
- /**
- RGB {{#crossLink "Texture"}}{{/crossLink}} with alpha in its *R* component.
-
- The *R* component multiplies by the {{#crossLink "SpecularMaterial/alpha:property"}}{{/crossLink}} property.
-
- @property alphaMap
- @default undefined
- @type {Texture}
- @final
- */
- alphaMap: {
- get: function () {
- return this._alphaMap;
- }
- },
-
- /**
- RGB tangent-space normal {{#crossLink "Texture"}}{{/crossLink}} attached to this SpecularMaterial.
-
- @property normalMap
- @default undefined
- @type {Texture}
- @final
- */
- normalMap: {
- get: function () {
- return this._normalMap;
- }
- },
-
- /**
- RGB ambient occlusion {{#crossLink "Texture"}}{{/crossLink}} attached to this SpecularMaterial.
-
- Within objectRenderers, multiplies by the specular and diffuse light reflected by surfaces.
-
- @property occlusionMap
- @default undefined
- @type {Texture}
- @final
- */
- occlusionMap: {
- get: function () {
- return this._occlusionMap;
- }
- },
-
- /**
- The alpha rendering mode.
-
- This governs how alpha is treated. Alpha is the combined result of the
- {{#crossLink "SpecularMaterial/alpha:property"}}{{/crossLink}} and
- {{#crossLink "SpecularMaterial/alphaMap:property"}}{{/crossLink}} properties.
-
- * "opaque" - The alpha value is ignored and the rendered output is fully opaque.
- * "mask" - The rendered output is either fully opaque or fully transparent depending on the alpha value and the specified alpha cutoff value.
- * "blend" - The alpha value is used to composite the source and destination areas. The rendered output is combined with the background using the normal painting operation (i.e. the Porter and Duff over operator)
-
- @property alphaMode
- @default "opaque"
- @type {String}
- */
- alphaMode: (function () {
- const modes = {"opaque": 0, "mask": 1, "blend": 2};
- const modeNames = ["opaque", "mask", "blend"];
- return {
- set: function (alphaMode) {
- alphaMode = alphaMode || "opaque";
- let value = modes[alphaMode];
- if (value === undefined) {
- this.error("Unsupported value for 'alphaMode': " + alphaMode + " defaulting to 'opaque'");
- value = "opaque";
- }
- if (this._state.alphaMode === value) {
- return;
- }
- this._state.alphaMode = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return modeNames[this._state.alphaMode];
- }
- };
- })(),
-
- /**
- The alpha cutoff value.
-
- Specifies the cutoff threshold when {{#crossLink "SpecularMaterial/alphaMode:property"}}{{/crossLink}}
- equals "mask". If the alpha is greater than or equal to this value then it is rendered as fully
- opaque, otherwise, it is rendered as fully transparent. A value greater than 1.0 will render the entire
- material as fully transparent. This value is ignored for other modes.
-
- Alpha is the combined result of the
- {{#crossLink "SpecularMaterial/alpha:property"}}{{/crossLink}} and
- {{#crossLink "SpecularMaterial/alphaMap:property"}}{{/crossLink}} properties.
-
- @property alphaCutoff
- @default 0.5
- @type {Number}
- */
- alphaCutoff: {
- set: function (alphaCutoff) {
- if (alphaCutoff === null || alphaCutoff === undefined) {
- alphaCutoff = 0.5;
- }
- if (this._state.alphaCutoff === alphaCutoff) {
- return;
- }
- this._state.alphaCutoff = alphaCutoff;
- },
- get: function () {
- return this._state.alphaCutoff;
- }
- },
-
-
- /**
- Whether backfaces are visible on attached {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
-
- The backfaces will belong to {{#crossLink "Geometry"}}{{/crossLink}} compoents that are also attached to
- the {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
-
- @property backfaces
- @default false
- @type Boolean
- */
- backfaces: {
- set: function (value) {
- value = !!value;
- if (this._state.backfaces === value) {
- return;
- }
- this._state.backfaces = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.backfaces;
- }
- },
-
- /**
- Indicates the winding direction of front faces on attached {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
-
- The faces will belong to {{#crossLink "Geometry"}}{{/crossLink}} components that are also attached to
- the {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
-
- @property frontface
- @default "ccw"
- @type String
- */
- frontface: {
- set: function (value) {
- value = value !== "cw";
- if (this._state.frontface === value) {
- return;
- }
- this._state.frontface = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.frontface ? "ccw" : "cw";
- }
- },
-
- /**
- The SpecularMaterial's line width.
-
- @property lineWidth
- @default 1.0
- @type Number
- */
- lineWidth: {
- set: function (value) {
- this._state.lineWidth = value || 1.0;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.lineWidth;
- }
- },
-
- /**
- The SpecularMaterial's point size.
-
- @property pointSize
- @default 1
- @type Number
- */
- pointSize: {
- set: function (value) {
- this._state.pointSize = value || 1;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.pointSize;
- }
- }
- },
+ /**
+ RGB diffuse color of this SpecularMaterial.
+
+ Multiplies by the *RGB* components of {{#crossLink "SpecularMaterial/diffuseMap:property"}}{{/crossLink}}.
+
+ @property diffuse
+ @default [1.0, 1.0, 1.0]
+ @type Float32Array
+ */
+ set diffuse(value) {
+ let diffuse = this._state.diffuse;
+ if (!diffuse) {
+ diffuse = this._state.diffuse = new Float32Array(3);
+ } else if (value && diffuse[0] === value[0] && diffuse[1] === value[1] && diffuse[2] === value[2]) {
+ return;
+ }
+ if (value) {
+ diffuse[0] = value[0];
+ diffuse[1] = value[1];
+ diffuse[2] = value[2];
+ } else {
+ diffuse[0] = 1;
+ diffuse[1] = 1;
+ diffuse[2] = 1;
+ }
+ this._renderer.imageDirty();
+ }
+
+ get diffuse() {
+ return this._state.diffuse;
+ }
+
+ /**
+ RGB {{#crossLink "Texture"}}{{/crossLink}} containing the diffuse color of this SpecularMaterial, with optional *A* component for alpha.
+
+ The *RGB* components multiply by the {{#crossLink "SpecularMaterial/diffuse:property"}}{{/crossLink}} property,
+ while the *A* component, if present, multiplies by the {{#crossLink "SpecularMaterial/alpha:property"}}{{/crossLink}} property.
+
+ @property diffuseMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get diffuseMap() {
+ return this._diffuseMap;
+ }
+
+ /**
+ RGB specular color of this SpecularMaterial.
+
+ Multiplies by the {{#crossLink "SpecularMaterial/specularMap:property"}}{{/crossLink}}
+ and the *A* component of {{#crossLink "SpecularMaterial/specularGlossinessMap:property"}}{{/crossLink}}.
+
+ @property specular
+ @default [1.0, 1.0, 1.0]
+ @type Float32Array
+ */
+ set specular(value) {
+ let specular = this._state.specular;
+ if (!specular) {
+ specular = this._state.specular = new Float32Array(3);
+ } else if (value && specular[0] === value[0] && specular[1] === value[1] && specular[2] === value[2]) {
+ return;
+ }
+ if (value) {
+ specular[0] = value[0];
+ specular[1] = value[1];
+ specular[2] = value[2];
+ } else {
+ specular[0] = 1;
+ specular[1] = 1;
+ specular[2] = 1;
+ }
+ this._renderer.imageDirty();
+ }
+
+ get specular() {
+ return this._state.specular;
+ }
+
+ /**
+ RGB texture containing the specular color of this SpecularMaterial.
+
+ Multiplies by the {{#crossLink "SpecularMaterial/specular:property"}}{{/crossLink}} property.
- _destroy: function () {
- this._super();
- this._state.destroy();
+ @property specularMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get specularMap() {
+ return this._specularMap;
+ }
+
+ /**
+ RGBA texture containing this SpecularMaterial's specular color in its *RGB* components and glossiness in its *A* component.
+
+ The *RGB* components multiply by the {{#crossLink "SpecularMaterial/specular:property"}}{{/crossLink}} property, while
+ the *A* component multiplies by the {{#crossLink "SpecularMaterial/glossiness:property"}}{{/crossLink}} property.
+
+ @property specularGlossinessMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get specularGlossinessMap() {
+ return this._specularGlossinessMap;
+ }
+
+ /**
+ Factor in the range [0..1] indicating how glossy this SpecularMaterial is.
+
+ 0 is no glossiness, 1 is full glossiness.
+
+ Multiplies by the *R* component of {{#crossLink "SpecularMaterial/glossinessMap:property"}}{{/crossLink}}
+ and the *A* component of {{#crossLink "SpecularMaterial/specularGlossinessMap:property"}}{{/crossLink}}.
+
+ @property glossiness
+ @default 1.0
+ @type Number
+ */
+ set glossiness(value) {
+ value = (value !== undefined && value !== null) ? value : 1.0;
+ if (this._state.glossiness === value) {
+ return;
}
- });
+ this._state.glossiness = value;
+ this._renderer.imageDirty();
+ }
+
+ get glossiness() {
+ return this._state.glossiness;
+ }
+
+ /**
+ RGB texture containing this SpecularMaterial's glossiness in its *R* component.
+
+ The *R* component multiplies by the {{#crossLink "SpecularMaterial/glossiness:property"}}{{/crossLink}} property.
+
+ Must be within the same {{#crossLink "Scene"}}Scene{{/crossLink}} as this SpecularMaterial.
+
+ @property glossinessMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get glossinessMap() {
+ return this._glossinessMap;
+ }
+
+ /**
+ Factor in the range [0..1] indicating amount of specular Fresnel.
+
+ @property specularF0
+ @default 0.0
+ @type Number
+ */
+ set specularF0(value) {
+ value = (value !== undefined && value !== null) ? value : 0.0;
+ if (this._state.specularF0 === value) {
+ return;
+ }
+ this._state.specularF0 = value;
+ this._renderer.imageDirty();
+ }
+
+ get specularF0() {
+ return this._state.specularF0;
+ }
+
+ /**
+ RGB emissive color of this SpecularMaterial.
+
+ Multiplies by {{#crossLink "SpecularMaterial/emissiveMap:property"}}{{/crossLink}}.
+
+ @property emissive
+ @default [0.0, 0.0, 0.0]
+ @type Float32Array
+ */
+ set emissive(value) {
+ let emissive = this._state.emissive;
+ if (!emissive) {
+ emissive = this._state.emissive = new Float32Array(3);
+ } else if (value && emissive[0] === value[0] && emissive[1] === value[1] && emissive[2] === value[2]) {
+ return;
+ }
+ if (value) {
+ emissive[0] = value[0];
+ emissive[1] = value[1];
+ emissive[2] = value[2];
+ } else {
+ emissive[0] = 0;
+ emissive[1] = 0;
+ emissive[2] = 0;
+ }
+ this._renderer.imageDirty();
+ }
+
+ get emissive() {
+ return this._state.emissive;
+ }
+
+ /**
+ RGB texture containing the emissive color of this SpecularMaterial.
+
+ Multiplies by the {{#crossLink "SpecularMaterial/emissive:property"}}{{/crossLink}} property.
+
+ @property emissiveMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get emissiveMap() {
+ return this._emissiveMap;
+ }
+
+ /**
+ Factor in the range [0..1] indicating how transparent this SpecularMaterial is.
+
+ A value of 0.0 is fully transparent, while 1.0 is fully opaque.
+
+ Multiplies by the *R* component of {{#crossLink "SpecularMaterial/alphaMap:property"}}{{/crossLink}} and
+ the *A* component, if present, of {{#crossLink "SpecularMaterial/diffuseMap:property"}}{{/crossLink}}.
+
+ @property alpha
+ @default 1.0
+ @type Number
+ */
+ set alpha(value) {
+ value = (value !== undefined && value !== null) ? value : 1.0;
+ if (this._state.alpha === value) {
+ return;
+ }
+ this._state.alpha = value;
+ this._renderer.imageDirty();
+ }
+
+ get alpha() {
+ return this._state.alpha;
+ }
+
+ /**
+ RGB {{#crossLink "Texture"}}{{/crossLink}} with alpha in its *R* component.
+
+ The *R* component multiplies by the {{#crossLink "SpecularMaterial/alpha:property"}}{{/crossLink}} property.
+
+ @property alphaMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get alphaMap() {
+ return this._alphaMap;
+ }
+
+ /**
+ RGB tangent-space normal {{#crossLink "Texture"}}{{/crossLink}} attached to this SpecularMaterial.
+
+ @property normalMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get normalMap() {
+ return this._normalMap;
+ }
+
+ /**
+ RGB ambient occlusion {{#crossLink "Texture"}}{{/crossLink}} attached to this SpecularMaterial.
+
+ Within objectRenderers, multiplies by the specular and diffuse light reflected by surfaces.
+
+ @property occlusionMap
+ @default undefined
+ @type {Texture}
+ @final
+ */
+ get occlusionMap() {
+ return this._occlusionMap;
+ }
+
+ /**
+ The alpha rendering mode.
+
+ This governs how alpha is treated. Alpha is the combined result of the
+ {{#crossLink "SpecularMaterial/alpha:property"}}{{/crossLink}} and
+ {{#crossLink "SpecularMaterial/alphaMap:property"}}{{/crossLink}} properties.
+
+ * "opaque" - The alpha value is ignored and the rendered output is fully opaque.
+ * "mask" - The rendered output is either fully opaque or fully transparent depending on the alpha value and the specified alpha cutoff value.
+ * "blend" - The alpha value is used to composite the source and destination areas. The rendered output is combined with the background using the normal painting operation (i.e. the Porter and Duff over operator)
+
+ @property alphaMode
+ @default "opaque"
+ @type {String}
+ */
+ set alphaMode(alphaMode) {
+ alphaMode = alphaMode || "opaque";
+ let value = alphaModes[alphaMode];
+ if (value === undefined) {
+ this.error("Unsupported value for 'alphaMode': " + alphaMode + " defaulting to 'opaque'");
+ value = "opaque";
+ }
+ if (this._state.alphaMode === value) {
+ return;
+ }
+ this._state.alphaMode = value;
+ this._renderer.imageDirty();
+ }
+
+ get alphaMode() {
+ return alphaModeNames[this._state.alphaMode];
+ }
+
+ /**
+ The alpha cutoff value.
+
+ Specifies the cutoff threshold when {{#crossLink "SpecularMaterial/alphaMode:property"}}{{/crossLink}}
+ equals "mask". If the alpha is greater than or equal to this value then it is rendered as fully
+ opaque, otherwise, it is rendered as fully transparent. A value greater than 1.0 will render the entire
+ material as fully transparent. This value is ignored for other modes.
+
+ Alpha is the combined result of the
+ {{#crossLink "SpecularMaterial/alpha:property"}}{{/crossLink}} and
+ {{#crossLink "SpecularMaterial/alphaMap:property"}}{{/crossLink}} properties.
+
+ @property alphaCutoff
+ @default 0.5
+ @type {Number}
+ */
+ set alphaCutoff(alphaCutoff) {
+ if (alphaCutoff === null || alphaCutoff === undefined) {
+ alphaCutoff = 0.5;
+ }
+ if (this._state.alphaCutoff === alphaCutoff) {
+ return;
+ }
+ this._state.alphaCutoff = alphaCutoff;
+ }
+
+ get alphaCutoff() {
+ return this._state.alphaCutoff;
+ }
+
+ /**
+ Whether backfaces are visible on attached {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
+
+ The backfaces will belong to {{#crossLink "Geometry"}}{{/crossLink}} compoents that are also attached to
+ the {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
+
+ @property backfaces
+ @default false
+ @type Boolean
+ */
+ set backfaces(value) {
+ value = !!value;
+ if (this._state.backfaces === value) {
+ return;
+ }
+ this._state.backfaces = value;
+ this._renderer.imageDirty();
+ }
+
+ get backfaces() {
+ return this._state.backfaces;
+ }
+
+ /**
+ Indicates the winding direction of front faces on attached {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
+
+ The faces will belong to {{#crossLink "Geometry"}}{{/crossLink}} components that are also attached to
+ the {{#crossLink "Mesh"}}Meshes{{/crossLink}}.
+
+ @property frontface
+ @default "ccw"
+ @type String
+ */
+ set frontface(value) {
+ value = value !== "cw";
+ if (this._state.frontface === value) {
+ return;
+ }
+ this._state.frontface = value;
+ this._renderer.imageDirty();
+ }
+
+ get frontface() {
+ return this._state.frontface ? "ccw" : "cw";
+ }
+
+ /**
+ The SpecularMaterial's line width.
+
+ @property lineWidth
+ @default 1.0
+ @type Number
+ */
+ set lineWidth(value) {
+ this._state.lineWidth = value || 1.0;
+ this._renderer.imageDirty();
+ }
+
+ get lineWidth() {
+ return this._state.lineWidth;
+ }
+
+ /**
+ The SpecularMaterial's point size.
+
+ @property pointSize
+ @default 1
+ @type Number
+ */
+ set pointSize(value) {
+ this._state.pointSize = value || 1;
+ this._renderer.imageDirty();
+ }
+
+ get pointSize() {
+ return this._state.pointSize;
+ }
+
+ destroy() {
+ super.destroy();
+ this._state.destroy();
+ }
+}
-})();
\ No newline at end of file
+export{SpecularMaterial};
\ No newline at end of file
diff --git a/src/materials/texture.js b/src/materials/texture.js
index ffc82aec7..d82404937 100644
--- a/src/materials/texture.js
+++ b/src/materials/texture.js
@@ -54,8 +54,7 @@
@module xeogl
@submodule materials
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}} - creates this Texture in the default
- {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
+ @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 for this Texture, unique among all components in the parent scene, generated automatically when omitted.
@param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to this Texture.
@@ -72,419 +71,443 @@
@param [cfg.encoding="linear"] {String} Encoding format. See the {{#crossLink "Texture/encoding:property"}}{{/crossLink}} property for more info.
@extends Component
*/
-(function () {
-
- "use strict";
-
- xeogl.Texture = xeogl.Component.extend({
-
- type: "xeogl.Texture",
-
- _init: function (cfg) {
-
- this._state = new xeogl.renderer.State({
- texture: new xeogl.renderer.Texture2D(this.scene.canvas.gl),
- matrix: xeogl.math.identityMat4(), // Float32Array
- hasMatrix: (cfg.translate && (cfg.translate[0] !== 0 || cfg.translate[1] !== 0)) || (!!cfg.rotate) || (cfg.scale && (cfg.scale[0] !== 0 || cfg.scale[1] !== 0)),
- minFilter: this._checkMinFilter(cfg.minFilter),
- magFilter: this._checkMagFilter(cfg.minFilter),
- wrapS: this._checkWrapS(cfg.wrapS),
- wrapT: this._checkWrapT(cfg.wrapT),
- flipY: this._checkFlipY(cfg.flipY),
- encoding: this._checkEncoding(cfg.encoding)
- });
-
- // Data source
-
- this._src = null;
- this._image = null;
-
- // Transformation
-
- this._translate = xeogl.math.vec2([0, 0]);
- this._scale = xeogl.math.vec2([1, 1]);
- this._rotate = xeogl.math.vec2([0, 0]);
-
- this._matrixDirty = false;
-
- // Transform
-
- this.translate = cfg.translate;
- this.scale = cfg.scale;
- this.rotate = cfg.rotate;
-
- // Data source
-
- if (cfg.src) {
- this.src = cfg.src; // Image file
- } else if (cfg.image) {
- this.image = cfg.image; // Image object
- }
+import {core} from "./../core.js";
+import {Component} from '../component.js';
+import {State} from '../renderer/state.js';
+import {Texture2D} from '../renderer/texture2d.js';
+import {math} from '../math/math.js';
+import {stats} from './../stats.js';
+
+function ensureImageSizePowerOfTwo(image) {
+ if (!isPowerOfTwo(image.width) || !isPowerOfTwo(image.height)) {
+ const canvas = document.createElement("canvas");
+ canvas.width = nextHighestPowerOfTwo(image.width);
+ canvas.height = nextHighestPowerOfTwo(image.height);
+ const ctx = canvas.getContext("2d");
+ ctx.drawImage(image,
+ 0, 0, image.width, image.height,
+ 0, 0, canvas.width, canvas.height);
+ image = canvas;
+ }
+ return image;
+}
+
+function isPowerOfTwo(x) {
+ return (x & (x - 1)) === 0;
+}
+
+function nextHighestPowerOfTwo(x) {
+ --x;
+ for (let i = 1; i < 32; i <<= 1) {
+ x = x | x >> i;
+ }
+ return x + 1;
+}
+
+class Texture extends Component {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return "xeogl.Texture";
+ }
+
+ init(cfg) {
+
+ super.init(cfg);
+
+ this._state = new State({
+ texture: new Texture2D(this.scene.canvas.gl),
+ matrix: math.identityMat4(), // Float32Array
+ hasMatrix: (cfg.translate && (cfg.translate[0] !== 0 || cfg.translate[1] !== 0)) || (!!cfg.rotate) || (cfg.scale && (cfg.scale[0] !== 0 || cfg.scale[1] !== 0)),
+ minFilter: this._checkMinFilter(cfg.minFilter),
+ magFilter: this._checkMagFilter(cfg.minFilter),
+ wrapS: this._checkWrapS(cfg.wrapS),
+ wrapT: this._checkWrapT(cfg.wrapT),
+ flipY: this._checkFlipY(cfg.flipY),
+ encoding: this._checkEncoding(cfg.encoding)
+ });
+
+ // Data source
+
+ this._src = null;
+ this._image = null;
+
+ // Transformation
+
+ this._translate = math.vec2([0, 0]);
+ this._scale = math.vec2([1, 1]);
+ this._rotate = math.vec2([0, 0]);
+
+ this._matrixDirty = false;
+
+ // Transform
+
+ this.translate = cfg.translate;
+ this.scale = cfg.scale;
+ this.rotate = cfg.rotate;
+
+ // Data source
+
+ if (cfg.src) {
+ this.src = cfg.src; // Image file
+ } else if (cfg.image) {
+ this.image = cfg.image; // Image object
+ }
- xeogl.stats.memory.textures++;
- },
-
- _checkMinFilter: function (value) {
- value = value || "linearMipmapLinear";
- if (value !== "linear" &&
- value !== "linearMipmapNearest" &&
- value !== "linearMipmapLinear" &&
- value !== "nearestMipmapLinear" &&
- value !== "nearestMipmapNearest") {
- this.error("Unsupported value for 'minFilter': '" + value +
- "' - supported values are 'linear', 'linearMipmapNearest', 'nearestMipmapNearest', " +
- "'nearestMipmapLinear' and 'linearMipmapLinear'. Defaulting to 'linearMipmapLinear'.");
- value = "linearMipmapLinear";
- }
- return value;
- },
-
- _checkMagFilter: function (value) {
- value = value || "linear";
- if (value !== "linear" && value !== "nearest") {
- this.error("Unsupported value for 'magFilter': '" + value +
- "' - supported values are 'linear' and 'nearest'. Defaulting to 'linear'.");
- value = "linear";
- }
- return value;
- },
-
- _checkFilter: function (value) {
- value = value || "linear";
- if (value !== "linear" && value !== "nearest") {
- this.error("Unsupported value for 'magFilter': '" + value +
- "' - supported values are 'linear' and 'nearest'. Defaulting to 'linear'.");
- value = "linear";
- }
- return value;
- },
-
- _checkWrapS: function (value) {
- value = value || "repeat";
- if (value !== "clampToEdge" && value !== "mirroredRepeat" && value !== "repeat") {
- this.error("Unsupported value for 'wrapS': '" + value +
- "' - supported values are 'clampToEdge', 'mirroredRepeat' and 'repeat'. Defaulting to 'repeat'.");
- value = "repeat";
- }
- return value;
- },
-
- _checkWrapT: function (value) {
- value = value || "repeat";
- if (value !== "clampToEdge" && value !== "mirroredRepeat" && value !== "repeat") {
- this.error("Unsupported value for 'wrapT': '" + value +
- "' - supported values are 'clampToEdge', 'mirroredRepeat' and 'repeat'. Defaulting to 'repeat'.");
- value = "repeat";
- }
- return value;
- },
-
- _checkFlipY: function (value) {
- return !!value;
- },
-
- _checkEncoding: function (value) {
- value = value || "linear";
- if (value !== "linear" && value !== "sRGB" && value !== "gamma") {
- this.error("Unsupported value for 'encoding': '" + value + "' - supported values are 'linear', 'sRGB', 'gamma'. Defaulting to 'linear'.");
- value = "linear";
- }
- return value;
- },
-
- _webglContextRestored: function () {
- this._state.texture = new xeogl.renderer.Texture2D(this.scene.canvas.gl);
- if (this._image) {
- this.image = this._image;
- } else if (this._src) {
- this.src = this._src;
+ stats.memory.textures++;
+ }
+
+ _checkMinFilter(value) {
+ value = value || "linearMipmapLinear";
+ if (value !== "linear" &&
+ value !== "linearMipmapNearest" &&
+ value !== "linearMipmapLinear" &&
+ value !== "nearestMipmapLinear" &&
+ value !== "nearestMipmapNearest") {
+ this.error("Unsupported value for 'minFilter': '" + value +
+ "' - supported values are 'linear', 'linearMipmapNearest', 'nearestMipmapNearest', " +
+ "'nearestMipmapLinear' and 'linearMipmapLinear'. Defaulting to 'linearMipmapLinear'.");
+ value = "linearMipmapLinear";
+ }
+ return value;
+ }
+
+ _checkMagFilter(value) {
+ value = value || "linear";
+ if (value !== "linear" && value !== "nearest") {
+ this.error("Unsupported value for 'magFilter': '" + value +
+ "' - supported values are 'linear' and 'nearest'. Defaulting to 'linear'.");
+ value = "linear";
+ }
+ return value;
+ }
+
+ _checkFilter(value) {
+ value = value || "linear";
+ if (value !== "linear" && value !== "nearest") {
+ this.error("Unsupported value for 'magFilter': '" + value +
+ "' - supported values are 'linear' and 'nearest'. Defaulting to 'linear'.");
+ value = "linear";
+ }
+ return value;
+ }
+
+ _checkWrapS(value) {
+ value = value || "repeat";
+ if (value !== "clampToEdge" && value !== "mirroredRepeat" && value !== "repeat") {
+ this.error("Unsupported value for 'wrapS': '" + value +
+ "' - supported values are 'clampToEdge', 'mirroredRepeat' and 'repeat'. Defaulting to 'repeat'.");
+ value = "repeat";
+ }
+ return value;
+ }
+
+ _checkWrapT(value) {
+ value = value || "repeat";
+ if (value !== "clampToEdge" && value !== "mirroredRepeat" && value !== "repeat") {
+ this.error("Unsupported value for 'wrapT': '" + value +
+ "' - supported values are 'clampToEdge', 'mirroredRepeat' and 'repeat'. Defaulting to 'repeat'.");
+ value = "repeat";
+ }
+ return value;
+ }
+
+ _checkFlipY(value) {
+ return !!value;
+ }
+
+ _checkEncoding(value) {
+ value = value || "linear";
+ if (value !== "linear" && value !== "sRGB" && value !== "gamma") {
+ this.error("Unsupported value for 'encoding': '" + value + "' - supported values are 'linear', 'sRGB', 'gamma'. Defaulting to 'linear'.");
+ value = "linear";
+ }
+ return value;
+ }
+
+ _webglContextRestored() {
+ this._state.texture = new Texture2D(this.scene.canvas.gl);
+ if (this._image) {
+ this.image = this._image;
+ } else if (this._src) {
+ this.src = this._src;
+ }
+ }
+
+ _update() {
+ const state = this._state;
+ if (this._matrixDirty) {
+ let matrix;
+ let t;
+ if (this._translate[0] !== 0 || this._translate[1] !== 0) {
+ matrix = math.translationMat4v([this._translate[0], this._translate[1], 0], this._state.matrix);
}
- },
-
- _update: function () {
- const state = this._state;
- if (this._matrixDirty) {
- let matrix;
- let t;
- if (this._translate[0] !== 0 || this._translate[1] !== 0) {
- matrix = xeogl.math.translationMat4v([this._translate[0], this._translate[1], 0], this._state.matrix);
- }
- if (this._scale[0] !== 1 || this._scale[1] !== 1) {
- t = xeogl.math.scalingMat4v([this._scale[0], this._scale[1], 1]);
- matrix = matrix ? xeogl.math.mulMat4(matrix, t) : t;
- }
- if (this._rotate !== 0) {
- t = xeogl.math.rotationMat4v(this._rotate * 0.0174532925, [0, 0, 1]);
- matrix = matrix ? xeogl.math.mulMat4(matrix, t) : t;
- }
- if (matrix) {
- state.matrix = matrix;
- }
- this._matrixDirty = false;
+ if (this._scale[0] !== 1 || this._scale[1] !== 1) {
+ t = math.scalingMat4v([this._scale[0], this._scale[1], 1]);
+ matrix = matrix ? math.mulMat4(matrix, t) : t;
}
- this._renderer.imageDirty();
- },
-
- _props: {
-
- /**
- Indicates an HTML DOM Image object to source this Texture from.
-
- Sets the {{#crossLink "Texture/src:property"}}{{/crossLink}} property to null.
-
- @property image
- @default null
- @type {HTMLImageElement}
- */
- image: {
- set: function (value) {
- this._image = xeogl.renderer.ensureImageSizePowerOfTwo(value);
- this._image.crossOrigin = "Anonymous";
- this._state.texture.setImage(this._image, this._state);
- this._state.texture.setProps(this._state); // Generate mipmaps
- this._src = null;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._image;
- }
- },
-
- /**
- Indicates a path to an image file to source this Texture from.
-
- Sets the {{#crossLink "Texture/image:property"}}{{/crossLink}} property to null.
-
- @property src
- @default null
- @type String
- */
- src: {
- set: function (src) {
- this.scene.loading++;
- this.scene.canvas.spinner.processes++;
- const self = this;
- let image = new Image();
- image.onload = function () {
- image = xeogl.renderer.ensureImageSizePowerOfTwo(image);
- //self._image = image; // For faster WebGL context restore - memory inefficient?
- self._state.texture.setImage(image, self._state);
- self._state.texture.setProps(self._state); // Generate mipmaps
- self.scene.loading--;
- self.scene.canvas.spinner.processes--;
- self._renderer.imageDirty();
- };
- image.src = src;
- this._src = src;
- this._image = null;
- },
- get: function () {
- return this._src;
- }
- },
-
- /**
- 2D translation vector that will be added to this Texture's *S* and *T* coordinates.
-
- @property translate
- @default [0, 0]
- @type Array(Number)
- */
- translate: {
- set: function (value) {
- this._translate.set(value || [0, 0]);
- this._matrixDirty = true;
- this._needUpdate();
- },
- get: function () {
- return this._translate;
- }
- },
-
- /**
- 2D scaling vector that will be applied to this Texture's *S* and *T* coordinates.
-
- @property scale
- @default [1, 1]
- @type Array(Number)
- */
- scale: {
- set: function (value) {
- this._scale.set(value || [1, 1]);
- this._matrixDirty = true;
- this._needUpdate();
- },
- get: function () {
- return this._scale;
- }
- },
-
- /**
- Rotation, in degrees, that will be applied to this Texture's *S* and *T* coordinates.
-
- @property rotate
- @default 0
- @type Number
- */
- rotate: {
- set: function (value) {
- value = value || 0;
- if (this._rotate === value) {
- return;
- }
- this._rotate = value;
- this._matrixDirty = true;
- this._needUpdate();
- },
- get: function () {
- return this._rotate;
- }
+ if (this._rotate !== 0) {
+ t = math.rotationMat4v(this._rotate * 0.0174532925, [0, 0, 1]);
+ matrix = matrix ? math.mulMat4(matrix, t) : t;
}
- //,
-
- /**
- How this Texture is sampled when a texel covers less than one pixel.
-
- Options are:
-
- * **"nearest"** - Uses the value of the texture element that is nearest
- (in Manhattan distance) to the center of the pixel being textured.
-
- * **"linear"** - Uses the weighted average of the four texture elements that are
- closest to the center of the pixel being textured.
-
- * **"nearestMipmapNearest"** - Chooses the mipmap that most closely matches the
- size of the pixel being textured and uses the "nearest" criterion (the texture
- element nearest to the center of the pixel) to produce a texture value.
-
- * **"linearMipmapNearest"** - Chooses the mipmap that most closely matches the size of
- the pixel being textured and uses the "linear" criterion (a weighted average of the
- four texture elements that are closest to the center of the pixel) to produce a
- texture value.
-
- * **"nearestMipmapLinear"** - Chooses the two mipmaps that most closely
- match the size of the pixel being textured and uses the "nearest" criterion
- (the texture element nearest to the center of the pixel) to produce a texture
- value from each mipmap. The final texture value is a weighted average of those two
- values.
-
- * **"linearMipmapLinear"** - **(default)** - Chooses the two mipmaps that most closely match the size
- of the pixel being textured and uses the "linear" criterion (a weighted average
- of the four texture elements that are closest to the center of the pixel) to
- produce a texture value from each mipmap. The final texture value is a weighted
- average of those two values.
-
- @property minFilter
- @default "linearMipmapLinear"
- @type String
- @final
- */
- // minFilter: {
- // get: function () {
- // return this._state.minFilter;
- // }
- // },
-
- /**
- How this Texture is sampled when a texel covers more than one pixel.
-
- Options are:
-
- * **"nearest"** - Uses the value of the texture element that is nearest
- (in Manhattan distance) to the center of the pixel being textured.
- * **"linear"** - **(default)** - Uses the weighted average of the four texture elements that are
- closest to the center of the pixel being textured.
-
- @property magFilter
- @default "linear"
- @type String
- @final
- */
- // magFilter: {
- // get: function () {
- // return this._state.magFilter;
- // }
- // },
-
- /**
- Wrap parameter for this Texture's *S* coordinate.
-
- Options are:
-
- * **"clampToEdge"** - causes *S* coordinates to be clamped to the size of the texture.
- * **"mirroredRepeat"** - causes the *S* coordinate to be set to the fractional part of the texture coordinate
- if the integer part of *S* is even; if the integer part of *S* is odd, then the *S* texture coordinate is
- set to *1 - frac S* , where *frac S* represents the fractional part of *S*.
- * **"repeat"** - **(default)** - causes the integer part of the *S* coordinate to be ignored; xeogl uses only the
- fractional part, thereby creating a repeating pattern.
-
- @property wrapS
- @default "repeat"
- @type String
- @final
- */
- // wrapS: {
- // get: function () {
- // return this._state.wrapS;
- // }
- // },
-
- /**
- Wrap parameter for this Texture's *T* coordinate.
-
- Options are:
-
- * **"clampToEdge"** - Causes *T* coordinates to be clamped to the size of the texture.
- * **"mirroredRepeat"** - Causes the *T* coordinate to be set to the fractional part of the texture coordinate
- if the integer part of *T* is even; if the integer part of *T* is odd, then the *T* texture coordinate is
- set to *1 - frac S* , where *frac S* represents the fractional part of *T*.
- * **"repeat"** - **(default)** - Causes the integer part of the *T* coordinate to be ignored; xeogl uses only the
- fractional part, thereby creating a repeating pattern.
-
- @property wrapT
- @default "repeat"
- @type String
- @final
- */
- // wrapT: {
- // get: function () {
- // return this._state.wrapT;
- // }
- // },
-
- /**
- Flips this Texture's source data along its vertical axis when true.
-
- @property flipY
- @type Boolean
- @final
- */
- // flipY: {
- // get: function () {
- // return this._state.flipY;
- // }
- // },
-
- /**
- The Texture's encoding format.
-
- @property encoding
- @type String
- @final
- */
- // encoding: {
- // get: function () {
- // return this._state.encoding;
- // }
- // }
- },
-
- _destroy: function () {
- if (this._state.texture) {
- this._state.texture.destroy();
+ if (matrix) {
+ state.matrix = matrix;
}
- xeogl.stats.memory.textures--;
+ this._matrixDirty = false;
+ }
+ this._renderer.imageDirty();
+ }
+
+
+ /**
+ Indicates an HTML DOM Image object to source this Texture from.
+
+ Sets the {{#crossLink "Texture/src:property"}}{{/crossLink}} property to null.
+
+ @property image
+ @default null
+ @type {HTMLImageElement}
+ */
+ set image(value) {
+ this._image = ensureImageSizePowerOfTwo(value);
+ this._image.crossOrigin = "Anonymous";
+ this._state.texture.setImage(this._image, this._state);
+ this._state.texture.setProps(this._state); // Generate mipmaps
+ this._src = null;
+ this._renderer.imageDirty();
+ }
+
+ get image() {
+ return this._image;
+ }
+
+ /**
+ Indicates a path to an image file to source this Texture from.
+
+ Sets the {{#crossLink "Texture/image:property"}}{{/crossLink}} property to null.
+
+ @property src
+ @default null
+ @type String
+ */
+ set src(src) {
+ this.scene.loading++;
+ this.scene.canvas.spinner.processes++;
+ const self = this;
+ let image = new Image();
+ image.onload = function () {
+ image = ensureImageSizePowerOfTwo(image);
+ //self._image = image; // For faster WebGL context restore - memory inefficient?
+ self._state.texture.setImage(image, self._state);
+ self._state.texture.setProps(self._state); // Generate mipmaps
+ self.scene.loading--;
+ self.scene.canvas.spinner.processes--;
+ self._renderer.imageDirty();
+ };
+ image.src = src;
+ this._src = src;
+ this._image = null;
+ }
+
+ get src() {
+ return this._src;
+ }
+
+ /**
+ 2D translation vector that will be added to this Texture's *S* and *T* coordinates.
+
+ @property translate
+ @default [0, 0]
+ @type Array(Number)
+ */
+ set translate(value) {
+ this._translate.set(value || [0, 0]);
+ this._matrixDirty = true;
+ this._needUpdate();
+ }
+
+ get translate() {
+ return this._translate;
+ }
+
+ /**
+ 2D scaling vector that will be applied to this Texture's *S* and *T* coordinates.
+
+ @property scale
+ @default [1, 1]
+ @type Array(Number)
+ */
+ set scale(value) {
+ this._scale.set(value || [1, 1]);
+ this._matrixDirty = true;
+ this._needUpdate();
+ }
+
+ get scale() {
+ return this._scale;
+ }
+
+ /**
+ Rotation, in degrees, that will be applied to this Texture's *S* and *T* coordinates.
+
+ @property rotate
+ @default 0
+ @type Number
+ */
+ set rotate(value) {
+ value = value || 0;
+ if (this._rotate === value) {
+ return;
+ }
+ this._rotate = value;
+ this._matrixDirty = true;
+ this._needUpdate();
+ }
+
+ get rotate() {
+ return this._rotate;
+ }
+
+ /**
+ How this Texture is sampled when a texel covers less than one pixel.
+
+ Options are:
+
+ * **"nearest"** - Uses the value of the texture element that is nearest
+ (in Manhattan distance) to the center of the pixel being textured.
+
+ * **"linear"** - Uses the weighted average of the four texture elements that are
+ closest to the center of the pixel being textured.
+
+ * **"nearestMipmapNearest"** - Chooses the mipmap that most closely matches the
+ size of the pixel being textured and uses the "nearest" criterion (the texture
+ element nearest to the center of the pixel) to produce a texture value.
+
+ * **"linearMipmapNearest"** - Chooses the mipmap that most closely matches the size of
+ the pixel being textured and uses the "linear" criterion (a weighted average of the
+ four texture elements that are closest to the center of the pixel) to produce a
+ texture value.
+
+ * **"nearestMipmapLinear"** - Chooses the two mipmaps that most closely
+ match the size of the pixel being textured and uses the "nearest" criterion
+ (the texture element nearest to the center of the pixel) to produce a texture
+ value from each mipmap. The final texture value is a weighted average of those two
+ values.
+
+ * **"linearMipmapLinear"** - **(default)** - Chooses the two mipmaps that most closely match the size
+ of the pixel being textured and uses the "linear" criterion (a weighted average
+ of the four texture elements that are closest to the center of the pixel) to
+ produce a texture value from each mipmap. The final texture value is a weighted
+ average of those two values.
+
+ @property minFilter
+ @default "linearMipmapLinear"
+ @type String
+ @final
+ */
+ get minFilter() {
+ return this._state.minFilter;
+ }
+
+ /**
+ How this Texture is sampled when a texel covers more than one pixel.
+
+ Options are:
+
+ * **"nearest"** - Uses the value of the texture element that is nearest
+ (in Manhattan distance) to the center of the pixel being textured.
+ * **"linear"** - **(default)** - Uses the weighted average of the four texture elements that are
+ closest to the center of the pixel being textured.
+
+ @property magFilter
+ @default "linear"
+ @type String
+ @final
+ */
+ get magFilter() {
+ return this._state.magFilter;
+ }
+
+ /**
+ Wrap parameter for this Texture's *S* coordinate.
+
+ Options are:
+
+ * **"clampToEdge"** - causes *S* coordinates to be clamped to the size of the texture.
+ * **"mirroredRepeat"** - causes the *S* coordinate to be set to the fractional part of the texture coordinate
+ if the integer part of *S* is even; if the integer part of *S* is odd, then the *S* texture coordinate is
+ set to *1 - frac S* , where *frac S* represents the fractional part of *S*.
+ * **"repeat"** - **(default)** - causes the integer part of the *S* coordinate to be ignored; xeogl uses only the
+ fractional part, thereby creating a repeating pattern.
+
+ @property wrapS
+ @default "repeat"
+ @type String
+ @final
+ */
+ get wrapS() {
+ return this._state.wrapS;
+ }
+
+ /**
+ Wrap parameter for this Texture's *T* coordinate.
+
+ Options are:
+
+ * **"clampToEdge"** - Causes *T* coordinates to be clamped to the size of the texture.
+ * **"mirroredRepeat"** - Causes the *T* coordinate to be set to the fractional part of the texture coordinate
+ if the integer part of *T* is even; if the integer part of *T* is odd, then the *T* texture coordinate is
+ set to *1 - frac S* , where *frac S* represents the fractional part of *T*.
+ * **"repeat"** - **(default)** - Causes the integer part of the *T* coordinate to be ignored; xeogl uses only the
+ fractional part, thereby creating a repeating pattern.
+
+ @property wrapT
+ @default "repeat"
+ @type String
+ @final
+ */
+ get wrapT() {
+ return this._state.wrapT;
+ }
+
+ /**
+ Flips this Texture's source data along its vertical axis when true.
+
+ @property flipY
+ @type Boolean
+ @final
+ */
+ get flipY() {
+ return this._state.flipY;
+ }
+
+ /**
+ The Texture's encoding format.
+
+ @property encoding
+ @type String
+ @final
+ */
+ get encoding() {
+ return this._state.encoding;
+ }
+
+ destroy() {
+ super.destroy();
+ if (this._state.texture) {
+ this._state.texture.destroy();
}
- });
+ this._state.destroy();
+ stats.memory.textures--;
+ }
+}
-})();
+export{Texture};
\ No newline at end of file
diff --git a/src/math/math.js b/src/math/math.js
index 57d0cc54a..316dbca5b 100644
--- a/src/math/math.js
+++ b/src/math/math.js
@@ -1,3019 +1,4786 @@
-(function () {
-
- "use strict";
-
- // Some temporary vars to help avoid garbage collection
-
- const tempMat1 = new Float32Array(16);
- const tempMat2 = new Float32Array(16);
-
- const tempVec4 = new Float32Array(4);
-
- let caching = false;
- const vec3Cache = [];
- let vec3CacheLen = 0;
-
- /**
- * This utility object provides math functions that are used within xeogl. These functions are also part of xeogl's
- * public API and are therefore available for you to use within your application code.
- * @module xeogl
- * @submodule math
- * @class math
- * @static
- */
- const math = xeogl.math = {
-
- MAX_DOUBLE: Number.MAX_VALUE,
- MIN_DOUBLE: Number.MIN_VALUE,
-
- /**
- * The number of radiians in a degree (0.0174532925).
- * @property DEGTORAD
- * @namespace xeogl.math
- * @type {Number}
- */
- DEGTORAD: 0.0174532925,
-
- /**
- * The number of degrees in a radian.
- * @property RADTODEG
- * @namespace xeogl.math
- * @type {Number}
- */
- RADTODEG: 57.295779513,
-
- openCache: function () {
- caching = true;
- vec3CacheLen = 0;
- },
-
- cacheVec3: function (value) {
- return value || (caching ? (vec3Cache[vec3CacheLen++] || (vec3Cache[vec3CacheLen - 1] = new Float32Array(3))) : new Float32Array(3));
- },
-
- cacheVec4: function (value) {
- return value || (caching ? (vec3Cache[vec4CacheLen++] || (vec3Cache[vec4CacheLen - 1] = new Float32Array(4))) : new Float32Array(4));
- },
-
- closeCache: function () {
- caching = false;
- },
-
- /**
- * Returns a new, uninitialized two-element vector.
- * @method vec2
- * @param [values] Initial values.
- * @static
- * @returns {Float32Array}
- */
- vec2: function (values) {
- return new Float32Array(values || 2);
- },
-
- /**
- * Returns a new, uninitialized three-element vector.
- * @method vec3
- * @param [values] Initial values.
- * @static
- * @returns {Float32Array}
- */
- vec3: function (values) {
- return new Float32Array(values || 3);
- },
-
- /**
- * Returns a new, uninitialized four-element vector.
- * @method vec4
- * @param [values] Initial values.
- * @static
- * @returns {Float32Array}
- */
- vec4: function (values) {
- return new Float32Array(values || 4);
- },
-
- /**
- * Returns a new, uninitialized 3x3 matrix.
- * @method mat3
- * @param [values] Initial values.
- * @static
- * @returns {Float32Array}
- */
- mat3: function (values) {
- return new Float32Array(values || 9);
- },
-
- /**
- * Converts a 3x3 matrix to 4x4
- * @method mat3ToMat4
- * @param mat3 3x3 matrix.
- * @param mat4 4x4 matrix
- * @static
- * @returns {Float32Array}
- */
- mat3ToMat4: function (mat3, mat4) { // TODO
- mat4 = mat4 || new Float32Array(16);
- mat4[0] = mat3[0];
- mat4[1] = mat3[1];
- mat4[2] = mat3[2];
- mat4[3] = 0;
- mat4[4] = mat3[3];
- mat4[5] = mat3[4];
- mat4[6] = mat3[5];
- mat4[7] = 0;
- mat4[8] = mat3[6];
- mat4[9] = mat3[7];
- mat4[10] = mat3[8];
- mat4[11] = 0;
- mat4[12] = 0;
- mat4[13] = 0;
- mat4[14] = 0;
- mat4[15] = 1;
- return mat4;
- },
-
- /**
- * Returns a new, uninitialized 4x4 matrix.
- * @method mat4
- * @param [values] Initial values.
- * @static
- * @returns {Float32Array}
- */
- mat4: function (values) {
- return new Float32Array(values || 16);
- },
-
- /**
- * Converts a 4x4 matrix to 3x3
- * @method mat4ToMat3
- * @param mat4 4x4 matrix.
- * @param mat3 3x3 matrix
- * @static
- * @returns {Float32Array}
- */
- mat4ToMat3: function (mat4, mat3) { // TODO
- //return new Float32Array(values || 9);
- },
-
- /**
- * Returns a new UUID.
- * @method createUUID
- * @static
- * @return string The new UUID
- */
- //createUUID: function () {
- // // http://www.broofa.com/Tools/Math.uuid.htm
- // var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
- // var uuid = new Array(36);
- // var rnd = 0;
- // var r;
- // return function () {
- // for (var i = 0; i < 36; i++) {
- // if (i === 8 || i === 13 || i === 18 || i === 23) {
- // uuid[i] = '-';
- // } else if (i === 14) {
- // uuid[i] = '4';
- // } else {
- // if (rnd <= 0x02) {
- // rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0;
- // }
- // r = rnd & 0xf;
- // rnd = rnd >> 4;
- // uuid[i] = chars[( i === 19 ) ? ( r & 0x3 ) | 0x8 : r];
- // }
- // }
- // return uuid.join('');
- // };
- //}(),
- //
- createUUID: (function () {
- const self = {};
- const lut = [];
- for (let i = 0; i < 256; i++) {
- lut[i] = (i < 16 ? '0' : '') + (i).toString(16);
- }
- return function () {
- const d0 = Math.random() * 0xffffffff | 0;
- const d1 = Math.random() * 0xffffffff | 0;
- const d2 = Math.random() * 0xffffffff | 0;
- const d3 = Math.random() * 0xffffffff | 0;
- return lut[d0 & 0xff] + lut[d0 >> 8 & 0xff] + lut[d0 >> 16 & 0xff] + lut[d0 >> 24 & 0xff] + '-' +
- lut[d1 & 0xff] + lut[d1 >> 8 & 0xff] + '-' + lut[d1 >> 16 & 0x0f | 0x40] + lut[d1 >> 24 & 0xff] + '-' +
- lut[d2 & 0x3f | 0x80] + lut[d2 >> 8 & 0xff] + '-' + lut[d2 >> 16 & 0xff] + lut[d2 >> 24 & 0xff] +
- lut[d3 & 0xff] + lut[d3 >> 8 & 0xff] + lut[d3 >> 16 & 0xff] + lut[d3 >> 24 & 0xff];
- };
- })(),
-
- /**
- * Clamps a value to the given range.
- * @param {Number} value Value to clamp.
- * @param {Number} min Lower bound.
- * @param {Number} max Upper bound.
- * @returns {Number} Clamped result.
- */
- clamp: function (value, min, max) {
- return Math.max(min, Math.min(max, value));
- },
-
- /**
- * Floating-point modulus
- * @method fmod
- * @static
- * @param {Number} a
- * @param {Number} b
- * @returns {*}
- */
- fmod: function (a, b) {
- if (a < b) {
- console.error("xeogl.math.fmod : Attempting to find modulus within negative range - would be infinite loop - ignoring");
- return a;
+// Some temporary vars to help avoid garbage collection
+
+const tempMat1 = new Float32Array(16);
+const tempMat2 = new Float32Array(16);
+const tempVec4 = new Float32Array(4);
+
+let caching = false;
+const vec3Cache = [];
+let vec3CacheLen = 0;
+
+const math = {
+
+ MAX_DOUBLE: Number.MAX_VALUE,
+ MIN_DOUBLE: Number.MIN_VALUE,
+
+ /**
+ * The number of radiians in a degree (0.0174532925).
+ * @property DEGTORAD
+ * @type {Number}
+ */
+ DEGTORAD: 0.0174532925,
+
+ /**
+ * The number of degrees in a radian.
+ * @property RADTODEG
+ * @type {Number}
+ */
+ RADTODEG: 57.295779513,
+
+ openCache() {
+ caching = true;
+ vec3CacheLen = 0;
+ },
+
+ cacheVec3(value) {
+ return value || (caching ? (vec3Cache[vec3CacheLen++] || (vec3Cache[vec3CacheLen - 1] = new Float32Array(3))) : new Float32Array(3));
+ },
+
+ cacheVec4(value) {
+ return value || (caching ? (vec3Cache[vec4CacheLen++] || (vec3Cache[vec4CacheLen - 1] = new Float32Array(4))) : new Float32Array(4));
+ },
+
+ closeCache() {
+ caching = false;
+ },
+
+ /**
+ * Returns a new, uninitialized two-element vector.
+ * @method vec2
+ * @param [values] Initial values.
+ * @static
+ * @returns {Float32Array}
+ */
+ vec2(values) {
+ return new Float32Array(values || 2);
+ },
+
+ /**
+ * Returns a new, uninitialized three-element vector.
+ * @method vec3
+ * @param [values] Initial values.
+ * @static
+ * @returns {Float32Array}
+ */
+ vec3(values) {
+ return new Float32Array(values || 3);
+ },
+
+ /**
+ * Returns a new, uninitialized four-element vector.
+ * @method vec4
+ * @param [values] Initial values.
+ * @static
+ * @returns {Float32Array}
+ */
+ vec4(values) {
+ return new Float32Array(values || 4);
+ },
+
+ /**
+ * Returns a new, uninitialized 3x3 matrix.
+ * @method mat3
+ * @param [values] Initial values.
+ * @static
+ * @returns {Float32Array}
+ */
+ mat3(values) {
+ return new Float32Array(values || 9);
+ },
+
+ /**
+ * Converts a 3x3 matrix to 4x4
+ * @method mat3ToMat4
+ * @param mat3 3x3 matrix.
+ * @param mat4 4x4 matrix
+ * @static
+ * @returns {Float32Array}
+ */
+ mat3ToMat4(mat3, mat4 = new Float32Array(16)) {
+ mat4[0] = mat3[0];
+ mat4[1] = mat3[1];
+ mat4[2] = mat3[2];
+ mat4[3] = 0;
+ mat4[4] = mat3[3];
+ mat4[5] = mat3[4];
+ mat4[6] = mat3[5];
+ mat4[7] = 0;
+ mat4[8] = mat3[6];
+ mat4[9] = mat3[7];
+ mat4[10] = mat3[8];
+ mat4[11] = 0;
+ mat4[12] = 0;
+ mat4[13] = 0;
+ mat4[14] = 0;
+ mat4[15] = 1;
+ return mat4;
+ },
+
+ /**
+ * Returns a new, uninitialized 4x4 matrix.
+ * @method mat4
+ * @param [values] Initial values.
+ * @static
+ * @returns {Float32Array}
+ */
+ mat4(values) {
+ return new Float32Array(values || 16);
+ },
+
+ /**
+ * Converts a 4x4 matrix to 3x3
+ * @method mat4ToMat3
+ * @param mat4 4x4 matrix.
+ * @param mat3 3x3 matrix
+ * @static
+ * @returns {Float32Array}
+ */
+ mat4ToMat3(mat4, mat3) { // TODO
+ //return new Float32Array(values || 9);
+ },
+
+ /**
+ * Returns a new UUID.
+ * @method createUUID
+ * @static
+ * @return string The new UUID
+ */
+ //createUUID: function () {
+ // // http://www.broofa.com/Tools/Math.uuid.htm
+ // var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
+ // var uuid = new Array(36);
+ // var rnd = 0;
+ // var r;
+ // return function () {
+ // for (var i = 0; i < 36; i++) {
+ // if (i === 8 || i === 13 || i === 18 || i === 23) {
+ // uuid[i] = '-';
+ // } else if (i === 14) {
+ // uuid[i] = '4';
+ // } else {
+ // if (rnd <= 0x02) {
+ // rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0;
+ // }
+ // r = rnd & 0xf;
+ // rnd = rnd >> 4;
+ // uuid[i] = chars[( i === 19 ) ? ( r & 0x3 ) | 0x8 : r];
+ // }
+ // }
+ // return uuid.join('');
+ // };
+ //}(),
+ //
+ createUUID: ((() => {
+ const self = {};
+ const lut = [];
+ for (let i = 0; i < 256; i++) {
+ lut[i] = (i < 16 ? '0' : '') + (i).toString(16);
+ }
+ return () => {
+ const d0 = Math.random() * 0xffffffff | 0;
+ const d1 = Math.random() * 0xffffffff | 0;
+ const d2 = Math.random() * 0xffffffff | 0;
+ const d3 = Math.random() * 0xffffffff | 0;
+ return `${lut[d0 & 0xff] + lut[d0 >> 8 & 0xff] + lut[d0 >> 16 & 0xff] + lut[d0 >> 24 & 0xff]}-${lut[d1 & 0xff]}${lut[d1 >> 8 & 0xff]}-${lut[d1 >> 16 & 0x0f | 0x40]}${lut[d1 >> 24 & 0xff]}-${lut[d2 & 0x3f | 0x80]}${lut[d2 >> 8 & 0xff]}-${lut[d2 >> 16 & 0xff]}${lut[d2 >> 24 & 0xff]}${lut[d3 & 0xff]}${lut[d3 >> 8 & 0xff]}${lut[d3 >> 16 & 0xff]}${lut[d3 >> 24 & 0xff]}`;
+ };
+ }))(),
+
+ /**
+ * Clamps a value to the given range.
+ * @param {Number} value Value to clamp.
+ * @param {Number} min Lower bound.
+ * @param {Number} max Upper bound.
+ * @returns {Number} Clamped result.
+ */
+ clamp(value, min, max) {
+ return Math.max(min, Math.min(max, value));
+ },
+
+ /**
+ * Floating-point modulus
+ * @method fmod
+ * @static
+ * @param {Number} a
+ * @param {Number} b
+ * @returns {*}
+ */
+ fmod(a, b) {
+ if (a < b) {
+ console.error("xeogl.math.fmod : Attempting to find modulus within negative range - would be infinite loop - ignoring");
+ return a;
+ }
+ while (b <= a) {
+ a -= b;
+ }
+ return a;
+ },
+
+ /**
+ * Negates a four-element vector.
+ * @method negateVec4
+ * @static
+ * @param {Array(Number)} v Vector to negate
+ * @param {Array(Number)} [dest] Destination vector
+ * @return {Array(Number)} dest if specified, v otherwise
+ */
+ negateVec4(v, dest) {
+ if (!dest) {
+ dest = v;
+ }
+ dest[0] = -v[0];
+ dest[1] = -v[1];
+ dest[2] = -v[2];
+ dest[3] = -v[3];
+ return dest;
+ },
+
+ /**
+ * Adds one four-element vector to another.
+ * @method addVec4
+ * @static
+ * @param {Array(Number)} u First vector
+ * @param {Array(Number)} v Second vector
+ * @param {Array(Number)} [dest] Destination vector
+ * @return {Array(Number)} dest if specified, u otherwise
+ */
+ addVec4(u, v, dest) {
+ if (!dest) {
+ dest = u;
+ }
+ dest[0] = u[0] + v[0];
+ dest[1] = u[1] + v[1];
+ dest[2] = u[2] + v[2];
+ dest[3] = u[3] + v[3];
+ return dest;
+ },
+
+ /**
+ * Adds a scalar value to each element of a four-element vector.
+ * @method addVec4Scalar
+ * @static
+ * @param {Array(Number)} v The vector
+ * @param {Number} s The scalar
+ * @param {Array(Number)} [dest] Destination vector
+ * @return {Array(Number)} dest if specified, v otherwise
+ */
+ addVec4Scalar(v, s, dest) {
+ if (!dest) {
+ dest = v;
+ }
+ dest[0] = v[0] + s;
+ dest[1] = v[1] + s;
+ dest[2] = v[2] + s;
+ dest[3] = v[3] + s;
+ return dest;
+ },
+
+ /**
+ * Adds one three-element vector to another.
+ * @method addVec3
+ * @static
+ * @param {Array(Number)} u First vector
+ * @param {Array(Number)} v Second vector
+ * @param {Array(Number)} [dest] Destination vector
+ * @return {Array(Number)} dest if specified, u otherwise
+ */
+ addVec3(u, v, dest) {
+ if (!dest) {
+ dest = u;
+ }
+ dest[0] = u[0] + v[0];
+ dest[1] = u[1] + v[1];
+ dest[2] = u[2] + v[2];
+ return dest;
+ },
+
+ /**
+ * Adds a scalar value to each element of a three-element vector.
+ * @method addVec4Scalar
+ * @static
+ * @param {Array(Number)} v The vector
+ * @param {Number} s The scalar
+ * @param {Array(Number)} [dest] Destination vector
+ * @return {Array(Number)} dest if specified, v otherwise
+ */
+ addVec3Scalar(v, s, dest) {
+ if (!dest) {
+ dest = v;
+ }
+ dest[0] = v[0] + s;
+ dest[1] = v[1] + s;
+ dest[2] = v[2] + s;
+ return dest;
+ },
+
+ /**
+ * Subtracts one four-element vector from another.
+ * @method subVec4
+ * @static
+ * @param {Array(Number)} u First vector
+ * @param {Array(Number)} v Vector to subtract
+ * @param {Array(Number)} [dest] Destination vector
+ * @return {Array(Number)} dest if specified, u otherwise
+ */
+ subVec4(u, v, dest) {
+ if (!dest) {
+ dest = u;
+ }
+ dest[0] = u[0] - v[0];
+ dest[1] = u[1] - v[1];
+ dest[2] = u[2] - v[2];
+ dest[3] = u[3] - v[3];
+ return dest;
+ },
+
+ /**
+ * Subtracts one three-element vector from another.
+ * @method subVec3
+ * @static
+ * @param {Array(Number)} u First vector
+ * @param {Array(Number)} v Vector to subtract
+ * @param {Array(Number)} [dest] Destination vector
+ * @return {Array(Number)} dest if specified, u otherwise
+ */
+ subVec3(u, v, dest) {
+ if (!dest) {
+ dest = u;
+ }
+ dest[0] = u[0] - v[0];
+ dest[1] = u[1] - v[1];
+ dest[2] = u[2] - v[2];
+ return dest;
+ },
+
+ /**
+ * Subtracts one two-element vector from another.
+ * @method subVec2
+ * @static
+ * @param {Array(Number)} u First vector
+ * @param {Array(Number)} v Vector to subtract
+ * @param {Array(Number)} [dest] Destination vector
+ * @return {Array(Number)} dest if specified, u otherwise
+ */
+ subVec2(u, v, dest) {
+ if (!dest) {
+ dest = u;
+ }
+ dest[0] = u[0] - v[0];
+ dest[1] = u[1] - v[1];
+ return dest;
+ },
+
+ /**
+ * Subtracts a scalar value from each element of a four-element vector.
+ * @method subVec4Scalar
+ * @static
+ * @param {Array(Number)} v The vector
+ * @param {Number} s The scalar
+ * @param {Array(Number)} [dest] Destination vector
+ * @return {Array(Number)} dest if specified, v otherwise
+ */
+ subVec4Scalar(v, s, dest) {
+ if (!dest) {
+ dest = v;
+ }
+ dest[0] = v[0] - s;
+ dest[1] = v[1] - s;
+ dest[2] = v[2] - s;
+ dest[3] = v[3] - s;
+ return dest;
+ },
+
+ /**
+ * Sets each element of a 4-element vector to a scalar value minus the value of that element.
+ * @method subScalarVec4
+ * @static
+ * @param {Array(Number)} v The vector
+ * @param {Number} s The scalar
+ * @param {Array(Number)} [dest] Destination vector
+ * @return {Array(Number)} dest if specified, v otherwise
+ */
+ subScalarVec4(v, s, dest) {
+ if (!dest) {
+ dest = v;
+ }
+ dest[0] = s - v[0];
+ dest[1] = s - v[1];
+ dest[2] = s - v[2];
+ dest[3] = s - v[3];
+ return dest;
+ },
+
+ /**
+ * Multiplies one three-element vector by another.
+ * @method mulVec3
+ * @static
+ * @param {Array(Number)} u First vector
+ * @param {Array(Number)} v Second vector
+ * @param {Array(Number)} [dest] Destination vector
+ * @return {Array(Number)} dest if specified, u otherwise
+ */
+ mulVec4(u, v, dest) {
+ if (!dest) {
+ dest = u;
+ }
+ dest[0] = u[0] * v[0];
+ dest[1] = u[1] * v[1];
+ dest[2] = u[2] * v[2];
+ dest[3] = u[3] * v[3];
+ return dest;
+ },
+
+ /**
+ * Multiplies each element of a four-element vector by a scalar.
+ * @method mulVec34calar
+ * @static
+ * @param {Array(Number)} v The vector
+ * @param {Number} s The scalar
+ * @param {Array(Number)} [dest] Destination vector
+ * @return {Array(Number)} dest if specified, v otherwise
+ */
+ mulVec4Scalar(v, s, dest) {
+ if (!dest) {
+ dest = v;
+ }
+ dest[0] = v[0] * s;
+ dest[1] = v[1] * s;
+ dest[2] = v[2] * s;
+ dest[3] = v[3] * s;
+ return dest;
+ },
+
+ /**
+ * Multiplies each element of a three-element vector by a scalar.
+ * @method mulVec3Scalar
+ * @static
+ * @param {Array(Number)} v The vector
+ * @param {Number} s The scalar
+ * @param {Array(Number)} [dest] Destination vector
+ * @return {Array(Number)} dest if specified, v otherwise
+ */
+ mulVec3Scalar(v, s, dest) {
+ if (!dest) {
+ dest = v;
+ }
+ dest[0] = v[0] * s;
+ dest[1] = v[1] * s;
+ dest[2] = v[2] * s;
+ return dest;
+ },
+
+ /**
+ * Multiplies each element of a two-element vector by a scalar.
+ * @method mulVec2Scalar
+ * @static
+ * @param {Array(Number)} v The vector
+ * @param {Number} s The scalar
+ * @param {Array(Number)} [dest] Destination vector
+ * @return {Array(Number)} dest if specified, v otherwise
+ */
+ mulVec2Scalar(v, s, dest) {
+ if (!dest) {
+ dest = v;
+ }
+ dest[0] = v[0] * s;
+ dest[1] = v[1] * s;
+ return dest;
+ },
+
+ /**
+ * Divides one three-element vector by another.
+ * @method divVec3
+ * @static
+ * @param {Array(Number)} u First vector
+ * @param {Array(Number)} v Second vector
+ * @param {Array(Number)} [dest] Destination vector
+ * @return {Array(Number)} dest if specified, u otherwise
+ */
+ divVec3(u, v, dest) {
+ if (!dest) {
+ dest = u;
+ }
+ dest[0] = u[0] / v[0];
+ dest[1] = u[1] / v[1];
+ dest[2] = u[2] / v[2];
+ return dest;
+ },
+
+ /**
+ * Divides one four-element vector by another.
+ * @method divVec4
+ * @static
+ * @param {Array(Number)} u First vector
+ * @param {Array(Number)} v Second vector
+ * @param {Array(Number)} [dest] Destination vector
+ * @return {Array(Number)} dest if specified, u otherwise
+ */
+ divVec4(u, v, dest) {
+ if (!dest) {
+ dest = u;
+ }
+ dest[0] = u[0] / v[0];
+ dest[1] = u[1] / v[1];
+ dest[2] = u[2] / v[2];
+ dest[3] = u[3] / v[3];
+ return dest;
+ },
+
+ /**
+ * Divides a scalar by a three-element vector, returning a new vector.
+ * @method divScalarVec3
+ * @static
+ * @param v vec3
+ * @param s scalar
+ * @param dest vec3 - optional destination
+ * @return [] dest if specified, v otherwise
+ */
+ divScalarVec3(s, v, dest) {
+ if (!dest) {
+ dest = v;
+ }
+ dest[0] = s / v[0];
+ dest[1] = s / v[1];
+ dest[2] = s / v[2];
+ return dest;
+ },
+
+ /**
+ * Divides a three-element vector by a scalar.
+ * @method divVec3Scalar
+ * @static
+ * @param v vec3
+ * @param s scalar
+ * @param dest vec3 - optional destination
+ * @return [] dest if specified, v otherwise
+ */
+ divVec3Scalar(v, s, dest) {
+ if (!dest) {
+ dest = v;
+ }
+ dest[0] = v[0] / s;
+ dest[1] = v[1] / s;
+ dest[2] = v[2] / s;
+ return dest;
+ },
+
+ /**
+ * Divides a four-element vector by a scalar.
+ * @method divVec4Scalar
+ * @static
+ * @param v vec4
+ * @param s scalar
+ * @param dest vec4 - optional destination
+ * @return [] dest if specified, v otherwise
+ */
+ divVec4Scalar(v, s, dest) {
+ if (!dest) {
+ dest = v;
+ }
+ dest[0] = v[0] / s;
+ dest[1] = v[1] / s;
+ dest[2] = v[2] / s;
+ dest[3] = v[3] / s;
+ return dest;
+ },
+
+
+ /**
+ * Divides a scalar by a four-element vector, returning a new vector.
+ * @method divScalarVec4
+ * @static
+ * @param s scalar
+ * @param v vec4
+ * @param dest vec4 - optional destination
+ * @return [] dest if specified, v otherwise
+ */
+ divScalarVec4(s, v, dest) {
+ if (!dest) {
+ dest = v;
+ }
+ dest[0] = s / v[0];
+ dest[1] = s / v[1];
+ dest[2] = s / v[2];
+ dest[3] = s / v[3];
+ return dest;
+ },
+
+ /**
+ * Returns the dot product of two four-element vectors.
+ * @method dotVec4
+ * @static
+ * @param {Array(Number)} u First vector
+ * @param {Array(Number)} v Second vector
+ * @return The dot product
+ */
+ dotVec4(u, v) {
+ return (u[0] * v[0] + u[1] * v[1] + u[2] * v[2] + u[3] * v[3]);
+ },
+
+ /**
+ * Returns the cross product of two four-element vectors.
+ * @method cross3Vec4
+ * @static
+ * @param {Array(Number)} u First vector
+ * @param {Array(Number)} v Second vector
+ * @return The cross product
+ */
+ cross3Vec4(u, v) {
+ const u0 = u[0];
+ const u1 = u[1];
+ const u2 = u[2];
+ const v0 = v[0];
+ const v1 = v[1];
+ const v2 = v[2];
+ return [
+ u1 * v2 - u2 * v1,
+ u2 * v0 - u0 * v2,
+ u0 * v1 - u1 * v0,
+ 0.0];
+ },
+
+ /**
+ * Returns the cross product of two three-element vectors.
+ * @method cross3Vec3
+ * @static
+ * @param {Array(Number)} u First vector
+ * @param {Array(Number)} v Second vector
+ * @return The cross product
+ */
+ cross3Vec3(u, v, dest) {
+ if (!dest) {
+ dest = u;
+ }
+ const x = u[0];
+ const y = u[1];
+ const z = u[2];
+ const x2 = v[0];
+ const y2 = v[1];
+ const z2 = v[2];
+ dest[0] = y * z2 - z * y2;
+ dest[1] = z * x2 - x * z2;
+ dest[2] = x * y2 - y * x2;
+ return dest;
+ },
+
+
+ sqLenVec4(v) { // TODO
+ return math.dotVec4(v, v);
+ },
+
+ /**
+ * Returns the length of a four-element vector.
+ * @method lenVec4
+ * @static
+ * @param {Array(Number)} v The vector
+ * @return The length
+ */
+ lenVec4(v) {
+ return Math.sqrt(math.sqLenVec4(v));
+ },
+
+ /**
+ * Returns the dot product of two three-element vectors.
+ * @method dotVec3
+ * @static
+ * @param {Array(Number)} u First vector
+ * @param {Array(Number)} v Second vector
+ * @return The dot product
+ */
+ dotVec3(u, v) {
+ return (u[0] * v[0] + u[1] * v[1] + u[2] * v[2]);
+ },
+
+ /**
+ * Returns the dot product of two two-element vectors.
+ * @method dotVec4
+ * @static
+ * @param {Array(Number)} u First vector
+ * @param {Array(Number)} v Second vector
+ * @return The dot product
+ */
+ dotVec2(u, v) {
+ return (u[0] * v[0] + u[1] * v[1]);
+ },
+
+
+ sqLenVec3(v) {
+ return math.dotVec3(v, v);
+ },
+
+
+ sqLenVec2(v) {
+ return math.dotVec2(v, v);
+ },
+
+ /**
+ * Returns the length of a three-element vector.
+ * @method lenVec3
+ * @static
+ * @param {Array(Number)} v The vector
+ * @return The length
+ */
+ lenVec3(v) {
+ return Math.sqrt(math.sqLenVec3(v));
+ },
+
+ distVec3: ((() => {
+ const vec = new Float32Array(3);
+ return (v, w) => math.lenVec3(math.subVec3(v, w, vec));
+ }))(),
+
+ /**
+ * Returns the length of a two-element vector.
+ * @method lenVec2
+ * @static
+ * @param {Array(Number)} v The vector
+ * @return The length
+ */
+ lenVec2(v) {
+ return Math.sqrt(math.sqLenVec2(v));
+ },
+
+ distVec2: ((() => {
+ const vec = new Float32Array(2);
+ return (v, w) => math.lenVec2(math.subVec2(v, w, vec));
+ }))(),
+
+ /**
+ * @method rcpVec3
+ * @static
+ * @param v vec3
+ * @param dest vec3 - optional destination
+ * @return [] dest if specified, v otherwise
+ *
+ */
+ rcpVec3(v, dest) {
+ return math.divScalarVec3(1.0, v, dest);
+ },
+
+ /**
+ * Normalizes a four-element vector
+ * @method normalizeVec4
+ * @static
+ * @param v vec4
+ * @param dest vec4 - optional destination
+ * @return [] dest if specified, v otherwise
+ *
+ */
+ normalizeVec4(v, dest) {
+ const f = 1.0 / math.lenVec4(v);
+ return math.mulVec4Scalar(v, f, dest);
+ },
+
+ /**
+ * Normalizes a three-element vector
+ * @method normalizeVec4
+ * @static
+ */
+ normalizeVec3(v, dest) {
+ const f = 1.0 / math.lenVec3(v);
+ return math.mulVec3Scalar(v, f, dest);
+ },
+
+ /**
+ * Normalizes a two-element vector
+ * @method normalizeVec2
+ * @static
+ */
+ normalizeVec2(v, dest) {
+ const f = 1.0 / math.lenVec2(v);
+ return math.mulVec2Scalar(v, f, dest);
+ },
+
+ /**
+ * Gets the angle between two vectors
+ * @method angleVec3
+ * @param v
+ * @param w
+ * @returns {number}
+ */
+ angleVec3(v, w) {
+ let theta = math.dotVec3(v, w) / ( Math.sqrt(math.sqLenVec3(v) * math.sqLenVec3(w)) );
+ theta = theta < -1 ? -1 : (theta > 1 ? 1 : theta); // Clamp to handle numerical problems
+ return Math.acos(theta);
+ },
+
+ /**
+ * Creates a three-element vector from the rotation part of a sixteen-element matrix.
+ * @param m
+ * @param dest
+ */
+ vec3FromMat4Scale: ((() => {
+
+ const tempVec3 = new Float32Array(3);
+
+ return (m, dest) => {
+
+ tempVec3[0] = m[0];
+ tempVec3[1] = m[1];
+ tempVec3[2] = m[2];
+
+ dest[0] = math.lenVec3(tempVec3);
+
+ tempVec3[0] = m[4];
+ tempVec3[1] = m[5];
+ tempVec3[2] = m[6];
+
+ dest[1] = math.lenVec3(tempVec3);
+
+ tempVec3[0] = m[8];
+ tempVec3[1] = m[9];
+ tempVec3[2] = m[10];
+
+ dest[2] = math.lenVec3(tempVec3);
+
+ return dest;
+ };
+ }))(),
+
+ /**
+ * Converts an n-element vector to a JSON-serializable
+ * array with values rounded to two decimal places.
+ */
+ vecToArray: ((() => {
+ function trunc(v) {
+ return Math.round(v * 100000) / 100000
+ }
+
+ return v => {
+ v = Array.prototype.slice.call(v);
+ for (let i = 0, len = v.length; i < len; i++) {
+ v[i] = trunc(v[i]);
}
- while (b <= a) {
- a -= b;
+ return v;
+ };
+ }))(),
+
+ /**
+ * Duplicates a 4x4 identity matrix.
+ * @method dupMat4
+ * @static
+ */
+ dupMat4(m) {
+ return m.slice(0, 16);
+ },
+
+ /**
+ * Extracts a 3x3 matrix from a 4x4 matrix.
+ * @method mat4To3
+ * @static
+ */
+ mat4To3(m) {
+ return [
+ m[0], m[1], m[2],
+ m[4], m[5], m[6],
+ m[8], m[9], m[10]
+ ];
+ },
+
+ /**
+ * Returns a 4x4 matrix with each element set to the given scalar value.
+ * @method m4s
+ * @static
+ */
+ m4s(s) {
+ return [
+ s, s, s, s,
+ s, s, s, s,
+ s, s, s, s,
+ s, s, s, s
+ ];
+ },
+
+ /**
+ * Returns a 4x4 matrix with each element set to zero.
+ * @method setMat4ToZeroes
+ * @static
+ */
+ setMat4ToZeroes() {
+ return math.m4s(0.0);
+ },
+
+ /**
+ * Returns a 4x4 matrix with each element set to 1.0.
+ * @method setMat4ToOnes
+ * @static
+ */
+ setMat4ToOnes() {
+ return math.m4s(1.0);
+ },
+
+ /**
+ * Returns a 4x4 matrix with each element set to 1.0.
+ * @method setMat4ToOnes
+ * @static
+ */
+ diagonalMat4v(v) {
+ return new Float32Array([
+ v[0], 0.0, 0.0, 0.0,
+ 0.0, v[1], 0.0, 0.0,
+ 0.0, 0.0, v[2], 0.0,
+ 0.0, 0.0, 0.0, v[3]
+ ]);
+ },
+
+ /**
+ * Returns a 4x4 matrix with diagonal elements set to the given vector.
+ * @method diagonalMat4c
+ * @static
+ */
+ diagonalMat4c(x, y, z, w) {
+ return math.diagonalMat4v([x, y, z, w]);
+ },
+
+ /**
+ * Returns a 4x4 matrix with diagonal elements set to the given scalar.
+ * @method diagonalMat4s
+ * @static
+ */
+ diagonalMat4s(s) {
+ return math.diagonalMat4c(s, s, s, s);
+ },
+
+ /**
+ * Returns a 4x4 identity matrix.
+ * @method identityMat4
+ * @static
+ */
+ identityMat4(mat = new Float32Array(16)) {
+ mat[0] = 1.0;
+ mat[1] = 0.0;
+ mat[2] = 0.0;
+ mat[3] = 0.0;
+
+ mat[4] = 0.0;
+ mat[5] = 1.0;
+ mat[6] = 0.0;
+ mat[7] = 0.0;
+
+ mat[8] = 0.0;
+ mat[9] = 0.0;
+ mat[10] = 1.0;
+ mat[11] = 0.0;
+
+ mat[12] = 0.0;
+ mat[13] = 0.0;
+ mat[14] = 0.0;
+ mat[15] = 1.0;
+
+ return mat;
+ },
+
+ /**
+ * Returns a 3x3 identity matrix.
+ * @method identityMat3
+ * @static
+ */
+ identityMat3(mat = new Float32Array(9)) {
+ mat[0] = 1.0;
+ mat[1] = 0.0;
+ mat[2] = 0.0;
+
+ mat[3] = 0.0;
+ mat[4] = 1.0;
+ mat[5] = 0.0;
+
+ mat[6] = 0.0;
+ mat[7] = 0.0;
+ mat[8] = 1.0;
+
+ return mat;
+ },
+
+ /**
+ * Tests if the given 4x4 matrix is the identity matrix.
+ * @method isIdentityMat4
+ * @static
+ */
+ isIdentityMat4(m) {
+ if (m[0] !== 1.0 || m[1] !== 0.0 || m[2] !== 0.0 || m[3] !== 0.0 ||
+ m[4] !== 0.0 || m[5] !== 1.0 || m[6] !== 0.0 || m[7] !== 0.0 ||
+ m[8] !== 0.0 || m[9] !== 0.0 || m[10] !== 1.0 || m[11] !== 0.0 ||
+ m[12] !== 0.0 || m[13] !== 0.0 || m[14] !== 0.0 || m[15] !== 1.0) {
+ return false;
+ }
+ return true;
+ },
+
+ /**
+ * Negates the given 4x4 matrix.
+ * @method negateMat4
+ * @static
+ */
+ negateMat4(m, dest) {
+ if (!dest) {
+ dest = m;
+ }
+ dest[0] = -m[0];
+ dest[1] = -m[1];
+ dest[2] = -m[2];
+ dest[3] = -m[3];
+ dest[4] = -m[4];
+ dest[5] = -m[5];
+ dest[6] = -m[6];
+ dest[7] = -m[7];
+ dest[8] = -m[8];
+ dest[9] = -m[9];
+ dest[10] = -m[10];
+ dest[11] = -m[11];
+ dest[12] = -m[12];
+ dest[13] = -m[13];
+ dest[14] = -m[14];
+ dest[15] = -m[15];
+ return dest;
+ },
+
+ /**
+ * Adds the given 4x4 matrices together.
+ * @method addMat4
+ * @static
+ */
+ addMat4(a, b, dest) {
+ if (!dest) {
+ dest = a;
+ }
+ dest[0] = a[0] + b[0];
+ dest[1] = a[1] + b[1];
+ dest[2] = a[2] + b[2];
+ dest[3] = a[3] + b[3];
+ dest[4] = a[4] + b[4];
+ dest[5] = a[5] + b[5];
+ dest[6] = a[6] + b[6];
+ dest[7] = a[7] + b[7];
+ dest[8] = a[8] + b[8];
+ dest[9] = a[9] + b[9];
+ dest[10] = a[10] + b[10];
+ dest[11] = a[11] + b[11];
+ dest[12] = a[12] + b[12];
+ dest[13] = a[13] + b[13];
+ dest[14] = a[14] + b[14];
+ dest[15] = a[15] + b[15];
+ return dest;
+ },
+
+ /**
+ * Adds the given scalar to each element of the given 4x4 matrix.
+ * @method addMat4Scalar
+ * @static
+ */
+ addMat4Scalar(m, s, dest) {
+ if (!dest) {
+ dest = m;
+ }
+ dest[0] = m[0] + s;
+ dest[1] = m[1] + s;
+ dest[2] = m[2] + s;
+ dest[3] = m[3] + s;
+ dest[4] = m[4] + s;
+ dest[5] = m[5] + s;
+ dest[6] = m[6] + s;
+ dest[7] = m[7] + s;
+ dest[8] = m[8] + s;
+ dest[9] = m[9] + s;
+ dest[10] = m[10] + s;
+ dest[11] = m[11] + s;
+ dest[12] = m[12] + s;
+ dest[13] = m[13] + s;
+ dest[14] = m[14] + s;
+ dest[15] = m[15] + s;
+ return dest;
+ },
+
+ /**
+ * Adds the given scalar to each element of the given 4x4 matrix.
+ * @method addScalarMat4
+ * @static
+ */
+ addScalarMat4(s, m, dest) {
+ return math.addMat4Scalar(m, s, dest);
+ },
+
+ /**
+ * Subtracts the second 4x4 matrix from the first.
+ * @method subMat4
+ * @static
+ */
+ subMat4(a, b, dest) {
+ if (!dest) {
+ dest = a;
+ }
+ dest[0] = a[0] - b[0];
+ dest[1] = a[1] - b[1];
+ dest[2] = a[2] - b[2];
+ dest[3] = a[3] - b[3];
+ dest[4] = a[4] - b[4];
+ dest[5] = a[5] - b[5];
+ dest[6] = a[6] - b[6];
+ dest[7] = a[7] - b[7];
+ dest[8] = a[8] - b[8];
+ dest[9] = a[9] - b[9];
+ dest[10] = a[10] - b[10];
+ dest[11] = a[11] - b[11];
+ dest[12] = a[12] - b[12];
+ dest[13] = a[13] - b[13];
+ dest[14] = a[14] - b[14];
+ dest[15] = a[15] - b[15];
+ return dest;
+ },
+
+ /**
+ * Subtracts the given scalar from each element of the given 4x4 matrix.
+ * @method subMat4Scalar
+ * @static
+ */
+ subMat4Scalar(m, s, dest) {
+ if (!dest) {
+ dest = m;
+ }
+ dest[0] = m[0] - s;
+ dest[1] = m[1] - s;
+ dest[2] = m[2] - s;
+ dest[3] = m[3] - s;
+ dest[4] = m[4] - s;
+ dest[5] = m[5] - s;
+ dest[6] = m[6] - s;
+ dest[7] = m[7] - s;
+ dest[8] = m[8] - s;
+ dest[9] = m[9] - s;
+ dest[10] = m[10] - s;
+ dest[11] = m[11] - s;
+ dest[12] = m[12] - s;
+ dest[13] = m[13] - s;
+ dest[14] = m[14] - s;
+ dest[15] = m[15] - s;
+ return dest;
+ },
+
+ /**
+ * Subtracts the given scalar from each element of the given 4x4 matrix.
+ * @method subScalarMat4
+ * @static
+ */
+ subScalarMat4(s, m, dest) {
+ if (!dest) {
+ dest = m;
+ }
+ dest[0] = s - m[0];
+ dest[1] = s - m[1];
+ dest[2] = s - m[2];
+ dest[3] = s - m[3];
+ dest[4] = s - m[4];
+ dest[5] = s - m[5];
+ dest[6] = s - m[6];
+ dest[7] = s - m[7];
+ dest[8] = s - m[8];
+ dest[9] = s - m[9];
+ dest[10] = s - m[10];
+ dest[11] = s - m[11];
+ dest[12] = s - m[12];
+ dest[13] = s - m[13];
+ dest[14] = s - m[14];
+ dest[15] = s - m[15];
+ return dest;
+ },
+
+ /**
+ * Multiplies the two given 4x4 matrix by each other.
+ * @method mulMat4
+ * @static
+ */
+ mulMat4(a, b, dest) {
+ if (!dest) {
+ dest = a;
+ }
+
+ // Cache the matrix values (makes for huge speed increases!)
+ const a00 = a[0];
+
+ const a01 = a[1];
+ const a02 = a[2];
+ const a03 = a[3];
+ const a10 = a[4];
+ const a11 = a[5];
+ const a12 = a[6];
+ const a13 = a[7];
+ const a20 = a[8];
+ const a21 = a[9];
+ const a22 = a[10];
+ const a23 = a[11];
+ const a30 = a[12];
+ const a31 = a[13];
+ const a32 = a[14];
+ const a33 = a[15];
+ const b00 = b[0];
+ const b01 = b[1];
+ const b02 = b[2];
+ const b03 = b[3];
+ const b10 = b[4];
+ const b11 = b[5];
+ const b12 = b[6];
+ const b13 = b[7];
+ const b20 = b[8];
+ const b21 = b[9];
+ const b22 = b[10];
+ const b23 = b[11];
+ const b30 = b[12];
+ const b31 = b[13];
+ const b32 = b[14];
+ const b33 = b[15];
+
+ dest[0] = b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30;
+ dest[1] = b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31;
+ dest[2] = b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32;
+ dest[3] = b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33;
+ dest[4] = b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30;
+ dest[5] = b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31;
+ dest[6] = b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32;
+ dest[7] = b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33;
+ dest[8] = b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30;
+ dest[9] = b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31;
+ dest[10] = b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32;
+ dest[11] = b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33;
+ dest[12] = b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30;
+ dest[13] = b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31;
+ dest[14] = b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32;
+ dest[15] = b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33;
+
+ return dest;
+ },
+
+ /**
+ * Multiplies the two given 3x3 matrices by each other.
+ * @method mulMat4
+ * @static
+ */
+ mulMat3(a, b, dest) {
+ if (!dest) {
+ dest = new Float32Array(9);
+ }
+
+ const a11 = a[0];
+ const a12 = a[3];
+ const a13 = a[6];
+ const a21 = a[1];
+ const a22 = a[4];
+ const a23 = a[7];
+ const a31 = a[2];
+ const a32 = a[5];
+ const a33 = a[8];
+ const b11 = b[0];
+ const b12 = b[3];
+ const b13 = b[6];
+ const b21 = b[1];
+ const b22 = b[4];
+ const b23 = b[7];
+ const b31 = b[2];
+ const b32 = b[5];
+ const b33 = b[8];
+
+ dest[0] = a11 * b11 + a12 * b21 + a13 * b31;
+ dest[3] = a11 * b12 + a12 * b22 + a13 * b32;
+ dest[6] = a11 * b13 + a12 * b23 + a13 * b33;
+
+ dest[1] = a21 * b11 + a22 * b21 + a23 * b31;
+ dest[4] = a21 * b12 + a22 * b22 + a23 * b32;
+ dest[7] = a21 * b13 + a22 * b23 + a23 * b33;
+
+ dest[2] = a31 * b11 + a32 * b21 + a33 * b31;
+ dest[5] = a31 * b12 + a32 * b22 + a33 * b32;
+ dest[8] = a31 * b13 + a32 * b23 + a33 * b33;
+
+ return dest;
+ },
+
+ /**
+ * Multiplies each element of the given 4x4 matrix by the given scalar.
+ * @method mulMat4Scalar
+ * @static
+ */
+ mulMat4Scalar(m, s, dest) {
+ if (!dest) {
+ dest = m;
+ }
+ dest[0] = m[0] * s;
+ dest[1] = m[1] * s;
+ dest[2] = m[2] * s;
+ dest[3] = m[3] * s;
+ dest[4] = m[4] * s;
+ dest[5] = m[5] * s;
+ dest[6] = m[6] * s;
+ dest[7] = m[7] * s;
+ dest[8] = m[8] * s;
+ dest[9] = m[9] * s;
+ dest[10] = m[10] * s;
+ dest[11] = m[11] * s;
+ dest[12] = m[12] * s;
+ dest[13] = m[13] * s;
+ dest[14] = m[14] * s;
+ dest[15] = m[15] * s;
+ return dest;
+ },
+
+ /**
+ * Multiplies the given 4x4 matrix by the given four-element vector.
+ * @method mulMat4v4
+ * @static
+ */
+ mulMat4v4(m, v, dest = math.vec4()) {
+ const v0 = v[0];
+ const v1 = v[1];
+ const v2 = v[2];
+ const v3 = v[3];
+ dest[0] = m[0] * v0 + m[4] * v1 + m[8] * v2 + m[12] * v3;
+ dest[1] = m[1] * v0 + m[5] * v1 + m[9] * v2 + m[13] * v3;
+ dest[2] = m[2] * v0 + m[6] * v1 + m[10] * v2 + m[14] * v3;
+ dest[3] = m[3] * v0 + m[7] * v1 + m[11] * v2 + m[15] * v3;
+ return dest;
+ },
+
+ /**
+ * Transposes the given 4x4 matrix.
+ * @method transposeMat4
+ * @static
+ */
+ transposeMat4(mat, dest) {
+ // If we are transposing ourselves we can skip a few steps but have to cache some values
+ const m4 = mat[4];
+
+ const m14 = mat[14];
+ const m8 = mat[8];
+ const m13 = mat[13];
+ const m12 = mat[12];
+ const m9 = mat[9];
+ if (!dest || mat === dest) {
+ const a01 = mat[1];
+ const a02 = mat[2];
+ const a03 = mat[3];
+ const a12 = mat[6];
+ const a13 = mat[7];
+ const a23 = mat[11];
+ mat[1] = m4;
+ mat[2] = m8;
+ mat[3] = m12;
+ mat[4] = a01;
+ mat[6] = m9;
+ mat[7] = m13;
+ mat[8] = a02;
+ mat[9] = a12;
+ mat[11] = m14;
+ mat[12] = a03;
+ mat[13] = a13;
+ mat[14] = a23;
+ return mat;
+ }
+ dest[0] = mat[0];
+ dest[1] = m4;
+ dest[2] = m8;
+ dest[3] = m12;
+ dest[4] = mat[1];
+ dest[5] = mat[5];
+ dest[6] = m9;
+ dest[7] = m13;
+ dest[8] = mat[2];
+ dest[9] = mat[6];
+ dest[10] = mat[10];
+ dest[11] = m14;
+ dest[12] = mat[3];
+ dest[13] = mat[7];
+ dest[14] = mat[11];
+ dest[15] = mat[15];
+ return dest;
+ },
+
+ /**
+ * Transposes the given 3x3 matrix.
+ *
+ * @method transposeMat3
+ * @static
+ */
+ transposeMat3(mat, dest) {
+ if (dest === mat) {
+ const a01 = mat[1];
+ const a02 = mat[2];
+ const a12 = mat[5];
+ dest[1] = mat[3];
+ dest[2] = mat[6];
+ dest[3] = a01;
+ dest[5] = mat[7];
+ dest[6] = a02;
+ dest[7] = a12;
+ } else {
+ dest[0] = mat[0];
+ dest[1] = mat[3];
+ dest[2] = mat[6];
+ dest[3] = mat[1];
+ dest[4] = mat[4];
+ dest[5] = mat[7];
+ dest[6] = mat[2];
+ dest[7] = mat[5];
+ dest[8] = mat[8];
+ }
+ return dest;
+ },
+
+ /**
+ * Returns the determinant of the given 4x4 matrix.
+ * @method determinantMat4
+ * @static
+ */
+ determinantMat4(mat) {
+ // Cache the matrix values (makes for huge speed increases!)
+ const a00 = mat[0];
+
+ const a01 = mat[1];
+ const a02 = mat[2];
+ const a03 = mat[3];
+ const a10 = mat[4];
+ const a11 = mat[5];
+ const a12 = mat[6];
+ const a13 = mat[7];
+ const a20 = mat[8];
+ const a21 = mat[9];
+ const a22 = mat[10];
+ const a23 = mat[11];
+ const a30 = mat[12];
+ const a31 = mat[13];
+ const a32 = mat[14];
+ const a33 = mat[15];
+ return a30 * a21 * a12 * a03 - a20 * a31 * a12 * a03 - a30 * a11 * a22 * a03 + a10 * a31 * a22 * a03 +
+ a20 * a11 * a32 * a03 - a10 * a21 * a32 * a03 - a30 * a21 * a02 * a13 + a20 * a31 * a02 * a13 +
+ a30 * a01 * a22 * a13 - a00 * a31 * a22 * a13 - a20 * a01 * a32 * a13 + a00 * a21 * a32 * a13 +
+ a30 * a11 * a02 * a23 - a10 * a31 * a02 * a23 - a30 * a01 * a12 * a23 + a00 * a31 * a12 * a23 +
+ a10 * a01 * a32 * a23 - a00 * a11 * a32 * a23 - a20 * a11 * a02 * a33 + a10 * a21 * a02 * a33 +
+ a20 * a01 * a12 * a33 - a00 * a21 * a12 * a33 - a10 * a01 * a22 * a33 + a00 * a11 * a22 * a33;
+ },
+
+ /**
+ * Returns the inverse of the given 4x4 matrix.
+ * @method inverseMat4
+ * @static
+ */
+ inverseMat4(mat, dest) {
+ if (!dest) {
+ dest = mat;
+ }
+
+ // Cache the matrix values (makes for huge speed increases!)
+ const a00 = mat[0];
+
+ const a01 = mat[1];
+ const a02 = mat[2];
+ const a03 = mat[3];
+ const a10 = mat[4];
+ const a11 = mat[5];
+ const a12 = mat[6];
+ const a13 = mat[7];
+ const a20 = mat[8];
+ const a21 = mat[9];
+ const a22 = mat[10];
+ const a23 = mat[11];
+ const a30 = mat[12];
+ const a31 = mat[13];
+ const a32 = mat[14];
+ const a33 = mat[15];
+ const b00 = a00 * a11 - a01 * a10;
+ const b01 = a00 * a12 - a02 * a10;
+ const b02 = a00 * a13 - a03 * a10;
+ const b03 = a01 * a12 - a02 * a11;
+ const b04 = a01 * a13 - a03 * a11;
+ const b05 = a02 * a13 - a03 * a12;
+ const b06 = a20 * a31 - a21 * a30;
+ const b07 = a20 * a32 - a22 * a30;
+ const b08 = a20 * a33 - a23 * a30;
+ const b09 = a21 * a32 - a22 * a31;
+ const b10 = a21 * a33 - a23 * a31;
+ const b11 = a22 * a33 - a23 * a32;
+
+ // Calculate the determinant (inlined to avoid double-caching)
+ const invDet = 1 / (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06);
+
+ dest[0] = (a11 * b11 - a12 * b10 + a13 * b09) * invDet;
+ dest[1] = (-a01 * b11 + a02 * b10 - a03 * b09) * invDet;
+ dest[2] = (a31 * b05 - a32 * b04 + a33 * b03) * invDet;
+ dest[3] = (-a21 * b05 + a22 * b04 - a23 * b03) * invDet;
+ dest[4] = (-a10 * b11 + a12 * b08 - a13 * b07) * invDet;
+ dest[5] = (a00 * b11 - a02 * b08 + a03 * b07) * invDet;
+ dest[6] = (-a30 * b05 + a32 * b02 - a33 * b01) * invDet;
+ dest[7] = (a20 * b05 - a22 * b02 + a23 * b01) * invDet;
+ dest[8] = (a10 * b10 - a11 * b08 + a13 * b06) * invDet;
+ dest[9] = (-a00 * b10 + a01 * b08 - a03 * b06) * invDet;
+ dest[10] = (a30 * b04 - a31 * b02 + a33 * b00) * invDet;
+ dest[11] = (-a20 * b04 + a21 * b02 - a23 * b00) * invDet;
+ dest[12] = (-a10 * b09 + a11 * b07 - a12 * b06) * invDet;
+ dest[13] = (a00 * b09 - a01 * b07 + a02 * b06) * invDet;
+ dest[14] = (-a30 * b03 + a31 * b01 - a32 * b00) * invDet;
+ dest[15] = (a20 * b03 - a21 * b01 + a22 * b00) * invDet;
+
+ return dest;
+ },
+
+ /**
+ * Returns the trace of the given 4x4 matrix.
+ * @method traceMat4
+ * @static
+ */
+ traceMat4(m) {
+ return (m[0] + m[5] + m[10] + m[15]);
+ },
+
+ /**
+ * Returns 4x4 translation matrix.
+ * @method translationMat4
+ * @static
+ */
+ translationMat4v(v, dest) {
+ const m = dest || math.identityMat4();
+ m[12] = v[0];
+ m[13] = v[1];
+ m[14] = v[2];
+ return m;
+ },
+
+ /**
+ * Returns 3x3 translation matrix.
+ * @method translationMat3
+ * @static
+ */
+ translationMat3v(v, dest) {
+ const m = dest || math.identityMat3();
+ m[6] = v[0];
+ m[7] = v[1];
+ return m;
+ },
+
+ /**
+ * Returns 4x4 translation matrix.
+ * @method translationMat4c
+ * @static
+ */
+ translationMat4c: ((() => {
+ const xyz = new Float32Array(3);
+ return (x, y, z, dest) => {
+ xyz[0] = x;
+ xyz[1] = y;
+ xyz[2] = z;
+ return math.translationMat4v(xyz, dest);
+ };
+ }))(),
+
+ /**
+ * Returns 4x4 translation matrix.
+ * @method translationMat4s
+ * @static
+ */
+ translationMat4s(s, dest) {
+ return math.translationMat4c(s, s, s, dest);
+ },
+
+ /**
+ * Efficiently post-concatenates a translation to the given matrix.
+ * @param v
+ * @param m
+ */
+ translateMat4v(xyz, m) {
+ return math.translateMat4c(xyz[0], xyz[1], xyz[2], m);
+ },
+
+ /**
+ * Efficiently post-concatenates a translation to the given matrix.
+ * @param x
+ * @param y
+ * @param z
+ * @param m
+ */
+ OLDtranslateMat4c(x, y, z, m) {
+
+ const m12 = m[12];
+ m[0] += m12 * x;
+ m[4] += m12 * y;
+ m[8] += m12 * z;
+
+ const m13 = m[13];
+ m[1] += m13 * x;
+ m[5] += m13 * y;
+ m[9] += m13 * z;
+
+ const m14 = m[14];
+ m[2] += m14 * x;
+ m[6] += m14 * y;
+ m[10] += m14 * z;
+
+ const m15 = m[15];
+ m[3] += m15 * x;
+ m[7] += m15 * y;
+ m[11] += m15 * z;
+
+ return m;
+ },
+
+ translateMat4c(x, y, z, m) {
+
+ const m3 = m[3];
+ m[0] += m3 * x;
+ m[1] += m3 * y;
+ m[2] += m3 * z;
+
+ const m7 = m[7];
+ m[4] += m7 * x;
+ m[5] += m7 * y;
+ m[6] += m7 * z;
+
+ const m11 = m[11];
+ m[8] += m11 * x;
+ m[9] += m11 * y;
+ m[10] += m11 * z;
+
+ const m15 = m[15];
+ m[12] += m15 * x;
+ m[13] += m15 * y;
+ m[14] += m15 * z;
+
+ return m;
+ },
+ /**
+ * Returns 4x4 rotation matrix.
+ * @method rotationMat4v
+ * @static
+ */
+ rotationMat4v(anglerad, axis, m) {
+ const ax = math.normalizeVec4([axis[0], axis[1], axis[2], 0.0], []);
+ const s = Math.sin(anglerad);
+ const c = Math.cos(anglerad);
+ const q = 1.0 - c;
+
+ const x = ax[0];
+ const y = ax[1];
+ const z = ax[2];
+
+ let xy;
+ let yz;
+ let zx;
+ let xs;
+ let ys;
+ let zs;
+
+ //xx = x * x; used once
+ //yy = y * y; used once
+ //zz = z * z; used once
+ xy = x * y;
+ yz = y * z;
+ zx = z * x;
+ xs = x * s;
+ ys = y * s;
+ zs = z * s;
+
+ m = m || math.mat4();
+
+ m[0] = (q * x * x) + c;
+ m[1] = (q * xy) + zs;
+ m[2] = (q * zx) - ys;
+ m[3] = 0.0;
+
+ m[4] = (q * xy) - zs;
+ m[5] = (q * y * y) + c;
+ m[6] = (q * yz) + xs;
+ m[7] = 0.0;
+
+ m[8] = (q * zx) + ys;
+ m[9] = (q * yz) - xs;
+ m[10] = (q * z * z) + c;
+ m[11] = 0.0;
+
+ m[12] = 0.0;
+ m[13] = 0.0;
+ m[14] = 0.0;
+ m[15] = 1.0;
+
+ return m;
+ },
+
+ /**
+ * Returns 4x4 rotation matrix.
+ * @method rotationMat4c
+ * @static
+ */
+ rotationMat4c(anglerad, x, y, z, mat) {
+ return math.rotationMat4v(anglerad, [x, y, z], mat);
+ },
+
+ /**
+ * Returns 4x4 scale matrix.
+ * @method scalingMat4v
+ * @static
+ */
+ scalingMat4v(v, m = math.identityMat4()) {
+ m[0] = v[0];
+ m[5] = v[1];
+ m[10] = v[2];
+ return m;
+ },
+
+ /**
+ * Returns 3x3 scale matrix.
+ * @method scalingMat3v
+ * @static
+ */
+ scalingMat3v(v, m = math.identityMat3()) {
+ m[0] = v[0];
+ m[4] = v[1];
+ return m;
+ },
+
+ /**
+ * Returns 4x4 scale matrix.
+ * @method scalingMat4c
+ * @static
+ */
+ scalingMat4c: ((() => {
+ const xyz = new Float32Array(3);
+ return (x, y, z, dest) => {
+ xyz[0] = x;
+ xyz[1] = y;
+ xyz[2] = z;
+ return math.scalingMat4v(xyz, dest);
+ };
+ }))(),
+
+ /**
+ * Efficiently post-concatenates a scaling to the given matrix.
+ * @method scaleMat4c
+ * @param x
+ * @param y
+ * @param z
+ * @param m
+ */
+ scaleMat4c(x, y, z, m) {
+
+ m[0] *= x;
+ m[4] *= y;
+ m[8] *= z;
+
+ m[1] *= x;
+ m[5] *= y;
+ m[9] *= z;
+
+ m[2] *= x;
+ m[6] *= y;
+ m[10] *= z;
+
+ m[3] *= x;
+ m[7] *= y;
+ m[11] *= z;
+ return m;
+ },
+
+ /**
+ * Efficiently post-concatenates a scaling to the given matrix.
+ * @method scaleMat4c
+ * @param xyz
+ * @param m
+ */
+ scaleMat4v(xyz, m) {
+
+ const x = xyz[0];
+ const y = xyz[1];
+ const z = xyz[2];
+
+ m[0] *= x;
+ m[4] *= y;
+ m[8] *= z;
+ m[1] *= x;
+ m[5] *= y;
+ m[9] *= z;
+ m[2] *= x;
+ m[6] *= y;
+ m[10] *= z;
+ m[3] *= x;
+ m[7] *= y;
+ m[11] *= z;
+
+ return m;
+ },
+
+ /**
+ * Returns 4x4 scale matrix.
+ * @method scalingMat4s
+ * @static
+ */
+ scalingMat4s(s) {
+ return math.scalingMat4c(s, s, s);
+ },
+
+ /**
+ * Creates a matrix from a quaternion rotation and vector translation
+ *
+ * @param {Float32Array} q Rotation quaternion
+ * @param {Float32Array} v Translation vector
+ * @param {Float32Array} dest Destination matrix
+ * @returns {Float32Array} dest
+ */
+ rotationTranslationMat4(q, v, dest = math.mat4()) {
+ const x = q[0];
+ const y = q[1];
+ const z = q[2];
+ const w = q[3];
+
+ const x2 = x + x;
+ const y2 = y + y;
+ const z2 = z + z;
+ const xx = x * x2;
+ const xy = x * y2;
+ const xz = x * z2;
+ const yy = y * y2;
+ const yz = y * z2;
+ const zz = z * z2;
+ const wx = w * x2;
+ const wy = w * y2;
+ const wz = w * z2;
+
+ dest[0] = 1 - (yy + zz);
+ dest[1] = xy + wz;
+ dest[2] = xz - wy;
+ dest[3] = 0;
+ dest[4] = xy - wz;
+ dest[5] = 1 - (xx + zz);
+ dest[6] = yz + wx;
+ dest[7] = 0;
+ dest[8] = xz + wy;
+ dest[9] = yz - wx;
+ dest[10] = 1 - (xx + yy);
+ dest[11] = 0;
+ dest[12] = v[0];
+ dest[13] = v[1];
+ dest[14] = v[2];
+ dest[15] = 1;
+
+ return dest;
+ },
+
+ /**
+ * Gets Euler angles from a 4x4 matrix.
+ *
+ * @param {Float32Array} mat The 4x4 matrix.
+ * @param {String} order Desired Euler angle order: "XYZ", "YXZ", "ZXY" etc.
+ * @param {Float32Array} [dest] Destination Euler angles, created by default.
+ * @returns {Float32Array} The Euler angles.
+ */
+ mat4ToEuler(mat, order, dest = math.vec4()) {
+ const clamp = math.clamp;
+
+ // Assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+ const m11 = mat[0];
+
+ const m12 = mat[4];
+ const m13 = mat[8];
+ const m21 = mat[1];
+ const m22 = mat[5];
+ const m23 = mat[9];
+ const m31 = mat[2];
+ const m32 = mat[6];
+ const m33 = mat[10];
+
+ if (order === 'XYZ') {
+
+ dest[1] = Math.asin(clamp(m13, -1, 1));
+
+ if (Math.abs(m13) < 0.99999) {
+ dest[0] = Math.atan2(-m23, m33);
+ dest[2] = Math.atan2(-m12, m11);
+ } else {
+ dest[0] = Math.atan2(m32, m22);
+ dest[2] = 0;
+
}
- return a;
- },
-
- /**
- * Negates a four-element vector.
- * @method negateVec4
- * @static
- * @param {Array(Number)} v Vector to negate
- * @param {Array(Number)} [dest] Destination vector
- * @return {Array(Number)} dest if specified, v otherwise
- */
- negateVec4: function (v, dest) {
- if (!dest) {
- dest = v;
+
+ } else if (order === 'YXZ') {
+
+ dest[0] = Math.asin(-clamp(m23, -1, 1));
+
+ if (Math.abs(m23) < 0.99999) {
+ dest[1] = Math.atan2(m13, m33);
+ dest[2] = Math.atan2(m21, m22);
+ } else {
+ dest[1] = Math.atan2(-m31, m11);
+ dest[2] = 0;
}
- dest[0] = -v[0];
- dest[1] = -v[1];
- dest[2] = -v[2];
- dest[3] = -v[3];
- return dest;
- },
-
- /**
- * Adds one four-element vector to another.
- * @method addVec4
- * @static
- * @param {Array(Number)} u First vector
- * @param {Array(Number)} v Second vector
- * @param {Array(Number)} [dest] Destination vector
- * @return {Array(Number)} dest if specified, u otherwise
- */
- addVec4: function (u, v, dest) {
- if (!dest) {
- dest = u;
+
+ } else if (order === 'ZXY') {
+
+ dest[0] = Math.asin(clamp(m32, -1, 1));
+
+ if (Math.abs(m32) < 0.99999) {
+ dest[1] = Math.atan2(-m31, m33);
+ dest[2] = Math.atan2(-m12, m22);
+ } else {
+ dest[1] = 0;
+ dest[2] = Math.atan2(m21, m11);
}
- dest[0] = u[0] + v[0];
- dest[1] = u[1] + v[1];
- dest[2] = u[2] + v[2];
- dest[3] = u[3] + v[3];
- return dest;
- },
-
- /**
- * Adds a scalar value to each element of a four-element vector.
- * @method addVec4Scalar
- * @static
- * @param {Array(Number)} v The vector
- * @param {Number} s The scalar
- * @param {Array(Number)} [dest] Destination vector
- * @return {Array(Number)} dest if specified, v otherwise
- */
- addVec4Scalar: function (v, s, dest) {
- if (!dest) {
- dest = v;
+
+ } else if (order === 'ZYX') {
+
+ dest[1] = Math.asin(-clamp(m31, -1, 1));
+
+ if (Math.abs(m31) < 0.99999) {
+ dest[0] = Math.atan2(m32, m33);
+ dest[2] = Math.atan2(m21, m11);
+ } else {
+ dest[0] = 0;
+ dest[2] = Math.atan2(-m12, m22);
}
- dest[0] = v[0] + s;
- dest[1] = v[1] + s;
- dest[2] = v[2] + s;
- dest[3] = v[3] + s;
- return dest;
- },
-
- /**
- * Adds one three-element vector to another.
- * @method addVec3
- * @static
- * @param {Array(Number)} u First vector
- * @param {Array(Number)} v Second vector
- * @param {Array(Number)} [dest] Destination vector
- * @return {Array(Number)} dest if specified, u otherwise
- */
- addVec3: function (u, v, dest) {
- if (!dest) {
- dest = u;
+
+ } else if (order === 'YZX') {
+
+ dest[2] = Math.asin(clamp(m21, -1, 1));
+
+ if (Math.abs(m21) < 0.99999) {
+ dest[0] = Math.atan2(-m23, m22);
+ dest[1] = Math.atan2(-m31, m11);
+ } else {
+ dest[0] = 0;
+ dest[1] = Math.atan2(m13, m33);
}
- dest[0] = u[0] + v[0];
- dest[1] = u[1] + v[1];
- dest[2] = u[2] + v[2];
- return dest;
- },
-
- /**
- * Adds a scalar value to each element of a three-element vector.
- * @method addVec4Scalar
- * @static
- * @param {Array(Number)} v The vector
- * @param {Number} s The scalar
- * @param {Array(Number)} [dest] Destination vector
- * @return {Array(Number)} dest if specified, v otherwise
- */
- addVec3Scalar: function (v, s, dest) {
- if (!dest) {
- dest = v;
+
+ } else if (order === 'XZY') {
+
+ dest[2] = Math.asin(-clamp(m12, -1, 1));
+
+ if (Math.abs(m12) < 0.99999) {
+ dest[0] = Math.atan2(m32, m22);
+ dest[1] = Math.atan2(m13, m11);
+ } else {
+ dest[0] = Math.atan2(-m23, m33);
+ dest[1] = 0;
}
- dest[0] = v[0] + s;
- dest[1] = v[1] + s;
- dest[2] = v[2] + s;
- return dest;
- },
-
- /**
- * Subtracts one four-element vector from another.
- * @method subVec4
- * @static
- * @param {Array(Number)} u First vector
- * @param {Array(Number)} v Vector to subtract
- * @param {Array(Number)} [dest] Destination vector
- * @return {Array(Number)} dest if specified, u otherwise
- */
- subVec4: function (u, v, dest) {
- if (!dest) {
- dest = u;
+ }
+
+ return dest;
+ },
+
+ composeMat4(position, quaternion, scale, mat = math.mat4()) {
+ math.quaternionToRotationMat4(quaternion, mat);
+ math.scaleMat4v(scale, mat);
+ math.translateMat4v(position, mat);
+
+ return mat;
+ },
+
+ decomposeMat4: (() => {
+
+ const vec = new Float32Array(3);
+ const matrix = new Float32Array(16);
+
+ return function decompose(mat, position, quaternion, scale) {
+
+ vec[0] = mat[0];
+ vec[1] = mat[1];
+ vec[2] = mat[2];
+
+ let sx = math.lenVec3(vec);
+
+ vec[0] = mat[4];
+ vec[1] = mat[5];
+ vec[2] = mat[6];
+
+ const sy = math.lenVec3(vec);
+
+ vec[8] = mat[8];
+ vec[9] = mat[9];
+ vec[10] = mat[10];
+
+ const sz = math.lenVec3(vec);
+
+ // if determine is negative, we need to invert one scale
+ const det = math.determinantMat4(mat);
+
+ if (det < 0) {
+ sx = -sx;
}
- dest[0] = u[0] - v[0];
- dest[1] = u[1] - v[1];
- dest[2] = u[2] - v[2];
- dest[3] = u[3] - v[3];
- return dest;
- },
-
- /**
- * Subtracts one three-element vector from another.
- * @method subVec3
- * @static
- * @param {Array(Number)} u First vector
- * @param {Array(Number)} v Vector to subtract
- * @param {Array(Number)} [dest] Destination vector
- * @return {Array(Number)} dest if specified, u otherwise
- */
- subVec3: function (u, v, dest) {
- if (!dest) {
- dest = u;
+
+ position[0] = mat[12];
+ position[1] = mat[13];
+ position[2] = mat[14];
+
+ // scale the rotation part
+ matrix.set(mat);
+
+ const invSX = 1 / sx;
+ const invSY = 1 / sy;
+ const invSZ = 1 / sz;
+
+ matrix[0] *= invSX;
+ matrix[1] *= invSX;
+ matrix[2] *= invSX;
+
+ matrix[4] *= invSY;
+ matrix[5] *= invSY;
+ matrix[6] *= invSY;
+
+ matrix[8] *= invSZ;
+ matrix[9] *= invSZ;
+ matrix[10] *= invSZ;
+
+ math.mat4ToQuaternion(matrix, quaternion);
+
+ scale[0] = sx;
+ scale[1] = sy;
+ scale[2] = sz;
+
+ return this;
+
+ };
+
+ })(),
+
+ /**
+ * Returns a 4x4 'lookat' viewing transform matrix.
+ * @method lookAtMat4v
+ * @param pos vec3 position of the viewer
+ * @param target vec3 point the viewer is looking at
+ * @param up vec3 pointing "up"
+ * @param dest mat4 Optional, mat4 matrix will be written into
+ *
+ * @return {mat4} dest if specified, a new mat4 otherwise
+ */
+ lookAtMat4v(pos, target, up, dest) {
+ if (!dest) {
+ dest = math.mat4();
+ }
+
+ const posx = pos[0];
+ const posy = pos[1];
+ const posz = pos[2];
+ const upx = up[0];
+ const upy = up[1];
+ const upz = up[2];
+ const targetx = target[0];
+ const targety = target[1];
+ const targetz = target[2];
+
+ if (posx === targetx && posy === targety && posz === targetz) {
+ return math.identityMat4();
+ }
+
+ let z0;
+ let z1;
+ let z2;
+ let x0;
+ let x1;
+ let x2;
+ let y0;
+ let y1;
+ let y2;
+ let len;
+
+ //vec3.direction(eye, center, z);
+ z0 = posx - targetx;
+ z1 = posy - targety;
+ z2 = posz - targetz;
+
+ // normalize (no check needed for 0 because of early return)
+ len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2);
+ z0 *= len;
+ z1 *= len;
+ z2 *= len;
+
+ //vec3.normalize(vec3.cross(up, z, x));
+ x0 = upy * z2 - upz * z1;
+ x1 = upz * z0 - upx * z2;
+ x2 = upx * z1 - upy * z0;
+ len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2);
+ if (!len) {
+ x0 = 0;
+ x1 = 0;
+ x2 = 0;
+ } else {
+ len = 1 / len;
+ x0 *= len;
+ x1 *= len;
+ x2 *= len;
+ }
+
+ //vec3.normalize(vec3.cross(z, x, y));
+ y0 = z1 * x2 - z2 * x1;
+ y1 = z2 * x0 - z0 * x2;
+ y2 = z0 * x1 - z1 * x0;
+
+ len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2);
+ if (!len) {
+ y0 = 0;
+ y1 = 0;
+ y2 = 0;
+ } else {
+ len = 1 / len;
+ y0 *= len;
+ y1 *= len;
+ y2 *= len;
+ }
+
+ dest[0] = x0;
+ dest[1] = y0;
+ dest[2] = z0;
+ dest[3] = 0;
+ dest[4] = x1;
+ dest[5] = y1;
+ dest[6] = z1;
+ dest[7] = 0;
+ dest[8] = x2;
+ dest[9] = y2;
+ dest[10] = z2;
+ dest[11] = 0;
+ dest[12] = -(x0 * posx + x1 * posy + x2 * posz);
+ dest[13] = -(y0 * posx + y1 * posy + y2 * posz);
+ dest[14] = -(z0 * posx + z1 * posy + z2 * posz);
+ dest[15] = 1;
+
+ return dest;
+ },
+
+ /**
+ * Returns a 4x4 'lookat' viewing transform matrix.
+ * @method lookAtMat4c
+ * @static
+ */
+ lookAtMat4c(posx, posy, posz, targetx, targety, targetz, upx, upy, upz) {
+ return math.lookAtMat4v([posx, posy, posz], [targetx, targety, targetz], [upx, upy, upz], []);
+ },
+
+ /**
+ * Returns a 4x4 orthographic projection matrix.
+ * @method orthoMat4c
+ * @static
+ */
+ orthoMat4c(left, right, bottom, top, near, far, dest) {
+ if (!dest) {
+ dest = math.mat4();
+ }
+ const rl = (right - left);
+ const tb = (top - bottom);
+ const fn = (far - near);
+
+ dest[0] = 2.0 / rl;
+ dest[1] = 0.0;
+ dest[2] = 0.0;
+ dest[3] = 0.0;
+
+ dest[4] = 0.0;
+ dest[5] = 2.0 / tb;
+ dest[6] = 0.0;
+ dest[7] = 0.0;
+
+ dest[8] = 0.0;
+ dest[9] = 0.0;
+ dest[10] = -2.0 / fn;
+ dest[11] = 0.0;
+
+ dest[12] = -(left + right) / rl;
+ dest[13] = -(top + bottom) / tb;
+ dest[14] = -(far + near) / fn;
+ dest[15] = 1.0;
+
+ return dest;
+ },
+
+ /**
+ * Returns a 4x4 perspective projection matrix.
+ * @method frustumMat4v
+ * @static
+ */
+ frustumMat4v(fmin, fmax, m) {
+ if (!m) {
+ m = math.mat4();
+ }
+
+ const fmin4 = [fmin[0], fmin[1], fmin[2], 0.0];
+ const fmax4 = [fmax[0], fmax[1], fmax[2], 0.0];
+
+ math.addVec4(fmax4, fmin4, tempMat1);
+ math.subVec4(fmax4, fmin4, tempMat2);
+
+ const t = 2.0 * fmin4[2];
+
+ const tempMat20 = tempMat2[0];
+ const tempMat21 = tempMat2[1];
+ const tempMat22 = tempMat2[2];
+
+ m[0] = t / tempMat20;
+ m[1] = 0.0;
+ m[2] = 0.0;
+ m[3] = 0.0;
+
+ m[4] = 0.0;
+ m[5] = t / tempMat21;
+ m[6] = 0.0;
+ m[7] = 0.0;
+
+ m[8] = tempMat1[0] / tempMat20;
+ m[9] = tempMat1[1] / tempMat21;
+ m[10] = -tempMat1[2] / tempMat22;
+ m[11] = -1.0;
+
+ m[12] = 0.0;
+ m[13] = 0.0;
+ m[14] = -t * fmax4[2] / tempMat22;
+ m[15] = 0.0;
+
+ return m;
+ },
+
+ /**
+ * Returns a 4x4 perspective projection matrix.
+ * @method frustumMat4v
+ * @static
+ */
+ frustumMat4(left, right, bottom, top, near, far, dest) {
+ if (!dest) {
+ dest = math.mat4();
+ }
+ const rl = (right - left);
+ const tb = (top - bottom);
+ const fn = (far - near);
+ dest[0] = (near * 2) / rl;
+ dest[1] = 0;
+ dest[2] = 0;
+ dest[3] = 0;
+ dest[4] = 0;
+ dest[5] = (near * 2) / tb;
+ dest[6] = 0;
+ dest[7] = 0;
+ dest[8] = (right + left) / rl;
+ dest[9] = (top + bottom) / tb;
+ dest[10] = -(far + near) / fn;
+ dest[11] = -1;
+ dest[12] = 0;
+ dest[13] = 0;
+ dest[14] = -(far * near * 2) / fn;
+ dest[15] = 0;
+ return dest;
+ },
+
+ /**
+ * Returns a 4x4 perspective projection matrix.
+ * @method perspectiveMat4v
+ * @static
+ */
+ perspectiveMat4(fovyrad, aspectratio, znear, zfar, m) {
+ const pmin = [];
+ const pmax = [];
+
+ pmin[2] = znear;
+ pmax[2] = zfar;
+
+ pmax[1] = pmin[2] * Math.tan(fovyrad / 2.0);
+ pmin[1] = -pmax[1];
+
+ pmax[0] = pmax[1] * aspectratio;
+ pmin[0] = -pmax[0];
+
+ return math.frustumMat4v(pmin, pmax, m);
+ },
+
+ /**
+ * Transforms a three-element position by a 4x4 matrix.
+ * @method transformPoint3
+ * @static
+ */
+ transformPoint3(m, p, dest = math.vec3()) {
+ dest[0] = (m[0] * p[0]) + (m[4] * p[1]) + (m[8] * p[2]) + m[12];
+ dest[1] = (m[1] * p[0]) + (m[5] * p[1]) + (m[9] * p[2]) + m[13];
+ dest[2] = (m[2] * p[0]) + (m[6] * p[1]) + (m[10] * p[2]) + m[14];
+
+ return dest;
+ },
+
+ /**
+ * Transforms a homogeneous coordinate by a 4x4 matrix.
+ * @method transformPoint3
+ * @static
+ */
+ transformPoint4(m, v, dest = math.vec4()) {
+ dest[0] = m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12] * v[3];
+ dest[1] = m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13] * v[3];
+ dest[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * v[3];
+ dest[3] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3];
+
+ return dest;
+ },
+
+
+ /**
+ * Transforms an array of three-element positions by a 4x4 matrix.
+ * @method transformPoints3
+ * @static
+ */
+ transformPoints3(m, points, points2) {
+ const result = points2 || [];
+ const len = points.length;
+ let p0;
+ let p1;
+ let p2;
+ let pi;
+
+ // cache values
+ const m0 = m[0];
+
+ const m1 = m[1];
+ const m2 = m[2];
+ const m3 = m[3];
+ const m4 = m[4];
+ const m5 = m[5];
+ const m6 = m[6];
+ const m7 = m[7];
+ const m8 = m[8];
+ const m9 = m[9];
+ const m10 = m[10];
+ const m11 = m[11];
+ const m12 = m[12];
+ const m13 = m[13];
+ const m14 = m[14];
+ const m15 = m[15];
+
+ let r;
+
+ for (let i = 0; i < len; ++i) {
+
+ // cache values
+ pi = points[i];
+
+ p0 = pi[0];
+ p1 = pi[1];
+ p2 = pi[2];
+
+ r = result[i] || (result[i] = [0, 0, 0]);
+
+ r[0] = (m0 * p0) + (m4 * p1) + (m8 * p2) + m12;
+ r[1] = (m1 * p0) + (m5 * p1) + (m9 * p2) + m13;
+ r[2] = (m2 * p0) + (m6 * p1) + (m10 * p2) + m14;
+ r[3] = (m3 * p0) + (m7 * p1) + (m11 * p2) + m15;
+ }
+
+ result.length = len;
+
+ return result;
+ },
+
+ /**
+ * Transforms an array of positions by a 4x4 matrix.
+ * @method transformPositions3
+ * @static
+ */
+ transformPositions3(m, p, p2 = p) {
+ let i;
+ const len = p.length;
+
+ let x;
+ let y;
+ let z;
+
+ const m0 = m[0];
+ const m1 = m[1];
+ const m2 = m[2];
+ const m3 = m[3];
+ const m4 = m[4];
+ const m5 = m[5];
+ const m6 = m[6];
+ const m7 = m[7];
+ const m8 = m[8];
+ const m9 = m[9];
+ const m10 = m[10];
+ const m11 = m[11];
+ const m12 = m[12];
+ const m13 = m[13];
+ const m14 = m[14];
+ const m15 = m[15];
+
+ for (i = 0; i < len; i += 3) {
+
+ x = p[i + 0];
+ y = p[i + 1];
+ z = p[i + 2];
+
+ p2[i + 0] = (m0 * x) + (m4 * y) + (m8 * z) + m12;
+ p2[i + 1] = (m1 * x) + (m5 * y) + (m9 * z) + m13;
+ p2[i + 2] = (m2 * x) + (m6 * y) + (m10 * z) + m14;
+ p2[i + 3] = (m3 * x) + (m7 * y) + (m11 * z) + m15;
+ }
+
+ return p2;
+ },
+
+ /**
+ * Transforms an array of positions by a 4x4 matrix.
+ * @method transformPositions4
+ * @static
+ */
+ transformPositions4(m, p, p2 = p) {
+ let i;
+ const len = p.length;
+
+ let x;
+ let y;
+ let z;
+
+ const m0 = m[0];
+ const m1 = m[1];
+ const m2 = m[2];
+ const m3 = m[3];
+ const m4 = m[4];
+ const m5 = m[5];
+ const m6 = m[6];
+ const m7 = m[7];
+ const m8 = m[8];
+ const m9 = m[9];
+ const m10 = m[10];
+ const m11 = m[11];
+ const m12 = m[12];
+ const m13 = m[13];
+ const m14 = m[14];
+ const m15 = m[15];
+
+ for (i = 0; i < len; i += 4) {
+
+ x = p[i + 0];
+ y = p[i + 1];
+ z = p[i + 2];
+
+ p2[i + 0] = (m0 * x) + (m4 * y) + (m8 * z) + m12;
+ p2[i + 1] = (m1 * x) + (m5 * y) + (m9 * z) + m13;
+ p2[i + 2] = (m2 * x) + (m6 * y) + (m10 * z) + m14;
+ p2[i + 3] = (m3 * x) + (m7 * y) + (m11 * z) + m15;
+ }
+
+ return p2;
+ },
+
+ /**
+ * Transforms a three-element vector by a 4x4 matrix.
+ * @method transformVec3
+ * @static
+ */
+ transformVec3(m, v, dest) {
+ const v0 = v[0];
+ const v1 = v[1];
+ const v2 = v[2];
+ dest = dest || this.vec3();
+ dest[0] = (m[0] * v0) + (m[4] * v1) + (m[8] * v2);
+ dest[1] = (m[1] * v0) + (m[5] * v1) + (m[9] * v2);
+ dest[2] = (m[2] * v0) + (m[6] * v1) + (m[10] * v2);
+ return dest;
+ },
+
+ /**
+ * Transforms a four-element vector by a 4x4 matrix.
+ * @method transformVec4
+ * @static
+ */
+ transformVec4(m, v, dest) {
+ const v0 = v[0];
+ const v1 = v[1];
+ const v2 = v[2];
+ const v3 = v[3];
+ dest = dest || math.vec4();
+ dest[0] = m[0] * v0 + m[4] * v1 + m[8] * v2 + m[12] * v3;
+ dest[1] = m[1] * v0 + m[5] * v1 + m[9] * v2 + m[13] * v3;
+ dest[2] = m[2] * v0 + m[6] * v1 + m[10] * v2 + m[14] * v3;
+ dest[3] = m[3] * v0 + m[7] * v1 + m[11] * v2 + m[15] * v3;
+ return dest;
+ },
+
+ /**
+ * Rotate a 3D vector around the x-axis
+ *
+ * @method rotateVec3X
+ * @param {Float32Array} a The vec3 point to rotate
+ * @param {Float32Array} b The origin of the rotation
+ * @param {Number} c The angle of rotation
+ * @param {Float32Array} dest The receiving vec3
+ * @returns {Float32Array} dest
+ * @static
+ */
+ rotateVec3X(a, b, c, dest) {
+ const p = [];
+ const r = [];
+
+ //Translate point to the origin
+ p[0] = a[0] - b[0];
+ p[1] = a[1] - b[1];
+ p[2] = a[2] - b[2];
+
+ //perform rotation
+ r[0] = p[0];
+ r[1] = p[1] * Math.cos(c) - p[2] * Math.sin(c);
+ r[2] = p[1] * Math.sin(c) + p[2] * Math.cos(c);
+
+ //translate to correct position
+ dest[0] = r[0] + b[0];
+ dest[1] = r[1] + b[1];
+ dest[2] = r[2] + b[2];
+
+ return dest;
+ },
+
+ /**
+ * Rotate a 3D vector around the y-axis
+ *
+ * @method rotateVec3Y
+ * @param {Float32Array} a The vec3 point to rotate
+ * @param {Float32Array} b The origin of the rotation
+ * @param {Number} c The angle of rotation
+ * @param {Float32Array} dest The receiving vec3
+ * @returns {Float32Array} dest
+ * @static
+ */
+ rotateVec3Y(a, b, c, dest) {
+ const p = [];
+ const r = [];
+
+ //Translate point to the origin
+ p[0] = a[0] - b[0];
+ p[1] = a[1] - b[1];
+ p[2] = a[2] - b[2];
+
+ //perform rotation
+ r[0] = p[2] * Math.sin(c) + p[0] * Math.cos(c);
+ r[1] = p[1];
+ r[2] = p[2] * Math.cos(c) - p[0] * Math.sin(c);
+
+ //translate to correct position
+ dest[0] = r[0] + b[0];
+ dest[1] = r[1] + b[1];
+ dest[2] = r[2] + b[2];
+
+ return dest;
+ },
+
+ /**
+ * Rotate a 3D vector around the z-axis
+ *
+ * @method rotateVec3Z
+ * @param {Float32Array} a The vec3 point to rotate
+ * @param {Float32Array} b The origin of the rotation
+ * @param {Number} c The angle of rotation
+ * @param {Float32Array} dest The receiving vec3
+ * @returns {Float32Array} dest
+ * @static
+ */
+ rotateVec3Z(a, b, c, dest) {
+ const p = [];
+ const r = [];
+
+ //Translate point to the origin
+ p[0] = a[0] - b[0];
+ p[1] = a[1] - b[1];
+ p[2] = a[2] - b[2];
+
+ //perform rotation
+ r[0] = p[0] * Math.cos(c) - p[1] * Math.sin(c);
+ r[1] = p[0] * Math.sin(c) + p[1] * Math.cos(c);
+ r[2] = p[2];
+
+ //translate to correct position
+ dest[0] = r[0] + b[0];
+ dest[1] = r[1] + b[1];
+ dest[2] = r[2] + b[2];
+
+ return dest;
+ },
+
+ /**
+ * Transforms a four-element vector by a 4x4 projection matrix.
+ *
+ * @method projectVec4
+ * @param {Float32Array} p 3D View-space coordinate
+ * @param {Float32Array} q 2D Projected coordinate
+ * @returns {Float32Array} 2D Projected coordinate
+ * @static
+ */
+ projectVec4(p, q) {
+ const f = 1.0 / p[3];
+ q = q || math.vec2();
+ q[0] = v[0] * f;
+ q[1] = v[1] * f;
+ return q;
+ },
+
+ /**
+ * Unprojects a three-element vector.
+ *
+ * @method unprojectVec3
+ * @param {Float32Array} p 3D Projected coordinate
+ * @param {Float32Array} viewMat View matrix
+ * @returns {Float32Array} projMat Projection matrix
+ * @static
+ */
+ unprojectVec3: ((() => {
+ const mat = new Float32Array(16);
+ const mat2 = new Float32Array(16);
+ const mat3 = new Float32Array(16);
+ return function (p, viewMat, projMat, q) {
+ return this.transformVec3(this.mulMat4(this.inverseMat4(viewMat, mat), this.inverseMat4(projMat, mat2), mat3), p, q)
+ };
+ }))(),
+
+ /**
+ * Linearly interpolates between two 3D vectors.
+ * @method lerpVec3
+ * @static
+ */
+ lerpVec3(t, t1, t2, p1, p2, dest) {
+ const result = dest || math.vec3();
+ const f = (t - t1) / (t2 - t1);
+ result[0] = p1[0] + (f * (p2[0] - p1[0]));
+ result[1] = p1[1] + (f * (p2[1] - p1[1]));
+ result[2] = p1[2] + (f * (p2[2] - p1[2]));
+ return result;
+ },
+
+
+ /**
+ * Flattens a two-dimensional array into a one-dimensional array.
+ *
+ * @method flatten
+ * @static
+ * @param {Array of Arrays} a A 2D array
+ * @returns Flattened 1D array
+ */
+ flatten(a) {
+
+ const result = [];
+
+ let i;
+ let leni;
+ let j;
+ let lenj;
+ let item;
+
+ for (i = 0, leni = a.length; i < leni; i++) {
+ item = a[i];
+ for (j = 0, lenj = item.length; j < lenj; j++) {
+ result.push(item[j]);
}
- dest[0] = u[0] - v[0];
- dest[1] = u[1] - v[1];
- dest[2] = u[2] - v[2];
- return dest;
- },
-
- /**
- * Subtracts one two-element vector from another.
- * @method subVec2
- * @static
- * @param {Array(Number)} u First vector
- * @param {Array(Number)} v Vector to subtract
- * @param {Array(Number)} [dest] Destination vector
- * @return {Array(Number)} dest if specified, u otherwise
- */
- subVec2: function (u, v, dest) {
- if (!dest) {
- dest = u;
+ }
+
+ return result;
+ },
+
+
+ identityQuaternion(dest = math.vec4()) {
+ dest[0] = 0.0;
+ dest[1] = 0.0;
+ dest[2] = 0.0;
+ dest[3] = 1.0;
+ return dest;
+ },
+
+ /**
+ * Initializes a quaternion from Euler angles.
+ *
+ * @param {Float32Array} euler The Euler angles.
+ * @param {String} order Euler angle order: "XYZ", "YXZ", "ZXY" etc.
+ * @param {Float32Array} [dest] Destination quaternion, created by default.
+ * @returns {Float32Array} The quaternion.
+ */
+ eulerToQuaternion(euler, order, dest = math.vec4()) {
+ // http://www.mathworks.com/matlabcentral/fileexchange/
+ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
+ // content/SpinCalc.m
+
+ const a = (euler[0] * math.DEGTORAD) / 2;
+ const b = (euler[1] * math.DEGTORAD) / 2;
+ const c = (euler[2] * math.DEGTORAD) / 2;
+
+ const c1 = Math.cos(a);
+ const c2 = Math.cos(b);
+ const c3 = Math.cos(c);
+ const s1 = Math.sin(a);
+ const s2 = Math.sin(b);
+ const s3 = Math.sin(c);
+
+ if (order === 'XYZ') {
+
+ dest[0] = s1 * c2 * c3 + c1 * s2 * s3;
+ dest[1] = c1 * s2 * c3 - s1 * c2 * s3;
+ dest[2] = c1 * c2 * s3 + s1 * s2 * c3;
+ dest[3] = c1 * c2 * c3 - s1 * s2 * s3;
+
+ } else if (order === 'YXZ') {
+
+ dest[0] = s1 * c2 * c3 + c1 * s2 * s3;
+ dest[1] = c1 * s2 * c3 - s1 * c2 * s3;
+ dest[2] = c1 * c2 * s3 - s1 * s2 * c3;
+ dest[3] = c1 * c2 * c3 + s1 * s2 * s3;
+
+ } else if (order === 'ZXY') {
+
+ dest[0] = s1 * c2 * c3 - c1 * s2 * s3;
+ dest[1] = c1 * s2 * c3 + s1 * c2 * s3;
+ dest[2] = c1 * c2 * s3 + s1 * s2 * c3;
+ dest[3] = c1 * c2 * c3 - s1 * s2 * s3;
+
+ } else if (order === 'ZYX') {
+
+ dest[0] = s1 * c2 * c3 - c1 * s2 * s3;
+ dest[1] = c1 * s2 * c3 + s1 * c2 * s3;
+ dest[2] = c1 * c2 * s3 - s1 * s2 * c3;
+ dest[3] = c1 * c2 * c3 + s1 * s2 * s3;
+
+ } else if (order === 'YZX') {
+
+ dest[0] = s1 * c2 * c3 + c1 * s2 * s3;
+ dest[1] = c1 * s2 * c3 + s1 * c2 * s3;
+ dest[2] = c1 * c2 * s3 - s1 * s2 * c3;
+ dest[3] = c1 * c2 * c3 - s1 * s2 * s3;
+
+ } else if (order === 'XZY') {
+
+ dest[0] = s1 * c2 * c3 - c1 * s2 * s3;
+ dest[1] = c1 * s2 * c3 - s1 * c2 * s3;
+ dest[2] = c1 * c2 * s3 + s1 * s2 * c3;
+ dest[3] = c1 * c2 * c3 + s1 * s2 * s3;
+ }
+
+ return dest;
+ },
+
+ mat4ToQuaternion(m, dest = math.vec4()) {
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
+
+ // Assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+ const m11 = m[0];
+ const m12 = m[4];
+ const m13 = m[8];
+ const m21 = m[1];
+ const m22 = m[5];
+ const m23 = m[9];
+ const m31 = m[2];
+ const m32 = m[6];
+ const m33 = m[10];
+ let s;
+
+ const trace = m11 + m22 + m33;
+
+ if (trace > 0) {
+
+ s = 0.5 / Math.sqrt(trace + 1.0);
+
+ dest[3] = 0.25 / s;
+ dest[0] = ( m32 - m23 ) * s;
+ dest[1] = ( m13 - m31 ) * s;
+ dest[2] = ( m21 - m12 ) * s;
+
+ } else if (m11 > m22 && m11 > m33) {
+
+ s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33);
+
+ dest[3] = ( m32 - m23 ) / s;
+ dest[0] = 0.25 * s;
+ dest[1] = ( m12 + m21 ) / s;
+ dest[2] = ( m13 + m31 ) / s;
+
+ } else if (m22 > m33) {
+
+ s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33);
+
+ dest[3] = ( m13 - m31 ) / s;
+ dest[0] = ( m12 + m21 ) / s;
+ dest[1] = 0.25 * s;
+ dest[2] = ( m23 + m32 ) / s;
+
+ } else {
+
+ s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22);
+
+ dest[3] = ( m21 - m12 ) / s;
+ dest[0] = ( m13 + m31 ) / s;
+ dest[1] = ( m23 + m32 ) / s;
+ dest[2] = 0.25 * s;
+ }
+
+ return dest;
+ },
+
+ vec3PairToQuaternion(u, v, dest = math.vec4()) {
+ const norm_u_norm_v = Math.sqrt(math.dotVec3(u, u) * math.dotVec3(v, v));
+ let real_part = norm_u_norm_v + math.dotVec3(u, v);
+
+ if (real_part < 0.00000001 * norm_u_norm_v) {
+
+ // If u and v are exactly opposite, rotate 180 degrees
+ // around an arbitrary orthogonal axis. Axis normalisation
+ // can happen later, when we normalise the quaternion.
+
+ real_part = 0.0;
+
+ if (Math.abs(u[0]) > Math.abs(u[2])) {
+
+ dest[0] = -u[1];
+ dest[1] = u[0];
+ dest[2] = 0;
+
+ } else {
+ dest[0] = 0;
+ dest[1] = -u[2];
+ dest[2] = u[1]
}
- dest[0] = u[0] - v[0];
- dest[1] = u[1] - v[1];
+
+ } else {
+
+ // Otherwise, build quaternion the standard way.
+ math.cross3Vec3(u, v, dest);
+ }
+
+ dest[3] = real_part;
+
+ return math.normalizeQuaternion(dest);
+ },
+
+ angleAxisToQuaternion(angleAxis, dest = math.vec4()) {
+ const halfAngle = angleAxis[3] / 2.0;
+ const fsin = Math.sin(halfAngle);
+ dest[0] = fsin * angleAxis[0];
+ dest[1] = fsin * angleAxis[1];
+ dest[2] = fsin * angleAxis[2];
+ dest[3] = Math.cos(halfAngle);
+ return dest;
+ },
+
+ quaternionToEuler: ((() => {
+ const mat = new Float32Array(16);
+ return (q, order, dest) => {
+ dest = dest || math.vec3();
+ math.quaternionToRotationMat4(q, mat);
+ math.mat4ToEuler(mat, order, dest);
return dest;
- },
-
- /**
- * Subtracts a scalar value from each element of a four-element vector.
- * @method subVec4Scalar
- * @static
- * @param {Array(Number)} v The vector
- * @param {Number} s The scalar
- * @param {Array(Number)} [dest] Destination vector
- * @return {Array(Number)} dest if specified, v otherwise
- */
- subVec4Scalar: function (v, s, dest) {
- if (!dest) {
- dest = v;
+ };
+ }))(),
+
+ mulQuaternions(p, q, dest = math.vec4()) {
+ const p0 = p[0];
+ const p1 = p[1];
+ const p2 = p[2];
+ const p3 = p[3];
+ const q0 = q[0];
+ const q1 = q[1];
+ const q2 = q[2];
+ const q3 = q[3];
+ dest[0] = p3 * q0 + p0 * q3 + p1 * q2 - p2 * q1;
+ dest[1] = p3 * q1 + p1 * q3 + p2 * q0 - p0 * q2;
+ dest[2] = p3 * q2 + p2 * q3 + p0 * q1 - p1 * q0;
+ dest[3] = p3 * q3 - p0 * q0 - p1 * q1 - p2 * q2;
+ return dest;
+ },
+
+ vec3ApplyQuaternion(q, vec, dest = math.vec3()) {
+ const x = vec[0];
+ const y = vec[1];
+ const z = vec[2];
+
+ const qx = q[0];
+ const qy = q[1];
+ const qz = q[2];
+ const qw = q[3];
+
+ // calculate quat * vector
+
+ const ix = qw * x + qy * z - qz * y;
+ const iy = qw * y + qz * x - qx * z;
+ const iz = qw * z + qx * y - qy * x;
+ const iw = -qx * x - qy * y - qz * z;
+
+ // calculate result * inverse quat
+
+ dest[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy;
+ dest[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz;
+ dest[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx;
+
+ return dest;
+ },
+
+ quaternionToMat4(q, dest) {
+
+ dest = math.identityMat4(dest);
+
+ const q0 = q[0]; //x
+ const q1 = q[1]; //y
+ const q2 = q[2]; //z
+ const q3 = q[3]; //w
+
+ const tx = 2.0 * q0;
+ const ty = 2.0 * q1;
+ const tz = 2.0 * q2;
+
+ const twx = tx * q3;
+ const twy = ty * q3;
+ const twz = tz * q3;
+
+ const txx = tx * q0;
+ const txy = ty * q0;
+ const txz = tz * q0;
+
+ const tyy = ty * q1;
+ const tyz = tz * q1;
+ const tzz = tz * q2;
+
+ dest[0] = 1.0 - (tyy + tzz);
+ dest[1] = txy + twz;
+ dest[2] = txz - twy;
+
+ dest[4] = txy - twz;
+ dest[5] = 1.0 - (txx + tzz);
+ dest[6] = tyz + twx;
+
+ dest[8] = txz + twy;
+ dest[9] = tyz - twx;
+
+ dest[10] = 1.0 - (txx + tyy);
+
+ return dest;
+ },
+
+ quaternionToRotationMat4(q, m) {
+ const x = q[0];
+ const y = q[1];
+ const z = q[2];
+ const w = q[3];
+
+ const x2 = x + x;
+ const y2 = y + y;
+ const z2 = z + z;
+ const xx = x * x2;
+ const xy = x * y2;
+ const xz = x * z2;
+ const yy = y * y2;
+ const yz = y * z2;
+ const zz = z * z2;
+ const wx = w * x2;
+ const wy = w * y2;
+ const wz = w * z2;
+
+ m[0] = 1 - ( yy + zz );
+ m[4] = xy - wz;
+ m[8] = xz + wy;
+
+ m[1] = xy + wz;
+ m[5] = 1 - ( xx + zz );
+ m[9] = yz - wx;
+
+ m[2] = xz - wy;
+ m[6] = yz + wx;
+ m[10] = 1 - ( xx + yy );
+
+ // last column
+ m[3] = 0;
+ m[7] = 0;
+ m[11] = 0;
+
+ // bottom row
+ m[12] = 0;
+ m[13] = 0;
+ m[14] = 0;
+ m[15] = 1;
+
+ return m;
+ },
+
+ normalizeQuaternion(q, dest = q) {
+ const len = math.lenVec4([q[0], q[1], q[2], q[3]]);
+ dest[0] = q[0] / len;
+ dest[1] = q[1] / len;
+ dest[2] = q[2] / len;
+ dest[3] = q[3] / len;
+ return dest;
+ },
+
+ conjugateQuaternion(q, dest = q) {
+ dest[0] = -q[0];
+ dest[1] = -q[1];
+ dest[2] = -q[2];
+ dest[3] = q[3];
+ return dest;
+ },
+
+ inverseQuaternion(q, dest) {
+ return math.normalizeQuaternion(math.conjugateQuaternion(q, dest));
+ },
+
+ quaternionToAngleAxis(q, angleAxis = math.vec4()) {
+ q = math.normalizeQuaternion(q, tempVec4);
+ const q3 = q[3];
+ const angle = 2 * Math.acos(q3);
+ const s = Math.sqrt(1 - q3 * q3);
+ if (s < 0.001) { // test to avoid divide by zero, s is always positive due to sqrt
+ angleAxis[0] = q[0];
+ angleAxis[1] = q[1];
+ angleAxis[2] = q[2];
+ } else {
+ angleAxis[0] = q[0] / s;
+ angleAxis[1] = q[1] / s;
+ angleAxis[2] = q[2] / s;
+ }
+ angleAxis[3] = angle; // * 57.295779579;
+ return angleAxis;
+ },
+
+ decompressPosition(position, decodeMatrix, dest) {
+ dest[0] = position[0] * decodeMatrix[0] + decodeMatrix[12];
+ dest[1] = position[1] * decodeMatrix[5] + decodeMatrix[13];
+ dest[2] = position[2] * decodeMatrix[10] + decodeMatrix[14];
+ },
+
+ decompressPositions(positions, decodeMatrix, dest = new Float32Array(positions.length)) {
+ for (let i = 0, len = positions.length; i < len; i += 3) {
+ dest[i + 0] = positions[i + 0] * decodeMatrix[0] + decodeMatrix[12];
+ dest[i + 1] = positions[i + 1] * decodeMatrix[5] + decodeMatrix[13];
+ dest[i + 2] = positions[i + 2] * decodeMatrix[10] + decodeMatrix[14];
+ }
+ return dest;
+ },
+
+ decompressUV(uv, decodeMatrix, dest) {
+ dest[0] = uv[0] * decodeMatrix[0] + decodeMatrix[6];
+ dest[1] = uv[1] * decodeMatrix[4] + decodeMatrix[7];
+ },
+
+ decompressUVs(uvs, decodeMatrix, dest = new Float32Array(uvs.length)) {
+ for (let i = 0, len = uvs.length; i < len; i += 3) {
+ dest[i + 0] = uvs[i + 0] * decodeMatrix[0] + decodeMatrix[6];
+ dest[i + 1] = uvs[i + 1] * decodeMatrix[4] + decodeMatrix[7];
+ }
+ return dest;
+ },
+
+ octDecodeVec2(oct, result) {
+ let x = oct[0];
+ let y = oct[1];
+ x = (2 * x + 1) / 255;
+ y = (2 * y + 1) / 255;
+ const z = 1 - Math.abs(x) - Math.abs(y);
+ if (z < 0) {
+ x = (1 - Math.abs(y)) * (x >= 0 ? 1 : -1);
+ y = (1 - Math.abs(x)) * (y >= 0 ? 1 : -1);
+ }
+ const length = Math.sqrt(x * x + y * y + z * z);
+ result[0] = x / length;
+ result[1] = y / length;
+ result[2] = z / length;
+ return result;
+ },
+
+ octDecodeVec2s(octs, result) {
+ for (let i = 0, j = 0, len = octs.length; i < len; i += 2) {
+ let x = octs[i + 0];
+ let y = octs[i + 1];
+ x = (2 * x + 1) / 255;
+ y = (2 * y + 1) / 255;
+ const z = 1 - Math.abs(x) - Math.abs(y);
+ if (z < 0) {
+ x = (1 - Math.abs(y)) * (x >= 0 ? 1 : -1);
+ y = (1 - Math.abs(x)) * (y >= 0 ? 1 : -1);
}
- dest[0] = v[0] - s;
- dest[1] = v[1] - s;
- dest[2] = v[2] - s;
- dest[3] = v[3] - s;
- return dest;
- },
-
- /**
- * Sets each element of a 4-element vector to a scalar value minus the value of that element.
- * @method subScalarVec4
- * @static
- * @param {Array(Number)} v The vector
- * @param {Number} s The scalar
- * @param {Array(Number)} [dest] Destination vector
- * @return {Array(Number)} dest if specified, v otherwise
- */
- subScalarVec4: function (v, s, dest) {
- if (!dest) {
- dest = v;
+ const length = Math.sqrt(x * x + y * y + z * z);
+ result[j + 0] = x / length;
+ result[j + 1] = y / length;
+ result[j + 2] = z / length;
+ j += 3;
+ }
+ return result;
+ },
+
+ //------------------------------------------------------------------------------------------------------------------
+ // Boundaries
+ //------------------------------------------------------------------------------------------------------------------
+
+ /**
+ * Returns a new, uninitialized 3D axis-aligned bounding box.
+ *
+ * @private
+ */
+ AABB3(values) {
+ return new Float32Array(values || 6);
+ },
+
+ /**
+ * Returns a new, uninitialized 2D axis-aligned bounding box.
+ *
+ * @private
+ */
+ AABB2(values) {
+ return new Float32Array(values || 4);
+ },
+
+ /**
+ * Returns a new, uninitialized 3D oriented bounding box (OBB).
+ *
+ * @private
+ */
+ OBB3(values) {
+ return new Float32Array(values || 32);
+ },
+
+ /**
+ * Returns a new, uninitialized 2D oriented bounding box (OBB).
+ *
+ * @private
+ */
+ OBB2(values) {
+ return new Float32Array(values || 16);
+ },
+
+
+ /**
+ * Transforms an OBB3 by a 4x4 matrix.
+ *
+ * @private
+ */
+ transformOBB3(m, p, p2 = p) {
+ let i;
+ const len = p.length;
+
+ let x;
+ let y;
+ let z;
+
+ const m0 = m[0];
+ const m1 = m[1];
+ const m2 = m[2];
+ const m3 = m[3];
+ const m4 = m[4];
+ const m5 = m[5];
+ const m6 = m[6];
+ const m7 = m[7];
+ const m8 = m[8];
+ const m9 = m[9];
+ const m10 = m[10];
+ const m11 = m[11];
+ const m12 = m[12];
+ const m13 = m[13];
+ const m14 = m[14];
+ const m15 = m[15];
+
+ for (i = 0; i < len; i += 4) {
+
+ x = p[i + 0];
+ y = p[i + 1];
+ z = p[i + 2];
+
+ p2[i + 0] = (m0 * x) + (m4 * y) + (m8 * z) + m12;
+ p2[i + 1] = (m1 * x) + (m5 * y) + (m9 * z) + m13;
+ p2[i + 2] = (m2 * x) + (m6 * y) + (m10 * z) + m14;
+ p2[i + 3] = (m3 * x) + (m7 * y) + (m11 * z) + m15;
+ }
+
+ return p2;
+ },
+
+ /**
+ * Gets the diagonal size of an AABB3 given as minima and maxima.
+ *
+ * @private
+ */
+ getAABB3Diag: ((() => {
+
+ const min = new Float32Array(3);
+ const max = new Float32Array(3);
+ const tempVec3 = new Float32Array(3);
+
+ return aabb => {
+
+ min[0] = aabb[0];
+ min[1] = aabb[1];
+ min[2] = aabb[2];
+
+ max[0] = aabb[3];
+ max[1] = aabb[4];
+ max[2] = aabb[5];
+
+ math.subVec3(max, min, tempVec3);
+
+ return Math.abs(math.lenVec3(tempVec3));
+ };
+ }))(),
+
+ /**
+ * Get a diagonal boundary size that is symmetrical about the given point.
+ *
+ * @private
+ */
+ getAABB3DiagPoint: ((() => {
+
+ const min = new Float32Array(3);
+ const max = new Float32Array(3);
+ const tempVec3 = new Float32Array(3);
+
+ return (aabb, p) => {
+
+ min[0] = aabb[0];
+ min[1] = aabb[1];
+ min[2] = aabb[2];
+
+ max[0] = aabb[3];
+ max[1] = aabb[4];
+ max[2] = aabb[5];
+
+ const diagVec = math.subVec3(max, min, tempVec3);
+
+ const xneg = p[0] - aabb[0];
+ const xpos = aabb[3] - p[0];
+ const yneg = p[1] - aabb[1];
+ const ypos = aabb[4] - p[1];
+ const zneg = p[2] - aabb[2];
+ const zpos = aabb[5] - p[2];
+
+ diagVec[0] += (xneg > xpos) ? xneg : xpos;
+ diagVec[1] += (yneg > ypos) ? yneg : ypos;
+ diagVec[2] += (zneg > zpos) ? zneg : zpos;
+
+ return Math.abs(math.lenVec3(diagVec));
+ };
+ }))(),
+
+ /**
+ * Gets the center of an AABB.
+ *
+ * @private
+ */
+ getAABB3Center(aabb, dest) {
+ const r = dest || math.vec3();
+
+ r[0] = (aabb[0] + aabb[3] ) / 2;
+ r[1] = (aabb[1] + aabb[4] ) / 2;
+ r[2] = (aabb[2] + aabb[5] ) / 2;
+
+ return r;
+ },
+
+ /**
+ * Gets the center of a 2D AABB.
+ *
+ * @private
+ */
+ getAABB2Center(aabb, dest) {
+ const r = dest || math.vec2();
+
+ r[0] = (aabb[2] + aabb[0] ) / 2;
+ r[1] = (aabb[3] + aabb[1] ) / 2;
+
+ return r;
+ },
+
+ /**
+ * Collapses a 3D axis-aligned boundary, ready to expand to fit 3D points.
+ * Creates new AABB if none supplied.
+ *
+ * @private
+ */
+ collapseAABB3(aabb = math.AABB3()) {
+ aabb[0] = math.MAX_DOUBLE;
+ aabb[1] = math.MAX_DOUBLE;
+ aabb[2] = math.MAX_DOUBLE;
+ aabb[3] = -math.MAX_DOUBLE;
+ aabb[4] = -math.MAX_DOUBLE;
+ aabb[5] = -math.MAX_DOUBLE;
+
+ return aabb;
+ },
+
+ /**
+ * Converts an axis-aligned 3D boundary into an oriented boundary consisting of
+ * an array of eight 3D positions, one for each corner of the boundary.
+ *
+ * @private
+ */
+ AABB3ToOBB3(aabb, obb = math.OBB3()) {
+ obb[0] = aabb[0];
+ obb[1] = aabb[1];
+ obb[2] = aabb[2];
+ obb[3] = 1;
+
+ obb[4] = aabb[3];
+ obb[5] = aabb[1];
+ obb[6] = aabb[2];
+ obb[7] = 1;
+
+ obb[8] = aabb[3];
+ obb[9] = aabb[4];
+ obb[10] = aabb[2];
+ obb[11] = 1;
+
+ obb[12] = aabb[0];
+ obb[13] = aabb[4];
+ obb[14] = aabb[2];
+ obb[15] = 1;
+
+ obb[16] = aabb[0];
+ obb[17] = aabb[1];
+ obb[18] = aabb[5];
+ obb[19] = 1;
+
+ obb[20] = aabb[3];
+ obb[21] = aabb[1];
+ obb[22] = aabb[5];
+ obb[23] = 1;
+
+ obb[24] = aabb[3];
+ obb[25] = aabb[4];
+ obb[26] = aabb[5];
+ obb[27] = 1;
+
+ obb[28] = aabb[0];
+ obb[29] = aabb[4];
+ obb[30] = aabb[5];
+ obb[31] = 1;
+
+ return obb;
+ },
+
+ /**
+ * Finds the minimum axis-aligned 3D boundary enclosing the homogeneous 3D points (x,y,z,w) given in a flattened array.
+ *
+ * @private
+ */
+ positions3ToAABB3: ((() => {
+
+ const p = new Float32Array(3);
+
+ return (positions, aabb, positionsDecodeMatrix) => {
+ aabb = aabb || math.AABB3();
+
+ let xmin = math.MAX_DOUBLE;
+ let ymin = math.MAX_DOUBLE;
+ let zmin = math.MAX_DOUBLE;
+ let xmax = -math.MAX_DOUBLE;
+ let ymax = -math.MAX_DOUBLE;
+ let zmax = -math.MAX_DOUBLE;
+
+ let x;
+ let y;
+ let z;
+
+ for (let i = 0, len = positions.length; i < len; i += 3) {
+
+ if (positionsDecodeMatrix) {
+
+ p[0] = positions[i + 0];
+ p[1] = positions[i + 1];
+ p[2] = positions[i + 2];
+
+ math.decompressPosition(p, positionsDecodeMatrix, p);
+
+ x = p[0];
+ y = p[1];
+ z = p[2];
+
+ } else {
+ x = positions[i + 0];
+ y = positions[i + 1];
+ z = positions[i + 2];
+ }
+
+ if (x < xmin) {
+ xmin = x;
+ }
+
+ if (y < ymin) {
+ ymin = y;
+ }
+
+ if (z < zmin) {
+ zmin = z;
+ }
+
+ if (x > xmax) {
+ xmax = x;
+ }
+
+ if (y > ymax) {
+ ymax = y;
+ }
+
+ if (z > zmax) {
+ zmax = z;
+ }
}
- dest[0] = s - v[0];
- dest[1] = s - v[1];
- dest[2] = s - v[2];
- dest[3] = s - v[3];
- return dest;
- },
-
- /**
- * Multiplies one three-element vector by another.
- * @method mulVec3
- * @static
- * @param {Array(Number)} u First vector
- * @param {Array(Number)} v Second vector
- * @param {Array(Number)} [dest] Destination vector
- * @return {Array(Number)} dest if specified, u otherwise
- */
- mulVec4: function (u, v, dest) {
- if (!dest) {
- dest = u;
+
+ aabb[0] = xmin;
+ aabb[1] = ymin;
+ aabb[2] = zmin;
+ aabb[3] = xmax;
+ aabb[4] = ymax;
+ aabb[5] = zmax;
+
+ return aabb;
+ };
+ }))(),
+
+ /**
+ * Finds the minimum axis-aligned 3D boundary enclosing the homogeneous 3D points (x,y,z,w) given in a flattened array.
+ *
+ * @private
+ */
+ OBB3ToAABB3(obb, aabb = math.AABB3()) {
+ let xmin = math.MAX_DOUBLE;
+ let ymin = math.MAX_DOUBLE;
+ let zmin = math.MAX_DOUBLE;
+ let xmax = -math.MAX_DOUBLE;
+ let ymax = -math.MAX_DOUBLE;
+ let zmax = -math.MAX_DOUBLE;
+
+ let x;
+ let y;
+ let z;
+
+ for (let i = 0, len = obb.length; i < len; i += 4) {
+
+ x = obb[i + 0];
+ y = obb[i + 1];
+ z = obb[i + 2];
+
+ if (x < xmin) {
+ xmin = x;
}
- dest[0] = u[0] * v[0];
- dest[1] = u[1] * v[1];
- dest[2] = u[2] * v[2];
- dest[3] = u[3] * v[3];
- return dest;
- },
-
- /**
- * Multiplies each element of a four-element vector by a scalar.
- * @method mulVec34calar
- * @static
- * @param {Array(Number)} v The vector
- * @param {Number} s The scalar
- * @param {Array(Number)} [dest] Destination vector
- * @return {Array(Number)} dest if specified, v otherwise
- */
- mulVec4Scalar: function (v, s, dest) {
- if (!dest) {
- dest = v;
+
+ if (y < ymin) {
+ ymin = y;
}
- dest[0] = v[0] * s;
- dest[1] = v[1] * s;
- dest[2] = v[2] * s;
- dest[3] = v[3] * s;
- return dest;
- },
-
- /**
- * Multiplies each element of a three-element vector by a scalar.
- * @method mulVec3Scalar
- * @static
- * @param {Array(Number)} v The vector
- * @param {Number} s The scalar
- * @param {Array(Number)} [dest] Destination vector
- * @return {Array(Number)} dest if specified, v otherwise
- */
- mulVec3Scalar: function (v, s, dest) {
- if (!dest) {
- dest = v;
+
+ if (z < zmin) {
+ zmin = z;
}
- dest[0] = v[0] * s;
- dest[1] = v[1] * s;
- dest[2] = v[2] * s;
- return dest;
- },
-
- /**
- * Multiplies each element of a two-element vector by a scalar.
- * @method mulVec2Scalar
- * @static
- * @param {Array(Number)} v The vector
- * @param {Number} s The scalar
- * @param {Array(Number)} [dest] Destination vector
- * @return {Array(Number)} dest if specified, v otherwise
- */
- mulVec2Scalar: function (v, s, dest) {
- if (!dest) {
- dest = v;
+
+ if (x > xmax) {
+ xmax = x;
}
- dest[0] = v[0] * s;
- dest[1] = v[1] * s;
- return dest;
- },
-
- /**
- * Divides one three-element vector by another.
- * @method divVec3
- * @static
- * @param {Array(Number)} u First vector
- * @param {Array(Number)} v Second vector
- * @param {Array(Number)} [dest] Destination vector
- * @return {Array(Number)} dest if specified, u otherwise
- */
- divVec3: function (u, v, dest) {
- if (!dest) {
- dest = u;
+
+ if (y > ymax) {
+ ymax = y;
}
- dest[0] = u[0] / v[0];
- dest[1] = u[1] / v[1];
- dest[2] = u[2] / v[2];
- return dest;
- },
-
- /**
- * Divides one four-element vector by another.
- * @method divVec4
- * @static
- * @param {Array(Number)} u First vector
- * @param {Array(Number)} v Second vector
- * @param {Array(Number)} [dest] Destination vector
- * @return {Array(Number)} dest if specified, u otherwise
- */
- divVec4: function (u, v, dest) {
- if (!dest) {
- dest = u;
+
+ if (z > zmax) {
+ zmax = z;
}
- dest[0] = u[0] / v[0];
- dest[1] = u[1] / v[1];
- dest[2] = u[2] / v[2];
- dest[3] = u[3] / v[3];
- return dest;
- },
-
- /**
- * Divides a scalar by a three-element vector, returning a new vector.
- * @method divScalarVec3
- * @static
- * @param v vec3
- * @param s scalar
- * @param dest vec3 - optional destination
- * @return [] dest if specified, v otherwise
- */
- divScalarVec3: function (s, v, dest) {
- if (!dest) {
- dest = v;
+ }
+
+ aabb[0] = xmin;
+ aabb[1] = ymin;
+ aabb[2] = zmin;
+ aabb[3] = xmax;
+ aabb[4] = ymax;
+ aabb[5] = zmax;
+
+ return aabb;
+ },
+
+ /**
+ * Finds the minimum axis-aligned 3D boundary enclosing the given 3D points.
+ *
+ * @private
+ */
+ points3ToAABB3(points, aabb = math.AABB3()) {
+ let xmin = math.MAX_DOUBLE;
+ let ymin = math.MAX_DOUBLE;
+ let zmin = math.MAX_DOUBLE;
+ let xmax = -math.MAX_DOUBLE;
+ let ymax = -math.MAX_DOUBLE;
+ let zmax = -math.MAX_DOUBLE;
+
+ let x;
+ let y;
+ let z;
+
+ for (let i = 0, len = points.length; i < len; i++) {
+
+ x = points[i][0];
+ y = points[i][1];
+ z = points[i][2];
+
+ if (x < xmin) {
+ xmin = x;
}
- dest[0] = s / v[0];
- dest[1] = s / v[1];
- dest[2] = s / v[2];
- return dest;
- },
-
- /**
- * Divides a three-element vector by a scalar.
- * @method divVec3Scalar
- * @static
- * @param v vec3
- * @param s scalar
- * @param dest vec3 - optional destination
- * @return [] dest if specified, v otherwise
- */
- divVec3Scalar: function (v, s, dest) {
- if (!dest) {
- dest = v;
+
+ if (y < ymin) {
+ ymin = y;
}
- dest[0] = v[0] / s;
- dest[1] = v[1] / s;
- dest[2] = v[2] / s;
- return dest;
- },
-
- /**
- * Divides a four-element vector by a scalar.
- * @method divVec4Scalar
- * @static
- * @param v vec4
- * @param s scalar
- * @param dest vec4 - optional destination
- * @return [] dest if specified, v otherwise
- */
- divVec4Scalar: function (v, s, dest) {
- if (!dest) {
- dest = v;
+
+ if (z < zmin) {
+ zmin = z;
}
- dest[0] = v[0] / s;
- dest[1] = v[1] / s;
- dest[2] = v[2] / s;
- dest[3] = v[3] / s;
- return dest;
- },
-
-
- /**
- * Divides a scalar by a four-element vector, returning a new vector.
- * @method divScalarVec4
- * @static
- * @param s scalar
- * @param v vec4
- * @param dest vec4 - optional destination
- * @return [] dest if specified, v otherwise
- */
- divScalarVec4: function (s, v, dest) {
- if (!dest) {
- dest = v;
+
+ if (x > xmax) {
+ xmax = x;
}
- dest[0] = s / v[0];
- dest[1] = s / v[1];
- dest[2] = s / v[2];
- dest[3] = s / v[3];
- return dest;
- },
-
- /**
- * Returns the dot product of two four-element vectors.
- * @method dotVec4
- * @static
- * @param {Array(Number)} u First vector
- * @param {Array(Number)} v Second vector
- * @return The dot product
- */
- dotVec4: function (u, v) {
- return (u[0] * v[0] + u[1] * v[1] + u[2] * v[2] + u[3] * v[3]);
- },
-
- /**
- * Returns the cross product of two four-element vectors.
- * @method cross3Vec4
- * @static
- * @param {Array(Number)} u First vector
- * @param {Array(Number)} v Second vector
- * @return The cross product
- */
- cross3Vec4: function (u, v) {
- const u0 = u[0], u1 = u[1], u2 = u[2];
- const v0 = v[0], v1 = v[1], v2 = v[2];
- return [
- u1 * v2 - u2 * v1,
- u2 * v0 - u0 * v2,
- u0 * v1 - u1 * v0,
- 0.0];
- },
-
- /**
- * Returns the cross product of two three-element vectors.
- * @method cross3Vec3
- * @static
- * @param {Array(Number)} u First vector
- * @param {Array(Number)} v Second vector
- * @return The cross product
- */
- cross3Vec3: function (u, v, dest) {
- if (!dest) {
- dest = u;
+
+ if (y > ymax) {
+ ymax = y;
}
- const x = u[0], y = u[1], z = u[2];
- const x2 = v[0], y2 = v[1], z2 = v[2];
- dest[0] = y * z2 - z * y2;
- dest[1] = z * x2 - x * z2;
- dest[2] = x * y2 - y * x2;
- return dest;
- },
-
-
- sqLenVec4: function (v) { // TODO
- return math.dotVec4(v, v);
- },
-
- /**
- * Returns the length of a four-element vector.
- * @method lenVec4
- * @static
- * @param {Array(Number)} v The vector
- * @return The length
- */
- lenVec4: function (v) {
- return Math.sqrt(math.sqLenVec4(v));
- },
-
- /**
- * Returns the dot product of two three-element vectors.
- * @method dotVec3
- * @static
- * @param {Array(Number)} u First vector
- * @param {Array(Number)} v Second vector
- * @return The dot product
- */
- dotVec3: function (u, v) {
- return (u[0] * v[0] + u[1] * v[1] + u[2] * v[2]);
- },
-
- /**
- * Returns the dot product of two two-element vectors.
- * @method dotVec4
- * @static
- * @param {Array(Number)} u First vector
- * @param {Array(Number)} v Second vector
- * @return The dot product
- */
- dotVec2: function (u, v) {
- return (u[0] * v[0] + u[1] * v[1]);
- },
-
-
- sqLenVec3: function (v) {
- return math.dotVec3(v, v);
- },
-
-
- sqLenVec2: function (v) {
- return math.dotVec2(v, v);
- },
-
- /**
- * Returns the length of a three-element vector.
- * @method lenVec3
- * @static
- * @param {Array(Number)} v The vector
- * @return The length
- */
- lenVec3: function (v) {
- return Math.sqrt(math.sqLenVec3(v));
- },
-
- distVec3: (function () {
- const vec = new Float32Array(3);
- return function (v, w) {
- return math.lenVec3(math.subVec3(v, w, vec));
- };
- })(),
-
- /**
- * Returns the length of a two-element vector.
- * @method lenVec2
- * @static
- * @param {Array(Number)} v The vector
- * @return The length
- */
- lenVec2: function (v) {
- return Math.sqrt(math.sqLenVec2(v));
- },
-
- distVec2: (function () {
- const vec = new Float32Array(2);
- return function (v, w) {
- return math.lenVec2(math.subVec2(v, w, vec));
- };
- })(),
-
- /**
- * @method rcpVec3
- * @static
- * @param v vec3
- * @param dest vec3 - optional destination
- * @return [] dest if specified, v otherwise
- *
- */
- rcpVec3: function (v, dest) {
- return math.divScalarVec3(1.0, v, dest);
- },
-
- /**
- * Normalizes a four-element vector
- * @method normalizeVec4
- * @static
- * @param v vec4
- * @param dest vec4 - optional destination
- * @return [] dest if specified, v otherwise
- *
- */
- normalizeVec4: function (v, dest) {
- const f = 1.0 / math.lenVec4(v);
- return math.mulVec4Scalar(v, f, dest);
- },
-
- /**
- * Normalizes a three-element vector
- * @method normalizeVec4
- * @static
- */
- normalizeVec3: function (v, dest) {
- const f = 1.0 / math.lenVec3(v);
- return math.mulVec3Scalar(v, f, dest);
- },
-
- /**
- * Normalizes a two-element vector
- * @method normalizeVec2
- * @static
- */
- normalizeVec2: function (v, dest) {
- const f = 1.0 / math.lenVec2(v);
- return math.mulVec2Scalar(v, f, dest);
- },
-
- /**
- * Gets the angle between two vectors
- * @method angleVec3
- * @param v
- * @param w
- * @returns {number}
- */
- angleVec3: function (v, w) {
- let theta = xeogl.math.dotVec3(v, w) / ( Math.sqrt(xeogl.math.sqLenVec3(v) * xeogl.math.sqLenVec3(w)) );
- theta = theta < -1 ? -1 : (theta > 1 ? 1 : theta); // Clamp to handle numerical problems
- return Math.acos(theta);
- },
-
- /**
- * Creates a three-element vector from the rotation part of a sixteen-element matrix.
- * @param m
- * @param dest
- */
- vec3FromMat4Scale: (function () {
-
- const tempVec3 = new Float32Array(3);
-
- return function (m, dest) {
-
- tempVec3[0] = m[0];
- tempVec3[1] = m[1];
- tempVec3[2] = m[2];
-
- dest[0] = math.lenVec3(tempVec3);
-
- tempVec3[0] = m[4];
- tempVec3[1] = m[5];
- tempVec3[2] = m[6];
-
- dest[1] = math.lenVec3(tempVec3);
-
- tempVec3[0] = m[8];
- tempVec3[1] = m[9];
- tempVec3[2] = m[10];
-
- dest[2] = math.lenVec3(tempVec3);
-
- return dest;
- };
- })(),
-
- /**
- * Converts an n-element vector to a JSON-serializable
- * array with values rounded to two decimal places.
- */
- vecToArray: (function () {
- function trunc(v) {
- return Math.round(v * 100000) / 100000
+
+ if (z > zmax) {
+ zmax = z;
}
+ }
- return function (v) {
- v = Array.prototype.slice.call(v);
- for (let i = 0, len = v.length; i < len; i++) {
- v[i] = trunc(v[i]);
- }
- return v;
- };
- })(),
-
- /**
- * Duplicates a 4x4 identity matrix.
- * @method dupMat4
- * @static
- */
- dupMat4: function (m) {
- return m.slice(0, 16);
- },
-
- /**
- * Extracts a 3x3 matrix from a 4x4 matrix.
- * @method mat4To3
- * @static
- */
- mat4To3: function (m) {
- return [
- m[0], m[1], m[2],
- m[4], m[5], m[6],
- m[8], m[9], m[10]
- ];
- },
-
- /**
- * Returns a 4x4 matrix with each element set to the given scalar value.
- * @method m4s
- * @static
- */
- m4s: function (s) {
- return [
- s, s, s, s,
- s, s, s, s,
- s, s, s, s,
- s, s, s, s
- ];
- },
-
- /**
- * Returns a 4x4 matrix with each element set to zero.
- * @method setMat4ToZeroes
- * @static
- */
- setMat4ToZeroes: function () {
- return math.m4s(0.0);
- },
-
- /**
- * Returns a 4x4 matrix with each element set to 1.0.
- * @method setMat4ToOnes
- * @static
- */
- setMat4ToOnes: function () {
- return math.m4s(1.0);
- },
-
- /**
- * Returns a 4x4 matrix with each element set to 1.0.
- * @method setMat4ToOnes
- * @static
- */
- diagonalMat4v: function (v) {
- return new Float32Array([
- v[0], 0.0, 0.0, 0.0,
- 0.0, v[1], 0.0, 0.0,
- 0.0, 0.0, v[2], 0.0,
- 0.0, 0.0, 0.0, v[3]
- ]);
- },
-
- /**
- * Returns a 4x4 matrix with diagonal elements set to the given vector.
- * @method diagonalMat4c
- * @static
- */
- diagonalMat4c: function (x, y, z, w) {
- return math.diagonalMat4v([x, y, z, w]);
- },
-
- /**
- * Returns a 4x4 matrix with diagonal elements set to the given scalar.
- * @method diagonalMat4s
- * @static
- */
- diagonalMat4s: function (s) {
- return math.diagonalMat4c(s, s, s, s);
- },
-
- /**
- * Returns a 4x4 identity matrix.
- * @method identityMat4
- * @static
- */
- identityMat4: function (mat) {
-
- mat = mat || new Float32Array(16);
-
- mat[0] = 1.0;
- mat[1] = 0.0;
- mat[2] = 0.0;
- mat[3] = 0.0;
-
- mat[4] = 0.0;
- mat[5] = 1.0;
- mat[6] = 0.0;
- mat[7] = 0.0;
-
- mat[8] = 0.0;
- mat[9] = 0.0;
- mat[10] = 1.0;
- mat[11] = 0.0;
-
- mat[12] = 0.0;
- mat[13] = 0.0;
- mat[14] = 0.0;
- mat[15] = 1.0;
+ aabb[0] = xmin;
+ aabb[1] = ymin;
+ aabb[2] = zmin;
+ aabb[3] = xmax;
+ aabb[4] = ymax;
+ aabb[5] = zmax;
- return mat;
- },
+ return aabb;
+ },
- /**
- * Returns a 3x3 identity matrix.
- * @method identityMat3
- * @static
- */
- identityMat3: function (mat) {
+ /**
+ * Finds the minimum boundary sphere enclosing the given 3D points.
+ *
+ * @private
+ */
+ points3ToSphere3: ((() => {
- mat = mat || new Float32Array(9);
+ const tempVec3 = new Float32Array(3);
- mat[0] = 1.0;
- mat[1] = 0.0;
- mat[2] = 0.0;
+ return (points, sphere) => {
- mat[3] = 0.0;
- mat[4] = 1.0;
- mat[5] = 0.0;
+ sphere = sphere || math.vec4();
- mat[6] = 0.0;
- mat[7] = 0.0;
- mat[8] = 1.0;
+ let x = 0;
+ let y = 0;
+ let z = 0;
- return mat;
- },
-
- /**
- * Tests if the given 4x4 matrix is the identity matrix.
- * @method isIdentityMat4
- * @static
- */
- isIdentityMat4: function (m) {
- if (m[0] !== 1.0 || m[1] !== 0.0 || m[2] !== 0.0 || m[3] !== 0.0 ||
- m[4] !== 0.0 || m[5] !== 1.0 || m[6] !== 0.0 || m[7] !== 0.0 ||
- m[8] !== 0.0 || m[9] !== 0.0 || m[10] !== 1.0 || m[11] !== 0.0 ||
- m[12] !== 0.0 || m[13] !== 0.0 || m[14] !== 0.0 || m[15] !== 1.0) {
- return false;
- }
- return true;
- },
-
- /**
- * Negates the given 4x4 matrix.
- * @method negateMat4
- * @static
- */
- negateMat4: function (m, dest) {
- if (!dest) {
- dest = m;
- }
- dest[0] = -m[0];
- dest[1] = -m[1];
- dest[2] = -m[2];
- dest[3] = -m[3];
- dest[4] = -m[4];
- dest[5] = -m[5];
- dest[6] = -m[6];
- dest[7] = -m[7];
- dest[8] = -m[8];
- dest[9] = -m[9];
- dest[10] = -m[10];
- dest[11] = -m[11];
- dest[12] = -m[12];
- dest[13] = -m[13];
- dest[14] = -m[14];
- dest[15] = -m[15];
- return dest;
- },
-
- /**
- * Adds the given 4x4 matrices together.
- * @method addMat4
- * @static
- */
- addMat4: function (a, b, dest) {
- if (!dest) {
- dest = a;
- }
- dest[0] = a[0] + b[0];
- dest[1] = a[1] + b[1];
- dest[2] = a[2] + b[2];
- dest[3] = a[3] + b[3];
- dest[4] = a[4] + b[4];
- dest[5] = a[5] + b[5];
- dest[6] = a[6] + b[6];
- dest[7] = a[7] + b[7];
- dest[8] = a[8] + b[8];
- dest[9] = a[9] + b[9];
- dest[10] = a[10] + b[10];
- dest[11] = a[11] + b[11];
- dest[12] = a[12] + b[12];
- dest[13] = a[13] + b[13];
- dest[14] = a[14] + b[14];
- dest[15] = a[15] + b[15];
- return dest;
- },
-
- /**
- * Adds the given scalar to each element of the given 4x4 matrix.
- * @method addMat4Scalar
- * @static
- */
- addMat4Scalar: function (m, s, dest) {
- if (!dest) {
- dest = m;
- }
- dest[0] = m[0] + s;
- dest[1] = m[1] + s;
- dest[2] = m[2] + s;
- dest[3] = m[3] + s;
- dest[4] = m[4] + s;
- dest[5] = m[5] + s;
- dest[6] = m[6] + s;
- dest[7] = m[7] + s;
- dest[8] = m[8] + s;
- dest[9] = m[9] + s;
- dest[10] = m[10] + s;
- dest[11] = m[11] + s;
- dest[12] = m[12] + s;
- dest[13] = m[13] + s;
- dest[14] = m[14] + s;
- dest[15] = m[15] + s;
- return dest;
- },
-
- /**
- * Adds the given scalar to each element of the given 4x4 matrix.
- * @method addScalarMat4
- * @static
- */
- addScalarMat4: function (s, m, dest) {
- return math.addMat4Scalar(m, s, dest);
- },
-
- /**
- * Subtracts the second 4x4 matrix from the first.
- * @method subMat4
- * @static
- */
- subMat4: function (a, b, dest) {
- if (!dest) {
- dest = a;
- }
- dest[0] = a[0] - b[0];
- dest[1] = a[1] - b[1];
- dest[2] = a[2] - b[2];
- dest[3] = a[3] - b[3];
- dest[4] = a[4] - b[4];
- dest[5] = a[5] - b[5];
- dest[6] = a[6] - b[6];
- dest[7] = a[7] - b[7];
- dest[8] = a[8] - b[8];
- dest[9] = a[9] - b[9];
- dest[10] = a[10] - b[10];
- dest[11] = a[11] - b[11];
- dest[12] = a[12] - b[12];
- dest[13] = a[13] - b[13];
- dest[14] = a[14] - b[14];
- dest[15] = a[15] - b[15];
- return dest;
- },
-
- /**
- * Subtracts the given scalar from each element of the given 4x4 matrix.
- * @method subMat4Scalar
- * @static
- */
- subMat4Scalar: function (m, s, dest) {
- if (!dest) {
- dest = m;
- }
- dest[0] = m[0] - s;
- dest[1] = m[1] - s;
- dest[2] = m[2] - s;
- dest[3] = m[3] - s;
- dest[4] = m[4] - s;
- dest[5] = m[5] - s;
- dest[6] = m[6] - s;
- dest[7] = m[7] - s;
- dest[8] = m[8] - s;
- dest[9] = m[9] - s;
- dest[10] = m[10] - s;
- dest[11] = m[11] - s;
- dest[12] = m[12] - s;
- dest[13] = m[13] - s;
- dest[14] = m[14] - s;
- dest[15] = m[15] - s;
- return dest;
- },
-
- /**
- * Subtracts the given scalar from each element of the given 4x4 matrix.
- * @method subScalarMat4
- * @static
- */
- subScalarMat4: function (s, m, dest) {
- if (!dest) {
- dest = m;
- }
- dest[0] = s - m[0];
- dest[1] = s - m[1];
- dest[2] = s - m[2];
- dest[3] = s - m[3];
- dest[4] = s - m[4];
- dest[5] = s - m[5];
- dest[6] = s - m[6];
- dest[7] = s - m[7];
- dest[8] = s - m[8];
- dest[9] = s - m[9];
- dest[10] = s - m[10];
- dest[11] = s - m[11];
- dest[12] = s - m[12];
- dest[13] = s - m[13];
- dest[14] = s - m[14];
- dest[15] = s - m[15];
- return dest;
- },
-
- /**
- * Multiplies the two given 4x4 matrix by each other.
- * @method mulMat4
- * @static
- */
- mulMat4: function (a, b, dest) {
- if (!dest) {
- dest = a;
+ let i;
+ const numPoints = points.length;
+
+ for (i = 0; i < numPoints; i++) {
+ x += points[i][0];
+ y += points[i][1];
+ z += points[i][2];
}
- // Cache the matrix values (makes for huge speed increases!)
- const a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
- const a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
- const a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
- const a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
-
- const b00 = b[0], b01 = b[1], b02 = b[2], b03 = b[3];
- const b10 = b[4], b11 = b[5], b12 = b[6], b13 = b[7];
- const b20 = b[8], b21 = b[9], b22 = b[10], b23 = b[11];
- const b30 = b[12], b31 = b[13], b32 = b[14], b33 = b[15];
-
- dest[0] = b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30;
- dest[1] = b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31;
- dest[2] = b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32;
- dest[3] = b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33;
- dest[4] = b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30;
- dest[5] = b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31;
- dest[6] = b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32;
- dest[7] = b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33;
- dest[8] = b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30;
- dest[9] = b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31;
- dest[10] = b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32;
- dest[11] = b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33;
- dest[12] = b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30;
- dest[13] = b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31;
- dest[14] = b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32;
- dest[15] = b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33;
+ sphere[0] = x / numPoints;
+ sphere[1] = y / numPoints;
+ sphere[2] = z / numPoints;
- return dest;
- },
+ let radius = 0;
+ let dist;
+
+ for (i = 0; i < numPoints; i++) {
- /**
- * Multiplies the two given 3x3 matrices by each other.
- * @method mulMat4
- * @static
- */
- mulMat3: function (a, b, dest) {
+ dist = Math.abs(math.lenVec3(math.subVec3(points[i], sphere, tempVec3)));
- if (!dest) {
- dest = new Float32Array(9);
+ if (dist > radius) {
+ radius = dist;
+ }
}
- const a11 = a[0], a12 = a[3], a13 = a[6];
- const a21 = a[1], a22 = a[4], a23 = a[7];
- const a31 = a[2], a32 = a[5], a33 = a[8];
+ sphere[3] = radius;
- const b11 = b[0], b12 = b[3], b13 = b[6];
- const b21 = b[1], b22 = b[4], b23 = b[7];
- const b31 = b[2], b32 = b[5], b33 = b[8];
+ return sphere;
+ };
+ }))(),
- dest[0] = a11 * b11 + a12 * b21 + a13 * b31;
- dest[3] = a11 * b12 + a12 * b22 + a13 * b32;
- dest[6] = a11 * b13 + a12 * b23 + a13 * b33;
+ /**
+ * Finds the minimum boundary sphere enclosing the given 3D points.
+ *
+ * @private
+ */
+ OBB3ToSphere3: ((() => {
- dest[1] = a21 * b11 + a22 * b21 + a23 * b31;
- dest[4] = a21 * b12 + a22 * b22 + a23 * b32;
- dest[7] = a21 * b13 + a22 * b23 + a23 * b33;
+ const point = new Float32Array(3);
+ const tempVec3 = new Float32Array(3);
- dest[2] = a31 * b11 + a32 * b21 + a33 * b31;
- dest[5] = a31 * b12 + a32 * b22 + a33 * b32;
- dest[8] = a31 * b13 + a32 * b23 + a33 * b33;
+ return (points, sphere) => {
- return dest;
- },
-
- /**
- * Multiplies each element of the given 4x4 matrix by the given scalar.
- * @method mulMat4Scalar
- * @static
- */
- mulMat4Scalar: function (m, s, dest) {
- if (!dest) {
- dest = m;
- }
- dest[0] = m[0] * s;
- dest[1] = m[1] * s;
- dest[2] = m[2] * s;
- dest[3] = m[3] * s;
- dest[4] = m[4] * s;
- dest[5] = m[5] * s;
- dest[6] = m[6] * s;
- dest[7] = m[7] * s;
- dest[8] = m[8] * s;
- dest[9] = m[9] * s;
- dest[10] = m[10] * s;
- dest[11] = m[11] * s;
- dest[12] = m[12] * s;
- dest[13] = m[13] * s;
- dest[14] = m[14] * s;
- dest[15] = m[15] * s;
- return dest;
- },
-
- /**
- * Multiplies the given 4x4 matrix by the given four-element vector.
- * @method mulMat4v4
- * @static
- */
- mulMat4v4: function (m, v, dest) {
- dest = dest || math.vec4();
- const v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
- dest[0] = m[0] * v0 + m[4] * v1 + m[8] * v2 + m[12] * v3;
- dest[1] = m[1] * v0 + m[5] * v1 + m[9] * v2 + m[13] * v3;
- dest[2] = m[2] * v0 + m[6] * v1 + m[10] * v2 + m[14] * v3;
- dest[3] = m[3] * v0 + m[7] * v1 + m[11] * v2 + m[15] * v3;
- return dest;
- },
-
- /**
- * Transposes the given 4x4 matrix.
- * @method transposeMat4
- * @static
- */
- transposeMat4: function (mat, dest) {
- // If we are transposing ourselves we can skip a few steps but have to cache some values
- const m4 = mat[4], m14 = mat[14], m8 = mat[8];
- const m13 = mat[13], m12 = mat[12], m9 = mat[9];
- if (!dest || mat === dest) {
- const a01 = mat[1], a02 = mat[2], a03 = mat[3];
- const a12 = mat[6], a13 = mat[7];
- const a23 = mat[11];
- mat[1] = m4;
- mat[2] = m8;
- mat[3] = m12;
- mat[4] = a01;
- mat[6] = m9;
- mat[7] = m13;
- mat[8] = a02;
- mat[9] = a12;
- mat[11] = m14;
- mat[12] = a03;
- mat[13] = a13;
- mat[14] = a23;
- return mat;
- }
- dest[0] = mat[0];
- dest[1] = m4;
- dest[2] = m8;
- dest[3] = m12;
- dest[4] = mat[1];
- dest[5] = mat[5];
- dest[6] = m9;
- dest[7] = m13;
- dest[8] = mat[2];
- dest[9] = mat[6];
- dest[10] = mat[10];
- dest[11] = m14;
- dest[12] = mat[3];
- dest[13] = mat[7];
- dest[14] = mat[11];
- dest[15] = mat[15];
- return dest;
- },
-
- /**
- * Transposes the given 3x3 matrix.
- *
- * @method transposeMat3
- * @static
- */
- transposeMat3: function (mat, dest) {
- if (dest === mat) {
- const a01 = mat[1];
- const a02 = mat[2];
- const a12 = mat[5];
- dest[1] = mat[3];
- dest[2] = mat[6];
- dest[3] = a01;
- dest[5] = mat[7];
- dest[6] = a02;
- dest[7] = a12;
- } else {
- dest[0] = mat[0];
- dest[1] = mat[3];
- dest[2] = mat[6];
- dest[3] = mat[1];
- dest[4] = mat[4];
- dest[5] = mat[7];
- dest[6] = mat[2];
- dest[7] = mat[5];
- dest[8] = mat[8];
+ sphere = sphere || math.vec4();
+
+ let x = 0;
+ let y = 0;
+ let z = 0;
+
+ let i;
+ const lenPoints = points.length;
+ const numPoints = lenPoints / 4;
+
+ for (i = 0; i < lenPoints; i += 4) {
+ x += points[i + 0];
+ y += points[i + 1];
+ z += points[i + 2];
}
- return dest;
- },
-
- /**
- * Returns the determinant of the given 4x4 matrix.
- * @method determinantMat4
- * @static
- */
- determinantMat4: function (mat) {
- // Cache the matrix values (makes for huge speed increases!)
- const a00 = mat[0], a01 = mat[1], a02 = mat[2], a03 = mat[3];
- const a10 = mat[4], a11 = mat[5], a12 = mat[6], a13 = mat[7];
- const a20 = mat[8], a21 = mat[9], a22 = mat[10], a23 = mat[11];
- const a30 = mat[12], a31 = mat[13], a32 = mat[14], a33 = mat[15];
- return a30 * a21 * a12 * a03 - a20 * a31 * a12 * a03 - a30 * a11 * a22 * a03 + a10 * a31 * a22 * a03 +
- a20 * a11 * a32 * a03 - a10 * a21 * a32 * a03 - a30 * a21 * a02 * a13 + a20 * a31 * a02 * a13 +
- a30 * a01 * a22 * a13 - a00 * a31 * a22 * a13 - a20 * a01 * a32 * a13 + a00 * a21 * a32 * a13 +
- a30 * a11 * a02 * a23 - a10 * a31 * a02 * a23 - a30 * a01 * a12 * a23 + a00 * a31 * a12 * a23 +
- a10 * a01 * a32 * a23 - a00 * a11 * a32 * a23 - a20 * a11 * a02 * a33 + a10 * a21 * a02 * a33 +
- a20 * a01 * a12 * a33 - a00 * a21 * a12 * a33 - a10 * a01 * a22 * a33 + a00 * a11 * a22 * a33;
- },
-
- /**
- * Returns the inverse of the given 4x4 matrix.
- * @method inverseMat4
- * @static
- */
- inverseMat4: function (mat, dest) {
- if (!dest) {
- dest = mat;
+
+ sphere[0] = x / numPoints;
+ sphere[1] = y / numPoints;
+ sphere[2] = z / numPoints;
+
+ let radius = 0;
+ let dist;
+
+ for (i = 0; i < lenPoints; i += 4) {
+
+ point[0] = points[i + 0];
+ point[1] = points[i + 1];
+ point[2] = points[i + 2];
+
+ dist = Math.abs(math.lenVec3(math.subVec3(point, sphere, tempVec3)));
+
+ if (dist > radius) {
+ radius = dist;
+ }
}
- // Cache the matrix values (makes for huge speed increases!)
- const a00 = mat[0], a01 = mat[1], a02 = mat[2], a03 = mat[3];
- const a10 = mat[4], a11 = mat[5], a12 = mat[6], a13 = mat[7];
- const a20 = mat[8], a21 = mat[9], a22 = mat[10], a23 = mat[11];
- const a30 = mat[12], a31 = mat[13], a32 = mat[14], a33 = mat[15];
- const b00 = a00 * a11 - a01 * a10;
- const b01 = a00 * a12 - a02 * a10;
- const b02 = a00 * a13 - a03 * a10;
- const b03 = a01 * a12 - a02 * a11;
- const b04 = a01 * a13 - a03 * a11;
- const b05 = a02 * a13 - a03 * a12;
- const b06 = a20 * a31 - a21 * a30;
- const b07 = a20 * a32 - a22 * a30;
- const b08 = a20 * a33 - a23 * a30;
- const b09 = a21 * a32 - a22 * a31;
- const b10 = a21 * a33 - a23 * a31;
- const b11 = a22 * a33 - a23 * a32;
-
- // Calculate the determinant (inlined to avoid double-caching)
- const invDet = 1 / (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06);
-
- dest[0] = (a11 * b11 - a12 * b10 + a13 * b09) * invDet;
- dest[1] = (-a01 * b11 + a02 * b10 - a03 * b09) * invDet;
- dest[2] = (a31 * b05 - a32 * b04 + a33 * b03) * invDet;
- dest[3] = (-a21 * b05 + a22 * b04 - a23 * b03) * invDet;
- dest[4] = (-a10 * b11 + a12 * b08 - a13 * b07) * invDet;
- dest[5] = (a00 * b11 - a02 * b08 + a03 * b07) * invDet;
- dest[6] = (-a30 * b05 + a32 * b02 - a33 * b01) * invDet;
- dest[7] = (a20 * b05 - a22 * b02 + a23 * b01) * invDet;
- dest[8] = (a10 * b10 - a11 * b08 + a13 * b06) * invDet;
- dest[9] = (-a00 * b10 + a01 * b08 - a03 * b06) * invDet;
- dest[10] = (a30 * b04 - a31 * b02 + a33 * b00) * invDet;
- dest[11] = (-a20 * b04 + a21 * b02 - a23 * b00) * invDet;
- dest[12] = (-a10 * b09 + a11 * b07 - a12 * b06) * invDet;
- dest[13] = (a00 * b09 - a01 * b07 + a02 * b06) * invDet;
- dest[14] = (-a30 * b03 + a31 * b01 - a32 * b00) * invDet;
- dest[15] = (a20 * b03 - a21 * b01 + a22 * b00) * invDet;
- return dest;
- },
-
- /**
- * Returns the trace of the given 4x4 matrix.
- * @method traceMat4
- * @static
- */
- traceMat4: function (m) {
- return (m[0] + m[5] + m[10] + m[15]);
- },
-
- /**
- * Returns 4x4 translation matrix.
- * @method translationMat4
- * @static
- */
- translationMat4v: function (v, dest) {
- const m = dest || math.identityMat4();
- m[12] = v[0];
- m[13] = v[1];
- m[14] = v[2];
- return m;
- },
-
- /**
- * Returns 3x3 translation matrix.
- * @method translationMat3
- * @static
- */
- translationMat3v: function (v, dest) {
- const m = dest || math.identityMat3();
- m[6] = v[0];
- m[7] = v[1];
- return m;
- },
-
- /**
- * Returns 4x4 translation matrix.
- * @method translationMat4c
- * @static
- */
- translationMat4c: (function () {
- const xyz = new Float32Array(3);
- return function (x, y, z, dest) {
- xyz[0] = x;
- xyz[1] = y;
- xyz[2] = z;
- return math.translationMat4v(xyz, dest);
- };
- })(),
-
- /**
- * Returns 4x4 translation matrix.
- * @method translationMat4s
- * @static
- */
- translationMat4s: function (s, dest) {
- return math.translationMat4c(s, s, s, dest);
- },
-
- /**
- * Efficiently post-concatenates a translation to the given matrix.
- * @param v
- * @param m
- */
- translateMat4v: function (xyz, m) {
- return math.translateMat4c(xyz[0], xyz[1], xyz[2], m);
- },
-
- /**
- * Efficiently post-concatenates a translation to the given matrix.
- * @param x
- * @param y
- * @param z
- * @param m
- */
- OLDtranslateMat4c: function (x, y, z, m) {
-
- const m12 = m[12];
- m[0] += m12 * x;
- m[4] += m12 * y;
- m[8] += m12 * z;
-
- const m13 = m[13];
- m[1] += m13 * x;
- m[5] += m13 * y;
- m[9] += m13 * z;
-
- const m14 = m[14];
- m[2] += m14 * x;
- m[6] += m14 * y;
- m[10] += m14 * z;
-
- const m15 = m[15];
- m[3] += m15 * x;
- m[7] += m15 * y;
- m[11] += m15 * z;
-
- return m;
- },
-
- translateMat4c: function (x, y, z, m) {
-
- const m3 = m[3];
- m[0] += m3 * x;
- m[1] += m3 * y;
- m[2] += m3 * z;
-
- const m7 = m[7];
- m[4] += m7 * x;
- m[5] += m7 * y;
- m[6] += m7 * z;
-
- const m11 = m[11];
- m[8] += m11 * x;
- m[9] += m11 * y;
- m[10] += m11 * z;
-
- const m15 = m[15];
- m[12] += m15 * x;
- m[13] += m15 * y;
- m[14] += m15 * z;
-
- return m;
- },
- /**
- * Returns 4x4 rotation matrix.
- * @method rotationMat4v
- * @static
- */
- rotationMat4v: function (anglerad, axis, m) {
- const ax = math.normalizeVec4([axis[0], axis[1], axis[2], 0.0], []);
- const s = Math.sin(anglerad);
- const c = Math.cos(anglerad);
- const q = 1.0 - c;
-
- const x = ax[0];
- const y = ax[1];
- const z = ax[2];
-
- let xy, yz, zx, xs, ys, zs;
-
- //xx = x * x; used once
- //yy = y * y; used once
- //zz = z * z; used once
- xy = x * y;
- yz = y * z;
- zx = z * x;
- xs = x * s;
- ys = y * s;
- zs = z * s;
-
- m = m || math.mat4();
-
- m[0] = (q * x * x) + c;
- m[1] = (q * xy) + zs;
- m[2] = (q * zx) - ys;
- m[3] = 0.0;
-
- m[4] = (q * xy) - zs;
- m[5] = (q * y * y) + c;
- m[6] = (q * yz) + xs;
- m[7] = 0.0;
-
- m[8] = (q * zx) + ys;
- m[9] = (q * yz) - xs;
- m[10] = (q * z * z) + c;
- m[11] = 0.0;
-
- m[12] = 0.0;
- m[13] = 0.0;
- m[14] = 0.0;
- m[15] = 1.0;
-
- return m;
- },
-
- /**
- * Returns 4x4 rotation matrix.
- * @method rotationMat4c
- * @static
- */
- rotationMat4c: function (anglerad, x, y, z, mat) {
- return math.rotationMat4v(anglerad, [x, y, z], mat);
- },
-
- /**
- * Returns 4x4 scale matrix.
- * @method scalingMat4v
- * @static
- */
- scalingMat4v: function (v, m) {
- m = m || math.identityMat4();
- m[0] = v[0];
- m[5] = v[1];
- m[10] = v[2];
- return m;
- },
-
- /**
- * Returns 3x3 scale matrix.
- * @method scalingMat3v
- * @static
- */
- scalingMat3v: function (v, m) {
- m = m || math.identityMat3();
- m[0] = v[0];
- m[4] = v[1];
- return m;
- },
-
- /**
- * Returns 4x4 scale matrix.
- * @method scalingMat4c
- * @static
- */
- scalingMat4c: (function () {
- const xyz = new Float32Array(3);
- return function (x, y, z, dest) {
- xyz[0] = x;
- xyz[1] = y;
- xyz[2] = z;
- return math.scalingMat4v(xyz, dest);
- };
- })(),
-
- /**
- * Efficiently post-concatenates a scaling to the given matrix.
- * @method scaleMat4c
- * @param x
- * @param y
- * @param z
- * @param m
- */
- scaleMat4c: function (x, y, z, m) {
-
- m[0] *= x;
- m[4] *= y;
- m[8] *= z;
-
- m[1] *= x;
- m[5] *= y;
- m[9] *= z;
-
- m[2] *= x;
- m[6] *= y;
- m[10] *= z;
-
- m[3] *= x;
- m[7] *= y;
- m[11] *= z;
- return m;
- },
-
- /**
- * Efficiently post-concatenates a scaling to the given matrix.
- * @method scaleMat4c
- * @param xyz
- * @param m
- */
- scaleMat4v: function (xyz, m) {
-
- const x = xyz[0];
- const y = xyz[1];
- const z = xyz[2];
-
- m[0] *= x;
- m[4] *= y;
- m[8] *= z;
- m[1] *= x;
- m[5] *= y;
- m[9] *= z;
- m[2] *= x;
- m[6] *= y;
- m[10] *= z;
- m[3] *= x;
- m[7] *= y;
- m[11] *= z;
-
- return m;
- },
-
- /**
- * Returns 4x4 scale matrix.
- * @method scalingMat4s
- * @static
- */
- scalingMat4s: function (s) {
- return math.scalingMat4c(s, s, s);
- },
-
- /**
- * Creates a matrix from a quaternion rotation and vector translation
- *
- * @param {Float32Array} q Rotation quaternion
- * @param {Float32Array} v Translation vector
- * @param {Float32Array} dest Destination matrix
- * @returns {Float32Array} dest
- */
- rotationTranslationMat4: function (q, v, dest) {
-
- dest = dest || math.mat4();
-
- const x = q[0];
- const y = q[1];
- const z = q[2];
- const w = q[3];
-
- const x2 = x + x;
- const y2 = y + y;
- const z2 = z + z;
- const xx = x * x2;
- const xy = x * y2;
- const xz = x * z2;
- const yy = y * y2;
- const yz = y * z2;
- const zz = z * z2;
- const wx = w * x2;
- const wy = w * y2;
- const wz = w * z2;
-
- dest[0] = 1 - (yy + zz);
- dest[1] = xy + wz;
- dest[2] = xz - wy;
- dest[3] = 0;
- dest[4] = xy - wz;
- dest[5] = 1 - (xx + zz);
- dest[6] = yz + wx;
- dest[7] = 0;
- dest[8] = xz + wy;
- dest[9] = yz - wx;
- dest[10] = 1 - (xx + yy);
- dest[11] = 0;
- dest[12] = v[0];
- dest[13] = v[1];
- dest[14] = v[2];
- dest[15] = 1;
+ sphere[3] = radius;
- return dest;
- },
+ return sphere;
+ };
+ }))(),
+
+ /**
+ * Gets the center of a bounding sphere.
+ *
+ * @private
+ */
+ getSphere3Center(sphere, dest = math.vec3()) {
+ dest[0] = sphere[0];
+ dest[1] = sphere[1];
+ dest[2] = sphere[2];
- /**
- * Gets Euler angles from a 4x4 matrix.
- *
- * @param {Float32Array} mat The 4x4 matrix.
- * @param {String} order Desired Euler angle order: "XYZ", "YXZ", "ZXY" etc.
- * @param {Float32Array} [dest] Destination Euler angles, created by default.
- * @returns {Float32Array} The Euler angles.
- */
- mat4ToEuler: function (mat, order, dest) {
+ return dest;
+ },
- dest = dest || math.vec4();
+ /**
+ * Expands the first axis-aligned 3D boundary to enclose the second, if required.
+ *
+ * @private
+ */
+ expandAABB3(aabb1, aabb2) {
- const clamp = math.clamp;
+ if (aabb1[0] > aabb2[0]) {
+ aabb1[0] = aabb2[0];
+ }
- // Assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+ if (aabb1[1] > aabb2[1]) {
+ aabb1[1] = aabb2[1];
+ }
- const m11 = mat[0], m12 = mat[4], m13 = mat[8];
- const m21 = mat[1], m22 = mat[5], m23 = mat[9];
- const m31 = mat[2], m32 = mat[6], m33 = mat[10];
+ if (aabb1[2] > aabb2[2]) {
+ aabb1[2] = aabb2[2];
+ }
- if (order === 'XYZ') {
+ if (aabb1[3] < aabb2[3]) {
+ aabb1[3] = aabb2[3];
+ }
- dest[1] = Math.asin(clamp(m13, -1, 1));
+ if (aabb1[4] < aabb2[4]) {
+ aabb1[4] = aabb2[4];
+ }
- if (Math.abs(m13) < 0.99999) {
- dest[0] = Math.atan2(-m23, m33);
- dest[2] = Math.atan2(-m12, m11);
- } else {
- dest[0] = Math.atan2(m32, m22);
- dest[2] = 0;
+ if (aabb1[5] < aabb2[5]) {
+ aabb1[5] = aabb2[5];
+ }
- }
+ return aabb1;
+ },
- } else if (order === 'YXZ') {
+ /**
+ * Expands an axis-aligned 3D boundary to enclose the given point, if needed.
+ *
+ * @private
+ */
+ expandAABB3Point3(aabb, p) {
- dest[0] = Math.asin(-clamp(m23, -1, 1));
+ if (aabb[0] < p[0]) {
+ aabb[0] = p[0];
+ }
- if (Math.abs(m23) < 0.99999) {
- dest[1] = Math.atan2(m13, m33);
- dest[2] = Math.atan2(m21, m22);
- } else {
- dest[1] = Math.atan2(-m31, m11);
- dest[2] = 0;
- }
+ if (aabb[1] < p[1]) {
+ aabb[1] = p[1];
+ }
- } else if (order === 'ZXY') {
+ if (aabb[2] < p[2]) {
+ aabb[2] = p[2];
+ }
- dest[0] = Math.asin(clamp(m32, -1, 1));
+ if (aabb[3] > p[0]) {
+ aabb[3] = p[0];
+ }
- if (Math.abs(m32) < 0.99999) {
- dest[1] = Math.atan2(-m31, m33);
- dest[2] = Math.atan2(-m12, m22);
- } else {
- dest[1] = 0;
- dest[2] = Math.atan2(m21, m11);
- }
+ if (aabb[4] > p[1]) {
+ aabb[4] = p[1];
+ }
- } else if (order === 'ZYX') {
+ if (aabb[5] > p[2]) {
+ aabb[5] = p[2];
+ }
- dest[1] = Math.asin(-clamp(m31, -1, 1));
+ return aabb;
+ },
- if (Math.abs(m31) < 0.99999) {
- dest[0] = Math.atan2(m32, m33);
- dest[2] = Math.atan2(m21, m11);
- } else {
- dest[0] = 0;
- dest[2] = Math.atan2(-m12, m22);
- }
+ /**
+ * Collapses a 2D axis-aligned boundary, ready to expand to fit 2D points.
+ * Creates new AABB if none supplied.
+ *
+ * @private
+ */
+ collapseAABB2(aabb = math.AABB2()) {
+ aabb[0] = math.MAX_DOUBLE;
+ aabb[1] = math.MAX_DOUBLE;
+ aabb[2] = -math.MAX_DOUBLE;
+ aabb[3] = -math.MAX_DOUBLE;
- } else if (order === 'YZX') {
+ return aabb;
+ },
- dest[2] = Math.asin(clamp(m21, -1, 1));
+ /**
+ * Finds the minimum 2D projected axis-aligned boundary enclosing the given 3D points.
+ *
+ * @private
+ */
+ OBB3ToAABB2(points, aabb = math.AABB2()) {
+ let xmin = math.MAX_DOUBLE;
+ let ymin = math.MAX_DOUBLE;
+ let xmax = -math.MAX_DOUBLE;
+ let ymax = -math.MAX_DOUBLE;
- if (Math.abs(m21) < 0.99999) {
- dest[0] = Math.atan2(-m23, m22);
- dest[1] = Math.atan2(-m31, m11);
- } else {
- dest[0] = 0;
- dest[1] = Math.atan2(m13, m33);
- }
+ let x;
+ let y;
+ let w;
+ let f;
- } else if (order === 'XZY') {
+ for (let i = 0, len = points.length; i < len; i += 4) {
- dest[2] = Math.asin(-clamp(m12, -1, 1));
+ x = points[i + 0];
+ y = points[i + 1];
+ w = points[i + 3] || 1.0;
- if (Math.abs(m12) < 0.99999) {
- dest[0] = Math.atan2(m32, m22);
- dest[1] = Math.atan2(m13, m11);
- } else {
- dest[0] = Math.atan2(-m23, m33);
- dest[1] = 0;
- }
+ f = 1.0 / w;
+
+ x *= f;
+ y *= f;
+
+ if (x < xmin) {
+ xmin = x;
}
- return dest;
- },
+ if (y < ymin) {
+ ymin = y;
+ }
- composeMat4: function (position, quaternion, scale, mat) {
- mat = mat || math.mat4();
- math.quaternionToRotationMat4(quaternion, mat);
- math.scaleMat4v(scale, mat);
- math.translateMat4v(position, mat);
+ if (x > xmax) {
+ xmax = x;
+ }
- return mat;
- },
+ if (y > ymax) {
+ ymax = y;
+ }
+ }
- decomposeMat4: function () {
+ aabb[0] = xmin;
+ aabb[1] = ymin;
+ aabb[2] = xmax;
+ aabb[3] = ymax;
- const vec = new Float32Array(3);
- const matrix = new Float32Array(16);
+ return aabb;
+ },
+
+ /**
+ * Expands the first axis-aligned 2D boundary to enclose the second, if required.
+ *
+ * @private
+ */
+ expandAABB2(aabb1, aabb2) {
- return function decompose(mat, position, quaternion, scale) {
+ if (aabb1[0] > aabb2[0]) {
+ aabb1[0] = aabb2[0];
+ }
- vec[0] = mat[0];
- vec[1] = mat[1];
- vec[2] = mat[2];
+ if (aabb1[1] > aabb2[1]) {
+ aabb1[1] = aabb2[1];
+ }
- let sx = math.lenVec3(vec);
+ if (aabb1[2] < aabb2[2]) {
+ aabb1[2] = aabb2[2];
+ }
- vec[0] = mat[4];
- vec[1] = mat[5];
- vec[2] = mat[6];
+ if (aabb1[3] < aabb2[3]) {
+ aabb1[3] = aabb2[3];
+ }
- const sy = math.lenVec3(vec);
+ return aabb1;
+ },
- vec[8] = mat[8];
- vec[9] = mat[9];
- vec[10] = mat[10];
+ /**
+ * Expands an axis-aligned 2D boundary to enclose the given point, if required.
+ *
+ * @private
+ */
+ expandAABB2Point2(aabb, p) {
- const sz = math.lenVec3(vec);
+ if (aabb[0] > p[0]) {
+ aabb[0] = p[0];
+ }
- // if determine is negative, we need to invert one scale
- const det = math.determinantMat4(mat);
+ if (aabb[1] > p[1]) {
+ aabb[1] = p[1];
+ }
- if (det < 0) {
- sx = -sx;
- }
+ if (aabb[2] < p[0]) {
+ aabb[2] = p[0];
+ }
- position[0] = mat[12];
- position[1] = mat[13];
- position[2] = mat[14];
+ if (aabb[3] < p[1]) {
+ aabb[3] = p[1];
+ }
- // scale the rotation part
- matrix.set(mat);
+ return aabb;
+ },
+
+ AABB2ToCanvas(aabb, canvasWidth, canvasHeight, aabb2 = aabb) {
+ const xmin = (aabb[0] + 1.0) * 0.5;
+ const ymin = (aabb[1] + 1.0) * 0.5;
+ const xmax = (aabb[2] + 1.0) * 0.5;
+ const ymax = (aabb[3] + 1.0) * 0.5;
+
+ aabb2[0] = Math.floor(xmin * canvasWidth);
+ aabb2[1] = canvasHeight - Math.floor(ymax * canvasHeight);
+ aabb2[2] = Math.floor(xmax * canvasWidth);
+ aabb2[3] = canvasHeight - Math.floor(ymin * canvasHeight);
+
+ return aabb2;
+ },
+
+ //------------------------------------------------------------------------------------------------------------------
+ // Curves
+ //------------------------------------------------------------------------------------------------------------------
+
+ tangentQuadraticBezier(t, p0, p1, p2) {
+ return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 );
+ },
+
+ tangentQuadraticBezier3(t, p0, p1, p2, p3) {
+ return -3 * p0 * (1 - t) * (1 - t) +
+ 3 * p1 * (1 - t) * (1 - t) - 6 * t * p1 * (1 - t) +
+ 6 * t * p2 * (1 - t) - 3 * t * t * p2 +
+ 3 * t * t * p3;
+ },
+
+ tangentSpline(t) {
+ const h00 = 6 * t * t - 6 * t;
+ const h10 = 3 * t * t - 4 * t + 1;
+ const h01 = -6 * t * t + 6 * t;
+ const h11 = 3 * t * t - 2 * t;
+ return h00 + h10 + h01 + h11;
+ },
+
+ catmullRomInterpolate(p0, p1, p2, p3, t) {
+ const v0 = ( p2 - p0 ) * 0.5;
+ const v1 = ( p3 - p1 ) * 0.5;
+ const t2 = t * t;
+ const t3 = t * t2;
+ return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( -3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;
+ },
+
+// Bezier Curve formulii from http://en.wikipedia.org/wiki/B%C3%A9zier_curve
+
+// Quad Bezier Functions
+
+ b2p0(t, p) {
+ const k = 1 - t;
+ return k * k * p;
+
+ },
+
+ b2p1(t, p) {
+ return 2 * ( 1 - t ) * t * p;
+ },
+
+ b2p2(t, p) {
+ return t * t * p;
+ },
+
+ b2(t, p0, p1, p2) {
+ return this.b2p0(t, p0) + this.b2p1(t, p1) + this.b2p2(t, p2);
+ },
+
+// Cubic Bezier Functions
+
+ b3p0(t, p) {
+ const k = 1 - t;
+ return k * k * k * p;
+ },
+
+ b3p1(t, p) {
+ const k = 1 - t;
+ return 3 * k * k * t * p;
+ },
+
+ b3p2(t, p) {
+ const k = 1 - t;
+ return 3 * k * t * t * p;
+ },
+
+ b3p3(t, p) {
+ return t * t * t * p;
+ },
+
+ b3(t, p0, p1, p2, p3) {
+ return this.b3p0(t, p0) + this.b3p1(t, p1) + this.b3p2(t, p2) + this.b3p3(t, p3);
+ },
+
+ //------------------------------------------------------------------------------------------------------------------
+ // Geometry
+ //------------------------------------------------------------------------------------------------------------------
- const invSX = 1 / sx;
- const invSY = 1 / sy;
- const invSZ = 1 / sz;
+ /**
+ * Calculates the normal vector of a triangle.
+ *
+ * @private
+ */
+ triangleNormal(a, b, c, normal = math.vec3()) {
+ const p1x = b[0] - a[0];
+ const p1y = b[1] - a[1];
+ const p1z = b[2] - a[2];
+
+ const p2x = c[0] - a[0];
+ const p2y = c[1] - a[1];
+ const p2z = c[2] - a[2];
+
+ const p3x = p1y * p2z - p1z * p2y;
+ const p3y = p1z * p2x - p1x * p2z;
+ const p3z = p1x * p2y - p1y * p2x;
+
+ const mag = Math.sqrt(p3x * p3x + p3y * p3y + p3z * p3z);
+ if (mag === 0) {
+ normal[0] = 0;
+ normal[1] = 0;
+ normal[2] = 0;
+ } else {
+ normal[0] = p3x / mag;
+ normal[1] = p3y / mag;
+ normal[2] = p3z / mag;
+ }
- matrix[0] *= invSX;
- matrix[1] *= invSX;
- matrix[2] *= invSX;
+ return normal
+ },
- matrix[4] *= invSY;
- matrix[5] *= invSY;
- matrix[6] *= invSY;
+ /**
+ * Finds the intersection of a 3D ray with a 3D triangle.
+ *
+ * @private
+ */
+ rayTriangleIntersect: ((() => {
- matrix[8] *= invSZ;
- matrix[9] *= invSZ;
- matrix[10] *= invSZ;
+ const tempVec3 = new Float32Array(3);
+ const tempVec3b = new Float32Array(3);
+ const tempVec3c = new Float32Array(3);
+ const tempVec3d = new Float32Array(3);
+ const tempVec3e = new Float32Array(3);
- math.mat4ToQuaternion(matrix, quaternion);
+ return (origin, dir, a, b, c, isect) => {
- scale[0] = sx;
- scale[1] = sy;
- scale[2] = sz;
+ isect = isect || math.vec3();
- return this;
+ const EPSILON = 0.000001;
- };
+ const edge1 = math.subVec3(b, a, tempVec3);
+ const edge2 = math.subVec3(c, a, tempVec3b);
- }(),
-
- /**
- * Returns a 4x4 'lookat' viewing transform matrix.
- * @method lookAtMat4v
- * @param pos vec3 position of the viewer
- * @param target vec3 point the viewer is looking at
- * @param up vec3 pointing "up"
- * @param dest mat4 Optional, mat4 matrix will be written into
- *
- * @return {mat4} dest if specified, a new mat4 otherwise
- */
- lookAtMat4v: function (pos, target, up, dest) {
- if (!dest) {
- dest = math.mat4();
+ const pvec = math.cross3Vec3(dir, edge2, tempVec3c);
+ const det = math.dotVec3(edge1, pvec);
+ if (det < EPSILON) {
+ return null;
}
- const posx = pos[0], posy = pos[1], posz = pos[2], upx = up[0], upy = up[1], upz = up[2], targetx = target[0], targety = target[1], targetz = target[2];
-
- if (posx === targetx && posy === targety && posz === targetz) {
- return math.identityMat4();
+ const tvec = math.subVec3(origin, a, tempVec3d);
+ const u = math.dotVec3(tvec, pvec);
+ if (u < 0 || u > det) {
+ return null;
}
- let z0, z1, z2, x0, x1, x2, y0, y1, y2, len;
-
- //vec3.direction(eye, center, z);
- z0 = posx - targetx;
- z1 = posy - targety;
- z2 = posz - targetz;
-
- // normalize (no check needed for 0 because of early return)
- len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2);
- z0 *= len;
- z1 *= len;
- z2 *= len;
-
- //vec3.normalize(vec3.cross(up, z, x));
- x0 = upy * z2 - upz * z1;
- x1 = upz * z0 - upx * z2;
- x2 = upx * z1 - upy * z0;
- len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2);
- if (!len) {
- x0 = 0;
- x1 = 0;
- x2 = 0;
- } else {
- len = 1 / len;
- x0 *= len;
- x1 *= len;
- x2 *= len;
+ const qvec = math.cross3Vec3(tvec, edge1, tempVec3e);
+ const v = math.dotVec3(dir, qvec);
+ if (v < 0 || u + v > det) {
+ return null;
}
- //vec3.normalize(vec3.cross(z, x, y));
- y0 = z1 * x2 - z2 * x1;
- y1 = z2 * x0 - z0 * x2;
- y2 = z0 * x1 - z1 * x0;
+ const t = math.dotVec3(edge2, qvec) / det;
+ isect[0] = origin[0] + t * dir[0];
+ isect[1] = origin[1] + t * dir[1];
+ isect[2] = origin[2] + t * dir[2];
- len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2);
- if (!len) {
- y0 = 0;
- y1 = 0;
- y2 = 0;
- } else {
- len = 1 / len;
- y0 *= len;
- y1 *= len;
- y2 *= len;
- }
+ return isect;
+ };
+ }))(),
- dest[0] = x0;
- dest[1] = y0;
- dest[2] = z0;
- dest[3] = 0;
- dest[4] = x1;
- dest[5] = y1;
- dest[6] = z1;
- dest[7] = 0;
- dest[8] = x2;
- dest[9] = y2;
- dest[10] = z2;
- dest[11] = 0;
- dest[12] = -(x0 * posx + x1 * posy + x2 * posz);
- dest[13] = -(y0 * posx + y1 * posy + y2 * posz);
- dest[14] = -(z0 * posx + z1 * posy + z2 * posz);
- dest[15] = 1;
+ /**
+ * Finds the intersection of a 3D ray with a plane defined by 3 points.
+ *
+ * @private
+ */
+ rayPlaneIntersect: ((() => {
- return dest;
- },
-
- /**
- * Returns a 4x4 'lookat' viewing transform matrix.
- * @method lookAtMat4c
- * @static
- */
- lookAtMat4c: function (posx, posy, posz, targetx, targety, targetz, upx, upy, upz) {
- return math.lookAtMat4v([posx, posy, posz], [targetx, targety, targetz], [upx, upy, upz], []);
- },
-
- /**
- * Returns a 4x4 orthographic projection matrix.
- * @method orthoMat4c
- * @static
- */
- orthoMat4c: function (left, right, bottom, top, near, far, dest) {
- if (!dest) {
- dest = math.mat4();
- }
- const rl = (right - left);
- const tb = (top - bottom);
- const fn = (far - near);
-
- dest[0] = 2.0 / rl;
- dest[1] = 0.0;
- dest[2] = 0.0;
- dest[3] = 0.0;
-
- dest[4] = 0.0;
- dest[5] = 2.0 / tb;
- dest[6] = 0.0;
- dest[7] = 0.0;
-
- dest[8] = 0.0;
- dest[9] = 0.0;
- dest[10] = -2.0 / fn;
- dest[11] = 0.0;
-
- dest[12] = -(left + right) / rl;
- dest[13] = -(top + bottom) / tb;
- dest[14] = -(far + near) / fn;
- dest[15] = 1.0;
+ const tempVec3 = new Float32Array(3);
+ const tempVec3b = new Float32Array(3);
+ const tempVec3c = new Float32Array(3);
+ const tempVec3d = new Float32Array(3);
- return dest;
- },
+ return (origin, dir, a, b, c, isect) => {
- /**
- * Returns a 4x4 perspective projection matrix.
- * @method frustumMat4v
- * @static
- */
- frustumMat4v: function (fmin, fmax, m) {
+ isect = isect || math.vec3();
- if (!m) {
- m = math.mat4();
- }
+ dir = math.normalizeVec3(dir, tempVec3);
- const fmin4 = [fmin[0], fmin[1], fmin[2], 0.0];
- const fmax4 = [fmax[0], fmax[1], fmax[2], 0.0];
+ const edge1 = math.subVec3(b, a, tempVec3b);
+ const edge2 = math.subVec3(c, a, tempVec3c);
- math.addVec4(fmax4, fmin4, tempMat1);
- math.subVec4(fmax4, fmin4, tempMat2);
+ const n = math.cross3Vec3(edge1, edge2, tempVec3d);
+ math.normalizeVec3(n, n);
- const t = 2.0 * fmin4[2];
+ const d = -math.dotVec3(a, n);
- const tempMat20 = tempMat2[0], tempMat21 = tempMat2[1], tempMat22 = tempMat2[2];
+ const t = -(math.dotVec3(origin, n) + d) / math.dotVec3(dir, n);
- m[0] = t / tempMat20;
- m[1] = 0.0;
- m[2] = 0.0;
- m[3] = 0.0;
+ isect[0] = origin[0] + t * dir[0];
+ isect[1] = origin[1] + t * dir[1];
+ isect[2] = origin[2] + t * dir[2];
- m[4] = 0.0;
- m[5] = t / tempMat21;
- m[6] = 0.0;
- m[7] = 0.0;
+ return isect;
+ };
+ }))(),
- m[8] = tempMat1[0] / tempMat20;
- m[9] = tempMat1[1] / tempMat21;
- m[10] = -tempMat1[2] / tempMat22;
- m[11] = -1.0;
+ /**
+ * Gets barycentric coordinates from cartesian coordinates within a triangle.
+ * Gets barycentric coordinates from cartesian coordinates within a triangle.
+ *
+ * @private
+ */
+ cartesianToBarycentric: ((() => {
- m[12] = 0.0;
- m[13] = 0.0;
- m[14] = -t * fmax4[2] / tempMat22;
- m[15] = 0.0;
+ const tempVec3 = new Float32Array(3);
+ const tempVec3b = new Float32Array(3);
+ const tempVec3c = new Float32Array(3);
- return m;
- },
+ return (cartesian, a, b, c, dest) => {
- /**
- * Returns a 4x4 perspective projection matrix.
- * @method frustumMat4v
- * @static
- */
- frustumMat4: function (left, right, bottom, top, near, far, dest) {
- if (!dest) {
- dest = math.mat4();
- }
- const rl = (right - left);
- const tb = (top - bottom);
- const fn = (far - near);
- dest[0] = (near * 2) / rl;
- dest[1] = 0;
- dest[2] = 0;
- dest[3] = 0;
- dest[4] = 0;
- dest[5] = (near * 2) / tb;
- dest[6] = 0;
- dest[7] = 0;
- dest[8] = (right + left) / rl;
- dest[9] = (top + bottom) / tb;
- dest[10] = -(far + near) / fn;
- dest[11] = -1;
- dest[12] = 0;
- dest[13] = 0;
- dest[14] = -(far * near * 2) / fn;
- dest[15] = 0;
- return dest;
- },
+ const v0 = math.subVec3(c, a, tempVec3);
+ const v1 = math.subVec3(b, a, tempVec3b);
+ const v2 = math.subVec3(cartesian, a, tempVec3c);
- /**
- * Returns a 4x4 perspective projection matrix.
- * @method perspectiveMat4v
- * @static
- */
- perspectiveMat4: function (fovyrad, aspectratio, znear, zfar, m) {
- const pmin = [];
- const pmax = [];
+ const dot00 = math.dotVec3(v0, v0);
+ const dot01 = math.dotVec3(v0, v1);
+ const dot02 = math.dotVec3(v0, v2);
+ const dot11 = math.dotVec3(v1, v1);
+ const dot12 = math.dotVec3(v1, v2);
- pmin[2] = znear;
- pmax[2] = zfar;
+ const denom = ( dot00 * dot11 - dot01 * dot01 );
- pmax[1] = pmin[2] * Math.tan(fovyrad / 2.0);
- pmin[1] = -pmax[1];
+ // Colinear or singular triangle
- pmax[0] = pmax[1] * aspectratio;
- pmin[0] = -pmax[0];
+ if (denom === 0) {
- return math.frustumMat4v(pmin, pmax, m);
- },
+ // Arbitrary location outside of triangle
- /**
- * Transforms a three-element position by a 4x4 matrix.
- * @method transformPoint3
- * @static
- */
- transformPoint3: function (m, p, dest) {
+ return null;
+ }
- dest = dest || math.vec3();
+ const invDenom = 1 / denom;
- dest[0] = (m[0] * p[0]) + (m[4] * p[1]) + (m[8] * p[2]) + m[12];
- dest[1] = (m[1] * p[0]) + (m[5] * p[1]) + (m[9] * p[2]) + m[13];
- dest[2] = (m[2] * p[0]) + (m[6] * p[1]) + (m[10] * p[2]) + m[14];
+ const u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;
+ const v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;
+
+ dest[0] = 1 - u - v;
+ dest[1] = v;
+ dest[2] = u;
return dest;
- },
+ };
+ }))(),
- /**
- * Transforms a homogeneous coordinate by a 4x4 matrix.
- * @method transformPoint3
- * @static
- */
- transformPoint4: function (m, v, dest) {
+ /**
+ * Returns true if the given barycentric coordinates are within their triangle.
+ *
+ * @private
+ */
+ barycentricInsideTriangle(bary) {
- dest = dest || math.vec4();
+ const v = bary[1];
+ const u = bary[2];
- dest[0] = m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12] * v[3];
- dest[1] = m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13] * v[3];
- dest[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * v[3];
- dest[3] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3];
+ return (u >= 0) && (v >= 0) && (u + v < 1);
+ },
- return dest;
- },
+ /**
+ * Gets cartesian coordinates from barycentric coordinates within a triangle.
+ *
+ * @private
+ */
+ barycentricToCartesian(bary, a, b, c, cartesian = math.vec3()) {
+ const u = bary[0];
+ const v = bary[1];
+ const w = bary[2];
+ cartesian[0] = a[0] * u + b[0] * v + c[0] * w;
+ cartesian[1] = a[1] * u + b[1] * v + c[1] * w;
+ cartesian[2] = a[2] * u + b[2] * v + c[2] * w;
- /**
- * Transforms an array of three-element positions by a 4x4 matrix.
- * @method transformPoints3
- * @static
- */
- transformPoints3: function (m, points, points2) {
- const result = points2 || [];
- const len = points.length;
- let p0, p1, p2;
- let pi;
+ return cartesian;
+ },
- // cache values
- const m0 = m[0], m1 = m[1], m2 = m[2], m3 = m[3];
- const m4 = m[4], m5 = m[5], m6 = m[6], m7 = m[7];
- const m8 = m[8], m9 = m[9], m10 = m[10], m11 = m[11];
- const m12 = m[12], m13 = m[13], m14 = m[14], m15 = m[15];
+ /**
+ * Given geometry defined as an array of positions, optional normals, option uv and an array of indices, returns
+ * modified arrays that have duplicate vertices removed.
+ *
+ * Note: does not work well when co-incident vertices have same positions but different normals and UVs.
+ *
+ * @param positions
+ * @param normals
+ * @param uv
+ * @param indices
+ * @returns {{positions: Array, indices: Array}}
+ * @private
+ */
+ mergeVertices(positions, normals, uv, indices) {
+ const positionsMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique)
+ const indicesLookup = [];
+ const uniquePositions = [];
+ const uniqueNormals = normals ? [] : null;
+ const uniqueUV = uv ? [] : null;
+ const indices2 = [];
+ let vx;
+ let vy;
+ let vz;
+ let key;
+ const precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001
+ const precision = 10 ** precisionPoints;
+ let i;
+ let len;
+ let uvi = 0;
+ for (i = 0, len = positions.length; i < len; i += 3) {
+ vx = positions[i];
+ vy = positions[i + 1];
+ vz = positions[i + 2];
+ key = `${Math.round(vx * precision)}_${Math.round(vy * precision)}_${Math.round(vz * precision)}`;
+ if (positionsMap[key] === undefined) {
+ positionsMap[key] = uniquePositions.length / 3;
+ uniquePositions.push(vx);
+ uniquePositions.push(vy);
+ uniquePositions.push(vz);
+ if (normals) {
+ uniqueNormals.push(normals[i]);
+ uniqueNormals.push(normals[i + 1]);
+ uniqueNormals.push(normals[i + 2]);
+ }
+ if (uv) {
+ uniqueUV.push(uv[uvi]);
+ uniqueUV.push(uv[uvi + 1]);
+ }
+ }
+ indicesLookup[i / 3] = positionsMap[key];
+ uvi += 2;
+ }
+ for (i = 0, len = indices.length; i < len; i++) {
+ indices2[i] = indicesLookup[indices[i]];
+ }
+ const result = {
+ positions: uniquePositions,
+ indices: indices2
+ };
+ if (uniqueNormals) {
+ result.normals = uniqueNormals;
+ }
+ if (uniqueUV) {
+ result.uv = uniqueUV;
+
+ }
+ return result;
+ },
- let r;
+ /**
+ * Builds normal vectors from positions and indices.
+ *
+ * @private
+ */
+ buildNormals: ((() => {
- for (let i = 0; i < len; ++i) {
+ const a = new Float32Array(3);
+ const b = new Float32Array(3);
+ const c = new Float32Array(3);
+ const ab = new Float32Array(3);
+ const ac = new Float32Array(3);
+ const crossVec = new Float32Array(3);
- // cache values
- pi = points[i];
+ return (positions, indices, normals) => {
- p0 = pi[0];
- p1 = pi[1];
- p2 = pi[2];
+ let i;
+ let len;
+ const nvecs = new Array(positions.length / 3);
+ let j0;
+ let j1;
+ let j2;
- r = result[i] || (result[i] = [0, 0, 0]);
+ for (i = 0, len = indices.length; i < len; i += 3) {
- r[0] = (m0 * p0) + (m4 * p1) + (m8 * p2) + m12;
- r[1] = (m1 * p0) + (m5 * p1) + (m9 * p2) + m13;
- r[2] = (m2 * p0) + (m6 * p1) + (m10 * p2) + m14;
- r[3] = (m3 * p0) + (m7 * p1) + (m11 * p2) + m15;
- }
+ j0 = indices[i];
+ j1 = indices[i + 1];
+ j2 = indices[i + 2];
- result.length = len;
+ a[0] = positions[j0 * 3];
+ a[1] = positions[j0 * 3 + 1];
+ a[2] = positions[j0 * 3 + 2];
- return result;
- },
+ b[0] = positions[j1 * 3];
+ b[1] = positions[j1 * 3 + 1];
+ b[2] = positions[j1 * 3 + 2];
- /**
- * Transforms an array of positions by a 4x4 matrix.
- * @method transformPositions3
- * @static
- */
- transformPositions3: function (m, p, p2) {
+ c[0] = positions[j2 * 3];
+ c[1] = positions[j2 * 3 + 1];
+ c[2] = positions[j2 * 3 + 2];
- p2 = p2 || p;
+ math.subVec3(b, a, ab);
+ math.subVec3(c, a, ac);
- let i;
- const len = p.length;
+ const normVec = new Float32Array(3);
+
+ math.normalizeVec3(math.cross3Vec3(ab, ac, crossVec), normVec);
+
+ if (!nvecs[j0]) {
+ nvecs[j0] = [];
+ }
+ if (!nvecs[j1]) {
+ nvecs[j1] = [];
+ }
+ if (!nvecs[j2]) {
+ nvecs[j2] = [];
+ }
+
+ nvecs[j0].push(normVec);
+ nvecs[j1].push(normVec);
+ nvecs[j2].push(normVec);
+ }
+
+ normals = (normals && normals.length === positions.length) ? normals : new Float32Array(positions.length);
+ let count;
let x;
let y;
let z;
- const m0 = m[0];
- const m1 = m[1];
- const m2 = m[2];
- const m3 = m[3];
- const m4 = m[4];
- const m5 = m[5];
- const m6 = m[6];
- const m7 = m[7];
- const m8 = m[8];
- const m9 = m[9];
- const m10 = m[10];
- const m11 = m[11];
- const m12 = m[12];
- const m13 = m[13];
- const m14 = m[14];
- const m15 = m[15];
-
- for (i = 0; i < len; i += 3) {
-
- x = p[i + 0];
- y = p[i + 1];
- z = p[i + 2];
-
- p2[i + 0] = (m0 * x) + (m4 * y) + (m8 * z) + m12;
- p2[i + 1] = (m1 * x) + (m5 * y) + (m9 * z) + m13;
- p2[i + 2] = (m2 * x) + (m6 * y) + (m10 * z) + m14;
- p2[i + 3] = (m3 * x) + (m7 * y) + (m11 * z) + m15;
+ for (i = 0, len = nvecs.length; i < len; i++) { // Now go through and average out everything
+
+ count = nvecs[i].length;
+
+ x = 0;
+ y = 0;
+ z = 0;
+
+ for (let j = 0; j < count; j++) {
+ x += nvecs[i][j][0];
+ y += nvecs[i][j][1];
+ z += nvecs[i][j][2];
+ }
+
+ normals[i * 3] = (x / count);
+ normals[i * 3 + 1] = (y / count);
+ normals[i * 3 + 2] = (z / count);
}
- return p2;
- },
+ return normals;
+ };
+ }))(),
- /**
- * Transforms an array of positions by a 4x4 matrix.
- * @method transformPositions4
- * @static
- */
- transformPositions4: function (m, p, p2) {
+ /**
+ * Builds vertex tangent vectors from positions, UVs and indices.
+ *
+ * @private
+ */
+ buildTangents: ((() => {
- p2 = p2 || p;
+ const tempVec3 = new Float32Array(3);
+ const tempVec3b = new Float32Array(3);
+ const tempVec3c = new Float32Array(3);
+ const tempVec3d = new Float32Array(3);
+ const tempVec3e = new Float32Array(3);
+ const tempVec3f = new Float32Array(3);
+ const tempVec3g = new Float32Array(3);
- let i;
- const len = p.length;
+ return (positions, indices, uv) => {
- let x;
- let y;
- let z;
+ const tangents = new Float32Array(positions.length);
- const m0 = m[0];
- const m1 = m[1];
- const m2 = m[2];
- const m3 = m[3];
- const m4 = m[4];
- const m5 = m[5];
- const m6 = m[6];
- const m7 = m[7];
- const m8 = m[8];
- const m9 = m[9];
- const m10 = m[10];
- const m11 = m[11];
- const m12 = m[12];
- const m13 = m[13];
- const m14 = m[14];
- const m15 = m[15];
-
- for (i = 0; i < len; i += 4) {
-
- x = p[i + 0];
- y = p[i + 1];
- z = p[i + 2];
-
- p2[i + 0] = (m0 * x) + (m4 * y) + (m8 * z) + m12;
- p2[i + 1] = (m1 * x) + (m5 * y) + (m9 * z) + m13;
- p2[i + 2] = (m2 * x) + (m6 * y) + (m10 * z) + m14;
- p2[i + 3] = (m3 * x) + (m7 * y) + (m11 * z) + m15;
- }
+ // The vertex arrays needs to be calculated
+ // before the calculation of the tangents
- return p2;
- },
-
- /**
- * Transforms a three-element vector by a 4x4 matrix.
- * @method transformVec3
- * @static
- */
- transformVec3: function (m, v, dest) {
- const v0 = v[0], v1 = v[1], v2 = v[2];
- dest = dest || this.vec3();
- dest[0] = (m[0] * v0) + (m[4] * v1) + (m[8] * v2);
- dest[1] = (m[1] * v0) + (m[5] * v1) + (m[9] * v2);
- dest[2] = (m[2] * v0) + (m[6] * v1) + (m[10] * v2);
- return dest;
- },
-
- /**
- * Transforms a four-element vector by a 4x4 matrix.
- * @method transformVec4
- * @static
- */
- transformVec4: function (m, v, dest) {
- const v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
- dest = dest || math.vec4();
- dest[0] = m[0] * v0 + m[4] * v1 + m[8] * v2 + m[12] * v3;
- dest[1] = m[1] * v0 + m[5] * v1 + m[9] * v2 + m[13] * v3;
- dest[2] = m[2] * v0 + m[6] * v1 + m[10] * v2 + m[14] * v3;
- dest[3] = m[3] * v0 + m[7] * v1 + m[11] * v2 + m[15] * v3;
- return dest;
- },
-
- /**
- * Rotate a 3D vector around the x-axis
- *
- * @method rotateVec3X
- * @param {Float32Array} a The vec3 point to rotate
- * @param {Float32Array} b The origin of the rotation
- * @param {Number} c The angle of rotation
- * @param {Float32Array} dest The receiving vec3
- * @returns {Float32Array} dest
- * @static
- */
- rotateVec3X: function (a, b, c, dest) {
-
- const p = [], r = [];
-
- //Translate point to the origin
- p[0] = a[0] - b[0];
- p[1] = a[1] - b[1];
- p[2] = a[2] - b[2];
-
- //perform rotation
- r[0] = p[0];
- r[1] = p[1] * Math.cos(c) - p[2] * Math.sin(c);
- r[2] = p[1] * Math.sin(c) + p[2] * Math.cos(c);
-
- //translate to correct position
- dest[0] = r[0] + b[0];
- dest[1] = r[1] + b[1];
- dest[2] = r[2] + b[2];
+ for (let location = 0; location < indices.length; location += 3) {
- return dest;
- },
-
- /**
- * Rotate a 3D vector around the y-axis
- *
- * @method rotateVec3Y
- * @param {Float32Array} a The vec3 point to rotate
- * @param {Float32Array} b The origin of the rotation
- * @param {Number} c The angle of rotation
- * @param {Float32Array} dest The receiving vec3
- * @returns {Float32Array} dest
- * @static
- */
- rotateVec3Y: function (a, b, c, dest) {
-
- const p = [], r = [];
-
- //Translate point to the origin
- p[0] = a[0] - b[0];
- p[1] = a[1] - b[1];
- p[2] = a[2] - b[2];
-
- //perform rotation
- r[0] = p[2] * Math.sin(c) + p[0] * Math.cos(c);
- r[1] = p[1];
- r[2] = p[2] * Math.cos(c) - p[0] * Math.sin(c);
-
- //translate to correct position
- dest[0] = r[0] + b[0];
- dest[1] = r[1] + b[1];
- dest[2] = r[2] + b[2];
+ // Recontructing each vertex and UV coordinate into the respective vectors
- return dest;
- },
-
- /**
- * Rotate a 3D vector around the z-axis
- *
- * @method rotateVec3Z
- * @param {Float32Array} a The vec3 point to rotate
- * @param {Float32Array} b The origin of the rotation
- * @param {Number} c The angle of rotation
- * @param {Float32Array} dest The receiving vec3
- * @returns {Float32Array} dest
- * @static
- */
- rotateVec3Z: function (a, b, c, dest) {
-
- const p = [], r = [];
-
- //Translate point to the origin
- p[0] = a[0] - b[0];
- p[1] = a[1] - b[1];
- p[2] = a[2] - b[2];
-
- //perform rotation
- r[0] = p[0] * Math.cos(c) - p[1] * Math.sin(c);
- r[1] = p[0] * Math.sin(c) + p[1] * Math.cos(c);
- r[2] = p[2];
-
- //translate to correct position
- dest[0] = r[0] + b[0];
- dest[1] = r[1] + b[1];
- dest[2] = r[2] + b[2];
+ let index = indices[location];
- return dest;
- },
-
- /**
- * Transforms a four-element vector by a 4x4 projection matrix.
- *
- * @method projectVec4
- * @param {Float32Array} p 3D View-space coordinate
- * @param {Float32Array} q 2D Projected coordinate
- * @returns {Float32Array} 2D Projected coordinate
- * @static
- */
- projectVec4: function (p, q) {
- const f = 1.0 / p[3];
- q = q || math.vec2();
- q[0] = v[0] * f;
- q[1] = v[1] * f;
- return q;
- },
-
- /**
- * Unprojects a three-element vector.
- *
- * @method unprojectVec3
- * @param {Float32Array} p 3D Projected coordinate
- * @param {Float32Array} viewMat View matrix
- * @returns {Float32Array} projMat Projection matrix
- * @static
- */
- unprojectVec3: (function () {
- const mat = new Float32Array(16);
- const mat2 = new Float32Array(16);
- const mat3 = new Float32Array(16);
- return function (p, viewMat, projMat, q) {
- return this.transformVec3(this.mulMat4(this.inverseMat4(viewMat, mat), this.inverseMat4(projMat, mat2), mat3), p, q)
- };
- })(),
-
- /**
- * Linearly interpolates between two 3D vectors.
- * @method lerpVec3
- * @static
- */
- lerpVec3: function (t, t1, t2, p1, p2, dest) {
- const result = dest || math.vec3();
- const f = (t - t1) / (t2 - t1);
- result[0] = p1[0] + (f * (p2[0] - p1[0]));
- result[1] = p1[1] + (f * (p2[1] - p1[1]));
- result[2] = p1[2] + (f * (p2[2] - p1[2]));
- return result;
- },
-
-
- /**
- * Flattens a two-dimensional array into a one-dimensional array.
- *
- * @method flatten
- * @static
- * @param {Array of Arrays} a A 2D array
- * @returns Flattened 1D array
- */
- flatten: function (a) {
-
- const result = [];
+ const v0 = positions.subarray(index * 3, index * 3 + 3);
+ const uv0 = uv.subarray(index * 2, index * 2 + 2);
- let i;
- let leni;
- let j;
- let lenj;
- let item;
-
- for (i = 0, leni = a.length; i < leni; i++) {
- item = a[i];
- for (j = 0, lenj = item.length; j < lenj; j++) {
- result.push(item[j]);
+ index = indices[location + 1];
+
+ const v1 = positions.subarray(index * 3, index * 3 + 3);
+ const uv1 = uv.subarray(index * 2, index * 2 + 2);
+
+ index = indices[location + 2];
+
+ const v2 = positions.subarray(index * 3, index * 3 + 3);
+ const uv2 = uv.subarray(index * 2, index * 2 + 2);
+
+ const deltaPos1 = math.subVec3(v1, v0, tempVec3);
+ const deltaPos2 = math.subVec3(v2, v0, tempVec3b);
+
+ const deltaUV1 = math.subVec2(uv1, uv0, tempVec3c);
+ const deltaUV2 = math.subVec2(uv2, uv0, tempVec3d);
+
+ const r = 1 / ((deltaUV1[0] * deltaUV2[1]) - (deltaUV1[1] * deltaUV2[0]));
+
+ const tangent = math.mulVec3Scalar(
+ math.subVec3(
+ math.mulVec3Scalar(deltaPos1, deltaUV2[1], tempVec3e),
+ math.mulVec3Scalar(deltaPos2, deltaUV1[1], tempVec3f),
+ tempVec3g
+ ),
+ r,
+ tempVec3f
+ );
+
+ // Average the value of the vectors
+
+ let addTo;
+
+ for (let v = 0; v < 3; v++) {
+ addTo = indices[location + v] * 3;
+ tangents[addTo] += tangent[0];
+ tangents[addTo + 1] += tangent[1];
+ tangents[addTo + 2] += tangent[2];
}
}
- return result;
- },
+ return tangents;
+ };
+ }))(),
+ /**
+ * Builds vertex and index arrays needed by color-indexed triangle picking.
+ *
+ * @private
+ */
+ buildPickTriangles(positions, indices, quantized) {
- identityQuaternion: function (dest) {
- dest = dest || math.vec4();
- dest[0] = 0.0;
- dest[1] = 0.0;
- dest[2] = 0.0;
- dest[3] = 1.0;
- return dest;
- },
+ const numIndices = indices.length;
+ const pickPositions = quantized ? new Uint16Array(numIndices * 9) : new Float32Array(numIndices * 9);
+ const pickColors = new Uint8Array(numIndices * 12);
+ let primIndex = 0;
+ let vi;// Positions array index
+ let pvi = 0;// Picking positions array index
+ let pci = 0; // Picking color array index
- /**
- * Initializes a quaternion from Euler angles.
- *
- * @param {Float32Array} euler The Euler angles.
- * @param {String} order Euler angle order: "XYZ", "YXZ", "ZXY" etc.
- * @param {Float32Array} [dest] Destination quaternion, created by default.
- * @returns {Float32Array} The quaternion.
- */
- eulerToQuaternion: function (euler, order, dest) {
+ // Triangle indices
+ let i;
+ let r;
+ let g;
+ let b;
+ let a;
- dest = dest || math.vec4();
+ for (let location = 0; location < numIndices; location += 3) {
- // http://www.mathworks.com/matlabcentral/fileexchange/
- // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
- // content/SpinCalc.m
+ // Primitive-indexed triangle pick color
- const a = (euler[0] * math.DEGTORAD) / 2;
- const b = (euler[1] * math.DEGTORAD) / 2;
- const c = (euler[2] * math.DEGTORAD) / 2;
+ a = (primIndex >> 24 & 0xFF);
+ b = (primIndex >> 16 & 0xFF);
+ g = (primIndex >> 8 & 0xFF);
+ r = (primIndex & 0xFF);
- const c1 = Math.cos(a);
- const c2 = Math.cos(b);
- const c3 = Math.cos(c);
- const s1 = Math.sin(a);
- const s2 = Math.sin(b);
- const s3 = Math.sin(c);
+ // A
- if (order === 'XYZ') {
+ i = indices[location];
+ vi = i * 3;
- dest[0] = s1 * c2 * c3 + c1 * s2 * s3;
- dest[1] = c1 * s2 * c3 - s1 * c2 * s3;
- dest[2] = c1 * c2 * s3 + s1 * s2 * c3;
- dest[3] = c1 * c2 * c3 - s1 * s2 * s3;
+ pickPositions[pvi++] = positions[vi];
+ pickPositions[pvi++] = positions[vi + 1];
+ pickPositions[pvi++] = positions[vi + 2];
- } else if (order === 'YXZ') {
+ pickColors[pci++] = r;
+ pickColors[pci++] = g;
+ pickColors[pci++] = b;
+ pickColors[pci++] = a;
- dest[0] = s1 * c2 * c3 + c1 * s2 * s3;
- dest[1] = c1 * s2 * c3 - s1 * c2 * s3;
- dest[2] = c1 * c2 * s3 - s1 * s2 * c3;
- dest[3] = c1 * c2 * c3 + s1 * s2 * s3;
+ // B
- } else if (order === 'ZXY') {
+ i = indices[location + 1];
+ vi = i * 3;
- dest[0] = s1 * c2 * c3 - c1 * s2 * s3;
- dest[1] = c1 * s2 * c3 + s1 * c2 * s3;
- dest[2] = c1 * c2 * s3 + s1 * s2 * c3;
- dest[3] = c1 * c2 * c3 - s1 * s2 * s3;
+ pickPositions[pvi++] = positions[vi];
+ pickPositions[pvi++] = positions[vi + 1];
+ pickPositions[pvi++] = positions[vi + 2];
- } else if (order === 'ZYX') {
+ pickColors[pci++] = r;
+ pickColors[pci++] = g;
+ pickColors[pci++] = b;
+ pickColors[pci++] = a;
- dest[0] = s1 * c2 * c3 - c1 * s2 * s3;
- dest[1] = c1 * s2 * c3 + s1 * c2 * s3;
- dest[2] = c1 * c2 * s3 - s1 * s2 * c3;
- dest[3] = c1 * c2 * c3 + s1 * s2 * s3;
+ // C
- } else if (order === 'YZX') {
+ i = indices[location + 2];
+ vi = i * 3;
- dest[0] = s1 * c2 * c3 + c1 * s2 * s3;
- dest[1] = c1 * s2 * c3 + s1 * c2 * s3;
- dest[2] = c1 * c2 * s3 - s1 * s2 * c3;
- dest[3] = c1 * c2 * c3 - s1 * s2 * s3;
+ pickPositions[pvi++] = positions[vi];
+ pickPositions[pvi++] = positions[vi + 1];
+ pickPositions[pvi++] = positions[vi + 2];
- } else if (order === 'XZY') {
+ pickColors[pci++] = r;
+ pickColors[pci++] = g;
+ pickColors[pci++] = b;
+ pickColors[pci++] = a;
- dest[0] = s1 * c2 * c3 - c1 * s2 * s3;
- dest[1] = c1 * s2 * c3 - s1 * c2 * s3;
- dest[2] = c1 * c2 * s3 + s1 * s2 * c3;
- dest[3] = c1 * c2 * c3 + s1 * s2 * s3;
- }
+ primIndex++;
+ }
- return dest;
- },
+ return {
+ positions: pickPositions,
+ colors: pickColors
+ };
+ },
- mat4ToQuaternion: function (m, dest) {
+ /**
+ * Converts surface-perpendicular face normals to vertex normals. Assumes that the mesh contains disjoint triangles
+ * that don't share vertex array elements. Works by finding groups of vertices that have the same location and
+ * averaging their normal vectors.
+ *
+ * @returns {{positions: Array, normals: *}}
+ */
+ faceToVertexNormals(positions, normals, options = {}) {
+ const smoothNormalsAngleThreshold = options.smoothNormalsAngleThreshold || 20;
+ const vertexMap = {};
+ const vertexNormals = [];
+ const vertexNormalAccum = {};
+ let acc;
+ let vx;
+ let vy;
+ let vz;
+ let key;
+ const precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001
+ const precision = 10 ** precisionPoints;
+ let posi;
+ let i;
+ let j;
+ let len;
+ let a;
+ let b;
+ let c;
+
+ for (i = 0, len = positions.length; i < len; i += 3) {
+
+ posi = i / 3;
+
+ vx = positions[i];
+ vy = positions[i + 1];
+ vz = positions[i + 2];
+
+ key = `${Math.round(vx * precision)}_${Math.round(vy * precision)}_${Math.round(vz * precision)}`;
+
+ if (vertexMap[key] === undefined) {
+ vertexMap[key] = [posi];
+ } else {
+ vertexMap[key].push(posi);
+ }
- dest = dest || math.vec4();
+ const normal = math.normalizeVec3([normals[i], normals[i + 1], normals[i + 2]]);
- // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
+ vertexNormals[posi] = normal;
- // Assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+ acc = math.vec4([normal[0], normal[1], normal[2], 1]);
- const m11 = m[0];
- const m12 = m[4];
- const m13 = m[8];
- const m21 = m[1];
- const m22 = m[5];
- const m23 = m[9];
- const m31 = m[2];
- const m32 = m[6];
- const m33 = m[10];
- let s;
+ vertexNormalAccum[posi] = acc;
+ }
- const trace = m11 + m22 + m33;
+ for (key in vertexMap) {
- if (trace > 0) {
+ if (vertexMap.hasOwnProperty(key)) {
- s = 0.5 / Math.sqrt(trace + 1.0);
+ const vertices = vertexMap[key];
+ const numVerts = vertices.length;
- dest[3] = 0.25 / s;
- dest[0] = ( m32 - m23 ) * s;
- dest[1] = ( m13 - m31 ) * s;
- dest[2] = ( m21 - m12 ) * s;
+ for (i = 0; i < numVerts; i++) {
- } else if (m11 > m22 && m11 > m33) {
+ const ii = vertices[i];
- s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33);
+ acc = vertexNormalAccum[ii];
- dest[3] = ( m32 - m23 ) / s;
- dest[0] = 0.25 * s;
- dest[1] = ( m12 + m21 ) / s;
- dest[2] = ( m13 + m31 ) / s;
+ for (j = 0; j < numVerts; j++) {
- } else if (m22 > m33) {
+ if (i === j) {
+ continue;
+ }
- s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33);
+ const jj = vertices[j];
- dest[3] = ( m13 - m31 ) / s;
- dest[0] = ( m12 + m21 ) / s;
- dest[1] = 0.25 * s;
- dest[2] = ( m23 + m32 ) / s;
+ a = vertexNormals[ii];
+ b = vertexNormals[jj];
- } else {
+ const angle = Math.abs(math.angleVec3(a, b) / math.DEGTORAD);
- s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22);
+ if (angle < smoothNormalsAngleThreshold) {
- dest[3] = ( m21 - m12 ) / s;
- dest[0] = ( m13 + m31 ) / s;
- dest[1] = ( m23 + m32 ) / s;
- dest[2] = 0.25 * s;
+ acc[0] += b[0];
+ acc[1] += b[1];
+ acc[2] += b[2];
+ acc[3] += 1.0;
+ }
+ }
+ }
}
+ }
- return dest;
- },
+ for (i = 0, len = normals.length; i < len; i += 3) {
- vec3PairToQuaternion: function (u, v, dest) {
+ acc = vertexNormalAccum[i / 3];
- dest = dest || math.vec4();
+ normals[i + 0] = acc[0] / acc[3];
+ normals[i + 1] = acc[1] / acc[3];
+ normals[i + 2] = acc[2] / acc[3];
- const norm_u_norm_v = Math.sqrt(math.dotVec3(u, u) * math.dotVec3(v, v));
- let real_part = norm_u_norm_v + math.dotVec3(u, v);
+ }
+ },
- if (real_part < 0.00000001 * norm_u_norm_v) {
+ //------------------------------------------------------------------------------------------------------------------
+ // Ray casting
+ //------------------------------------------------------------------------------------------------------------------
- // If u and v are exactly opposite, rotate 180 degrees
- // around an arbitrary orthogonal axis. Axis normalisation
- // can happen later, when we normalise the quaternion.
+ /**
+ Transforms a Canvas-space position into a World-space ray, in the context of a Camera.
+ @method canvasPosToWorldRay
+ @static
+ @param {Camera} camera The Camera.
+ @param {Float32Array} canvasPos The Canvas-space position.
+ @param {Float32Array} worldRayOrigin The World-space ray origin.
+ @param {Float32Array} worldRayDir The World-space ray direction.
+ */
+ canvasPosToWorldRay: ((() => {
- real_part = 0.0;
+ const tempMat4b = new Float32Array(16);
+ const tempMat4c = new Float32Array(16);
+ const tempVec4a = new Float32Array(4);
+ const tempVec4b = new Float32Array(4);
+ const tempVec4c = new Float32Array(4);
+ const tempVec4d = new Float32Array(4);
- if (Math.abs(u[0]) > Math.abs(u[2])) {
+ return (camera, canvasPos, worldRayOrigin, worldRayDir) => {
- dest[0] = -u[1];
- dest[1] = u[0];
- dest[2] = 0;
+ const canvas = camera.scene.canvas.canvas;
- } else {
- dest[0] = 0;
- dest[1] = -u[2];
- dest[2] = u[1]
- }
+ const viewMat = camera.viewMatrix;
+ const projMat = camera.projection === "ortho" ? camera.ortho.matrix : camera.perspective.matrix;
- } else {
+ const pvMat = math.mulMat4(projMat, viewMat, tempMat4b);
+ const pvMatInverse = math.inverseMat4(pvMat, tempMat4c);
- // Otherwise, build quaternion the standard way.
- math.cross3Vec3(u, v, dest);
- }
+ // Calculate clip space coordinates, which will be in range
+ // of x=[-1..1] and y=[-1..1], with y=(+1) at top
- dest[3] = real_part;
+ const canvasWidth = canvas.width;
+ const canvasHeight = canvas.height;
- return math.normalizeQuaternion(dest);
- },
+ const clipX = (canvasPos[0] - canvasWidth / 2) / (canvasWidth / 2); // Calculate clip space coordinates
+ const clipY = -(canvasPos[1] - canvasHeight / 2) / (canvasHeight / 2);
- angleAxisToQuaternion: function (angleAxis, dest) {
- dest = dest || math.vec4();
- const halfAngle = angleAxis[3] / 2.0;
- const fsin = Math.sin(halfAngle);
- dest[0] = fsin * angleAxis[0];
- dest[1] = fsin * angleAxis[1];
- dest[2] = fsin * angleAxis[2];
- dest[3] = Math.cos(halfAngle);
- return dest;
- },
-
- quaternionToEuler: (function () {
- const mat = new Float32Array(16);
- return function (q, order, dest) {
- dest = dest || math.vec3();
- math.quaternionToRotationMat4(q, mat);
- math.mat4ToEuler(mat, order, dest);
- return dest;
- };
- })(),
-
- mulQuaternions: function (p, q, dest) {
- dest = dest || math.vec4();
- const p0 = p[0], p1 = p[1], p2 = p[2], p3 = p[3];
- const q0 = q[0], q1 = q[1], q2 = q[2], q3 = q[3];
- dest[0] = p3 * q0 + p0 * q3 + p1 * q2 - p2 * q1;
- dest[1] = p3 * q1 + p1 * q3 + p2 * q0 - p0 * q2;
- dest[2] = p3 * q2 + p2 * q3 + p0 * q1 - p1 * q0;
- dest[3] = p3 * q3 - p0 * q0 - p1 * q1 - p2 * q2;
- return dest;
- },
+ tempVec4a[0] = clipX;
+ tempVec4a[1] = clipY;
+ tempVec4a[2] = -1;
+ tempVec4a[3] = 1;
- vec3ApplyQuaternion: function (q, vec, dest) {
+ math.transformVec4(pvMatInverse, tempVec4a, tempVec4b);
+ math.mulVec4Scalar(tempVec4b, 1 / tempVec4b[3]);
- dest = dest || math.vec3();
+ tempVec4c[0] = clipX;
+ tempVec4c[1] = clipY;
+ tempVec4c[2] = 1;
+ tempVec4c[3] = 1;
- const x = vec[0];
- const y = vec[1];
- const z = vec[2];
+ math.transformVec4(pvMatInverse, tempVec4c, tempVec4d);
+ math.mulVec4Scalar(tempVec4d, 1 / tempVec4d[3]);
- const qx = q[0];
- const qy = q[1];
- const qz = q[2];
- const qw = q[3];
+ worldRayOrigin[0] = tempVec4d[0];
+ worldRayOrigin[1] = tempVec4d[1];
+ worldRayOrigin[2] = tempVec4d[2];
- // calculate quat * vector
+ math.subVec3(tempVec4d, tempVec4b, worldRayDir);
- const ix = qw * x + qy * z - qz * y;
- const iy = qw * y + qz * x - qx * z;
- const iz = qw * z + qx * y - qy * x;
- const iw = -qx * x - qy * y - qz * z;
+ math.normalizeVec3(worldRayDir);
+ };
+ }))(),
- // calculate result * inverse quat
+ /**
+ Transforms a Canvas-space position to a Mesh's Local-space coordinate system, in the context of a Camera.
+ @method canvasPosToLocalRay
+ @static
+ @param {Camera} camera The Camera.
+ @param {Mesh} mesh The Mesh.
+ @param {Float32Array} canvasPos The Canvas-space position.
+ @param {Float32Array} localRayOrigin The Local-space ray origin.
+ @param {Float32Array} localRayDir The Local-space ray direction.
+ */
+ canvasPosToLocalRay: ((() => {
- dest[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy;
- dest[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz;
- dest[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx;
+ const worldRayOrigin = new Float32Array(3);
+ const worldRayDir = new Float32Array(3);
- return dest;
- },
+ return (camera, mesh, canvasPos, localRayOrigin, localRayDir) => {
+ math.canvasPosToWorldRay(camera, canvasPos, worldRayOrigin, worldRayDir);
+ math.worldRayToLocalRay(mesh, worldRayOrigin, worldRayDir, localRayOrigin, localRayDir);
+ };
+ }))(),
- quaternionToMat4: function (q, dest) {
+ /**
+ Transforms a ray from World-space to a Mesh's Local-space coordinate system.
+ @method worldRayToLocalRay
+ @static
+ @param {Mesh} mesh The Mesh.
+ @param {Float32Array} worldRayOrigin The World-space ray origin.
+ @param {Float32Array} worldRayDir The World-space ray direction.
+ @param {Float32Array} localRayOrigin The Local-space ray origin.
+ @param {Float32Array} localRayDir The Local-space ray direction.
+ */
+ worldRayToLocalRay: ((() => {
- dest = math.identityMat4(dest);
+ const tempMat4 = new Float32Array(16);
+ const tempVec4a = new Float32Array(4);
+ const tempVec4b = new Float32Array(4);
- const q0 = q[0]; //x
- const q1 = q[1]; //y
- const q2 = q[2]; //z
- const q3 = q[3]; //w
+ return (mesh, worldRayOrigin, worldRayDir, localRayOrigin, localRayDir) => {
- const tx = 2.0 * q0;
- const ty = 2.0 * q1;
- const tz = 2.0 * q2;
+ const modelMat = mesh.worldMatrix || mesh.matrix;
+ const modelMatInverse = math.inverseMat4(modelMat, tempMat4);
- const twx = tx * q3;
- const twy = ty * q3;
- const twz = tz * q3;
+ tempVec4a[0] = worldRayOrigin[0];
+ tempVec4a[1] = worldRayOrigin[1];
+ tempVec4a[2] = worldRayOrigin[2];
+ tempVec4a[3] = 1;
- const txx = tx * q0;
- const txy = ty * q0;
- const txz = tz * q0;
+ math.transformVec4(modelMatInverse, tempVec4a, tempVec4b);
- const tyy = ty * q1;
- const tyz = tz * q1;
- const tzz = tz * q2;
+ localRayOrigin[0] = tempVec4b[0];
+ localRayOrigin[1] = tempVec4b[1];
+ localRayOrigin[2] = tempVec4b[2];
- dest[0] = 1.0 - (tyy + tzz);
- dest[1] = txy + twz;
- dest[2] = txz - twy;
+ math.transformVec3(modelMatInverse, worldRayDir, localRayDir);
+ };
+ }))(),
- dest[4] = txy - twz;
- dest[5] = 1.0 - (txx + tzz);
- dest[6] = tyz + twx;
+ buildKDTree: ((() => {
- dest[8] = txz + twy;
- dest[9] = tyz - twx;
+ const KD_TREE_MAX_DEPTH = 10;
+ const KD_TREE_MIN_TRIANGLES = 20;
- dest[10] = 1.0 - (txx + tyy);
+ const dimLength = new Float32Array();
- return dest;
- },
-
- quaternionToRotationMat4: function (q, m) {
-
- const x = q[0];
- const y = q[1];
- const z = q[2];
- const w = q[3];
-
- const x2 = x + x, y2 = y + y, z2 = z + z;
- const xx = x * x2, xy = x * y2, xz = x * z2;
- const yy = y * y2, yz = y * z2, zz = z * z2;
- const wx = w * x2, wy = w * y2, wz = w * z2;
-
- m[0] = 1 - ( yy + zz );
- m[4] = xy - wz;
- m[8] = xz + wy;
-
- m[1] = xy + wz;
- m[5] = 1 - ( xx + zz );
- m[9] = yz - wx;
-
- m[2] = xz - wy;
- m[6] = yz + wx;
- m[10] = 1 - ( xx + yy );
-
- // last column
- m[3] = 0;
- m[7] = 0;
- m[11] = 0;
-
- // bottom row
- m[12] = 0;
- m[13] = 0;
- m[14] = 0;
- m[15] = 1;
-
- return m;
- },
-
- normalizeQuaternion: function (q, dest) {
- dest = dest || q;
- const len = math.lenVec4([q[0], q[1], q[2], q[3]]);
- dest[0] = q[0] / len;
- dest[1] = q[1] / len;
- dest[2] = q[2] / len;
- dest[3] = q[3] / len;
- return dest;
- },
-
- conjugateQuaternion: function (q, dest) {
- dest = dest || q;
- dest[0] = -q[0];
- dest[1] = -q[1];
- dest[2] = -q[2];
- dest[3] = q[3];
- return dest;
- },
-
- inverseQuaternion: function (q, dest) {
- return math.normalizeQuaternion(math.conjugateQuaternion(q, dest));
- },
-
- quaternionToAngleAxis: function (q, angleAxis) {
- angleAxis = angleAxis || math.vec4();
- q = math.normalizeQuaternion(q, tempVec4);
- const q3 = q[3];
- const angle = 2 * Math.acos(q3);
- const s = Math.sqrt(1 - q3 * q3);
- if (s < 0.001) { // test to avoid divide by zero, s is always positive due to sqrt
- angleAxis[0] = q[0];
- angleAxis[1] = q[1];
- angleAxis[2] = q[2];
- } else {
- angleAxis[0] = q[0] / s;
- angleAxis[1] = q[1] / s;
- angleAxis[2] = q[2] / s;
+ function buildNode(triangles, indices, positions, depth) {
+ const aabb = new Float32Array(6);
+
+ const node = {
+ triangles: null,
+ left: null,
+ right: null,
+ leaf: false,
+ splitDim: 0,
+ aabb
+ };
+
+ aabb[0] = aabb[1] = aabb[2] = Number.POSITIVE_INFINITY;
+ aabb[3] = aabb[4] = aabb[5] = Number.NEGATIVE_INFINITY;
+
+ let t;
+ let i;
+ let len;
+
+ for (t = 0, len = triangles.length; t < len; ++t) {
+ var ii = triangles[t] * 3;
+ for (let j = 0; j < 3; ++j) {
+ const pi = indices[ii + j] * 3;
+ if (positions[pi] < aabb[0]) {
+ aabb[0] = positions[pi]
+ }
+ if (positions[pi] > aabb[3]) {
+ aabb[3] = positions[pi]
+ }
+ if (positions[pi + 1] < aabb[1]) {
+ aabb[1] = positions[pi + 1]
+ }
+ if (positions[pi + 1] > aabb[4]) {
+ aabb[4] = positions[pi + 1]
+ }
+ if (positions[pi + 2] < aabb[2]) {
+ aabb[2] = positions[pi + 2]
+ }
+ if (positions[pi + 2] > aabb[5]) {
+ aabb[5] = positions[pi + 2]
+ }
+ }
}
- angleAxis[3] = angle; // * 57.295779579;
- return angleAxis;
- },
-
- decompressPosition: function (position, decodeMatrix, dest) {
- dest[0] = position[0] * decodeMatrix[0] + decodeMatrix[12];
- dest[1] = position[1] * decodeMatrix[5] + decodeMatrix[13];
- dest[2] = position[2] * decodeMatrix[10] + decodeMatrix[14];
- },
-
- decompressPositions: function (positions, decodeMatrix, dest) {
- dest = dest || new Float32Array(positions.length);
- for (let i = 0, len = positions.length; i < len; i += 3) {
- dest[i + 0] = positions[i + 0] * decodeMatrix[0] + decodeMatrix[12];
- dest[i + 1] = positions[i + 1] * decodeMatrix[5] + decodeMatrix[13];
- dest[i + 2] = positions[i + 2] * decodeMatrix[10] + decodeMatrix[14];
+
+ if (triangles.length < KD_TREE_MIN_TRIANGLES || depth > KD_TREE_MAX_DEPTH) {
+ node.triangles = triangles;
+ node.leaf = true;
+ return node;
}
- return dest;
- },
-
- decompressUV: function (uv, decodeMatrix, dest) {
- dest[0] = uv[0] * decodeMatrix[0] + decodeMatrix[6];
- dest[1] = uv[1] * decodeMatrix[4] + decodeMatrix[7];
- },
-
- decompressUVs: function (uvs, decodeMatrix, dest) {
- dest = dest || new Float32Array(uvs.length);
- for (let i = 0, len = uvs.length; i < len; i += 3) {
- dest[i + 0] = uvs[i + 0] * decodeMatrix[0] + decodeMatrix[6];
- dest[i + 1] = uvs[i + 1] * decodeMatrix[4] + decodeMatrix[7];
+
+ dimLength[0] = aabb[3] - aabb[0];
+ dimLength[1] = aabb[4] - aabb[1];
+ dimLength[2] = aabb[5] - aabb[2];
+
+ let dim = 0;
+
+ if (dimLength[1] > dimLength[dim]) {
+ dim = 1;
}
- return dest;
- },
- octDecodeVec2: function (oct, result) {
- let x = oct[0];
- let y = oct[1];
- x = (2 * x + 1) / 255;
- y = (2 * y + 1) / 255;
- const z = 1 - Math.abs(x) - Math.abs(y);
- if (z < 0) {
- x = (1 - Math.abs(y)) * (x >= 0 ? 1 : -1);
- y = (1 - Math.abs(x)) * (y >= 0 ? 1 : -1);
+ if (dimLength[2] > dimLength[dim]) {
+ dim = 2;
}
- const length = Math.sqrt(x * x + y * y + z * z);
- result[0] = x / length;
- result[1] = y / length;
- result[2] = z / length;
- return result;
- },
-
- octDecodeVec2s: function (octs, result) {
- for (let i = 0, j = 0, len = octs.length; i < len; i += 2) {
- let x = octs[i + 0];
- let y = octs[i + 1];
- x = (2 * x + 1) / 255;
- y = (2 * y + 1) / 255;
- const z = 1 - Math.abs(x) - Math.abs(y);
- if (z < 0) {
- x = (1 - Math.abs(y)) * (x >= 0 ? 1 : -1);
- y = (1 - Math.abs(x)) * (y >= 0 ? 1 : -1);
+
+ node.splitDim = dim;
+
+ const mid = (aabb[dim] + aabb[dim + 3]) / 2;
+ const left = new Array(triangles.length);
+ let numLeft = 0;
+ const right = new Array(triangles.length);
+ let numRight = 0;
+
+ for (t = 0, len = triangles.length; t < len; ++t) {
+
+ var ii = triangles[t] * 3;
+ const i0 = indices[ii];
+ const i1 = indices[ii + 1];
+ const i2 = indices[ii + 2];
+
+ const pi0 = i0 * 3;
+ const pi1 = i1 * 3;
+ const pi2 = i2 * 3;
+
+ if (positions[pi0 + dim] <= mid || positions[pi1 + dim] <= mid || positions[pi2 + dim] <= mid) {
+ left[numLeft++] = triangles[t];
+ } else {
+ right[numRight++] = triangles[t];
}
- const length = Math.sqrt(x * x + y * y + z * z);
- result[j + 0] = x / length;
- result[j + 1] = y / length;
- result[j + 2] = z / length;
- j += 3;
}
- return result;
+
+ left.length = numLeft;
+ right.length = numRight;
+
+ node.left = buildNode(left, indices, positions, depth + 1);
+ node.right = buildNode(right, indices, positions, depth + 1);
+
+ return node;
}
- };
-})();
\ No newline at end of file
+ return (indices, positions) => {
+ const numTris = indices.length / 3;
+ const triangles = new Array(numTris);
+ for (let i = 0; i < numTris; ++i) {
+ triangles[i] = i;
+ }
+ return buildNode(triangles, indices, positions, 0);
+ };
+ }))()
+};
+
+export {math};
\ No newline at end of file
diff --git a/src/math/mathBoundaries.js b/src/math/mathBoundaries.js
deleted file mode 100644
index b949eaaa9..000000000
--- a/src/math/mathBoundaries.js
+++ /dev/null
@@ -1,785 +0,0 @@
-/**
- * Boundary math functions.
- */
-(function () {
-
- "use strict";
-
- const math = xeogl.math;
-
- /**
- * Returns a new, uninitialized 3D axis-aligned bounding box.
- *
- * @private
- */
- math.AABB3 = function (values) {
- return new Float32Array(values || 6);
- };
-
- /**
- * Returns a new, uninitialized 2D axis-aligned bounding box.
- *
- * @private
- */
- math.AABB2 = function (values) {
- return new Float32Array(values || 4);
- };
-
- /**
- * Returns a new, uninitialized 3D oriented bounding box (OBB).
- *
- * @private
- */
- math.OBB3 = function (values) {
- return new Float32Array(values || 32);
- };
-
- /**
- * Returns a new, uninitialized 2D oriented bounding box (OBB).
- *
- * @private
- */
- math.OBB2 = function (values) {
- return new Float32Array(values || 16);
- };
-
-
- /**
- * Transforms an OBB3 by a 4x4 matrix.
- *
- * @private
- */
- math.transformOBB3 = function (m, p, p2) {
-
- p2 = p2 || p;
-
- let i;
- const len = p.length;
-
- let x;
- let y;
- let z;
-
- const m0 = m[0];
- const m1 = m[1];
- const m2 = m[2];
- const m3 = m[3];
- const m4 = m[4];
- const m5 = m[5];
- const m6 = m[6];
- const m7 = m[7];
- const m8 = m[8];
- const m9 = m[9];
- const m10 = m[10];
- const m11 = m[11];
- const m12 = m[12];
- const m13 = m[13];
- const m14 = m[14];
- const m15 = m[15];
-
- for (i = 0; i < len; i += 4) {
-
- x = p[i + 0];
- y = p[i + 1];
- z = p[i + 2];
-
- p2[i + 0] = (m0 * x) + (m4 * y) + (m8 * z) + m12;
- p2[i + 1] = (m1 * x) + (m5 * y) + (m9 * z) + m13;
- p2[i + 2] = (m2 * x) + (m6 * y) + (m10 * z) + m14;
- p2[i + 3] = (m3 * x) + (m7 * y) + (m11 * z) + m15;
- }
-
- return p2;
- };
-
- /**
- * Gets the diagonal size of an AABB3 given as minima and maxima.
- *
- * @private
- */
- math.getAABB3Diag = (function () {
-
- const min = new Float32Array(3);
- const max = new Float32Array(3);
- const tempVec3 = new Float32Array(3);
-
- return function (aabb) {
-
- min[0] = aabb[0];
- min[1] = aabb[1];
- min[2] = aabb[2];
-
- max[0] = aabb[3];
- max[1] = aabb[4];
- max[2] = aabb[5];
-
- math.subVec3(max, min, tempVec3);
-
- return Math.abs(math.lenVec3(tempVec3));
- };
- })();
-
- /**
- * Get a diagonal boundary size that is symmetrical about the given point.
- *
- * @private
- */
- math.getAABB3DiagPoint = (function () {
-
- const min = new Float32Array(3);
- const max = new Float32Array(3);
- const tempVec3 = new Float32Array(3);
-
- return function (aabb, p) {
-
- min[0] = aabb[0];
- min[1] = aabb[1];
- min[2] = aabb[2];
-
- max[0] = aabb[3];
- max[1] = aabb[4];
- max[2] = aabb[5];
-
- const diagVec = math.subVec3(max, min, tempVec3);
-
- const xneg = p[0] - aabb[0];
- const xpos = aabb[3] - p[0];
- const yneg = p[1] - aabb[1];
- const ypos = aabb[4] - p[1];
- const zneg = p[2] - aabb[2];
- const zpos = aabb[5] - p[2];
-
- diagVec[0] += (xneg > xpos) ? xneg : xpos;
- diagVec[1] += (yneg > ypos) ? yneg : ypos;
- diagVec[2] += (zneg > zpos) ? zneg : zpos;
-
- return Math.abs(math.lenVec3(diagVec));
- };
- })();
-
- /**
- * Gets the center of an AABB.
- *
- * @private
- */
- math.getAABB3Center = function (aabb, dest) {
- const r = dest || math.vec3();
-
- r[0] = (aabb[0] + aabb[3] ) / 2;
- r[1] = (aabb[1] + aabb[4] ) / 2;
- r[2] = (aabb[2] + aabb[5] ) / 2;
-
- return r;
- };
-
- /**
- * Gets the center of a 2D AABB.
- *
- * @private
- */
- math.getAABB2Center = function (aabb, dest) {
- const r = dest || math.vec2();
-
- r[0] = (aabb[2] + aabb[0] ) / 2;
- r[1] = (aabb[3] + aabb[1] ) / 2;
-
- return r;
- };
-
- /**
- * Collapses a 3D axis-aligned boundary, ready to expand to fit 3D points.
- * Creates new AABB if none supplied.
- *
- * @private
- */
- math.collapseAABB3 = function (aabb) {
-
- aabb = aabb || math.AABB3();
-
- aabb[0] = xeogl.math.MAX_DOUBLE;
- aabb[1] = xeogl.math.MAX_DOUBLE;
- aabb[2] = xeogl.math.MAX_DOUBLE;
- aabb[3] = -xeogl.math.MAX_DOUBLE;
- aabb[4] = -xeogl.math.MAX_DOUBLE;
- aabb[5] = -xeogl.math.MAX_DOUBLE;
-
- return aabb;
- };
-
- /**
- * Converts an axis-aligned 3D boundary into an oriented boundary consisting of
- * an array of eight 3D positions, one for each corner of the boundary.
- *
- * @private
- */
- math.AABB3ToOBB3 = function (aabb, obb) {
-
- obb = obb || math.OBB3();
-
- obb[0] = aabb[0];
- obb[1] = aabb[1];
- obb[2] = aabb[2];
- obb[3] = 1;
-
- obb[4] = aabb[3];
- obb[5] = aabb[1];
- obb[6] = aabb[2];
- obb[7] = 1;
-
- obb[8] = aabb[3];
- obb[9] = aabb[4];
- obb[10] = aabb[2];
- obb[11] = 1;
-
- obb[12] = aabb[0];
- obb[13] = aabb[4];
- obb[14] = aabb[2];
- obb[15] = 1;
-
- obb[16] = aabb[0];
- obb[17] = aabb[1];
- obb[18] = aabb[5];
- obb[19] = 1;
-
- obb[20] = aabb[3];
- obb[21] = aabb[1];
- obb[22] = aabb[5];
- obb[23] = 1;
-
- obb[24] = aabb[3];
- obb[25] = aabb[4];
- obb[26] = aabb[5];
- obb[27] = 1;
-
- obb[28] = aabb[0];
- obb[29] = aabb[4];
- obb[30] = aabb[5];
- obb[31] = 1;
-
- return obb;
- };
-
- /**
- * Finds the minimum axis-aligned 3D boundary enclosing the homogeneous 3D points (x,y,z,w) given in a flattened array.
- *
- * @private
- */
- math.positions3ToAABB3 = (function() {
-
- const p = new Float32Array(3);
-
- return function (positions, aabb, positionsDecodeMatrix) {
-
- aabb = aabb || math.AABB3();
-
- let xmin = xeogl.math.MAX_DOUBLE;
- let ymin = xeogl.math.MAX_DOUBLE;
- let zmin = xeogl.math.MAX_DOUBLE;
- let xmax = -xeogl.math.MAX_DOUBLE;
- let ymax = -xeogl.math.MAX_DOUBLE;
- let zmax = -xeogl.math.MAX_DOUBLE;
-
- let x, y, z;
-
- for (let i = 0, len = positions.length; i < len; i += 3) {
-
- if (positionsDecodeMatrix) {
-
- p[0] = positions[i + 0];
- p[1] = positions[i + 1];
- p[2] = positions[i + 2];
-
- math.decompressPosition(p, positionsDecodeMatrix, p);
-
- x = p[0];
- y = p[1];
- z = p[2];
-
- } else {
- x = positions[i + 0];
- y = positions[i + 1];
- z = positions[i + 2];
- }
-
- if (x < xmin) {
- xmin = x;
- }
-
- if (y < ymin) {
- ymin = y;
- }
-
- if (z < zmin) {
- zmin = z;
- }
-
- if (x > xmax) {
- xmax = x;
- }
-
- if (y > ymax) {
- ymax = y;
- }
-
- if (z > zmax) {
- zmax = z;
- }
- }
-
- aabb[0] = xmin;
- aabb[1] = ymin;
- aabb[2] = zmin;
- aabb[3] = xmax;
- aabb[4] = ymax;
- aabb[5] = zmax;
-
- return aabb;
- };
- })();
-
- /**
- * Finds the minimum axis-aligned 3D boundary enclosing the homogeneous 3D points (x,y,z,w) given in a flattened array.
- *
- * @private
- */
- math.OBB3ToAABB3 = function (obb, aabb) {
-
- aabb = aabb || math.AABB3();
-
- let xmin = xeogl.math.MAX_DOUBLE;
- let ymin = xeogl.math.MAX_DOUBLE;
- let zmin = xeogl.math.MAX_DOUBLE;
- let xmax = -xeogl.math.MAX_DOUBLE;
- let ymax = -xeogl.math.MAX_DOUBLE;
- let zmax = -xeogl.math.MAX_DOUBLE;
-
- let x, y, z;
-
- for (let i = 0, len = obb.length; i < len; i += 4) {
-
- x = obb[i + 0];
- y = obb[i + 1];
- z = obb[i + 2];
-
- if (x < xmin) {
- xmin = x;
- }
-
- if (y < ymin) {
- ymin = y;
- }
-
- if (z < zmin) {
- zmin = z;
- }
-
- if (x > xmax) {
- xmax = x;
- }
-
- if (y > ymax) {
- ymax = y;
- }
-
- if (z > zmax) {
- zmax = z;
- }
- }
-
- aabb[0] = xmin;
- aabb[1] = ymin;
- aabb[2] = zmin;
- aabb[3] = xmax;
- aabb[4] = ymax;
- aabb[5] = zmax;
-
- return aabb;
- };
-
- /**
- * Finds the minimum axis-aligned 3D boundary enclosing the given 3D points.
- *
- * @private
- */
- math.points3ToAABB3 = function (points, aabb) {
-
- aabb = aabb || math.AABB3();
-
- let xmin = xeogl.math.MAX_DOUBLE;
- let ymin = xeogl.math.MAX_DOUBLE;
- let zmin = xeogl.math.MAX_DOUBLE;
- let xmax = -xeogl.math.MAX_DOUBLE;
- let ymax = -xeogl.math.MAX_DOUBLE;
- let zmax = -xeogl.math.MAX_DOUBLE;
-
- let x, y, z;
-
- for (let i = 0, len = points.length; i < len; i++) {
-
- x = points[i][0];
- y = points[i][1];
- z = points[i][2];
-
- if (x < xmin) {
- xmin = x;
- }
-
- if (y < ymin) {
- ymin = y;
- }
-
- if (z < zmin) {
- zmin = z;
- }
-
- if (x > xmax) {
- xmax = x;
- }
-
- if (y > ymax) {
- ymax = y;
- }
-
- if (z > zmax) {
- zmax = z;
- }
- }
-
- aabb[0] = xmin;
- aabb[1] = ymin;
- aabb[2] = zmin;
- aabb[3] = xmax;
- aabb[4] = ymax;
- aabb[5] = zmax;
-
- return aabb;
- };
-
- /**
- * Finds the minimum boundary sphere enclosing the given 3D points.
- *
- * @private
- */
- math.points3ToSphere3 = (function () {
-
- const tempVec3 = new Float32Array(3);
-
- return function (points, sphere) {
-
- sphere = sphere || math.vec4();
-
- let x = 0;
- let y = 0;
- let z = 0;
-
- let i;
- const numPoints = points.length;
-
- for (i = 0; i < numPoints; i++) {
- x += points[i][0];
- y += points[i][1];
- z += points[i][2];
- }
-
- sphere[0] = x / numPoints;
- sphere[1] = y / numPoints;
- sphere[2] = z / numPoints;
-
- let radius = 0;
- let dist;
-
- for (i = 0; i < numPoints; i++) {
-
- dist = Math.abs(math.lenVec3(math.subVec3(points[i], sphere, tempVec3)));
-
- if (dist > radius) {
- radius = dist;
- }
- }
-
- sphere[3] = radius;
-
- return sphere;
- };
- })();
-
- /**
- * Finds the minimum boundary sphere enclosing the given 3D points.
- *
- * @private
- */
- math.OBB3ToSphere3 = (function () {
-
- const point = new Float32Array(3);
- const tempVec3 = new Float32Array(3);
-
- return function (points, sphere) {
-
- sphere = sphere || math.vec4();
-
- let x = 0;
- let y = 0;
- let z = 0;
-
- let i;
- const lenPoints = points.length;
- const numPoints = lenPoints / 4;
-
- for (i = 0; i < lenPoints; i += 4) {
- x += points[i + 0];
- y += points[i + 1];
- z += points[i + 2];
- }
-
- sphere[0] = x / numPoints;
- sphere[1] = y / numPoints;
- sphere[2] = z / numPoints;
-
- let radius = 0;
- let dist;
-
- for (i = 0; i < lenPoints; i += 4) {
-
- point[0] = points[i + 0];
- point[1] = points[i + 1];
- point[2] = points[i + 2];
-
- dist = Math.abs(math.lenVec3(math.subVec3(point, sphere, tempVec3)));
-
- if (dist > radius) {
- radius = dist;
- }
- }
-
- sphere[3] = radius;
-
- return sphere;
- };
- })();
-
- /**
- * Gets the center of a bounding sphere.
- *
- * @private
- */
- math.getSphere3Center = function (sphere, dest) {
- dest = dest || math.vec3();
-
- dest[0] = sphere[0];
- dest[1] = sphere[1];
- dest[2] = sphere[2];
-
- return dest;
- };
-
- /**
- * Expands the first axis-aligned 3D boundary to enclose the second, if required.
- *
- * @private
- */
- math.expandAABB3 = function (aabb1, aabb2) {
-
- if (aabb1[0] > aabb2[0]) {
- aabb1[0] = aabb2[0];
- }
-
- if (aabb1[1] > aabb2[1]) {
- aabb1[1] = aabb2[1];
- }
-
- if (aabb1[2] > aabb2[2]) {
- aabb1[2] = aabb2[2];
- }
-
- if (aabb1[3] < aabb2[3]) {
- aabb1[3] = aabb2[3];
- }
-
- if (aabb1[4] < aabb2[4]) {
- aabb1[4] = aabb2[4];
- }
-
- if (aabb1[5] < aabb2[5]) {
- aabb1[5] = aabb2[5];
- }
-
- return aabb1;
- };
-
- /**
- * Expands an axis-aligned 3D boundary to enclose the given point, if needed.
- *
- * @private
- */
- math.expandAABB3Point3 = function (aabb, p) {
-
- if (aabb[0] < p[0]) {
- aabb[0] = p[0];
- }
-
- if (aabb[1] < p[1]) {
- aabb[1] = p[1];
- }
-
- if (aabb[2] < p[2]) {
- aabb[2] = p[2];
- }
-
- if (aabb[3] > p[0]) {
- aabb[3] = p[0];
- }
-
- if (aabb[4] > p[1]) {
- aabb[4] = p[1];
- }
-
- if (aabb[5] > p[2]) {
- aabb[5] = p[2];
- }
-
- return aabb;
- };
-
- /**
- * Collapses a 2D axis-aligned boundary, ready to expand to fit 2D points.
- * Creates new AABB if none supplied.
- *
- * @private
- */
- math.collapseAABB2 = function (aabb) {
-
- aabb = aabb || math.AABB2();
-
- aabb[0] = xeogl.math.MAX_DOUBLE;
- aabb[1] = xeogl.math.MAX_DOUBLE;
- aabb[2] = -xeogl.math.MAX_DOUBLE;
- aabb[3] = -xeogl.math.MAX_DOUBLE;
-
- return aabb;
- };
-
- /**
- * Finds the minimum 2D projected axis-aligned boundary enclosing the given 3D points.
- *
- * @private
- */
- math.OBB3ToAABB2 = function (points, aabb) {
-
- aabb = aabb || math.AABB2();
-
- let xmin = xeogl.math.MAX_DOUBLE;
- let ymin = xeogl.math.MAX_DOUBLE;
- let xmax = -xeogl.math.MAX_DOUBLE;
- let ymax = -xeogl.math.MAX_DOUBLE;
-
- let x;
- let y;
- let w;
- let f;
-
- for (let i = 0, len = points.length; i < len; i += 4) {
-
- x = points[i + 0];
- y = points[i + 1];
- w = points[i + 3] || 1.0;
-
- f = 1.0 / w;
-
- x *= f;
- y *= f;
-
- if (x < xmin) {
- xmin = x;
- }
-
- if (y < ymin) {
- ymin = y;
- }
-
- if (x > xmax) {
- xmax = x;
- }
-
- if (y > ymax) {
- ymax = y;
- }
- }
-
- aabb[0] = xmin;
- aabb[1] = ymin;
- aabb[2] = xmax;
- aabb[3] = ymax;
-
- return aabb;
- };
-
- /**
- * Expands the first axis-aligned 2D boundary to enclose the second, if required.
- *
- * @private
- */
- math.expandAABB2 = function (aabb1, aabb2) {
-
- if (aabb1[0] > aabb2[0]) {
- aabb1[0] = aabb2[0];
- }
-
- if (aabb1[1] > aabb2[1]) {
- aabb1[1] = aabb2[1];
- }
-
- if (aabb1[2] < aabb2[2]) {
- aabb1[2] = aabb2[2];
- }
-
- if (aabb1[3] < aabb2[3]) {
- aabb1[3] = aabb2[3];
- }
-
- return aabb1;
- };
-
- /**
- * Expands an axis-aligned 2D boundary to enclose the given point, if required.
- *
- * @private
- */
- math.expandAABB2Point2 = function (aabb, p) {
-
- if (aabb[0] > p[0]) {
- aabb[0] = p[0];
- }
-
- if (aabb[1] > p[1]) {
- aabb[1] = p[1];
- }
-
- if (aabb[2] < p[0]) {
- aabb[2] = p[0];
- }
-
- if (aabb[3] < p[1]) {
- aabb[3] = p[1];
- }
-
- return aabb;
- };
-
- math.AABB2ToCanvas = function (aabb, canvasWidth, canvasHeight, aabb2) {
-
- aabb2 = aabb2 || aabb;
-
- const xmin = (aabb[0] + 1.0) * 0.5;
- const ymin = (aabb[1] + 1.0) * 0.5;
- const xmax = (aabb[2] + 1.0) * 0.5;
- const ymax = (aabb[3] + 1.0) * 0.5;
-
- aabb2[0] = Math.floor(xmin * canvasWidth);
- aabb2[1] = canvasHeight - Math.floor(ymax * canvasHeight);
- aabb2[2] = Math.floor(xmax * canvasWidth);
- aabb2[3] = canvasHeight - Math.floor(ymin * canvasHeight);
-
- return aabb2;
- };
-
-})();
\ No newline at end of file
diff --git a/src/math/mathCurves.js b/src/math/mathCurves.js
deleted file mode 100644
index d759b93a5..000000000
--- a/src/math/mathCurves.js
+++ /dev/null
@@ -1,83 +0,0 @@
-/**
- * Curve math functions.
- */
-(function () {
-
- "use strict";
-
- const math = xeogl.math;
-
- math.tangentQuadraticBezier = function (t, p0, p1, p2) {
- return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 );
- };
-
- math.tangentQuadraticBezier = function (t, p0, p1, p2, p3) {
- return -3 * p0 * (1 - t) * (1 - t) +
- 3 * p1 * (1 - t) * (1 - t) - 6 * t * p1 * (1 - t) +
- 6 * t * p2 * (1 - t) - 3 * t * t * p2 +
- 3 * t * t * p3;
- };
-
- math.tangentSpline = function (t) {
- const h00 = 6 * t * t - 6 * t;
- const h10 = 3 * t * t - 4 * t + 1;
- const h01 = -6 * t * t + 6 * t;
- const h11 = 3 * t * t - 2 * t;
- return h00 + h10 + h01 + h11;
- };
-
- math.catmullRomInterpolate = function (p0, p1, p2, p3, t) {
- const v0 = ( p2 - p0 ) * 0.5;
- const v1 = ( p3 - p1 ) * 0.5;
- const t2 = t * t;
- const t3 = t * t2;
- return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( -3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;
- };
-
- // Bezier Curve formulii from http://en.wikipedia.org/wiki/B%C3%A9zier_curve
-
- // Quad Bezier Functions
-
- math.b2p0 = function (t, p) {
- const k = 1 - t;
- return k * k * p;
-
- };
-
- math.b2p1 = function (t, p) {
- return 2 * ( 1 - t ) * t * p;
- };
-
- math.b2p2 = function (t, p) {
- return t * t * p;
- };
-
- math.b2 = function (t, p0, p1, p2) {
- return this.b2p0(t, p0) + this.b2p1(t, p1) + this.b2p2(t, p2);
- };
-
- // Cubic Bezier Functions
-
- math.b3p0 = function (t, p) {
- const k = 1 - t;
- return k * k * k * p;
- };
-
- math.b3p1 = function (t, p) {
- const k = 1 - t;
- return 3 * k * k * t * p;
- };
-
- math.b3p2 = function (t, p) {
- const k = 1 - t;
- return 3 * k * t * t * p;
- };
-
- math.b3p3 = function (t, p) {
- return t * t * t * p;
- };
-
- math.b3 = function (t, p0, p1, p2, p3) {
- return this.b3p0(t, p0) + this.b3p1(t, p1) + this.b3p2(t, p2) + this.b3p3(t, p3);
- };
-})();
\ No newline at end of file
diff --git a/src/math/mathEntityKDTree.js b/src/math/mathEntityKDTree.js
deleted file mode 100644
index fcba97c27..000000000
--- a/src/math/mathEntityKDTree.js
+++ /dev/null
@@ -1,112 +0,0 @@
-(function () {
-
- "use strict";
-
- const KD_TREE_MAX_DEPTH = 10;
- const KD_TREE_MIN_meshes = 20;
-
- const math = xeogl.math;
-
- math.buildMeshKDTree = function (meshes) {
- return buildNode(meshes, 0);
- };
-
- const dimLength = new Float32Array();
-
- function buildNode(meshes, depth) {
-
- const aabb = new Float32Array(6);
-
- const node = {
- meshes: null,
- left: null,
- right: null,
- leaf: false,
- splitDim: 0,
- aabb: aabb
- };
-
- aabb[0] = aabb[1] = aabb[2] = Number.POSITIVE_INFINITY;
- aabb[3] = aabb[4] = aabb[5] = Number.NEGATIVE_INFINITY;
-
- let t, i, len;
-
- for (t = 0, len = meshes.length; t < len; ++t) {
-
- var mesh = meshes[t] * 3;
- var meshAABB = mesh.aabb;
-
- if (meshAABB[0] < aabb[0]) {
- aabb[0] = meshAABB[p0]
- }
-
- if (meshAABB[3] > aabb[3]) {
- aabb[3] = meshAABB[3]
- }
-
- if (meshAABB[1] < aabb[1]) {
- aabb[1] = meshAABB[1]
- }
-
- if (meshAABB[4] > aabb[4]) {
- aabb[4] = meshAABB[4]
- }
-
- if (meshAABB[2] < aabb[2]) {
- aabb[2] = meshAABB[2]
- }
-
- if (meshAABB[5] > aabb[5]) {
- aabb[5] = meshAABB[5]
- }
- }
-
- if (meshes.length < KD_TREE_MIN_meshes || depth > KD_TREE_MAX_DEPTH) {
- node.meshes = meshes;
- node.leaf = true;
- return node;
- }
-
- dimLength[0] = aabb[3] - aabb[0];
- dimLength[1] = aabb[4] - aabb[1];
- dimLength[2] = aabb[5] - aabb[2];
-
- let dim = 0;
-
- if (dimLength[1] > dimLength[dim]) {
- dim = 1;
- }
-
- if (dimLength[2] > dimLength[dim]) {
- dim = 2;
- }
-
- node.splitDim = dim;
-
- const mid = (aabb[dim] + aabb[dim + 3]) / 2;
- const left = new Array(meshes.length);
- let numLeft = 0;
- const right = new Array(meshes.length);
- let numRight = 0;
-
- for (t = 0, len = meshes.length; t < len; ++t) {
-
- var mesh = meshes[t];
- var meshAABB = mesh.aabb;
-
- if (meshAABB[3 + dim] <= mid) {
- left[numLeft++] = meshes[t];
- } else {
- right[numRight++] = meshes[t];
- }
- }
-
- left.length = numLeft;
- right.length = numRight;
-
- node.left = buildNode(left, depth + 1);
- node.right = buildNode(right, depth + 1);
-
- return node;
- }
-})();
\ No newline at end of file
diff --git a/src/math/mathGeometry.js b/src/math/mathGeometry.js
deleted file mode 100644
index 80d0b8750..000000000
--- a/src/math/mathGeometry.js
+++ /dev/null
@@ -1,281 +0,0 @@
-/**
- * Geometry math functions.
- */
-(function () {
-
- "use strict";
-
- const math = xeogl.math;
-
- /**
- * Calculates the normal vector of a triangle.
- *
- * @private
- */
- math.triangleNormal = function (a, b, c, normal) {
-
- normal = normal || math.vec3();
-
- const p1x = b[0] - a[0];
- const p1y = b[1] - a[1];
- const p1z = b[2] - a[2];
-
- const p2x = c[0] - a[0];
- const p2y = c[1] - a[1];
- const p2z = c[2] - a[2];
-
- const p3x = p1y * p2z - p1z * p2y;
- const p3y = p1z * p2x - p1x * p2z;
- const p3z = p1x * p2y - p1y * p2x;
-
- const mag = Math.sqrt(p3x * p3x + p3y * p3y + p3z * p3z);
- if (mag === 0) {
- normal[0] = 0;
- normal[1] = 0;
- normal[2] = 0;
- } else {
- normal[0] = p3x / mag;
- normal[1] = p3y / mag;
- normal[2] = p3z / mag;
- }
-
- return normal
- };
-
- /**
- * Finds the intersection of a 3D ray with a 3D triangle.
- *
- * @private
- */
- math.rayTriangleIntersect = (function () {
-
- const tempVec3 = new Float32Array(3);
- const tempVec3b = new Float32Array(3);
- const tempVec3c = new Float32Array(3);
- const tempVec3d = new Float32Array(3);
- const tempVec3e = new Float32Array(3);
-
- return function (origin, dir, a, b, c, isect) {
-
- isect = isect || math.vec3();
-
- const EPSILON = 0.000001;
-
- const edge1 = math.subVec3(b, a, tempVec3);
- const edge2 = math.subVec3(c, a, tempVec3b);
-
- const pvec = math.cross3Vec3(dir, edge2, tempVec3c);
- const det = math.dotVec3(edge1, pvec);
- if (det < EPSILON) {
- return null;
- }
-
- const tvec = math.subVec3(origin, a, tempVec3d);
- const u = math.dotVec3(tvec, pvec);
- if (u < 0 || u > det) {
- return null;
- }
-
- const qvec = math.cross3Vec3(tvec, edge1, tempVec3e);
- const v = math.dotVec3(dir, qvec);
- if (v < 0 || u + v > det) {
- return null;
- }
-
- const t = math.dotVec3(edge2, qvec) / det;
- isect[0] = origin[0] + t * dir[0];
- isect[1] = origin[1] + t * dir[1];
- isect[2] = origin[2] + t * dir[2];
-
- return isect;
- };
- })();
-
- /**
- * Finds the intersection of a 3D ray with a plane defined by 3 points.
- *
- * @private
- */
- math.rayPlaneIntersect = (function () {
-
- const tempVec3 = new Float32Array(3);
- const tempVec3b = new Float32Array(3);
- const tempVec3c = new Float32Array(3);
- const tempVec3d = new Float32Array(3);
-
- return function (origin, dir, a, b, c, isect) {
-
- isect = isect || math.vec3();
-
- dir = math.normalizeVec3(dir, tempVec3);
-
- const edge1 = math.subVec3(b, a, tempVec3b);
- const edge2 = math.subVec3(c, a, tempVec3c);
-
- const n = math.cross3Vec3(edge1, edge2, tempVec3d);
- math.normalizeVec3(n, n);
-
- const d = -math.dotVec3(a, n);
-
- const t = -(math.dotVec3(origin, n) + d) / math.dotVec3(dir, n);
-
- isect[0] = origin[0] + t * dir[0];
- isect[1] = origin[1] + t * dir[1];
- isect[2] = origin[2] + t * dir[2];
-
- return isect;
- };
- })();
-
- /**
- * Gets barycentric coordinates from cartesian coordinates within a triangle.
- * Gets barycentric coordinates from cartesian coordinates within a triangle.
- *
- * @private
- */
- math.cartesianToBarycentric = (function () {
-
- const tempVec3 = new Float32Array(3);
- const tempVec3b = new Float32Array(3);
- const tempVec3c = new Float32Array(3);
-
- return function (cartesian, a, b, c, dest) {
-
- const v0 = math.subVec3(c, a, tempVec3);
- const v1 = math.subVec3(b, a, tempVec3b);
- const v2 = math.subVec3(cartesian, a, tempVec3c);
-
- const dot00 = math.dotVec3(v0, v0);
- const dot01 = math.dotVec3(v0, v1);
- const dot02 = math.dotVec3(v0, v2);
- const dot11 = math.dotVec3(v1, v1);
- const dot12 = math.dotVec3(v1, v2);
-
- const denom = ( dot00 * dot11 - dot01 * dot01 );
-
- // Colinear or singular triangle
-
- if (denom === 0) {
-
- // Arbitrary location outside of triangle
-
- return null;
- }
-
- const invDenom = 1 / denom;
-
- const u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;
- const v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;
-
- dest[0] = 1 - u - v;
- dest[1] = v;
- dest[2] = u;
-
- return dest;
- };
- })();
-
- /**
- * Returns true if the given barycentric coordinates are within their triangle.
- *
- * @private
- */
- math.barycentricInsideTriangle = function (bary) {
-
- const v = bary[1];
- const u = bary[2];
-
- return (u >= 0) && (v >= 0) && (u + v < 1);
- };
-
- /**
- * Gets cartesian coordinates from barycentric coordinates within a triangle.
- *
- * @private
- */
- math.barycentricToCartesian = function (bary, a, b, c, cartesian) {
-
- cartesian = cartesian || math.vec3();
-
- const u = bary[0];
- const v = bary[1];
- const w = bary[2];
-
- cartesian[0] = a[0] * u + b[0] * v + c[0] * w;
- cartesian[1] = a[1] * u + b[1] * v + c[1] * w;
- cartesian[2] = a[2] * u + b[2] * v + c[2] * w;
-
- return cartesian;
- };
-
- /**
- * Given geometry defined as an array of positions, optional normals, option uv and an array of indices, returns
- * modified arrays that have duplicate vertices removed.
- *
- * Note: does not work well when co-incident vertices have same positions but different normals and UVs.
- *
- * @param positions
- * @param normals
- * @param uv
- * @param indices
- * @returns {{positions: Array, indices: Array}}
- * @private
- */
- math.mergeVertices = function (positions, normals, uv, indices) {
- const positionsMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique)
- const indicesLookup = [];
- const uniquePositions = [];
- const uniqueNormals = normals ? [] : null;
- const uniqueUV = uv ? [] : null;
- const indices2 = [];
- let vx;
- let vy;
- let vz;
- let key;
- const precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001
- const precision = Math.pow(10, precisionPoints);
- let i;
- let len;
- let uvi = 0;
- for (i = 0, len = positions.length; i < len; i += 3) {
- vx = positions[i];
- vy = positions[i + 1];
- vz = positions[i + 2];
- key = Math.round(vx * precision) + '_' + Math.round(vy * precision) + '_' + Math.round(vz * precision);
- if (positionsMap[key] === undefined) {
- positionsMap[key] = uniquePositions.length / 3;
- uniquePositions.push(vx);
- uniquePositions.push(vy);
- uniquePositions.push(vz);
- if (normals) {
- uniqueNormals.push(normals[i]);
- uniqueNormals.push(normals[i + 1]);
- uniqueNormals.push(normals[i + 2]);
- }
- if (uv) {
- uniqueUV.push(uv[uvi]);
- uniqueUV.push(uv[uvi + 1]);
- }
- }
- indicesLookup[i / 3] = positionsMap[key];
- uvi += 2;
- }
- for (i = 0, len = indices.length; i < len; i++) {
- indices2[i] = indicesLookup[indices[i]];
- }
- const result = {
- positions: uniquePositions,
- indices: indices2
- };
- if (uniqueNormals) {
- result.normals = uniqueNormals;
- }
- if (uniqueUV) {
- result.uv = uniqueUV;
-
- }
- return result;
- }
-
-
-})();
\ No newline at end of file
diff --git a/src/math/mathGeometryBuild.js b/src/math/mathGeometryBuild.js
deleted file mode 100644
index cccbaa99c..000000000
--- a/src/math/mathGeometryBuild.js
+++ /dev/null
@@ -1,361 +0,0 @@
-/**
- * Boundary math functions.
- */
-(function () {
-
- "use strict";
-
- const math = xeogl.math;
-
- /**
- * Builds normal vectors from positions and indices.
- *
- * @private
- */
- math.buildNormals = (function () {
-
- const a = math.vec3();
- const b = math.vec3();
- const c = math.vec3();
- const ab = math.vec3();
- const ac = math.vec3();
- const crossVec = math.vec3();
-
- return function (positions, indices, normals) {
-
- let i;
- let len;
- const nvecs = new Array(positions.length / 3);
- let j0;
- let j1;
- let j2;
-
- for (i = 0, len = indices.length; i < len; i += 3) {
-
- j0 = indices[i];
- j1 = indices[i + 1];
- j2 = indices[i + 2];
-
- a[0] = positions[j0 * 3];
- a[1] = positions[j0 * 3 + 1];
- a[2] = positions[j0 * 3 + 2];
-
- b[0] = positions[j1 * 3];
- b[1] = positions[j1 * 3 + 1];
- b[2] = positions[j1 * 3 + 2];
-
- c[0] = positions[j2 * 3];
- c[1] = positions[j2 * 3 + 1];
- c[2] = positions[j2 * 3 + 2];
-
- math.subVec3(b, a, ab);
- math.subVec3(c, a, ac);
-
- const normVec = math.vec3();
-
- math.normalizeVec3(math.cross3Vec3(ab, ac, crossVec), normVec);
-
- if (!nvecs[j0]) {
- nvecs[j0] = [];
- }
- if (!nvecs[j1]) {
- nvecs[j1] = [];
- }
- if (!nvecs[j2]) {
- nvecs[j2] = [];
- }
-
- nvecs[j0].push(normVec);
- nvecs[j1].push(normVec);
- nvecs[j2].push(normVec);
- }
-
- normals = (normals && normals.length === positions.length) ? normals : new Float32Array(positions.length);
-
- let count;
- let x;
- let y;
- let z;
-
- for (i = 0, len = nvecs.length; i < len; i++) { // Now go through and average out everything
-
- count = nvecs[i].length;
-
- x = 0;
- y = 0;
- z = 0;
-
- for (let j = 0; j < count; j++) {
- x += nvecs[i][j][0];
- y += nvecs[i][j][1];
- z += nvecs[i][j][2];
- }
-
- normals[i * 3] = (x / count);
- normals[i * 3 + 1] = (y / count);
- normals[i * 3 + 2] = (z / count);
- }
-
- return normals;
- };
- })();
-
- /**
- * Builds vertex tangent vectors from positions, UVs and indices.
- *
- * @private
- */
- math.buildTangents = (function () {
-
- const tempVec3 = new Float32Array(3);
- const tempVec3b = new Float32Array(3);
- const tempVec3c = new Float32Array(3);
- const tempVec3d = new Float32Array(3);
- const tempVec3e = new Float32Array(3);
- const tempVec3f = new Float32Array(3);
- const tempVec3g = new Float32Array(3);
-
- return function (positions, indices, uv) {
-
- const tangents = new Float32Array(positions.length);
-
- // The vertex arrays needs to be calculated
- // before the calculation of the tangents
-
- for (let location = 0; location < indices.length; location += 3) {
-
- // Recontructing each vertex and UV coordinate into the respective vectors
-
- let index = indices[location];
-
- const v0 = positions.subarray(index * 3, index * 3 + 3);
- const uv0 = uv.subarray(index * 2, index * 2 + 2);
-
- index = indices[location + 1];
-
- const v1 = positions.subarray(index * 3, index * 3 + 3);
- const uv1 = uv.subarray(index * 2, index * 2 + 2);
-
- index = indices[location + 2];
-
- const v2 = positions.subarray(index * 3, index * 3 + 3);
- const uv2 = uv.subarray(index * 2, index * 2 + 2);
-
- const deltaPos1 = math.subVec3(v1, v0, tempVec3);
- const deltaPos2 = math.subVec3(v2, v0, tempVec3b);
-
- const deltaUV1 = math.subVec2(uv1, uv0, tempVec3c);
- const deltaUV2 = math.subVec2(uv2, uv0, tempVec3d);
-
- const r = 1 / ((deltaUV1[0] * deltaUV2[1]) - (deltaUV1[1] * deltaUV2[0]));
-
- const tangent = math.mulVec3Scalar(
- math.subVec3(
- math.mulVec3Scalar(deltaPos1, deltaUV2[1], tempVec3e),
- math.mulVec3Scalar(deltaPos2, deltaUV1[1], tempVec3f),
- tempVec3g
- ),
- r,
- tempVec3f
- );
-
- // Average the value of the vectors
-
- let addTo;
-
- for (let v = 0; v < 3; v++) {
- addTo = indices[location + v] * 3;
- tangents[addTo] += tangent[0];
- tangents[addTo + 1] += tangent[1];
- tangents[addTo + 2] += tangent[2];
- }
- }
-
- return tangents;
- };
- })();
-
- /**
- * Builds vertex and index arrays needed by color-indexed triangle picking.
- *
- * @private
- */
- math.buildPickTriangles = function (positions, indices, quantized) {
-
- const numIndices = indices.length;
- const pickPositions = quantized ? new Uint16Array(numIndices * 9) : new Float32Array(numIndices * 9);
- const pickColors = new Uint8Array(numIndices * 12);
- let primIndex = 0;
- let vi;// Positions array index
- let pvi = 0;// Picking positions array index
- let pci = 0; // Picking color array index
-
- // Triangle indices
- let i;
- let r;
- let g;
- let b;
- let a;
-
- for (let location = 0; location < numIndices; location += 3) {
-
- // Primitive-indexed triangle pick color
-
- a = (primIndex >> 24 & 0xFF);
- b = (primIndex >> 16 & 0xFF);
- g = (primIndex >> 8 & 0xFF);
- r = (primIndex & 0xFF);
-
- // A
-
- i = indices[location];
- vi = i * 3;
-
- pickPositions[pvi++] = positions[vi];
- pickPositions[pvi++] = positions[vi + 1];
- pickPositions[pvi++] = positions[vi + 2];
-
- pickColors[pci++] = r;
- pickColors[pci++] = g;
- pickColors[pci++] = b;
- pickColors[pci++] = a;
-
- // B
-
- i = indices[location + 1];
- vi = i * 3;
-
- pickPositions[pvi++] = positions[vi];
- pickPositions[pvi++] = positions[vi + 1];
- pickPositions[pvi++] = positions[vi + 2];
-
- pickColors[pci++] = r;
- pickColors[pci++] = g;
- pickColors[pci++] = b;
- pickColors[pci++] = a;
-
- // C
-
- i = indices[location + 2];
- vi = i * 3;
-
- pickPositions[pvi++] = positions[vi];
- pickPositions[pvi++] = positions[vi + 1];
- pickPositions[pvi++] = positions[vi + 2];
-
- pickColors[pci++] = r;
- pickColors[pci++] = g;
- pickColors[pci++] = b;
- pickColors[pci++] = a;
-
- primIndex++;
- }
-
- return {
- positions: pickPositions,
- colors: pickColors
- };
- };
-
- /**
- * Converts surface-perpendicular face normals to vertex normals. Assumes that the mesh contains disjoint triangles
- * that don't share vertex array elements. Works by finding groups of vertices that have the same location and
- * averaging their normal vectors.
- *
- * @returns {{positions: Array, normals: *}}
- */
- math.faceToVertexNormals = function (positions, normals, options) {
- options = options || {};
- const smoothNormalsAngleThreshold = options.smoothNormalsAngleThreshold || 20;
- const vertexMap = {};
- const vertexNormals = [];
- const vertexNormalAccum = {};
- let acc;
- let vx;
- let vy;
- let vz;
- let key;
- const precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001
- const precision = Math.pow(10, precisionPoints);
- let posi;
- let i;
- let j;
- let len;
- let a;
- let b;
- let c;
-
- for (i = 0, len = positions.length; i < len; i += 3) {
-
- posi = i / 3;
-
- vx = positions[i];
- vy = positions[i + 1];
- vz = positions[i + 2];
-
- key = Math.round(vx * precision) + '_' + Math.round(vy * precision) + '_' + Math.round(vz * precision);
-
- if (vertexMap[key] === undefined) {
- vertexMap[key] = [posi];
- } else {
- vertexMap[key].push(posi);
- }
-
- const normal = math.normalizeVec3([normals[i], normals[i + 1], normals[i + 2]]);
-
- vertexNormals[posi] = normal;
-
- acc = math.vec4([normal[0], normal[1], normal[2], 1]);
-
- vertexNormalAccum[posi] = acc;
- }
-
- for (key in vertexMap) {
-
- if (vertexMap.hasOwnProperty(key)) {
-
- const vertices = vertexMap[key];
- const numVerts = vertices.length;
-
- for (i = 0; i < numVerts; i++) {
-
- const ii = vertices[i];
-
- acc = vertexNormalAccum[ii];
-
- for (j = 0; j < numVerts; j++) {
-
- if (i === j) {
- continue;
- }
-
- const jj = vertices[j];
-
- a = vertexNormals[ii];
- b = vertexNormals[jj];
-
- const angle = Math.abs(math.angleVec3(a, b) / math.DEGTORAD);
-
- if (angle < smoothNormalsAngleThreshold) {
-
- acc[0] += b[0];
- acc[1] += b[1];
- acc[2] += b[2];
- acc[3] += 1.0;
- }
- }
- }
- }
- }
-
- for (i = 0, len = normals.length; i < len; i += 3) {
-
- acc = vertexNormalAccum[i / 3];
-
- normals[i + 0] = acc[0] / acc[3];
- normals[i + 1] = acc[1] / acc[3];
- normals[i + 2] = acc[2] / acc[3];
-
- }
- };
-}());
\ No newline at end of file
diff --git a/src/math/mathKDTree.js b/src/math/mathKDTree.js
deleted file mode 100644
index cf2fc73a1..000000000
--- a/src/math/mathKDTree.js
+++ /dev/null
@@ -1,127 +0,0 @@
-/**
- * KD-tree functions
- */
-(function () {
-
- "use strict";
-
- const KD_TREE_MAX_DEPTH = 10;
- const KD_TREE_MIN_TRIANGLES = 20;
-
- const math = xeogl.math;
-
- /**
- * Returns a KD-tree that contains the triangles of the given mesh
- *
- * @private
- */
- math.buildKDTree = function (indices, positions) {
- const numTris = indices.length / 3;
- const triangles = new Array(numTris);
- for (let i = 0; i < numTris; ++i) {
- triangles[i] = i;
- }
- return buildNode(triangles, indices, positions, 0);
- };
-
- const dimLength = new Float32Array();
-
- function buildNode(triangles, indices, positions, depth) {
-
- const aabb = new Float32Array(6);
-
- const node = {
- triangles: null,
- left: null,
- right: null,
- leaf: false,
- splitDim: 0,
- aabb: aabb
- };
-
- aabb[0] = aabb[1] = aabb[2] = Number.POSITIVE_INFINITY;
- aabb[3] = aabb[4] = aabb[5] = Number.NEGATIVE_INFINITY;
-
- let t, i, len;
-
- for (t = 0, len = triangles.length; t < len; ++t) {
- var ii = triangles[t] * 3;
- for (let j = 0; j < 3; ++j) {
- const pi = indices[ii + j] * 3;
- if (positions[pi] < aabb[0]) {
- aabb[0] = positions[pi]
- }
- if (positions[pi] > aabb[3]) {
- aabb[3] = positions[pi]
- }
- if (positions[pi + 1] < aabb[1]) {
- aabb[1] = positions[pi + 1]
- }
- if (positions[pi + 1] > aabb[4]) {
- aabb[4] = positions[pi + 1]
- }
- if (positions[pi + 2] < aabb[2]) {
- aabb[2] = positions[pi + 2]
- }
- if (positions[pi + 2] > aabb[5]) {
- aabb[5] = positions[pi + 2]
- }
- }
- }
-
- if (triangles.length < KD_TREE_MIN_TRIANGLES || depth > KD_TREE_MAX_DEPTH) {
- node.triangles = triangles;
- node.leaf = true;
- return node;
- }
-
- dimLength[0] = aabb[3] - aabb[0];
- dimLength[1] = aabb[4] - aabb[1];
- dimLength[2] = aabb[5] - aabb[2];
-
- let dim = 0;
-
- if (dimLength[1] > dimLength[dim]) {
- dim = 1;
- }
-
- if (dimLength[2] > dimLength[dim]) {
- dim = 2;
- }
-
- node.splitDim = dim;
-
- const mid = (aabb[dim] + aabb[dim + 3]) / 2;
- const left = new Array(triangles.length);
- let numLeft = 0;
- const right = new Array(triangles.length);
- let numRight = 0;
-
- for (t = 0, len = triangles.length; t < len; ++t) {
-
- var ii = triangles[t] * 3;
- const i0 = indices[ii];
- const i1 = indices[ii + 1];
- const i2 = indices[ii + 2];
-
- const pi0 = i0 * 3;
- const pi1 = i1 * 3;
- const pi2 = i2 * 3;
-
- if (positions[pi0 + dim] <= mid || positions[pi1 + dim] <= mid || positions[pi2 + dim] <= mid) {
- left[numLeft++] = triangles[t];
- } else {
- right[numRight++] = triangles[t];
- }
- }
-
- left.length = numLeft;
- right.length = numRight;
-
- node.left = buildNode(left, indices, positions, depth + 1);
- node.right = buildNode(right, indices, positions, depth + 1);
-
- return node;
- }
-
-})();
\ No newline at end of file
diff --git a/src/math/mathRays.js b/src/math/mathRays.js
deleted file mode 100644
index 63848162e..000000000
--- a/src/math/mathRays.js
+++ /dev/null
@@ -1,129 +0,0 @@
-/**
- * Ray casting support functions.
- */
-(function () {
-
- "use strict";
-
- const math = xeogl.math;
-
- /**
- Transforms a Canvas-space position into a World-space ray, in the context of a Camera.
- @method canvasPosToWorldRay
- @static
- @param {Camera} camera The Camera.
- @param {Float32Array} canvasPos The Canvas-space position.
- @param {Float32Array} worldRayOrigin The World-space ray origin.
- @param {Float32Array} worldRayDir The World-space ray direction.
- */
- math.canvasPosToWorldRay = (function () {
-
- const tempMat4b = math.mat4();
- const tempMat4c = math.mat4();
- const tempVec4a = math.vec4();
- const tempVec4b = math.vec4();
- const tempVec4c = math.vec4();
- const tempVec4d = math.vec4();
-
- return function (camera, canvasPos, worldRayOrigin, worldRayDir) {
-
- const canvas = camera.scene.canvas.canvas;
-
- const viewMat = camera.viewMatrix;
- const projMat = camera.projection === "ortho" ? camera.ortho.matrix : camera.perspective.matrix;
-
- const pvMat = math.mulMat4(projMat, viewMat, tempMat4b);
- const pvMatInverse = math.inverseMat4(pvMat, tempMat4c);
-
- // Calculate clip space coordinates, which will be in range
- // of x=[-1..1] and y=[-1..1], with y=(+1) at top
-
- const canvasWidth = canvas.width;
- const canvasHeight = canvas.height;
-
- const clipX = (canvasPos[0] - canvasWidth / 2) / (canvasWidth / 2); // Calculate clip space coordinates
- const clipY = -(canvasPos[1] - canvasHeight / 2) / (canvasHeight / 2);
-
- tempVec4a[0] = clipX;
- tempVec4a[1] = clipY;
- tempVec4a[2] = -1;
- tempVec4a[3] = 1;
-
- math.transformVec4(pvMatInverse, tempVec4a, tempVec4b);
- math.mulVec4Scalar(tempVec4b, 1 / tempVec4b[3]);
-
- tempVec4c[0] = clipX;
- tempVec4c[1] = clipY;
- tempVec4c[2] = 1;
- tempVec4c[3] = 1;
-
- math.transformVec4(pvMatInverse, tempVec4c, tempVec4d);
- math.mulVec4Scalar(tempVec4d, 1 / tempVec4d[3]);
-
- worldRayOrigin[0] = tempVec4d[0];
- worldRayOrigin[1] = tempVec4d[1];
- worldRayOrigin[2] = tempVec4d[2];
-
- math.subVec3(tempVec4d, tempVec4b, worldRayDir);
-
- math.normalizeVec3(worldRayDir);
- };
- })();
-
- /**
- Transforms a Canvas-space position to a Mesh's Local-space coordinate system, in the context of a Camera.
- @method canvasPosToLocalRay
- @static
- @param {Camera} camera The Camera.
- @param {Mesh} mesh The Mesh.
- @param {Float32Array} canvasPos The Canvas-space position.
- @param {Float32Array} localRayOrigin The Local-space ray origin.
- @param {Float32Array} localRayDir The Local-space ray direction.
- */
- math.canvasPosToLocalRay = (function () {
-
- const worldRayOrigin = math.vec3();
- const worldRayDir = math.vec3();
-
- return function (camera, mesh, canvasPos, localRayOrigin, localRayDir) {
- math.canvasPosToWorldRay(camera, canvasPos, worldRayOrigin, worldRayDir);
- math.worldRayToLocalRay(mesh, worldRayOrigin, worldRayDir, localRayOrigin, localRayDir);
- };
- })();
-
- /**
- Transforms a ray from World-space to a Mesh's Local-space coordinate system.
- @method worldRayToLocalRay
- @static
- @param {Mesh} mesh The Mesh.
- @param {Float32Array} worldRayOrigin The World-space ray origin.
- @param {Float32Array} worldRayDir The World-space ray direction.
- @param {Float32Array} localRayOrigin The Local-space ray origin.
- @param {Float32Array} localRayDir The Local-space ray direction.
- */
- math.worldRayToLocalRay = (function () {
-
- const tempMat4 = math.mat4();
- const tempVec4a = math.vec4();
- const tempVec4b = math.vec4();
-
- return function (mesh, worldRayOrigin, worldRayDir, localRayOrigin, localRayDir) {
-
- const modelMat = mesh.worldMatrix || mesh.matrix;
- const modelMatInverse = math.inverseMat4(modelMat, tempMat4);
-
- tempVec4a[0] = worldRayOrigin[0];
- tempVec4a[1] = worldRayOrigin[1];
- tempVec4a[2] = worldRayOrigin[2];
- tempVec4a[3] = 1;
-
- math.transformVec4(modelMatInverse, tempVec4a, tempVec4b);
-
- localRayOrigin[0] = tempVec4b[0];
- localRayOrigin[1] = tempVec4b[1];
- localRayOrigin[2] = tempVec4b[2];
-
- math.transformVec3(modelMatInverse, worldRayDir, localRayDir);
- };
- })();
-})();
\ No newline at end of file
diff --git a/src/models/model.js b/src/models/model.js
index 9b6fb2946..b6304b762 100644
--- a/src/models/model.js
+++ b/src/models/model.js
@@ -14,8 +14,7 @@
@module xeogl
@submodule models
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}} - creates this ModelModel in the default
- {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
+ @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 scene, generated automatically when omitted.
@param [cfg.meta] {String:Object} Optional map of user-defined metadata.
@@ -44,294 +43,291 @@
@extends Group
*/
-(function () {
+import {core} from "./../core.js";
+import {utils} from '../utils.js';
+import {Group} from "../objects/group.js";
- "use strict";
+const type = "xeogl.Model";
- xeogl.Model = xeogl.Group.extend({
+class Model extends Group {
- /**
- JavaScript class name for this Component.
+ /**
+ JavaScript class name for this Component.
- @property type
- @type String
- @final
- */
- type: "xeogl.Model",
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
- _init: function (cfg) {
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return type;
+ }
- /**
- All contained {{#crossLink "Components"}}{{/crossLink}}, mapped to their IDs.
+ init(cfg) {
+
+ /**
+ All contained {{#crossLink "Components"}}{{/crossLink}}, mapped to their IDs.
- @property components
- @type {{String:Component}}
- */
- this.components = {};
+ @property components
+ @type {{String:Component}}
+ */
+ this.components = {};
- /**
- Number of contained {{#crossLink "Components"}}{{/crossLink}}.
+ /**
+ Number of contained {{#crossLink "Components"}}{{/crossLink}}.
- @property numComponents
- @type Number
- */
- this.numComponents = 0;
+ @property numComponents
+ @type Number
+ */
+ this.numComponents = 0;
- /**
- A map of maps; for each contained {{#crossLink "Component"}}{{/crossLink}} type,
- a map to IDs to {{#crossLink "Component"}}{{/crossLink}} instances, eg.
+ /**
+ A map of maps; for each contained {{#crossLink "Component"}}{{/crossLink}} type,
+ a map to IDs to {{#crossLink "Component"}}{{/crossLink}} instances, eg.
- ````
- "xeogl.Geometry": {
+ ````
+ "xeogl.Geometry": {
"alpha": ,
"beta":
},
- "xeogl.Rotate": {
+ "xeogl.Rotate": {
"charlie": ,
"delta": ,
"echo": ,
},
- //...
- ````
-
- @property types
- @type {String:{String:xeogl.Component}}
- */
- this.types = {};
-
- /**
- All contained {{#crossLink "Object"}}Objects{{/crossLink}}, mapped to their IDs.
-
- @property objects
- @final
- @type {{String:Object}}
- */
- this.objects = {};
-
- /**
- {{#crossLink "Object"}}Objects{{/crossLink}} in this Model that have GUIDs, mapped to their GUIDs.
-
- Each Object is registered in this map when its {{#crossLink "Object/guid:property"}}{{/crossLink}} is
- assigned a value.
-
- @property guidObjects
- @final
- @type {{String:Object}}
- */
- this.guidObjects = {};
-
- /**
- All contained {{#crossLink "Mesh"}}Meshes{{/crossLink}}, mapped to their IDs.
-
- @property meshes
- @final
- @type {String:xeogl.Mesh}
- */
- this.meshes = {};
-
- /**
- {{#crossLink "Object"}}Objects{{/crossLink}} in this Model that have entity types, mapped to their IDs.
-
- Each Object is registered in this map when its {{#crossLink "Object/entityType:property"}}{{/crossLink}} is
- set to value.
-
- @property entities
- @final
- @type {{String:Object}}
- */
- this.entities = {};
-
- /**
- For each entity type, a map of IDs to {{#crossLink "Object"}}Objects{{/crossLink}} of that entity type.
-
- Each Object is registered in this map when its {{#crossLink "Object/entityType:property"}}{{/crossLink}} is
- assigned a value.
-
- @property entityTypes
- @final
- @type {String:{String:xeogl.Component}}
- */
- this.entityTypes = {};
-
- /**
- Lazy-regenerated ID lists.
- */
- this._objectGUIDs = null;
- this._entityIds = null;
-
- // xeogl.Model overrides xeogl.Group / xeogl.Object state properties, (eg. visible, ghosted etc)
- // and those redefined properties are being set here through the super constructor.
-
- this._super(cfg); // Call xeogl.Group._init()
-
- this.scene._modelCreated(this);
- },
-
- _addComponent: function (component) {
- let componentId;
- let types;
- if (xeogl._isNumeric(component) || xeogl._isString(component)) { // Component ID
- component = this.scene.components[component];
- if (!component) {
- this.warn("Component not found: " + xeogl._inQuotes(component));
- return;
- }
- } else if (xeogl._isObject(component)) { // Component config
- const type = component.type || "xeogl.Component";
- if (!xeogl._isComponentType(type)) {
- this.error("Not a xeogl component type: " + type);
- return;
- }
- component = new window[type](this.scene, component);
- }
- if (component.scene !== this.scene) { // Component in wrong Scene
- this.error("Attempted to add component from different xeogl.Scene: " + xeogl._inQuotes(component.id));
+ //...
+ ````
+
+ @property types
+ @type {String:{String:xeogl.Component}}
+ */
+ this.types = {};
+
+ /**
+ All contained {{#crossLink "Object"}}Objects{{/crossLink}}, mapped to their IDs.
+
+ @property objects
+ @final
+ @type {{String:Object}}
+ */
+ this.objects = {};
+
+ /**
+ {{#crossLink "Object"}}Objects{{/crossLink}} in this Model that have GUIDs, mapped to their GUIDs.
+
+ Each Object is registered in this map when its {{#crossLink "Object/guid:property"}}{{/crossLink}} is
+ assigned a value.
+
+ @property guidObjects
+ @final
+ @type {{String:Object}}
+ */
+ this.guidObjects = {};
+
+ /**
+ All contained {{#crossLink "Mesh"}}Meshes{{/crossLink}}, mapped to their IDs.
+
+ @property meshes
+ @final
+ @type {String:xeogl.Mesh}
+ */
+ this.meshes = {};
+
+ /**
+ {{#crossLink "Object"}}Objects{{/crossLink}} in this Model that have entity types, mapped to their IDs.
+
+ Each Object is registered in this map when its {{#crossLink "Object/entityType:property"}}{{/crossLink}} is
+ set to value.
+
+ @property entities
+ @final
+ @type {{String:Object}}
+ */
+ this.entities = {};
+
+ /**
+ For each entity type, a map of IDs to {{#crossLink "Object"}}Objects{{/crossLink}} of that entity type.
+
+ Each Object is registered in this map when its {{#crossLink "Object/entityType:property"}}{{/crossLink}} is
+ assigned a value.
+
+ @property entityTypes
+ @final
+ @type {String:{String:xeogl.Component}}
+ */
+ this.entityTypes = {};
+
+ /**
+ Lazy-regenerated ID lists.
+ */
+ this._objectGUIDs = null;
+ this._entityIds = null;
+
+ // xeogl.Model overrides xeogl.Group / xeogl.Object state properties, (eg. visible, ghosted etc)
+ // and those redefined properties are being set here through the super constructor.
+
+ super.init(cfg); // Call xeogl.Group._init()
+
+ this.scene._modelCreated(this);
+ }
+
+ _addComponent(component) {
+ let componentId;
+ let types;
+ if (utils.isNumeric(component) || utils.isString(component)) { // Component ID
+ component = this.scene.components[component];
+ if (!component) {
+ this.warn("Component not found: " + utils.inQuotes(component));
return;
}
- if (this.components[component.id]) { // Component already in this Model
+ } else if (utils.isObject(component)) { // Component config
+ const type = component.type || "xeogl.Component";
+ if (!utils.isComponentType(type)) {
+ this.error("Not a xeogl component type: " + type);
return;
}
- if (component.model && component.model.id !== this.id) { // Component in other Model
- component.model._removeComponent(component); // Transferring to this Model
- }
- this.components[component.id] = component;
- types = this.types[component.type];
- if (!types) {
- types = this.types[component.type] = {};
- }
- types[component.id] = component;
- if (component.isType("xeogl.Object")) {
- const object = component;
- this.objects[object.id] = object;
- if (object.entityType) {
- this.entities[object.id] = object;
- let objectsOfType = this.entityTypes[object.entityType];
- if (!objectsOfType) {
- objectsOfType = {};
- this.entityTypes[object.entityType] = objectsOfType;
- }
- objectsOfType[object.id] = object;
- this._entityIds = null; // Lazy regenerate
- this._entityTypeIds = null; // Lazy regenerate
- }
- if (object.guid) {
- this.guidObjects[object.id] = object;
- this._objectGUIDs = null; // To lazy-rebuild
- }
- if (component.isType("xeogl.Mesh")) {
- this.meshes[component.id] = component;
- }
- }
- this.numComponents++;
- component._addedToModel(this);
- return component;
- },
-
- _removeComponent: function(component) {
- const id = component.id;
- delete this.components[id];
- delete this.meshes[id];
- delete this.objects[id];
- if (component.entityType) {
- delete this.entities[id];
- const objectsOfType = this.entityTypes[component.entityType];
- if (objectsOfType) {
- delete objectsOfType[id];
+ component = new window[type](this.scene, component);
+ }
+ if (component.scene !== this.scene) { // Component in wrong Scene
+ this.error("Attempted to add component from different xeogl.Scene: " + utils.inQuotes(component.id));
+ return;
+ }
+ if (this.components[component.id]) { // Component already in this Model
+ return;
+ }
+ if (component.model && component.model.id !== this.id) { // Component in other Model
+ component.model._removeComponent(component); // Transferring to this Model
+ }
+ this.components[component.id] = component;
+ types = this.types[component.type];
+ if (!types) {
+ types = this.types[component.type] = {};
+ }
+ types[component.id] = component;
+ if (component.isType("xeogl.Object")) {
+ const object = component;
+ this.objects[object.id] = object;
+ if (object.entityType) {
+ this.entities[object.id] = object;
+ let objectsOfType = this.entityTypes[object.entityType];
+ if (!objectsOfType) {
+ objectsOfType = {};
+ this.entityTypes[object.entityType] = objectsOfType;
}
+ objectsOfType[object.id] = object;
this._entityIds = null; // Lazy regenerate
this._entityTypeIds = null; // Lazy regenerate
}
- if (component.guid) {
- delete this.guidObjects[component.guid];
+ if (object.guid) {
+ this.guidObjects[object.id] = object;
this._objectGUIDs = null; // To lazy-rebuild
}
- },
-
- /**
- Destroys all {{#crossLink "Component"}}Components{{/crossLink}} in this Model.
- @method clear
- */
- clear: function () {
- // For efficiency, destroy Meshes first to avoid
- // xeogl's automatic default component substitutions
- for (var id in this.meshes) {
- if (this.meshes.hasOwnProperty(id)) {
- this.meshes[id].destroy();
- }
+ if (component.isType("xeogl.Mesh")) {
+ this.meshes[component.id] = component;
}
- for (var id in this.components) {
- if (this.components.hasOwnProperty(id)) {
- this.components[id].destroy(); // Groups in this Model will remove themselves when they're destroyed
- }
+ }
+ this.numComponents++;
+ component._addedToModel(this);
+ return component;
+ }
+
+ _removeComponent(component) {
+ const id = component.id;
+ delete this.components[id];
+ delete this.meshes[id];
+ delete this.objects[id];
+ if (component.entityType) {
+ delete this.entities[id];
+ const objectsOfType = this.entityTypes[component.entityType];
+ if (objectsOfType) {
+ delete objectsOfType[id];
}
- this.components = {};
- this.numComponents = 0;
- this.types = {};
- this.objects = {};
- this.meshes = {};
- this.entities = {};
- },
-
- _props: {
-
- /**
- Convenience array of entity type IDs in {{#crossLink "Model/entityTypes:property"}}{{/crossLink}}.
- @property entityTypeIds
- @final
- @type {Array of String}
- */
- objectGUIDs: {
- get: function () {
- if (!this._objectGUIDs) {
- this._objectGUIDs = Object.keys(this.guidObjects);
- }
- return this._objectGUIDs;
- }
- },
-
- /**
- Convenience array of entity type IDs in {{#crossLink "Model/entityTypes:property"}}{{/crossLink}}.
- @property entityTypeIds
- @final
- @type {Array of String}
- */
- entityTypeIds: {
- get: function () {
- if (!this._entityTypeIds) {
- this._entityTypeIds = Object.keys(this.entityTypes);
- }
- return this._entityTypeIds;
- }
- },
-
- /**
- Convenience array of IDs in {{#crossLink "Model/entities:property"}}{{/crossLink}}.
- @property entityIds
- @final
- @type {Array of String}
- */
- entityIds: {
- get: function () {
- if (!this._entityIds) {
- this._entityIds = Object.keys(this.entities);
- }
- return this._entityIds;
- }
+ this._entityIds = null; // Lazy regenerate
+ this._entityTypeIds = null; // Lazy regenerate
+ }
+ if (component.guid) {
+ delete this.guidObjects[component.guid];
+ this._objectGUIDs = null; // To lazy-rebuild
+ }
+ }
+
+ /**
+ Destroys all {{#crossLink "Component"}}Components{{/crossLink}} in this Model.
+ @method clear
+ */
+ clear() {
+ // For efficiency, destroy Meshes first to avoid
+ // xeogl's automatic default component substitutions
+ for (var id in this.meshes) {
+ if (this.meshes.hasOwnProperty(id)) {
+ this.meshes[id].destroy();
}
- },
-
- /**
- * @deprecated
- */
- destroyAll: function () {
- this.clear();
- },
-
- _destroy: function () {
- this._super();
- this.clear();
- this.scene._modelDestroyed(this);
}
- });
-
-})();
\ No newline at end of file
+ for (var id in this.components) {
+ if (this.components.hasOwnProperty(id)) {
+ this.components[id].destroy(); // Groups in this Model will remove themselves when they're destroyed
+ }
+ }
+ this.components = {};
+ this.numComponents = 0;
+ this.types = {};
+ this.objects = {};
+ this.meshes = {};
+ this.entities = {};
+ }
+
+ /**
+ Convenience array of entity type IDs in {{#crossLink "Model/entityTypes:property"}}{{/crossLink}}.
+ @property entityTypeIds
+ @final
+ @type {Array of String}
+ */
+ get objectGUIDs() {
+ if (!this._objectGUIDs) {
+ this._objectGUIDs = Object.keys(this.guidObjects);
+ }
+ return this._objectGUIDs;
+ }
+
+ /**
+ Convenience array of entity type IDs in {{#crossLink "Model/entityTypes:property"}}{{/crossLink}}.
+ @property entityTypeIds
+ @final
+ @type {Array of String}
+ */
+ get entityTypeIds() {
+ if (!this._entityTypeIds) {
+ this._entityTypeIds = Object.keys(this.entityTypes);
+ }
+ return this._entityTypeIds;
+ }
+
+ /**
+ Convenience array of IDs in {{#crossLink "Model/entities:property"}}{{/crossLink}}.
+ @property entityIds
+ @final
+ @type {Array of String}
+ */
+ get entityIds() {
+ if (!this._entityIds) {
+ this._entityIds = Object.keys(this.entities);
+ }
+ return this._entityIds;
+ }
+
+ /**
+ * @deprecated
+ */
+ destroyAll() {
+ this.clear();
+ }
+
+ destroy() {
+ super.destroy();
+ this.clear();
+ this.scene._modelDestroyed(this);
+ }
+}
+
+export{Model};
\ No newline at end of file
diff --git a/src/objects/group.js b/src/objects/group.js
index f0ce0c1fc..e9fb7f5a2 100644
--- a/src/objects/group.js
+++ b/src/objects/group.js
@@ -9,8 +9,7 @@
@module xeogl
@submodule objects
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}{{/crossLink}}.
-
+ @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 scene, generated automatically when omitted.
@param [cfg.meta] {String:Object} Optional map of user-defined metadata.
@@ -43,18 +42,26 @@
{{#crossLink "Object/selected:property"}}{{/crossLink}}, {{#crossLink "Object/colorize:property"}}{{/crossLink}} and {{#crossLink "Object/opacity:property"}}{{/crossLink}}.
@extends Object
*/
-xeogl.Group = xeogl.Object.extend({
+import {Object} from "./object.js";
+
+class Group extends Object{
/**
- JavaScript class name for this xeogl.Group.
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
@property type
@type String
@final
*/
- type: "xeogl.Group",
+ static get type() {
+ return "xeogl.Group";
+ }
- _init: function (cfg) {
- this._super(cfg); // Call xeogl.Object._init()
+ init(cfg) {
+ super.init(cfg);
}
-});
\ No newline at end of file
+}
+
+export {Group};
\ No newline at end of file
diff --git a/src/objects/mesh.js b/src/objects/mesh.js
index 58295a6fb..e392a3641 100644
--- a/src/objects/mesh.js
+++ b/src/objects/mesh.js
@@ -317,7 +317,7 @@
edges: true
});
````
-
+
### Outlining
Outline a Mesh by setting its {{#crossLink "Mesh/outlined:property"}}{{/crossLink}} property true. The Mesh's
@@ -505,7 +505,7 @@
@module xeogl
@submodule objects
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}} - creates this Mesh within xeogl's default {{#crossLink "xeogl/scene:property"}}scene{{/crossLink}} by default.
+ @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.
@@ -562,835 +562,819 @@
The event parameters will be the hit result returned by the {{#crossLink "Scene/pick:method"}}Scene#pick(){{/crossLink}} method.
@event picked
*/
-(function () {
-
- "use strict";
-
- xeogl.Mesh = xeogl.Object.extend({
-
- type: "xeogl.Mesh",
-
- _init: function (cfg) {
-
- this._state = new xeogl.renderer.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: ""
- });
+import {core} from "./../core.js";
+import {math} from '../math/math.js';
+import {Object} 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";
+
+const obb = math.OBB3();
+
+const type = "xeogl.Mesh";
+
+class Mesh extends Object {
+
+ /**
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
+ */
+ static 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._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._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._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();
+ }
+ }
- this._drawRenderer = null;
- this._emphasisFillRenderer = null;
- this._emphasisEdgesRenderer = null;
- this._emphasisVerticesRenderer = null;
- this._pickMeshRenderer = null;
- this._pickTriangleRenderer = null;
+ _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("");
+ }
- 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();
-
- this._super(cfg); // Call xeogl.Object._init()
-
- this.scene._meshCreated(this);
- },
-
- _checkBillboard: function (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: function () {
- this._putRenderers();
- this._makeHash();
- this._drawRenderer = xeogl.renderer.DrawRenderer.get(this);
- this._emphasisFillRenderer = xeogl.renderer.EmphasisFillRenderer.get(this);
- this._emphasisEdgesRenderer = xeogl.renderer.EmphasisEdgesRenderer.get(this);
- this._emphasisVerticesRenderer = xeogl.renderer.EmphasisVerticesRenderer.get(this);
- this._pickMeshRenderer = xeogl.renderer.PickMeshRenderer.get(this);
-
- this._renderer.meshListDirty();
- },
-
- _webglContextRestored: function() {
- if (this._drawRenderer) {
- this._drawRenderer.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();
- }
- },
+ _buildMeshAABB(worldMatrix, aabb) { // TODO: factor out into class member
+ math.transformOBB3(worldMatrix, this._geometry.obb, obb);
+ math.OBB3ToAABB3(obb, aabb);
+ }
- _makeHash: function () {
- 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: (function () {
- const math = xeogl.math;
- const obb = math.OBB3();
- return function (worldMatrix, aabb) { // TODO: factor out into class member
- math.transformOBB3(worldMatrix, this._geometry.obb, obb);
- math.OBB3ToAABB3(obb, aabb);
- };
- })(),
-
- _getSceneHash: function () {
- return (this.scene.gammaInput ? "gi;" : ";") + (this.scene.gammaOutput ? "go" : "");
- },
-
- //--------------------- Rendering ------------------------------------------------------------------------------
-
- _draw: function (frame) {
- if (this._drawRenderer || (this._drawRenderer = xeogl.renderer.DrawRenderer.get(this))) {
- this._drawRenderer.drawMesh(frame, this);
- }
- },
+ _getSceneHash() {
+ return (this.scene.gammaInput ? "gi;" : ";") + (this.scene.gammaOutput ? "go" : "");
+ }
- _drawGhostFill: function (frame) {
- if (this._emphasisFillRenderer || (this._emphasisFillRenderer = xeogl.renderer.EmphasisFillRenderer.get(this))) {
- this._emphasisFillRenderer.drawMesh(frame, this, 0); // 0 == ghost
- }
- },
+ //--------------------- Rendering ------------------------------------------------------------------------------
- _drawGhostEdges: function (frame) {
- if (this._emphasisEdgesRenderer || (this._emphasisEdgesRenderer = xeogl.renderer.EmphasisEdgesRenderer.get(this))) {
- this._emphasisEdgesRenderer.drawMesh(frame, this, 0); // 0 == ghost
- }
- },
+ _draw(frame) {
+ if (this._drawRenderer || (this._drawRenderer = DrawRenderer.get(this))) {
+ this._drawRenderer.drawMesh(frame, this);
+ }
+ }
- _drawGhostVertices: function (frame) {
- if (this._emphasisVerticesRenderer || (this._emphasisVerticesRenderer = xeogl.renderer.EmphasisVerticesRenderer.get(this))) {
- this._emphasisVerticesRenderer.drawMesh(frame, this, 0); // 0 == ghost
- }
- },
+ _drawGhostFill(frame) {
+ if (this._emphasisFillRenderer || (this._emphasisFillRenderer = EmphasisFillRenderer.get(this))) {
+ this._emphasisFillRenderer.drawMesh(frame, this, 0); // 0 == ghost
+ }
+ }
- _drawHighlightFill: function (frame) {
- if (this._emphasisFillRenderer || (this._emphasisFillRenderer = xeogl.renderer.EmphasisFillRenderer.get(this))) {
- this._emphasisFillRenderer.drawMesh(frame, this, 1); // 1 == highlight
- }
- },
+ _drawGhostEdges(frame) {
+ if (this._emphasisEdgesRenderer || (this._emphasisEdgesRenderer = EmphasisEdgesRenderer.get(this))) {
+ this._emphasisEdgesRenderer.drawMesh(frame, this, 0); // 0 == ghost
+ }
+ }
- _drawHighlightEdges: function (frame) {
- if (this._emphasisEdgesRenderer || (this._emphasisEdgesRenderer = xeogl.renderer.EmphasisEdgesRenderer.get(this))) {
- this._emphasisEdgesRenderer.drawMesh(frame, this, 1); // 1 == highlight
- }
- },
+ _drawGhostVertices(frame) {
+ if (this._emphasisVerticesRenderer || (this._emphasisVerticesRenderer = EmphasisVerticesRenderer.get(this))) {
+ this._emphasisVerticesRenderer.drawMesh(frame, this, 0); // 0 == ghost
+ }
+ }
- _drawHighlightVertices: function (frame) {
- if (this._emphasisVerticesRenderer || (this._emphasisVerticesRenderer = xeogl.renderer.EmphasisVerticesRenderer.get(this))) {
- this._emphasisVerticesRenderer.drawMesh(frame, this, 1); // 1 == highlight
- }
- },
+ _drawHighlightFill(frame) {
+ if (this._emphasisFillRenderer || (this._emphasisFillRenderer = EmphasisFillRenderer.get(this))) {
+ this._emphasisFillRenderer.drawMesh(frame, this, 1); // 1 == highlight
+ }
+ }
- _drawSelectedFill: function (frame) {
- if (this._emphasisFillRenderer || (this._emphasisFillRenderer = xeogl.renderer.EmphasisFillRenderer.get(this))) {
- this._emphasisFillRenderer.drawMesh(frame, this, 2); // 2 == selected
- }
- },
+ _drawHighlightEdges(frame) {
+ if (this._emphasisEdgesRenderer || (this._emphasisEdgesRenderer = EmphasisEdgesRenderer.get(this))) {
+ this._emphasisEdgesRenderer.drawMesh(frame, this, 1); // 1 == highlight
+ }
+ }
- _drawSelectedEdges: function (frame) {
- if (this._emphasisEdgesRenderer || (this._emphasisEdgesRenderer = xeogl.renderer.EmphasisEdgesRenderer.get(this))) {
- this._emphasisEdgesRenderer.drawMesh(frame, this, 2); // 2 == selected
- }
- },
+ _drawHighlightVertices(frame) {
+ if (this._emphasisVerticesRenderer || (this._emphasisVerticesRenderer = EmphasisVerticesRenderer.get(this))) {
+ this._emphasisVerticesRenderer.drawMesh(frame, this, 1); // 1 == highlight
+ }
+ }
- _drawSelectedVertices: function (frame) {
- if (this._emphasisVerticesRenderer || (this._emphasisVerticesRenderer = xeogl.renderer.EmphasisVerticesRenderer.get(this))) {
- this._emphasisVerticesRenderer.drawMesh(frame, this, 2); // 2 == selected
- }
- },
+ _drawSelectedFill(frame) {
+ if (this._emphasisFillRenderer || (this._emphasisFillRenderer = EmphasisFillRenderer.get(this))) {
+ this._emphasisFillRenderer.drawMesh(frame, this, 2); // 2 == selected
+ }
+ }
- _drawEdges: function (frame) {
- if (this._emphasisEdgesRenderer || (this._emphasisEdgesRenderer = xeogl.renderer.EmphasisEdgesRenderer.get(this))) {
- this._emphasisEdgesRenderer.drawMesh(frame, this, 3); // 3 == edges
- }
- },
+ _drawSelectedEdges(frame) {
+ if (this._emphasisEdgesRenderer || (this._emphasisEdgesRenderer = EmphasisEdgesRenderer.get(this))) {
+ this._emphasisEdgesRenderer.drawMesh(frame, this, 2); // 2 == selected
+ }
+ }
- _drawShadow: function (frame, light) {
- if (this._shadowRenderer || (this._shadowRenderer = xeogl.renderer.ShadowRenderer.get(this))) {
- this._shadowRenderer.drawMesh(frame, this, light);
- }
- },
+ _drawSelectedVertices(frame) {
+ if (this._emphasisVerticesRenderer || (this._emphasisVerticesRenderer = EmphasisVerticesRenderer.get(this))) {
+ this._emphasisVerticesRenderer.drawMesh(frame, this, 2); // 2 == selected
+ }
+ }
- _drawOutline: function (frame) {
- if (this._shadowRenderer || (this._outlineRenderer = xeogl.renderer.OutlineRenderer.get(this))) {
- this._outlineRenderer.drawMesh(frame, this);
- }
- },
+ _drawEdges(frame) {
+ if (this._emphasisEdgesRenderer || (this._emphasisEdgesRenderer = EmphasisEdgesRenderer.get(this))) {
+ this._emphasisEdgesRenderer.drawMesh(frame, this, 3); // 3 == edges
+ }
+ }
- _pickMesh: function (frame) {
- if (this._pickMeshRenderer || (this._pickMeshRenderer = xeogl.renderer.PickMeshRenderer.get(this))) {
- this._pickMeshRenderer.drawMesh(frame, this);
- }
- },
+ _drawShadow(frame, light) {
+ if (this._shadowRenderer || (this._shadowRenderer = ShadowRenderer.get(this))) {
+ this._shadowRenderer.drawMesh(frame, this, light);
+ }
+ }
- _pickTriangle: function (frame) {
- if (this._pickTriangleRenderer || (this._pickTriangleRenderer = xeogl.renderer.PickTriangleRenderer.get(this))) {
- this._pickTriangleRenderer.drawMesh(frame, this);
- }
- },
+ _drawOutline(frame) {
+ if (this._shadowRenderer || (this._outlineRenderer = OutlineRenderer.get(this))) {
+ this._outlineRenderer.drawMesh(frame, this);
+ }
+ }
- _pickVertex: function (frame) {
- if (this._pickVertexRenderer || (this._pickVertexRenderer = xeogl.renderer.PickVertexRenderer.get(this))) {
- this._pickVertexRenderer.drawMesh(frame, this);
- }
- },
-
- _getOutlineRenderer: function () {
- this._outlineRenderer = xeogl.renderer.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;
- },
+ _pickMesh(frame) {
+ if (this._pickMeshRenderer || (this._pickMeshRenderer = PickMeshRenderer.get(this))) {
+ this._pickMeshRenderer.drawMesh(frame, this);
+ }
+ }
- _putRenderers: function () {
- if (this._drawRenderer) {
- this._drawRenderer.put();
- this._drawRenderer = 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._shadowRenderer) {
- this._shadowRenderer.put();
- this._shadowRenderer = 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;
- }
- },
-
- _props: {
-
- /**
- 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
- */
- worldPositions: {
- get: function () {
- if (this._worldPositionsDirty) {
- const positions = this._geometry.positions;
- if (!this._worldPositions) {
- this._worldPositions = new Float32Array(positions.length);
- }
- xeogl.math.transformPositions3(this.worldMatrix, positions, this._worldPositions);
- this._worldPositionsDirty = false;
- }
- return this._worldPositions;
- },
- set: function (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
- */
- geometry: {
- get: function () {
- return this._geometry;
- }
- },
-
- /**
- Defines appearance when rendering normally, ie. when not ghosted, highlighted or selected.
-
- @property material
- @type Material
- @final
- */
- material: {
- get: function () {
- return this._material;
- }
- },
-
- /**
- Defines surface appearance when ghosted.
-
- @property ghostMaterial
- @type EmphasisMaterial
- @final
- */
- ghostMaterial: {
- get: function () {
- return this._ghostMaterial;
- }
- },
-
- /**
- Defines surface appearance when highlighted.
-
- @property highlightMaterial
- @type EmphasisMaterial
- @final
- */
- highlightMaterial: {
- get: function () {
- return this._highlightMaterial;
- }
- },
-
- /**
- Defines surface appearance when selected.
-
- @property selectedMaterial
- @type EmphasisMaterial
- */
- selectedMaterial: {
- get: function () {
- return this._selectedMaterial;
- }
- },
-
- /**
- Defines surface appearance when edges are shown.
-
- @property edgeMaterial
- @type EdgeMaterial
- */
- edgeMaterial: {
- get: function () {
- return this._edgeMaterial;
- }
- },
-
- /**
- Defines surface appearance when outlined.
-
- @property outlineMaterial
- @type OutlineMaterial
- */
- outlineMaterial: {
- get: function () {
- 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
- */
- visible: {
- set: function (visible) {
- visible = visible !== false;
- this._state.visible = visible;
- if (this._entityType) {
- this.scene._entityVisibilityUpdated(this, visible);
- }
- this._renderer.imageDirty();
- },
- get: function () {
- 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
- */
- "ghosted,ghost": {
- set: function (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: function () {
- 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
- */
- "highlight,highlighted": {
- set: function (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: function () {
- 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
- */
- selected: {
- set: function (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: function () {
- 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
- */
- edges: {
- set: function (edges) {
- edges = !!edges;
- if (edges === this._state.edges) {
- return;
- }
- this._state.edges = edges;
- this._renderer.imageDirty();
- },
- get: function () {
- 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
- */
- culled: {
- set: function (value) {
- this._state.culled = !!value;
- this._renderer.imageDirty();
- },
- get: function () {
- 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
- */
- pickable: {
- set: function (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: function () {
- 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
- */
- clippable: {
- set: function (value) {
- value = value !== false;
- if (this._state.clippable === value) {
- return;
- }
- this._state.clippable = value;
- this._renderer.imageDirty();
- },
- get: function () {
- 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
- */
- collidable: {
- set: function (value) {
- value = value !== false;
- if (value === this._state.collidable) {
- return;
- }
- this._state.collidable = value;
- },
- get: function () {
- return this._state.collidable;
- }
- },
-
-
- /**
- Indicates if casting shadows.
-
- @property castShadow
- @default true
- @type Boolean
- */
- castShadow: {
- set: function (value) {
- // value = value !== false;
- // if (value === this._state.castShadow) {
- // return;
- // }
- // this._state.castShadow = value;
- // this._renderer.imageDirty(); // Re-render in next shadow map generation pass
- },
- get: function () {
- return this._state.castShadow;
- }
- },
-
- /**
- Indicates if receiving shadows.
-
- @property receiveShadow
- @default true
- @type Boolean
- */
- receiveShadow: {
- set: function (value) {
- // 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: function () {
- 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
- */
- "outlined,outline": {
- set: function (value) {
- value = !!value;
- if (value === this._state.outlined) {
- return;
- }
- this._state.outlined = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._state.outlined;
- }
- },
-
- /**
- RGB colorize color, multiplies by the rendered fragment colors.
-
- @property colorize
- @default [1.0, 1.0, 1.0]
- @type Float32Array
- */
- colorize: {
- set: function (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: function () {
- 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
- */
- opacity: {
- set: function (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: function () {
- 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
- */
- layer: {
- set: function (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: function () {
- 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
- */
- stationary: {
- get: function () {
- 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
- */
- billboard: {
- get: function () {
- return this._state.billboard;
- }
- }
- },
+ _pickTriangle(frame) {
+ if (this._pickTriangleRenderer || (this._pickTriangleRenderer = PickTriangleRenderer.get(this))) {
+ this._pickTriangleRenderer.drawMesh(frame, this);
+ }
+ }
- _destroy: function () {
- this._super(); // xeogl.Object
- this._putRenderers();
- this._renderer.meshListDirty();
- this.scene._meshDestroyed(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;
+ }
- xeogl.Mesh._compareState = function (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
- };
-})();
\ No newline at end of file
+ _putRenderers() {
+ if (this._drawRenderer) {
+ this._drawRenderer.put();
+ this._drawRenderer = 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._shadowRenderer) {
+ this._shadowRenderer.put();
+ this._shadowRenderer = 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();
+ }
+
+ 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();
+ }
+
+ 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.imageDirty(); // Re-render in next shadow map generation pass
+ }
+
+ get castShadow() {
+ return this._state.castShadow;
+ }
+
+ /**
+ Indicates if receiving shadows.
+
+ @property receiveShadow
+ @default true
+ @type Boolean
+ */
+ set receiveShadow(value) {
+ // 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);
+ }
+}
+
+export {Mesh};
\ No newline at end of file
diff --git a/src/objects/object.js b/src/objects/object.js
index 760b6e0ed..ab8a6c1bf 100644
--- a/src/objects/object.js
+++ b/src/objects/object.js
@@ -568,7 +568,7 @@
@module xeogl
@submodule objects
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}{{/crossLink}}.
+ @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 scene, generated automatically when omitted.
@param [cfg.guid] {String} Optional globally unique identifier. This is unique not only within the {{#crossLink "Scene"}}{{/crossLink}}, but throughout the entire universe.
@@ -601,20 +601,46 @@
{{#crossLink "Object/selected:property"}}{{/crossLink}}, {{#crossLink "Object/colorize:property"}}{{/crossLink}} and {{#crossLink "Object/opacity:property"}}{{/crossLink}}.
@extends Component
*/
-xeogl.Object = xeogl.Component.extend({
+
+import {core} from "./../core.js";
+import {utils} from '../utils.js';
+import {Component} from '../component.js';
+// import {Mesh} from './mesh.js';
+// import {AABBGeometry} from '../geometry/aabbGeometry.js';
+// import {PhongMaterial} from '../materials/phongMaterial.js';
+import {math} from '../math/math.js';
+
+const angleAxis = new Float32Array(4);
+const q1 = new Float32Array(4);
+const q2 = new Float32Array(4);
+const xAxis = new Float32Array([1, 0, 0]);
+const yAxis = new Float32Array([0, 1, 0]);
+const zAxis = new Float32Array([0, 0, 1]);
+
+const veca = new Float32Array(3);
+const vecb = new Float32Array(3);
+
+const identityMat = math.identityMat4();
+
+class Object extends Component {
+
/**
- JavaScript class name for this xeogl.Object.
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
@property type
@type String
@final
*/
- type: "xeogl.Object",
+ static get type() {
+ return "xeogl.Object";
+ }
- _init: function (cfg) {
+ init(cfg) {
- const math = xeogl.math;
+ super.init(cfg);
this._guid = cfg.guid;
@@ -694,14 +720,14 @@ xeogl.Object = xeogl.Component.extend({
}
this.scene._objectCreated(this);
- },
+ }
- _setLocalMatrixDirty: function () {
+ _setLocalMatrixDirty() {
this._localMatrixDirty = true;
this._setWorldMatrixDirty();
- },
+ }
- _setWorldMatrixDirty: function () {
+ _setWorldMatrixDirty() {
this._worldMatrixDirty = true;
this._worldNormalMatrixDirty = true;
if (this._childList) {
@@ -709,79 +735,76 @@ xeogl.Object = xeogl.Component.extend({
this._childList[i]._setWorldMatrixDirty();
}
}
- },
+ }
- _buildWorldMatrix: function () {
+ _buildWorldMatrix() {
const localMatrix = this.matrix;
if (!this._parent) {
for (let i = 0, len = localMatrix.length; i < len; i++) {
this._worldMatrix[i] = localMatrix[i];
}
} else {
- xeogl.math.mulMat4(this._parent.worldMatrix, localMatrix, this._worldMatrix);
+ math.mulMat4(this._parent.worldMatrix, localMatrix, this._worldMatrix);
}
this._worldMatrixDirty = false;
- },
+ }
- _buildWorldNormalMatrix: function () {
+ _buildWorldNormalMatrix() {
if (this._worldMatrixDirty) {
this._buildWorldMatrix();
}
if (!this._worldNormalMatrix) {
- this._worldNormalMatrix = xeogl.math.mat4();
+ this._worldNormalMatrix = math.mat4();
}
- xeogl.math.inverseMat4(this._worldMatrix, this._worldNormalMatrix);
- xeogl.math.transposeMat4(this._worldNormalMatrix);
+ math.inverseMat4(this._worldMatrix, this._worldNormalMatrix);
+ math.transposeMat4(this._worldNormalMatrix);
this._worldNormalMatrixDirty = false;
- },
-
- _setAABBDirty: (function () {
+ }
- function setSubtreeAABBsDirty(object) {
- object._aabbDirty = true;
- object.fire("boundary", true);
- if (object._childList) {
- for (let i = 0, len = object._childList.length; i < len; i++) {
- setSubtreeAABBsDirty(object._childList[i]);
- }
+ _setSubtreeAABBsDirty(object) {
+ object._aabbDirty = true;
+ object.fire("boundary", true);
+ if (object._childList) {
+ for (let i = 0, len = object._childList.length; i < len; i++) {
+ this._setSubtreeAABBsDirty(object._childList[i]);
}
}
+ }
- return function () {
- setSubtreeAABBsDirty(this);
- if (this.collidable) {
- for (let object = this; object; object = object._parent) {
- object._aabbDirty = true;
- object.fire("boundary", true);
- }
+ _setAABBDirty() {
+ this._setSubtreeAABBsDirty(this);
+ if (this.collidable) {
+ for (let object = this; object; object = object._parent) {
+ object._aabbDirty = true;
+ object.fire("boundary", true);
}
- };
- })(),
+ }
+ }
- _updateAABB: function () {
+ _updateAABB() {
this.scene._aabbDirty = true;
if (!this._aabb) {
- this._aabb = xeogl.math.AABB3();
+ this._aabb = math.AABB3();
}
if (this._buildMeshAABB) {
this._buildMeshAABB(this.worldMatrix, this._aabb); // Geometry
} else { // Object | Group | Model
- xeogl.math.collapseAABB3(this._aabb);
+ math.collapseAABB3(this._aabb);
let object;
for (let i = 0, len = this._childList.length; i < len; i++) {
object = this._childList[i];
if (!object.collidable) {
continue;
}
- xeogl.math.expandAABB3(this._aabb, object.aabb);
+ math.expandAABB3(this._aabb, object.aabb);
}
if (!this._aabbCenter) {
this._aabbCenter = new Float32Array(3);
}
- xeogl.math.getAABB3Center(this._aabb, this._aabbCenter);
+ math.getAABB3Center(this._aabb, this._aabbCenter);
}
this._aabbDirty = false;
- },
+ }
/**
Adds a child.
@@ -800,15 +823,15 @@ xeogl.Object = xeogl.Component.extend({
{{#crossLink "Object/selected:property"}}{{/crossLink}}, {{#crossLink "Object/edges:property"}}{{/crossLink}}, {{#crossLink "Object/colorize:property"}}{{/crossLink}} and {{#crossLink "Object/opacity:property"}}{{/crossLink}}.
@returns {Object} The child object.
*/
- addChild: function (object, inheritStates) {
- if (xeogl._isNumeric(object) || xeogl._isString(object)) {
+ addChild(object, inheritStates) {
+ if (utils.isNumeric(object) || utils.isString(object)) {
const objectId = object;
object = this.scene.objects[objectId];
if (!object) {
- this.warn("Object not found: " + xeogl._inQuotes(objectId));
+ this.warn("Object not found: " + utils.inQuotes(objectId));
return;
}
- } else if (xeogl._isObject(object)) {
+ } else if (utils.isObject(object)) {
throw "addChild( * ) not implemented";
const cfg = object;
// object = new xeogl.Group(this.scene, cfg);
@@ -857,7 +880,7 @@ xeogl.Object = xeogl.Component.extend({
object._setWorldMatrixDirty();
object._setAABBDirty();
return object;
- },
+ }
/**
Removes the given child.
@@ -865,7 +888,7 @@ xeogl.Object = xeogl.Component.extend({
@method removeChild
@param {Object} object Child to remove.
*/
- removeChild: function (object) {
+ removeChild(object) {
for (let i = 0, len = this._childList.length; i < len; i++) {
if (this._childList[i].id === object.id) {
object._parent = null;
@@ -879,14 +902,14 @@ xeogl.Object = xeogl.Component.extend({
return;
}
}
- },
+ }
/**
Removes all children.
@method removeChildren
*/
- removeChildren: function () {
+ removeChildren() {
let object;
for (let i = 0, len = this._childList.length; i < len; i++) {
object = this._childList[i];
@@ -899,7 +922,7 @@ xeogl.Object = xeogl.Component.extend({
this._childMap = {};
this._childIDs = null;
this._setAABBDirty();
- },
+ }
/**
Rotates about the given local axis by the given increment.
@@ -908,24 +931,19 @@ xeogl.Object = xeogl.Component.extend({
@paream {Float32Array} axis Local axis about which to rotate.
@param {Number} angle Angle increment in degrees.
*/
- rotate: (function () {
- const angleAxis = new Float32Array(4);
- const q1 = new Float32Array(4);
- const q2 = new Float32Array(4);
- return function rotateOnWorldAxis(axis, angle) {
- angleAxis[0] = axis[0];
- angleAxis[1] = axis[1];
- angleAxis[2] = axis[2];
- angleAxis[3] = angle * xeogl.math.DEGTORAD;
- xeogl.math.angleAxisToQuaternion(angleAxis, q1);
- xeogl.math.mulQuaternions(this.quaternion, q1, q2);
- this.quaternion = q2;
- this._setLocalMatrixDirty();
- this._setAABBDirty();
- this._renderer.imageDirty();
- return this;
- };
- })(),
+ rotate(axis, angle) {
+ angleAxis[0] = axis[0];
+ angleAxis[1] = axis[1];
+ angleAxis[2] = axis[2];
+ angleAxis[3] = angle * math.DEGTORAD;
+ math.angleAxisToQuaternion(angleAxis, q1);
+ math.mulQuaternions(this.quaternion, q1, q2);
+ this.quaternion = q2;
+ this._setLocalMatrixDirty();
+ this._setAABBDirty();
+ this._renderer.imageDirty();
+ return this;
+ }
/**
Rotates about the given World-space axis by the given increment.
@@ -934,20 +952,16 @@ xeogl.Object = xeogl.Component.extend({
@paream {Float32Array} axis Local axis about which to rotate.
@param {Number} angle Angle increment in degrees.
*/
- rotateOnWorldAxis: (function () {
- const angleAxis = new Float32Array(4);
- const q1 = new Float32Array(4);
- return function rotateOnWorldAxis(axis, angle) {
- angleAxis[0] = axis[0];
- angleAxis[1] = axis[1];
- angleAxis[2] = axis[2];
- angleAxis[3] = angle * xeogl.math.DEGTORAD;
- xeogl.math.angleAxisToQuaternion(angleAxis, q1);
- xeogl.math.mulQuaternions(q1, this.quaternion, q1);
- //this.quaternion.premultiply(q1);
- return this;
- };
- })(),
+ rotateOnWorldAxis(axis, angle) {
+ angleAxis[0] = axis[0];
+ angleAxis[1] = axis[1];
+ angleAxis[2] = axis[2];
+ angleAxis[3] = angle * math.DEGTORAD;
+ math.angleAxisToQuaternion(angleAxis, q1);
+ math.mulQuaternions(q1, this.quaternion, q1);
+ //this.quaternion.premultiply(q1);
+ return this;
+ }
/**
Rotates about the local X-axis by the given increment.
@@ -955,12 +969,9 @@ xeogl.Object = xeogl.Component.extend({
@method rotateX
@param {Number} angle Angle increment in degrees.
*/
- rotateX: (function () {
- const axis = new Float32Array([1, 0, 0]);
- return function rotateX(angle) {
- return this.rotate(axis, angle);
- };
- })(),
+ rotateX(angle) {
+ return this.rotate(xAxis, angle);
+ }
/**
Rotates about the local Y-axis by the given increment.
@@ -968,12 +979,9 @@ xeogl.Object = xeogl.Component.extend({
@method rotateY
@param {Number} angle Angle increment in degrees.
*/
- rotateY: (function () {
- const axis = new Float32Array([0, 1, 0]);
- return function rotateY(angle) {
- return this.rotate(axis, angle);
- };
- })(),
+ rotateY(angle) {
+ return this.rotate(yAxis, angle);
+ }
/**
Rotates about the local Z-axis by the given increment.
@@ -981,12 +989,9 @@ xeogl.Object = xeogl.Component.extend({
@method rotateZ
@param {Number} angle Angle increment in degrees.
*/
- rotateZ: (function () {
- const axis = new Float32Array([0, 0, 1]);
- return function rotateZ(angle) {
- return this.rotate(axis, angle);
- };
- })(),
+ rotateZ(angle) {
+ return this.rotate(zAxis, angle);
+ }
/**
Translates along local space vector by the given increment.
@@ -995,19 +1000,15 @@ xeogl.Object = xeogl.Component.extend({
@param {Float32Array} axis Normalized local space 3D vector along which to translate.
@param {Number} distance Distance to translate along the vector.
*/
- translate: (function () {
- const veca = new Float32Array(3);
- const vecb = new Float32Array(3);
- return function (axis, distance) {
- xeogl.math.vec3ApplyQuaternion(this.quaternion, axis, veca);
- xeogl.math.mulVec3Scalar(veca, distance, vecb);
- xeogl.math.addVec3(this.position, vecb, this.position);
- this._setLocalMatrixDirty();
- this._setAABBDirty();
- this._renderer.imageDirty();
- return this;
- };
- })(),
+ translate(axis, distance) {
+ math.vec3ApplyQuaternion(this.quaternion, axis, veca);
+ math.mulVec3Scalar(veca, distance, vecb);
+ math.addVec3(this.position, vecb, this.position);
+ this._setLocalMatrixDirty();
+ this._setAABBDirty();
+ this._renderer.imageDirty();
+ return this;
+ }
/**
Translates along the local X-axis by the given increment.
@@ -1015,12 +1016,9 @@ xeogl.Object = xeogl.Component.extend({
@method translateX
@param {Number} distance Distance to translate along the X-axis.
*/
- translateX: (function () {
- const v1 = new Float32Array([1, 0, 0]);
- return function translateX(distance) {
- return this.translate(v1, distance);
- };
- })(),
+ translateX(distance) {
+ return this.translate(xAxis, distance);
+ }
/**
* Translates along the local Y-axis by the given increment.
@@ -1028,12 +1026,9 @@ xeogl.Object = xeogl.Component.extend({
* @method translateX
* @param {Number} distance Distance to translate along the Y-axis.
*/
- translateY: (function () {
- const v1 = new Float32Array([0, 1, 0]);
- return function translateY(distance) {
- return this.translate(v1, distance);
- };
- })(),
+ translateY(distance) {
+ return this.translate(yAxis, distance);
+ }
/**
Translates along the local Z-axis by the given increment.
@@ -1041,754 +1036,705 @@ xeogl.Object = xeogl.Component.extend({
@method translateX
@param {Number} distance Distance to translate along the Z-axis.
*/
- translateZ: (function () {
- const v1 = new Float32Array([0, 0, 1]);
- return function translateZ(distance) {
- return this.translate(v1, distance);
- };
- })(),
+ translateZ(distance) {
+ return this.translate(zAxis, distance);
+ }
- _props: {
+ /**
+ Globally unique identifier.
- /**
- Globally unique identifier.
+ This is unique not only within the {{#crossLink "Scene"}}{{/crossLink}}, but throughout the entire universe.
- This is unique not only within the {{#crossLink "Scene"}}{{/crossLink}}, but throughout the entire universe.
+ Only defined when given to the constructor.
- Only defined when given to the constructor.
+ @property guid
+ @type String
+ @final
+ */
+ get guid() {
+ return this._guid;
+ }
- @property guid
- @type String
- @final
- */
- guid: {
- get: function () {
- return this._guid;
- }
- },
+ /**
+ Optional entity classification when using within a semantic data model.
- /**
- Optional entity classification when using within a semantic data model.
+ See the Object documentation on "Applying a semantic data model" for usage.
- See the Object documentation on "Applying a semantic data model" for usage.
+ @property entityType
+ @default null
+ @type String
+ @final
+ */
+ get entityType() {
+ return this._entityType;
+ }
- @property entityType
- @default null
- @type String
- @final
- */
- entityType: {
- get: function () {
- return this._entityType;
- }
- },
-
- //------------------------------------------------------------------------------------------------------------------
- // Children and parent properties
- //------------------------------------------------------------------------------------------------------------------
-
- /**
- Number of child {{#crossLink "Object"}}Objects{{/crossLink}}.
-
- @property numChildren
- @final
- @type Number
- */
- numChildren: {
- get: function () {
- return this._childList.length;
- }
- },
-
- /**
- Array of child {{#crossLink "Object"}}Objects{{/crossLink}}.
-
- @property children
- @final
- @type Array
- */
- children: {
- get: function () {
- return this._childList;
- }
- },
-
- /**
- Child {{#crossLink "Object"}}Objects{{/crossLink}} mapped to their IDs.
-
- @property childMap
- @final
- @type {*}
- */
- childMap: {
- get: function () {
- return this._childMap;
- }
- },
-
- /**
- IDs of child {{#crossLink "Object"}}Objects{{/crossLink}}.
-
- @property childIDs
- @final
- @type Array
- */
- childIDs: {
- get: function () {
- if (!this._childIDs) {
- this._childIDs = Object.keys(this._childMap);
- }
- return this._childIDs;
- }
- },
-
- /**
- The parent.
-
- The parent Group may also be set by passing the Object to the
- Group/Model's {{#crossLink "Group/addChild:method"}}addChild(){{/crossLink}} method.
-
- @property parent
- @type Group
- */
- parent: {
- set: function (object) {
- if (xeogl._isNumeric(object) || xeogl._isString(object)) {
- const objectId = object;
- object = this.scene.objects[objectId];
- if (!object) {
- this.warn("Group not found: " + xeogl._inQuotes(objectId));
- return;
- }
- }
- if (object.scene.id !== this.scene.id) {
- this.error("Group not in same Scene: " + object.id);
- return;
- }
- if (this._parent && this._parent.id === object.id) {
- this.warn("Already a child of Group: " + object.id);
- return;
- }
- object.addChild(this);
- },
- get: function () {
- return this._parent;
- }
- },
-
- //------------------------------------------------------------------------------------------------------------------
- // Transform properties
- //------------------------------------------------------------------------------------------------------------------
-
- /**
- Local translation.
-
- @property position
- @default [0,0,0]
- @type {Float32Array}
- */
- position: {
- set: function (value) {
- this._position.set(value || [0, 0, 0]);
- this._setLocalMatrixDirty();
- this._setAABBDirty();
- this._renderer.imageDirty();
- },
- get: function () {
- return this._position;
- }
- },
-
- /**
- Local rotation, as Euler angles given in degrees, for each of the X, Y and Z axis.
-
- @property rotation
- @default [0,0,0]
- @type {Float32Array}
- */
- rotation: {
- set: function (value) {
- this._rotation.set(value || [0, 0, 0]);
- xeogl.math.eulerToQuaternion(this._rotation, "XYZ", this._quaternion);
- this._setLocalMatrixDirty();
- this._setAABBDirty();
- this._renderer.imageDirty();
- },
- get: function () {
- return this._rotation;
- }
- },
-
- /**
- Local rotation quaternion.
-
- @property quaternion
- @default [0,0,0, 1]
- @type {Float32Array}
- */
- quaternion: {
- set: function (value) {
- this._quaternion.set(value || [0, 0, 0, 1]);
- xeogl.math.quaternionToEuler(this._quaternion, "XYZ", this._rotation);
- this._setLocalMatrixDirty();
- this._setAABBDirty();
- this._renderer.imageDirty();
- },
- get: function () {
- return this._quaternion;
- }
- },
-
- /**
- Local scale.
-
- @property scale
- @default [1,1,1]
- @type {Float32Array}
- */
- scale: {
- set: function (value) {
- this._scale.set(value || [1, 1, 1]);
- this._setLocalMatrixDirty();
- this._setAABBDirty();
- this._renderer.imageDirty();
- },
- get: function () {
- return this._scale;
- }
- },
-
- /**
- * Local matrix.
- *
- * @property matrix
- * @default [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
- * @type {Float32Array}
- */
- matrix: {
- set: (function () {
- const identityMat = xeogl.math.identityMat4();
- return function (value) {
- if (!this.__localMatrix) {
- this.__localMatrix = xeogl.math.identityMat4();
- }
- this.__localMatrix.set(value || identityMat);
- xeogl.math.decomposeMat4(this.__localMatrix, this._position, this._quaternion, this._scale);
- this._localMatrixDirty = false;
- this._setWorldMatrixDirty();
- this._setAABBDirty();
- this._renderer.imageDirty();
- };
- })(),
- get: function () {
- if (this._localMatrixDirty) {
- if (!this.__localMatrix) {
- this.__localMatrix = xeogl.math.identityMat4();
- }
- xeogl.math.composeMat4(this._position, this._quaternion, this._scale, this.__localMatrix);
- this._localMatrixDirty = false;
- }
- return this.__localMatrix;
- }
- },
-
- /**
- * The World matrix.
- *
- * @property worldMatrix
- * @type {Float32Array}
- */
- worldMatrix: {
- get: function () {
- if (this._worldMatrixDirty) {
- this._buildWorldMatrix();
- }
- return this._worldMatrix;
- }
- },
-
- /**
- * This World normal matrix.
- *
- * @property worldNormalMatrix
- * @default [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
- * @type {Float32Array}
- */
- worldNormalMatrix: {
- get: function () {
- if (this._worldNormalMatrixDirty) {
- this._buildWorldNormalMatrix();
- }
- return this._worldNormalMatrix;
- }
- },
-
- // worldPosition: {
- // get: function (optionalTarget) {
- // var result = optionalTarget || new Vector3();
- // this.updateMatrixWorld(true);
- // return result.setFromMatrixPosition(this.matrixWorld);
- // }
- // },
- //
- // worldQuaternion: {
- // get: function () {
- // var position = new Vector3();
- // var scale = new Vector3();
- // return function getWorldQuaternion(optionalTarget) {
- // var result = optionalTarget || new Quaternion();
- // this.updateMatrixWorld(true);
- // this.matrixWorld.decompose(position, result, scale);
- // return result;
- // };
- // }()
- // },
- //
- // worldRotation: {
- // get: function () {
- // var quaternion = new Quaternion();
- // return function getWorldRotation(optionalTarget) {
- // var result = optionalTarget || new Euler();
- // this.getWorldQuaternion(quaternion);
- // return result.setFromQuaternion(quaternion, this.rotation.order, false)
- // };
- // }
- // }(),
- //
- // worldScale: {
- // get: (function () {
- // var position = new Float32Array(3);
- // var quaternion = new Float32Array(4);
- // return function getWorldScale(optionalTarget) {
- // var result = optionalTarget || new Float32Array(3);
- // xeogl.math.decomposeMat4(this.worldMatrix, position, quaternion, result);
- // return result;
- // };
- // })()
- // },
- //
- // worldDirection: {
- // get: (function () {
- // var quaternion = new Quaternion();
- // return function getWorldDirection(optionalTarget) {
- // var result = optionalTarget || new Vector3();
- // this.getWorldQuaternion(quaternion);
- // return result.set(0, 0, 1).applyQuaternion(quaternion);
- // };
- // })()
- // },
-
- //------------------------------------------------------------------------------------------------------------------
- // Boundary properties
- //------------------------------------------------------------------------------------------------------------------
-
- /**
- World-space 3D axis-aligned bounding box (AABB).
-
- Represented by a six-element Float32Array containing the min/max extents of the
- axis-aligned volume, ie. ````[xmin, ymin,zmin,xmax,ymax, zmax]````.
-
- @property aabb
- @final
- @type {Float32Array}
- */
- aabb: {
- get: function () {
- if (this._aabbDirty) {
- this._updateAABB();
- }
- return this._aabb;
- }
- },
-
- /**
- World-space 3D center.
-
- @property center
- @final
- @type {Float32Array}
- */
- center: {
- get: function () {
- if (this._aabbDirty) {
- this._updateAABB();
- }
- return this._aabbCenter;
- }
- },
-
- /**
- Indicates if visible.
-
- Only rendered when {{#crossLink "Object/visible:property"}}{{/crossLink}} is true and
- {{#crossLink "Object/culled:property"}}{{/crossLink}} is false.
-
- Each visible Object is registered in its {{#crossLink "Scene"}}{{/crossLink}}'s
- {{#crossLink "Scene/visibleEntities:property"}}{{/crossLink}} map while its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
- is set to a value.
-
- @property visible
- @default true
- @type Boolean
- */
- visible: {
- set: function (visible) {
- visible = visible !== false;
- this._visible = visible;
- for (let i = 0, len = this._childList.length; i < len; i++) {
- this._childList[i].visible = visible;
- }
- if (this._entityType) {
- this.scene._entityVisibilityUpdated(this, visible);
- }
- },
- get: function () {
- return this._visible;
- }
- },
-
- /**
- Indicates if highlighted.
-
- Each highlighted Object is registered in its {{#crossLink "Scene"}}{{/crossLink}}'s
- {{#crossLink "Scene/highlightedEntities:property"}}{{/crossLink}} map while its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
- is set to a value.
-
- @property highlighted
- @default false
- @type Boolean
- */
- highlighted: {
- set: function (highlighted) {
- highlighted = !!highlighted;
- this._highlighted = highlighted;
- for (let i = 0, len = this._childList.length; i < len; i++) {
- this._childList[i].highlighted = highlighted;
- }
- if (this._entityType) {
- this.scene._entityHighlightedUpdated(this, highlighted);
- }
- },
- get: function () {
- return this._highlighted;
- }
- },
-
- /**
- Indicates if ghosted.
-
- Each ghosted Object is registered in its {{#crossLink "Scene"}}{{/crossLink}}'s
- {{#crossLink "Scene/ghostedEntities:property"}}{{/crossLink}} map while its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
- is set to a value.
-
- @property ghosted
- @default false
- @type Boolean
- */
- ghosted: {
- set: function (ghosted) {
- ghosted = !!ghosted;
- this._ghosted = ghosted;
- for (let i = 0, len = this._childList.length; i < len; i++) {
- this._childList[i].ghosted = ghosted;
- }
- if (this._entityType) {
- this.scene._entityGhostedUpdated(this, ghosted);
- }
- },
- get: function () {
- return this._ghosted;
- }
- },
-
- /**
- Indicates if selected.
-
- Each selected Object is registered in its {{#crossLink "Scene"}}{{/crossLink}}'s
- {{#crossLink "Scene/selectedEntities:property"}}{{/crossLink}} map while its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
- is set to a value.
-
- @property selected
- @default false
- @type Boolean
- */
- selected: {
- set: function (selected) {
- selected = !!selected;
- this._selected = selected;
- for (let i = 0, len = this._childList.length; i < len; i++) {
- this._childList[i].selected = selected;
- }
- if (this._entityType) {
- this.scene._entitySelectedUpdated(this, selected);
- }
- },
- get: function () {
- return this._selected;
- }
- },
-
- /**
- Indicates if edges are emphasized.
-
- @property edges
- @default false
- @type Boolean
- */
- edges: {
- set: function (edges) {
- edges = !!edges;
- this._edges = edges;
- for (let i = 0, len = this._childList.length; i < len; i++) {
- this._childList[i].edges = edges;
- }
- },
- get: function () {
- return this._edges;
- }
- },
-
- /**
- Indicates if culled from view.
-
- Only rendered when {{#crossLink "Object/visible:property"}}{{/crossLink}} is true and
- {{#crossLink "Object/culled:property"}}{{/crossLink}} is false.
-
- @property culled
- @default false
- @type Boolean
- */
- culled: {
- set: function (culled) {
- culled = !!culled;
- this._culled = culled;
- for (let i = 0, len = this._childList.length; i < len; i++) {
- this._childList[i].culled = culled;
- }
- },
- get: function () {
- return this._culled;
- }
- },
-
- /**
- Indicates if clippable.
-
- Clipping is done by the {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Clips"}}{{/crossLink}} component.
-
- @property clippable
- @default true
- @type Boolean
- */
- clippable: {
- set: function (clippable) {
- clippable = clippable !== false;
- this._clippable = clippable;
- for (let i = 0, len = this._childList.length; i < len; i++) {
- this._childList[i].clippable = clippable;
- }
- },
- get: function () {
- return this._clippable;
- }
- },
-
- /**
- Indicates if included in boundary calculations.
-
- @property collidable
- @default true
- @type Boolean
- */
- collidable: {
- set: function (collidable) {
- collidable = collidable !== false;
- this._collidable = collidable;
- for (let i = 0, len = this._childList.length; i < len; i++) {
- this._childList[i].collidable = collidable;
- }
- },
- get: function () {
- return this._collidable;
- }
- },
-
- /**
- Whether or not to allow picking.
-
- Picking is done via calls to {{#crossLink "Scene/pick:method"}}Scene#pick(){{/crossLink}}.
-
- @property pickable
- @default true
- @type Boolean
- */
- pickable: {
- set: function (pickable) {
- pickable = pickable !== false;
- this._pickable = pickable;
- for (let i = 0, len = this._childList.length; i < len; i++) {
- this._childList[i].pickable = pickable;
- }
- },
- get: function () {
- return this._pickable;
- }
- },
-
- /**
- RGB colorize color, multiplies by the rendered fragment color.
-
- @property colorize
- @default [1.0, 1.0, 1.0]
- @type Float32Array
- */
- colorize: {
- set: function (rgb) {
- let colorize = this._colorize;
- if (!colorize) {
- colorize = this._colorize = new Float32Array(4);
- colorize[3] = 1.0;
- }
- if (rgb) {
- colorize[0] = rgb[0];
- colorize[1] = rgb[1];
- colorize[2] = rgb[2];
- } else {
- colorize[0] = 1;
- colorize[1] = 1;
- colorize[2] = 1;
- }
- for (let i = 0, len = this._childList.length; i < len; i++) {
- this._childList[i].colorize = colorize;
- }
- },
- get: function () {
- return this._colorize.slice(0, 3);
- }
- },
-
- /**
- Opacity factor, multiplies by the rendered fragment alpha.
-
- This is a factor in range ````[0..1]````.
-
- @property opacity
- @default 1.0
- @type Number
- */
- opacity: {
- set: function (opacity) {
- let colorize = this._colorize;
- if (!colorize) {
- colorize = this._colorize = new Float32Array(4);
- colorize[0] = 1;
- colorize[1] = 1;
- colorize[2] = 1;
- }
- colorize[3] = opacity !== null && opacity !== undefined ? opacity : 1.0;
- for (let i = 0, len = this._childList.length; i < len; i++) {
- this._childList[i].opacity = opacity;
- }
- },
- get: function () {
- return this._colorize[3];
- }
- },
-
- /**
- Indicates if outlined.
-
- @property outlined
- @default false
- @type Boolean
- */
- outlined: {
- set: function (outlined) {
- outlined = !!outlined;
- this._outlined = outlined;
- for (let i = 0, len = this._childList.length; i < len; i++) {
- this._childList[i].outlined = outlined;
- }
- },
- get: function () {
- return this._outlined;
- }
- },
-
- /**
- Indicates if casting shadows.
-
- @property castShadow
- @default true
- @type Boolean
- */
- castShadow: {
- set: function (castShadow) {
- castShadow = !!castShadow;
- this._castShadow = castShadow;
- for (let i = 0, len = this._childList.length; i < len; i++) {
- this._childList[i].castShadow = castShadow;
- }
- },
- get: function () {
- return this._castShadow;
- }
- },
-
- /**
- Indicates if receiving shadows.
-
- @property receiveShadow
- @default true
- @type Boolean
- */
- receiveShadow: {
- set: function (receiveShadow) {
- receiveShadow = !!receiveShadow;
- this._receiveShadow = receiveShadow;
- for (let i = 0, len = this._childList.length; i < len; i++) {
- this._childList[i].receiveShadow = receiveShadow;
- }
- },
- get: function () {
- return this._receiveShadow;
+ //------------------------------------------------------------------------------------------------------------------
+ // Children and parent properties
+ //------------------------------------------------------------------------------------------------------------------
+
+ /**
+ Number of child {{#crossLink "Object"}}Objects{{/crossLink}}.
+
+ @property numChildren
+ @final
+ @type Number
+ */
+ get numChildren() {
+ return this._childList.length;
+ }
+
+ /**
+ Array of child {{#crossLink "Object"}}Objects{{/crossLink}}.
+
+ @property children
+ @final
+ @type Array
+ */
+ get children() {
+ return this._childList;
+ }
+
+ /**
+ Child {{#crossLink "Object"}}Objects{{/crossLink}} mapped to their IDs.
+
+ @property childMap
+ @final
+ @type {*}
+ */
+ get childMap() {
+ return this._childMap;
+ }
+
+ /**
+ IDs of child {{#crossLink "Object"}}Objects{{/crossLink}}.
+
+ @property childIDs
+ @final
+ @type Array
+ */
+ get childIDs() {
+ if (!this._childIDs) {
+ this._childIDs = Object.keys(this._childMap);
+ }
+ return this._childIDs;
+ }
+
+ /**
+ The parent.
+
+ The parent Group may also be set by passing the Object to the
+ Group/Model's {{#crossLink "Group/addChild:method"}}addChild(){{/crossLink}} method.
+
+ @property parent
+ @type Group
+ */
+ set parent(object) {
+ if (utils.isNumeric(object) || utils.isString(object)) {
+ const objectId = object;
+ object = this.scene.objects[objectId];
+ if (!object) {
+ this.warn("Group not found: " + utils.inQuotes(objectId));
+ return;
}
- },
-
- /**
- Indicates if the 3D World-space axis-aligned bounding box (AABB) is visible.
-
- @property aabbVisible
- @default false
- @type {Boolean}
- */
- aabbVisible: {
- set: function (show) {
- if (!show && !this._aabbHelper) {
- return;
- }
- if (!this._aabbHelper) {
- this._aabbHelper = new xeogl.Mesh(this, {
- geometry: new xeogl.AABBGeometry(this, {
- target: this
- }),
- material: new xeogl.PhongMaterial(this, {
- diffuse: [0.5, 1.0, 0.5],
- emissive: [0.5, 1.0, 0.5],
- lineWidth: 2
- })
- });
- }
- this._aabbHelper.visible = show;
- },
- get: function () {
- return this._aabbHelper ? this._aabbHelper.visible : false;
+ }
+ if (object.scene.id !== this.scene.id) {
+ this.error("Group not in same Scene: " + object.id);
+ return;
+ }
+ if (this._parent && this._parent.id === object.id) {
+ this.warn("Already a child of Group: " + object.id);
+ return;
+ }
+ object.addChild(this);
+ }
+
+ get parent() {
+ return this._parent;
+ }
+
+ //------------------------------------------------------------------------------------------------------------------
+ // Transform properties
+ //------------------------------------------------------------------------------------------------------------------
+
+ /**
+ Local translation.
+
+ @property position
+ @default [0,0,0]
+ @type {Float32Array}
+ */
+ set position(value) {
+ this._position.set(value || [0, 0, 0]);
+ this._setLocalMatrixDirty();
+ this._setAABBDirty();
+ this._renderer.imageDirty();
+ }
+
+ get position() {
+ return this._position;
+ }
+
+ /**
+ Local rotation, as Euler angles given in degrees, for each of the X, Y and Z axis.
+
+ @property rotation
+ @default [0,0,0]
+ @type {Float32Array}
+ */
+ set rotation(value) {
+ this._rotation.set(value || [0, 0, 0]);
+ math.eulerToQuaternion(this._rotation, "XYZ", this._quaternion);
+ this._setLocalMatrixDirty();
+ this._setAABBDirty();
+ this._renderer.imageDirty();
+ }
+
+ get rotation() {
+ return this._rotation;
+ }
+
+ /**
+ Local rotation quaternion.
+
+ @property quaternion
+ @default [0,0,0, 1]
+ @type {Float32Array}
+ */
+ set quaternion(value) {
+ this._quaternion.set(value || [0, 0, 0, 1]);
+ math.quaternionToEuler(this._quaternion, "XYZ", this._rotation);
+ this._setLocalMatrixDirty();
+ this._setAABBDirty();
+ this._renderer.imageDirty();
+ }
+
+ get quaternion() {
+ return this._quaternion;
+ }
+
+ /**
+ Local scale.
+
+ @property scale
+ @default [1,1,1]
+ @type {Float32Array}
+ */
+ set scale(value) {
+ this._scale.set(value || [1, 1, 1]);
+ this._setLocalMatrixDirty();
+ this._setAABBDirty();
+ this._renderer.imageDirty();
+ }
+
+ get scale() {
+ return this._scale;
+ }
+
+ /**
+ * Local matrix.
+ *
+ * @property matrix
+ * @default [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
+ * @type {Float32Array}
+ */
+ set matrix(value) {
+
+ if (!this.__localMatrix) {
+ this.__localMatrix = math.identityMat4();
+ }
+ this.__localMatrix.set(value || identityMat);
+ math.decomposeMat4(this.__localMatrix, this._position, this._quaternion, this._scale);
+ this._localMatrixDirty = false;
+ this._setWorldMatrixDirty();
+ this._setAABBDirty();
+ this._renderer.imageDirty();
+ }
+
+ get matrix() {
+ if (this._localMatrixDirty) {
+ if (!this.__localMatrix) {
+ this.__localMatrix = math.identityMat4();
}
+ math.composeMat4(this._position, this._quaternion, this._scale, this.__localMatrix);
+ this._localMatrixDirty = false;
+ }
+ return this.__localMatrix;
+ }
+
+ /**
+ * The World matrix.
+ *
+ * @property worldMatrix
+ * @type {Float32Array}
+ */
+ get worldMatrix() {
+ if (this._worldMatrixDirty) {
+ this._buildWorldMatrix();
+ }
+ return this._worldMatrix;
+ }
+
+ /**
+ * This World normal matrix.
+ *
+ * @property worldNormalMatrix
+ * @default [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
+ * @type {Float32Array}
+ */
+ get worldNormalMatrix() {
+ if (this._worldNormalMatrixDirty) {
+ this._buildWorldNormalMatrix();
+ }
+ return this._worldNormalMatrix;
+ }
+
+ // worldPosition: {
+ // get: function (optionalTarget) {
+ // var result = optionalTarget || new Vector3();
+ // this.updateMatrixWorld(true);
+ // return result.setFromMatrixPosition(this.matrixWorld);
+ // }
+ // },
+ //
+ // worldQuaternion: {
+ // get: function () {
+ // var position = new Vector3();
+ // var scale = new Vector3();
+ // return function getWorldQuaternion(optionalTarget) {
+ // var result = optionalTarget || new Quaternion();
+ // this.updateMatrixWorld(true);
+ // this.matrixWorld.decompose(position, result, scale);
+ // return result;
+ // };
+ // }()
+ // },
+ //
+ // worldRotation: {
+ // get: function () {
+ // var quaternion = new Quaternion();
+ // return function getWorldRotation(optionalTarget) {
+ // var result = optionalTarget || new Euler();
+ // this.getWorldQuaternion(quaternion);
+ // return result.setFromQuaternion(quaternion, this.rotation.order, false)
+ // };
+ // }
+ // }(),
+ //
+ // worldScale: {
+ // get: (function () {
+ // var position = new Float32Array(3);
+ // var quaternion = new Float32Array(4);
+ // return function getWorldScale(optionalTarget) {
+ // var result = optionalTarget || new Float32Array(3);
+ // math.decomposeMat4(this.worldMatrix, position, quaternion, result);
+ // return result;
+ // };
+ // })()
+ // },
+ //
+ // worldDirection: {
+ // get: (function () {
+ // var quaternion = new Quaternion();
+ // return function getWorldDirection(optionalTarget) {
+ // var result = optionalTarget || new Vector3();
+ // this.getWorldQuaternion(quaternion);
+ // return result.set(0, 0, 1).applyQuaternion(quaternion);
+ // };
+ // })()
+ // },
+
+ //------------------------------------------------------------------------------------------------------------------
+ // Boundary properties
+ //------------------------------------------------------------------------------------------------------------------
+
+ /**
+ World-space 3D axis-aligned bounding box (AABB).
+
+ Represented by a six-element Float32Array containing the min/max extents of the
+ axis-aligned volume, ie. ````[xmin, ymin,zmin,xmax,ymax, zmax]````.
+
+ @property aabb
+ @final
+ @type {Float32Array}
+ */
+ get aabb() {
+ if (this._aabbDirty) {
+ this._updateAABB();
+ }
+ return this._aabb;
+ }
+
+ /**
+ World-space 3D center.
+
+ @property center
+ @final
+ @type {Float32Array}
+ */
+ get center() {
+ if (this._aabbDirty) {
+ this._updateAABB();
+ }
+ return this._aabbCenter;
+ }
+
+ /**
+ Indicates if visible.
+
+ Only rendered when {{#crossLink "Object/visible:property"}}{{/crossLink}} is true and
+ {{#crossLink "Object/culled:property"}}{{/crossLink}} is false.
+
+ Each visible Object is registered in its {{#crossLink "Scene"}}{{/crossLink}}'s
+ {{#crossLink "Scene/visibleEntities:property"}}{{/crossLink}} map while its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
+ is set to a value.
+
+ @property visible
+ @default true
+ @type Boolean
+ */
+ set visible(visible) {
+ visible = visible !== false;
+ this._visible = visible;
+ for (let i = 0, len = this._childList.length; i < len; i++) {
+ this._childList[i].visible = visible;
+ }
+ if (this._entityType) {
+ this.scene._entityVisibilityUpdated(this, visible);
+ }
+ }
+
+ get visible() {
+ return this._visible;
+ }
+
+ /**
+ Indicates if highlighted.
+
+ Each highlighted Object is registered in its {{#crossLink "Scene"}}{{/crossLink}}'s
+ {{#crossLink "Scene/highlightedEntities:property"}}{{/crossLink}} map while its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
+ is set to a value.
+
+ @property highlighted
+ @default false
+ @type Boolean
+ */
+ set highlighted(highlighted) {
+ highlighted = !!highlighted;
+ this._highlighted = highlighted;
+ for (let i = 0, len = this._childList.length; i < len; i++) {
+ this._childList[i].highlighted = highlighted;
+ }
+ if (this._entityType) {
+ this.scene._entityHighlightedUpdated(this, highlighted);
+ }
+ }
+
+ get highlighted() {
+ return this._highlighted;
+ }
+
+ /**
+ Indicates if ghosted.
+
+ Each ghosted Object is registered in its {{#crossLink "Scene"}}{{/crossLink}}'s
+ {{#crossLink "Scene/ghostedEntities:property"}}{{/crossLink}} map while its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
+ is set to a value.
+
+ @property ghosted
+ @default false
+ @type Boolean
+ */
+ set ghosted(ghosted) {
+ ghosted = !!ghosted;
+ this._ghosted = ghosted;
+ for (let i = 0, len = this._childList.length; i < len; i++) {
+ this._childList[i].ghosted = ghosted;
+ }
+ if (this._entityType) {
+ this.scene._entityGhostedUpdated(this, ghosted);
+ }
+ }
+
+ get ghosted() {
+ return this._ghosted;
+ }
+
+ /**
+ Indicates if selected.
+
+ Each selected Object is registered in its {{#crossLink "Scene"}}{{/crossLink}}'s
+ {{#crossLink "Scene/selectedEntities:property"}}{{/crossLink}} map while its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
+ is set to a value.
+
+ @property selected
+ @default false
+ @type Boolean
+ */
+ set selected(selected) {
+ selected = !!selected;
+ this._selected = selected;
+ for (let i = 0, len = this._childList.length; i < len; i++) {
+ this._childList[i].selected = selected;
+ }
+ if (this._entityType) {
+ this.scene._entitySelectedUpdated(this, selected);
+ }
+ }
+
+ get selected() {
+ return this._selected;
+ }
+
+ /**
+ Indicates if edges are emphasized.
+
+ @property edges
+ @default false
+ @type Boolean
+ */
+ set edges(edges) {
+ edges = !!edges;
+ this._edges = edges;
+ for (let i = 0, len = this._childList.length; i < len; i++) {
+ this._childList[i].edges = edges;
+ }
+ }
+
+ get edges() {
+ return this._edges;
+ }
+
+ /**
+ Indicates if culled from view.
+
+ Only rendered when {{#crossLink "Object/visible:property"}}{{/crossLink}} is true and
+ {{#crossLink "Object/culled:property"}}{{/crossLink}} is false.
+
+ @property culled
+ @default false
+ @type Boolean
+ */
+ set culled(culled) {
+ culled = !!culled;
+ this._culled = culled;
+ for (let i = 0, len = this._childList.length; i < len; i++) {
+ this._childList[i].culled = culled;
+ }
+ }
+
+ get culled() {
+ return this._culled;
+ }
+
+ /**
+ Indicates if clippable.
+
+ Clipping is done by the {{#crossLink "Scene"}}Scene{{/crossLink}}'s {{#crossLink "Clips"}}{{/crossLink}} component.
+
+ @property clippable
+ @default true
+ @type Boolean
+ */
+ set clippable(clippable) {
+ clippable = clippable !== false;
+ this._clippable = clippable;
+ for (let i = 0, len = this._childList.length; i < len; i++) {
+ this._childList[i].clippable = clippable;
+ }
+ }
+
+ get clippable() {
+ return this._clippable;
+ }
+
+ /**
+ Indicates if included in boundary calculations.
+
+ @property collidable
+ @default true
+ @type Boolean
+ */
+ set collidable(collidable) {
+ collidable = collidable !== false;
+ this._collidable = collidable;
+ for (let i = 0, len = this._childList.length; i < len; i++) {
+ this._childList[i].collidable = collidable;
+ }
+ }
+
+ get collidable() {
+ return this._collidable;
+ }
+
+ /**
+ Whether or not to allow picking.
+
+ Picking is done via calls to {{#crossLink "Scene/pick:method"}}Scene#pick(){{/crossLink}}.
+
+ @property pickable
+ @default true
+ @type Boolean
+ */
+ set pickable(pickable) {
+ pickable = pickable !== false;
+ this._pickable = pickable;
+ for (let i = 0, len = this._childList.length; i < len; i++) {
+ this._childList[i].pickable = pickable;
+ }
+ }
+
+ get pickable() {
+ return this._pickable;
+ }
+
+ /**
+ RGB colorize color, multiplies by the rendered fragment color.
+
+ @property colorize
+ @default [1.0, 1.0, 1.0]
+ @type Float32Array
+ */
+ set colorize(rgb) {
+ let colorize = this._colorize;
+ if (!colorize) {
+ colorize = this._colorize = new Float32Array(4);
+ colorize[3] = 1.0;
+ }
+ if (rgb) {
+ colorize[0] = rgb[0];
+ colorize[1] = rgb[1];
+ colorize[2] = rgb[2];
+ } else {
+ colorize[0] = 1;
+ colorize[1] = 1;
+ colorize[2] = 1;
+ }
+ for (let i = 0, len = this._childList.length; i < len; i++) {
+ this._childList[i].colorize = colorize;
+ }
+ }
+
+ get colorize() {
+ return this._colorize.slice(0, 3);
+ }
+
+ /**
+ 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._colorize;
+ if (!colorize) {
+ colorize = this._colorize = new Float32Array(4);
+ colorize[0] = 1;
+ colorize[1] = 1;
+ colorize[2] = 1;
+ }
+ colorize[3] = opacity !== null && opacity !== undefined ? opacity : 1.0;
+ for (let i = 0, len = this._childList.length; i < len; i++) {
+ this._childList[i].opacity = opacity;
+ }
+ }
+
+ get opacity() {
+ return this._colorize[3];
+ }
+
+ /**
+ Indicates if outlined.
+
+ @property outlined
+ @default false
+ @type Boolean
+ */
+ set outlined(outlined) {
+ outlined = !!outlined;
+ this._outlined = outlined;
+ for (let i = 0, len = this._childList.length; i < len; i++) {
+ this._childList[i].outlined = outlined;
+ }
+ }
+
+ get outlined() {
+ return this._outlined;
+ }
+
+ /**
+ Indicates if casting shadows.
+
+ @property castShadow
+ @default true
+ @type Boolean
+ */
+ set castShadow(castShadow) {
+ castShadow = !!castShadow;
+ this._castShadow = castShadow;
+ for (let i = 0, len = this._childList.length; i < len; i++) {
+ this._childList[i].castShadow = castShadow;
+ }
+ }
+
+ get castShadow() {
+ return this._castShadow;
+ }
+
+ /**
+ Indicates if receiving shadows.
+
+ @property receiveShadow
+ @default true
+ @type Boolean
+ */
+ set receiveShadow(receiveShadow) {
+ receiveShadow = !!receiveShadow;
+ this._receiveShadow = receiveShadow;
+ for (let i = 0, len = this._childList.length; i < len; i++) {
+ this._childList[i].receiveShadow = receiveShadow;
}
- },
+ }
+
+ get receiveShadow() {
+ return this._receiveShadow;
+ }
+
+ /**
+ Indicates if the 3D World-space axis-aligned bounding box (AABB) is visible.
- _destroy: function () {
- this._super();
+ @property aabbVisible
+ @default false
+ @type {Boolean}
+ */
+ set aabbVisible(visible) {
+ // if (!show && !this._aabbHelper) {
+ // return;
+ // }
+ // if (!this._aabbHelper) {
+ // this._aabbHelper = new Mesh(this, {
+ // geometry: new AABBGeometry(this, {
+ // target: this
+ // }),
+ // material: new PhongMaterial(this, {
+ // diffuse: [0.5, 1.0, 0.5],
+ // emissive: [0.5, 1.0, 0.5],
+ // lineWidth: 2
+ // })
+ // });
+ // }
+ // this._aabbHelper.visible = visible;
+ }
+
+ get aabbVisible() {
+ return this._aabbHelper ? this._aabbHelper.visible : false;
+ }
+
+ destroy() {
+ super.destroy();
if (this._parent) {
this._parent.removeChild(this);
}
@@ -1824,4 +1770,6 @@ xeogl.Object = xeogl.Component.extend({
this.scene._aabbDirty = true;
this.scene._objectDestroyed(this);
}
-});
+}
+
+export {Object};
diff --git a/src/renderer/arrayBuffer.js b/src/renderer/arrayBuffer.js
index 8ebcf9e63..81d7d78ac 100644
--- a/src/renderer/arrayBuffer.js
+++ b/src/renderer/arrayBuffer.js
@@ -2,114 +2,115 @@
* @author xeolabs / https://github.com/xeolabs
*/
-/**
- * Wraps a WebGL ArrayBuffer in convenience methods/properties.
- */
-
-xeogl.renderer.ArrayBuffer = function (gl, type, data, numItems, itemSize, usage) {
+class ArrayBuffer {
- this._gl = gl;
- this.type = type;
- this.allocated = false;
+ constructor(gl, type, data, numItems, itemSize, usage) {
- switch (data.constructor) {
+ this._gl = gl;
+ this.type = type;
+ this.allocated = false;
- case Uint8Array:
- this.itemType = gl.UNSIGNED_BYTE;
- this.itemByteSize = 1;
- break;
+ switch (data.constructor) {
- case Int8Array:
- this.itemType = gl.BYTE;
- this.itemByteSize = 1;
- break;
+ case Uint8Array:
+ this.itemType = gl.UNSIGNED_BYTE;
+ this.itemByteSize = 1;
+ break;
- case Uint16Array:
- this.itemType = gl.UNSIGNED_SHORT;
- this.itemByteSize = 2;
- break;
+ case Int8Array:
+ this.itemType = gl.BYTE;
+ this.itemByteSize = 1;
+ break;
- case Int16Array:
- this.itemType = gl.SHORT;
- this.itemByteSize = 2;
- break;
+ case Uint16Array:
+ this.itemType = gl.UNSIGNED_SHORT;
+ this.itemByteSize = 2;
+ break;
- case Uint32Array:
- this.itemType = gl.UNSIGNED_INT;
- this.itemByteSize = 4;
- break;
+ case Int16Array:
+ this.itemType = gl.SHORT;
+ this.itemByteSize = 2;
+ break;
- case Int32Array:
- this.itemType = gl.INT;
- this.itemByteSize = 4;
- break;
+ case Uint32Array:
+ this.itemType = gl.UNSIGNED_INT;
+ this.itemByteSize = 4;
+ break;
- default:
- this.itemType = gl.FLOAT;
- this.itemByteSize = 4;
- }
+ case Int32Array:
+ this.itemType = gl.INT;
+ this.itemByteSize = 4;
+ break;
- this.usage = usage;
- this.length = 0;
- this.numItems = 0;
- this.itemSize = itemSize;
+ default:
+ this.itemType = gl.FLOAT;
+ this.itemByteSize = 4;
+ }
- this._allocate(data);
-};
+ this.usage = usage;
+ this.length = 0;
+ this.numItems = 0;
+ this.itemSize = itemSize;
-xeogl.renderer.ArrayBuffer.prototype._allocate = function (data) {
- this.allocated = false;
- this._handle = this._gl.createBuffer();
- if (!this._handle) {
- throw "Failed to allocate WebGL ArrayBuffer";
+ this._allocate(data);
}
- if (this._handle) {
- this._gl.bindBuffer(this.type, this._handle);
- this._gl.bufferData(this.type, data, this.usage);
- this._gl.bindBuffer(this.type, null);
- this.length = data.length;
- this.numItems = this.length / this.itemSize;
- this.allocated = true;
- }
-};
-xeogl.renderer.ArrayBuffer.prototype.setData = function (data, offset) {
- if (!this.allocated) {
- return;
- }
- if (data.length > this.length) { // Needs reallocation
- this.destroy();
- this._allocate(data, data.length);
- } else { // No reallocation needed
- this._gl.bindBuffer(this.type, this._handle);
- if (offset || offset === 0) {
- this._gl.bufferSubData(this.type, offset * this.itemByteSize, data);
- } else {
+ _allocate(data) {
+ this.allocated = false;
+ this._handle = this._gl.createBuffer();
+ if (!this._handle) {
+ throw "Failed to allocate WebGL ArrayBuffer";
+ }
+ if (this._handle) {
+ this._gl.bindBuffer(this.type, this._handle);
this._gl.bufferData(this.type, data, this.usage);
+ this._gl.bindBuffer(this.type, null);
+ this.length = data.length;
+ this.numItems = this.length / this.itemSize;
+ this.allocated = true;
}
- this._gl.bindBuffer(this.type, null);
}
-};
-xeogl.renderer.ArrayBuffer.prototype.bind = function () {
- if (!this.allocated) {
- return;
+ setData(data, offset) {
+ if (!this.allocated) {
+ return;
+ }
+ if (data.length > this.length) { // Needs reallocation
+ this.destroy();
+ this._allocate(data, data.length);
+ } else { // No reallocation needed
+ this._gl.bindBuffer(this.type, this._handle);
+ if (offset || offset === 0) {
+ this._gl.bufferSubData(this.type, offset * this.itemByteSize, data);
+ } else {
+ this._gl.bufferData(this.type, data, this.usage);
+ }
+ this._gl.bindBuffer(this.type, null);
+ }
}
- this._gl.bindBuffer(this.type, this._handle);
-};
-xeogl.renderer.ArrayBuffer.prototype.unbind = function () {
- if (!this.allocated) {
- return;
+ bind() {
+ if (!this.allocated) {
+ return;
+ }
+ this._gl.bindBuffer(this.type, this._handle);
}
- this._gl.bindBuffer(this.type, null);
-};
-xeogl.renderer.ArrayBuffer.prototype.destroy = function () {
- if (!this.allocated) {
- return;
+ unbind() {
+ if (!this.allocated) {
+ return;
+ }
+ this._gl.bindBuffer(this.type, null);
}
- this._gl.deleteBuffer(this._handle);
- this._handle = null;
- this.allocated = false;
-};
+
+ destroy() {
+ if (!this.allocated) {
+ return;
+ }
+ this._gl.deleteBuffer(this._handle);
+ this._handle = null;
+ this.allocated = false;
+ }
+}
+
+export{ArrayBuffer};
diff --git a/src/renderer/attribute.js b/src/renderer/attribute.js
index 25247aa32..37c19a6c8 100644
--- a/src/renderer/attribute.js
+++ b/src/renderer/attribute.js
@@ -2,25 +2,21 @@
* @author xeolabs / https://github.com/xeolabs
*/
-/**
- * Wraps a WebGL Attribute in convenience methods/properties.
- */
+class Attribute {
-xeogl.renderer.Attribute = function (gl, location) {
- this._gl = gl;
- this.location = location;
-};
+ constructor(gl, location) {
+ this._gl = gl;
+ this.location = location;
+ }
-xeogl.renderer.Attribute.prototype.bindArrayBuffer = function (buffer, type) {
- if (!buffer) {
- return;
+ bindArrayBuffer(buffer, type) {
+ if (!buffer) {
+ return;
+ }
+ buffer.bind();
+ this._gl.enableVertexAttribArray(this.location);
+ this._gl.vertexAttribPointer(this.location, type === this._gl.BYTE ? 2 : buffer.itemSize, type || this._gl.FLOAT, type === this._gl.BYTE, 0, 0);
}
- buffer.bind();
- this._gl.enableVertexAttribArray(this.location);
- this._gl.vertexAttribPointer(
- this.location,
- type === this._gl.BYTE ? 2 : buffer.itemSize,
- type || this._gl.FLOAT,
- type === this._gl.BYTE,
- 0, 0);
-};
+}
+
+export {Attribute};
diff --git a/src/renderer/draw/drawRenderer.js b/src/renderer/draw/drawRenderer.js
index 9de7e15a4..fc7e50b71 100644
--- a/src/renderer/draw/drawRenderer.js
+++ b/src/renderer/draw/drawRenderer.js
@@ -2,1008 +2,1008 @@
* @author xeolabs / https://github.com/xeolabs
*/
-(function () {
-
- "use strict";
-
- const ids = new xeogl.utils.Map({});
-
- xeogl.renderer.DrawRenderer = function (hash, mesh) {
- this.id = ids.addItem({});
- this._hash = hash;
- this._scene = mesh.scene;
- this._useCount = 0;
- this._shaderSource = new xeogl.renderer.DrawShaderSource(mesh);
- this._allocate(mesh);
- };
-
- const drawRenderers = {};
-
-
- xeogl.renderer.DrawRenderer.get = function (mesh) {
- const scene = mesh.scene;
- const hash = [
- scene.canvas.canvas.id,
- (scene.gammaInput ? "gi;" : ";") + (scene.gammaOutput ? "go" : ""),
- scene._lightsState.getHash(),
- scene._clipsState.getHash(),
- mesh._geometry._state.hash,
- mesh._material._state.hash,
- mesh._state.hash
- ].join(";");
- let renderer = drawRenderers[hash];
- if (!renderer) {
- renderer = new xeogl.renderer.DrawRenderer(hash, mesh);
- if (renderer.errors) {
- console.log(renderer.errors.join("\n"));
- return null;
- }
- drawRenderers[hash] = renderer;
- xeogl.stats.memory.programs++;
+import {Map} from "../../utils/map.js";
+import {DrawShaderSource} from "./drawShaderSource.js";
+import {Program} from "../program.js";
+import {stats} from './../../stats.js';
+import {WEBGL_INFO} from './../../webglInfo.js';
+
+const ids = new Map({});
+
+const DrawRenderer = function (hash, mesh) {
+ this.id = ids.addItem({});
+ this._hash = hash;
+ this._scene = mesh.scene;
+ this._useCount = 0;
+ this._shaderSource = new DrawShaderSource(mesh);
+ this._allocate(mesh);
+};
+
+const drawRenderers = {};
+
+DrawRenderer.get = function (mesh) {
+ const scene = mesh.scene;
+ const hash = [
+ scene.canvas.canvas.id,
+ (scene.gammaInput ? "gi;" : ";") + (scene.gammaOutput ? "go" : ""),
+ scene._lightsState.getHash(),
+ scene._clipsState.getHash(),
+ mesh._geometry._state.hash,
+ mesh._material._state.hash,
+ mesh._state.hash
+ ].join(";");
+ let renderer = drawRenderers[hash];
+ if (!renderer) {
+ renderer = new DrawRenderer(hash, mesh);
+ if (renderer.errors) {
+ console.log(renderer.errors.join("\n"));
+ return null;
}
- renderer._useCount++;
- return renderer;
- };
-
- xeogl.renderer.DrawRenderer.prototype.put = function () {
- if (--this._useCount === 0) {
- ids.removeItem(this.id);
- if (this._program) {
- this._program.destroy();
- }
- delete drawRenderers[this._hash];
- xeogl.stats.memory.programs--;
+ drawRenderers[hash] = renderer;
+ stats.memory.programs++;
+ }
+ renderer._useCount++;
+ return renderer;
+};
+
+DrawRenderer.prototype.put = function () {
+ if (--this._useCount === 0) {
+ ids.removeItem(this.id);
+ if (this._program) {
+ this._program.destroy();
}
- };
+ delete drawRenderers[this._hash];
+ stats.memory.programs--;
+ }
+};
- xeogl.renderer.DrawRenderer.prototype.webglContextRestored = function () {
- this._program = null;
- };
+DrawRenderer.prototype.webglContextRestored = function () {
+ this._program = null;
+};
- xeogl.renderer.DrawRenderer.prototype.drawMesh = function (frame, mesh) {
- if (!this._program) {
- this._allocate(mesh);
- }
- const maxTextureUnits = xeogl.WEBGL_INFO.MAX_TEXTURE_UNITS;
- const scene = mesh.scene;
- const material = mesh._material;
- const gl = scene.canvas.gl;
- const program = this._program;
- const meshState = mesh._state;
- const materialState = mesh._material._state;
- const geometryState = mesh._geometry._state;
-
- if (frame.lastProgramId !== this._program.id) {
- frame.lastProgramId = this._program.id;
- this._bindProgram(frame);
+DrawRenderer.prototype.drawMesh = function (frame, mesh) {
+ if (!this._program) {
+ this._allocate(mesh);
+ }
+ const maxTextureUnits = WEBGL_INFO.MAX_TEXTURE_UNITS;
+ const scene = mesh.scene;
+ const material = mesh._material;
+ const gl = scene.canvas.gl;
+ const program = this._program;
+ const meshState = mesh._state;
+ const materialState = mesh._material._state;
+ const geometryState = mesh._geometry._state;
+
+ if (frame.lastProgramId !== this._program.id) {
+ frame.lastProgramId = this._program.id;
+ this._bindProgram(frame);
+ }
+
+ if (materialState.id !== this._lastMaterialId) {
+
+ frame.textureUnit = this._baseTextureUnit;
+
+ const backfaces = materialState.backfaces;
+ if (frame.backfaces !== backfaces) {
+ if (backfaces) {
+ gl.disable(gl.CULL_FACE);
+ } else {
+ gl.enable(gl.CULL_FACE);
+ }
+ frame.backfaces = backfaces;
}
- if (materialState.id !== this._lastMaterialId) {
-
- frame.textureUnit = this._baseTextureUnit;
-
- const backfaces = materialState.backfaces;
- if (frame.backfaces !== backfaces) {
- if (backfaces) {
- gl.disable(gl.CULL_FACE);
- } else {
- gl.enable(gl.CULL_FACE);
- }
- frame.backfaces = backfaces;
+ const frontface = materialState.frontface;
+ if (frame.frontface !== frontface) {
+ if (frontface) {
+ gl.frontFace(gl.CCW);
+ } else {
+ gl.frontFace(gl.CW);
}
-
- const frontface = materialState.frontface;
- if (frame.frontface !== frontface) {
- if (frontface) {
- gl.frontFace(gl.CCW);
- } else {
- gl.frontFace(gl.CW);
- }
- frame.frontface = frontface;
- }
-
- if (frame.lineWidth !== materialState.lineWidth) {
- gl.lineWidth(materialState.lineWidth);
- frame.lineWidth = materialState.lineWidth;
- }
-
- if (this._uPointSize) {
- gl.uniform1f(this._uPointSize, materialState.pointSize);
- }
-
- switch (materialState.type) {
- case "LambertMaterial":
- if (this._uMaterialAmbient) {
- gl.uniform3fv(this._uMaterialAmbient, materialState.ambient);
- }
- if (this._uMaterialColor) {
- gl.uniform4f(this._uMaterialColor, materialState.color[0], materialState.color[1], materialState.color[2], materialState.alpha);
- }
- if (this._uMaterialEmissive) {
- gl.uniform3fv(this._uMaterialEmissive, materialState.emissive);
- }
- break;
-
- case "PhongMaterial":
- if (this._uMaterialShininess) {
- gl.uniform1f(this._uMaterialShininess, materialState.shininess);
- }
- if (this._uMaterialAmbient) {
- gl.uniform3fv(this._uMaterialAmbient, materialState.ambient);
- }
- if (this._uMaterialDiffuse) {
- gl.uniform3fv(this._uMaterialDiffuse, materialState.diffuse);
- }
- if (this._uMaterialSpecular) {
- gl.uniform3fv(this._uMaterialSpecular, materialState.specular);
- }
- if (this._uMaterialEmissive) {
- gl.uniform3fv(this._uMaterialEmissive, materialState.emissive);
- }
- if (this._uAlphaModeCutoff) {
- gl.uniform4f(
- this._uAlphaModeCutoff,
- 1.0 * materialState.alpha,
- materialState.alphaMode === 1 ? 1.0 : 0.0,
- materialState.alphaCutoff,
- 0);
- }
- if (material._ambientMap && material._ambientMap._state.texture && this._uMaterialAmbientMap) {
- program.bindTexture(this._uMaterialAmbientMap, material._ambientMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uMaterialAmbientMapMatrix) {
- gl.uniformMatrix4fv(this._uMaterialAmbientMapMatrix, false, material._ambientMap._state.matrix);
- }
- }
- if (material._diffuseMap && material._diffuseMap._state.texture && this._uDiffuseMap) {
- program.bindTexture(this._uDiffuseMap, material._diffuseMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uDiffuseMapMatrix) {
- gl.uniformMatrix4fv(this._uDiffuseMapMatrix, false, material._diffuseMap._state.matrix);
- }
- }
- if (material._specularMap && material._specularMap._state.texture && this._uSpecularMap) {
- program.bindTexture(this._uSpecularMap, material._specularMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uSpecularMapMatrix) {
- gl.uniformMatrix4fv(this._uSpecularMapMatrix, false, material._specularMap._state.matrix);
- }
- }
- if (material._emissiveMap && material._emissiveMap._state.texture && this._uEmissiveMap) {
- program.bindTexture(this._uEmissiveMap, material._emissiveMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uEmissiveMapMatrix) {
- gl.uniformMatrix4fv(this._uEmissiveMapMatrix, false, material._emissiveMap._state.matrix);
- }
- }
- if (material._alphaMap && material._alphaMap._state.texture && this._uAlphaMap) {
- program.bindTexture(this._uAlphaMap, material._alphaMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uAlphaMapMatrix) {
- gl.uniformMatrix4fv(this._uAlphaMapMatrix, false, material._alphaMap._state.matrix);
- }
- }
- if (material._reflectivityMap && material._reflectivityMap._state.texture && this._uReflectivityMap) {
- program.bindTexture(this._uReflectivityMap, material._reflectivityMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- if (this._uReflectivityMapMatrix) {
- gl.uniformMatrix4fv(this._uReflectivityMapMatrix, false, material._reflectivityMap._state.matrix);
- }
- }
- if (material._normalMap && material._normalMap._state.texture && this._uNormalMap) {
- program.bindTexture(this._uNormalMap, material._normalMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uNormalMapMatrix) {
- gl.uniformMatrix4fv(this._uNormalMapMatrix, false, material._normalMap._state.matrix);
- }
- }
- if (material._occlusionMap && material._occlusionMap._state.texture && this._uOcclusionMap) {
- program.bindTexture(this._uOcclusionMap, material._occlusionMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uOcclusionMapMatrix) {
- gl.uniformMatrix4fv(this._uOcclusionMapMatrix, false, material._occlusionMap._state.matrix);
- }
- }
- if (material._diffuseFresnel) {
- if (this._uDiffuseFresnelEdgeBias) {
- gl.uniform1f(this._uDiffuseFresnelEdgeBias, material._diffuseFresnel.edgeBias);
- }
- if (this._uDiffuseFresnelCenterBias) {
- gl.uniform1f(this._uDiffuseFresnelCenterBias, material._diffuseFresnel.centerBias);
- }
- if (this._uDiffuseFresnelEdgeColor) {
- gl.uniform3fv(this._uDiffuseFresnelEdgeColor, material._diffuseFresnel.edgeColor);
- }
- if (this._uDiffuseFresnelCenterColor) {
- gl.uniform3fv(this._uDiffuseFresnelCenterColor, material._diffuseFresnel.centerColor);
- }
- if (this._uDiffuseFresnelPower) {
- gl.uniform1f(this._uDiffuseFresnelPower, material._diffuseFresnel.power);
- }
- }
- if (material._specularFresnel) {
- if (this._uSpecularFresnelEdgeBias) {
- gl.uniform1f(this._uSpecularFresnelEdgeBias, material._specularFresnel.edgeBias);
- }
- if (this._uSpecularFresnelCenterBias) {
- gl.uniform1f(this._uSpecularFresnelCenterBias, material._specularFresnel.centerBias);
- }
- if (this._uSpecularFresnelEdgeColor) {
- gl.uniform3fv(this._uSpecularFresnelEdgeColor, material._specularFresnel.edgeColor);
- }
- if (this._uSpecularFresnelCenterColor) {
- gl.uniform3fv(this._uSpecularFresnelCenterColor, material._specularFresnel.centerColor);
- }
- if (this._uSpecularFresnelPower) {
- gl.uniform1f(this._uSpecularFresnelPower, material._specularFresnel.power);
- }
- }
- if (material._alphaFresnel) {
- if (this._uAlphaFresnelEdgeBias) {
- gl.uniform1f(this._uAlphaFresnelEdgeBias, material._alphaFresnel.edgeBias);
- }
- if (this._uAlphaFresnelCenterBias) {
- gl.uniform1f(this._uAlphaFresnelCenterBias, material._alphaFresnel.centerBias);
- }
- if (this._uAlphaFresnelEdgeColor) {
- gl.uniform3fv(this._uAlphaFresnelEdgeColor, material._alphaFresnel.edgeColor);
- }
- if (this._uAlphaFresnelCenterColor) {
- gl.uniform3fv(this._uAlphaFresnelCenterColor, material._alphaFresnel.centerColor);
- }
- if (this._uAlphaFresnelPower) {
- gl.uniform1f(this._uAlphaFresnelPower, material._alphaFresnel.power);
- }
- }
- if (material._reflectivityFresnel) {
- if (this._uReflectivityFresnelEdgeBias) {
- gl.uniform1f(this._uReflectivityFresnelEdgeBias, material._reflectivityFresnel.edgeBias);
- }
- if (this._uReflectivityFresnelCenterBias) {
- gl.uniform1f(this._uReflectivityFresnelCenterBias, material._reflectivityFresnel.centerBias);
- }
- if (this._uReflectivityFresnelEdgeColor) {
- gl.uniform3fv(this._uReflectivityFresnelEdgeColor, material._reflectivityFresnel.edgeColor);
- }
- if (this._uReflectivityFresnelCenterColor) {
- gl.uniform3fv(this._uReflectivityFresnelCenterColor, material._reflectivityFresnel.centerColor);
- }
- if (this._uReflectivityFresnelPower) {
- gl.uniform1f(this._uReflectivityFresnelPower, material._reflectivityFresnel.power);
- }
- }
- if (material._emissiveFresnel) {
- if (this._uEmissiveFresnelEdgeBias) {
- gl.uniform1f(this._uEmissiveFresnelEdgeBias, material._emissiveFresnel.edgeBias);
- }
- if (this._uEmissiveFresnelCenterBias) {
- gl.uniform1f(this._uEmissiveFresnelCenterBias, material._emissiveFresnel.centerBias);
- }
- if (this._uEmissiveFresnelEdgeColor) {
- gl.uniform3fv(this._uEmissiveFresnelEdgeColor, material._emissiveFresnel.edgeColor);
- }
- if (this._uEmissiveFresnelCenterColor) {
- gl.uniform3fv(this._uEmissiveFresnelCenterColor, material._emissiveFresnel.centerColor);
- }
- if (this._uEmissiveFresnelPower) {
- gl.uniform1f(this._uEmissiveFresnelPower, material._emissiveFresnel.power);
- }
- }
- break;
-
- case "MetallicMaterial":
- if (this._uBaseColor) {
- gl.uniform3fv(this._uBaseColor, materialState.baseColor);
- }
- if (this._uMaterialMetallic) {
- gl.uniform1f(this._uMaterialMetallic, materialState.metallic);
- }
- if (this._uMaterialRoughness) {
- gl.uniform1f(this._uMaterialRoughness, materialState.roughness);
- }
- if (this._uMaterialSpecularF0) {
- gl.uniform1f(this._uMaterialSpecularF0, materialState.specularF0);
- }
- if (this._uMaterialEmissive) {
- gl.uniform3fv(this._uMaterialEmissive, materialState.emissive);
- }
- if (this._uAlphaModeCutoff) {
- gl.uniform4f(
- this._uAlphaModeCutoff,
- 1.0 * materialState.alpha,
- materialState.alphaMode === 1 ? 1.0 : 0.0,
- materialState.alphaCutoff,
- 0.0);
- }
- const baseColorMap = material._baseColorMap;
- if (baseColorMap && baseColorMap._state.texture && this._uBaseColorMap) {
- program.bindTexture(this._uBaseColorMap, baseColorMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uBaseColorMapMatrix) {
- gl.uniformMatrix4fv(this._uBaseColorMapMatrix, false, baseColorMap._state.matrix);
- }
- }
- const metallicMap = material._metallicMap;
- if (metallicMap && metallicMap._state.texture && this._uMetallicMap) {
- program.bindTexture(this._uMetallicMap, metallicMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uMetallicMapMatrix) {
- gl.uniformMatrix4fv(this._uMetallicMapMatrix, false, metallicMap._state.matrix);
- }
- }
- const roughnessMap = material._roughnessMap;
- if (roughnessMap && roughnessMap._state.texture && this._uRoughnessMap) {
- program.bindTexture(this._uRoughnessMap, roughnessMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uRoughnessMapMatrix) {
- gl.uniformMatrix4fv(this._uRoughnessMapMatrix, false, roughnessMap._state.matrix);
- }
- }
- const metallicRoughnessMap = material._metallicRoughnessMap;
- if (metallicRoughnessMap && metallicRoughnessMap._state.texture && this._uMetallicRoughnessMap) {
- program.bindTexture(this._uMetallicRoughnessMap, metallicRoughnessMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uMetallicRoughnessMapMatrix) {
- gl.uniformMatrix4fv(this._uMetallicRoughnessMapMatrix, false, metallicRoughnessMap._state.matrix);
- }
- }
- var emissiveMap = material._emissiveMap;
- if (emissiveMap && emissiveMap._state.texture && this._uEmissiveMap) {
- program.bindTexture(this._uEmissiveMap, emissiveMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uEmissiveMapMatrix) {
- gl.uniformMatrix4fv(this._uEmissiveMapMatrix, false, emissiveMap._state.matrix);
- }
- }
- var occlusionMap = material._occlusionMap;
- if (occlusionMap && material._occlusionMap._state.texture && this._uOcclusionMap) {
- program.bindTexture(this._uOcclusionMap, occlusionMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uOcclusionMapMatrix) {
- gl.uniformMatrix4fv(this._uOcclusionMapMatrix, false, occlusionMap._state.matrix);
- }
- }
- var alphaMap = material._alphaMap;
- if (alphaMap && alphaMap._state.texture && this._uAlphaMap) {
- program.bindTexture(this._uAlphaMap, alphaMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uAlphaMapMatrix) {
- gl.uniformMatrix4fv(this._uAlphaMapMatrix, false, alphaMap._state.matrix);
- }
- }
- var normalMap = material._normalMap;
- if (normalMap && normalMap._state.texture && this._uNormalMap) {
- program.bindTexture(this._uNormalMap, normalMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uNormalMapMatrix) {
- gl.uniformMatrix4fv(this._uNormalMapMatrix, false, normalMap._state.matrix);
- }
- }
- break;
-
- case "SpecularMaterial":
- if (this._uMaterialDiffuse) {
- gl.uniform3fv(this._uMaterialDiffuse, materialState.diffuse);
- }
- if (this._uMaterialSpecular) {
- gl.uniform3fv(this._uMaterialSpecular, materialState.specular);
- }
- if (this._uMaterialGlossiness) {
- gl.uniform1f(this._uMaterialGlossiness, materialState.glossiness);
- }
- if (this._uMaterialReflectivity) {
- gl.uniform1f(this._uMaterialReflectivity, materialState.reflectivity);
- }
- if (this._uMaterialEmissive) {
- gl.uniform3fv(this._uMaterialEmissive, materialState.emissive);
- }
- if (this._uAlphaModeCutoff) {
- gl.uniform4f(
- this._uAlphaModeCutoff,
- 1.0 * materialState.alpha,
- materialState.alphaMode === 1 ? 1.0 : 0.0,
- materialState.alphaCutoff,
- 0.0);
- }
- const diffuseMap = material._diffuseMap;
- if (diffuseMap && diffuseMap._state.texture && this._uDiffuseMap) {
- program.bindTexture(this._uDiffuseMap, diffuseMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uDiffuseMapMatrix) {
- gl.uniformMatrix4fv(this._uDiffuseMapMatrix, false, diffuseMap._state.matrix);
- }
- }
- const specularMap = material._specularMap;
- if (specularMap && specularMap._state.texture && this._uSpecularMap) {
- program.bindTexture(this._uSpecularMap, specularMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uSpecularMapMatrix) {
- gl.uniformMatrix4fv(this._uSpecularMapMatrix, false, specularMap._state.matrix);
- }
- }
- const glossinessMap = material._glossinessMap;
- if (glossinessMap && glossinessMap._state.texture && this._uGlossinessMap) {
- program.bindTexture(this._uGlossinessMap, glossinessMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uGlossinessMapMatrix) {
- gl.uniformMatrix4fv(this._uGlossinessMapMatrix, false, glossinessMap._state.matrix);
- }
- }
- const specularGlossinessMap = material._specularGlossinessMap;
- if (specularGlossinessMap && specularGlossinessMap._state.texture && this._uSpecularGlossinessMap) {
- program.bindTexture(this._uSpecularGlossinessMap, specularGlossinessMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uSpecularGlossinessMapMatrix) {
- gl.uniformMatrix4fv(this._uSpecularGlossinessMapMatrix, false, specularGlossinessMap._state.matrix);
- }
- }
- var emissiveMap = material._emissiveMap;
- if (emissiveMap && emissiveMap._state.texture && this._uEmissiveMap) {
- program.bindTexture(this._uEmissiveMap, emissiveMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uEmissiveMapMatrix) {
- gl.uniformMatrix4fv(this._uEmissiveMapMatrix, false, emissiveMap._state.matrix);
- }
- }
- var occlusionMap = material._occlusionMap;
- if (occlusionMap && occlusionMap._state.texture && this._uOcclusionMap) {
- program.bindTexture(this._uOcclusionMap, occlusionMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uOcclusionMapMatrix) {
- gl.uniformMatrix4fv(this._uOcclusionMapMatrix, false, occlusionMap._state.matrix);
- }
- }
- var alphaMap = material._alphaMap;
- if (alphaMap && alphaMap._state.texture && this._uAlphaMap) {
- program.bindTexture(this._uAlphaMap, alphaMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uAlphaMapMatrix) {
- gl.uniformMatrix4fv(this._uAlphaMapMatrix, false, alphaMap._state.matrix);
- }
- }
- var normalMap = material._normalMap;
- if (normalMap && normalMap._state.texture && this._uNormalMap) {
- program.bindTexture(this._uNormalMap, normalMap._state.texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- if (this._uNormalMapMatrix) {
- gl.uniformMatrix4fv(this._uNormalMapMatrix, false, normalMap._state.matrix);
- }
- }
- break;
- }
- this._lastMaterialId = materialState.id;
+ frame.frontface = frontface;
}
- gl.uniformMatrix4fv(this._uModelMatrix, gl.FALSE, mesh.worldMatrix);
- if (this._uModelNormalMatrix) {
- gl.uniformMatrix4fv(this._uModelNormalMatrix, gl.FALSE, mesh.worldNormalMatrix);
+ if (frame.lineWidth !== materialState.lineWidth) {
+ gl.lineWidth(materialState.lineWidth);
+ frame.lineWidth = materialState.lineWidth;
}
- if (this._uClippable) {
- gl.uniform1i(this._uClippable, meshState.clippable);
+ if (this._uPointSize) {
+ gl.uniform1f(this._uPointSize, materialState.pointSize);
}
- if (this._uColorize) {
- const colorize = meshState.colorize;
- const lastColorize = this._lastColorize;
- if (lastColorize[0] !== colorize[0] ||
- lastColorize[1] !== colorize[1] ||
- lastColorize[2] !== colorize[2] ||
- lastColorize[3] !== colorize[3]) {
- gl.uniform4fv(this._uColorize, colorize);
- lastColorize[0] = colorize[0];
- lastColorize[1] = colorize[1];
- lastColorize[2] = colorize[2];
- lastColorize[3] = colorize[3];
- }
- }
+ switch (materialState.type) {
+ case "LambertMaterial":
+ if (this._uMaterialAmbient) {
+ gl.uniform3fv(this._uMaterialAmbient, materialState.ambient);
+ }
+ if (this._uMaterialColor) {
+ gl.uniform4f(this._uMaterialColor, materialState.color[0], materialState.color[1], materialState.color[2], materialState.alpha);
+ }
+ if (this._uMaterialEmissive) {
+ gl.uniform3fv(this._uMaterialEmissive, materialState.emissive);
+ }
+ break;
- if (geometryState.combined) {
- const vertexBufs = mesh._geometry._getVertexBufs();
- if (vertexBufs.id !== this._lastVertexBufsId) {
- if (vertexBufs.positionsBuf && this._aPosition) {
- this._aPosition.bindArrayBuffer(vertexBufs.positionsBuf, vertexBufs.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
- frame.bindArray++;
+ case "PhongMaterial":
+ if (this._uMaterialShininess) {
+ gl.uniform1f(this._uMaterialShininess, materialState.shininess);
}
- if (vertexBufs.normalsBuf && this._aNormal) {
- this._aNormal.bindArrayBuffer(vertexBufs.normalsBuf, vertexBufs.quantized ? gl.BYTE : gl.FLOAT);
- frame.bindArray++;
+ if (this._uMaterialAmbient) {
+ gl.uniform3fv(this._uMaterialAmbient, materialState.ambient);
}
- if (vertexBufs.uvBuf && this._aUV) {
- this._aUV.bindArrayBuffer(vertexBufs.uvBuf, geometryState.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
- frame.bindArray++;
+ if (this._uMaterialDiffuse) {
+ gl.uniform3fv(this._uMaterialDiffuse, materialState.diffuse);
}
- if (vertexBufs.colorsBuf && this._aColor) {
- this._aColor.bindArrayBuffer(vertexBufs.colorsBuf);
- frame.bindArray++;
+ if (this._uMaterialSpecular) {
+ gl.uniform3fv(this._uMaterialSpecular, materialState.specular);
}
- if (vertexBufs.flagsBuf && this._aFlags) {
- this._aFlags.bindArrayBuffer(vertexBufs.flagsBuf, gl.UNSIGNED_SHORT);
- frame.bindArray++;
+ if (this._uMaterialEmissive) {
+ gl.uniform3fv(this._uMaterialEmissive, materialState.emissive);
}
- this._lastVertexBufsId = vertexBufs.id;
- }
- }
-
- // Bind VBOs
-
- if (geometryState.id !== this._lastGeometryId) {
- if (this._uPositionsDecodeMatrix) {
- gl.uniformMatrix4fv(this._uPositionsDecodeMatrix, false, geometryState.positionsDecodeMatrix);
- }
- if (this._uUVDecodeMatrix) {
- gl.uniformMatrix3fv(this._uUVDecodeMatrix, false, geometryState.uvDecodeMatrix);
- }
- if (geometryState.combined) { // VBOs were bound by the VertexBufs logic above
- if (geometryState.indicesBufCombined) {
- geometryState.indicesBufCombined.bind();
- frame.bindArray++;
+ if (this._uAlphaModeCutoff) {
+ gl.uniform4f(
+ this._uAlphaModeCutoff,
+ 1.0 * materialState.alpha,
+ materialState.alphaMode === 1 ? 1.0 : 0.0,
+ materialState.alphaCutoff,
+ 0);
}
- } else {
- if (this._aPosition) {
- this._aPosition.bindArrayBuffer(geometryState.positionsBuf, geometryState.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
- frame.bindArray++;
+ if (material._ambientMap && material._ambientMap._state.texture && this._uMaterialAmbientMap) {
+ program.bindTexture(this._uMaterialAmbientMap, material._ambientMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uMaterialAmbientMapMatrix) {
+ gl.uniformMatrix4fv(this._uMaterialAmbientMapMatrix, false, material._ambientMap._state.matrix);
+ }
}
- if (this._aNormal) {
- this._aNormal.bindArrayBuffer(geometryState.normalsBuf, geometryState.quantized ? gl.BYTE : gl.FLOAT);
- frame.bindArray++;
+ if (material._diffuseMap && material._diffuseMap._state.texture && this._uDiffuseMap) {
+ program.bindTexture(this._uDiffuseMap, material._diffuseMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uDiffuseMapMatrix) {
+ gl.uniformMatrix4fv(this._uDiffuseMapMatrix, false, material._diffuseMap._state.matrix);
+ }
}
- if (this._aUV) {
- this._aUV.bindArrayBuffer(geometryState.uvBuf, geometryState.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
- frame.bindArray++;
+ if (material._specularMap && material._specularMap._state.texture && this._uSpecularMap) {
+ program.bindTexture(this._uSpecularMap, material._specularMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uSpecularMapMatrix) {
+ gl.uniformMatrix4fv(this._uSpecularMapMatrix, false, material._specularMap._state.matrix);
+ }
}
- if (this._aColor) {
- this._aColor.bindArrayBuffer(geometryState.colorsBuf);
- frame.bindArray++;
+ if (material._emissiveMap && material._emissiveMap._state.texture && this._uEmissiveMap) {
+ program.bindTexture(this._uEmissiveMap, material._emissiveMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uEmissiveMapMatrix) {
+ gl.uniformMatrix4fv(this._uEmissiveMapMatrix, false, material._emissiveMap._state.matrix);
+ }
}
- if (this._aFlags) {
- this._aFlags.bindArrayBuffer(geometryState.flagsBuf);
- frame.bindArray++;
+ if (material._alphaMap && material._alphaMap._state.texture && this._uAlphaMap) {
+ program.bindTexture(this._uAlphaMap, material._alphaMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uAlphaMapMatrix) {
+ gl.uniformMatrix4fv(this._uAlphaMapMatrix, false, material._alphaMap._state.matrix);
+ }
}
- if (geometryState.indicesBuf) {
- geometryState.indicesBuf.bind();
- frame.bindArray++;
- // gl.drawElements(geometryState.primitive, geometryState.indicesBuf.numItems, geometryState.indicesBuf.itemType, 0);
- // frame.drawElements++;
- } else if (geometryState.positions) {
- // gl.drawArrays(gl.TRIANGLES, 0, geometryState.positions.numItems);
- // frame.drawArrays++;
+ if (material._reflectivityMap && material._reflectivityMap._state.texture && this._uReflectivityMap) {
+ program.bindTexture(this._uReflectivityMap, material._reflectivityMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ if (this._uReflectivityMapMatrix) {
+ gl.uniformMatrix4fv(this._uReflectivityMapMatrix, false, material._reflectivityMap._state.matrix);
+ }
}
- }
- this._lastGeometryId = geometryState.id;
- }
-
- // Draw (indices bound in prev step)
-
- if (geometryState.combined) {
- if (geometryState.indicesBufCombined) { // Geometry indices into portion of uber-array
- gl.drawElements(geometryState.primitive, geometryState.indicesBufCombined.numItems, geometryState.indicesBufCombined.itemType, 0);
- frame.drawElements++;
- } else {
- // TODO: drawArrays() with VertexBufs positions
- }
- } else {
- if (geometryState.indicesBuf) {
- gl.drawElements(geometryState.primitive, geometryState.indicesBuf.numItems, geometryState.indicesBuf.itemType, 0);
- frame.drawElements++;
- } else if (geometryState.positions) {
- gl.drawArrays(gl.TRIANGLES, 0, geometryState.positions.numItems);
- frame.drawArrays++;
- }
- }
- };
-
- xeogl.renderer.DrawRenderer.prototype._allocate = function (mesh) {
- const gl = mesh.scene.canvas.gl;
- const material = mesh._material;
- const lightsState = mesh.scene._lightsState;
- const clipsState = mesh.scene._clipsState;
- const materialState = mesh._material._state;
- this._program = new xeogl.renderer.Program(gl, this._shaderSource);
- if (this._program.errors) {
- this.errors = this._program.errors;
- return;
- }
- const program = this._program;
- this._uPositionsDecodeMatrix = program.getLocation("positionsDecodeMatrix");
- this._uUVDecodeMatrix = program.getLocation("uvDecodeMatrix");
- this._uModelMatrix = program.getLocation("modelMatrix");
- this._uModelNormalMatrix = program.getLocation("modelNormalMatrix");
- this._uViewMatrix = program.getLocation("viewMatrix");
- this._uViewNormalMatrix = program.getLocation("viewNormalMatrix");
- this._uProjMatrix = program.getLocation("projMatrix");
- this._uGammaFactor = program.getLocation("gammaFactor");
- this._uLightAmbient = [];
- this._uLightColor = [];
- this._uLightDir = [];
- this._uLightPos = [];
- this._uLightAttenuation = [];
- this._uShadowViewMatrix = [];
- this._uShadowProjMatrix = [];
-
- const lights = lightsState.lights;
- let light;
-
- for (var i = 0, len = lights.length; i < len; i++) {
- light = lights[i];
- switch (light.type) {
-
- case "ambient":
- this._uLightAmbient[i] = program.getLocation("lightAmbient");
- break;
-
- case "dir":
- this._uLightColor[i] = program.getLocation("lightColor" + i);
- this._uLightPos[i] = null;
- this._uLightDir[i] = program.getLocation("lightDir" + i);
- break;
-
- case "point":
- this._uLightColor[i] = program.getLocation("lightColor" + i);
- this._uLightPos[i] = program.getLocation("lightPos" + i);
- this._uLightDir[i] = null;
- this._uLightAttenuation[i] = program.getLocation("lightAttenuation" + i);
- break;
-
- case "spot":
- this._uLightColor[i] = program.getLocation("lightColor" + i);
- this._uLightPos[i] = program.getLocation("lightPos" + i);
- this._uLightDir[i] = program.getLocation("lightDir" + i);
- this._uLightAttenuation[i] = program.getLocation("lightAttenuation" + i);
- break;
- }
-
- if (light.shadow) {
- this._uShadowViewMatrix[i] = program.getLocation("shadowViewMatrix" + i);
- this._uShadowProjMatrix[i] = program.getLocation("shadowProjMatrix" + i);
- }
- }
-
- if (lightsState.lightMaps.length > 0) {
- this._uLightMap = "lightMap";
- }
-
- if (lightsState.reflectionMaps.length > 0) {
- this._uReflectionMap = "reflectionMap";
- }
-
- this._uClips = [];
- const clips = clipsState.clips;
- for (var i = 0, len = clips.length; i < len; i++) {
- this._uClips.push({
- active: program.getLocation("clipActive" + i),
- pos: program.getLocation("clipPos" + i),
- dir: program.getLocation("clipDir" + i)
- });
- }
-
- this._uPointSize = program.getLocation("pointSize");
-
- switch (materialState.type) {
- case "LambertMaterial":
- this._uMaterialColor = program.getLocation("materialColor");
- this._uMaterialEmissive = program.getLocation("materialEmissive");
- this._uAlphaModeCutoff = program.getLocation("materialAlphaModeCutoff");
- break;
-
- case "PhongMaterial":
- this._uMaterialAmbient = program.getLocation("materialAmbient");
- this._uMaterialDiffuse = program.getLocation("materialDiffuse");
- this._uMaterialSpecular = program.getLocation("materialSpecular");
- this._uMaterialEmissive = program.getLocation("materialEmissive");
- this._uAlphaModeCutoff = program.getLocation("materialAlphaModeCutoff");
- this._uMaterialShininess = program.getLocation("materialShininess");
- if (material._ambientMap) {
- this._uMaterialAmbientMap = "ambientMap";
- this._uMaterialAmbientMapMatrix = program.getLocation("ambientMapMatrix");
- }
- if (material._diffuseMap) {
- this._uDiffuseMap = "diffuseMap";
- this._uDiffuseMapMatrix = program.getLocation("diffuseMapMatrix");
- }
- if (material._specularMap) {
- this._uSpecularMap = "specularMap";
- this._uSpecularMapMatrix = program.getLocation("specularMapMatrix");
- }
- if (material._emissiveMap) {
- this._uEmissiveMap = "emissiveMap";
- this._uEmissiveMapMatrix = program.getLocation("emissiveMapMatrix");
- }
- if (material._alphaMap) {
- this._uAlphaMap = "alphaMap";
- this._uAlphaMapMatrix = program.getLocation("alphaMapMatrix");
- }
- if (material._reflectivityMap) {
- this._uReflectivityMap = "reflectivityMap";
- this._uReflectivityMapMatrix = program.getLocation("reflectivityMapMatrix");
- }
- if (material._normalMap) {
- this._uNormalMap = "normalMap";
- this._uNormalMapMatrix = program.getLocation("normalMapMatrix");
- }
- if (material._occlusionMap) {
- this._uOcclusionMap = "occlusionMap";
- this._uOcclusionMapMatrix = program.getLocation("occlusionMapMatrix");
+ if (material._normalMap && material._normalMap._state.texture && this._uNormalMap) {
+ program.bindTexture(this._uNormalMap, material._normalMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uNormalMapMatrix) {
+ gl.uniformMatrix4fv(this._uNormalMapMatrix, false, material._normalMap._state.matrix);
+ }
+ }
+ if (material._occlusionMap && material._occlusionMap._state.texture && this._uOcclusionMap) {
+ program.bindTexture(this._uOcclusionMap, material._occlusionMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uOcclusionMapMatrix) {
+ gl.uniformMatrix4fv(this._uOcclusionMapMatrix, false, material._occlusionMap._state.matrix);
+ }
}
if (material._diffuseFresnel) {
- this._uDiffuseFresnelEdgeBias = program.getLocation("diffuseFresnelEdgeBias");
- this._uDiffuseFresnelCenterBias = program.getLocation("diffuseFresnelCenterBias");
- this._uDiffuseFresnelEdgeColor = program.getLocation("diffuseFresnelEdgeColor");
- this._uDiffuseFresnelCenterColor = program.getLocation("diffuseFresnelCenterColor");
- this._uDiffuseFresnelPower = program.getLocation("diffuseFresnelPower");
+ if (this._uDiffuseFresnelEdgeBias) {
+ gl.uniform1f(this._uDiffuseFresnelEdgeBias, material._diffuseFresnel.edgeBias);
+ }
+ if (this._uDiffuseFresnelCenterBias) {
+ gl.uniform1f(this._uDiffuseFresnelCenterBias, material._diffuseFresnel.centerBias);
+ }
+ if (this._uDiffuseFresnelEdgeColor) {
+ gl.uniform3fv(this._uDiffuseFresnelEdgeColor, material._diffuseFresnel.edgeColor);
+ }
+ if (this._uDiffuseFresnelCenterColor) {
+ gl.uniform3fv(this._uDiffuseFresnelCenterColor, material._diffuseFresnel.centerColor);
+ }
+ if (this._uDiffuseFresnelPower) {
+ gl.uniform1f(this._uDiffuseFresnelPower, material._diffuseFresnel.power);
+ }
}
if (material._specularFresnel) {
- this._uSpecularFresnelEdgeBias = program.getLocation("specularFresnelEdgeBias");
- this._uSpecularFresnelCenterBias = program.getLocation("specularFresnelCenterBias");
- this._uSpecularFresnelEdgeColor = program.getLocation("specularFresnelEdgeColor");
- this._uSpecularFresnelCenterColor = program.getLocation("specularFresnelCenterColor");
- this._uSpecularFresnelPower = program.getLocation("specularFresnelPower");
+ if (this._uSpecularFresnelEdgeBias) {
+ gl.uniform1f(this._uSpecularFresnelEdgeBias, material._specularFresnel.edgeBias);
+ }
+ if (this._uSpecularFresnelCenterBias) {
+ gl.uniform1f(this._uSpecularFresnelCenterBias, material._specularFresnel.centerBias);
+ }
+ if (this._uSpecularFresnelEdgeColor) {
+ gl.uniform3fv(this._uSpecularFresnelEdgeColor, material._specularFresnel.edgeColor);
+ }
+ if (this._uSpecularFresnelCenterColor) {
+ gl.uniform3fv(this._uSpecularFresnelCenterColor, material._specularFresnel.centerColor);
+ }
+ if (this._uSpecularFresnelPower) {
+ gl.uniform1f(this._uSpecularFresnelPower, material._specularFresnel.power);
+ }
}
if (material._alphaFresnel) {
- this._uAlphaFresnelEdgeBias = program.getLocation("alphaFresnelEdgeBias");
- this._uAlphaFresnelCenterBias = program.getLocation("alphaFresnelCenterBias");
- this._uAlphaFresnelEdgeColor = program.getLocation("alphaFresnelEdgeColor");
- this._uAlphaFresnelCenterColor = program.getLocation("alphaFresnelCenterColor");
- this._uAlphaFresnelPower = program.getLocation("alphaFresnelPower");
+ if (this._uAlphaFresnelEdgeBias) {
+ gl.uniform1f(this._uAlphaFresnelEdgeBias, material._alphaFresnel.edgeBias);
+ }
+ if (this._uAlphaFresnelCenterBias) {
+ gl.uniform1f(this._uAlphaFresnelCenterBias, material._alphaFresnel.centerBias);
+ }
+ if (this._uAlphaFresnelEdgeColor) {
+ gl.uniform3fv(this._uAlphaFresnelEdgeColor, material._alphaFresnel.edgeColor);
+ }
+ if (this._uAlphaFresnelCenterColor) {
+ gl.uniform3fv(this._uAlphaFresnelCenterColor, material._alphaFresnel.centerColor);
+ }
+ if (this._uAlphaFresnelPower) {
+ gl.uniform1f(this._uAlphaFresnelPower, material._alphaFresnel.power);
+ }
}
if (material._reflectivityFresnel) {
- this._uReflectivityFresnelEdgeBias = program.getLocation("reflectivityFresnelEdgeBias");
- this._uReflectivityFresnelCenterBias = program.getLocation("reflectivityFresnelCenterBias");
- this._uReflectivityFresnelEdgeColor = program.getLocation("reflectivityFresnelEdgeColor");
- this._uReflectivityFresnelCenterColor = program.getLocation("reflectivityFresnelCenterColor");
- this._uReflectivityFresnelPower = program.getLocation("reflectivityFresnelPower");
+ if (this._uReflectivityFresnelEdgeBias) {
+ gl.uniform1f(this._uReflectivityFresnelEdgeBias, material._reflectivityFresnel.edgeBias);
+ }
+ if (this._uReflectivityFresnelCenterBias) {
+ gl.uniform1f(this._uReflectivityFresnelCenterBias, material._reflectivityFresnel.centerBias);
+ }
+ if (this._uReflectivityFresnelEdgeColor) {
+ gl.uniform3fv(this._uReflectivityFresnelEdgeColor, material._reflectivityFresnel.edgeColor);
+ }
+ if (this._uReflectivityFresnelCenterColor) {
+ gl.uniform3fv(this._uReflectivityFresnelCenterColor, material._reflectivityFresnel.centerColor);
+ }
+ if (this._uReflectivityFresnelPower) {
+ gl.uniform1f(this._uReflectivityFresnelPower, material._reflectivityFresnel.power);
+ }
}
if (material._emissiveFresnel) {
- this._uEmissiveFresnelEdgeBias = program.getLocation("emissiveFresnelEdgeBias");
- this._uEmissiveFresnelCenterBias = program.getLocation("emissiveFresnelCenterBias");
- this._uEmissiveFresnelEdgeColor = program.getLocation("emissiveFresnelEdgeColor");
- this._uEmissiveFresnelCenterColor = program.getLocation("emissiveFresnelCenterColor");
- this._uEmissiveFresnelPower = program.getLocation("emissiveFresnelPower");
+ if (this._uEmissiveFresnelEdgeBias) {
+ gl.uniform1f(this._uEmissiveFresnelEdgeBias, material._emissiveFresnel.edgeBias);
+ }
+ if (this._uEmissiveFresnelCenterBias) {
+ gl.uniform1f(this._uEmissiveFresnelCenterBias, material._emissiveFresnel.centerBias);
+ }
+ if (this._uEmissiveFresnelEdgeColor) {
+ gl.uniform3fv(this._uEmissiveFresnelEdgeColor, material._emissiveFresnel.edgeColor);
+ }
+ if (this._uEmissiveFresnelCenterColor) {
+ gl.uniform3fv(this._uEmissiveFresnelCenterColor, material._emissiveFresnel.centerColor);
+ }
+ if (this._uEmissiveFresnelPower) {
+ gl.uniform1f(this._uEmissiveFresnelPower, material._emissiveFresnel.power);
+ }
}
break;
case "MetallicMaterial":
- this._uBaseColor = program.getLocation("materialBaseColor");
- this._uMaterialMetallic = program.getLocation("materialMetallic");
- this._uMaterialRoughness = program.getLocation("materialRoughness");
- this._uMaterialSpecularF0 = program.getLocation("materialSpecularF0");
- this._uMaterialEmissive = program.getLocation("materialEmissive");
- this._uAlphaModeCutoff = program.getLocation("materialAlphaModeCutoff");
- if (material._baseColorMap) {
- this._uBaseColorMap = "baseColorMap";
- this._uBaseColorMapMatrix = program.getLocation("baseColorMapMatrix");
- }
- if (material._metallicMap) {
- this._uMetallicMap = "metallicMap";
- this._uMetallicMapMatrix = program.getLocation("metallicMapMatrix");
- }
- if (material._roughnessMap) {
- this._uRoughnessMap = "roughnessMap";
- this._uRoughnessMapMatrix = program.getLocation("roughnessMapMatrix");
- }
- if (material._metallicRoughnessMap) {
- this._uMetallicRoughnessMap = "metallicRoughnessMap";
- this._uMetallicRoughnessMapMatrix = program.getLocation("metallicRoughnessMapMatrix");
- }
- if (material._emissiveMap) {
- this._uEmissiveMap = "emissiveMap";
- this._uEmissiveMapMatrix = program.getLocation("emissiveMapMatrix");
- }
- if (material._occlusionMap) {
- this._uOcclusionMap = "occlusionMap";
- this._uOcclusionMapMatrix = program.getLocation("occlusionMapMatrix");
- }
- if (material._alphaMap) {
- this._uAlphaMap = "alphaMap";
- this._uAlphaMapMatrix = program.getLocation("alphaMapMatrix");
- }
- if (material._normalMap) {
- this._uNormalMap = "normalMap";
- this._uNormalMapMatrix = program.getLocation("normalMapMatrix");
+ if (this._uBaseColor) {
+ gl.uniform3fv(this._uBaseColor, materialState.baseColor);
+ }
+ if (this._uMaterialMetallic) {
+ gl.uniform1f(this._uMaterialMetallic, materialState.metallic);
+ }
+ if (this._uMaterialRoughness) {
+ gl.uniform1f(this._uMaterialRoughness, materialState.roughness);
+ }
+ if (this._uMaterialSpecularF0) {
+ gl.uniform1f(this._uMaterialSpecularF0, materialState.specularF0);
+ }
+ if (this._uMaterialEmissive) {
+ gl.uniform3fv(this._uMaterialEmissive, materialState.emissive);
+ }
+ if (this._uAlphaModeCutoff) {
+ gl.uniform4f(
+ this._uAlphaModeCutoff,
+ 1.0 * materialState.alpha,
+ materialState.alphaMode === 1 ? 1.0 : 0.0,
+ materialState.alphaCutoff,
+ 0.0);
+ }
+ const baseColorMap = material._baseColorMap;
+ if (baseColorMap && baseColorMap._state.texture && this._uBaseColorMap) {
+ program.bindTexture(this._uBaseColorMap, baseColorMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uBaseColorMapMatrix) {
+ gl.uniformMatrix4fv(this._uBaseColorMapMatrix, false, baseColorMap._state.matrix);
+ }
+ }
+ const metallicMap = material._metallicMap;
+ if (metallicMap && metallicMap._state.texture && this._uMetallicMap) {
+ program.bindTexture(this._uMetallicMap, metallicMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uMetallicMapMatrix) {
+ gl.uniformMatrix4fv(this._uMetallicMapMatrix, false, metallicMap._state.matrix);
+ }
+ }
+ const roughnessMap = material._roughnessMap;
+ if (roughnessMap && roughnessMap._state.texture && this._uRoughnessMap) {
+ program.bindTexture(this._uRoughnessMap, roughnessMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uRoughnessMapMatrix) {
+ gl.uniformMatrix4fv(this._uRoughnessMapMatrix, false, roughnessMap._state.matrix);
+ }
+ }
+ const metallicRoughnessMap = material._metallicRoughnessMap;
+ if (metallicRoughnessMap && metallicRoughnessMap._state.texture && this._uMetallicRoughnessMap) {
+ program.bindTexture(this._uMetallicRoughnessMap, metallicRoughnessMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uMetallicRoughnessMapMatrix) {
+ gl.uniformMatrix4fv(this._uMetallicRoughnessMapMatrix, false, metallicRoughnessMap._state.matrix);
+ }
+ }
+ var emissiveMap = material._emissiveMap;
+ if (emissiveMap && emissiveMap._state.texture && this._uEmissiveMap) {
+ program.bindTexture(this._uEmissiveMap, emissiveMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uEmissiveMapMatrix) {
+ gl.uniformMatrix4fv(this._uEmissiveMapMatrix, false, emissiveMap._state.matrix);
+ }
+ }
+ var occlusionMap = material._occlusionMap;
+ if (occlusionMap && material._occlusionMap._state.texture && this._uOcclusionMap) {
+ program.bindTexture(this._uOcclusionMap, occlusionMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uOcclusionMapMatrix) {
+ gl.uniformMatrix4fv(this._uOcclusionMapMatrix, false, occlusionMap._state.matrix);
+ }
+ }
+ var alphaMap = material._alphaMap;
+ if (alphaMap && alphaMap._state.texture && this._uAlphaMap) {
+ program.bindTexture(this._uAlphaMap, alphaMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uAlphaMapMatrix) {
+ gl.uniformMatrix4fv(this._uAlphaMapMatrix, false, alphaMap._state.matrix);
+ }
+ }
+ var normalMap = material._normalMap;
+ if (normalMap && normalMap._state.texture && this._uNormalMap) {
+ program.bindTexture(this._uNormalMap, normalMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uNormalMapMatrix) {
+ gl.uniformMatrix4fv(this._uNormalMapMatrix, false, normalMap._state.matrix);
+ }
}
break;
case "SpecularMaterial":
- this._uMaterialDiffuse = program.getLocation("materialDiffuse");
- this._uMaterialSpecular = program.getLocation("materialSpecular");
- this._uMaterialGlossiness = program.getLocation("materialGlossiness");
- this._uMaterialReflectivity = program.getLocation("reflectivityFresnel");
- this._uMaterialEmissive = program.getLocation("materialEmissive");
- this._uAlphaModeCutoff = program.getLocation("materialAlphaModeCutoff");
- if (material._diffuseMap) {
- this._uDiffuseMap = "diffuseMap";
- this._uDiffuseMapMatrix = program.getLocation("diffuseMapMatrix");
- }
- if (material._specularMap) {
- this._uSpecularMap = "specularMap";
- this._uSpecularMapMatrix = program.getLocation("specularMapMatrix");
- }
- if (material._glossinessMap) {
- this._uGlossinessMap = "glossinessMap";
- this._uGlossinessMapMatrix = program.getLocation("glossinessMapMatrix");
- }
- if (material._specularGlossinessMap) {
- this._uSpecularGlossinessMap = "materialSpecularGlossinessMap";
- this._uSpecularGlossinessMapMatrix = program.getLocation("materialSpecularGlossinessMapMatrix");
- }
- if (material._emissiveMap) {
- this._uEmissiveMap = "emissiveMap";
- this._uEmissiveMapMatrix = program.getLocation("emissiveMapMatrix");
- }
- if (material._occlusionMap) {
- this._uOcclusionMap = "occlusionMap";
- this._uOcclusionMapMatrix = program.getLocation("occlusionMapMatrix");
- }
- if (material._alphaMap) {
- this._uAlphaMap = "alphaMap";
- this._uAlphaMapMatrix = program.getLocation("alphaMapMatrix");
- }
- if (material._normalMap) {
- this._uNormalMap = "normalMap";
- this._uNormalMapMatrix = program.getLocation("normalMapMatrix");
+ if (this._uMaterialDiffuse) {
+ gl.uniform3fv(this._uMaterialDiffuse, materialState.diffuse);
+ }
+ if (this._uMaterialSpecular) {
+ gl.uniform3fv(this._uMaterialSpecular, materialState.specular);
+ }
+ if (this._uMaterialGlossiness) {
+ gl.uniform1f(this._uMaterialGlossiness, materialState.glossiness);
+ }
+ if (this._uMaterialReflectivity) {
+ gl.uniform1f(this._uMaterialReflectivity, materialState.reflectivity);
+ }
+ if (this._uMaterialEmissive) {
+ gl.uniform3fv(this._uMaterialEmissive, materialState.emissive);
+ }
+ if (this._uAlphaModeCutoff) {
+ gl.uniform4f(
+ this._uAlphaModeCutoff,
+ 1.0 * materialState.alpha,
+ materialState.alphaMode === 1 ? 1.0 : 0.0,
+ materialState.alphaCutoff,
+ 0.0);
+ }
+ const diffuseMap = material._diffuseMap;
+ if (diffuseMap && diffuseMap._state.texture && this._uDiffuseMap) {
+ program.bindTexture(this._uDiffuseMap, diffuseMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uDiffuseMapMatrix) {
+ gl.uniformMatrix4fv(this._uDiffuseMapMatrix, false, diffuseMap._state.matrix);
+ }
+ }
+ const specularMap = material._specularMap;
+ if (specularMap && specularMap._state.texture && this._uSpecularMap) {
+ program.bindTexture(this._uSpecularMap, specularMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uSpecularMapMatrix) {
+ gl.uniformMatrix4fv(this._uSpecularMapMatrix, false, specularMap._state.matrix);
+ }
+ }
+ const glossinessMap = material._glossinessMap;
+ if (glossinessMap && glossinessMap._state.texture && this._uGlossinessMap) {
+ program.bindTexture(this._uGlossinessMap, glossinessMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uGlossinessMapMatrix) {
+ gl.uniformMatrix4fv(this._uGlossinessMapMatrix, false, glossinessMap._state.matrix);
+ }
+ }
+ const specularGlossinessMap = material._specularGlossinessMap;
+ if (specularGlossinessMap && specularGlossinessMap._state.texture && this._uSpecularGlossinessMap) {
+ program.bindTexture(this._uSpecularGlossinessMap, specularGlossinessMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uSpecularGlossinessMapMatrix) {
+ gl.uniformMatrix4fv(this._uSpecularGlossinessMapMatrix, false, specularGlossinessMap._state.matrix);
+ }
+ }
+ var emissiveMap = material._emissiveMap;
+ if (emissiveMap && emissiveMap._state.texture && this._uEmissiveMap) {
+ program.bindTexture(this._uEmissiveMap, emissiveMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uEmissiveMapMatrix) {
+ gl.uniformMatrix4fv(this._uEmissiveMapMatrix, false, emissiveMap._state.matrix);
+ }
+ }
+ var occlusionMap = material._occlusionMap;
+ if (occlusionMap && occlusionMap._state.texture && this._uOcclusionMap) {
+ program.bindTexture(this._uOcclusionMap, occlusionMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uOcclusionMapMatrix) {
+ gl.uniformMatrix4fv(this._uOcclusionMapMatrix, false, occlusionMap._state.matrix);
+ }
+ }
+ var alphaMap = material._alphaMap;
+ if (alphaMap && alphaMap._state.texture && this._uAlphaMap) {
+ program.bindTexture(this._uAlphaMap, alphaMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uAlphaMapMatrix) {
+ gl.uniformMatrix4fv(this._uAlphaMapMatrix, false, alphaMap._state.matrix);
+ }
+ }
+ var normalMap = material._normalMap;
+ if (normalMap && normalMap._state.texture && this._uNormalMap) {
+ program.bindTexture(this._uNormalMap, normalMap._state.texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ if (this._uNormalMapMatrix) {
+ gl.uniformMatrix4fv(this._uNormalMapMatrix, false, normalMap._state.matrix);
+ }
}
break;
}
+ this._lastMaterialId = materialState.id;
+ }
+
+ gl.uniformMatrix4fv(this._uModelMatrix, gl.FALSE, mesh.worldMatrix);
+ if (this._uModelNormalMatrix) {
+ gl.uniformMatrix4fv(this._uModelNormalMatrix, gl.FALSE, mesh.worldNormalMatrix);
+ }
+
+ if (this._uClippable) {
+ gl.uniform1i(this._uClippable, meshState.clippable);
+ }
+
+ if (this._uColorize) {
+ const colorize = meshState.colorize;
+ const lastColorize = this._lastColorize;
+ if (lastColorize[0] !== colorize[0] ||
+ lastColorize[1] !== colorize[1] ||
+ lastColorize[2] !== colorize[2] ||
+ lastColorize[3] !== colorize[3]) {
+ gl.uniform4fv(this._uColorize, colorize);
+ lastColorize[0] = colorize[0];
+ lastColorize[1] = colorize[1];
+ lastColorize[2] = colorize[2];
+ lastColorize[3] = colorize[3];
+ }
+ }
+
+ if (geometryState.combined) {
+ const vertexBufs = mesh._geometry._getVertexBufs();
+ if (vertexBufs.id !== this._lastVertexBufsId) {
+ if (vertexBufs.positionsBuf && this._aPosition) {
+ this._aPosition.bindArrayBuffer(vertexBufs.positionsBuf, vertexBufs.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
+ frame.bindArray++;
+ }
+ if (vertexBufs.normalsBuf && this._aNormal) {
+ this._aNormal.bindArrayBuffer(vertexBufs.normalsBuf, vertexBufs.quantized ? gl.BYTE : gl.FLOAT);
+ frame.bindArray++;
+ }
+ if (vertexBufs.uvBuf && this._aUV) {
+ this._aUV.bindArrayBuffer(vertexBufs.uvBuf, geometryState.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
+ frame.bindArray++;
+ }
+ if (vertexBufs.colorsBuf && this._aColor) {
+ this._aColor.bindArrayBuffer(vertexBufs.colorsBuf);
+ frame.bindArray++;
+ }
+ if (vertexBufs.flagsBuf && this._aFlags) {
+ this._aFlags.bindArrayBuffer(vertexBufs.flagsBuf, gl.UNSIGNED_SHORT);
+ frame.bindArray++;
+ }
+ this._lastVertexBufsId = vertexBufs.id;
+ }
+ }
- this._aPosition = program.getAttribute("position");
- this._aNormal = program.getAttribute("normal");
- this._aUV = program.getAttribute("uv");
- this._aColor = program.getAttribute("color");
- this._aFlags = program.getAttribute("flags");
+ // Bind VBOs
- this._uClippable = program.getLocation("clippable");
- this._uColorize = program.getLocation("colorize");
+ if (geometryState.id !== this._lastGeometryId) {
+ if (this._uPositionsDecodeMatrix) {
+ gl.uniformMatrix4fv(this._uPositionsDecodeMatrix, false, geometryState.positionsDecodeMatrix);
+ }
+ if (this._uUVDecodeMatrix) {
+ gl.uniformMatrix3fv(this._uUVDecodeMatrix, false, geometryState.uvDecodeMatrix);
+ }
+ if (geometryState.combined) { // VBOs were bound by the VertexBufs logic above
+ if (geometryState.indicesBufCombined) {
+ geometryState.indicesBufCombined.bind();
+ frame.bindArray++;
+ }
+ } else {
+ if (this._aPosition) {
+ this._aPosition.bindArrayBuffer(geometryState.positionsBuf, geometryState.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
+ frame.bindArray++;
+ }
+ if (this._aNormal) {
+ this._aNormal.bindArrayBuffer(geometryState.normalsBuf, geometryState.quantized ? gl.BYTE : gl.FLOAT);
+ frame.bindArray++;
+ }
+ if (this._aUV) {
+ this._aUV.bindArrayBuffer(geometryState.uvBuf, geometryState.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
+ frame.bindArray++;
+ }
+ if (this._aColor) {
+ this._aColor.bindArrayBuffer(geometryState.colorsBuf);
+ frame.bindArray++;
+ }
+ if (this._aFlags) {
+ this._aFlags.bindArrayBuffer(geometryState.flagsBuf);
+ frame.bindArray++;
+ }
+ if (geometryState.indicesBuf) {
+ geometryState.indicesBuf.bind();
+ frame.bindArray++;
+ // gl.drawElements(geometryState.primitive, geometryState.indicesBuf.numItems, geometryState.indicesBuf.itemType, 0);
+ // frame.drawElements++;
+ } else if (geometryState.positions) {
+ // gl.drawArrays(gl.TRIANGLES, 0, geometryState.positions.numItems);
+ // frame.drawArrays++;
+ }
+ }
+ this._lastGeometryId = geometryState.id;
+ }
- this._lastMaterialId = null;
- this._lastVertexBufsId = null;
- this._lastGeometryId = null;
+ // Draw (indices bound in prev step)
- this._lastColorize = new Float32Array(4);
+ if (geometryState.combined) {
+ if (geometryState.indicesBufCombined) { // Geometry indices into portion of uber-array
+ gl.drawElements(geometryState.primitive, geometryState.indicesBufCombined.numItems, geometryState.indicesBufCombined.itemType, 0);
+ frame.drawElements++;
+ } else {
+ // TODO: drawArrays() with VertexBufs positions
+ }
+ } else {
+ if (geometryState.indicesBuf) {
+ gl.drawElements(geometryState.primitive, geometryState.indicesBuf.numItems, geometryState.indicesBuf.itemType, 0);
+ frame.drawElements++;
+ } else if (geometryState.positions) {
+ gl.drawArrays(gl.TRIANGLES, 0, geometryState.positions.numItems);
+ frame.drawArrays++;
+ }
+ }
+};
+
+DrawRenderer.prototype._allocate = function (mesh) {
+ const gl = mesh.scene.canvas.gl;
+ const material = mesh._material;
+ const lightsState = mesh.scene._lightsState;
+ const clipsState = mesh.scene._clipsState;
+ const materialState = mesh._material._state;
+ this._program = new Program(gl, this._shaderSource);
+ if (this._program.errors) {
+ this.errors = this._program.errors;
+ return;
+ }
+ const program = this._program;
+ this._uPositionsDecodeMatrix = program.getLocation("positionsDecodeMatrix");
+ this._uUVDecodeMatrix = program.getLocation("uvDecodeMatrix");
+ this._uModelMatrix = program.getLocation("modelMatrix");
+ this._uModelNormalMatrix = program.getLocation("modelNormalMatrix");
+ this._uViewMatrix = program.getLocation("viewMatrix");
+ this._uViewNormalMatrix = program.getLocation("viewNormalMatrix");
+ this._uProjMatrix = program.getLocation("projMatrix");
+ this._uGammaFactor = program.getLocation("gammaFactor");
+ this._uLightAmbient = [];
+ this._uLightColor = [];
+ this._uLightDir = [];
+ this._uLightPos = [];
+ this._uLightAttenuation = [];
+ this._uShadowViewMatrix = [];
+ this._uShadowProjMatrix = [];
+
+ const lights = lightsState.lights;
+ let light;
+
+ for (var i = 0, len = lights.length; i < len; i++) {
+ light = lights[i];
+ switch (light.type) {
+
+ case "ambient":
+ this._uLightAmbient[i] = program.getLocation("lightAmbient");
+ break;
- this._baseTextureUnit = 0;
+ case "dir":
+ this._uLightColor[i] = program.getLocation("lightColor" + i);
+ this._uLightPos[i] = null;
+ this._uLightDir[i] = program.getLocation("lightDir" + i);
+ break;
- };
+ case "point":
+ this._uLightColor[i] = program.getLocation("lightColor" + i);
+ this._uLightPos[i] = program.getLocation("lightPos" + i);
+ this._uLightDir[i] = null;
+ this._uLightAttenuation[i] = program.getLocation("lightAttenuation" + i);
+ break;
- xeogl.renderer.DrawRenderer.prototype._bindProgram = function (frame) {
+ case "spot":
+ this._uLightColor[i] = program.getLocation("lightColor" + i);
+ this._uLightPos[i] = program.getLocation("lightPos" + i);
+ this._uLightDir[i] = program.getLocation("lightDir" + i);
+ this._uLightAttenuation[i] = program.getLocation("lightAttenuation" + i);
+ break;
+ }
- const math = xeogl.math;
- const maxTextureUnits = xeogl.WEBGL_INFO.MAX_TEXTURE_UNITS;
- const scene = this._scene;
- const gl = scene.canvas.gl;
- const lightsState = scene._lightsState;
- const clipsState = scene._clipsState;
- const lights = lightsState.lights;
- let light;
+ if (light.shadow) {
+ this._uShadowViewMatrix[i] = program.getLocation("shadowViewMatrix" + i);
+ this._uShadowProjMatrix[i] = program.getLocation("shadowProjMatrix" + i);
+ }
+ }
+
+ if (lightsState.lightMaps.length > 0) {
+ this._uLightMap = "lightMap";
+ }
+
+ if (lightsState.reflectionMaps.length > 0) {
+ this._uReflectionMap = "reflectionMap";
+ }
+
+ this._uClips = [];
+ const clips = clipsState.clips;
+ for (var i = 0, len = clips.length; i < len; i++) {
+ this._uClips.push({
+ active: program.getLocation("clipActive" + i),
+ pos: program.getLocation("clipPos" + i),
+ dir: program.getLocation("clipDir" + i)
+ });
+ }
+
+ this._uPointSize = program.getLocation("pointSize");
+
+ switch (materialState.type) {
+ case "LambertMaterial":
+ this._uMaterialColor = program.getLocation("materialColor");
+ this._uMaterialEmissive = program.getLocation("materialEmissive");
+ this._uAlphaModeCutoff = program.getLocation("materialAlphaModeCutoff");
+ break;
+
+ case "PhongMaterial":
+ this._uMaterialAmbient = program.getLocation("materialAmbient");
+ this._uMaterialDiffuse = program.getLocation("materialDiffuse");
+ this._uMaterialSpecular = program.getLocation("materialSpecular");
+ this._uMaterialEmissive = program.getLocation("materialEmissive");
+ this._uAlphaModeCutoff = program.getLocation("materialAlphaModeCutoff");
+ this._uMaterialShininess = program.getLocation("materialShininess");
+ if (material._ambientMap) {
+ this._uMaterialAmbientMap = "ambientMap";
+ this._uMaterialAmbientMapMatrix = program.getLocation("ambientMapMatrix");
+ }
+ if (material._diffuseMap) {
+ this._uDiffuseMap = "diffuseMap";
+ this._uDiffuseMapMatrix = program.getLocation("diffuseMapMatrix");
+ }
+ if (material._specularMap) {
+ this._uSpecularMap = "specularMap";
+ this._uSpecularMapMatrix = program.getLocation("specularMapMatrix");
+ }
+ if (material._emissiveMap) {
+ this._uEmissiveMap = "emissiveMap";
+ this._uEmissiveMapMatrix = program.getLocation("emissiveMapMatrix");
+ }
+ if (material._alphaMap) {
+ this._uAlphaMap = "alphaMap";
+ this._uAlphaMapMatrix = program.getLocation("alphaMapMatrix");
+ }
+ if (material._reflectivityMap) {
+ this._uReflectivityMap = "reflectivityMap";
+ this._uReflectivityMapMatrix = program.getLocation("reflectivityMapMatrix");
+ }
+ if (material._normalMap) {
+ this._uNormalMap = "normalMap";
+ this._uNormalMapMatrix = program.getLocation("normalMapMatrix");
+ }
+ if (material._occlusionMap) {
+ this._uOcclusionMap = "occlusionMap";
+ this._uOcclusionMapMatrix = program.getLocation("occlusionMapMatrix");
+ }
+ if (material._diffuseFresnel) {
+ this._uDiffuseFresnelEdgeBias = program.getLocation("diffuseFresnelEdgeBias");
+ this._uDiffuseFresnelCenterBias = program.getLocation("diffuseFresnelCenterBias");
+ this._uDiffuseFresnelEdgeColor = program.getLocation("diffuseFresnelEdgeColor");
+ this._uDiffuseFresnelCenterColor = program.getLocation("diffuseFresnelCenterColor");
+ this._uDiffuseFresnelPower = program.getLocation("diffuseFresnelPower");
+ }
+ if (material._specularFresnel) {
+ this._uSpecularFresnelEdgeBias = program.getLocation("specularFresnelEdgeBias");
+ this._uSpecularFresnelCenterBias = program.getLocation("specularFresnelCenterBias");
+ this._uSpecularFresnelEdgeColor = program.getLocation("specularFresnelEdgeColor");
+ this._uSpecularFresnelCenterColor = program.getLocation("specularFresnelCenterColor");
+ this._uSpecularFresnelPower = program.getLocation("specularFresnelPower");
+ }
+ if (material._alphaFresnel) {
+ this._uAlphaFresnelEdgeBias = program.getLocation("alphaFresnelEdgeBias");
+ this._uAlphaFresnelCenterBias = program.getLocation("alphaFresnelCenterBias");
+ this._uAlphaFresnelEdgeColor = program.getLocation("alphaFresnelEdgeColor");
+ this._uAlphaFresnelCenterColor = program.getLocation("alphaFresnelCenterColor");
+ this._uAlphaFresnelPower = program.getLocation("alphaFresnelPower");
+ }
+ if (material._reflectivityFresnel) {
+ this._uReflectivityFresnelEdgeBias = program.getLocation("reflectivityFresnelEdgeBias");
+ this._uReflectivityFresnelCenterBias = program.getLocation("reflectivityFresnelCenterBias");
+ this._uReflectivityFresnelEdgeColor = program.getLocation("reflectivityFresnelEdgeColor");
+ this._uReflectivityFresnelCenterColor = program.getLocation("reflectivityFresnelCenterColor");
+ this._uReflectivityFresnelPower = program.getLocation("reflectivityFresnelPower");
+ }
+ if (material._emissiveFresnel) {
+ this._uEmissiveFresnelEdgeBias = program.getLocation("emissiveFresnelEdgeBias");
+ this._uEmissiveFresnelCenterBias = program.getLocation("emissiveFresnelCenterBias");
+ this._uEmissiveFresnelEdgeColor = program.getLocation("emissiveFresnelEdgeColor");
+ this._uEmissiveFresnelCenterColor = program.getLocation("emissiveFresnelCenterColor");
+ this._uEmissiveFresnelPower = program.getLocation("emissiveFresnelPower");
+ }
+ break;
+
+ case "MetallicMaterial":
+ this._uBaseColor = program.getLocation("materialBaseColor");
+ this._uMaterialMetallic = program.getLocation("materialMetallic");
+ this._uMaterialRoughness = program.getLocation("materialRoughness");
+ this._uMaterialSpecularF0 = program.getLocation("materialSpecularF0");
+ this._uMaterialEmissive = program.getLocation("materialEmissive");
+ this._uAlphaModeCutoff = program.getLocation("materialAlphaModeCutoff");
+ if (material._baseColorMap) {
+ this._uBaseColorMap = "baseColorMap";
+ this._uBaseColorMapMatrix = program.getLocation("baseColorMapMatrix");
+ }
+ if (material._metallicMap) {
+ this._uMetallicMap = "metallicMap";
+ this._uMetallicMapMatrix = program.getLocation("metallicMapMatrix");
+ }
+ if (material._roughnessMap) {
+ this._uRoughnessMap = "roughnessMap";
+ this._uRoughnessMapMatrix = program.getLocation("roughnessMapMatrix");
+ }
+ if (material._metallicRoughnessMap) {
+ this._uMetallicRoughnessMap = "metallicRoughnessMap";
+ this._uMetallicRoughnessMapMatrix = program.getLocation("metallicRoughnessMapMatrix");
+ }
+ if (material._emissiveMap) {
+ this._uEmissiveMap = "emissiveMap";
+ this._uEmissiveMapMatrix = program.getLocation("emissiveMapMatrix");
+ }
+ if (material._occlusionMap) {
+ this._uOcclusionMap = "occlusionMap";
+ this._uOcclusionMapMatrix = program.getLocation("occlusionMapMatrix");
+ }
+ if (material._alphaMap) {
+ this._uAlphaMap = "alphaMap";
+ this._uAlphaMapMatrix = program.getLocation("alphaMapMatrix");
+ }
+ if (material._normalMap) {
+ this._uNormalMap = "normalMap";
+ this._uNormalMapMatrix = program.getLocation("normalMapMatrix");
+ }
+ break;
+
+ case "SpecularMaterial":
+ this._uMaterialDiffuse = program.getLocation("materialDiffuse");
+ this._uMaterialSpecular = program.getLocation("materialSpecular");
+ this._uMaterialGlossiness = program.getLocation("materialGlossiness");
+ this._uMaterialReflectivity = program.getLocation("reflectivityFresnel");
+ this._uMaterialEmissive = program.getLocation("materialEmissive");
+ this._uAlphaModeCutoff = program.getLocation("materialAlphaModeCutoff");
+ if (material._diffuseMap) {
+ this._uDiffuseMap = "diffuseMap";
+ this._uDiffuseMapMatrix = program.getLocation("diffuseMapMatrix");
+ }
+ if (material._specularMap) {
+ this._uSpecularMap = "specularMap";
+ this._uSpecularMapMatrix = program.getLocation("specularMapMatrix");
+ }
+ if (material._glossinessMap) {
+ this._uGlossinessMap = "glossinessMap";
+ this._uGlossinessMapMatrix = program.getLocation("glossinessMapMatrix");
+ }
+ if (material._specularGlossinessMap) {
+ this._uSpecularGlossinessMap = "materialSpecularGlossinessMap";
+ this._uSpecularGlossinessMapMatrix = program.getLocation("materialSpecularGlossinessMapMatrix");
+ }
+ if (material._emissiveMap) {
+ this._uEmissiveMap = "emissiveMap";
+ this._uEmissiveMapMatrix = program.getLocation("emissiveMapMatrix");
+ }
+ if (material._occlusionMap) {
+ this._uOcclusionMap = "occlusionMap";
+ this._uOcclusionMapMatrix = program.getLocation("occlusionMapMatrix");
+ }
+ if (material._alphaMap) {
+ this._uAlphaMap = "alphaMap";
+ this._uAlphaMapMatrix = program.getLocation("alphaMapMatrix");
+ }
+ if (material._normalMap) {
+ this._uNormalMap = "normalMap";
+ this._uNormalMapMatrix = program.getLocation("normalMapMatrix");
+ }
+ break;
+ }
- const program = this._program;
+ this._aPosition = program.getAttribute("position");
+ this._aNormal = program.getAttribute("normal");
+ this._aUV = program.getAttribute("uv");
+ this._aColor = program.getAttribute("color");
+ this._aFlags = program.getAttribute("flags");
- program.bind();
+ this._uClippable = program.getLocation("clippable");
+ this._uColorize = program.getLocation("colorize");
- frame.useProgram++;
- frame.textureUnit = 0;
+ this._lastMaterialId = null;
+ this._lastVertexBufsId = null;
+ this._lastGeometryId = null;
- this._lastMaterialId = null;
- this._lastVertexBufsId = null;
- this._lastGeometryId = null;
+ this._lastColorize = new Float32Array(4);
- this._lastColorize[0] = -1;
- this._lastColorize[1] = -1;
- this._lastColorize[2] = -1;
- this._lastColorize[3] = -1;
+ this._baseTextureUnit = 0;
- const camera = scene.camera;
- const cameraState = camera._state;
+};
- gl.uniformMatrix4fv(this._uViewMatrix, false, cameraState.matrix);
- gl.uniformMatrix4fv(this._uViewNormalMatrix, false, cameraState.normalMatrix);
- gl.uniformMatrix4fv(this._uProjMatrix, false, camera._project._state.matrix);
+DrawRenderer.prototype._bindProgram = function (frame) {
- for (var i = 0, len = lightsState.lights.length; i < len; i++) {
+ const maxTextureUnits = WEBGL_INFO.MAX_TEXTURE_UNITS;
+ const scene = this._scene;
+ const gl = scene.canvas.gl;
+ const lightsState = scene._lightsState;
+ const clipsState = scene._clipsState;
+ const lights = lightsState.lights;
+ let light;
- light = lightsState.lights[i];
+ const program = this._program;
- if (this._uLightAmbient[i]) {
- gl.uniform4f(this._uLightAmbient[i], light.color[0], light.color[1], light.color[2], light.intensity);
+ program.bind();
- } else {
+ frame.useProgram++;
+ frame.textureUnit = 0;
- if (this._uLightColor[i]) {
- gl.uniform4f(this._uLightColor[i], light.color[0], light.color[1], light.color[2], light.intensity);
- }
+ this._lastMaterialId = null;
+ this._lastVertexBufsId = null;
+ this._lastGeometryId = null;
- if (this._uLightPos[i]) {
- gl.uniform3fv(this._uLightPos[i], light.pos);
- if (this._uLightAttenuation[i]) {
- gl.uniform1f(this._uLightAttenuation[i], light.attenuation);
- }
- }
+ this._lastColorize[0] = -1;
+ this._lastColorize[1] = -1;
+ this._lastColorize[2] = -1;
+ this._lastColorize[3] = -1;
- if (this._uLightDir[i]) {
- gl.uniform3fv(this._uLightDir[i], light.dir);
- }
+ const camera = scene.camera;
+ const cameraState = camera._state;
- if (light.shadow) {
- if (this._uShadowViewMatrix[i]) {
- gl.uniformMatrix4fv(this._uShadowViewMatrix[i], false, light.getShadowViewMatrix());
- }
- if (this._uShadowProjMatrix[i]) {
- gl.uniformMatrix4fv(this._uShadowProjMatrix[i], false, light.getShadowProjMatrix());
- }
- const shadowRenderBuf = light.getShadowRenderBuf();
- if (shadowRenderBuf) {
- program.bindTexture("shadowMap" + i, shadowRenderBuf.getTexture(), frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- }
- }
+ gl.uniformMatrix4fv(this._uViewMatrix, false, cameraState.matrix);
+ gl.uniformMatrix4fv(this._uViewNormalMatrix, false, cameraState.normalMatrix);
+ gl.uniformMatrix4fv(this._uProjMatrix, false, camera._project._state.matrix);
+
+ for (var i = 0, len = lightsState.lights.length; i < len; i++) {
+
+ light = lightsState.lights[i];
+
+ if (this._uLightAmbient[i]) {
+ gl.uniform4f(this._uLightAmbient[i], light.color[0], light.color[1], light.color[2], light.intensity);
+
+ } else {
+
+ if (this._uLightColor[i]) {
+ gl.uniform4f(this._uLightColor[i], light.color[0], light.color[1], light.color[2], light.intensity);
}
- }
- if (lightsState.lightMaps.length > 0 && lightsState.lightMaps[0].texture && this._uLightMap) {
- program.bindTexture(this._uLightMap, lightsState.lightMaps[0].texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- }
+ if (this._uLightPos[i]) {
+ gl.uniform3fv(this._uLightPos[i], light.pos);
+ if (this._uLightAttenuation[i]) {
+ gl.uniform1f(this._uLightAttenuation[i], light.attenuation);
+ }
+ }
- if (lightsState.reflectionMaps.length > 0 && lightsState.reflectionMaps[0].texture && this._uReflectionMap) {
- program.bindTexture(this._uReflectionMap, lightsState.reflectionMaps[0].texture, frame.textureUnit);
- frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
- frame.bindTexture++;
- }
+ if (this._uLightDir[i]) {
+ gl.uniform3fv(this._uLightDir[i], light.dir);
+ }
- if (clipsState.clips.length > 0) {
- const clips = scene._clipsState.clips;
- let clipUniforms;
- let uClipActive;
- let clip;
- let uClipPos;
- let uClipDir;
- for (var i = 0, len = this._uClips.length; i < len; i++) {
- clipUniforms = this._uClips[i];
- uClipActive = clipUniforms.active;
- clip = clips[i];
- if (uClipActive) {
- gl.uniform1i(uClipActive, clip.active);
- }
- uClipPos = clipUniforms.pos;
- if (uClipPos) {
- gl.uniform3fv(clipUniforms.pos, clip.pos);
- }
- uClipDir = clipUniforms.dir;
- if (uClipDir) {
- gl.uniform3fv(clipUniforms.dir, clip.dir);
+ if (light.shadow) {
+ if (this._uShadowViewMatrix[i]) {
+ gl.uniformMatrix4fv(this._uShadowViewMatrix[i], false, light.getShadowViewMatrix());
+ }
+ if (this._uShadowProjMatrix[i]) {
+ gl.uniformMatrix4fv(this._uShadowProjMatrix[i], false, light.getShadowProjMatrix());
+ }
+ const shadowRenderBuf = light.getShadowRenderBuf();
+ if (shadowRenderBuf) {
+ program.bindTexture("shadowMap" + i, shadowRenderBuf.getTexture(), frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
}
}
}
-
- if (this._uGammaFactor) {
- gl.uniform1f(this._uGammaFactor, scene.gammaFactor);
+ }
+
+ if (lightsState.lightMaps.length > 0 && lightsState.lightMaps[0].texture && this._uLightMap) {
+ program.bindTexture(this._uLightMap, lightsState.lightMaps[0].texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ }
+
+ if (lightsState.reflectionMaps.length > 0 && lightsState.reflectionMaps[0].texture && this._uReflectionMap) {
+ program.bindTexture(this._uReflectionMap, lightsState.reflectionMaps[0].texture, frame.textureUnit);
+ frame.textureUnit = (frame.textureUnit + 1) % maxTextureUnits;
+ frame.bindTexture++;
+ }
+
+ if (clipsState.clips.length > 0) {
+ const clips = scene._clipsState.clips;
+ let clipUniforms;
+ let uClipActive;
+ let clip;
+ let uClipPos;
+ let uClipDir;
+ for (var i = 0, len = this._uClips.length; i < len; i++) {
+ clipUniforms = this._uClips[i];
+ uClipActive = clipUniforms.active;
+ clip = clips[i];
+ if (uClipActive) {
+ gl.uniform1i(uClipActive, clip.active);
+ }
+ uClipPos = clipUniforms.pos;
+ if (uClipPos) {
+ gl.uniform3fv(clipUniforms.pos, clip.pos);
+ }
+ uClipDir = clipUniforms.dir;
+ if (uClipDir) {
+ gl.uniform3fv(clipUniforms.dir, clip.dir);
+ }
}
+ }
+
+ if (this._uGammaFactor) {
+ gl.uniform1f(this._uGammaFactor, scene.gammaFactor);
+ }
- this._baseTextureUnit = frame.textureUnit;
- };
+ this._baseTextureUnit = frame.textureUnit;
+};
-})();
+export {DrawRenderer};
diff --git a/src/renderer/draw/drawShaderSource.js b/src/renderer/draw/drawShaderSource.js
index ac9c254b3..67c5e2df5 100644
--- a/src/renderer/draw/drawShaderSource.js
+++ b/src/renderer/draw/drawShaderSource.js
@@ -2,1526 +2,1524 @@
* @author xeolabs / https://github.com/xeolabs
*/
-(function () {
- "use strict";
-
- xeogl.renderer.DrawShaderSource = function (mesh) {
- if (mesh._material._state.type === "LambertMaterial") {
- this.vertex = buildVertexLambert(mesh);
- this.fragment = buildFragmentLambert(mesh);
- } else {
- this.vertex = buildVertexDraw(mesh);
- this.fragment = buildFragmentDraw(mesh);
- }
- };
+const DrawShaderSource = function (mesh) {
+ if (mesh._material._state.type === "LambertMaterial") {
+ this.vertex = buildVertexLambert(mesh);
+ this.fragment = buildFragmentLambert(mesh);
+ } else {
+ this.vertex = buildVertexDraw(mesh);
+ this.fragment = buildFragmentDraw(mesh);
+ }
+};
- const TEXTURE_DECODE_FUNCS = {
- "linear": "linearToLinear",
- "sRGB": "sRGBToLinear",
- "gamma": "gammaToLinear"
- };
+const TEXTURE_DECODE_FUNCS = {
+ "linear": "linearToLinear",
+ "sRGB": "sRGBToLinear",
+ "gamma": "gammaToLinear"
+};
- function receivesShadow(mesh) {
- if (!mesh._state.receiveShadow) {
- return false;
- }
- const lights = mesh.scene._lightsState.lights;
- if (!lights || lights.length === 0) {
- return false;
- }
- for (let i = 0, len = lights.length; i < len; i++) {
- if (lights[i].shadow) {
- return true;
- }
- }
+function receivesShadow(mesh) {
+ if (!mesh._state.receiveShadow) {
return false;
}
-
- function hasTextures(mesh) {
- if (!mesh._geometry._state.uv) {
- return false;
- }
- const material = mesh._material;
- return !!(material._ambientMap ||
- material._occlusionMap ||
- material._baseColorMap ||
- material._diffuseMap ||
- material._alphaMap ||
- material._specularMap ||
- material._glossinessMap ||
- material._specularGlossinessMap ||
- material._emissiveMap ||
- material._metallicMap ||
- material._roughnessMap ||
- material._metallicRoughnessMap ||
- material._reflectivityMap ||
- material._normalMap);
- }
-
- function hasNormals(mesh) {
- const primitive = mesh._geometry._state.primitiveName;
- if ((mesh._geometry._state.autoVertexNormals || mesh._geometry._state.normals) && (primitive === "triangles" || primitive === "triangle-strip" || primitive === "triangle-fan")) {
+ const lights = mesh.scene._lightsState.lights;
+ if (!lights || lights.length === 0) {
+ return false;
+ }
+ for (let i = 0, len = lights.length; i < len; i++) {
+ if (lights[i].shadow) {
return true;
}
+ }
+ return false;
+}
+
+function hasTextures(mesh) {
+ if (!mesh._geometry._state.uv) {
return false;
}
+ const material = mesh._material;
+ return !!(material._ambientMap ||
+ material._occlusionMap ||
+ material._baseColorMap ||
+ material._diffuseMap ||
+ material._alphaMap ||
+ material._specularMap ||
+ material._glossinessMap ||
+ material._specularGlossinessMap ||
+ material._emissiveMap ||
+ material._metallicMap ||
+ material._roughnessMap ||
+ material._metallicRoughnessMap ||
+ material._reflectivityMap ||
+ material._normalMap);
+}
+
+function hasNormals(mesh) {
+ const primitive = mesh._geometry._state.primitiveName;
+ if ((mesh._geometry._state.autoVertexNormals || mesh._geometry._state.normals) && (primitive === "triangles" || primitive === "triangle-strip" || primitive === "triangle-fan")) {
+ return true;
+ }
+ return false;
+}
- function getFragmentFloatPrecision(gl) {
- if (!gl.getShaderPrecisionFormat) {
- return "mediump";
- }
- if (gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT).precision > 0) {
- return "highp";
- }
- if (gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT).precision > 0) {
- return "mediump";
- }
- return "lowp";
- }
-
- function buildVertexLambert(mesh) {
- const clipsState = mesh.scene._clipsState;
- const lightsState = mesh.scene._lightsState;
- const geometryState = mesh._geometry._state;
- const billboard = mesh._state.billboard;
- const stationary = mesh._state.stationary;
- const clipping = clipsState.clips.length > 0;
- const quantizedGeometry = !!geometryState.quantized;
- let i;
- let len;
- let light;
- const src = [];
- src.push("// Lambertian drawing vertex shader");
- src.push("attribute vec3 position;");
- src.push("uniform mat4 modelMatrix;");
- src.push("uniform mat4 viewMatrix;");
- src.push("uniform mat4 projMatrix;");
- src.push("uniform vec4 colorize;");
- if (quantizedGeometry) {
- src.push("uniform mat4 positionsDecodeMatrix;");
- }
- if (clipping) {
- src.push("varying vec4 vWorldPosition;");
- }
- src.push("uniform vec4 lightAmbient;");
- src.push("uniform vec4 materialColor;");
- if (geometryState.normals) {
- src.push("attribute vec3 normal;");
- src.push("uniform mat4 modelNormalMatrix;");
- src.push("uniform mat4 viewNormalMatrix;");
- for (i = 0, len = lightsState.lights.length; i < len; i++) {
- light = lightsState.lights[i];
- if (light.type === "ambient") {
- continue;
- }
- src.push("uniform vec4 lightColor" + i + ";");
- if (light.type === "dir") {
- src.push("uniform vec3 lightDir" + i + ";");
- }
- if (light.type === "point") {
- src.push("uniform vec3 lightPos" + i + ";");
- }
- if (light.type === "spot") {
- src.push("uniform vec3 lightPos" + i + ";");
- src.push("uniform vec3 lightDir" + i + ";");
- }
+function getFragmentFloatPrecision(gl) {
+ if (!gl.getShaderPrecisionFormat) {
+ return "mediump";
+ }
+ if (gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT).precision > 0) {
+ return "highp";
+ }
+ if (gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT).precision > 0) {
+ return "mediump";
+ }
+ return "lowp";
+}
+
+function buildVertexLambert(mesh) {
+ const clipsState = mesh.scene._clipsState;
+ const lightsState = mesh.scene._lightsState;
+ const geometryState = mesh._geometry._state;
+ const billboard = mesh._state.billboard;
+ const stationary = mesh._state.stationary;
+ const clipping = clipsState.clips.length > 0;
+ const quantizedGeometry = !!geometryState.quantized;
+ let i;
+ let len;
+ let light;
+ const src = [];
+ src.push("// Lambertian drawing vertex shader");
+ src.push("attribute vec3 position;");
+ src.push("uniform mat4 modelMatrix;");
+ src.push("uniform mat4 viewMatrix;");
+ src.push("uniform mat4 projMatrix;");
+ src.push("uniform vec4 colorize;");
+ if (quantizedGeometry) {
+ src.push("uniform mat4 positionsDecodeMatrix;");
+ }
+ if (clipping) {
+ src.push("varying vec4 vWorldPosition;");
+ }
+ src.push("uniform vec4 lightAmbient;");
+ src.push("uniform vec4 materialColor;");
+ if (geometryState.normals) {
+ src.push("attribute vec3 normal;");
+ src.push("uniform mat4 modelNormalMatrix;");
+ src.push("uniform mat4 viewNormalMatrix;");
+ for (i = 0, len = lightsState.lights.length; i < len; i++) {
+ light = lightsState.lights[i];
+ if (light.type === "ambient") {
+ continue;
}
- if (quantizedGeometry) {
- src.push("vec3 octDecode(vec2 oct) {");
- src.push(" vec3 v = vec3(oct.xy, 1.0 - abs(oct.x) - abs(oct.y));");
- src.push(" if (v.z < 0.0) {");
- src.push(" v.xy = (1.0 - abs(v.yx)) * vec2(v.x >= 0.0 ? 1.0 : -1.0, v.y >= 0.0 ? 1.0 : -1.0);");
- src.push(" }");
- src.push(" return normalize(v);");
- src.push("}");
+ src.push("uniform vec4 lightColor" + i + ";");
+ if (light.type === "dir") {
+ src.push("uniform vec3 lightDir" + i + ";");
}
- }
- src.push("varying vec4 vColor;");
- if (geometryState.primitiveName === "points") {
- src.push("uniform float pointSize;");
- }
- if (billboard === "spherical" || billboard === "cylindrical") {
- src.push("void billboard(inout mat4 mat) {");
- src.push(" mat[0][0] = 1.0;");
- src.push(" mat[0][1] = 0.0;");
- src.push(" mat[0][2] = 0.0;");
- if (billboard === "spherical") {
- src.push(" mat[1][0] = 0.0;");
- src.push(" mat[1][1] = 1.0;");
- src.push(" mat[1][2] = 0.0;");
+ if (light.type === "point") {
+ src.push("uniform vec3 lightPos" + i + ";");
+ }
+ if (light.type === "spot") {
+ src.push("uniform vec3 lightPos" + i + ";");
+ src.push("uniform vec3 lightDir" + i + ";");
}
- src.push(" mat[2][0] = 0.0;");
- src.push(" mat[2][1] = 0.0;");
- src.push(" mat[2][2] =1.0;");
- src.push("}");
}
- src.push("void main(void) {");
- src.push("vec4 localPosition = vec4(position, 1.0); ");
- src.push("vec4 worldPosition;");
if (quantizedGeometry) {
- src.push("localPosition = positionsDecodeMatrix * localPosition;");
+ src.push("vec3 octDecode(vec2 oct) {");
+ src.push(" vec3 v = vec3(oct.xy, 1.0 - abs(oct.x) - abs(oct.y));");
+ src.push(" if (v.z < 0.0) {");
+ src.push(" v.xy = (1.0 - abs(v.yx)) * vec2(v.x >= 0.0 ? 1.0 : -1.0, v.y >= 0.0 ? 1.0 : -1.0);");
+ src.push(" }");
+ src.push(" return normalize(v);");
+ src.push("}");
}
- if (geometryState.normals) {
- if (quantizedGeometry) {
- src.push("vec4 localNormal = vec4(octDecode(normal.xy), 0.0); ");
- } else {
- src.push("vec4 localNormal = vec4(normal, 0.0); ");
- }
- src.push("mat4 modelNormalMatrix2 = modelNormalMatrix;");
- src.push("mat4 viewNormalMatrix2 = viewNormalMatrix;");
- }
- src.push("mat4 viewMatrix2 = viewMatrix;");
- src.push("mat4 modelMatrix2 = modelMatrix;");
- if (stationary) {
- src.push("viewMatrix2[3][0] = viewMatrix2[3][1] = viewMatrix2[3][2] = 0.0;")
- }
- if (billboard === "spherical" || billboard === "cylindrical") {
- src.push("mat4 modelViewMatrix = viewMatrix2 * modelMatrix2;");
- src.push("billboard(modelMatrix2);");
- src.push("billboard(viewMatrix2);");
- src.push("billboard(modelViewMatrix);");
- if (geometryState.normals) {
- src.push("mat4 modelViewNormalMatrix = viewNormalMatrix2 * modelNormalMatrix2;");
- src.push("billboard(modelNormalMatrix2);");
- src.push("billboard(viewNormalMatrix2);");
- src.push("billboard(modelViewNormalMatrix);");
- }
- src.push("worldPosition = modelMatrix2 * localPosition;");
- src.push("vec4 viewPosition = modelViewMatrix * localPosition;");
+ }
+ src.push("varying vec4 vColor;");
+ if (geometryState.primitiveName === "points") {
+ src.push("uniform float pointSize;");
+ }
+ if (billboard === "spherical" || billboard === "cylindrical") {
+ src.push("void billboard(inout mat4 mat) {");
+ src.push(" mat[0][0] = 1.0;");
+ src.push(" mat[0][1] = 0.0;");
+ src.push(" mat[0][2] = 0.0;");
+ if (billboard === "spherical") {
+ src.push(" mat[1][0] = 0.0;");
+ src.push(" mat[1][1] = 1.0;");
+ src.push(" mat[1][2] = 0.0;");
+ }
+ src.push(" mat[2][0] = 0.0;");
+ src.push(" mat[2][1] = 0.0;");
+ src.push(" mat[2][2] =1.0;");
+ src.push("}");
+ }
+ src.push("void main(void) {");
+ src.push("vec4 localPosition = vec4(position, 1.0); ");
+ src.push("vec4 worldPosition;");
+ if (quantizedGeometry) {
+ src.push("localPosition = positionsDecodeMatrix * localPosition;");
+ }
+ if (geometryState.normals) {
+ if (quantizedGeometry) {
+ src.push("vec4 localNormal = vec4(octDecode(normal.xy), 0.0); ");
} else {
- src.push("worldPosition = modelMatrix2 * localPosition;");
- src.push("vec4 viewPosition = viewMatrix2 * worldPosition; ");
- }
- if (geometryState.normals) {
- src.push("vec3 viewNormal = normalize((viewNormalMatrix2 * modelNormalMatrix2 * localNormal).xyz);");
+ src.push("vec4 localNormal = vec4(normal, 0.0); ");
}
- src.push("vec3 reflectedColor = vec3(0.0, 0.0, 0.0);");
- src.push("vec3 viewLightDir = vec3(0.0, 0.0, -1.0);");
- src.push("float lambertian = 1.0;");
+ src.push("mat4 modelNormalMatrix2 = modelNormalMatrix;");
+ src.push("mat4 viewNormalMatrix2 = viewNormalMatrix;");
+ }
+ src.push("mat4 viewMatrix2 = viewMatrix;");
+ src.push("mat4 modelMatrix2 = modelMatrix;");
+ if (stationary) {
+ src.push("viewMatrix2[3][0] = viewMatrix2[3][1] = viewMatrix2[3][2] = 0.0;")
+ }
+ if (billboard === "spherical" || billboard === "cylindrical") {
+ src.push("mat4 modelViewMatrix = viewMatrix2 * modelMatrix2;");
+ src.push("billboard(modelMatrix2);");
+ src.push("billboard(viewMatrix2);");
+ src.push("billboard(modelViewMatrix);");
if (geometryState.normals) {
- for (i = 0, len = lightsState.lights.length; i < len; i++) {
- light = lightsState.lights[i];
- if (light.type === "ambient") {
- continue;
+ src.push("mat4 modelViewNormalMatrix = viewNormalMatrix2 * modelNormalMatrix2;");
+ src.push("billboard(modelNormalMatrix2);");
+ src.push("billboard(viewNormalMatrix2);");
+ src.push("billboard(modelViewNormalMatrix);");
+ }
+ src.push("worldPosition = modelMatrix2 * localPosition;");
+ src.push("vec4 viewPosition = modelViewMatrix * localPosition;");
+ } else {
+ src.push("worldPosition = modelMatrix2 * localPosition;");
+ src.push("vec4 viewPosition = viewMatrix2 * worldPosition; ");
+ }
+ if (geometryState.normals) {
+ src.push("vec3 viewNormal = normalize((viewNormalMatrix2 * modelNormalMatrix2 * localNormal).xyz);");
+ }
+ src.push("vec3 reflectedColor = vec3(0.0, 0.0, 0.0);");
+ src.push("vec3 viewLightDir = vec3(0.0, 0.0, -1.0);");
+ src.push("float lambertian = 1.0;");
+ if (geometryState.normals) {
+ for (i = 0, len = lightsState.lights.length; i < len; i++) {
+ light = lightsState.lights[i];
+ if (light.type === "ambient") {
+ continue;
+ }
+ if (light.type === "dir") {
+ if (light.space === "view") {
+ src.push("viewLightDir = normalize(lightDir" + i + ");");
+ } else {
+ src.push("viewLightDir = normalize((viewMatrix2 * vec4(lightDir" + i + ", 0.0)).xyz);");
+ }
+ } else if (light.type === "point") {
+ if (light.space === "view") {
+ src.push("viewLightDir = normalize(lightPos" + i + " - viewPosition.xyz);");
+ } else {
+ src.push("viewLightDir = normalize((viewMatrix2 * vec4(lightPos" + i + ", 0.0)).xyz);");
}
- if (light.type === "dir") {
- if (light.space === "view") {
- src.push("viewLightDir = normalize(lightDir" + i + ");");
- } else {
- src.push("viewLightDir = normalize((viewMatrix2 * vec4(lightDir" + i + ", 0.0)).xyz);");
- }
- } else if (light.type === "point") {
- if (light.space === "view") {
- src.push("viewLightDir = normalize(lightPos" + i + " - viewPosition.xyz);");
- } else {
- src.push("viewLightDir = normalize((viewMatrix2 * vec4(lightPos" + i + ", 0.0)).xyz);");
- }
- } else if (light.type === "spot") {
- if (light.space === "view") {
- src.push("viewLightDir = normalize(lightDir" + i + ");");
- } else {
- src.push("viewLightDir = normalize((viewMatrix2 * vec4(lightDir" + i + ", 0.0)).xyz);");
- }
+ } else if (light.type === "spot") {
+ if (light.space === "view") {
+ src.push("viewLightDir = normalize(lightDir" + i + ");");
} else {
- continue;
+ src.push("viewLightDir = normalize((viewMatrix2 * vec4(lightDir" + i + ", 0.0)).xyz);");
}
- src.push("lambertian = max(dot(-viewNormal, viewLightDir), 0.0);");
- src.push("reflectedColor += lambertian * (lightColor" + i + ".rgb * lightColor" + i + ".a);");
+ } else {
+ continue;
}
+ src.push("lambertian = max(dot(-viewNormal, viewLightDir), 0.0);");
+ src.push("reflectedColor += lambertian * (lightColor" + i + ".rgb * lightColor" + i + ".a);");
}
- //src.push("vColor = vec4((reflectedColor * materialColor) + (lightAmbient.rgb * lightAmbient.a), 1.0) * colorize;");
- src.push("vColor = vec4((reflectedColor * materialColor.rgb), materialColor.a) * colorize;"); // TODO: How to have ambient bright enough for canvas BG but not too bright for scene?
- if (clipping) {
- src.push("vWorldPosition = worldPosition;");
- }
- if (geometryState.primitiveName === "points") {
- src.push("gl_PointSize = pointSize;");
+ }
+ //src.push("vColor = vec4((reflectedColor * materialColor) + (lightAmbient.rgb * lightAmbient.a), 1.0) * colorize;");
+ src.push("vColor = vec4((reflectedColor * materialColor.rgb), materialColor.a) * colorize;"); // TODO: How to have ambient bright enough for canvas BG but not too bright for scene?
+ if (clipping) {
+ src.push("vWorldPosition = worldPosition;");
+ }
+ if (geometryState.primitiveName === "points") {
+ src.push("gl_PointSize = pointSize;");
+ }
+ src.push(" gl_Position = projMatrix * viewPosition;");
+ src.push("}");
+ return src;
+}
+
+function buildFragmentLambert(mesh) {
+ const scene = mesh.scene;
+ const clipsState = scene._clipsState;
+ const materialState = mesh._material._state;
+ const geometryState = mesh._geometry._state;
+ let i;
+ let len;
+ const clipping = clipsState.clips.length > 0;
+ const solid = false && materialState.backfaces;
+ const gammaOutput = scene.gammaOutput; // If set, then it expects that all textures and colors need to be outputted in premultiplied gamma. Default is false.
+ const src = [];
+ src.push("// Lambertian drawing fragment shader");
+ src.push("precision lowp float;");
+ if (clipping) {
+ src.push("varying vec4 vWorldPosition;");
+ src.push("uniform bool clippable;");
+ for (i = 0, len = clipsState.clips.length; i < len; i++) {
+ src.push("uniform bool clipActive" + i + ";");
+ src.push("uniform vec3 clipPos" + i + ";");
+ src.push("uniform vec3 clipDir" + i + ";");
}
- src.push(" gl_Position = projMatrix * viewPosition;");
+ }
+ src.push("varying vec4 vColor;");
+ if (gammaOutput) {
+ src.push("uniform float gammaFactor;");
+ src.push(" vec4 linearToGamma( in vec4 value, in float gammaFactor ) {");
+ src.push(" return vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );");
src.push("}");
- return src;
- }
-
- function buildFragmentLambert(mesh) {
- const scene = mesh.scene;
- const clipsState = scene._clipsState;
- const materialState = mesh._material._state;
- const geometryState = mesh._geometry._state;
- let i;
- let len;
- const clipping = clipsState.clips.length > 0;
- const solid = false && materialState.backfaces;
- const gammaOutput = scene.gammaOutput; // If set, then it expects that all textures and colors need to be outputted in premultiplied gamma. Default is false.
- const src = [];
- src.push("// Lambertian drawing fragment shader");
- src.push("precision lowp float;");
- if (clipping) {
- src.push("varying vec4 vWorldPosition;");
- src.push("uniform bool clippable;");
- for (i = 0, len = clipsState.clips.length; i < len; i++) {
- src.push("uniform bool clipActive" + i + ";");
- src.push("uniform vec3 clipPos" + i + ";");
- src.push("uniform vec3 clipDir" + i + ";");
- }
- }
- src.push("varying vec4 vColor;");
- if (gammaOutput) {
- src.push("uniform float gammaFactor;");
- src.push(" vec4 linearToGamma( in vec4 value, in float gammaFactor ) {");
- src.push(" return vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );");
- src.push("}");
- }
- src.push("void main(void) {");
- if (clipping) {
- src.push("if (clippable) {");
- src.push(" float dist = 0.0;");
- for (i = 0, len = clipsState.clips.length; i < len; i++) {
- src.push("if (clipActive" + i + ") {");
- src.push(" dist += clamp(dot(-clipDir" + i + ".xyz, vWorldPosition.xyz - clipPos" + i + ".xyz), 0.0, 1000.0);");
- src.push("}");
- }
- src.push(" if (dist > 0.0) { discard; }");
- if (solid) {
- src.push(" if (gl_FrontFacing == false) {");
- src.push(" gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);");
- src.push(" return;");
- src.push(" }");
- }
+ }
+ src.push("void main(void) {");
+ if (clipping) {
+ src.push("if (clippable) {");
+ src.push(" float dist = 0.0;");
+ for (i = 0, len = clipsState.clips.length; i < len; i++) {
+ src.push("if (clipActive" + i + ") {");
+ src.push(" dist += clamp(dot(-clipDir" + i + ".xyz, vWorldPosition.xyz - clipPos" + i + ".xyz), 0.0, 1000.0);");
src.push("}");
}
- if (geometryState.primitiveName === "points") {
- src.push("vec2 cxy = 2.0 * gl_PointCoord - 1.0;");
- src.push("float r = dot(cxy, cxy);");
- src.push("if (r > 1.0) {");
- src.push(" discard;");
- src.push("}");
-
- }
- if (gammaOutput) {
- src.push("gl_FragColor = linearToGamma(vColor, gammaFactor);");
- } else {
- src.push("gl_FragColor = vColor;");
+ src.push(" if (dist > 0.0) { discard; }");
+ if (solid) {
+ src.push(" if (gl_FrontFacing == false) {");
+ src.push(" gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);");
+ src.push(" return;");
+ src.push(" }");
}
src.push("}");
- return src;
- }
-
- function buildVertexDraw(mesh) {
- const scene = mesh.scene;
- const material = mesh._material;
- const meshState = mesh._state;
- const clipsState = scene._clipsState;
- const geometryState = mesh._geometry._state;
- const materialState = mesh._material._state;
- const lightsState = scene._lightsState;
- let i;
- let len;
- let light;
- const billboard = meshState.billboard;
- const stationary = meshState.stationary;
- const texturing = hasTextures(mesh);
- const normals = hasNormals(mesh);
- const clipping = clipsState.clips.length > 0;
- const receiveShadow = receivesShadow(mesh);
- const quantizedGeometry = !!geometryState.quantized;
- const src = [];
- if (normals && material._normalMap) {
- src.push("#extension GL_OES_standard_derivatives : enable");
- }
- src.push("// Drawing vertex shader");
- src.push("attribute vec3 position;");
+ }
+ if (geometryState.primitiveName === "points") {
+ src.push("vec2 cxy = 2.0 * gl_PointCoord - 1.0;");
+ src.push("float r = dot(cxy, cxy);");
+ src.push("if (r > 1.0) {");
+ src.push(" discard;");
+ src.push("}");
- if (quantizedGeometry) {
- src.push("uniform mat4 positionsDecodeMatrix;");
- }
- src.push("uniform mat4 modelMatrix;");
- src.push("uniform mat4 viewMatrix;");
- src.push("uniform mat4 projMatrix;");
- src.push("varying vec3 vViewPosition;");
- if (clipping) {
- src.push("varying vec4 vWorldPosition;");
- }
- if (lightsState.lightMaps.length > 0) {
- src.push("varying vec3 vWorldNormal;");
- }
- if (normals) {
- src.push("attribute vec3 normal;");
- src.push("uniform mat4 modelNormalMatrix;");
- src.push("uniform mat4 viewNormalMatrix;");
- src.push("varying vec3 vViewNormal;");
- for (i = 0, len = lightsState.lights.length; i < len; i++) {
- light = lightsState.lights[i];
- if (light.type === "ambient") {
- continue;
- }
- if (light.type === "dir") {
- src.push("uniform vec3 lightDir" + i + ";");
- }
- if (light.type === "point") {
- src.push("uniform vec3 lightPos" + i + ";");
- }
- if (light.type === "spot") {
- src.push("uniform vec3 lightPos" + i + ";");
- src.push("uniform vec3 lightDir" + i + ";");
- }
- if (!(light.type === "dir" && light.space === "view")) {
- src.push("varying vec4 vViewLightReverseDirAndDist" + i + ";");
- }
+ }
+ if (gammaOutput) {
+ src.push("gl_FragColor = linearToGamma(vColor, gammaFactor);");
+ } else {
+ src.push("gl_FragColor = vColor;");
+ }
+ src.push("}");
+ return src;
+}
+
+function buildVertexDraw(mesh) {
+ const scene = mesh.scene;
+ const material = mesh._material;
+ const meshState = mesh._state;
+ const clipsState = scene._clipsState;
+ const geometryState = mesh._geometry._state;
+ const materialState = mesh._material._state;
+ const lightsState = scene._lightsState;
+ let i;
+ let len;
+ let light;
+ const billboard = meshState.billboard;
+ const stationary = meshState.stationary;
+ const texturing = hasTextures(mesh);
+ const normals = hasNormals(mesh);
+ const clipping = clipsState.clips.length > 0;
+ const receiveShadow = receivesShadow(mesh);
+ const quantizedGeometry = !!geometryState.quantized;
+ const src = [];
+ if (normals && material._normalMap) {
+ src.push("#extension GL_OES_standard_derivatives : enable");
+ }
+ src.push("// Drawing vertex shader");
+ src.push("attribute vec3 position;");
+
+ if (quantizedGeometry) {
+ src.push("uniform mat4 positionsDecodeMatrix;");
+ }
+ src.push("uniform mat4 modelMatrix;");
+ src.push("uniform mat4 viewMatrix;");
+ src.push("uniform mat4 projMatrix;");
+ src.push("varying vec3 vViewPosition;");
+ if (clipping) {
+ src.push("varying vec4 vWorldPosition;");
+ }
+ if (lightsState.lightMaps.length > 0) {
+ src.push("varying vec3 vWorldNormal;");
+ }
+ if (normals) {
+ src.push("attribute vec3 normal;");
+ src.push("uniform mat4 modelNormalMatrix;");
+ src.push("uniform mat4 viewNormalMatrix;");
+ src.push("varying vec3 vViewNormal;");
+ for (i = 0, len = lightsState.lights.length; i < len; i++) {
+ light = lightsState.lights[i];
+ if (light.type === "ambient") {
+ continue;
}
- if (quantizedGeometry) {
- src.push("vec3 octDecode(vec2 oct) {");
- src.push(" vec3 v = vec3(oct.xy, 1.0 - abs(oct.x) - abs(oct.y));");
- src.push(" if (v.z < 0.0) {");
- src.push(" v.xy = (1.0 - abs(v.yx)) * vec2(v.x >= 0.0 ? 1.0 : -1.0, v.y >= 0.0 ? 1.0 : -1.0);");
- src.push(" }");
- src.push(" return normalize(v);");
- src.push("}");
+ if (light.type === "dir") {
+ src.push("uniform vec3 lightDir" + i + ";");
}
- }
- if (texturing) {
- src.push("attribute vec2 uv;");
- src.push("varying vec2 vUV;");
- if (quantizedGeometry) {
- src.push("uniform mat3 uvDecodeMatrix;")
+ if (light.type === "point") {
+ src.push("uniform vec3 lightPos" + i + ";");
}
- }
- if (geometryState.colors) {
- src.push("attribute vec4 color;");
- src.push("varying vec4 vColor;");
- }
- if (geometryState.primitiveName === "points") {
- src.push("uniform float pointSize;");
- }
- if (billboard === "spherical" || billboard === "cylindrical") {
- src.push("void billboard(inout mat4 mat) {");
- src.push(" mat[0][0] = 1.0;");
- src.push(" mat[0][1] = 0.0;");
- src.push(" mat[0][2] = 0.0;");
- if (billboard === "spherical") {
- src.push(" mat[1][0] = 0.0;");
- src.push(" mat[1][1] = 1.0;");
- src.push(" mat[1][2] = 0.0;");
+ if (light.type === "spot") {
+ src.push("uniform vec3 lightPos" + i + ";");
+ src.push("uniform vec3 lightDir" + i + ";");
}
- src.push(" mat[2][0] = 0.0;");
- src.push(" mat[2][1] = 0.0;");
- src.push(" mat[2][2] =1.0;");
+ if (!(light.type === "dir" && light.space === "view")) {
+ src.push("varying vec4 vViewLightReverseDirAndDist" + i + ";");
+ }
+ }
+ if (quantizedGeometry) {
+ src.push("vec3 octDecode(vec2 oct) {");
+ src.push(" vec3 v = vec3(oct.xy, 1.0 - abs(oct.x) - abs(oct.y));");
+ src.push(" if (v.z < 0.0) {");
+ src.push(" v.xy = (1.0 - abs(v.yx)) * vec2(v.x >= 0.0 ? 1.0 : -1.0, v.y >= 0.0 ? 1.0 : -1.0);");
+ src.push(" }");
+ src.push(" return normalize(v);");
src.push("}");
}
- if (receiveShadow) {
- src.push("const mat4 texUnitConverter = mat4(0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.5, 0.5, 0.5, 1.0);");
- // for (i = 0, len = lights.length; i < len; i++) { // Light sources
- // if (lights[i].shadow) {
- // src.push("uniform mat4 shadowViewMatrix" + i + ";");
- // src.push("uniform mat4 shadowProjMatrix" + i + ";");
- // src.push("varying vec4 vShadowPosFromLight" + i + ";");
- // }
- // }
- }
- src.push("void main(void) {");
- src.push("vec4 localPosition = vec4(position, 1.0); ");
- src.push("vec4 worldPosition;");
+ }
+ if (texturing) {
+ src.push("attribute vec2 uv;");
+ src.push("varying vec2 vUV;");
if (quantizedGeometry) {
- src.push("localPosition = positionsDecodeMatrix * localPosition;");
+ src.push("uniform mat3 uvDecodeMatrix;")
}
- if (normals) {
- if (quantizedGeometry) {
- src.push("vec4 localNormal = vec4(octDecode(normal.xy), 0.0); ");
- } else {
- src.push("vec4 localNormal = vec4(normal, 0.0); ");
- }
- src.push("mat4 modelNormalMatrix2 = modelNormalMatrix;");
- src.push("mat4 viewNormalMatrix2 = viewNormalMatrix;");
- }
- src.push("mat4 viewMatrix2 = viewMatrix;");
- src.push("mat4 modelMatrix2 = modelMatrix;");
- if (stationary) {
- src.push("viewMatrix2[3][0] = viewMatrix2[3][1] = viewMatrix2[3][2] = 0.0;")
- }
- if (billboard === "spherical" || billboard === "cylindrical") {
- src.push("mat4 modelViewMatrix = viewMatrix2 * modelMatrix2;");
- src.push("billboard(modelMatrix2);");
- src.push("billboard(viewMatrix2);");
- src.push("billboard(modelViewMatrix);");
- if (normals) {
- src.push("mat4 modelViewNormalMatrix = viewNormalMatrix2 * modelNormalMatrix2;");
- src.push("billboard(modelNormalMatrix2);");
- src.push("billboard(viewNormalMatrix2);");
- src.push("billboard(modelViewNormalMatrix);");
- }
- src.push("worldPosition = modelMatrix2 * localPosition;");
- src.push("vec4 viewPosition = modelViewMatrix * localPosition;");
+ }
+ if (geometryState.colors) {
+ src.push("attribute vec4 color;");
+ src.push("varying vec4 vColor;");
+ }
+ if (geometryState.primitiveName === "points") {
+ src.push("uniform float pointSize;");
+ }
+ if (billboard === "spherical" || billboard === "cylindrical") {
+ src.push("void billboard(inout mat4 mat) {");
+ src.push(" mat[0][0] = 1.0;");
+ src.push(" mat[0][1] = 0.0;");
+ src.push(" mat[0][2] = 0.0;");
+ if (billboard === "spherical") {
+ src.push(" mat[1][0] = 0.0;");
+ src.push(" mat[1][1] = 1.0;");
+ src.push(" mat[1][2] = 0.0;");
+ }
+ src.push(" mat[2][0] = 0.0;");
+ src.push(" mat[2][1] = 0.0;");
+ src.push(" mat[2][2] =1.0;");
+ src.push("}");
+ }
+ if (receiveShadow) {
+ src.push("const mat4 texUnitConverter = mat4(0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.5, 0.5, 0.5, 1.0);");
+ // for (i = 0, len = lights.length; i < len; i++) { // Light sources
+ // if (lights[i].shadow) {
+ // src.push("uniform mat4 shadowViewMatrix" + i + ";");
+ // src.push("uniform mat4 shadowProjMatrix" + i + ";");
+ // src.push("varying vec4 vShadowPosFromLight" + i + ";");
+ // }
+ // }
+ }
+ src.push("void main(void) {");
+ src.push("vec4 localPosition = vec4(position, 1.0); ");
+ src.push("vec4 worldPosition;");
+ if (quantizedGeometry) {
+ src.push("localPosition = positionsDecodeMatrix * localPosition;");
+ }
+ if (normals) {
+ if (quantizedGeometry) {
+ src.push("vec4 localNormal = vec4(octDecode(normal.xy), 0.0); ");
} else {
- src.push("worldPosition = modelMatrix2 * localPosition;");
- src.push("vec4 viewPosition = viewMatrix2 * worldPosition; ");
+ src.push("vec4 localNormal = vec4(normal, 0.0); ");
}
+ src.push("mat4 modelNormalMatrix2 = modelNormalMatrix;");
+ src.push("mat4 viewNormalMatrix2 = viewNormalMatrix;");
+ }
+ src.push("mat4 viewMatrix2 = viewMatrix;");
+ src.push("mat4 modelMatrix2 = modelMatrix;");
+ if (stationary) {
+ src.push("viewMatrix2[3][0] = viewMatrix2[3][1] = viewMatrix2[3][2] = 0.0;")
+ }
+ if (billboard === "spherical" || billboard === "cylindrical") {
+ src.push("mat4 modelViewMatrix = viewMatrix2 * modelMatrix2;");
+ src.push("billboard(modelMatrix2);");
+ src.push("billboard(viewMatrix2);");
+ src.push("billboard(modelViewMatrix);");
if (normals) {
- src.push("vec3 worldNormal = (modelNormalMatrix2 * localNormal).xyz; ");
- if (lightsState.lightMaps.length > 0) {
- src.push("vWorldNormal = worldNormal;");
- }
- src.push("vViewNormal = normalize((viewNormalMatrix2 * vec4(worldNormal, 1.0)).xyz);");
- src.push("vec3 tmpVec3;");
- src.push("float lightDist;");
- for (i = 0, len = lightsState.lights.length; i < len; i++) { // Lights
- light = lightsState.lights[i];
- if (light.type === "ambient") {
- continue;
- }
- if (light.type === "dir") {
- if (light.space === "world") {
- src.push("tmpVec3 = vec3(viewMatrix2 * vec4(lightDir" + i + ", 0.0) ).xyz;");
- src.push("vViewLightReverseDirAndDist" + i + " = vec4(-tmpVec3, 0.0);");
- }
- }
- if (light.type === "point") {
- if (light.space === "world") {
- src.push("tmpVec3 = (viewMatrix2 * vec4(lightPos" + i + ", 1.0)).xyz - viewPosition.xyz;");
- src.push("lightDist = abs(length(tmpVec3));");
- } else {
- src.push("tmpVec3 = lightPos" + i + ".xyz - viewPosition.xyz;");
- src.push("lightDist = abs(length(tmpVec3));");
- }
- src.push("vViewLightReverseDirAndDist" + i + " = vec4(tmpVec3, lightDist);");
+ src.push("mat4 modelViewNormalMatrix = viewNormalMatrix2 * modelNormalMatrix2;");
+ src.push("billboard(modelNormalMatrix2);");
+ src.push("billboard(viewNormalMatrix2);");
+ src.push("billboard(modelViewNormalMatrix);");
+ }
+ src.push("worldPosition = modelMatrix2 * localPosition;");
+ src.push("vec4 viewPosition = modelViewMatrix * localPosition;");
+ } else {
+ src.push("worldPosition = modelMatrix2 * localPosition;");
+ src.push("vec4 viewPosition = viewMatrix2 * worldPosition; ");
+ }
+ if (normals) {
+ src.push("vec3 worldNormal = (modelNormalMatrix2 * localNormal).xyz; ");
+ if (lightsState.lightMaps.length > 0) {
+ src.push("vWorldNormal = worldNormal;");
+ }
+ src.push("vViewNormal = normalize((viewNormalMatrix2 * vec4(worldNormal, 1.0)).xyz);");
+ src.push("vec3 tmpVec3;");
+ src.push("float lightDist;");
+ for (i = 0, len = lightsState.lights.length; i < len; i++) { // Lights
+ light = lightsState.lights[i];
+ if (light.type === "ambient") {
+ continue;
+ }
+ if (light.type === "dir") {
+ if (light.space === "world") {
+ src.push("tmpVec3 = vec3(viewMatrix2 * vec4(lightDir" + i + ", 0.0) ).xyz;");
+ src.push("vViewLightReverseDirAndDist" + i + " = vec4(-tmpVec3, 0.0);");
}
}
- }
- if (texturing) {
- if (quantizedGeometry) {
- src.push("vUV = (uvDecodeMatrix * vec3(uv, 1.0)).xy;");
- } else {
- src.push("vUV = uv;");
+ if (light.type === "point") {
+ if (light.space === "world") {
+ src.push("tmpVec3 = (viewMatrix2 * vec4(lightPos" + i + ", 1.0)).xyz - viewPosition.xyz;");
+ src.push("lightDist = abs(length(tmpVec3));");
+ } else {
+ src.push("tmpVec3 = lightPos" + i + ".xyz - viewPosition.xyz;");
+ src.push("lightDist = abs(length(tmpVec3));");
+ }
+ src.push("vViewLightReverseDirAndDist" + i + " = vec4(tmpVec3, lightDist);");
}
}
- if (geometryState.colors) {
- src.push("vColor = color;");
- }
- if (geometryState.primitiveName === "points") {
- src.push("gl_PointSize = pointSize;");
- }
- if (clipping) {
- src.push("vWorldPosition = worldPosition;");
+ }
+ if (texturing) {
+ if (quantizedGeometry) {
+ src.push("vUV = (uvDecodeMatrix * vec3(uv, 1.0)).xy;");
+ } else {
+ src.push("vUV = uv;");
}
- src.push(" vViewPosition = viewPosition.xyz;");
- src.push(" gl_Position = projMatrix * viewPosition;");
- src.push("const mat4 texUnitConverter = mat4(0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.5, 0.5, 0.5, 1.0);");
- if (receiveShadow) {
- src.push("vec4 tempx; ");
- for (i = 0, len = lightsState.lights.length; i < len; i++) { // Light sources
- if (lightsState.lights[i].shadow) {
- src.push("vShadowPosFromLight" + i + " = texUnitConverter * shadowProjMatrix" + i + " * (shadowViewMatrix" + i + " * worldPosition); ");
- }
+ }
+ if (geometryState.colors) {
+ src.push("vColor = color;");
+ }
+ if (geometryState.primitiveName === "points") {
+ src.push("gl_PointSize = pointSize;");
+ }
+ if (clipping) {
+ src.push("vWorldPosition = worldPosition;");
+ }
+ src.push(" vViewPosition = viewPosition.xyz;");
+ src.push(" gl_Position = projMatrix * viewPosition;");
+ src.push("const mat4 texUnitConverter = mat4(0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.5, 0.5, 0.5, 1.0);");
+ if (receiveShadow) {
+ src.push("vec4 tempx; ");
+ for (i = 0, len = lightsState.lights.length; i < len; i++) { // Light sources
+ if (lightsState.lights[i].shadow) {
+ src.push("vShadowPosFromLight" + i + " = texUnitConverter * shadowProjMatrix" + i + " * (shadowViewMatrix" + i + " * worldPosition); ");
}
}
- src.push("}");
- return src;
- }
-
- function buildFragmentDraw(mesh) {
-
- const scene = mesh.scene;
- const gl = scene.canvas.gl;
- const material = mesh._material;
- const geometryState = mesh._geometry._state;
- const clipsState = mesh.scene._clipsState;
- const lightsState = mesh.scene._lightsState;
- const materialState = mesh._material._state;
- const clipping = clipsState.clips.length > 0;
- const normals = hasNormals(mesh);
- const uvs = geometryState.uv;
- const solid = false && materialState.backfaces;
- const phongMaterial = (materialState.type === "PhongMaterial");
- const metallicMaterial = (materialState.type === "MetallicMaterial");
- const specularMaterial = (materialState.type === "SpecularMaterial");
- const receiveShadow = receivesShadow(mesh);
- const gammaInput = scene.gammaInput; // If set, then it expects that all textures and colors are premultiplied gamma. Default is false.
- const gammaOutput = scene.gammaOutput; // If set, then it expects that all textures and colors need to be outputted in premultiplied gamma. Default is false.
- var i;
- let len;
- let light;
- const src = [];
-
- src.push("// Fragment vertex shader");
-
- if (normals && material._normalMap) {
- src.push("#extension GL_OES_standard_derivatives : enable");
- }
-
- src.push("precision " + getFragmentFloatPrecision(gl) + " float;");
-
- if (receiveShadow) {
- src.push("float unpackDepth (vec4 color) {");
- src.push(" const vec4 bitShift = vec4(1.0, 1.0/256.0, 1.0/(256.0 * 256.0), 1.0/(256.0*256.0*256.0));");
- src.push(" return dot(color, bitShift);");
- src.push("}");
- }
+ }
+ src.push("}");
+ return src;
+}
+
+function buildFragmentDraw(mesh) {
+
+ const scene = mesh.scene;
+ const gl = scene.canvas.gl;
+ const material = mesh._material;
+ const geometryState = mesh._geometry._state;
+ const clipsState = mesh.scene._clipsState;
+ const lightsState = mesh.scene._lightsState;
+ const materialState = mesh._material._state;
+ const clipping = clipsState.clips.length > 0;
+ const normals = hasNormals(mesh);
+ const uvs = geometryState.uv;
+ const solid = false && materialState.backfaces;
+ const phongMaterial = (materialState.type === "PhongMaterial");
+ const metallicMaterial = (materialState.type === "MetallicMaterial");
+ const specularMaterial = (materialState.type === "SpecularMaterial");
+ const receiveShadow = receivesShadow(mesh);
+ const gammaInput = scene.gammaInput; // If set, then it expects that all textures and colors are premultiplied gamma. Default is false.
+ const gammaOutput = scene.gammaOutput; // If set, then it expects that all textures and colors need to be outputted in premultiplied gamma. Default is false.
+ var i;
+ let len;
+ let light;
+ const src = [];
+
+ src.push("// Fragment vertex shader");
+
+ if (normals && material._normalMap) {
+ src.push("#extension GL_OES_standard_derivatives : enable");
+ }
- //--------------------------------------------------------------------------------
- // GAMMA CORRECTION
- //--------------------------------------------------------------------------------
+ src.push("precision " + getFragmentFloatPrecision(gl) + " float;");
- src.push("uniform float gammaFactor;");
- src.push("vec4 linearToLinear( in vec4 value ) {");
- src.push(" return value;");
+ if (receiveShadow) {
+ src.push("float unpackDepth (vec4 color) {");
+ src.push(" const vec4 bitShift = vec4(1.0, 1.0/256.0, 1.0/(256.0 * 256.0), 1.0/(256.0*256.0*256.0));");
+ src.push(" return dot(color, bitShift);");
src.push("}");
- src.push("vec4 sRGBToLinear( in vec4 value ) {");
- src.push(" return vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.w );");
- src.push("}");
- src.push("vec4 gammaToLinear( in vec4 value) {");
- src.push(" return vec4( pow( value.xyz, vec3( gammaFactor ) ), value.w );");
+ }
+
+ //--------------------------------------------------------------------------------
+ // GAMMA CORRECTION
+ //--------------------------------------------------------------------------------
+
+ src.push("uniform float gammaFactor;");
+ src.push("vec4 linearToLinear( in vec4 value ) {");
+ src.push(" return value;");
+ src.push("}");
+ src.push("vec4 sRGBToLinear( in vec4 value ) {");
+ src.push(" return vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.w );");
+ src.push("}");
+ src.push("vec4 gammaToLinear( in vec4 value) {");
+ src.push(" return vec4( pow( value.xyz, vec3( gammaFactor ) ), value.w );");
+ src.push("}");
+ if (gammaOutput) {
+ src.push("vec4 linearToGamma( in vec4 value, in float gammaFactor ) {");
+ src.push(" return vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );");
src.push("}");
- if (gammaOutput) {
- src.push("vec4 linearToGamma( in vec4 value, in float gammaFactor ) {");
- src.push(" return vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );");
- src.push("}");
+ }
+
+ //--------------------------------------------------------------------------------
+ // USER CLIP PLANES
+ //--------------------------------------------------------------------------------
+
+ if (clipping) {
+ src.push("varying vec4 vWorldPosition;");
+ src.push("uniform bool clippable;");
+ for (var i = 0; i < clipsState.clips.length; i++) {
+ src.push("uniform bool clipActive" + i + ";");
+ src.push("uniform vec3 clipPos" + i + ";");
+ src.push("uniform vec3 clipDir" + i + ";");
}
+ }
+
+ if (normals) {
//--------------------------------------------------------------------------------
- // USER CLIP PLANES
+ // LIGHT AND REFLECTION MAP INPUTS
+ // Define here so available globally to shader functions
//--------------------------------------------------------------------------------
- if (clipping) {
- src.push("varying vec4 vWorldPosition;");
- src.push("uniform bool clippable;");
- for (var i = 0; i < clipsState.clips.length; i++) {
- src.push("uniform bool clipActive" + i + ";");
- src.push("uniform vec3 clipPos" + i + ";");
- src.push("uniform vec3 clipDir" + i + ";");
- }
+ if (lightsState.lightMaps.length > 0) {
+ src.push("uniform samplerCube lightMap;");
+ src.push("uniform mat4 viewNormalMatrix;");
+ }
+ if (lightsState.reflectionMaps.length > 0) {
+ src.push("uniform samplerCube reflectionMap;");
+ }
+ if (lightsState.lightMaps.length > 0 || lightsState.reflectionMaps.length > 0) {
+ src.push("uniform mat4 viewMatrix;");
}
- if (normals) {
-
- //--------------------------------------------------------------------------------
- // LIGHT AND REFLECTION MAP INPUTS
- // Define here so available globally to shader functions
- //--------------------------------------------------------------------------------
-
- if (lightsState.lightMaps.length > 0) {
- src.push("uniform samplerCube lightMap;");
- src.push("uniform mat4 viewNormalMatrix;");
- }
- if (lightsState.reflectionMaps.length > 0) {
- src.push("uniform samplerCube reflectionMap;");
- }
- if (lightsState.lightMaps.length > 0 || lightsState.reflectionMaps.length > 0) {
- src.push("uniform mat4 viewMatrix;");
- }
+ //--------------------------------------------------------------------------------
+ // SHADING FUNCTIONS
+ //--------------------------------------------------------------------------------
- //--------------------------------------------------------------------------------
- // SHADING FUNCTIONS
- //--------------------------------------------------------------------------------
+ // CONSTANT DEFINITIONS
- // CONSTANT DEFINITIONS
+ src.push("#define PI 3.14159265359");
+ src.push("#define RECIPROCAL_PI 0.31830988618");
+ src.push("#define RECIPROCAL_PI2 0.15915494");
+ src.push("#define EPSILON 1e-6");
- src.push("#define PI 3.14159265359");
- src.push("#define RECIPROCAL_PI 0.31830988618");
- src.push("#define RECIPROCAL_PI2 0.15915494");
- src.push("#define EPSILON 1e-6");
+ src.push("#define saturate(a) clamp( a, 0.0, 1.0 )");
- src.push("#define saturate(a) clamp( a, 0.0, 1.0 )");
+ // UTILITY DEFINITIONS
- // UTILITY DEFINITIONS
+ src.push("vec3 inverseTransformDirection(in vec3 dir, in mat4 matrix) {");
+ src.push(" return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );");
+ src.push("}");
- src.push("vec3 inverseTransformDirection(in vec3 dir, in mat4 matrix) {");
- src.push(" return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );");
- src.push("}");
+ // STRUCTURES
- // STRUCTURES
+ src.push("struct IncidentLight {");
+ src.push(" vec3 color;");
+ src.push(" vec3 direction;");
+ src.push("};");
- src.push("struct IncidentLight {");
- src.push(" vec3 color;");
- src.push(" vec3 direction;");
- src.push("};");
+ src.push("struct ReflectedLight {");
+ src.push(" vec3 diffuse;");
+ src.push(" vec3 specular;");
+ src.push("};");
- src.push("struct ReflectedLight {");
- src.push(" vec3 diffuse;");
- src.push(" vec3 specular;");
- src.push("};");
+ src.push("struct Geometry {");
+ src.push(" vec3 position;");
+ src.push(" vec3 viewNormal;");
+ src.push(" vec3 worldNormal;");
+ src.push(" vec3 viewEyeDir;");
+ src.push("};");
- src.push("struct Geometry {");
- src.push(" vec3 position;");
- src.push(" vec3 viewNormal;");
- src.push(" vec3 worldNormal;");
- src.push(" vec3 viewEyeDir;");
- src.push("};");
+ src.push("struct Material {");
+ src.push(" vec3 diffuseColor;");
+ src.push(" float specularRoughness;");
+ src.push(" vec3 specularColor;");
+ src.push(" float shine;"); // Only used for Phong
+ src.push("};");
- src.push("struct Material {");
- src.push(" vec3 diffuseColor;");
- src.push(" float specularRoughness;");
- src.push(" vec3 specularColor;");
- src.push(" float shine;"); // Only used for Phong
- src.push("};");
+ // COMMON UTILS
- // COMMON UTILS
+ if (phongMaterial) {
- if (phongMaterial) {
+ if (lightsState.lightMaps.length > 0 || lightsState.reflectionMaps.length > 0) {
- if (lightsState.lightMaps.length > 0 || lightsState.reflectionMaps.length > 0) {
-
- src.push("void computePhongLightMapping(const in Geometry geometry, const in Material material, inout ReflectedLight reflectedLight) {");
- if (lightsState.lightMaps.length > 0) {
- src.push(" vec3 irradiance = " + TEXTURE_DECODE_FUNCS[lightsState.lightMaps[0].encoding] + "(textureCube(lightMap, geometry.worldNormal)).rgb;");
- src.push(" irradiance *= PI;");
- src.push(" vec3 diffuseBRDFContrib = (RECIPROCAL_PI * material.diffuseColor);");
- src.push(" reflectedLight.diffuse += irradiance * diffuseBRDFContrib;");
- }
- if (lightsState.reflectionMaps.length > 0) {
- src.push(" vec3 reflectVec = reflect(-geometry.viewEyeDir, geometry.viewNormal);");
- src.push(" vec3 radiance = textureCube(reflectionMap, reflectVec).rgb * 0.2;");
- // src.push(" radiance *= PI;");
- src.push(" reflectedLight.specular += radiance;");
- }
- src.push("}");
+ src.push("void computePhongLightMapping(const in Geometry geometry, const in Material material, inout ReflectedLight reflectedLight) {");
+ if (lightsState.lightMaps.length > 0) {
+ src.push(" vec3 irradiance = " + TEXTURE_DECODE_FUNCS[lightsState.lightMaps[0].encoding] + "(textureCube(lightMap, geometry.worldNormal)).rgb;");
+ src.push(" irradiance *= PI;");
+ src.push(" vec3 diffuseBRDFContrib = (RECIPROCAL_PI * material.diffuseColor);");
+ src.push(" reflectedLight.diffuse += irradiance * diffuseBRDFContrib;");
+ }
+ if (lightsState.reflectionMaps.length > 0) {
+ src.push(" vec3 reflectVec = reflect(-geometry.viewEyeDir, geometry.viewNormal);");
+ src.push(" vec3 radiance = textureCube(reflectionMap, reflectVec).rgb * 0.2;");
+ // src.push(" radiance *= PI;");
+ src.push(" reflectedLight.specular += radiance;");
}
-
- src.push("void computePhongLighting(const in IncidentLight directLight, const in Geometry geometry, const in Material material, inout ReflectedLight reflectedLight) {");
- src.push(" float dotNL = saturate(dot(geometry.viewNormal, directLight.direction));");
- src.push(" vec3 irradiance = dotNL * directLight.color * PI;");
- src.push(" reflectedLight.diffuse += irradiance * (RECIPROCAL_PI * material.diffuseColor);");
- src.push(" reflectedLight.specular += directLight.color * material.specularColor * pow(max(dot(reflect(-directLight.direction, -geometry.viewNormal), geometry.viewEyeDir), 0.0), material.shine);");
src.push("}");
}
- if (metallicMaterial || specularMaterial) {
+ src.push("void computePhongLighting(const in IncidentLight directLight, const in Geometry geometry, const in Material material, inout ReflectedLight reflectedLight) {");
+ src.push(" float dotNL = saturate(dot(geometry.viewNormal, directLight.direction));");
+ src.push(" vec3 irradiance = dotNL * directLight.color * PI;");
+ src.push(" reflectedLight.diffuse += irradiance * (RECIPROCAL_PI * material.diffuseColor);");
+ src.push(" reflectedLight.specular += directLight.color * material.specularColor * pow(max(dot(reflect(-directLight.direction, -geometry.viewNormal), geometry.viewEyeDir), 0.0), material.shine);");
+ src.push("}");
+ }
- // IRRADIANCE EVALUATION
+ if (metallicMaterial || specularMaterial) {
- src.push("float GGXRoughnessToBlinnExponent(const in float ggxRoughness) {");
- src.push(" float r = ggxRoughness + 0.0001;");
- src.push(" return (2.0 / (r * r) - 2.0);");
- src.push("}");
+ // IRRADIANCE EVALUATION
- src.push("float getSpecularMIPLevel(const in float blinnShininessExponent, const in int maxMIPLevel) {");
- src.push(" float maxMIPLevelScalar = float( maxMIPLevel );");
- src.push(" float desiredMIPLevel = maxMIPLevelScalar - 0.79248 - 0.5 * log2( ( blinnShininessExponent * blinnShininessExponent ) + 1.0 );");
- src.push(" return clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );");
+ src.push("float GGXRoughnessToBlinnExponent(const in float ggxRoughness) {");
+ src.push(" float r = ggxRoughness + 0.0001;");
+ src.push(" return (2.0 / (r * r) - 2.0);");
+ src.push("}");
+
+ src.push("float getSpecularMIPLevel(const in float blinnShininessExponent, const in int maxMIPLevel) {");
+ src.push(" float maxMIPLevelScalar = float( maxMIPLevel );");
+ src.push(" float desiredMIPLevel = maxMIPLevelScalar - 0.79248 - 0.5 * log2( ( blinnShininessExponent * blinnShininessExponent ) + 1.0 );");
+ src.push(" return clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );");
+ src.push("}");
+
+ if (lightsState.reflectionMaps.length > 0) {
+ src.push("vec3 getLightProbeIndirectRadiance(const in vec3 reflectVec, const in float blinnShininessExponent, const in int maxMIPLevel) {");
+ src.push(" float mipLevel = 0.5 * getSpecularMIPLevel(blinnShininessExponent, maxMIPLevel);"); //TODO: a random factor - fix this
+ src.push(" vec3 envMapColor = " + TEXTURE_DECODE_FUNCS[lightsState.reflectionMaps[0].encoding] + "(textureCube(reflectionMap, reflectVec, mipLevel)).rgb;");
+ src.push(" return envMapColor;");
src.push("}");
+ }
- if (lightsState.reflectionMaps.length > 0) {
- src.push("vec3 getLightProbeIndirectRadiance(const in vec3 reflectVec, const in float blinnShininessExponent, const in int maxMIPLevel) {");
- src.push(" float mipLevel = 0.5 * getSpecularMIPLevel(blinnShininessExponent, maxMIPLevel);"); //TODO: a random factor - fix this
- src.push(" vec3 envMapColor = " + TEXTURE_DECODE_FUNCS[lightsState.reflectionMaps[0].encoding] + "(textureCube(reflectionMap, reflectVec, mipLevel)).rgb;");
- src.push(" return envMapColor;");
- src.push("}");
- }
+ // SPECULAR BRDF EVALUATION
- // SPECULAR BRDF EVALUATION
+ src.push("vec3 F_Schlick(const in vec3 specularColor, const in float dotLH) {");
+ src.push(" float fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );");
+ src.push(" return ( 1.0 - specularColor ) * fresnel + specularColor;");
+ src.push("}");
- src.push("vec3 F_Schlick(const in vec3 specularColor, const in float dotLH) {");
- src.push(" float fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );");
- src.push(" return ( 1.0 - specularColor ) * fresnel + specularColor;");
- src.push("}");
+ src.push("float G_GGX_Smith(const in float alpha, const in float dotNL, const in float dotNV) {");
+ src.push(" float a2 = ( alpha * alpha );");
+ src.push(" float gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * ( dotNL * dotNL ) );");
+ src.push(" float gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * ( dotNV * dotNV ) );");
+ src.push(" return 1.0 / ( gl * gv );");
+ src.push("}");
- src.push("float G_GGX_Smith(const in float alpha, const in float dotNL, const in float dotNV) {");
- src.push(" float a2 = ( alpha * alpha );");
- src.push(" float gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * ( dotNL * dotNL ) );");
- src.push(" float gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * ( dotNV * dotNV ) );");
- src.push(" return 1.0 / ( gl * gv );");
- src.push("}");
+ src.push("float G_GGX_SmithCorrelated(const in float alpha, const in float dotNL, const in float dotNV) {");
+ src.push(" float a2 = ( alpha * alpha );");
+ src.push(" float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * ( dotNV * dotNV ) );");
+ src.push(" float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * ( dotNL * dotNL ) );");
+ src.push(" return 0.5 / max( gv + gl, EPSILON );");
+ src.push("}");
- src.push("float G_GGX_SmithCorrelated(const in float alpha, const in float dotNL, const in float dotNV) {");
- src.push(" float a2 = ( alpha * alpha );");
- src.push(" float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * ( dotNV * dotNV ) );");
- src.push(" float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * ( dotNL * dotNL ) );");
- src.push(" return 0.5 / max( gv + gl, EPSILON );");
- src.push("}");
+ src.push("float D_GGX(const in float alpha, const in float dotNH) {");
+ src.push(" float a2 = ( alpha * alpha );");
+ src.push(" float denom = ( dotNH * dotNH) * ( a2 - 1.0 ) + 1.0;");
+ src.push(" return RECIPROCAL_PI * a2 / ( denom * denom);");
+ src.push("}");
- src.push("float D_GGX(const in float alpha, const in float dotNH) {");
- src.push(" float a2 = ( alpha * alpha );");
- src.push(" float denom = ( dotNH * dotNH) * ( a2 - 1.0 ) + 1.0;");
- src.push(" return RECIPROCAL_PI * a2 / ( denom * denom);");
- src.push("}");
+ src.push("vec3 BRDF_Specular_GGX(const in IncidentLight incidentLight, const in Geometry geometry, const in vec3 specularColor, const in float roughness) {");
+ src.push(" float alpha = ( roughness * roughness );");
+ src.push(" vec3 halfDir = normalize( incidentLight.direction + geometry.viewEyeDir );");
+ src.push(" float dotNL = saturate( dot( geometry.viewNormal, incidentLight.direction ) );");
+ src.push(" float dotNV = saturate( dot( geometry.viewNormal, geometry.viewEyeDir ) );");
+ src.push(" float dotNH = saturate( dot( geometry.viewNormal, halfDir ) );");
+ src.push(" float dotLH = saturate( dot( incidentLight.direction, halfDir ) );");
+ src.push(" vec3 F = F_Schlick( specularColor, dotLH );");
+ src.push(" float G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );");
+ src.push(" float D = D_GGX( alpha, dotNH );");
+ src.push(" return F * (G * D);");
+ src.push("}");
- src.push("vec3 BRDF_Specular_GGX(const in IncidentLight incidentLight, const in Geometry geometry, const in vec3 specularColor, const in float roughness) {");
- src.push(" float alpha = ( roughness * roughness );");
- src.push(" vec3 halfDir = normalize( incidentLight.direction + geometry.viewEyeDir );");
- src.push(" float dotNL = saturate( dot( geometry.viewNormal, incidentLight.direction ) );");
- src.push(" float dotNV = saturate( dot( geometry.viewNormal, geometry.viewEyeDir ) );");
- src.push(" float dotNH = saturate( dot( geometry.viewNormal, halfDir ) );");
- src.push(" float dotLH = saturate( dot( incidentLight.direction, halfDir ) );");
- src.push(" vec3 F = F_Schlick( specularColor, dotLH );");
- src.push(" float G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );");
- src.push(" float D = D_GGX( alpha, dotNH );");
- src.push(" return F * (G * D);");
- src.push("}");
+ src.push("vec3 BRDF_Specular_GGX_Environment(const in Geometry geometry, const in vec3 specularColor, const in float roughness) {");
+ src.push(" float dotNV = saturate(dot(geometry.viewNormal, geometry.viewEyeDir));");
+ src.push(" const vec4 c0 = vec4( -1, -0.0275, -0.572, 0.022);");
+ src.push(" const vec4 c1 = vec4( 1, 0.0425, 1.04, -0.04);");
+ src.push(" vec4 r = roughness * c0 + c1;");
+ src.push(" float a004 = min(r.x * r.x, exp2(-9.28 * dotNV)) * r.x + r.y;");
+ src.push(" vec2 AB = vec2(-1.04, 1.04) * a004 + r.zw;");
+ src.push(" return specularColor * AB.x + AB.y;");
+ src.push("}");
- src.push("vec3 BRDF_Specular_GGX_Environment(const in Geometry geometry, const in vec3 specularColor, const in float roughness) {");
- src.push(" float dotNV = saturate(dot(geometry.viewNormal, geometry.viewEyeDir));");
- src.push(" const vec4 c0 = vec4( -1, -0.0275, -0.572, 0.022);");
- src.push(" const vec4 c1 = vec4( 1, 0.0425, 1.04, -0.04);");
- src.push(" vec4 r = roughness * c0 + c1;");
- src.push(" float a004 = min(r.x * r.x, exp2(-9.28 * dotNV)) * r.x + r.y;");
- src.push(" vec2 AB = vec2(-1.04, 1.04) * a004 + r.zw;");
- src.push(" return specularColor * AB.x + AB.y;");
- src.push("}");
+ if (lightsState.lightMaps.length > 0 || lightsState.reflectionMaps.length > 0) {
- if (lightsState.lightMaps.length > 0 || lightsState.reflectionMaps.length > 0) {
-
- src.push("void computePBRLightMapping(const in Geometry geometry, const in Material material, inout ReflectedLight reflectedLight) {");
- if (lightsState.lightMaps.length > 0) {
- src.push(" vec3 irradiance = sRGBToLinear(textureCube(lightMap, geometry.worldNormal)).rgb;");
- src.push(" irradiance *= PI;");
- src.push(" vec3 diffuseBRDFContrib = (RECIPROCAL_PI * material.diffuseColor);");
- src.push(" reflectedLight.diffuse += irradiance * diffuseBRDFContrib;");
- // src.push(" reflectedLight.diffuse = vec3(1.0, 0.0, 0.0);");
- }
- if (lightsState.reflectionMaps.length > 0) {
- src.push(" vec3 reflectVec = reflect(-geometry.viewEyeDir, geometry.viewNormal);");
- src.push(" reflectVec = inverseTransformDirection(reflectVec, viewMatrix);");
- src.push(" float blinnExpFromRoughness = GGXRoughnessToBlinnExponent(material.specularRoughness);");
- src.push(" vec3 radiance = getLightProbeIndirectRadiance(reflectVec, blinnExpFromRoughness, 8);");
- src.push(" vec3 specularBRDFContrib = BRDF_Specular_GGX_Environment(geometry, material.specularColor, material.specularRoughness);");
- src.push(" reflectedLight.specular += radiance * specularBRDFContrib;");
- }
- src.push("}");
+ src.push("void computePBRLightMapping(const in Geometry geometry, const in Material material, inout ReflectedLight reflectedLight) {");
+ if (lightsState.lightMaps.length > 0) {
+ src.push(" vec3 irradiance = sRGBToLinear(textureCube(lightMap, geometry.worldNormal)).rgb;");
+ src.push(" irradiance *= PI;");
+ src.push(" vec3 diffuseBRDFContrib = (RECIPROCAL_PI * material.diffuseColor);");
+ src.push(" reflectedLight.diffuse += irradiance * diffuseBRDFContrib;");
+ // src.push(" reflectedLight.diffuse = vec3(1.0, 0.0, 0.0);");
}
+ if (lightsState.reflectionMaps.length > 0) {
+ src.push(" vec3 reflectVec = reflect(-geometry.viewEyeDir, geometry.viewNormal);");
+ src.push(" reflectVec = inverseTransformDirection(reflectVec, viewMatrix);");
+ src.push(" float blinnExpFromRoughness = GGXRoughnessToBlinnExponent(material.specularRoughness);");
+ src.push(" vec3 radiance = getLightProbeIndirectRadiance(reflectVec, blinnExpFromRoughness, 8);");
+ src.push(" vec3 specularBRDFContrib = BRDF_Specular_GGX_Environment(geometry, material.specularColor, material.specularRoughness);");
+ src.push(" reflectedLight.specular += radiance * specularBRDFContrib;");
+ }
+ src.push("}");
+ }
- // MAIN LIGHTING COMPUTATION FUNCTION
+ // MAIN LIGHTING COMPUTATION FUNCTION
- src.push("void computePBRLighting(const in IncidentLight incidentLight, const in Geometry geometry, const in Material material, inout ReflectedLight reflectedLight) {");
- src.push(" float dotNL = saturate(dot(geometry.viewNormal, incidentLight.direction));");
- src.push(" vec3 irradiance = dotNL * incidentLight.color * PI;");
- src.push(" reflectedLight.diffuse += irradiance * (RECIPROCAL_PI * material.diffuseColor);");
- src.push(" reflectedLight.specular += irradiance * BRDF_Specular_GGX(incidentLight, geometry, material.specularColor, material.specularRoughness);");
- src.push("}");
+ src.push("void computePBRLighting(const in IncidentLight incidentLight, const in Geometry geometry, const in Material material, inout ReflectedLight reflectedLight) {");
+ src.push(" float dotNL = saturate(dot(geometry.viewNormal, incidentLight.direction));");
+ src.push(" vec3 irradiance = dotNL * incidentLight.color * PI;");
+ src.push(" reflectedLight.diffuse += irradiance * (RECIPROCAL_PI * material.diffuseColor);");
+ src.push(" reflectedLight.specular += irradiance * BRDF_Specular_GGX(incidentLight, geometry, material.specularColor, material.specularRoughness);");
+ src.push("}");
- } // (metallicMaterial || specularMaterial)
+ } // (metallicMaterial || specularMaterial)
- } // geometry.normals
+ } // geometry.normals
- //--------------------------------------------------------------------------------
- // GEOMETRY INPUTS
- //--------------------------------------------------------------------------------
+ //--------------------------------------------------------------------------------
+ // GEOMETRY INPUTS
+ //--------------------------------------------------------------------------------
- src.push("varying vec3 vViewPosition;");
+ src.push("varying vec3 vViewPosition;");
- if (geometryState.colors) {
- src.push("varying vec4 vColor;");
- }
+ if (geometryState.colors) {
+ src.push("varying vec4 vColor;");
+ }
- if (uvs &&
- ((normals && material._normalMap)
- || material._ambientMap
- || material._baseColorMap
- || material._diffuseMap
- || material._emissiveMap
- || material._metallicMap
- || material._roughnessMap
- || material._metallicRoughnessMap
- || material._specularMap
- || material._glossinessMap
- || material._specularGlossinessMap
- || material._occlusionMap
- || material._alphaMap)) {
- src.push("varying vec2 vUV;");
- }
+ if (uvs &&
+ ((normals && material._normalMap)
+ || material._ambientMap
+ || material._baseColorMap
+ || material._diffuseMap
+ || material._emissiveMap
+ || material._metallicMap
+ || material._roughnessMap
+ || material._metallicRoughnessMap
+ || material._specularMap
+ || material._glossinessMap
+ || material._specularGlossinessMap
+ || material._occlusionMap
+ || material._alphaMap)) {
+ src.push("varying vec2 vUV;");
+ }
- if (normals) {
- if (lightsState.lightMaps.length > 0) {
- src.push("varying vec3 vWorldNormal;");
- }
- src.push("varying vec3 vViewNormal;");
+ if (normals) {
+ if (lightsState.lightMaps.length > 0) {
+ src.push("varying vec3 vWorldNormal;");
}
+ src.push("varying vec3 vViewNormal;");
+ }
- //--------------------------------------------------------------------------------
- // MATERIAL CHANNEL INPUTS
- //--------------------------------------------------------------------------------
+ //--------------------------------------------------------------------------------
+ // MATERIAL CHANNEL INPUTS
+ //--------------------------------------------------------------------------------
- if (materialState.ambient) {
- src.push("uniform vec3 materialAmbient;");
- }
- if (materialState.baseColor) {
- src.push("uniform vec3 materialBaseColor;");
- }
- if (materialState.alpha !== undefined && materialState.alpha !== null) {
- src.push("uniform vec4 materialAlphaModeCutoff;"); // [alpha, alphaMode, alphaCutoff]
- }
- if (materialState.emissive) {
- src.push("uniform vec3 materialEmissive;");
- }
- if (materialState.diffuse) {
- src.push("uniform vec3 materialDiffuse;");
- }
- if (materialState.glossiness !== undefined && materialState.glossiness !== null) {
- src.push("uniform float materialGlossiness;");
- }
- if (materialState.shininess !== undefined && materialState.shininess !== null) {
- src.push("uniform float materialShininess;"); // Phong channel
- }
- if (materialState.specular) {
- src.push("uniform vec3 materialSpecular;");
- }
- if (materialState.metallic !== undefined && materialState.metallic !== null) {
- src.push("uniform float materialMetallic;");
- }
- if (materialState.roughness !== undefined && materialState.roughness !== null) {
- src.push("uniform float materialRoughness;");
- }
- if (materialState.specularF0 !== undefined && materialState.specularF0 !== null) {
- src.push("uniform float materialSpecularF0;");
- }
+ if (materialState.ambient) {
+ src.push("uniform vec3 materialAmbient;");
+ }
+ if (materialState.baseColor) {
+ src.push("uniform vec3 materialBaseColor;");
+ }
+ if (materialState.alpha !== undefined && materialState.alpha !== null) {
+ src.push("uniform vec4 materialAlphaModeCutoff;"); // [alpha, alphaMode, alphaCutoff]
+ }
+ if (materialState.emissive) {
+ src.push("uniform vec3 materialEmissive;");
+ }
+ if (materialState.diffuse) {
+ src.push("uniform vec3 materialDiffuse;");
+ }
+ if (materialState.glossiness !== undefined && materialState.glossiness !== null) {
+ src.push("uniform float materialGlossiness;");
+ }
+ if (materialState.shininess !== undefined && materialState.shininess !== null) {
+ src.push("uniform float materialShininess;"); // Phong channel
+ }
+ if (materialState.specular) {
+ src.push("uniform vec3 materialSpecular;");
+ }
+ if (materialState.metallic !== undefined && materialState.metallic !== null) {
+ src.push("uniform float materialMetallic;");
+ }
+ if (materialState.roughness !== undefined && materialState.roughness !== null) {
+ src.push("uniform float materialRoughness;");
+ }
+ if (materialState.specularF0 !== undefined && materialState.specularF0 !== null) {
+ src.push("uniform float materialSpecularF0;");
+ }
- //--------------------------------------------------------------------------------
- // MATERIAL TEXTURE INPUTS
- //--------------------------------------------------------------------------------
+ //--------------------------------------------------------------------------------
+ // MATERIAL TEXTURE INPUTS
+ //--------------------------------------------------------------------------------
- if (uvs && material._ambientMap) {
- src.push("uniform sampler2D ambientMap;");
- if (material._ambientMap._state.matrix) {
- src.push("uniform mat4 ambientMapMatrix;");
- }
+ if (uvs && material._ambientMap) {
+ src.push("uniform sampler2D ambientMap;");
+ if (material._ambientMap._state.matrix) {
+ src.push("uniform mat4 ambientMapMatrix;");
}
- if (uvs && material._baseColorMap) {
- src.push("uniform sampler2D baseColorMap;");
- if (material._baseColorMap._state.matrix) {
- src.push("uniform mat4 baseColorMapMatrix;");
- }
+ }
+ if (uvs && material._baseColorMap) {
+ src.push("uniform sampler2D baseColorMap;");
+ if (material._baseColorMap._state.matrix) {
+ src.push("uniform mat4 baseColorMapMatrix;");
}
- if (uvs && material._diffuseMap) {
- src.push("uniform sampler2D diffuseMap;");
- if (material._diffuseMap._state.matrix) {
- src.push("uniform mat4 diffuseMapMatrix;");
- }
+ }
+ if (uvs && material._diffuseMap) {
+ src.push("uniform sampler2D diffuseMap;");
+ if (material._diffuseMap._state.matrix) {
+ src.push("uniform mat4 diffuseMapMatrix;");
}
- if (uvs && material._emissiveMap) {
- src.push("uniform sampler2D emissiveMap;");
- if (material._emissiveMap._state.matrix) {
- src.push("uniform mat4 emissiveMapMatrix;");
- }
+ }
+ if (uvs && material._emissiveMap) {
+ src.push("uniform sampler2D emissiveMap;");
+ if (material._emissiveMap._state.matrix) {
+ src.push("uniform mat4 emissiveMapMatrix;");
}
- if (normals && uvs && material._metallicMap) {
- src.push("uniform sampler2D metallicMap;");
- if (material._metallicMap._state.matrix) {
- src.push("uniform mat4 metallicMapMatrix;");
- }
+ }
+ if (normals && uvs && material._metallicMap) {
+ src.push("uniform sampler2D metallicMap;");
+ if (material._metallicMap._state.matrix) {
+ src.push("uniform mat4 metallicMapMatrix;");
}
- if (normals && uvs && material._roughnessMap) {
- src.push("uniform sampler2D roughnessMap;");
- if (material._roughnessMap._state.matrix) {
- src.push("uniform mat4 roughnessMapMatrix;");
- }
+ }
+ if (normals && uvs && material._roughnessMap) {
+ src.push("uniform sampler2D roughnessMap;");
+ if (material._roughnessMap._state.matrix) {
+ src.push("uniform mat4 roughnessMapMatrix;");
}
- if (normals && uvs && material._metallicRoughnessMap) {
- src.push("uniform sampler2D metallicRoughnessMap;");
- if (material._metallicRoughnessMap._state.matrix) {
- src.push("uniform mat4 metallicRoughnessMapMatrix;");
- }
+ }
+ if (normals && uvs && material._metallicRoughnessMap) {
+ src.push("uniform sampler2D metallicRoughnessMap;");
+ if (material._metallicRoughnessMap._state.matrix) {
+ src.push("uniform mat4 metallicRoughnessMapMatrix;");
}
- if (normals && material._normalMap) {
- src.push("uniform sampler2D normalMap;");
- if (material._normalMap._state.matrix) {
- src.push("uniform mat4 normalMapMatrix;");
- }
- src.push("vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec2 uv ) {");
- src.push(" vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );");
- src.push(" vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );");
- src.push(" vec2 st0 = dFdx( uv.st );");
- src.push(" vec2 st1 = dFdy( uv.st );");
- src.push(" vec3 S = normalize( q0 * st1.t - q1 * st0.t );");
- src.push(" vec3 T = normalize( -q0 * st1.s + q1 * st0.s );");
- src.push(" vec3 N = normalize( surf_norm );");
- src.push(" vec3 mapN = texture2D( normalMap, uv ).xyz * 2.0 - 1.0;");
- src.push(" mat3 tsn = mat3( S, T, N );");
- // src.push(" mapN *= 3.0;");
- src.push(" return normalize( tsn * mapN );");
- src.push("}");
+ }
+ if (normals && material._normalMap) {
+ src.push("uniform sampler2D normalMap;");
+ if (material._normalMap._state.matrix) {
+ src.push("uniform mat4 normalMapMatrix;");
+ }
+ src.push("vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec2 uv ) {");
+ src.push(" vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );");
+ src.push(" vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );");
+ src.push(" vec2 st0 = dFdx( uv.st );");
+ src.push(" vec2 st1 = dFdy( uv.st );");
+ src.push(" vec3 S = normalize( q0 * st1.t - q1 * st0.t );");
+ src.push(" vec3 T = normalize( -q0 * st1.s + q1 * st0.s );");
+ src.push(" vec3 N = normalize( surf_norm );");
+ src.push(" vec3 mapN = texture2D( normalMap, uv ).xyz * 2.0 - 1.0;");
+ src.push(" mat3 tsn = mat3( S, T, N );");
+ // src.push(" mapN *= 3.0;");
+ src.push(" return normalize( tsn * mapN );");
+ src.push("}");
+ }
+ if (uvs && material._occlusionMap) {
+ src.push("uniform sampler2D occlusionMap;");
+ if (material._occlusionMap._state.matrix) {
+ src.push("uniform mat4 occlusionMapMatrix;");
}
- if (uvs && material._occlusionMap) {
- src.push("uniform sampler2D occlusionMap;");
- if (material._occlusionMap._state.matrix) {
- src.push("uniform mat4 occlusionMapMatrix;");
- }
+ }
+ if (uvs && material._alphaMap) {
+ src.push("uniform sampler2D alphaMap;");
+ if (material._alphaMap._state.matrix) {
+ src.push("uniform mat4 alphaMapMatrix;");
}
- if (uvs && material._alphaMap) {
- src.push("uniform sampler2D alphaMap;");
- if (material._alphaMap._state.matrix) {
- src.push("uniform mat4 alphaMapMatrix;");
- }
+ }
+ if (normals && uvs && material._specularMap) {
+ src.push("uniform sampler2D specularMap;");
+ if (material._specularMap._state.matrix) {
+ src.push("uniform mat4 specularMapMatrix;");
}
- if (normals && uvs && material._specularMap) {
- src.push("uniform sampler2D specularMap;");
- if (material._specularMap._state.matrix) {
- src.push("uniform mat4 specularMapMatrix;");
- }
+ }
+ if (normals && uvs && material._glossinessMap) {
+ src.push("uniform sampler2D glossinessMap;");
+ if (material._glossinessMap._state.matrix) {
+ src.push("uniform mat4 glossinessMapMatrix;");
}
- if (normals && uvs && material._glossinessMap) {
- src.push("uniform sampler2D glossinessMap;");
- if (material._glossinessMap._state.matrix) {
- src.push("uniform mat4 glossinessMapMatrix;");
- }
+ }
+ if (normals && uvs && material._specularGlossinessMap) {
+ src.push("uniform sampler2D materialSpecularGlossinessMap;");
+ if (material._specularGlossinessMap._state.matrix) {
+ src.push("uniform mat4 materialSpecularGlossinessMapMatrix;");
}
- if (normals && uvs && material._specularGlossinessMap) {
- src.push("uniform sampler2D materialSpecularGlossinessMap;");
- if (material._specularGlossinessMap._state.matrix) {
- src.push("uniform mat4 materialSpecularGlossinessMapMatrix;");
- }
+ }
+
+ //--------------------------------------------------------------------------------
+ // MATERIAL FRESNEL INPUTS
+ //--------------------------------------------------------------------------------
+
+ if (normals && (material._diffuseFresnel ||
+ material._specularFresnel ||
+ material._alphaFresnel ||
+ material._emissiveFresnel ||
+ material._reflectivityFresnel)) {
+ src.push("float fresnel(vec3 eyeDir, vec3 normal, float edgeBias, float centerBias, float power) {");
+ src.push(" float fr = abs(dot(eyeDir, normal));");
+ src.push(" float finalFr = clamp((fr - edgeBias) / (centerBias - edgeBias), 0.0, 1.0);");
+ src.push(" return pow(finalFr, power);");
+ src.push("}");
+ if (material._diffuseFresnel) {
+ src.push("uniform float diffuseFresnelCenterBias;");
+ src.push("uniform float diffuseFresnelEdgeBias;");
+ src.push("uniform float diffuseFresnelPower;");
+ src.push("uniform vec3 diffuseFresnelCenterColor;");
+ src.push("uniform vec3 diffuseFresnelEdgeColor;");
+ }
+ if (material._specularFresnel) {
+ src.push("uniform float specularFresnelCenterBias;");
+ src.push("uniform float specularFresnelEdgeBias;");
+ src.push("uniform float specularFresnelPower;");
+ src.push("uniform vec3 specularFresnelCenterColor;");
+ src.push("uniform vec3 specularFresnelEdgeColor;");
+ }
+ if (material._alphaFresnel) {
+ src.push("uniform float alphaFresnelCenterBias;");
+ src.push("uniform float alphaFresnelEdgeBias;");
+ src.push("uniform float alphaFresnelPower;");
+ src.push("uniform vec3 alphaFresnelCenterColor;");
+ src.push("uniform vec3 alphaFresnelEdgeColor;");
+ }
+ if (material._reflectivityFresnel) {
+ src.push("uniform float materialSpecularF0FresnelCenterBias;");
+ src.push("uniform float materialSpecularF0FresnelEdgeBias;");
+ src.push("uniform float materialSpecularF0FresnelPower;");
+ src.push("uniform vec3 materialSpecularF0FresnelCenterColor;");
+ src.push("uniform vec3 materialSpecularF0FresnelEdgeColor;");
+ }
+ if (material._emissiveFresnel) {
+ src.push("uniform float emissiveFresnelCenterBias;");
+ src.push("uniform float emissiveFresnelEdgeBias;");
+ src.push("uniform float emissiveFresnelPower;");
+ src.push("uniform vec3 emissiveFresnelCenterColor;");
+ src.push("uniform vec3 emissiveFresnelEdgeColor;");
}
+ }
- //--------------------------------------------------------------------------------
- // MATERIAL FRESNEL INPUTS
- //--------------------------------------------------------------------------------
+ //--------------------------------------------------------------------------------
+ // LIGHT SOURCES
+ //--------------------------------------------------------------------------------
- if (normals && (material._diffuseFresnel ||
- material._specularFresnel ||
- material._alphaFresnel ||
- material._emissiveFresnel ||
- material._reflectivityFresnel)) {
- src.push("float fresnel(vec3 eyeDir, vec3 normal, float edgeBias, float centerBias, float power) {");
- src.push(" float fr = abs(dot(eyeDir, normal));");
- src.push(" float finalFr = clamp((fr - edgeBias) / (centerBias - edgeBias), 0.0, 1.0);");
- src.push(" return pow(finalFr, power);");
- src.push("}");
- if (material._diffuseFresnel) {
- src.push("uniform float diffuseFresnelCenterBias;");
- src.push("uniform float diffuseFresnelEdgeBias;");
- src.push("uniform float diffuseFresnelPower;");
- src.push("uniform vec3 diffuseFresnelCenterColor;");
- src.push("uniform vec3 diffuseFresnelEdgeColor;");
- }
- if (material._specularFresnel) {
- src.push("uniform float specularFresnelCenterBias;");
- src.push("uniform float specularFresnelEdgeBias;");
- src.push("uniform float specularFresnelPower;");
- src.push("uniform vec3 specularFresnelCenterColor;");
- src.push("uniform vec3 specularFresnelEdgeColor;");
+ src.push("uniform vec4 lightAmbient;");
+
+ if (normals) {
+ for (i = 0, len = lightsState.lights.length; i < len; i++) { // Light sources
+ light = lightsState.lights[i];
+ if (light.type === "ambient") {
+ continue;
}
- if (material._alphaFresnel) {
- src.push("uniform float alphaFresnelCenterBias;");
- src.push("uniform float alphaFresnelEdgeBias;");
- src.push("uniform float alphaFresnelPower;");
- src.push("uniform vec3 alphaFresnelCenterColor;");
- src.push("uniform vec3 alphaFresnelEdgeColor;");
+ src.push("uniform vec4 lightColor" + i + ";");
+ if (light.type === "point") {
+ src.push("uniform vec3 lightAttenuation" + i + ";");
}
- if (material._reflectivityFresnel) {
- src.push("uniform float materialSpecularF0FresnelCenterBias;");
- src.push("uniform float materialSpecularF0FresnelEdgeBias;");
- src.push("uniform float materialSpecularF0FresnelPower;");
- src.push("uniform vec3 materialSpecularF0FresnelCenterColor;");
- src.push("uniform vec3 materialSpecularF0FresnelEdgeColor;");
+ if (light.type === "dir" && light.space === "view") {
+ src.push("uniform vec3 lightDir" + i + ";");
}
- if (material._emissiveFresnel) {
- src.push("uniform float emissiveFresnelCenterBias;");
- src.push("uniform float emissiveFresnelEdgeBias;");
- src.push("uniform float emissiveFresnelPower;");
- src.push("uniform vec3 emissiveFresnelCenterColor;");
- src.push("uniform vec3 emissiveFresnelEdgeColor;");
+ if (light.type === "point" && light.space === "view") {
+ src.push("uniform vec3 lightPos" + i + ";");
+ } else {
+ src.push("varying vec4 vViewLightReverseDirAndDist" + i + ";");
}
}
+ }
- //--------------------------------------------------------------------------------
- // LIGHT SOURCES
- //--------------------------------------------------------------------------------
+ if (receiveShadow) {
- src.push("uniform vec4 lightAmbient;");
+ // Variance shadow mapping filter
- if (normals) {
- for (i = 0, len = lightsState.lights.length; i < len; i++) { // Light sources
- light = lightsState.lights[i];
- if (light.type === "ambient") {
- continue;
- }
- src.push("uniform vec4 lightColor" + i + ";");
- if (light.type === "point") {
- src.push("uniform vec3 lightAttenuation" + i + ";");
- }
- if (light.type === "dir" && light.space === "view") {
- src.push("uniform vec3 lightDir" + i + ";");
- }
- if (light.type === "point" && light.space === "view") {
- src.push("uniform vec3 lightPos" + i + ";");
- } else {
- src.push("varying vec4 vViewLightReverseDirAndDist" + i + ";");
- }
+ // src.push("float linstep(float low, float high, float v){");
+ // src.push(" return clamp((v-low)/(high-low), 0.0, 1.0);");
+ // src.push("}");
+ //
+ // src.push("float VSM(sampler2D depths, vec2 uv, float compare){");
+ // src.push(" vec2 moments = texture2D(depths, uv).xy;");
+ // src.push(" float p = smoothstep(compare-0.02, compare, moments.x);");
+ // src.push(" float variance = max(moments.y - moments.x*moments.x, -0.001);");
+ // src.push(" float d = compare - moments.x;");
+ // src.push(" float p_max = linstep(0.2, 1.0, variance / (variance + d*d));");
+ // src.push(" return clamp(max(p, p_max), 0.0, 1.0);");
+ // src.push("}");
+
+ for (i = 0, len = lightsState.lights.length; i < len; i++) { // Light sources
+ if (lightsState.lights[i].shadow) {
+ src.push("varying vec4 vShadowPosFromLight" + i + ";");
+ src.push("uniform sampler2D shadowMap" + i + ";");
}
}
+ }
- if (receiveShadow) {
+ src.push("uniform vec4 colorize;");
- // Variance shadow mapping filter
+ //================================================================================
+ // MAIN
+ //================================================================================
- // src.push("float linstep(float low, float high, float v){");
- // src.push(" return clamp((v-low)/(high-low), 0.0, 1.0);");
- // src.push("}");
- //
- // src.push("float VSM(sampler2D depths, vec2 uv, float compare){");
- // src.push(" vec2 moments = texture2D(depths, uv).xy;");
- // src.push(" float p = smoothstep(compare-0.02, compare, moments.x);");
- // src.push(" float variance = max(moments.y - moments.x*moments.x, -0.001);");
- // src.push(" float d = compare - moments.x;");
- // src.push(" float p_max = linstep(0.2, 1.0, variance / (variance + d*d));");
- // src.push(" return clamp(max(p, p_max), 0.0, 1.0);");
- // src.push("}");
+ src.push("void main(void) {");
- for (i = 0, len = lightsState.lights.length; i < len; i++) { // Light sources
- if (lightsState.lights[i].shadow) {
- src.push("varying vec4 vShadowPosFromLight" + i + ";");
- src.push("uniform sampler2D shadowMap" + i + ";");
- }
- }
+ if (clipping) {
+ src.push("if (clippable) {");
+ src.push(" float dist = 0.0;");
+ for (var i = 0; i < clipsState.clips.length; i++) {
+ src.push("if (clipActive" + i + ") {");
+ src.push(" dist += clamp(dot(-clipDir" + i + ".xyz, vWorldPosition.xyz - clipPos" + i + ".xyz), 0.0, 1000.0);");
+ src.push("}");
+ }
+ src.push(" if (dist > 0.0) { discard; }");
+ if (solid) {
+ src.push(" if (gl_FrontFacing == false) {");
+ src.push(" gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);");
+ src.push(" return;");
+ src.push(" }");
}
+ src.push("}");
+ }
- src.push("uniform vec4 colorize;");
+ if (geometryState.primitiveName === "points") {
+ src.push("vec2 cxy = 2.0 * gl_PointCoord - 1.0;");
+ src.push("float r = dot(cxy, cxy);");
+ src.push("if (r > 1.0) {");
+ src.push(" discard;");
+ src.push("}");
+ }
- //================================================================================
- // MAIN
- //================================================================================
+ src.push("float occlusion = 1.0;");
- src.push("void main(void) {");
+ if (materialState.ambient) {
+ src.push("vec3 ambientColor = materialAmbient;");
+ } else {
+ src.push("vec3 ambientColor = vec3(1.0, 1.0, 1.0);");
+ }
- if (clipping) {
- src.push("if (clippable) {");
- src.push(" float dist = 0.0;");
- for (var i = 0; i < clipsState.clips.length; i++) {
- src.push("if (clipActive" + i + ") {");
- src.push(" dist += clamp(dot(-clipDir" + i + ".xyz, vWorldPosition.xyz - clipPos" + i + ".xyz), 0.0, 1000.0);");
- src.push("}");
- }
- src.push(" if (dist > 0.0) { discard; }");
- if (solid) {
- src.push(" if (gl_FrontFacing == false) {");
- src.push(" gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);");
- src.push(" return;");
- src.push(" }");
- }
- src.push("}");
- }
+ if (materialState.diffuse) {
+ src.push("vec3 diffuseColor = materialDiffuse;");
+ } else if (materialState.baseColor) {
+ src.push("vec3 diffuseColor = materialBaseColor;");
+ } else {
+ src.push("vec3 diffuseColor = vec3(1.0, 1.0, 1.0);");
+ }
- if (geometryState.primitiveName === "points") {
- src.push("vec2 cxy = 2.0 * gl_PointCoord - 1.0;");
- src.push("float r = dot(cxy, cxy);");
- src.push("if (r > 1.0) {");
- src.push(" discard;");
- src.push("}");
- }
+ if (geometryState.colors) {
+ src.push("diffuseColor *= vColor.rgb;");
+ }
- src.push("float occlusion = 1.0;");
+ if (materialState.emissive) {
+ src.push("vec3 emissiveColor = materialEmissive;"); // Emissive default is (0,0,0), so initializing here
+ } else {
+ src.push("vec3 emissiveColor = vec3(0.0, 0.0, 0.0);");
+ }
- if (materialState.ambient) {
- src.push("vec3 ambientColor = materialAmbient;");
- } else {
- src.push("vec3 ambientColor = vec3(1.0, 1.0, 1.0);");
- }
+ if (materialState.specular) {
+ src.push("vec3 specular = materialSpecular;");
+ } else {
+ src.push("vec3 specular = vec3(1.0, 1.0, 1.0);");
+ }
- if (materialState.diffuse) {
- src.push("vec3 diffuseColor = materialDiffuse;");
- } else if (materialState.baseColor) {
- src.push("vec3 diffuseColor = materialBaseColor;");
- } else {
- src.push("vec3 diffuseColor = vec3(1.0, 1.0, 1.0);");
- }
+ if (materialState.alpha !== undefined) {
+ src.push("float alpha = materialAlphaModeCutoff[0];");
+ } else {
+ src.push("float alpha = 1.0;");
+ }
- if (geometryState.colors) {
- src.push("diffuseColor *= vColor.rgb;");
- }
+ if (geometryState.colors) {
+ src.push("alpha *= vColor.a;");
+ }
- if (materialState.emissive) {
- src.push("vec3 emissiveColor = materialEmissive;"); // Emissive default is (0,0,0), so initializing here
- } else {
- src.push("vec3 emissiveColor = vec3(0.0, 0.0, 0.0);");
- }
+ if (materialState.glossiness !== undefined) {
+ src.push("float glossiness = materialGlossiness;");
+ } else {
+ src.push("float glossiness = 1.0;");
+ }
- if (materialState.specular) {
- src.push("vec3 specular = materialSpecular;");
- } else {
- src.push("vec3 specular = vec3(1.0, 1.0, 1.0);");
- }
+ if (materialState.metallic !== undefined) {
+ src.push("float metallic = materialMetallic;");
+ } else {
+ src.push("float metallic = 1.0;");
+ }
+
+ if (materialState.roughness !== undefined) {
+ src.push("float roughness = materialRoughness;");
+ } else {
+ src.push("float roughness = 1.0;");
+ }
+
+ if (materialState.specularF0 !== undefined) {
+ src.push("float specularF0 = materialSpecularF0;");
+ } else {
+ src.push("float specularF0 = 1.0;");
+ }
+
+ //--------------------------------------------------------------------------------
+ // TEXTURING
+ //--------------------------------------------------------------------------------
+
+ if (uvs && ((normals && material._normalMap)
+ || material._ambientMap
+ || material._baseColorMap
+ || material._diffuseMap
+ || material._occlusionMap
+ || material._emissiveMap
+ || material._metallicMap
+ || material._roughnessMap
+ || material._metallicRoughnessMap
+ || material._specularMap
+ || material._glossinessMap
+ || material._specularGlossinessMap
+ || material._alphaMap)) {
+ src.push("vec4 texturePos = vec4(vUV.s, vUV.t, 1.0, 1.0);");
+ src.push("vec2 textureCoord;");
+ }
- if (materialState.alpha !== undefined) {
- src.push("float alpha = materialAlphaModeCutoff[0];");
+ if (uvs && material._ambientMap) {
+ if (material._ambientMap._state.matrix) {
+ src.push("textureCoord = (ambientMapMatrix * texturePos).xy;");
} else {
- src.push("float alpha = 1.0;");
+ src.push("textureCoord = texturePos.xy;");
}
+ src.push("vec4 ambientTexel = texture2D(ambientMap, textureCoord).rgb;");
+ src.push("ambientTexel = " + TEXTURE_DECODE_FUNCS[material._ambientMap._state.encoding] + "(ambientTexel);");
+ src.push("ambientColor *= ambientTexel.rgb;");
+ }
- if (geometryState.colors) {
- src.push("alpha *= vColor.a;");
+ if (uvs && material._diffuseMap) {
+ if (material._diffuseMap._state.matrix) {
+ src.push("textureCoord = (diffuseMapMatrix * texturePos).xy;");
+ } else {
+ src.push("textureCoord = texturePos.xy;");
}
+ src.push("vec4 diffuseTexel = texture2D(diffuseMap, textureCoord);");
+ src.push("diffuseTexel = " + TEXTURE_DECODE_FUNCS[material._diffuseMap._state.encoding] + "(diffuseTexel);");
+ src.push("diffuseColor *= diffuseTexel.rgb;");
+ src.push("alpha *= diffuseTexel.a;");
+ }
- if (materialState.glossiness !== undefined) {
- src.push("float glossiness = materialGlossiness;");
+ if (uvs && material._baseColorMap) {
+ if (material._baseColorMap._state.matrix) {
+ src.push("textureCoord = (baseColorMapMatrix * texturePos).xy;");
} else {
- src.push("float glossiness = 1.0;");
+ src.push("textureCoord = texturePos.xy;");
}
+ src.push("vec4 baseColorTexel = texture2D(baseColorMap, textureCoord);");
+ src.push("baseColorTexel = " + TEXTURE_DECODE_FUNCS[material._baseColorMap._state.encoding] + "(baseColorTexel);");
+ src.push("diffuseColor *= baseColorTexel.rgb;");
+ src.push("alpha *= baseColorTexel.a;");
+ }
- if (materialState.metallic !== undefined) {
- src.push("float metallic = materialMetallic;");
+ if (uvs && material._emissiveMap) {
+ if (material._emissiveMap._state.matrix) {
+ src.push("textureCoord = (emissiveMapMatrix * texturePos).xy;");
} else {
- src.push("float metallic = 1.0;");
+ src.push("textureCoord = texturePos.xy;");
}
+ src.push("vec4 emissiveTexel = texture2D(emissiveMap, textureCoord);");
+ src.push("emissiveTexel = " + TEXTURE_DECODE_FUNCS[material._emissiveMap._state.encoding] + "(emissiveTexel);");
+ src.push("emissiveColor *= emissiveTexel.rgb;");
+ }
- if (materialState.roughness !== undefined) {
- src.push("float roughness = materialRoughness;");
+ if (uvs && material._alphaMap) {
+ if (material._alphaMap._state.matrix) {
+ src.push("textureCoord = (alphaMapMatrix * texturePos).xy;");
} else {
- src.push("float roughness = 1.0;");
+ src.push("textureCoord = texturePos.xy;");
}
+ src.push("alpha *= texture2D(alphaMap, textureCoord).r;");
+ }
- if (materialState.specularF0 !== undefined) {
- src.push("float specularF0 = materialSpecularF0;");
+ if (uvs && material._occlusionMap) {
+ if (material._occlusionMap._state.matrix) {
+ src.push("textureCoord = (occlusionMapMatrix * texturePos).xy;");
} else {
- src.push("float specularF0 = 1.0;");
+ src.push("textureCoord = texturePos.xy;");
}
+ src.push("occlusion *= texture2D(occlusionMap, textureCoord).r;");
+ }
+
+ if (normals && ((lightsState.lights.length > 0) || lightsState.lightMaps.length > 0 || lightsState.reflectionMaps.length > 0)) {
//--------------------------------------------------------------------------------
- // TEXTURING
+ // SHADING
//--------------------------------------------------------------------------------
- if (uvs && ((normals && material._normalMap)
- || material._ambientMap
- || material._baseColorMap
- || material._diffuseMap
- || material._occlusionMap
- || material._emissiveMap
- || material._metallicMap
- || material._roughnessMap
- || material._metallicRoughnessMap
- || material._specularMap
- || material._glossinessMap
- || material._specularGlossinessMap
- || material._alphaMap)) {
- src.push("vec4 texturePos = vec4(vUV.s, vUV.t, 1.0, 1.0);");
- src.push("vec2 textureCoord;");
- }
-
- if (uvs && material._ambientMap) {
- if (material._ambientMap._state.matrix) {
- src.push("textureCoord = (ambientMapMatrix * texturePos).xy;");
+ if (uvs && material._normalMap) {
+ if (material._normalMap._state.matrix) {
+ src.push("textureCoord = (normalMapMatrix * texturePos).xy;");
} else {
src.push("textureCoord = texturePos.xy;");
}
- src.push("vec4 ambientTexel = texture2D(ambientMap, textureCoord).rgb;");
- src.push("ambientTexel = " + TEXTURE_DECODE_FUNCS[material._ambientMap._state.encoding] + "(ambientTexel);");
- src.push("ambientColor *= ambientTexel.rgb;");
+ src.push("vec3 viewNormal = perturbNormal2Arb( vViewPosition, normalize(vViewNormal), textureCoord );");
+ } else {
+ src.push("vec3 viewNormal = normalize(vViewNormal);");
}
- if (uvs && material._diffuseMap) {
- if (material._diffuseMap._state.matrix) {
- src.push("textureCoord = (diffuseMapMatrix * texturePos).xy;");
+ if (uvs && material._specularMap) {
+ if (material._specularMap._state.matrix) {
+ src.push("textureCoord = (specularMapMatrix * texturePos).xy;");
} else {
src.push("textureCoord = texturePos.xy;");
}
- src.push("vec4 diffuseTexel = texture2D(diffuseMap, textureCoord);");
- src.push("diffuseTexel = " + TEXTURE_DECODE_FUNCS[material._diffuseMap._state.encoding] + "(diffuseTexel);");
- src.push("diffuseColor *= diffuseTexel.rgb;");
- src.push("alpha *= diffuseTexel.a;");
+ src.push("specular *= texture2D(specularMap, textureCoord).rgb;");
}
- if (uvs && material._baseColorMap) {
- if (material._baseColorMap._state.matrix) {
- src.push("textureCoord = (baseColorMapMatrix * texturePos).xy;");
+ if (uvs && material._glossinessMap) {
+ if (material._glossinessMap._state.matrix) {
+ src.push("textureCoord = (glossinessMapMatrix * texturePos).xy;");
} else {
src.push("textureCoord = texturePos.xy;");
}
- src.push("vec4 baseColorTexel = texture2D(baseColorMap, textureCoord);");
- src.push("baseColorTexel = " + TEXTURE_DECODE_FUNCS[material._baseColorMap._state.encoding] + "(baseColorTexel);");
- src.push("diffuseColor *= baseColorTexel.rgb;");
- src.push("alpha *= baseColorTexel.a;");
+ src.push("glossiness *= texture2D(glossinessMap, textureCoord).r;");
}
- if (uvs && material._emissiveMap) {
- if (material._emissiveMap._state.matrix) {
- src.push("textureCoord = (emissiveMapMatrix * texturePos).xy;");
+ if (uvs && material._specularGlossinessMap) {
+ if (material._specularGlossinessMap._state.matrix) {
+ src.push("textureCoord = (materialSpecularGlossinessMapMatrix * texturePos).xy;");
} else {
src.push("textureCoord = texturePos.xy;");
}
- src.push("vec4 emissiveTexel = texture2D(emissiveMap, textureCoord);");
- src.push("emissiveTexel = " + TEXTURE_DECODE_FUNCS[material._emissiveMap._state.encoding] + "(emissiveTexel);");
- src.push("emissiveColor *= emissiveTexel.rgb;");
+ src.push("vec4 specGlossRGB = texture2D(materialSpecularGlossinessMap, textureCoord).rgba;"); // TODO: what if only RGB texture?
+ src.push("specular *= specGlossRGB.rgb;");
+ src.push("glossiness *= specGlossRGB.a;");
}
- if (uvs && material._alphaMap) {
- if (material._alphaMap._state.matrix) {
- src.push("textureCoord = (alphaMapMatrix * texturePos).xy;");
+ if (uvs && material._metallicMap) {
+ if (material._metallicMap._state.matrix) {
+ src.push("textureCoord = (metallicMapMatrix * texturePos).xy;");
} else {
src.push("textureCoord = texturePos.xy;");
}
- src.push("alpha *= texture2D(alphaMap, textureCoord).r;");
+ src.push("metallic *= texture2D(metallicMap, textureCoord).r;");
}
- if (uvs && material._occlusionMap) {
- if (material._occlusionMap._state.matrix) {
- src.push("textureCoord = (occlusionMapMatrix * texturePos).xy;");
+ if (uvs && material._roughnessMap) {
+ if (material._roughnessMap._state.matrix) {
+ src.push("textureCoord = (roughnessMapMatrix * texturePos).xy;");
} else {
src.push("textureCoord = texturePos.xy;");
}
- src.push("occlusion *= texture2D(occlusionMap, textureCoord).r;");
+ src.push("roughness *= texture2D(roughnessMap, textureCoord).r;");
}
- if (normals && ((lightsState.lights.length > 0) || lightsState.lightMaps.length > 0 || lightsState.reflectionMaps.length > 0)) {
-
- //--------------------------------------------------------------------------------
- // SHADING
- //--------------------------------------------------------------------------------
-
- if (uvs && material._normalMap) {
- if (material._normalMap._state.matrix) {
- src.push("textureCoord = (normalMapMatrix * texturePos).xy;");
- } else {
- src.push("textureCoord = texturePos.xy;");
- }
- src.push("vec3 viewNormal = perturbNormal2Arb( vViewPosition, normalize(vViewNormal), textureCoord );");
+ if (uvs && material._metallicRoughnessMap) {
+ if (material._metallicRoughnessMap._state.matrix) {
+ src.push("textureCoord = (metallicRoughnessMapMatrix * texturePos).xy;");
} else {
- src.push("vec3 viewNormal = normalize(vViewNormal);");
+ src.push("textureCoord = texturePos.xy;");
}
+ src.push("vec3 metalRoughRGB = texture2D(metallicRoughnessMap, textureCoord).rgb;");
+ src.push("metallic *= metalRoughRGB.b;");
+ src.push("roughness *= metalRoughRGB.g;");
+ }
- if (uvs && material._specularMap) {
- if (material._specularMap._state.matrix) {
- src.push("textureCoord = (specularMapMatrix * texturePos).xy;");
- } else {
- src.push("textureCoord = texturePos.xy;");
- }
- src.push("specular *= texture2D(specularMap, textureCoord).rgb;");
- }
+ src.push("vec3 viewEyeDir = normalize(-vViewPosition);");
- if (uvs && material._glossinessMap) {
- if (material._glossinessMap._state.matrix) {
- src.push("textureCoord = (glossinessMapMatrix * texturePos).xy;");
- } else {
- src.push("textureCoord = texturePos.xy;");
- }
- src.push("glossiness *= texture2D(glossinessMap, textureCoord).r;");
- }
+ if (material._diffuseFresnel) {
+ src.push("float diffuseFresnel = fresnel(viewEyeDir, viewNormal, diffuseFresnelEdgeBias, diffuseFresnelCenterBias, diffuseFresnelPower);");
+ src.push("diffuseColor *= mix(diffuseFresnelEdgeColor, diffuseFresnelCenterColor, diffuseFresnel);");
+ }
+ if (material._specularFresnel) {
+ src.push("float specularFresnel = fresnel(viewEyeDir, viewNormal, specularFresnelEdgeBias, specularFresnelCenterBias, specularFresnelPower);");
+ src.push("specular *= mix(specularFresnelEdgeColor, specularFresnelCenterColor, specularFresnel);");
+ }
+ if (material._alphaFresnel) {
+ src.push("float alphaFresnel = fresnel(viewEyeDir, viewNormal, alphaFresnelEdgeBias, alphaFresnelCenterBias, alphaFresnelPower);");
+ src.push("alpha *= mix(alphaFresnelEdgeColor.r, alphaFresnelCenterColor.r, alphaFresnel);");
+ }
+ if (material._emissiveFresnel) {
+ src.push("float emissiveFresnel = fresnel(viewEyeDir, viewNormal, emissiveFresnelEdgeBias, emissiveFresnelCenterBias, emissiveFresnelPower);");
+ src.push("emissiveColor *= mix(emissiveFresnelEdgeColor, emissiveFresnelCenterColor, emissiveFresnel);");
+ }
- if (uvs && material._specularGlossinessMap) {
- if (material._specularGlossinessMap._state.matrix) {
- src.push("textureCoord = (materialSpecularGlossinessMapMatrix * texturePos).xy;");
- } else {
- src.push("textureCoord = texturePos.xy;");
- }
- src.push("vec4 specGlossRGB = texture2D(materialSpecularGlossinessMap, textureCoord).rgba;"); // TODO: what if only RGB texture?
- src.push("specular *= specGlossRGB.rgb;");
- src.push("glossiness *= specGlossRGB.a;");
- }
+ src.push("if (materialAlphaModeCutoff[1] == 1.0 && alpha < materialAlphaModeCutoff[2]) {"); // ie. (alphaMode == "mask" && alpha < alphaCutoff)
+ src.push(" discard;"); // TODO: Discard earlier within this shader?
+ src.push("}");
- if (uvs && material._metallicMap) {
- if (material._metallicMap._state.matrix) {
- src.push("textureCoord = (metallicMapMatrix * texturePos).xy;");
- } else {
- src.push("textureCoord = texturePos.xy;");
- }
- src.push("metallic *= texture2D(metallicMap, textureCoord).r;");
- }
+ // PREPARE INPUTS FOR SHADER FUNCTIONS
- if (uvs && material._roughnessMap) {
- if (material._roughnessMap._state.matrix) {
- src.push("textureCoord = (roughnessMapMatrix * texturePos).xy;");
- } else {
- src.push("textureCoord = texturePos.xy;");
- }
- src.push("roughness *= texture2D(roughnessMap, textureCoord).r;");
- }
+ src.push("IncidentLight light;");
+ src.push("Material material;");
+ src.push("Geometry geometry;");
+ src.push("ReflectedLight reflectedLight = ReflectedLight(vec3(0.0,0.0,0.0), vec3(0.0,0.0,0.0));");
+ src.push("vec3 viewLightDir;");
- if (uvs && material._metallicRoughnessMap) {
- if (material._metallicRoughnessMap._state.matrix) {
- src.push("textureCoord = (metallicRoughnessMapMatrix * texturePos).xy;");
- } else {
- src.push("textureCoord = texturePos.xy;");
- }
- src.push("vec3 metalRoughRGB = texture2D(metallicRoughnessMap, textureCoord).rgb;");
- src.push("metallic *= metalRoughRGB.b;");
- src.push("roughness *= metalRoughRGB.g;");
- }
+ if (phongMaterial) {
+ src.push("material.diffuseColor = diffuseColor;");
+ src.push("material.specularColor = specular;");
+ src.push("material.shine = materialShininess;");
+ }
+
+ if (specularMaterial) {
+ src.push("float oneMinusSpecularStrength = 1.0 - max(max(specular.r, specular.g ),specular.b);"); // Energy conservation
+ src.push("material.diffuseColor = diffuseColor * oneMinusSpecularStrength;");
+ src.push("material.specularRoughness = clamp( 1.0 - glossiness, 0.04, 1.0 );");
+ src.push("material.specularColor = specular;");
+ }
- src.push("vec3 viewEyeDir = normalize(-vViewPosition);");
+ if (metallicMaterial) {
+ src.push("float dielectricSpecular = 0.16 * specularF0 * specularF0;");
+ src.push("material.diffuseColor = diffuseColor * (1.0 - dielectricSpecular) * (1.0 - metallic);");
+ src.push("material.specularRoughness = clamp(roughness, 0.04, 1.0);");
+ src.push("material.specularColor = mix(vec3(dielectricSpecular), diffuseColor, metallic);");
+ }
- if (material._diffuseFresnel) {
- src.push("float diffuseFresnel = fresnel(viewEyeDir, viewNormal, diffuseFresnelEdgeBias, diffuseFresnelCenterBias, diffuseFresnelPower);");
- src.push("diffuseColor *= mix(diffuseFresnelEdgeColor, diffuseFresnelCenterColor, diffuseFresnel);");
- }
- if (material._specularFresnel) {
- src.push("float specularFresnel = fresnel(viewEyeDir, viewNormal, specularFresnelEdgeBias, specularFresnelCenterBias, specularFresnelPower);");
- src.push("specular *= mix(specularFresnelEdgeColor, specularFresnelCenterColor, specularFresnel);");
- }
- if (material._alphaFresnel) {
- src.push("float alphaFresnel = fresnel(viewEyeDir, viewNormal, alphaFresnelEdgeBias, alphaFresnelCenterBias, alphaFresnelPower);");
- src.push("alpha *= mix(alphaFresnelEdgeColor.r, alphaFresnelCenterColor.r, alphaFresnel);");
- }
- if (material._emissiveFresnel) {
- src.push("float emissiveFresnel = fresnel(viewEyeDir, viewNormal, emissiveFresnelEdgeBias, emissiveFresnelCenterBias, emissiveFresnelPower);");
- src.push("emissiveColor *= mix(emissiveFresnelEdgeColor, emissiveFresnelCenterColor, emissiveFresnel);");
- }
+ src.push("geometry.position = vViewPosition;");
+ if (lightsState.lightMaps.length > 0) {
+ src.push("geometry.worldNormal = normalize(vWorldNormal);");
+ }
+ src.push("geometry.viewNormal = viewNormal;");
+ src.push("geometry.viewEyeDir = viewEyeDir;");
- src.push("if (materialAlphaModeCutoff[1] == 1.0 && alpha < materialAlphaModeCutoff[2]) {"); // ie. (alphaMode == "mask" && alpha < alphaCutoff)
- src.push(" discard;"); // TODO: Discard earlier within this shader?
- src.push("}");
+ // ENVIRONMENT AND REFLECTION MAP SHADING
- // PREPARE INPUTS FOR SHADER FUNCTIONS
+ if ((phongMaterial) && (lightsState.lightMaps.length > 0 || lightsState.reflectionMaps.length > 0)) {
+ src.push("computePhongLightMapping(geometry, material, reflectedLight);");
+ }
- src.push("IncidentLight light;");
- src.push("Material material;");
- src.push("Geometry geometry;");
- src.push("ReflectedLight reflectedLight = ReflectedLight(vec3(0.0,0.0,0.0), vec3(0.0,0.0,0.0));");
- src.push("vec3 viewLightDir;");
+ if ((specularMaterial || metallicMaterial) && (lightsState.lightMaps.length > 0 || lightsState.reflectionMaps.length > 0)) {
+ src.push("computePBRLightMapping(geometry, material, reflectedLight);");
+ }
- if (phongMaterial) {
- src.push("material.diffuseColor = diffuseColor;");
- src.push("material.specularColor = specular;");
- src.push("material.shine = materialShininess;");
- }
+ // LIGHT SOURCE SHADING
- if (specularMaterial) {
- src.push("float oneMinusSpecularStrength = 1.0 - max(max(specular.r, specular.g ),specular.b);"); // Energy conservation
- src.push("material.diffuseColor = diffuseColor * oneMinusSpecularStrength;");
- src.push("material.specularRoughness = clamp( 1.0 - glossiness, 0.04, 1.0 );");
- src.push("material.specularColor = specular;");
- }
+ src.push("float shadow = 1.0;");
- if (metallicMaterial) {
- src.push("float dielectricSpecular = 0.16 * specularF0 * specularF0;");
- src.push("material.diffuseColor = diffuseColor * (1.0 - dielectricSpecular) * (1.0 - metallic);");
- src.push("material.specularRoughness = clamp(roughness, 0.04, 1.0);");
- src.push("material.specularColor = mix(vec3(dielectricSpecular), diffuseColor, metallic);");
- }
+ // if (receiveShadow) {
+ //
+ // src.push("float lightDepth2 = clamp(length(lightPos)/40.0, 0.0, 1.0);");
+ // src.push("float illuminated = VSM(sLightDepth, lightUV, lightDepth2);");
+ //
+ src.push("float shadowAcneRemover = 0.0007;");
+ src.push("vec3 fragmentDepth;");
+ src.push("float texelSize = 1.0 / 1024.0;");
+ src.push("float amountInLight = 0.0;");
+ src.push("vec3 shadowCoord;");
+ src.push('vec4 rgbaDepth;');
+ src.push("float depth;");
+ // }
- src.push("geometry.position = vViewPosition;");
- if (lightsState.lightMaps.length > 0) {
- src.push("geometry.worldNormal = normalize(vWorldNormal);");
- }
- src.push("geometry.viewNormal = viewNormal;");
- src.push("geometry.viewEyeDir = viewEyeDir;");
+ const numShadows = 0;
+ for (i = 0, len = lightsState.lights.length; i < len; i++) {
- // ENVIRONMENT AND REFLECTION MAP SHADING
+ light = lightsState.lights[i];
- if ((phongMaterial) && (lightsState.lightMaps.length > 0 || lightsState.reflectionMaps.length > 0)) {
- src.push("computePhongLightMapping(geometry, material, reflectedLight);");
+ if (light.type === "ambient") {
+ continue;
}
+ if (light.type === "dir" && light.space === "view") {
+ src.push("viewLightDir = -normalize(lightDir" + i + ");");
+ } else if (light.type === "point" && light.space === "view") {
+ src.push("viewLightDir = normalize(lightPos" + i + " - vViewPosition);");
+ //src.push("tmpVec3 = lightPos" + i + ".xyz - viewPosition.xyz;");
+ //src.push("lightDist = abs(length(tmpVec3));");
+ } else {
+ src.push("viewLightDir = normalize(vViewLightReverseDirAndDist" + i + ".xyz);"); // If normal mapping, the fragment->light vector will be in tangent space
+ }
+
+ if (receiveShadow && light.shadow) {
+
+ // if (true) {
+ // src.push('shadowCoord = (vShadowPosFromLight' + i + '.xyz/vShadowPosFromLight' + i + '.w)/2.0 + 0.5;');
+ // src.push("lightDepth2 = clamp(length(vec3[0.0, 20.0, 20.0])/40.0, 0.0, 1.0);");
+ // src.push("shadow *= VSM(shadowMap' + i + ', shadowCoord, lightDepth2);");
+ // }
+ //
+ // if (false) {
+ //
+ // PCF
+
+ src.push("shadow = 0.0;");
+
+ src.push("fragmentDepth = vShadowPosFromLight" + i + ".xyz;");
+ src.push("fragmentDepth.z -= shadowAcneRemover;");
+ src.push("for (int x = -3; x <= 3; x++) {");
+ src.push(" for (int y = -3; y <= 3; y++) {");
+ src.push(" float texelDepth = unpackDepth(texture2D(shadowMap" + i + ", fragmentDepth.xy + vec2(x, y) * texelSize));");
+ src.push(" if (fragmentDepth.z < texelDepth) {");
+ src.push(" shadow += 1.0;");
+ src.push(" }");
+ src.push(" }");
+ src.push("}");
- if ((specularMaterial || metallicMaterial) && (lightsState.lightMaps.length > 0 || lightsState.reflectionMaps.length > 0)) {
- src.push("computePBRLightMapping(geometry, material, reflectedLight);");
+ src.push("light.color = lightColor" + i + ".rgb * (lightColor" + i + ".a * (shadow / 49.0));");
+ //
+ // }
+ //
+ // if (false){
+ //
+ // src.push("shadow = 1.0;");
+ //
+ // src.push('shadowCoord = (vShadowPosFromLight' + i + '.xyz/vShadowPosFromLight' + i + '.w)/2.0 + 0.5;');
+ //
+ // src.push('shadow -= (shadowCoord.z > unpackDepth(texture2D(shadowMap' + i + ', shadowCoord.xy + vec2( -0.94201624, -0.39906216 ) / 700.0)) + 0.0015) ? 0.2 : 0.0;');
+ // src.push('shadow -= (shadowCoord.z > unpackDepth(texture2D(shadowMap' + i + ', shadowCoord.xy + vec2( 0.94558609, -0.76890725 ) / 700.0)) + 0.0015) ? 0.2 : 0.0;');
+ // src.push('shadow -= (shadowCoord.z > unpackDepth(texture2D(shadowMap' + i + ', shadowCoord.xy + vec2( -0.094184101, -0.92938870 ) / 700.0)) + 0.0015) ? 0.2 : 0.0;');
+ // src.push('shadow -= (shadowCoord.z > unpackDepth(texture2D(shadowMap' + i + ', shadowCoord.xy + vec2( 0.34495938, 0.29387760 ) / 700.0)) + 0.0015) ? 0.2 : 0.0;');
+ //
+ // src.push("light.color = lightColor" + i + ".rgb * (lightColor" + i + ".a * shadow);");
+ // }
+ } else {
+ src.push("light.color = lightColor" + i + ".rgb * (lightColor" + i + ".a );");
}
- // LIGHT SOURCE SHADING
-
- src.push("float shadow = 1.0;");
+ src.push("light.direction = viewLightDir;");
- // if (receiveShadow) {
- //
- // src.push("float lightDepth2 = clamp(length(lightPos)/40.0, 0.0, 1.0);");
- // src.push("float illuminated = VSM(sLightDepth, lightUV, lightDepth2);");
- //
- src.push("float shadowAcneRemover = 0.0007;");
- src.push("vec3 fragmentDepth;");
- src.push("float texelSize = 1.0 / 1024.0;");
- src.push("float amountInLight = 0.0;");
- src.push("vec3 shadowCoord;");
- src.push('vec4 rgbaDepth;');
- src.push("float depth;");
- // }
- const numShadows = 0;
- for (i = 0, len = lightsState.lights.length; i < len; i++) {
-
- light = lightsState.lights[i];
-
- if (light.type === "ambient") {
- continue;
- }
- if (light.type === "dir" && light.space === "view") {
- src.push("viewLightDir = -normalize(lightDir" + i + ");");
- } else if (light.type === "point" && light.space === "view") {
- src.push("viewLightDir = normalize(lightPos" + i + " - vViewPosition);");
- //src.push("tmpVec3 = lightPos" + i + ".xyz - viewPosition.xyz;");
- //src.push("lightDist = abs(length(tmpVec3));");
- } else {
- src.push("viewLightDir = normalize(vViewLightReverseDirAndDist" + i + ".xyz);"); // If normal mapping, the fragment->light vector will be in tangent space
- }
-
- if (receiveShadow && light.shadow) {
-
- // if (true) {
- // src.push('shadowCoord = (vShadowPosFromLight' + i + '.xyz/vShadowPosFromLight' + i + '.w)/2.0 + 0.5;');
- // src.push("lightDepth2 = clamp(length(vec3[0.0, 20.0, 20.0])/40.0, 0.0, 1.0);");
- // src.push("shadow *= VSM(shadowMap' + i + ', shadowCoord, lightDepth2);");
- // }
- //
- // if (false) {
- //
- // PCF
-
- src.push("shadow = 0.0;");
-
- src.push("fragmentDepth = vShadowPosFromLight" + i + ".xyz;");
- src.push("fragmentDepth.z -= shadowAcneRemover;");
- src.push("for (int x = -3; x <= 3; x++) {");
- src.push(" for (int y = -3; y <= 3; y++) {");
- src.push(" float texelDepth = unpackDepth(texture2D(shadowMap" + i + ", fragmentDepth.xy + vec2(x, y) * texelSize));");
- src.push(" if (fragmentDepth.z < texelDepth) {");
- src.push(" shadow += 1.0;");
- src.push(" }");
- src.push(" }");
- src.push("}");
-
- src.push("light.color = lightColor" + i + ".rgb * (lightColor" + i + ".a * (shadow / 49.0));");
- //
- // }
- //
- // if (false){
- //
- // src.push("shadow = 1.0;");
- //
- // src.push('shadowCoord = (vShadowPosFromLight' + i + '.xyz/vShadowPosFromLight' + i + '.w)/2.0 + 0.5;');
- //
- // src.push('shadow -= (shadowCoord.z > unpackDepth(texture2D(shadowMap' + i + ', shadowCoord.xy + vec2( -0.94201624, -0.39906216 ) / 700.0)) + 0.0015) ? 0.2 : 0.0;');
- // src.push('shadow -= (shadowCoord.z > unpackDepth(texture2D(shadowMap' + i + ', shadowCoord.xy + vec2( 0.94558609, -0.76890725 ) / 700.0)) + 0.0015) ? 0.2 : 0.0;');
- // src.push('shadow -= (shadowCoord.z > unpackDepth(texture2D(shadowMap' + i + ', shadowCoord.xy + vec2( -0.094184101, -0.92938870 ) / 700.0)) + 0.0015) ? 0.2 : 0.0;');
- // src.push('shadow -= (shadowCoord.z > unpackDepth(texture2D(shadowMap' + i + ', shadowCoord.xy + vec2( 0.34495938, 0.29387760 ) / 700.0)) + 0.0015) ? 0.2 : 0.0;');
- //
- // src.push("light.color = lightColor" + i + ".rgb * (lightColor" + i + ".a * shadow);");
- // }
- } else {
- src.push("light.color = lightColor" + i + ".rgb * (lightColor" + i + ".a );");
- }
-
- src.push("light.direction = viewLightDir;");
-
-
- if (phongMaterial) {
- src.push("computePhongLighting(light, geometry, material, reflectedLight);");
- }
-
- if (specularMaterial || metallicMaterial) {
- src.push("computePBRLighting(light, geometry, material, reflectedLight);");
- }
+ if (phongMaterial) {
+ src.push("computePhongLighting(light, geometry, material, reflectedLight);");
}
- if (numShadows > 0) {
- //src.push("shadow /= " + (9 * numShadows) + ".0;");
+ if (specularMaterial || metallicMaterial) {
+ src.push("computePBRLighting(light, geometry, material, reflectedLight);");
}
+ }
- //src.push("reflectedLight.diffuse *= shadow;");
+ if (numShadows > 0) {
+ //src.push("shadow /= " + (9 * numShadows) + ".0;");
+ }
- // COMBINE TERMS
+ //src.push("reflectedLight.diffuse *= shadow;");
- if (phongMaterial) {
+ // COMBINE TERMS
- src.push("ambientColor *= (lightAmbient.rgb * lightAmbient.a);");
+ if (phongMaterial) {
- src.push("vec3 outgoingLight = ((occlusion * (( reflectedLight.diffuse + reflectedLight.specular)))) + emissiveColor;");
+ src.push("ambientColor *= (lightAmbient.rgb * lightAmbient.a);");
- } else {
- src.push("vec3 outgoingLight = (occlusion * (reflectedLight.diffuse)) + (shadow * occlusion * reflectedLight.specular) + emissiveColor;");
- }
+ src.push("vec3 outgoingLight = ((occlusion * (( reflectedLight.diffuse + reflectedLight.specular)))) + emissiveColor;");
+ } else {
+ src.push("vec3 outgoingLight = (occlusion * (reflectedLight.diffuse)) + (shadow * occlusion * reflectedLight.specular) + emissiveColor;");
}
- else {
-
- //--------------------------------------------------------------------------------
- // NO SHADING - EMISSIVE and AMBIENT ONLY
- //--------------------------------------------------------------------------------
+ }
- src.push("ambientColor *= (lightAmbient.rgb * lightAmbient.a);");
+ else {
- src.push("vec3 outgoingLight = emissiveColor + ambientColor;");
- }
+ //--------------------------------------------------------------------------------
+ // NO SHADING - EMISSIVE and AMBIENT ONLY
+ //--------------------------------------------------------------------------------
- src.push("gl_FragColor = vec4(outgoingLight, alpha) * colorize;");
+ src.push("ambientColor *= (lightAmbient.rgb * lightAmbient.a);");
- if (gammaOutput) {
- src.push("gl_FragColor = linearToGamma(gl_FragColor, gammaFactor);");
- }
+ src.push("vec3 outgoingLight = emissiveColor + ambientColor;");
+ }
- src.push("}");
+ src.push("gl_FragColor = vec4(outgoingLight, alpha) * colorize;");
- return src;
+ if (gammaOutput) {
+ src.push("gl_FragColor = linearToGamma(gl_FragColor, gammaFactor);");
}
-})();
\ No newline at end of file
+
+ src.push("}");
+
+ return src;
+}
+
+export {DrawShaderSource};
\ No newline at end of file
diff --git a/src/renderer/emphasis/emphasisEdgesRenderer.js b/src/renderer/emphasis/emphasisEdgesRenderer.js
index c1b90a222..aad3d230b 100644
--- a/src/renderer/emphasis/emphasisEdgesRenderer.js
+++ b/src/renderer/emphasis/emphasisEdgesRenderer.js
@@ -2,221 +2,222 @@
* @author xeolabs / https://github.com/xeolabs
*/
-(function () {
+import {Map} from "../../utils/map.js";
+import {EmphasisEdgesShaderSource} from "./emphasisEdgesShaderSource.js";
+import {Program} from "../program.js";
+import {stats} from './../../stats.js';
- "use strict";
+const ids = new Map({});
- const ids = new xeogl.utils.Map({});
+const EmphasisEdgesRenderer = function (hash, mesh) {
+ this.id = ids.addItem({});
+ this._hash = hash;
+ this._scene = mesh.scene;
+ this._useCount = 0;
+ this._shaderSource = new EmphasisEdgesShaderSource(mesh);
+ this._allocate(mesh);
+};
- xeogl.renderer.EmphasisEdgesRenderer = function (hash, mesh) {
- this.id = ids.addItem({});
- this._hash = hash;
- this._scene = mesh.scene;
- this._useCount = 0;
- this._shaderSource = new xeogl.renderer.EmphasisEdgesShaderSource(mesh);
- this._allocate(mesh);
- };
-
- const renderers = {};
+const renderers = {};
- xeogl.renderer.EmphasisEdgesRenderer.get = function (mesh) {
- const hash = [
- mesh.scene.id,
- mesh.scene.gammaOutput ? "go" : "", // Gamma input not needed
- mesh.scene._clipsState.getHash(),
- mesh._geometry._state.quantized ? "cp" : "",
- mesh._state.hash
- ].join(";");
- let renderer = renderers[hash];
- if (!renderer) {
- renderer = new xeogl.renderer.EmphasisEdgesRenderer(hash, mesh);
- renderers[hash] = renderer;
- xeogl.stats.memory.programs++;
- }
- renderer._useCount++;
- return renderer;
- };
+EmphasisEdgesRenderer.get = function (mesh) {
+ const hash = [
+ mesh.scene.id,
+ mesh.scene.gammaOutput ? "go" : "", // Gamma input not needed
+ mesh.scene._clipsState.getHash(),
+ mesh._geometry._state.quantized ? "cp" : "",
+ mesh._state.hash
+ ].join(";");
+ let renderer = renderers[hash];
+ if (!renderer) {
+ renderer = new EmphasisEdgesRenderer(hash, mesh);
+ renderers[hash] = renderer;
+ stats.memory.programs++;
+ }
+ renderer._useCount++;
+ return renderer;
+};
- xeogl.renderer.EmphasisEdgesRenderer.prototype.put = function () {
- if (--this._useCount === 0) {
- ids.removeItem(this.id);
- if (this._program) {
- this._program.destroy();
- }
- delete renderers[this._hash];
- xeogl.stats.memory.programs--;
+EmphasisEdgesRenderer.prototype.put = function () {
+ if (--this._useCount === 0) {
+ ids.removeItem(this.id);
+ if (this._program) {
+ this._program.destroy();
}
- };
+ delete renderers[this._hash];
+ stats.memory.programs--;
+ }
+};
- xeogl.renderer.EmphasisEdgesRenderer.prototype.webglContextRestored = function () {
- this._program = null;
- };
+EmphasisEdgesRenderer.prototype.webglContextRestored = function () {
+ this._program = null;
+};
- xeogl.renderer.EmphasisEdgesRenderer.prototype.drawMesh = function (frame, mesh, mode) {
- if (!this._program) {
- this._allocate(mesh);
- }
- const scene = this._scene;
- const gl = scene.canvas.gl;
- let materialState;
- const meshState = mesh._state;
- const geometry = mesh._geometry;
- const geometryState = geometry._state;
- if (frame.lastProgramId !== this._program.id) {
- frame.lastProgramId = this._program.id;
- this._bindProgram(frame);
- }
- switch (mode) {
- case 0:
- materialState = mesh._ghostMaterial._state;
- break;
- case 1:
- materialState = mesh._highlightMaterial._state;
- break;
- case 2:
- materialState = mesh._selectedMaterial._state;
- break;
- case 3:
- default:
- materialState = mesh._edgeMaterial._state;
- break;
- }
- if (materialState.id !== this._lastMaterialId) {
- const backfaces = materialState.backfaces;
- if (frame.backfaces !== backfaces) {
- if (backfaces) {
- gl.disable(gl.CULL_FACE);
- } else {
- gl.enable(gl.CULL_FACE);
- }
- frame.backfaces = backfaces;
- }
- if (frame.lineWidth !== materialState.edgeWidth) {
- gl.lineWidth(materialState.edgeWidth);
- frame.lineWidth = materialState.edgeWidth;
- }
- if (this._uEdgeColor) {
- const edgeColor = materialState.edgeColor;
- const edgeAlpha = materialState.edgeAlpha;
- gl.uniform4f(this._uEdgeColor, edgeColor[0], edgeColor[1], edgeColor[2], edgeAlpha);
+EmphasisEdgesRenderer.prototype.drawMesh = function (frame, mesh, mode) {
+ if (!this._program) {
+ this._allocate(mesh);
+ }
+ const scene = this._scene;
+ const gl = scene.canvas.gl;
+ let materialState;
+ const meshState = mesh._state;
+ const geometry = mesh._geometry;
+ const geometryState = geometry._state;
+ if (frame.lastProgramId !== this._program.id) {
+ frame.lastProgramId = this._program.id;
+ this._bindProgram(frame);
+ }
+ switch (mode) {
+ case 0:
+ materialState = mesh._ghostMaterial._state;
+ break;
+ case 1:
+ materialState = mesh._highlightMaterial._state;
+ break;
+ case 2:
+ materialState = mesh._selectedMaterial._state;
+ break;
+ case 3:
+ default:
+ materialState = mesh._edgeMaterial._state;
+ break;
+ }
+ if (materialState.id !== this._lastMaterialId) {
+ const backfaces = materialState.backfaces;
+ if (frame.backfaces !== backfaces) {
+ if (backfaces) {
+ gl.disable(gl.CULL_FACE);
+ } else {
+ gl.enable(gl.CULL_FACE);
}
- this._lastMaterialId = materialState.id;
+ frame.backfaces = backfaces;
}
- gl.uniformMatrix4fv(this._uModelMatrix, gl.FALSE, mesh.worldMatrix);
- if (this._uModelNormalMatrix) {
- gl.uniformMatrix4fv(this._uModelNormalMatrix, gl.FALSE, mesh.worldNormalMatrix);
+ if (frame.lineWidth !== materialState.edgeWidth) {
+ gl.lineWidth(materialState.edgeWidth);
+ frame.lineWidth = materialState.edgeWidth;
}
- if (this._uClippable) {
- gl.uniform1i(this._uClippable, meshState.clippable);
+ if (this._uEdgeColor) {
+ const edgeColor = materialState.edgeColor;
+ const edgeAlpha = materialState.edgeAlpha;
+ gl.uniform4f(this._uEdgeColor, edgeColor[0], edgeColor[1], edgeColor[2], edgeAlpha);
}
- if (geometryState.combined) {
- const vertexBufs = mesh._geometry._getVertexBufs();
- if (vertexBufs.id !== this._lastVertexBufsId) {
- if (vertexBufs.positionsBuf && this._aPosition) {
- this._aPosition.bindArrayBuffer(vertexBufs.positionsBuf, vertexBufs.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
- frame.bindArray++;
- }
- this._lastVertexBufsId = vertexBufs.id;
+ this._lastMaterialId = materialState.id;
+ }
+ gl.uniformMatrix4fv(this._uModelMatrix, gl.FALSE, mesh.worldMatrix);
+ if (this._uModelNormalMatrix) {
+ gl.uniformMatrix4fv(this._uModelNormalMatrix, gl.FALSE, mesh.worldNormalMatrix);
+ }
+ if (this._uClippable) {
+ gl.uniform1i(this._uClippable, meshState.clippable);
+ }
+ if (geometryState.combined) {
+ const vertexBufs = mesh._geometry._getVertexBufs();
+ if (vertexBufs.id !== this._lastVertexBufsId) {
+ if (vertexBufs.positionsBuf && this._aPosition) {
+ this._aPosition.bindArrayBuffer(vertexBufs.positionsBuf, vertexBufs.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
+ frame.bindArray++;
}
+ this._lastVertexBufsId = vertexBufs.id;
}
- // Bind VBOs
- let indicesBuf;
- if (geometryState.primitive === gl.TRIANGLES) {
- indicesBuf = geometry._getEdgesIndices();
- } else if (geometryState.primitive === gl.LINES) {
- indicesBuf = geometryState.indicesBuf;
- }
- if (indicesBuf) {
- if (geometryState.id !== this._lastGeometryId) {
- if (this._uPositionsDecodeMatrix) {
- gl.uniformMatrix4fv(this._uPositionsDecodeMatrix, false, geometryState.positionsDecodeMatrix);
- }
- if (!geometryState.combined) { // VBOs were bound by the VertexBufs logic above
- if (this._aPosition) {
- this._aPosition.bindArrayBuffer(geometryState.positionsBuf, geometryState.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
- frame.bindArray++;
- }
+ }
+ // Bind VBOs
+ let indicesBuf;
+ if (geometryState.primitive === gl.TRIANGLES) {
+ indicesBuf = geometry._getEdgesIndices();
+ } else if (geometryState.primitive === gl.LINES) {
+ indicesBuf = geometryState.indicesBuf;
+ }
+ if (indicesBuf) {
+ if (geometryState.id !== this._lastGeometryId) {
+ if (this._uPositionsDecodeMatrix) {
+ gl.uniformMatrix4fv(this._uPositionsDecodeMatrix, false, geometryState.positionsDecodeMatrix);
+ }
+ if (!geometryState.combined) { // VBOs were bound by the VertexBufs logic above
+ if (this._aPosition) {
+ this._aPosition.bindArrayBuffer(geometryState.positionsBuf, geometryState.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
+ frame.bindArray++;
}
- indicesBuf.bind();
- frame.bindArray++;
- this._lastGeometryId = geometryState.id;
}
- gl.drawElements(gl.LINES, indicesBuf.numItems, indicesBuf.itemType, 0);
- frame.drawElements++;
+ indicesBuf.bind();
+ frame.bindArray++;
+ this._lastGeometryId = geometryState.id;
}
- };
+ gl.drawElements(gl.LINES, indicesBuf.numItems, indicesBuf.itemType, 0);
+ frame.drawElements++;
+ }
+};
- xeogl.renderer.EmphasisEdgesRenderer.prototype._allocate = function (mesh) {
- const gl = mesh.scene.canvas.gl;
- const clipsState = mesh.scene._clipsState;
- this._program = new xeogl.renderer.Program(gl, this._shaderSource);
- if (this._program.errors) {
- this.errors = this._program.errors;
- return;
- }
- const program = this._program;
- this._uPositionsDecodeMatrix = program.getLocation("positionsDecodeMatrix");
- this._uModelMatrix = program.getLocation("modelMatrix");
- this._uViewMatrix = program.getLocation("viewMatrix");
- this._uProjMatrix = program.getLocation("projMatrix");
- this._uClips = [];
- for (let i = 0, len = clipsState.clips.length; i < len; i++) {
- this._uClips.push({
- active: program.getLocation("clipActive" + i),
- pos: program.getLocation("clipPos" + i),
- dir: program.getLocation("clipDir" + i)
- });
- }
- this._uEdgeColor = program.getLocation("edgeColor");
- this._aPosition = program.getAttribute("position");
- this._uClippable = program.getLocation("clippable");
- this._uGammaFactor = program.getLocation("gammaFactor");
- this._lastMaterialId = null;
- this._lastVertexBufsId = null;
- this._lastGeometryId = null;
- };
+EmphasisEdgesRenderer.prototype._allocate = function (mesh) {
+ const gl = mesh.scene.canvas.gl;
+ const clipsState = mesh.scene._clipsState;
+ this._program = new Program(gl, this._shaderSource);
+ if (this._program.errors) {
+ this.errors = this._program.errors;
+ return;
+ }
+ const program = this._program;
+ this._uPositionsDecodeMatrix = program.getLocation("positionsDecodeMatrix");
+ this._uModelMatrix = program.getLocation("modelMatrix");
+ this._uViewMatrix = program.getLocation("viewMatrix");
+ this._uProjMatrix = program.getLocation("projMatrix");
+ this._uClips = [];
+ for (let i = 0, len = clipsState.clips.length; i < len; i++) {
+ this._uClips.push({
+ active: program.getLocation("clipActive" + i),
+ pos: program.getLocation("clipPos" + i),
+ dir: program.getLocation("clipDir" + i)
+ });
+ }
+ this._uEdgeColor = program.getLocation("edgeColor");
+ this._aPosition = program.getAttribute("position");
+ this._uClippable = program.getLocation("clippable");
+ this._uGammaFactor = program.getLocation("gammaFactor");
+ this._lastMaterialId = null;
+ this._lastVertexBufsId = null;
+ this._lastGeometryId = null;
+};
- xeogl.renderer.EmphasisEdgesRenderer.prototype._bindProgram = function (frame) {
- const program = this._program;
- const scene = this._scene;
- const gl = scene.canvas.gl;
- const clipsState = scene._clipsState;
- const camera = scene.camera;
- const cameraState = camera._state;
- program.bind();
- frame.useProgram++;
- this._lastMaterialId = null;
- this._lastVertexBufsId = null;
- this._lastGeometryId = null;
- gl.uniformMatrix4fv(this._uViewMatrix, false, cameraState.matrix);
- gl.uniformMatrix4fv(this._uProjMatrix, false, camera.project._state.matrix);
- if (clipsState.clips.length > 0) {
- const clips = clipsState.clips;
- let clipUniforms;
- let uClipActive;
- let clip;
- let uClipPos;
- let uClipDir;
- for (let i = 0, len = this._uClips.length; i < len; i++) {
- clipUniforms = this._uClips[i];
- uClipActive = clipUniforms.active;
- clip = clips[i];
- if (uClipActive) {
- gl.uniform1i(uClipActive, clip.active);
- }
- uClipPos = clipUniforms.pos;
- if (uClipPos) {
- gl.uniform3fv(clipUniforms.pos, clip.pos);
- }
- uClipDir = clipUniforms.dir;
- if (uClipDir) {
- gl.uniform3fv(clipUniforms.dir, clip.dir);
- }
+EmphasisEdgesRenderer.prototype._bindProgram = function (frame) {
+ const program = this._program;
+ const scene = this._scene;
+ const gl = scene.canvas.gl;
+ const clipsState = scene._clipsState;
+ const camera = scene.camera;
+ const cameraState = camera._state;
+ program.bind();
+ frame.useProgram++;
+ this._lastMaterialId = null;
+ this._lastVertexBufsId = null;
+ this._lastGeometryId = null;
+ gl.uniformMatrix4fv(this._uViewMatrix, false, cameraState.matrix);
+ gl.uniformMatrix4fv(this._uProjMatrix, false, camera.project._state.matrix);
+ if (clipsState.clips.length > 0) {
+ const clips = clipsState.clips;
+ let clipUniforms;
+ let uClipActive;
+ let clip;
+ let uClipPos;
+ let uClipDir;
+ for (let i = 0, len = this._uClips.length; i < len; i++) {
+ clipUniforms = this._uClips[i];
+ uClipActive = clipUniforms.active;
+ clip = clips[i];
+ if (uClipActive) {
+ gl.uniform1i(uClipActive, clip.active);
+ }
+ uClipPos = clipUniforms.pos;
+ if (uClipPos) {
+ gl.uniform3fv(clipUniforms.pos, clip.pos);
+ }
+ uClipDir = clipUniforms.dir;
+ if (uClipDir) {
+ gl.uniform3fv(clipUniforms.dir, clip.dir);
}
}
- if (this._uGammaFactor) {
- gl.uniform1f(this._uGammaFactor, scene.gammaFactor);
- }
- };
+ }
+ if (this._uGammaFactor) {
+ gl.uniform1f(this._uGammaFactor, scene.gammaFactor);
+ }
+};
-})();
+export {EmphasisEdgesRenderer};
diff --git a/src/renderer/emphasis/emphasisEdgesShaderSource.js b/src/renderer/emphasis/emphasisEdgesShaderSource.js
index 8ffe3438b..384112e2a 100644
--- a/src/renderer/emphasis/emphasisEdgesShaderSource.js
+++ b/src/renderer/emphasis/emphasisEdgesShaderSource.js
@@ -2,126 +2,124 @@
* @author xeolabs / https://github.com/xeolabs
*/
-(function () {
-
- "use strict";
-
- xeogl.renderer.EmphasisEdgesShaderSource = function (mesh) {
+class EmphasisEdgesShaderSource {
+ constructor(mesh) {
this.vertex = buildVertex(mesh);
this.fragment = buildFragment(mesh);
- };
+ }
+}
- function buildVertex(mesh) {
- const scene = mesh.scene;
- const clipping = scene._clipsState.clips.length > 0;
- const quantizedGeometry = !!mesh._geometry._state.quantized;
- const billboard = mesh._state.billboard;
- const stationary = mesh._state.stationary;
- const src = [];
- src.push("// Edges drawing vertex shader");
- src.push("attribute vec3 position;");
- src.push("uniform mat4 modelMatrix;");
- src.push("uniform mat4 viewMatrix;");
- src.push("uniform mat4 projMatrix;");
- src.push("uniform vec4 edgeColor;");
- if (quantizedGeometry) {
- src.push("uniform mat4 positionsDecodeMatrix;");
- }
- if (clipping) {
- src.push("varying vec4 vWorldPosition;");
- }
- src.push("varying vec4 vColor;");
- if (billboard === "spherical" || billboard === "cylindrical") {
- src.push("void billboard(inout mat4 mat) {");
- src.push(" mat[0][0] = 1.0;");
- src.push(" mat[0][1] = 0.0;");
- src.push(" mat[0][2] = 0.0;");
- if (billboard === "spherical") {
- src.push(" mat[1][0] = 0.0;");
- src.push(" mat[1][1] = 1.0;");
- src.push(" mat[1][2] = 0.0;");
- }
- src.push(" mat[2][0] = 0.0;");
- src.push(" mat[2][1] = 0.0;");
- src.push(" mat[2][2] =1.0;");
- src.push("}");
- }
- src.push("void main(void) {");
- src.push("vec4 localPosition = vec4(position, 1.0); ");
- src.push("vec4 worldPosition;");
- if (quantizedGeometry) {
- src.push("localPosition = positionsDecodeMatrix * localPosition;");
- }
- src.push("mat4 viewMatrix2 = viewMatrix;");
- src.push("mat4 modelMatrix2 = modelMatrix;");
- if (stationary) {
- src.push("viewMatrix2[3][0] = viewMatrix2[3][1] = viewMatrix2[3][2] = 0.0;")
- }
- if (billboard === "spherical" || billboard === "cylindrical") {
- src.push("mat4 modelViewMatrix = viewMatrix2 * modelMatrix2;");
- src.push("billboard(modelMatrix2);");
- src.push("billboard(viewMatrix2);");
- src.push("billboard(modelViewMatrix);");
- src.push("worldPosition = modelMatrix2 * localPosition;");
- src.push("vec4 viewPosition = modelViewMatrix * localPosition;");
- } else {
- src.push("worldPosition = modelMatrix2 * localPosition;");
- src.push("vec4 viewPosition = viewMatrix2 * worldPosition; ");
- }
- src.push("vColor = edgeColor;");
- if (clipping) {
- src.push("vWorldPosition = worldPosition;");
+function buildVertex(mesh) {
+ const scene = mesh.scene;
+ const clipping = scene._clipsState.clips.length > 0;
+ const quantizedGeometry = !!mesh._geometry._state.quantized;
+ const billboard = mesh._state.billboard;
+ const stationary = mesh._state.stationary;
+ const src = [];
+ src.push("// Edges drawing vertex shader");
+ src.push("attribute vec3 position;");
+ src.push("uniform mat4 modelMatrix;");
+ src.push("uniform mat4 viewMatrix;");
+ src.push("uniform mat4 projMatrix;");
+ src.push("uniform vec4 edgeColor;");
+ if (quantizedGeometry) {
+ src.push("uniform mat4 positionsDecodeMatrix;");
+ }
+ if (clipping) {
+ src.push("varying vec4 vWorldPosition;");
+ }
+ src.push("varying vec4 vColor;");
+ if (billboard === "spherical" || billboard === "cylindrical") {
+ src.push("void billboard(inout mat4 mat) {");
+ src.push(" mat[0][0] = 1.0;");
+ src.push(" mat[0][1] = 0.0;");
+ src.push(" mat[0][2] = 0.0;");
+ if (billboard === "spherical") {
+ src.push(" mat[1][0] = 0.0;");
+ src.push(" mat[1][1] = 1.0;");
+ src.push(" mat[1][2] = 0.0;");
}
- src.push(" gl_Position = projMatrix * viewPosition;");
+ src.push(" mat[2][0] = 0.0;");
+ src.push(" mat[2][1] = 0.0;");
+ src.push(" mat[2][2] =1.0;");
src.push("}");
- return src;
}
+ src.push("void main(void) {");
+ src.push("vec4 localPosition = vec4(position, 1.0); ");
+ src.push("vec4 worldPosition;");
+ if (quantizedGeometry) {
+ src.push("localPosition = positionsDecodeMatrix * localPosition;");
+ }
+ src.push("mat4 viewMatrix2 = viewMatrix;");
+ src.push("mat4 modelMatrix2 = modelMatrix;");
+ if (stationary) {
+ src.push("viewMatrix2[3][0] = viewMatrix2[3][1] = viewMatrix2[3][2] = 0.0;")
+ }
+ if (billboard === "spherical" || billboard === "cylindrical") {
+ src.push("mat4 modelViewMatrix = viewMatrix2 * modelMatrix2;");
+ src.push("billboard(modelMatrix2);");
+ src.push("billboard(viewMatrix2);");
+ src.push("billboard(modelViewMatrix);");
+ src.push("worldPosition = modelMatrix2 * localPosition;");
+ src.push("vec4 viewPosition = modelViewMatrix * localPosition;");
+ } else {
+ src.push("worldPosition = modelMatrix2 * localPosition;");
+ src.push("vec4 viewPosition = viewMatrix2 * worldPosition; ");
+ }
+ src.push("vColor = edgeColor;");
+ if (clipping) {
+ src.push("vWorldPosition = worldPosition;");
+ }
+ src.push(" gl_Position = projMatrix * viewPosition;");
+ src.push("}");
+ return src;
+}
- function buildFragment(mesh) {
- const clipsState = mesh.scene._clipsState;
- const gammaOutput = mesh.scene.gammaOutput;
- const clipping = clipsState.clips.length > 0;
- let i;
- let len;
- const src = [];
- src.push("// Edges drawing fragment shader");
- src.push("precision lowp float;");
- if (gammaOutput) {
- src.push("uniform float gammaFactor;");
- src.push("vec4 linearToGamma( in vec4 value, in float gammaFactor ) {");
- src.push(" return vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );");
- src.push("}");
- }
- if (clipping) {
- src.push("varying vec4 vWorldPosition;");
- src.push("uniform bool clippable;");
- for (i = 0, len = clipsState.clips.length; i < len; i++) {
- src.push("uniform bool clipActive" + i + ";");
- src.push("uniform vec3 clipPos" + i + ";");
- src.push("uniform vec3 clipDir" + i + ";");
- }
+function buildFragment(mesh) {
+ const clipsState = mesh.scene._clipsState;
+ const gammaOutput = mesh.scene.gammaOutput;
+ const clipping = clipsState.clips.length > 0;
+ let i;
+ let len;
+ const src = [];
+ src.push("// Edges drawing fragment shader");
+ src.push("precision lowp float;");
+ if (gammaOutput) {
+ src.push("uniform float gammaFactor;");
+ src.push("vec4 linearToGamma( in vec4 value, in float gammaFactor ) {");
+ src.push(" return vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );");
+ src.push("}");
+ }
+ if (clipping) {
+ src.push("varying vec4 vWorldPosition;");
+ src.push("uniform bool clippable;");
+ for (i = 0, len = clipsState.clips.length; i < len; i++) {
+ src.push("uniform bool clipActive" + i + ";");
+ src.push("uniform vec3 clipPos" + i + ";");
+ src.push("uniform vec3 clipDir" + i + ";");
}
- src.push("varying vec4 vColor;");
- src.push("void main(void) {");
- if (clipping) {
- src.push("if (clippable) {");
- src.push(" float dist = 0.0;");
- for (i = 0, len = clipsState.clips.length; i < len; i++) {
- src.push("if (clipActive" + i + ") {");
- src.push(" dist += clamp(dot(-clipDir" + i + ".xyz, vWorldPosition.xyz - clipPos" + i + ".xyz), 0.0, 1000.0);");
- src.push("}");
- }
- src.push(" if (dist > 0.0) { discard; }");
+ }
+ src.push("varying vec4 vColor;");
+ src.push("void main(void) {");
+ if (clipping) {
+ src.push("if (clippable) {");
+ src.push(" float dist = 0.0;");
+ for (i = 0, len = clipsState.clips.length; i < len; i++) {
+ src.push("if (clipActive" + i + ") {");
+ src.push(" dist += clamp(dot(-clipDir" + i + ".xyz, vWorldPosition.xyz - clipPos" + i + ".xyz), 0.0, 1000.0);");
src.push("}");
}
- src.push("gl_FragColor = vColor;");
- if (gammaOutput) {
- src.push("gl_FragColor = linearToGamma(vColor, gammaFactor);");
- } else {
- src.push("gl_FragColor = vColor;");
- }
+ src.push(" if (dist > 0.0) { discard; }");
src.push("}");
- return src;
}
+ src.push("gl_FragColor = vColor;");
+ if (gammaOutput) {
+ src.push("gl_FragColor = linearToGamma(vColor, gammaFactor);");
+ } else {
+ src.push("gl_FragColor = vColor;");
+ }
+ src.push("}");
+ return src;
+}
-})();
\ No newline at end of file
+export {EmphasisEdgesShaderSource};
\ No newline at end of file
diff --git a/src/renderer/emphasis/emphasisFillRenderer.js b/src/renderer/emphasis/emphasisFillRenderer.js
index 96592a10d..1aa2fe0a0 100644
--- a/src/renderer/emphasis/emphasisFillRenderer.js
+++ b/src/renderer/emphasis/emphasisFillRenderer.js
@@ -2,281 +2,282 @@
* @author xeolabs / https://github.com/xeolabs
*/
-(function () {
+import {Map} from "../../utils/map.js";
+import {EmphasisFillShaderSource} from "./emphasisFillShaderSource.js";
+import {Program} from "../program.js";
+import {stats} from './../../stats.js';
- "use strict";
+const ids = new Map({});
- const ids = new xeogl.utils.Map({});
+const EmphasisFillRenderer = function (hash, mesh) {
+ this.id = ids.addItem({});
+ this._hash = hash;
+ this._scene = mesh.scene;
+ this._useCount = 0;
+ this._shaderSource = new EmphasisFillShaderSource(mesh);
+ this._allocate(mesh);
+};
- xeogl.renderer.EmphasisFillRenderer = function (hash, mesh) {
- this.id = ids.addItem({});
- this._hash = hash;
- this._scene = mesh.scene;
- this._useCount = 0;
- this._shaderSource = new xeogl.renderer.EmphasisFillShaderSource(mesh);
- this._allocate(mesh);
- };
+const ghostFillRenderers = {};
- const ghostFillRenderers = {};
+EmphasisFillRenderer.get = function (mesh) {
+ const hash = [
+ mesh.scene.id,
+ mesh.scene.gammaOutput ? "go" : "", // Gamma input not needed
+ mesh.scene._clipsState.getHash(),
+ !!mesh._geometry.normals ? "n" : "",
+ mesh._geometry._state.quantized ? "cp" : "",
+ mesh._state.hash
+ ].join(";");
+ let renderer = ghostFillRenderers[hash];
+ if (!renderer) {
+ renderer = new EmphasisFillRenderer(hash, mesh);
+ ghostFillRenderers[hash] = renderer;
+ stats.memory.programs++;
+ }
+ renderer._useCount++;
+ return renderer;
+};
- xeogl.renderer.EmphasisFillRenderer.get = function (mesh) {
- const hash = [
- mesh.scene.id,
- mesh.scene.gammaOutput ? "go" : "", // Gamma input not needed
- mesh.scene._clipsState.getHash(),
- !!mesh._geometry.normals ? "n" : "",
- mesh._geometry._state.quantized ? "cp" : "",
- mesh._state.hash
- ].join(";");
- let renderer = ghostFillRenderers[hash];
- if (!renderer) {
- renderer = new xeogl.renderer.EmphasisFillRenderer(hash, mesh);
- ghostFillRenderers[hash] = renderer;
- xeogl.stats.memory.programs++;
+EmphasisFillRenderer.prototype.put = function () {
+ if (--this._useCount === 0) {
+ ids.removeItem(this.id);
+ if (this._program) {
+ this._program.destroy();
}
- renderer._useCount++;
- return renderer;
- };
+ delete ghostFillRenderers[this._hash];
+ stats.memory.programs--;
+ }
+};
- xeogl.renderer.EmphasisFillRenderer.prototype.put = function () {
- if (--this._useCount === 0) {
- ids.removeItem(this.id);
- if (this._program) {
- this._program.destroy();
- }
- delete ghostFillRenderers[this._hash];
- xeogl.stats.memory.programs--;
- }
- };
+EmphasisFillRenderer.prototype.webglContextRestored = function () {
+ this._program = null;
+};
- xeogl.renderer.EmphasisFillRenderer.prototype.webglContextRestored = function () {
- this._program = null;
- };
-
- xeogl.renderer.EmphasisFillRenderer.prototype.drawMesh = function (frame, mesh, mode) {
- if (!this._program) {
- this._allocate(mesh);
- }
- const scene = this._scene;
- const gl = scene.canvas.gl;
- const materialState = mode === 0 ? mesh._ghostMaterial._state : (mode === 1 ? mesh._highlightMaterial._state : mesh._selectedMaterial._state);
- const meshState = mesh._state;
- const geometryState = mesh._geometry._state;
- if (frame.lastProgramId !== this._program.id) {
- frame.lastProgramId = this._program.id;
- this._bindProgram(frame);
- }
- if (materialState.id !== this._lastMaterialId) {
- const fillColor = materialState.fillColor;
- const backfaces = materialState.backfaces;
- if (frame.backfaces !== backfaces) {
- if (backfaces) {
- gl.disable(gl.CULL_FACE);
- } else {
- gl.enable(gl.CULL_FACE);
- }
- frame.backfaces = backfaces;
+EmphasisFillRenderer.prototype.drawMesh = function (frame, mesh, mode) {
+ if (!this._program) {
+ this._allocate(mesh);
+ }
+ const scene = this._scene;
+ const gl = scene.canvas.gl;
+ const materialState = mode === 0 ? mesh._ghostMaterial._state : (mode === 1 ? mesh._highlightMaterial._state : mesh._selectedMaterial._state);
+ const meshState = mesh._state;
+ const geometryState = mesh._geometry._state;
+ if (frame.lastProgramId !== this._program.id) {
+ frame.lastProgramId = this._program.id;
+ this._bindProgram(frame);
+ }
+ if (materialState.id !== this._lastMaterialId) {
+ const fillColor = materialState.fillColor;
+ const backfaces = materialState.backfaces;
+ if (frame.backfaces !== backfaces) {
+ if (backfaces) {
+ gl.disable(gl.CULL_FACE);
+ } else {
+ gl.enable(gl.CULL_FACE);
}
- gl.uniform4f(this._uFillColor, fillColor[0], fillColor[1], fillColor[2], materialState.fillAlpha);
- this._lastMaterialId = materialState.id;
+ frame.backfaces = backfaces;
}
- gl.uniformMatrix4fv(this._uModelMatrix, gl.FALSE, mesh.worldMatrix);
- if (this._uModelNormalMatrix) {
- gl.uniformMatrix4fv(this._uModelNormalMatrix, gl.FALSE, mesh.worldNormalMatrix);
+ gl.uniform4f(this._uFillColor, fillColor[0], fillColor[1], fillColor[2], materialState.fillAlpha);
+ this._lastMaterialId = materialState.id;
+ }
+ gl.uniformMatrix4fv(this._uModelMatrix, gl.FALSE, mesh.worldMatrix);
+ if (this._uModelNormalMatrix) {
+ gl.uniformMatrix4fv(this._uModelNormalMatrix, gl.FALSE, mesh.worldNormalMatrix);
+ }
+ if (this._uClippable) {
+ gl.uniform1i(this._uClippable, meshState.clippable);
+ }
+ if (geometryState.combined) {
+ const vertexBufs = mesh._geometry._getVertexBufs();
+ if (vertexBufs.id !== this._lastVertexBufsId) {
+ if (vertexBufs.positionsBuf && this._aPosition) {
+ this._aPosition.bindArrayBuffer(vertexBufs.positionsBuf, vertexBufs.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
+ frame.bindArray++;
+ }
+ if (vertexBufs.normalsBuf && this._aNormal) {
+ this._aNormal.bindArrayBuffer(vertexBufs.normalsBuf, vertexBufs.quantized ? gl.BYTE : gl.FLOAT);
+ frame.bindArray++;
+ }
+ this._lastVertexBufsId = vertexBufs.id;
}
- if (this._uClippable) {
- gl.uniform1i(this._uClippable, meshState.clippable);
+ }
+ // Bind VBOs
+ if (geometryState.id !== this._lastGeometryId) {
+ if (this._uPositionsDecodeMatrix) {
+ gl.uniformMatrix4fv(this._uPositionsDecodeMatrix, false, geometryState.positionsDecodeMatrix);
}
- if (geometryState.combined) {
- const vertexBufs = mesh._geometry._getVertexBufs();
- if (vertexBufs.id !== this._lastVertexBufsId) {
- if (vertexBufs.positionsBuf && this._aPosition) {
- this._aPosition.bindArrayBuffer(vertexBufs.positionsBuf, vertexBufs.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
- frame.bindArray++;
- }
- if (vertexBufs.normalsBuf && this._aNormal) {
- this._aNormal.bindArrayBuffer(vertexBufs.normalsBuf, vertexBufs.quantized ? gl.BYTE : gl.FLOAT);
- frame.bindArray++;
- }
- this._lastVertexBufsId = vertexBufs.id;
- }
+ if (this._uUVDecodeMatrix) {
+ gl.uniformMatrix3fv(this._uUVDecodeMatrix, false, geometryState.uvDecodeMatrix);
}
- // Bind VBOs
- if (geometryState.id !== this._lastGeometryId) {
- if (this._uPositionsDecodeMatrix) {
- gl.uniformMatrix4fv(this._uPositionsDecodeMatrix, false, geometryState.positionsDecodeMatrix);
- }
- if (this._uUVDecodeMatrix) {
- gl.uniformMatrix3fv(this._uUVDecodeMatrix, false, geometryState.uvDecodeMatrix);
+ if (geometryState.combined) { // VBOs were bound by the VertexBufs logic above
+ if (geometryState.indicesBufCombined) {
+ geometryState.indicesBufCombined.bind();
+ frame.bindArray++;
}
- if (geometryState.combined) { // VBOs were bound by the VertexBufs logic above
- if (geometryState.indicesBufCombined) {
- geometryState.indicesBufCombined.bind();
- frame.bindArray++;
- }
- } else {
- if (this._aPosition) {
- this._aPosition.bindArrayBuffer(geometryState.positionsBuf, geometryState.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
- frame.bindArray++;
- }
- if (this._aNormal) {
- this._aNormal.bindArrayBuffer(geometryState.normalsBuf, geometryState.quantized ? gl.BYTE : gl.FLOAT);
- frame.bindArray++;
- }
- if (geometryState.indicesBuf) {
- geometryState.indicesBuf.bind();
- frame.bindArray++;
- // gl.drawElements(geometryState.primitive, geometryState.indicesBuf.numItems, geometryState.indicesBuf.itemType, 0);
- // frame.drawElements++;
- } else if (geometryState.positions) {
- // gl.drawArrays(gl.TRIANGLES, 0, geometryState.positions.numItems);
- // frame.drawArrays++;
- }
+ } else {
+ if (this._aPosition) {
+ this._aPosition.bindArrayBuffer(geometryState.positionsBuf, geometryState.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
+ frame.bindArray++;
}
- this._lastGeometryId = geometryState.id;
- }
- // Draw (indices bound in prev step)
- if (geometryState.combined) {
- if (geometryState.indicesBufCombined) { // Geometry indices into portion of uber-array
- gl.drawElements(geometryState.primitive, geometryState.indicesBufCombined.numItems, geometryState.indicesBufCombined.itemType, 0);
- frame.drawElements++;
- } else {
- // TODO: drawArrays() with VertexBufs positions
+ if (this._aNormal) {
+ this._aNormal.bindArrayBuffer(geometryState.normalsBuf, geometryState.quantized ? gl.BYTE : gl.FLOAT);
+ frame.bindArray++;
}
- } else {
if (geometryState.indicesBuf) {
- gl.drawElements(geometryState.primitive, geometryState.indicesBuf.numItems, geometryState.indicesBuf.itemType, 0);
- frame.drawElements++;
+ geometryState.indicesBuf.bind();
+ frame.bindArray++;
+ // gl.drawElements(geometryState.primitive, geometryState.indicesBuf.numItems, geometryState.indicesBuf.itemType, 0);
+ // frame.drawElements++;
} else if (geometryState.positions) {
- gl.drawArrays(gl.TRIANGLES, 0, geometryState.positions.numItems);
- frame.drawArrays++;
+ // gl.drawArrays(gl.TRIANGLES, 0, geometryState.positions.numItems);
+ // frame.drawArrays++;
}
}
- };
-
- xeogl.renderer.EmphasisFillRenderer.prototype._allocate = function (mesh) {
- const lightsState = mesh.scene._lightsState;
- const clipsState = mesh.scene._clipsState;
- const gl = mesh.scene.canvas.gl;
- this._program = new xeogl.renderer.Program(gl, this._shaderSource);
- if (this._program.errors) {
- this.errors = this._program.errors;
- return;
+ this._lastGeometryId = geometryState.id;
+ }
+ // Draw (indices bound in prev step)
+ if (geometryState.combined) {
+ if (geometryState.indicesBufCombined) { // Geometry indices into portion of uber-array
+ gl.drawElements(geometryState.primitive, geometryState.indicesBufCombined.numItems, geometryState.indicesBufCombined.itemType, 0);
+ frame.drawElements++;
+ } else {
+ // TODO: drawArrays() with VertexBufs positions
}
- const program = this._program;
- this._uPositionsDecodeMatrix = program.getLocation("positionsDecodeMatrix");
- this._uModelMatrix = program.getLocation("modelMatrix");
- this._uModelNormalMatrix = program.getLocation("modelNormalMatrix");
- this._uViewMatrix = program.getLocation("viewMatrix");
- this._uViewNormalMatrix = program.getLocation("viewNormalMatrix");
- this._uProjMatrix = program.getLocation("projMatrix");
- this._uLightAmbient = [];
- this._uLightColor = [];
- this._uLightDir = [];
- this._uLightPos = [];
- this._uLightAttenuation = [];
- for (var i = 0, len = lightsState.lights.length; i < len; i++) {
- const light = lightsState.lights[i];
- switch (light.type) {
- case "ambient":
- this._uLightAmbient[i] = program.getLocation("lightAmbient");
- break;
- case "dir":
- this._uLightColor[i] = program.getLocation("lightColor" + i);
- this._uLightPos[i] = null;
- this._uLightDir[i] = program.getLocation("lightDir" + i);
- break;
- case "point":
- this._uLightColor[i] = program.getLocation("lightColor" + i);
- this._uLightPos[i] = program.getLocation("lightPos" + i);
- this._uLightDir[i] = null;
- this._uLightAttenuation[i] = program.getLocation("lightAttenuation" + i);
- break;
- }
+ } else {
+ if (geometryState.indicesBuf) {
+ gl.drawElements(geometryState.primitive, geometryState.indicesBuf.numItems, geometryState.indicesBuf.itemType, 0);
+ frame.drawElements++;
+ } else if (geometryState.positions) {
+ gl.drawArrays(gl.TRIANGLES, 0, geometryState.positions.numItems);
+ frame.drawArrays++;
}
- this._uClips = [];
- for (var i = 0, len = clipsState.clips.length; i < len; i++) {
- this._uClips.push({
- active: program.getLocation("clipActive" + i),
- pos: program.getLocation("clipPos" + i),
- dir: program.getLocation("clipDir" + i)
- });
+ }
+};
+
+EmphasisFillRenderer.prototype._allocate = function (mesh) {
+ const lightsState = mesh.scene._lightsState;
+ const clipsState = mesh.scene._clipsState;
+ const gl = mesh.scene.canvas.gl;
+ this._program = new Program(gl, this._shaderSource);
+ if (this._program.errors) {
+ this.errors = this._program.errors;
+ return;
+ }
+ const program = this._program;
+ this._uPositionsDecodeMatrix = program.getLocation("positionsDecodeMatrix");
+ this._uModelMatrix = program.getLocation("modelMatrix");
+ this._uModelNormalMatrix = program.getLocation("modelNormalMatrix");
+ this._uViewMatrix = program.getLocation("viewMatrix");
+ this._uViewNormalMatrix = program.getLocation("viewNormalMatrix");
+ this._uProjMatrix = program.getLocation("projMatrix");
+ this._uLightAmbient = [];
+ this._uLightColor = [];
+ this._uLightDir = [];
+ this._uLightPos = [];
+ this._uLightAttenuation = [];
+ for (var i = 0, len = lightsState.lights.length; i < len; i++) {
+ const light = lightsState.lights[i];
+ switch (light.type) {
+ case "ambient":
+ this._uLightAmbient[i] = program.getLocation("lightAmbient");
+ break;
+ case "dir":
+ this._uLightColor[i] = program.getLocation("lightColor" + i);
+ this._uLightPos[i] = null;
+ this._uLightDir[i] = program.getLocation("lightDir" + i);
+ break;
+ case "point":
+ this._uLightColor[i] = program.getLocation("lightColor" + i);
+ this._uLightPos[i] = program.getLocation("lightPos" + i);
+ this._uLightDir[i] = null;
+ this._uLightAttenuation[i] = program.getLocation("lightAttenuation" + i);
+ break;
}
- this._uFillColor = program.getLocation("fillColor");
- this._aPosition = program.getAttribute("position");
- this._aNormal = program.getAttribute("normal");
- this._uClippable = program.getLocation("clippable");
- this._uGammaFactor = program.getLocation("gammaFactor");
- this._lastMaterialId = null;
- this._lastVertexBufsId = null;
- this._lastGeometryId = null;
- };
+ }
+ this._uClips = [];
+ for (var i = 0, len = clipsState.clips.length; i < len; i++) {
+ this._uClips.push({
+ active: program.getLocation("clipActive" + i),
+ pos: program.getLocation("clipPos" + i),
+ dir: program.getLocation("clipDir" + i)
+ });
+ }
+ this._uFillColor = program.getLocation("fillColor");
+ this._aPosition = program.getAttribute("position");
+ this._aNormal = program.getAttribute("normal");
+ this._uClippable = program.getLocation("clippable");
+ this._uGammaFactor = program.getLocation("gammaFactor");
+ this._lastMaterialId = null;
+ this._lastVertexBufsId = null;
+ this._lastGeometryId = null;
+};
- xeogl.renderer.EmphasisFillRenderer.prototype._bindProgram = function (frame) {
- const scene = this._scene;
- const gl = scene.canvas.gl;
- const clipsState = scene._clipsState;
- const lightsState = scene._lightsState;
- const camera = scene.camera;
- const cameraState = camera._state;
- let light;
- const program = this._program;
- program.bind();
- frame.useProgram++;
- frame.textureUnit = 0;
- this._lastMaterialId = null;
- this._lastVertexBufsId = null;
- this._lastGeometryId = null;
- this._lastIndicesBufId = null;
- gl.uniformMatrix4fv(this._uViewMatrix, false, cameraState.matrix);
- gl.uniformMatrix4fv(this._uViewNormalMatrix, false, cameraState.normalMatrix);
- gl.uniformMatrix4fv(this._uProjMatrix, false, camera.project._state.matrix);
- for (var i = 0, len = lightsState.lights.length; i < len; i++) {
- light = lightsState.lights[i];
- if (this._uLightAmbient[i]) {
- gl.uniform4f(this._uLightAmbient[i], light.color[0], light.color[1], light.color[2], light.intensity);
- } else {
- if (this._uLightColor[i]) {
- gl.uniform4f(this._uLightColor[i], light.color[0], light.color[1], light.color[2], light.intensity);
- }
- if (this._uLightPos[i]) {
- gl.uniform3fv(this._uLightPos[i], light.pos);
- if (this._uLightAttenuation[i]) {
- gl.uniform1f(this._uLightAttenuation[i], light.attenuation);
- }
- }
- if (this._uLightDir[i]) {
- gl.uniform3fv(this._uLightDir[i], light.dir);
- }
+EmphasisFillRenderer.prototype._bindProgram = function (frame) {
+ const scene = this._scene;
+ const gl = scene.canvas.gl;
+ const clipsState = scene._clipsState;
+ const lightsState = scene._lightsState;
+ const camera = scene.camera;
+ const cameraState = camera._state;
+ let light;
+ const program = this._program;
+ program.bind();
+ frame.useProgram++;
+ frame.textureUnit = 0;
+ this._lastMaterialId = null;
+ this._lastVertexBufsId = null;
+ this._lastGeometryId = null;
+ this._lastIndicesBufId = null;
+ gl.uniformMatrix4fv(this._uViewMatrix, false, cameraState.matrix);
+ gl.uniformMatrix4fv(this._uViewNormalMatrix, false, cameraState.normalMatrix);
+ gl.uniformMatrix4fv(this._uProjMatrix, false, camera.project._state.matrix);
+ for (var i = 0, len = lightsState.lights.length; i < len; i++) {
+ light = lightsState.lights[i];
+ if (this._uLightAmbient[i]) {
+ gl.uniform4f(this._uLightAmbient[i], light.color[0], light.color[1], light.color[2], light.intensity);
+ } else {
+ if (this._uLightColor[i]) {
+ gl.uniform4f(this._uLightColor[i], light.color[0], light.color[1], light.color[2], light.intensity);
}
- }
- if (clipsState.clips.length > 0) {
- const clips = scene._clipsState.clips;
- let clipUniforms;
- let uClipActive;
- let clip;
- let uClipPos;
- let uClipDir;
- for (var i = 0, len = this._uClips.length; i < len; i++) {
- clipUniforms = this._uClips[i];
- uClipActive = clipUniforms.active;
- clip = clips[i];
- if (uClipActive) {
- gl.uniform1i(uClipActive, clip.active);
- }
- uClipPos = clipUniforms.pos;
- if (uClipPos) {
- gl.uniform3fv(clipUniforms.pos, clip.pos);
- }
- uClipDir = clipUniforms.dir;
- if (uClipDir) {
- gl.uniform3fv(clipUniforms.dir, clip.dir);
+ if (this._uLightPos[i]) {
+ gl.uniform3fv(this._uLightPos[i], light.pos);
+ if (this._uLightAttenuation[i]) {
+ gl.uniform1f(this._uLightAttenuation[i], light.attenuation);
}
}
+ if (this._uLightDir[i]) {
+ gl.uniform3fv(this._uLightDir[i], light.dir);
+ }
}
- if (this._uGammaFactor) {
- gl.uniform1f(this._uGammaFactor, scene.gammaFactor);
+ }
+ if (clipsState.clips.length > 0) {
+ const clips = scene._clipsState.clips;
+ let clipUniforms;
+ let uClipActive;
+ let clip;
+ let uClipPos;
+ let uClipDir;
+ for (var i = 0, len = this._uClips.length; i < len; i++) {
+ clipUniforms = this._uClips[i];
+ uClipActive = clipUniforms.active;
+ clip = clips[i];
+ if (uClipActive) {
+ gl.uniform1i(uClipActive, clip.active);
+ }
+ uClipPos = clipUniforms.pos;
+ if (uClipPos) {
+ gl.uniform3fv(clipUniforms.pos, clip.pos);
+ }
+ uClipDir = clipUniforms.dir;
+ if (uClipDir) {
+ gl.uniform3fv(clipUniforms.dir, clip.dir);
+ }
}
- };
+ }
+ if (this._uGammaFactor) {
+ gl.uniform1f(this._uGammaFactor, scene.gammaFactor);
+ }
+};
-})();
+export{EmphasisFillRenderer};
diff --git a/src/renderer/emphasis/emphasisFillShaderSource.js b/src/renderer/emphasis/emphasisFillShaderSource.js
index b0246451d..2683c215a 100644
--- a/src/renderer/emphasis/emphasisFillShaderSource.js
+++ b/src/renderer/emphasis/emphasisFillShaderSource.js
@@ -2,229 +2,228 @@
* @author xeolabs / https://github.com/xeolabs
*/
-(function () {
-
- "use strict";
-
- xeogl.renderer.EmphasisFillShaderSource = function (mesh) {
+class EmphasisFillShaderSource {
+ constructor(mesh) {
this.vertex = buildVertex(mesh);
this.fragment = buildFragment(mesh);
- };
+ }
+}
- function buildVertex(mesh) {
- const scene = mesh.scene;
- const lightsState = scene._lightsState;
- const normals = hasNormals(mesh);
- const clipping = scene._clipsState.clips.length > 0;
- const quantizedGeometry = !!mesh._geometry._state.quantized;
- const billboard = mesh._state.billboard;
- const stationary = mesh._state.stationary;
- const src = [];
- let i;
- let len;
- let light;
- src.push("// EmphasisFillShaderSource vertex shader");
- src.push("attribute vec3 position;");
- src.push("uniform mat4 modelMatrix;");
- src.push("uniform mat4 viewMatrix;");
- src.push("uniform mat4 projMatrix;");
- src.push("uniform vec4 colorize;");
- if (quantizedGeometry) {
- src.push("uniform mat4 positionsDecodeMatrix;");
- }
- if (clipping) {
- src.push("varying vec4 vWorldPosition;");
- }
- src.push("uniform vec4 lightAmbient;");
- src.push("uniform vec4 fillColor;");
- if (normals) {
- src.push("attribute vec3 normal;");
- src.push("uniform mat4 modelNormalMatrix;");
- src.push("uniform mat4 viewNormalMatrix;");
- for (i = 0, len = lightsState.lights.length; i < len; i++) {
- light = lightsState.lights[i];
- if (light.type === "ambient") {
- continue;
- }
- src.push("uniform vec4 lightColor" + i + ";");
- if (light.type === "dir") {
- src.push("uniform vec3 lightDir" + i + ";");
- }
- if (light.type === "point") {
- src.push("uniform vec3 lightPos" + i + ";");
- }
- if (light.type === "spot") {
- src.push("uniform vec3 lightPos" + i + ";");
- }
+function buildVertex(mesh) {
+ const scene = mesh.scene;
+ const lightsState = scene._lightsState;
+ const normals = hasNormals(mesh);
+ const clipping = scene._clipsState.clips.length > 0;
+ const quantizedGeometry = !!mesh._geometry._state.quantized;
+ const billboard = mesh._state.billboard;
+ const stationary = mesh._state.stationary;
+ const src = [];
+ let i;
+ let len;
+ let light;
+ src.push("// EmphasisFillShaderSource vertex shader");
+ src.push("attribute vec3 position;");
+ src.push("uniform mat4 modelMatrix;");
+ src.push("uniform mat4 viewMatrix;");
+ src.push("uniform mat4 projMatrix;");
+ src.push("uniform vec4 colorize;");
+ if (quantizedGeometry) {
+ src.push("uniform mat4 positionsDecodeMatrix;");
+ }
+ if (clipping) {
+ src.push("varying vec4 vWorldPosition;");
+ }
+ src.push("uniform vec4 lightAmbient;");
+ src.push("uniform vec4 fillColor;");
+ if (normals) {
+ src.push("attribute vec3 normal;");
+ src.push("uniform mat4 modelNormalMatrix;");
+ src.push("uniform mat4 viewNormalMatrix;");
+ for (i = 0, len = lightsState.lights.length; i < len; i++) {
+ light = lightsState.lights[i];
+ if (light.type === "ambient") {
+ continue;
}
- if (quantizedGeometry) {
- src.push("vec3 octDecode(vec2 oct) {");
- src.push(" vec3 v = vec3(oct.xy, 1.0 - abs(oct.x) - abs(oct.y));");
- src.push(" if (v.z < 0.0) {");
- src.push(" v.xy = (1.0 - abs(v.yx)) * vec2(v.x >= 0.0 ? 1.0 : -1.0, v.y >= 0.0 ? 1.0 : -1.0);");
- src.push(" }");
- src.push(" return normalize(v);");
- src.push("}");
+ src.push("uniform vec4 lightColor" + i + ";");
+ if (light.type === "dir") {
+ src.push("uniform vec3 lightDir" + i + ";");
}
- }
- src.push("varying vec4 vColor;");
- if (billboard === "spherical" || billboard === "cylindrical") {
- src.push("void billboard(inout mat4 mat) {");
- src.push(" mat[0][0] = 1.0;");
- src.push(" mat[0][1] = 0.0;");
- src.push(" mat[0][2] = 0.0;");
- if (billboard === "spherical") {
- src.push(" mat[1][0] = 0.0;");
- src.push(" mat[1][1] = 1.0;");
- src.push(" mat[1][2] = 0.0;");
+ if (light.type === "point") {
+ src.push("uniform vec3 lightPos" + i + ";");
}
- src.push(" mat[2][0] = 0.0;");
- src.push(" mat[2][1] = 0.0;");
- src.push(" mat[2][2] =1.0;");
- src.push("}");
- }
- src.push("void main(void) {");
- src.push("vec4 localPosition = vec4(position, 1.0); ");
- src.push("vec4 worldPosition;");
- if (quantizedGeometry) {
- src.push("localPosition = positionsDecodeMatrix * localPosition;");
- }
- if (normals) {
- if (quantizedGeometry) {
- src.push("vec4 localNormal = vec4(octDecode(normal.xy), 0.0); ");
- } else {
- src.push("vec4 localNormal = vec4(normal, 0.0); ");
+ if (light.type === "spot") {
+ src.push("uniform vec3 lightPos" + i + ";");
}
- src.push("mat4 modelNormalMatrix2 = modelNormalMatrix;");
- src.push("mat4 viewNormalMatrix2 = viewNormalMatrix;");
}
- src.push("mat4 viewMatrix2 = viewMatrix;");
- src.push("mat4 modelMatrix2 = modelMatrix;");
- if (stationary) {
- src.push("viewMatrix2[3][0] = viewMatrix2[3][1] = viewMatrix2[3][2] = 0.0;")
+ if (quantizedGeometry) {
+ src.push("vec3 octDecode(vec2 oct) {");
+ src.push(" vec3 v = vec3(oct.xy, 1.0 - abs(oct.x) - abs(oct.y));");
+ src.push(" if (v.z < 0.0) {");
+ src.push(" v.xy = (1.0 - abs(v.yx)) * vec2(v.x >= 0.0 ? 1.0 : -1.0, v.y >= 0.0 ? 1.0 : -1.0);");
+ src.push(" }");
+ src.push(" return normalize(v);");
+ src.push("}");
}
- if (billboard === "spherical" || billboard === "cylindrical") {
- src.push("mat4 modelViewMatrix = viewMatrix2 * modelMatrix2;");
- src.push("billboard(modelMatrix2);");
- src.push("billboard(viewMatrix2);");
- src.push("billboard(modelViewMatrix);");
- if (normals) {
- src.push("mat4 modelViewNormalMatrix = viewNormalMatrix2 * modelNormalMatrix2;");
- src.push("billboard(modelNormalMatrix2);");
- src.push("billboard(viewNormalMatrix2);");
- src.push("billboard(modelViewNormalMatrix);");
- }
- src.push("worldPosition = modelMatrix2 * localPosition;");
- src.push("vec4 viewPosition = modelViewMatrix * localPosition;");
+ }
+ src.push("varying vec4 vColor;");
+ if (billboard === "spherical" || billboard === "cylindrical") {
+ src.push("void billboard(inout mat4 mat) {");
+ src.push(" mat[0][0] = 1.0;");
+ src.push(" mat[0][1] = 0.0;");
+ src.push(" mat[0][2] = 0.0;");
+ if (billboard === "spherical") {
+ src.push(" mat[1][0] = 0.0;");
+ src.push(" mat[1][1] = 1.0;");
+ src.push(" mat[1][2] = 0.0;");
+ }
+ src.push(" mat[2][0] = 0.0;");
+ src.push(" mat[2][1] = 0.0;");
+ src.push(" mat[2][2] =1.0;");
+ src.push("}");
+ }
+ src.push("void main(void) {");
+ src.push("vec4 localPosition = vec4(position, 1.0); ");
+ src.push("vec4 worldPosition;");
+ if (quantizedGeometry) {
+ src.push("localPosition = positionsDecodeMatrix * localPosition;");
+ }
+ if (normals) {
+ if (quantizedGeometry) {
+ src.push("vec4 localNormal = vec4(octDecode(normal.xy), 0.0); ");
} else {
- src.push("worldPosition = modelMatrix2 * localPosition;");
- src.push("vec4 viewPosition = viewMatrix2 * worldPosition; ");
- }
- if (normals) {
- src.push("vec3 viewNormal = normalize((viewNormalMatrix2 * modelNormalMatrix2 * localNormal).xyz);");
+ src.push("vec4 localNormal = vec4(normal, 0.0); ");
}
- src.push("vec3 reflectedColor = vec3(0.0, 0.0, 0.0);");
- src.push("vec3 viewLightDir = vec3(0.0, 0.0, -1.0);");
- src.push("float lambertian = 1.0;");
+ src.push("mat4 modelNormalMatrix2 = modelNormalMatrix;");
+ src.push("mat4 viewNormalMatrix2 = viewNormalMatrix;");
+ }
+ src.push("mat4 viewMatrix2 = viewMatrix;");
+ src.push("mat4 modelMatrix2 = modelMatrix;");
+ if (stationary) {
+ src.push("viewMatrix2[3][0] = viewMatrix2[3][1] = viewMatrix2[3][2] = 0.0;")
+ }
+ if (billboard === "spherical" || billboard === "cylindrical") {
+ src.push("mat4 modelViewMatrix = viewMatrix2 * modelMatrix2;");
+ src.push("billboard(modelMatrix2);");
+ src.push("billboard(viewMatrix2);");
+ src.push("billboard(modelViewMatrix);");
if (normals) {
- for (i = 0, len = lightsState.lights.length; i < len; i++) {
- light = lightsState.lights[i];
- if (light.type === "ambient") {
- continue;
+ src.push("mat4 modelViewNormalMatrix = viewNormalMatrix2 * modelNormalMatrix2;");
+ src.push("billboard(modelNormalMatrix2);");
+ src.push("billboard(viewNormalMatrix2);");
+ src.push("billboard(modelViewNormalMatrix);");
+ }
+ src.push("worldPosition = modelMatrix2 * localPosition;");
+ src.push("vec4 viewPosition = modelViewMatrix * localPosition;");
+ } else {
+ src.push("worldPosition = modelMatrix2 * localPosition;");
+ src.push("vec4 viewPosition = viewMatrix2 * worldPosition; ");
+ }
+ if (normals) {
+ src.push("vec3 viewNormal = normalize((viewNormalMatrix2 * modelNormalMatrix2 * localNormal).xyz);");
+ }
+ src.push("vec3 reflectedColor = vec3(0.0, 0.0, 0.0);");
+ src.push("vec3 viewLightDir = vec3(0.0, 0.0, -1.0);");
+ src.push("float lambertian = 1.0;");
+ if (normals) {
+ for (i = 0, len = lightsState.lights.length; i < len; i++) {
+ light = lightsState.lights[i];
+ if (light.type === "ambient") {
+ continue;
+ }
+ if (light.type === "dir") {
+ if (light.space === "view") {
+ src.push("viewLightDir = normalize(lightDir" + i + ");");
+ } else {
+ src.push("viewLightDir = normalize((viewMatrix2 * vec4(lightDir" + i + ", 0.0)).xyz);");
}
- if (light.type === "dir") {
- if (light.space === "view") {
- src.push("viewLightDir = normalize(lightDir" + i + ");");
- } else {
- src.push("viewLightDir = normalize((viewMatrix2 * vec4(lightDir" + i + ", 0.0)).xyz);");
- }
- } else if (light.type === "point") {
- if (light.space === "view") {
- src.push("viewLightDir = normalize(lightPos" + i + " - viewPosition.xyz);");
- } else {
- src.push("viewLightDir = normalize((viewMatrix2 * vec4(lightPos" + i + ", 0.0)).xyz);");
- }
+ } else if (light.type === "point") {
+ if (light.space === "view") {
+ src.push("viewLightDir = normalize(lightPos" + i + " - viewPosition.xyz);");
} else {
- continue;
+ src.push("viewLightDir = normalize((viewMatrix2 * vec4(lightPos" + i + ", 0.0)).xyz);");
}
- src.push("lambertian = max(dot(-viewNormal, viewLightDir), 0.0);");
- src.push("reflectedColor += lambertian * (lightColor" + i + ".rgb * lightColor" + i + ".a);");
+ } else {
+ continue;
}
+ src.push("lambertian = max(dot(-viewNormal, viewLightDir), 0.0);");
+ src.push("reflectedColor += lambertian * (lightColor" + i + ".rgb * lightColor" + i + ".a);");
}
- // TODO: A blending mode for emphasis materials, to select add/multiply/mix
- //src.push("vColor = vec4((mix(reflectedColor, fillColor.rgb, 0.7)), fillColor.a);");
- src.push("vColor = vec4(reflectedColor * fillColor.rgb, fillColor.a);");
- //src.push("vColor = vec4(reflectedColor + fillColor.rgb, fillColor.a);");
- if (clipping) {
- src.push("vWorldPosition = worldPosition;");
- }
- if (mesh._geometry._state.primitiveName === "points") {
- src.push("gl_PointSize = pointSize;");
- }
- src.push(" gl_Position = projMatrix * viewPosition;");
- src.push("}");
- return src;
}
+ // TODO: A blending mode for emphasis materials, to select add/multiply/mix
+ //src.push("vColor = vec4((mix(reflectedColor, fillColor.rgb, 0.7)), fillColor.a);");
+ src.push("vColor = vec4(reflectedColor * fillColor.rgb, fillColor.a);");
+ //src.push("vColor = vec4(reflectedColor + fillColor.rgb, fillColor.a);");
+ if (clipping) {
+ src.push("vWorldPosition = worldPosition;");
+ }
+ if (mesh._geometry._state.primitiveName === "points") {
+ src.push("gl_PointSize = pointSize;");
+ }
+ src.push(" gl_Position = projMatrix * viewPosition;");
+ src.push("}");
+ return src;
+}
- function hasNormals(mesh) {
- const primitive = mesh._geometry._state.primitiveName;
- if ((mesh._geometry._state.autoVertexNormals || mesh._geometry._state.normals) && (primitive === "triangles" || primitive === "triangle-strip" || primitive === "triangle-fan")) {
- return true;
- }
- return false;
+function hasNormals(mesh) {
+ const primitive = mesh._geometry._state.primitiveName;
+ if ((mesh._geometry._state.autoVertexNormals || mesh._geometry._state.normals) && (primitive === "triangles" || primitive === "triangle-strip" || primitive === "triangle-fan")) {
+ return true;
}
+ return false;
+}
- function buildFragment(mesh) {
- const clipsState = mesh.scene._clipsState;
- const gammaOutput = mesh.scene.gammaOutput;
- const clipping = clipsState.clips.length > 0;
- let i;
- let len;
- const src = [];
- src.push("// Lambertian drawing fragment shader");
- src.push("precision lowp float;");
- if (gammaOutput) {
- src.push("uniform float gammaFactor;");
- src.push("vec4 linearToGamma( in vec4 value, in float gammaFactor ) {");
- src.push(" return vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );");
- src.push("}");
- }
- if (clipping) {
- src.push("varying vec4 vWorldPosition;");
- src.push("uniform bool clippable;");
- for (i = 0, len = clipsState.clips.length; i < len; i++) {
- src.push("uniform bool clipActive" + i + ";");
- src.push("uniform vec3 clipPos" + i + ";");
- src.push("uniform vec3 clipDir" + i + ";");
- }
- }
- src.push("varying vec4 vColor;");
- src.push("void main(void) {");
- if (clipping) {
- src.push("if (clippable) {");
- src.push(" float dist = 0.0;");
- for (i = 0, len = clipsState.clips.length; i < len; i++) {
- src.push("if (clipActive" + i + ") {");
- src.push(" dist += clamp(dot(-clipDir" + i + ".xyz, vWorldPosition.xyz - clipPos" + i + ".xyz), 0.0, 1000.0);");
- src.push("}");
- }
- src.push(" if (dist > 0.0) { discard; }");
- src.push("}");
+function buildFragment(mesh) {
+ const clipsState = mesh.scene._clipsState;
+ const gammaOutput = mesh.scene.gammaOutput;
+ const clipping = clipsState.clips.length > 0;
+ let i;
+ let len;
+ const src = [];
+ src.push("// Lambertian drawing fragment shader");
+ src.push("precision lowp float;");
+ if (gammaOutput) {
+ src.push("uniform float gammaFactor;");
+ src.push("vec4 linearToGamma( in vec4 value, in float gammaFactor ) {");
+ src.push(" return vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );");
+ src.push("}");
+ }
+ if (clipping) {
+ src.push("varying vec4 vWorldPosition;");
+ src.push("uniform bool clippable;");
+ for (i = 0, len = clipsState.clips.length; i < len; i++) {
+ src.push("uniform bool clipActive" + i + ";");
+ src.push("uniform vec3 clipPos" + i + ";");
+ src.push("uniform vec3 clipDir" + i + ";");
}
- if (mesh._geometry._state.primitiveName === "points") {
- src.push("vec2 cxy = 2.0 * gl_PointCoord - 1.0;");
- src.push("float r = dot(cxy, cxy);");
- src.push("if (r > 1.0) {");
- src.push(" discard;");
+ }
+ src.push("varying vec4 vColor;");
+ src.push("void main(void) {");
+ if (clipping) {
+ src.push("if (clippable) {");
+ src.push(" float dist = 0.0;");
+ for (i = 0, len = clipsState.clips.length; i < len; i++) {
+ src.push("if (clipActive" + i + ") {");
+ src.push(" dist += clamp(dot(-clipDir" + i + ".xyz, vWorldPosition.xyz - clipPos" + i + ".xyz), 0.0, 1000.0);");
src.push("}");
}
- src.push("gl_FragColor = vColor;");
- if (gammaOutput) {
- src.push("gl_FragColor = linearToGamma(vColor, gammaFactor);");
- } else {
- src.push("gl_FragColor = vColor;");
- }
+ src.push(" if (dist > 0.0) { discard; }");
+ src.push("}");
+ }
+ if (mesh._geometry._state.primitiveName === "points") {
+ src.push("vec2 cxy = 2.0 * gl_PointCoord - 1.0;");
+ src.push("float r = dot(cxy, cxy);");
+ src.push("if (r > 1.0) {");
+ src.push(" discard;");
src.push("}");
- return src;
}
-})();
\ No newline at end of file
+ src.push("gl_FragColor = vColor;");
+ if (gammaOutput) {
+ src.push("gl_FragColor = linearToGamma(vColor, gammaFactor);");
+ } else {
+ src.push("gl_FragColor = vColor;");
+ }
+ src.push("}");
+ return src;
+}
+
+export{EmphasisFillShaderSource};
\ No newline at end of file
diff --git a/src/renderer/emphasis/emphasisVerticesRenderer.js b/src/renderer/emphasis/emphasisVerticesRenderer.js
index f2bebd5b3..3cb72f651 100644
--- a/src/renderer/emphasis/emphasisVerticesRenderer.js
+++ b/src/renderer/emphasis/emphasisVerticesRenderer.js
@@ -2,226 +2,227 @@
* @author xeolabs / https://github.com/xeolabs
*/
-(function () {
+import {Map} from "../../utils/map.js";
+import {EmphasisVerticesShaderSource} from "./emphasisVerticesShaderSource.js";
+import {Program} from "../program.js";
+import {stats} from "../../stats.js";
- "use strict";
+const ids = new Map({});
- const ids = new xeogl.utils.Map({});
+const EmphasisVerticesRenderer = function (hash, mesh) {
+ this.id = ids.addItem({});
+ this._hash = hash;
+ this._scene = mesh.scene;
+ this._shaderSource = new EmphasisVerticesShaderSource(mesh);
+ this._allocate(mesh);
+};
- xeogl.renderer.EmphasisVerticesRenderer = function (hash, mesh) {
- this.id = ids.addItem({});
- this._hash = hash;
- this._scene = mesh.scene;
- this._shaderSource = new xeogl.renderer.EmphasisVerticesShaderSource(mesh);
- this._allocate(mesh);
- };
-
- const renderers = {};
+const renderers = {};
- xeogl.renderer.EmphasisVerticesRenderer.get = function (mesh) {
- const hash = [
- mesh.scene.id,
- mesh.scene.gammaOutput ? "go" : "",
- mesh.scene._clipsState.getHash(),
- mesh._geometry._state.quantized ? "cp" : "",
- mesh._state.hash
- ].join(";");
- let renderer = renderers[hash];
- if (!renderer) {
- renderer = new xeogl.renderer.EmphasisVerticesRenderer(hash, mesh);
- renderers[hash] = renderer;
- xeogl.stats.memory.programs++;
- }
- renderer._useCount++;
- return renderer;
- };
+EmphasisVerticesRenderer.get = function (mesh) {
+ const hash = [
+ mesh.scene.id,
+ mesh.scene.gammaOutput ? "go" : "",
+ mesh.scene._clipsState.getHash(),
+ mesh._geometry._state.quantized ? "cp" : "",
+ mesh._state.hash
+ ].join(";");
+ let renderer = renderers[hash];
+ if (!renderer) {
+ renderer = new EmphasisVerticesRenderer(hash, mesh);
+ renderers[hash] = renderer;
+ stats.memory.programs++;
+ }
+ renderer._useCount++;
+ return renderer;
+};
- xeogl.renderer.EmphasisVerticesRenderer.prototype.put = function () {
- if (--this._useCount === 0) {
- this._scene.off(this._onWebglcontextrestored);
- ids.removeItem(this.id);
- if (this._program) {
- this._program.destroy();
- }
- delete renderers[this._hash];
- xeogl.stats.memory.programs--;
+EmphasisVerticesRenderer.prototype.put = function () {
+ if (--this._useCount === 0) {
+ this._scene.off(this._onWebglcontextrestored);
+ ids.removeItem(this.id);
+ if (this._program) {
+ this._program.destroy();
}
- };
+ delete renderers[this._hash];
+ stats.memory.programs--;
+ }
+};
- xeogl.renderer.EmphasisVerticesRenderer.prototype.webglContextRestored = function () {
- this._program = null;
- };
+EmphasisVerticesRenderer.prototype.webglContextRestored = function () {
+ this._program = null;
+};
- xeogl.renderer.EmphasisVerticesRenderer.prototype.drawMesh = function (frame, mesh, mode) {
- if (!this._program) {
- this._allocate(mesh);
- }
- const scene = this._scene;
- const gl = scene.canvas.gl;
- const materialState = mode === 0 ? mesh._ghostMaterial._state : (mode === 1 ? mesh._highlightMaterial._state : mesh._selectedMaterial._state);
- const meshState = mesh._state;
- const geometryState = mesh._geometry._state;
- if (frame.lastProgramId !== this._program.id) {
- frame.lastProgramId = this._program.id;
- this._bindProgram(frame, mesh);
- }
- if (materialState.id !== this._lastMaterialId) {
- const backfaces = materialState.backfaces;
- if (frame.backfaces !== backfaces) {
- if (backfaces) {
- gl.disable(gl.CULL_FACE);
- } else {
- gl.enable(gl.CULL_FACE);
- }
- frame.backfaces = backfaces;
- }
- if (this._uVertexSize) { // TODO: cache
- gl.uniform1f(this._uVertexSize, materialState.vertexSize);
- }
- if (this._uVertexColor) {
- const vertexColor = materialState.vertexColor;
- const vertexAlpha = materialState.vertexAlpha;
- gl.uniform4f(this._uVertexColor, vertexColor[0], vertexColor[1], vertexColor[2], vertexAlpha);
+EmphasisVerticesRenderer.prototype.drawMesh = function (frame, mesh, mode) {
+ if (!this._program) {
+ this._allocate(mesh);
+ }
+ const scene = this._scene;
+ const gl = scene.canvas.gl;
+ const materialState = mode === 0 ? mesh._ghostMaterial._state : (mode === 1 ? mesh._highlightMaterial._state : mesh._selectedMaterial._state);
+ const meshState = mesh._state;
+ const geometryState = mesh._geometry._state;
+ if (frame.lastProgramId !== this._program.id) {
+ frame.lastProgramId = this._program.id;
+ this._bindProgram(frame, mesh);
+ }
+ if (materialState.id !== this._lastMaterialId) {
+ const backfaces = materialState.backfaces;
+ if (frame.backfaces !== backfaces) {
+ if (backfaces) {
+ gl.disable(gl.CULL_FACE);
+ } else {
+ gl.enable(gl.CULL_FACE);
}
- this._lastMaterialId = materialState.id;
+ frame.backfaces = backfaces;
}
- gl.uniformMatrix4fv(this._uModelMatrix, gl.FALSE, mesh.worldMatrix);
- if (this._uModelNormalMatrix) {
- gl.uniformMatrix4fv(this._uModelNormalMatrix, gl.FALSE, mesh.worldNormalMatrix);
+ if (this._uVertexSize) { // TODO: cache
+ gl.uniform1f(this._uVertexSize, materialState.vertexSize);
}
- if (this._uClippable) {
- gl.uniform1i(this._uClippable, meshState.clippable);
+ if (this._uVertexColor) {
+ const vertexColor = materialState.vertexColor;
+ const vertexAlpha = materialState.vertexAlpha;
+ gl.uniform4f(this._uVertexColor, vertexColor[0], vertexColor[1], vertexColor[2], vertexAlpha);
}
- if (geometryState.combined) {
- const vertexBufs = mesh._geometry._getVertexBufs();
- if (vertexBufs.id !== this._lastVertexBufsId) {
- if (vertexBufs.positionsBuf && this._aPosition) {
- this._aPosition.bindArrayBuffer(vertexBufs.positionsBuf, vertexBufs.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
- frame.bindArray++;
- }
- this._lastVertexBufsId = vertexBufs.id;
+ this._lastMaterialId = materialState.id;
+ }
+ gl.uniformMatrix4fv(this._uModelMatrix, gl.FALSE, mesh.worldMatrix);
+ if (this._uModelNormalMatrix) {
+ gl.uniformMatrix4fv(this._uModelNormalMatrix, gl.FALSE, mesh.worldNormalMatrix);
+ }
+ if (this._uClippable) {
+ gl.uniform1i(this._uClippable, meshState.clippable);
+ }
+ if (geometryState.combined) {
+ const vertexBufs = mesh._geometry._getVertexBufs();
+ if (vertexBufs.id !== this._lastVertexBufsId) {
+ if (vertexBufs.positionsBuf && this._aPosition) {
+ this._aPosition.bindArrayBuffer(vertexBufs.positionsBuf, vertexBufs.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
+ frame.bindArray++;
}
+ this._lastVertexBufsId = vertexBufs.id;
}
- // Bind VBOs
- if (geometryState.id !== this._lastGeometryId) {
- if (this._uPositionsDecodeMatrix) {
- gl.uniformMatrix4fv(this._uPositionsDecodeMatrix, false, geometryState.positionsDecodeMatrix);
- }
- if (geometryState.combined) { // VBOs were bound by the VertexBufs logic above
- if (geometryState.indicesBufCombined) {
- geometryState.indicesBufCombined.bind();
- frame.bindArray++;
- }
- } else {
- if (this._aPosition) {
- this._aPosition.bindArrayBuffer(geometryState.positionsBuf, geometryState.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
- frame.bindArray++;
- }
- if (geometryState.indicesBuf) {
- geometryState.indicesBuf.bind();
- frame.bindArray++;
- // gl.drawElements(geometryState.primitive, geometryState.indicesBuf.numItems, geometryState.indicesBuf.itemType, 0);
- // frame.drawElements++;
- } else if (geometryState.positions) {
- // gl.drawArrays(gl.TRIANGLES, 0, geometryState.positions.numItems);
- // frame.drawArrays++;
- }
- }
- this._lastGeometryId = geometryState.id;
+ }
+ // Bind VBOs
+ if (geometryState.id !== this._lastGeometryId) {
+ if (this._uPositionsDecodeMatrix) {
+ gl.uniformMatrix4fv(this._uPositionsDecodeMatrix, false, geometryState.positionsDecodeMatrix);
}
- // Draw (indices bound in prev step)
- if (geometryState.combined) {
- if (geometryState.indicesBufCombined) { // Geometry indices into portion of uber-array
- gl.drawElements(gl.POINTS, geometryState.indicesBufCombined.numItems, geometryState.indicesBufCombined.itemType, 0);
- frame.drawElements++;
- } else {
- // TODO: drawArrays() with VertexBufs positions
+ if (geometryState.combined) { // VBOs were bound by the VertexBufs logic above
+ if (geometryState.indicesBufCombined) {
+ geometryState.indicesBufCombined.bind();
+ frame.bindArray++;
}
} else {
+ if (this._aPosition) {
+ this._aPosition.bindArrayBuffer(geometryState.positionsBuf, geometryState.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
+ frame.bindArray++;
+ }
if (geometryState.indicesBuf) {
- gl.drawElements(gl.POINTS, geometryState.indicesBuf.numItems, geometryState.indicesBuf.itemType, 0);
- frame.drawElements++;
+ geometryState.indicesBuf.bind();
+ frame.bindArray++;
+ // gl.drawElements(geometryState.primitive, geometryState.indicesBuf.numItems, geometryState.indicesBuf.itemType, 0);
+ // frame.drawElements++;
} else if (geometryState.positions) {
- gl.drawArrays(gl.POINTS, 0, geometryState.positions.numItems);
- frame.drawArrays++;
+ // gl.drawArrays(gl.TRIANGLES, 0, geometryState.positions.numItems);
+ // frame.drawArrays++;
}
}
- };
-
- xeogl.renderer.EmphasisVerticesRenderer.prototype._allocate = function (mesh) {
- const clipsState = mesh.scene._clipsState;
- const gl = mesh.scene.canvas.gl;
- this._program = new xeogl.renderer.Program(gl, this._shaderSource);
- this._useCount = 0;
- if (this._program.errors) {
- this.errors = this._program.errors;
- return;
+ this._lastGeometryId = geometryState.id;
+ }
+ // Draw (indices bound in prev step)
+ if (geometryState.combined) {
+ if (geometryState.indicesBufCombined) { // Geometry indices into portion of uber-array
+ gl.drawElements(gl.POINTS, geometryState.indicesBufCombined.numItems, geometryState.indicesBufCombined.itemType, 0);
+ frame.drawElements++;
+ } else {
+ // TODO: drawArrays() with VertexBufs positions
}
- const program = this._program;
- this._uPositionsDecodeMatrix = program.getLocation("positionsDecodeMatrix");
- this._uModelMatrix = program.getLocation("modelMatrix");
- this._uViewMatrix = program.getLocation("viewMatrix");
- this._uProjMatrix = program.getLocation("projMatrix");
- this._uClips = [];
- for (let i = 0, len = clipsState.clips.length; i < len; i++) {
- this._uClips.push({
- active: program.getLocation("clipActive" + i),
- pos: program.getLocation("clipPos" + i),
- dir: program.getLocation("clipDir" + i)
- });
+ } else {
+ if (geometryState.indicesBuf) {
+ gl.drawElements(gl.POINTS, geometryState.indicesBuf.numItems, geometryState.indicesBuf.itemType, 0);
+ frame.drawElements++;
+ } else if (geometryState.positions) {
+ gl.drawArrays(gl.POINTS, 0, geometryState.positions.numItems);
+ frame.drawArrays++;
}
- this._uVertexColor = program.getLocation("vertexColor");
- this._uVertexSize = program.getLocation("vertexSize");
- this._aPosition = program.getAttribute("position");
- this._uClippable = program.getLocation("clippable");
- this._uGammaFactor = program.getLocation("gammaFactor");
- this._lastMaterialId = null;
- this._lastVertexBufsId = null;
- this._lastGeometryId = null;
- };
+ }
+};
+
+EmphasisVerticesRenderer.prototype._allocate = function (mesh) {
+ const clipsState = mesh.scene._clipsState;
+ const gl = mesh.scene.canvas.gl;
+ this._program = new Program(gl, this._shaderSource);
+ this._useCount = 0;
+ if (this._program.errors) {
+ this.errors = this._program.errors;
+ return;
+ }
+ const program = this._program;
+ this._uPositionsDecodeMatrix = program.getLocation("positionsDecodeMatrix");
+ this._uModelMatrix = program.getLocation("modelMatrix");
+ this._uViewMatrix = program.getLocation("viewMatrix");
+ this._uProjMatrix = program.getLocation("projMatrix");
+ this._uClips = [];
+ for (let i = 0, len = clipsState.clips.length; i < len; i++) {
+ this._uClips.push({
+ active: program.getLocation("clipActive" + i),
+ pos: program.getLocation("clipPos" + i),
+ dir: program.getLocation("clipDir" + i)
+ });
+ }
+ this._uVertexColor = program.getLocation("vertexColor");
+ this._uVertexSize = program.getLocation("vertexSize");
+ this._aPosition = program.getAttribute("position");
+ this._uClippable = program.getLocation("clippable");
+ this._uGammaFactor = program.getLocation("gammaFactor");
+ this._lastMaterialId = null;
+ this._lastVertexBufsId = null;
+ this._lastGeometryId = null;
+};
- xeogl.renderer.EmphasisVerticesRenderer.prototype._bindProgram = function (frame, mesh) {
- const scene = this._scene;
- const gl = scene.canvas.gl;
- const clipsState = scene._clipsState;
- const program = this._program;
- const camera = scene.camera;
- const cameraState = camera._state;
- program.bind();
- frame.useProgram++;
- frame.textureUnit = 0;
- this._lastMaterialId = null;
- this._lastVertexBufsId = null;
- this._lastGeometryId = null;
- gl.uniformMatrix4fv(this._uViewMatrix, false, cameraState.matrix);
- gl.uniformMatrix4fv(this._uProjMatrix, false, camera.project._state.matrix);
- if (clipsState.clips.length > 0) {
- const clips = clipsState.clips;
- let clipUniforms;
- let uClipActive;
- let clip;
- let uClipPos;
- let uClipDir;
- for (let i = 0, len = this._uClips.length; i < len; i++) {
- clipUniforms = this._uClips[i];
- uClipActive = clipUniforms.active;
- clip = clips[i];
- if (uClipActive) {
- gl.uniform1i(uClipActive, clip.active);
- }
- uClipPos = clipUniforms.pos;
- if (uClipPos) {
- gl.uniform3fv(clipUniforms.pos, clip.pos);
- }
- uClipDir = clipUniforms.dir;
- if (uClipDir) {
- gl.uniform3fv(clipUniforms.dir, clip.dir);
- }
+EmphasisVerticesRenderer.prototype._bindProgram = function (frame, mesh) {
+ const scene = this._scene;
+ const gl = scene.canvas.gl;
+ const clipsState = scene._clipsState;
+ const program = this._program;
+ const camera = scene.camera;
+ const cameraState = camera._state;
+ program.bind();
+ frame.useProgram++;
+ frame.textureUnit = 0;
+ this._lastMaterialId = null;
+ this._lastVertexBufsId = null;
+ this._lastGeometryId = null;
+ gl.uniformMatrix4fv(this._uViewMatrix, false, cameraState.matrix);
+ gl.uniformMatrix4fv(this._uProjMatrix, false, camera.project._state.matrix);
+ if (clipsState.clips.length > 0) {
+ const clips = clipsState.clips;
+ let clipUniforms;
+ let uClipActive;
+ let clip;
+ let uClipPos;
+ let uClipDir;
+ for (let i = 0, len = this._uClips.length; i < len; i++) {
+ clipUniforms = this._uClips[i];
+ uClipActive = clipUniforms.active;
+ clip = clips[i];
+ if (uClipActive) {
+ gl.uniform1i(uClipActive, clip.active);
+ }
+ uClipPos = clipUniforms.pos;
+ if (uClipPos) {
+ gl.uniform3fv(clipUniforms.pos, clip.pos);
+ }
+ uClipDir = clipUniforms.dir;
+ if (uClipDir) {
+ gl.uniform3fv(clipUniforms.dir, clip.dir);
}
}
- if (this._uGammaFactor) {
- gl.uniform1f(this._uGammaFactor, scene.gammaFactor);
- }
- };
+ }
+ if (this._uGammaFactor) {
+ gl.uniform1f(this._uGammaFactor, scene.gammaFactor);
+ }
+};
-})();
+export{EmphasisVerticesRenderer};
diff --git a/src/renderer/emphasis/emphasisVerticesShaderSource.js b/src/renderer/emphasis/emphasisVerticesShaderSource.js
index 1d80d35db..0e199871c 100644
--- a/src/renderer/emphasis/emphasisVerticesShaderSource.js
+++ b/src/renderer/emphasis/emphasisVerticesShaderSource.js
@@ -2,136 +2,134 @@
* @author xeolabs / https://github.com/xeolabs
*/
-(function () {
-
- "use strict";
-
- xeogl.renderer.EmphasisVerticesShaderSource = function (mesh) {
+class EmphasisVerticesShaderSource {
+ constructor(mesh) {
this.vertex = buildVertex(mesh);
this.fragment = buildFragment(mesh);
- };
+ }
+}
- function buildVertex(mesh) {
- const scene = mesh.scene;
- const clipping = scene._clipsState.clips.length > 0;
- const quantizedGeometry = !!mesh._geometry._state.quantized;
- const billboard = mesh._state.billboard;
- const stationary = mesh._state.stationary;
- const src = [];
- src.push("// Vertices drawing vertex shader");
- src.push("attribute vec3 position;");
- src.push("uniform mat4 modelMatrix;");
- src.push("uniform mat4 viewMatrix;");
- src.push("uniform mat4 projMatrix;");
- src.push("uniform vec4 vertexColor;");
- src.push("uniform float vertexSize;");
- if (quantizedGeometry) {
- src.push("uniform mat4 positionsDecodeMatrix;");
- }
- if (clipping) {
- src.push("varying vec4 vWorldPosition;");
- }
- src.push("varying vec4 vColor;");
- if (billboard === "spherical" || billboard === "cylindrical") {
- src.push("void billboard(inout mat4 mat) {");
- src.push(" mat[0][0] = 1.0;");
- src.push(" mat[0][1] = 0.0;");
- src.push(" mat[0][2] = 0.0;");
- if (billboard === "spherical") {
- src.push(" mat[1][0] = 0.0;");
- src.push(" mat[1][1] = 1.0;");
- src.push(" mat[1][2] = 0.0;");
- }
- src.push(" mat[2][0] = 0.0;");
- src.push(" mat[2][1] = 0.0;");
- src.push(" mat[2][2] =1.0;");
- src.push("}");
- }
- src.push("void main(void) {");
- src.push("vec4 localPosition = vec4(position, 1.0); ");
- src.push("vec4 worldPosition;");
- if (quantizedGeometry) {
- src.push("localPosition = positionsDecodeMatrix * localPosition;");
- }
- src.push("mat4 viewMatrix2 = viewMatrix;");
- src.push("mat4 modelMatrix2 = modelMatrix;");
- if (stationary) {
- src.push("viewMatrix2[3][0] = viewMatrix2[3][1] = viewMatrix2[3][2] = 0.0;")
- }
- if (billboard === "spherical" || billboard === "cylindrical") {
- src.push("mat4 modelViewMatrix = viewMatrix2 * modelMatrix2;");
- src.push("billboard(modelMatrix2);");
- src.push("billboard(viewMatrix2);");
- src.push("billboard(modelViewMatrix);");
- src.push("worldPosition = modelMatrix2 * localPosition;");
- src.push("vec4 viewPosition = modelViewMatrix * localPosition;");
- } else {
- src.push("worldPosition = modelMatrix2 * localPosition;");
- src.push("vec4 viewPosition = viewMatrix2 * worldPosition; ");
- }
- src.push("vColor = vertexColor;");
- src.push("gl_PointSize = vertexSize;");
- if (clipping) {
- src.push("vWorldPosition = worldPosition;");
+function buildVertex(mesh) {
+ const scene = mesh.scene;
+ const clipping = scene._clipsState.clips.length > 0;
+ const quantizedGeometry = !!mesh._geometry._state.quantized;
+ const billboard = mesh._state.billboard;
+ const stationary = mesh._state.stationary;
+ const src = [];
+ src.push("// Vertices drawing vertex shader");
+ src.push("attribute vec3 position;");
+ src.push("uniform mat4 modelMatrix;");
+ src.push("uniform mat4 viewMatrix;");
+ src.push("uniform mat4 projMatrix;");
+ src.push("uniform vec4 vertexColor;");
+ src.push("uniform float vertexSize;");
+ if (quantizedGeometry) {
+ src.push("uniform mat4 positionsDecodeMatrix;");
+ }
+ if (clipping) {
+ src.push("varying vec4 vWorldPosition;");
+ }
+ src.push("varying vec4 vColor;");
+ if (billboard === "spherical" || billboard === "cylindrical") {
+ src.push("void billboard(inout mat4 mat) {");
+ src.push(" mat[0][0] = 1.0;");
+ src.push(" mat[0][1] = 0.0;");
+ src.push(" mat[0][2] = 0.0;");
+ if (billboard === "spherical") {
+ src.push(" mat[1][0] = 0.0;");
+ src.push(" mat[1][1] = 1.0;");
+ src.push(" mat[1][2] = 0.0;");
}
- src.push(" gl_Position = projMatrix * viewPosition;");
+ src.push(" mat[2][0] = 0.0;");
+ src.push(" mat[2][1] = 0.0;");
+ src.push(" mat[2][2] =1.0;");
src.push("}");
- return src;
}
+ src.push("void main(void) {");
+ src.push("vec4 localPosition = vec4(position, 1.0); ");
+ src.push("vec4 worldPosition;");
+ if (quantizedGeometry) {
+ src.push("localPosition = positionsDecodeMatrix * localPosition;");
+ }
+ src.push("mat4 viewMatrix2 = viewMatrix;");
+ src.push("mat4 modelMatrix2 = modelMatrix;");
+ if (stationary) {
+ src.push("viewMatrix2[3][0] = viewMatrix2[3][1] = viewMatrix2[3][2] = 0.0;")
+ }
+ if (billboard === "spherical" || billboard === "cylindrical") {
+ src.push("mat4 modelViewMatrix = viewMatrix2 * modelMatrix2;");
+ src.push("billboard(modelMatrix2);");
+ src.push("billboard(viewMatrix2);");
+ src.push("billboard(modelViewMatrix);");
+ src.push("worldPosition = modelMatrix2 * localPosition;");
+ src.push("vec4 viewPosition = modelViewMatrix * localPosition;");
+ } else {
+ src.push("worldPosition = modelMatrix2 * localPosition;");
+ src.push("vec4 viewPosition = viewMatrix2 * worldPosition; ");
+ }
+ src.push("vColor = vertexColor;");
+ src.push("gl_PointSize = vertexSize;");
+ if (clipping) {
+ src.push("vWorldPosition = worldPosition;");
+ }
+ src.push(" gl_Position = projMatrix * viewPosition;");
+ src.push("}");
+ return src;
+}
- function buildFragment(mesh) {
- const clipsState = mesh.scene._clipsState;
- const gammaOutput = mesh.scene.gammaOutput;
- const clipping = clipsState.clips.length > 0;
- let i;
- let len;
- const src = [];
- src.push("// Vertices drawing fragment shader");
- src.push("precision lowp float;");
- if (gammaOutput) {
- src.push("uniform float gammaFactor;");
- src.push("vec4 linearToGamma( in vec4 value, in float gammaFactor ) {");
- src.push(" return vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );");
- src.push("}");
- }
+function buildFragment(mesh) {
+ const clipsState = mesh.scene._clipsState;
+ const gammaOutput = mesh.scene.gammaOutput;
+ const clipping = clipsState.clips.length > 0;
+ let i;
+ let len;
+ const src = [];
+ src.push("// Vertices drawing fragment shader");
+ src.push("precision lowp float;");
+ if (gammaOutput) {
+ src.push("uniform float gammaFactor;");
+ src.push("vec4 linearToGamma( in vec4 value, in float gammaFactor ) {");
+ src.push(" return vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );");
+ src.push("}");
+ }
- if (clipping) {
- src.push("varying vec4 vWorldPosition;");
- src.push("uniform bool clippable;");
- for (i = 0, len = clipsState.clips.length; i < len; i++) {
- src.push("uniform bool clipActive" + i + ";");
- src.push("uniform vec3 clipPos" + i + ";");
- src.push("uniform vec3 clipDir" + i + ";");
- }
+ if (clipping) {
+ src.push("varying vec4 vWorldPosition;");
+ src.push("uniform bool clippable;");
+ for (i = 0, len = clipsState.clips.length; i < len; i++) {
+ src.push("uniform bool clipActive" + i + ";");
+ src.push("uniform vec3 clipPos" + i + ";");
+ src.push("uniform vec3 clipDir" + i + ";");
}
- src.push("varying vec4 vColor;");
- src.push("void main(void) {");
- if (clipping) {
- src.push("if (clippable) {");
- src.push(" float dist = 0.0;");
- for (i = 0, len = clipsState.clips.length; i < len; i++) {
- src.push("if (clipActive" + i + ") {");
- src.push(" dist += clamp(dot(-clipDir" + i + ".xyz, vWorldPosition.xyz - clipPos" + i + ".xyz), 0.0, 1000.0);");
- src.push("}");
- }
- src.push(" if (dist > 0.0) { discard; }");
+ }
+ src.push("varying vec4 vColor;");
+ src.push("void main(void) {");
+ if (clipping) {
+ src.push("if (clippable) {");
+ src.push(" float dist = 0.0;");
+ for (i = 0, len = clipsState.clips.length; i < len; i++) {
+ src.push("if (clipActive" + i + ") {");
+ src.push(" dist += clamp(dot(-clipDir" + i + ".xyz, vWorldPosition.xyz - clipPos" + i + ".xyz), 0.0, 1000.0);");
src.push("}");
}
- //if (roundPoints) {
- src.push("vec2 cxy = 2.0 * gl_PointCoord - 1.0;");
- src.push("float r = dot(cxy, cxy);");
- src.push("if (r > 1.0) {");
- src.push(" discard;");
+ src.push(" if (dist > 0.0) { discard; }");
src.push("}");
- //}
+ }
+ //if (roundPoints) {
+ src.push("vec2 cxy = 2.0 * gl_PointCoord - 1.0;");
+ src.push("float r = dot(cxy, cxy);");
+ src.push("if (r > 1.0) {");
+ src.push(" discard;");
+ src.push("}");
+ //}
+ src.push("gl_FragColor = vColor;");
+ if (gammaOutput) {
+ src.push("gl_FragColor = linearToGamma(vColor, gammaFactor);");
+ } else {
src.push("gl_FragColor = vColor;");
- if (gammaOutput) {
- src.push("gl_FragColor = linearToGamma(vColor, gammaFactor);");
- } else {
- src.push("gl_FragColor = vColor;");
- }
- src.push("}");
- return src;
}
+ src.push("}");
+ return src;
+}
-})();
\ No newline at end of file
+export{EmphasisVerticesShaderSource};
\ No newline at end of file
diff --git a/src/renderer/frame.js b/src/renderer/frame.js
index cc7fc53a1..9e92c40cf 100644
--- a/src/renderer/frame.js
+++ b/src/renderer/frame.js
@@ -2,30 +2,33 @@
* @author xeolabs / https://github.com/xeolabs
*/
-xeogl.renderer = xeogl.renderer || {};
-
/**
* Rendering context for a frame.
*/
-xeogl.renderer.Frame = function () {
- this.reset();
-};
+class Frame {
+
+ constructor() {
+ this.reset();
+ }
+
+ reset() {
+ this.lastProgramId = null;
+ this.backfaces = false;
+ this.frontface = true; // true == "ccw" else "cw"
+ this.textureUnit = 0;
+ this.drawElements = 0;
+ this.drawArrays = 0;
+ this.useProgram = 0;
+ this.bindTexture = 0;
+ this.bindArray = 0;
+ this.pass = 0;
+ this.shadowViewMatrix = null;
+ this.shadowProjMatrix = null;
+ this.pickViewMatrix = null;
+ this.pickProjMatrix = null;
+ this.pickmeshIndex = 1;
+ }
+}
-xeogl.renderer.Frame.prototype.reset = function () {
- this.lastProgramId = null;
- this.backfaces = false;
- this.frontface = true; // true == "ccw" else "cw"
- this.textureUnit = 0;
- this.drawElements = 0;
- this.drawArrays = 0;
- this.useProgram = 0;
- this.bindTexture = 0;
- this.bindArray = 0;
- this.pass = 0;
- this.shadowViewMatrix = null;
- this.shadowProjMatrix = null;
- this.pickViewMatrix = null;
- this.pickProjMatrix = null;
- this.pickmeshIndex = 1;
-};
\ No newline at end of file
+export {Frame};
\ No newline at end of file
diff --git a/src/renderer/outline/outlineRenderer.js b/src/renderer/outline/outlineRenderer.js
index 9919efc85..58d17b4e8 100644
--- a/src/renderer/outline/outlineRenderer.js
+++ b/src/renderer/outline/outlineRenderer.js
@@ -2,213 +2,215 @@
* @author xeolabs / https://github.com/xeolabs
*/
-(function () {
+import {Map} from "../../utils/map.js";
+import {OutlineShaderSource} from "./outlineShaderSource.js";
+import {Program} from "../program.js";
+import {stats} from "../../stats.js";
- "use strict";
+const ids = new Map({});
- const ids = new xeogl.utils.Map({});
+const OutlineRenderer = function (hash, mesh) {
+ this._init(hash, mesh);
+};
- xeogl.renderer.OutlineRenderer = function (hash, mesh) {
- this._init(hash, mesh);
- };
+const outlineRenderers = {};
- const outlineRenderers = {};
+OutlineRenderer.get = function (mesh) {
+ const hash = [
+ mesh.scene.canvas.canvas.id,
+ mesh.scene.gammaOutput ? "go" : "", // Gamma input not needed
+ mesh.scene._clipsState.getHash(),
+ mesh._geometry._state.hash,
+ mesh._state.hash
+ ].join(";");
+ let renderer = outlineRenderers[hash];
+ if (!renderer) {
+ renderer = new OutlineRenderer(hash, mesh);
+ outlineRenderers[hash] = renderer;
+ stats.memory.programs++;
+ }
+ renderer._useCount++;
+ return renderer;
+};
- xeogl.renderer.OutlineRenderer.get = function (mesh) {
- const hash = [
- mesh.scene.canvas.canvas.id,
- mesh.scene.gammaOutput ? "go" : "", // Gamma input not needed
- mesh.scene._clipsState.getHash(),
- mesh._geometry._state.hash,
- mesh._state.hash
- ].join(";");
- let renderer = outlineRenderers[hash];
- if (!renderer) {
- renderer = new xeogl.renderer.OutlineRenderer(hash, mesh);
- outlineRenderers[hash] = renderer;
- xeogl.stats.memory.programs++;
- }
- renderer._useCount++;
- return renderer;
- };
-
- xeogl.renderer.OutlineRenderer.prototype.put = function () {
- if (--this._useCount === 0) {
- ids.removeItem(this.id);
- this._program.destroy();
- delete outlineRenderers[this._hash];
- xeogl.stats.memory.programs--;
- }
- };
+OutlineRenderer.prototype.put = function () {
+ if (--this._useCount === 0) {
+ ids.removeItem(this.id);
+ this._program.destroy();
+ delete outlineRenderers[this._hash];
+ stats.memory.programs--;
+ }
+};
- xeogl.renderer.OutlineRenderer.prototype._init = function (hash, mesh) {
- this.id = ids.addItem({});
- this._scene = mesh.scene;
- this._hash = hash;
- this._shaderSource = new xeogl.renderer.OutlineShaderSource(mesh);
- this._program = new xeogl.renderer.Program(mesh.scene.canvas.gl, this._shaderSource);
- this._useCount = 0;
- if (this._program.errors) {
- this.errors = this._program.errors;
- return;
- }
- const program = this._program;
- this._uPositionsDecodeMatrix = program.getLocation("positionsDecodeMatrix");
- this._uModelMatrix = program.getLocation("modelMatrix");
- this._uViewMatrix = program.getLocation("viewMatrix");
- this._uProjMatrix = program.getLocation("projMatrix");
- this._uClips = [];
- const clips = mesh.scene._clipsState.clips;
- for (let i = 0, len = clips.length; i < len; i++) {
- this._uClips.push({
- active: program.getLocation("clipActive" + i),
- pos: program.getLocation("clipPos" + i),
- dir: program.getLocation("clipDir" + i)
- });
- }
- this._uColor = program.getLocation("color");
- this._uWidth = program.getLocation("width");
- this._aPosition = program.getAttribute("position");
- this._aNormal = program.getAttribute("normal");
- this._uClippable = program.getLocation("clippable");
- this._uGammaFactor = program.getLocation("gammaFactor");
- this._lastMaterialId = null;
- this._lastVertexBufsId = null;
- this._lastGeometryId = null;
- };
+OutlineRenderer.prototype._init = function (hash, mesh) {
+ this.id = ids.addItem({});
+ this._scene = mesh.scene;
+ this._hash = hash;
+ this._shaderSource = new OutlineShaderSource(mesh);
+ this._program = new Program(mesh.scene.canvas.gl, this._shaderSource);
+ this._useCount = 0;
+ if (this._program.errors) {
+ this.errors = this._program.errors;
+ return;
+ }
+ const program = this._program;
+ this._uPositionsDecodeMatrix = program.getLocation("positionsDecodeMatrix");
+ this._uModelMatrix = program.getLocation("modelMatrix");
+ this._uViewMatrix = program.getLocation("viewMatrix");
+ this._uProjMatrix = program.getLocation("projMatrix");
+ this._uClips = [];
+ const clips = mesh.scene._clipsState.clips;
+ for (let i = 0, len = clips.length; i < len; i++) {
+ this._uClips.push({
+ active: program.getLocation("clipActive" + i),
+ pos: program.getLocation("clipPos" + i),
+ dir: program.getLocation("clipDir" + i)
+ });
+ }
+ this._uColor = program.getLocation("color");
+ this._uWidth = program.getLocation("width");
+ this._aPosition = program.getAttribute("position");
+ this._aNormal = program.getAttribute("normal");
+ this._uClippable = program.getLocation("clippable");
+ this._uGammaFactor = program.getLocation("gammaFactor");
+ this._lastMaterialId = null;
+ this._lastVertexBufsId = null;
+ this._lastGeometryId = null;
+};
- xeogl.renderer.OutlineRenderer.prototype._bindProgram = function (frame) {
- const scene = this._scene;
- const gl = scene.canvas.gl;
- const program = this._program;
- const clipsState = scene._clipsState;
- program.bind();
- frame.useProgram++;
- this._lastMaterialId = null;
- this._lastVertexBufsId = null;
- this._lastGeometryId = null;
- gl.uniformMatrix4fv(this._uViewMatrix, false, scene.viewTransform.matrix);
- gl.uniformMatrix4fv(this._uProjMatrix, false, scene.projTransform.matrix);
- if (clipsState.clips.length > 0) {
- let clipUniforms;
- let uClipActive;
- let clip;
- let uClipPos;
- let uClipDir;
- for (let i = 0, len = this._uClips.length; i < len; i++) {
- clipUniforms = this._uClips[i];
- uClipActive = clipUniforms.active;
- clip = clipsState.clips[i];
- if (uClipActive) {
- gl.uniform1i(uClipActive, clip.active);
- }
- uClipPos = clipUniforms.pos;
- if (uClipPos) {
- gl.uniform3fv(clipUniforms.pos, clip.pos);
- }
- uClipDir = clipUniforms.dir;
- if (uClipDir) {
- gl.uniform3fv(clipUniforms.dir, clip.dir);
- }
+OutlineRenderer.prototype._bindProgram = function (frame) {
+ const scene = this._scene;
+ const gl = scene.canvas.gl;
+ const program = this._program;
+ const clipsState = scene._clipsState;
+ program.bind();
+ frame.useProgram++;
+ this._lastMaterialId = null;
+ this._lastVertexBufsId = null;
+ this._lastGeometryId = null;
+ gl.uniformMatrix4fv(this._uViewMatrix, false, scene.viewTransform.matrix);
+ gl.uniformMatrix4fv(this._uProjMatrix, false, scene.projTransform.matrix);
+ if (clipsState.clips.length > 0) {
+ let clipUniforms;
+ let uClipActive;
+ let clip;
+ let uClipPos;
+ let uClipDir;
+ for (let i = 0, len = this._uClips.length; i < len; i++) {
+ clipUniforms = this._uClips[i];
+ uClipActive = clipUniforms.active;
+ clip = clipsState.clips[i];
+ if (uClipActive) {
+ gl.uniform1i(uClipActive, clip.active);
+ }
+ uClipPos = clipUniforms.pos;
+ if (uClipPos) {
+ gl.uniform3fv(clipUniforms.pos, clip.pos);
+ }
+ uClipDir = clipUniforms.dir;
+ if (uClipDir) {
+ gl.uniform3fv(clipUniforms.dir, clip.dir);
}
}
- if (this._uGammaFactor) {
- gl.uniform1f(this._uGammaFactor, scene.gammaFactor);
- }
- };
+ }
+ if (this._uGammaFactor) {
+ gl.uniform1f(this._uGammaFactor, scene.gammaFactor);
+ }
+};
- xeogl.renderer.OutlineRenderer.prototype.drawMesh = function (frame, mesh) {
- const scene = this._scene;
- const gl = scene.canvas.gl;
- const materialState = mesh.outlineMaterial;
- const meshState = mesh._state;
- const geometryState = mesh._geometry._state;
- if (frame.lastProgramId !== this._program.id) {
- frame.lastProgramId = this._program.id;
- this._bindProgram(frame);
+OutlineRenderer.prototype.drawMesh = function (frame, mesh) {
+ const scene = this._scene;
+ const gl = scene.canvas.gl;
+ const materialState = mesh.outlineMaterial;
+ const meshState = mesh._state;
+ const geometryState = mesh._geometry._state;
+ if (frame.lastProgramId !== this._program.id) {
+ frame.lastProgramId = this._program.id;
+ this._bindProgram(frame);
+ }
+ if (materialState.id !== this._lastMaterialId) {
+ if (this._uWidth) {
+ gl.uniform1f(this._uWidth, materialState.width);
}
- if (materialState.id !== this._lastMaterialId) {
- if (this._uWidth) {
- gl.uniform1f(this._uWidth, materialState.width);
+ if (this._uColor) {
+ const color = materialState.color;
+ const alpha = materialState.alpha;
+ gl.uniform4f(this._uColor, color[0], color[1], color[2], alpha);
+ }
+ this._lastMaterialId = materialState.id;
+ }
+ gl.uniformMatrix4fv(this._uModelMatrix, gl.FALSE, mesh.worldMatrix);
+ if (this._uModelNormalMatrix) {
+ gl.uniformMatrix4fv(this._uModelNormalMatrix, gl.FALSE, mesh.worldNormalMatrix);
+ }
+ if (this._uClippable) {
+ gl.uniform1i(this._uClippable, meshState.clippable);
+ }
+ if (geometryState.combined) {
+ const vertexBufs = mesh._geometry._getVertexBufs();
+ if (vertexBufs.id !== this._lastVertexBufsId) {
+ if (vertexBufs.positionsBuf && this._aPosition) {
+ this._aPosition.bindArrayBuffer(vertexBufs.positionsBuf, vertexBufs.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
+ frame.bindArray++;
}
- if (this._uColor) {
- const color = materialState.color;
- const alpha = materialState.alpha;
- gl.uniform4f(this._uColor, color[0], color[1], color[2], alpha);
+ if (vertexBufs.normalsBuf && this._aNormal) {
+ this._aNormal.bindArrayBuffer(vertexBufs.normalsBuf, vertexBufs.quantized ? gl.BYTE : gl.FLOAT);
+ frame.bindArray++;
}
- this._lastMaterialId = materialState.id;
+ this._lastVertexBufsId = vertexBufs.id;
}
- gl.uniformMatrix4fv(this._uModelMatrix, gl.FALSE, mesh.worldMatrix);
- if (this._uModelNormalMatrix) {
- gl.uniformMatrix4fv(this._uModelNormalMatrix, gl.FALSE, mesh.worldNormalMatrix);
+ }
+ // Bind VBOs
+ if (geometryState.id !== this._lastGeometryId) {
+ if (this._uPositionsDecodeMatrix) {
+ gl.uniformMatrix4fv(this._uPositionsDecodeMatrix, false, geometryState.positionsDecodeMatrix);
}
- if (this._uClippable) {
- gl.uniform1i(this._uClippable, meshState.clippable);
+ if (this._uUVDecodeMatrix) {
+ gl.uniformMatrix3fv(this._uUVDecodeMatrix, false, geometryState.uvDecodeMatrix);
}
- if (geometryState.combined) {
- const vertexBufs = mesh._geometry._getVertexBufs();
- if (vertexBufs.id !== this._lastVertexBufsId) {
- if (vertexBufs.positionsBuf && this._aPosition) {
- this._aPosition.bindArrayBuffer(vertexBufs.positionsBuf, vertexBufs.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
- frame.bindArray++;
- }
- if (vertexBufs.normalsBuf && this._aNormal) {
- this._aNormal.bindArrayBuffer(vertexBufs.normalsBuf, vertexBufs.quantized ? gl.BYTE : gl.FLOAT);
- frame.bindArray++;
- }
- this._lastVertexBufsId = vertexBufs.id;
+ if (geometryState.combined) { // VBOs were bound by the VertexBufs logic above
+ if (geometryState.indicesBufCombined) {
+ geometryState.indicesBufCombined.bind();
+ frame.bindArray++;
}
- }
- // Bind VBOs
- if (geometryState.id !== this._lastGeometryId) {
- if (this._uPositionsDecodeMatrix) {
- gl.uniformMatrix4fv(this._uPositionsDecodeMatrix, false, geometryState.positionsDecodeMatrix);
- }
- if (this._uUVDecodeMatrix) {
- gl.uniformMatrix3fv(this._uUVDecodeMatrix, false, geometryState.uvDecodeMatrix);
+ } else {
+ if (this._aPosition) {
+ this._aPosition.bindArrayBuffer(geometryState.positionsBuf, geometryState.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
+ frame.bindArray++;
}
- if (geometryState.combined) { // VBOs were bound by the VertexBufs logic above
- if (geometryState.indicesBufCombined) {
- geometryState.indicesBufCombined.bind();
- frame.bindArray++;
- }
- } else {
- if (this._aPosition) {
- this._aPosition.bindArrayBuffer(geometryState.positionsBuf, geometryState.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
- frame.bindArray++;
- }
- if (this._aNormal) {
- this._aNormal.bindArrayBuffer(geometryState.normalsBuf, geometryState.quantized ? gl.BYTE : gl.FLOAT);
- frame.bindArray++;
- }
- if (geometryState.indicesBuf) {
- geometryState.indicesBuf.bind();
- frame.bindArray++;
- // gl.drawElements(geometryState.primitive, geometryState.indicesBuf.numItems, geometryState.indicesBuf.itemType, 0);
- // frame.drawElements++;
- } else if (geometryState.positions) {
- // gl.drawArrays(gl.TRIANGLES, 0, geometryState.positions.numItems);
- // frame.drawArrays++;
- }
+ if (this._aNormal) {
+ this._aNormal.bindArrayBuffer(geometryState.normalsBuf, geometryState.quantized ? gl.BYTE : gl.FLOAT);
+ frame.bindArray++;
}
- this._lastGeometryId = geometryState.id;
- }
- // Draw (indices bound in prev step)
- if (geometryState.combined) {
- if (geometryState.indicesBufCombined) { // Geometry indices into portion of uber-array
- gl.drawElements(geometryState.primitive, geometryState.indicesBufCombined.numItems, geometryState.indicesBufCombined.itemType, 0);
- frame.drawElements++;
- } else {
- // TODO: drawArrays() with VertexBufs positions
- }
- } else {
if (geometryState.indicesBuf) {
- gl.drawElements(geometryState.primitive, geometryState.indicesBuf.numItems, geometryState.indicesBuf.itemType, 0);
- frame.drawElements++;
+ geometryState.indicesBuf.bind();
+ frame.bindArray++;
+ // gl.drawElements(geometryState.primitive, geometryState.indicesBuf.numItems, geometryState.indicesBuf.itemType, 0);
+ // frame.drawElements++;
} else if (geometryState.positions) {
- gl.drawArrays(gl.TRIANGLES, 0, geometryState.positions.numItems);
- frame.drawArrays++;
+ // gl.drawArrays(gl.TRIANGLES, 0, geometryState.positions.numItems);
+ // frame.drawArrays++;
}
}
- };
-})();
+ this._lastGeometryId = geometryState.id;
+ }
+ // Draw (indices bound in prev step)
+ if (geometryState.combined) {
+ if (geometryState.indicesBufCombined) { // Geometry indices into portion of uber-array
+ gl.drawElements(geometryState.primitive, geometryState.indicesBufCombined.numItems, geometryState.indicesBufCombined.itemType, 0);
+ frame.drawElements++;
+ } else {
+ // TODO: drawArrays() with VertexBufs positions
+ }
+ } else {
+ if (geometryState.indicesBuf) {
+ gl.drawElements(geometryState.primitive, geometryState.indicesBuf.numItems, geometryState.indicesBuf.itemType, 0);
+ frame.drawElements++;
+ } else if (geometryState.positions) {
+ gl.drawArrays(gl.TRIANGLES, 0, geometryState.positions.numItems);
+ frame.drawArrays++;
+ }
+ }
+};
+
+export{OutlineRenderer};
diff --git a/src/renderer/outline/outlineShaderSource.js b/src/renderer/outline/outlineShaderSource.js
index 5ff578ac3..747fa5810 100644
--- a/src/renderer/outline/outlineShaderSource.js
+++ b/src/renderer/outline/outlineShaderSource.js
@@ -2,141 +2,138 @@
* @author xeolabs / https://github.com/xeolabs
*/
-(function () {
-
- "use strict";
-
- xeogl.renderer.OutlineShaderSource = function (mesh) {
+class OutlineShaderSource {
+ constructor(mesh) {
this.vertex = buildVertex(mesh);
this.fragment = buildFragment(mesh);
- };
+ }
+}
- function hasNormals(mesh) {
- const primitive = mesh._geometry._state.primitiveName;
- if ((mesh._geometry._state.autoVertexNormals || mesh._geometry._state.normals) && (primitive === "triangles" || primitive === "triangle-strip" || primitive === "triangle-fan")) {
- return true;
- }
- return false;
+function hasNormals(mesh) {
+ const primitive = mesh._geometry._state.primitiveName;
+ if ((mesh._geometry._state.autoVertexNormals || mesh._geometry._state.normals) && (primitive === "triangles" || primitive === "triangle-strip" || primitive === "triangle-fan")) {
+ return true;
}
+ return false;
+}
- function buildVertex(mesh) {
- const scene = mesh.scene;
- const clipping = scene._clipsState.clips.length > 0;
- const quantizedGeometry = !!mesh._geometry._state.quantized;
- const normals = hasNormals(mesh);
- const billboard = mesh._state.billboard;
- const stationary = mesh._state.stationary;
- const src = [];
- src.push("// Outline effect vertex shader");
- src.push("attribute vec3 position;");
- src.push("uniform mat4 modelMatrix;");
- src.push("uniform mat4 viewMatrix;");
- src.push("uniform mat4 projMatrix;");
- src.push("uniform float width;");
+function buildVertex(mesh) {
+ const scene = mesh.scene;
+ const clipping = scene._clipsState.clips.length > 0;
+ const quantizedGeometry = !!mesh._geometry._state.quantized;
+ const normals = hasNormals(mesh);
+ const billboard = mesh._state.billboard;
+ const stationary = mesh._state.stationary;
+ const src = [];
+ src.push("// Outline effect vertex shader");
+ src.push("attribute vec3 position;");
+ src.push("uniform mat4 modelMatrix;");
+ src.push("uniform mat4 viewMatrix;");
+ src.push("uniform mat4 projMatrix;");
+ src.push("uniform float width;");
+ if (quantizedGeometry) {
+ src.push("uniform mat4 positionsDecodeMatrix;");
+ }
+ if (clipping) {
+ src.push("varying vec4 vWorldPosition;");
+ }
+ if (normals) {
+ src.push("attribute vec3 normal;");
if (quantizedGeometry) {
- src.push("uniform mat4 positionsDecodeMatrix;");
- }
- if (clipping) {
- src.push("varying vec4 vWorldPosition;");
- }
- if (normals) {
- src.push("attribute vec3 normal;");
- if (quantizedGeometry) {
- src.push("vec3 octDecode(vec2 oct) {");
- src.push(" vec3 v = vec3(oct.xy, 1.0 - abs(oct.x) - abs(oct.y));");
- src.push(" if (v.z < 0.0) {");
- src.push(" v.xy = (1.0 - abs(v.yx)) * vec2(v.x >= 0.0 ? 1.0 : -1.0, v.y >= 0.0 ? 1.0 : -1.0);");
- src.push(" }");
- src.push(" return normalize(v);");
- src.push("}");
- }
- }
- if (billboard === "spherical" || billboard === "cylindrical") {
- src.push("void billboard(inout mat4 mat) {");
- src.push(" mat[0][0] = 1.0;");
- src.push(" mat[0][1] = 0.0;");
- src.push(" mat[0][2] = 0.0;");
- if (billboard === "spherical") {
- src.push(" mat[1][0] = 0.0;");
- src.push(" mat[1][1] = 1.0;");
- src.push(" mat[1][2] = 0.0;");
- }
- src.push(" mat[2][0] = 0.0;");
- src.push(" mat[2][1] = 0.0;");
- src.push(" mat[2][2] =1.0;");
+ src.push("vec3 octDecode(vec2 oct) {");
+ src.push(" vec3 v = vec3(oct.xy, 1.0 - abs(oct.x) - abs(oct.y));");
+ src.push(" if (v.z < 0.0) {");
+ src.push(" v.xy = (1.0 - abs(v.yx)) * vec2(v.x >= 0.0 ? 1.0 : -1.0, v.y >= 0.0 ? 1.0 : -1.0);");
+ src.push(" }");
+ src.push(" return normalize(v);");
src.push("}");
}
- src.push("void main(void) {");
- src.push("vec4 localPosition = vec4(position, 1.0); ");
- src.push("vec4 worldPosition;");
- if (quantizedGeometry) {
- src.push("localPosition = positionsDecodeMatrix * localPosition;");
- }
- if (normals) {
- if (quantizedGeometry) {
- src.push("vec3 localNormal = octDecode(normal.xy); ");
- } else {
- src.push("vec3 localNormal = normal; ");
- }
- //src.push(" localPosition.xyz += (normalize(normal) * (width * 0.0005 * (projPos.z/1.0)));");
- src.push(" localPosition.xyz += (normalize(normal) * (width * 0.0005));");
- }
- src.push("mat4 viewMatrix2 = viewMatrix;");
- src.push("mat4 modelMatrix2 = modelMatrix;");
- if (stationary) {
- src.push("viewMatrix2[3][0] = viewMatrix2[3][1] = viewMatrix2[3][2] = 0.0;")
+ }
+ if (billboard === "spherical" || billboard === "cylindrical") {
+ src.push("void billboard(inout mat4 mat) {");
+ src.push(" mat[0][0] = 1.0;");
+ src.push(" mat[0][1] = 0.0;");
+ src.push(" mat[0][2] = 0.0;");
+ if (billboard === "spherical") {
+ src.push(" mat[1][0] = 0.0;");
+ src.push(" mat[1][1] = 1.0;");
+ src.push(" mat[1][2] = 0.0;");
}
- if (billboard === "spherical" || billboard === "cylindrical") {
- src.push("mat4 modelViewMatrix = viewMatrix2 * modelMatrix2;");
- src.push("billboard(modelMatrix2);");
- src.push("billboard(viewMatrix2);");
- src.push("billboard(modelViewMatrix);");
- src.push("worldPosition = modelMatrix2 * localPosition;");
- src.push("vec4 viewPosition = modelViewMatrix * localPosition;");
+ src.push(" mat[2][0] = 0.0;");
+ src.push(" mat[2][1] = 0.0;");
+ src.push(" mat[2][2] =1.0;");
+ src.push("}");
+ }
+ src.push("void main(void) {");
+ src.push("vec4 localPosition = vec4(position, 1.0); ");
+ src.push("vec4 worldPosition;");
+ if (quantizedGeometry) {
+ src.push("localPosition = positionsDecodeMatrix * localPosition;");
+ }
+ if (normals) {
+ if (quantizedGeometry) {
+ src.push("vec3 localNormal = octDecode(normal.xy); ");
} else {
- src.push("worldPosition = modelMatrix2 * localPosition;");
- src.push("vec4 viewPosition = viewMatrix2 * worldPosition; ");
- }
- if (clipping) {
- src.push("vWorldPosition = worldPosition;");
+ src.push("vec3 localNormal = normal; ");
}
- src.push(" gl_Position = projMatrix * viewPosition;");
- src.push("}");
- return src;
+ //src.push(" localPosition.xyz += (normalize(normal) * (width * 0.0005 * (projPos.z/1.0)));");
+ src.push(" localPosition.xyz += (normalize(normal) * (width * 0.0005));");
+ }
+ src.push("mat4 viewMatrix2 = viewMatrix;");
+ src.push("mat4 modelMatrix2 = modelMatrix;");
+ if (stationary) {
+ src.push("viewMatrix2[3][0] = viewMatrix2[3][1] = viewMatrix2[3][2] = 0.0;")
+ }
+ if (billboard === "spherical" || billboard === "cylindrical") {
+ src.push("mat4 modelViewMatrix = viewMatrix2 * modelMatrix2;");
+ src.push("billboard(modelMatrix2);");
+ src.push("billboard(viewMatrix2);");
+ src.push("billboard(modelViewMatrix);");
+ src.push("worldPosition = modelMatrix2 * localPosition;");
+ src.push("vec4 viewPosition = modelViewMatrix * localPosition;");
+ } else {
+ src.push("worldPosition = modelMatrix2 * localPosition;");
+ src.push("vec4 viewPosition = viewMatrix2 * worldPosition; ");
}
+ if (clipping) {
+ src.push("vWorldPosition = worldPosition;");
+ }
+ src.push(" gl_Position = projMatrix * viewPosition;");
+ src.push("}");
+ return src;
+}
- function buildFragment(mesh) {
- const scene = mesh.scene;
- const clipsState = scene._clipsState;
- const clipping = clipsState.clips.length > 0;
- const src = [];
- src.push("precision lowp float;");
- src.push("uniform vec4 color;");
- if (clipping) {
- src.push("uniform bool clippable;");
- src.push("varying vec4 vWorldPosition;");
- for (var i = 0; i < clipsState.clips.length; i++) {
- src.push("uniform bool clipActive" + i + ";");
- src.push("uniform vec3 clipPos" + i + ";");
- src.push("uniform vec3 clipDir" + i + ";");
- }
+function buildFragment(mesh) {
+ const scene = mesh.scene;
+ const clipsState = scene._clipsState;
+ const clipping = clipsState.clips.length > 0;
+ const src = [];
+ src.push("precision lowp float;");
+ src.push("uniform vec4 color;");
+ if (clipping) {
+ src.push("uniform bool clippable;");
+ src.push("varying vec4 vWorldPosition;");
+ for (var i = 0; i < clipsState.clips.length; i++) {
+ src.push("uniform bool clipActive" + i + ";");
+ src.push("uniform vec3 clipPos" + i + ";");
+ src.push("uniform vec3 clipDir" + i + ";");
}
- src.push("void main(void) {");
- if (clipping) {
- src.push("if (clippable) {");
- src.push(" float dist = 0.0;");
- for (var i = 0; i < clipsState.clips.length; i++) {
- src.push("if (clipActive" + i + ") {");
- src.push(" dist += clamp(dot(-clipDir" + i + ".xyz, vWorldPosition.xyz - clipPos" + i + ".xyz), 0.0, 1000.0);");
- src.push("}");
- }
- src.push(" if (dist > 0.0) { discard; }");
+ }
+ src.push("void main(void) {");
+ if (clipping) {
+ src.push("if (clippable) {");
+ src.push(" float dist = 0.0;");
+ for (var i = 0; i < clipsState.clips.length; i++) {
+ src.push("if (clipActive" + i + ") {");
+ src.push(" dist += clamp(dot(-clipDir" + i + ".xyz, vWorldPosition.xyz - clipPos" + i + ".xyz), 0.0, 1000.0);");
src.push("}");
}
- src.push(" gl_FragColor = color;");
+ src.push(" if (dist > 0.0) { discard; }");
src.push("}");
- return src;
}
+ src.push(" gl_FragColor = color;");
+ src.push("}");
+ return src;
+}
-
-})();
\ No newline at end of file
+export{OutlineShaderSource};
\ No newline at end of file
diff --git a/src/renderer/pick/pickMeshRenderer.js b/src/renderer/pick/pickMeshRenderer.js
index f08a793bd..81b377619 100644
--- a/src/renderer/pick/pickMeshRenderer.js
+++ b/src/renderer/pick/pickMeshRenderer.js
@@ -2,228 +2,228 @@
* @author xeolabs / https://github.com/xeolabs
*/
-(function () {
+import {PickMeshShaderSource} from "./pickMeshShaderSource.js";
+import {Program} from "../program.js";
+import {stats} from "../../stats.js";
- "use strict";
+// No ID, because there is exactly one PickMeshRenderer per scene
- // No ID, because there is exactly one PickMeshRenderer per scene
+const PickMeshRenderer = function (hash, mesh) {
+ this._hash = hash;
+ this._shaderSource = new PickMeshShaderSource(mesh);
+ this._scene = mesh.scene;
+ this._useCount = 0;
+ this._allocate(mesh);
+};
- xeogl.renderer.PickMeshRenderer = function (hash, mesh) {
- this._hash = hash;
- this._shaderSource = new xeogl.renderer.PickMeshShaderSource(mesh);
- this._scene = mesh.scene;
- this._useCount = 0;
- this._allocate(mesh);
- };
-
- const renderers = {};
+const renderers = {};
- xeogl.renderer.PickMeshRenderer.get = function (mesh) {
- const hash = [
- mesh.scene.canvas.canvas.id,
- mesh.scene._clipsState.getHash(),
- mesh._geometry._state.hash,
- mesh._state.hash
- ].join(";");
- let renderer = renderers[hash];
- if (!renderer) {
- renderer = new xeogl.renderer.PickMeshRenderer(hash, mesh);
- if (renderer.errors) {
- console.log(renderer.errors.join("\n"));
- return null;
- }
- renderers[hash] = renderer;
- xeogl.stats.memory.programs++;
+PickMeshRenderer.get = function (mesh) {
+ const hash = [
+ mesh.scene.canvas.canvas.id,
+ mesh.scene._clipsState.getHash(),
+ mesh._geometry._state.hash,
+ mesh._state.hash
+ ].join(";");
+ let renderer = renderers[hash];
+ if (!renderer) {
+ renderer = new PickMeshRenderer(hash, mesh);
+ if (renderer.errors) {
+ console.log(renderer.errors.join("\n"));
+ return null;
}
- renderer._useCount++;
- return renderer;
- };
+ renderers[hash] = renderer;
+ stats.memory.programs++;
+ }
+ renderer._useCount++;
+ return renderer;
+};
- xeogl.renderer.PickMeshRenderer.prototype.put = function () {
- if (--this._useCount === 0) {
- if (this._program) {
- this._program.destroy();
- }
- delete renderers[this._hash];
- xeogl.stats.memory.programs--;
+PickMeshRenderer.prototype.put = function () {
+ if (--this._useCount === 0) {
+ if (this._program) {
+ this._program.destroy();
}
- };
+ delete renderers[this._hash];
+ stats.memory.programs--;
+ }
+};
- xeogl.renderer.PickMeshRenderer.prototype.webglContextRestored = function () {
- this._program = null;
- };
+PickMeshRenderer.prototype.webglContextRestored = function () {
+ this._program = null;
+};
- xeogl.renderer.PickMeshRenderer.prototype.drawMesh = function (frame, mesh) {
- if (!this._program) {
- this._allocate(mesh);
- }
- const scene = this._scene;
- const gl = scene.canvas.gl;
- const materialState = mesh._material._state;
- const meshState = mesh._state;
- const geometryState = mesh._geometry._state;
- if (frame.lastProgramId !== this._program.id) {
- frame.lastProgramId = this._program.id;
- this._bindProgram(frame);
- }
- if (materialState.id !== this._lastMaterialId) {
- const backfaces = materialState.backfaces;
- if (frame.backfaces !== backfaces) {
- if (backfaces) {
- gl.disable(gl.CULL_FACE);
- } else {
- gl.enable(gl.CULL_FACE);
- }
- frame.backfaces = backfaces;
- }
- const frontface = materialState.frontface;
- if (frame.frontface !== frontface) {
- if (frontface) {
- gl.frontFace(gl.CCW);
- } else {
- gl.frontFace(gl.CW);
- }
- frame.frontface = frontface;
- }
- if (frame.lineWidth !== materialState.lineWidth) {
- gl.lineWidth(materialState.lineWidth);
- frame.lineWidth = materialState.lineWidth;
- }
- if (this._uPointSize) {
- gl.uniform1i(this._uPointSize, materialState.pointSize);
+PickMeshRenderer.prototype.drawMesh = function (frame, mesh) {
+ if (!this._program) {
+ this._allocate(mesh);
+ }
+ const scene = this._scene;
+ const gl = scene.canvas.gl;
+ const materialState = mesh._material._state;
+ const meshState = mesh._state;
+ const geometryState = mesh._geometry._state;
+ if (frame.lastProgramId !== this._program.id) {
+ frame.lastProgramId = this._program.id;
+ this._bindProgram(frame);
+ }
+ if (materialState.id !== this._lastMaterialId) {
+ const backfaces = materialState.backfaces;
+ if (frame.backfaces !== backfaces) {
+ if (backfaces) {
+ gl.disable(gl.CULL_FACE);
+ } else {
+ gl.enable(gl.CULL_FACE);
}
- this._lastMaterialId = materialState.id;
+ frame.backfaces = backfaces;
}
- gl.uniformMatrix4fv(this._uModelMatrix, gl.FALSE, mesh.worldMatrix);
- if (geometryState.combined) {
- const vertexBufs = mesh._geometry._getVertexBufs();
- if (vertexBufs.id !== this._lastVertexBufsId) {
- if (vertexBufs.positionsBuf && this._aPosition) {
- this._aPosition.bindArrayBuffer(vertexBufs.positionsBuf, vertexBufs.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
- frame.bindArray++;
- }
- this._lastVertexBufsId = vertexBufs.id;
+ const frontface = materialState.frontface;
+ if (frame.frontface !== frontface) {
+ if (frontface) {
+ gl.frontFace(gl.CCW);
+ } else {
+ gl.frontFace(gl.CW);
}
+ frame.frontface = frontface;
}
- // Mesh state
- if (this._uClippable) {
- gl.uniform1i(this._uClippable, mesh._state.clippable);
+ if (frame.lineWidth !== materialState.lineWidth) {
+ gl.lineWidth(materialState.lineWidth);
+ frame.lineWidth = materialState.lineWidth;
}
- // Bind VBOs
- if (geometryState.id !== this._lastGeometryId) {
- if (this._uPositionsDecodeMatrix) {
- gl.uniformMatrix4fv(this._uPositionsDecodeMatrix, false, geometryState.positionsDecodeMatrix);
- }
- if (geometryState.combined) { // VBOs were bound by the preceding VertexBufs chunk
- if (geometryState.indicesBufCombined) {
- geometryState.indicesBufCombined.bind();
- frame.bindArray++;
- }
- } else {
- if (this._aPosition) {
- this._aPosition.bindArrayBuffer(geometryState.positionsBuf, geometryState.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
- frame.bindArray++;
- }
- if (geometryState.indicesBuf) {
- geometryState.indicesBuf.bind();
- frame.bindArray++;
- }
+ if (this._uPointSize) {
+ gl.uniform1i(this._uPointSize, materialState.pointSize);
+ }
+ this._lastMaterialId = materialState.id;
+ }
+ gl.uniformMatrix4fv(this._uModelMatrix, gl.FALSE, mesh.worldMatrix);
+ if (geometryState.combined) {
+ const vertexBufs = mesh._geometry._getVertexBufs();
+ if (vertexBufs.id !== this._lastVertexBufsId) {
+ if (vertexBufs.positionsBuf && this._aPosition) {
+ this._aPosition.bindArrayBuffer(vertexBufs.positionsBuf, vertexBufs.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
+ frame.bindArray++;
}
- this._lastGeometryId = geometryState.id;
+ this._lastVertexBufsId = vertexBufs.id;
}
- // Mesh-indexed color
- const a = frame.pickmeshIndex >> 24 & 0xFF;
- const b = frame.pickmeshIndex >> 16 & 0xFF;
- const g = frame.pickmeshIndex >> 8 & 0xFF;
- const r = frame.pickmeshIndex & 0xFF;
- frame.pickmeshIndex++;
- gl.uniform4f(this._uPickColor, r / 255, g / 255, b / 255, a / 255);
- // Draw (indices bound in prev step)
- if (geometryState.combined) {
- if (geometryState.indicesBufCombined) { // Geometry indices into portion of uber-array
- gl.drawElements(geometryState.primitive, geometryState.indicesBufCombined.numItems, geometryState.indicesBufCombined.itemType, 0);
- frame.drawElements++;
- } else {
- // TODO: drawArrays() with VertexBufs positions
+ }
+ // Mesh state
+ if (this._uClippable) {
+ gl.uniform1i(this._uClippable, mesh._state.clippable);
+ }
+ // Bind VBOs
+ if (geometryState.id !== this._lastGeometryId) {
+ if (this._uPositionsDecodeMatrix) {
+ gl.uniformMatrix4fv(this._uPositionsDecodeMatrix, false, geometryState.positionsDecodeMatrix);
+ }
+ if (geometryState.combined) { // VBOs were bound by the preceding VertexBufs chunk
+ if (geometryState.indicesBufCombined) {
+ geometryState.indicesBufCombined.bind();
+ frame.bindArray++;
}
} else {
+ if (this._aPosition) {
+ this._aPosition.bindArrayBuffer(geometryState.positionsBuf, geometryState.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
+ frame.bindArray++;
+ }
if (geometryState.indicesBuf) {
- gl.drawElements(geometryState.primitive, geometryState.indicesBuf.numItems, geometryState.indicesBuf.itemType, 0);
- frame.drawElements++;
- } else if (geometryState.positions) {
- gl.drawArrays(gl.TRIANGLES, 0, geometryState.positions.numItems);
+ geometryState.indicesBuf.bind();
+ frame.bindArray++;
}
}
- };
-
- xeogl.renderer.PickMeshRenderer.prototype._allocate = function (mesh) {
- const gl = mesh.scene.canvas.gl;
- this._program = new xeogl.renderer.Program(gl, this._shaderSource);
- if (this._program.errors) {
- this.errors = this._program.errors;
- return;
+ this._lastGeometryId = geometryState.id;
+ }
+ // Mesh-indexed color
+ const a = frame.pickmeshIndex >> 24 & 0xFF;
+ const b = frame.pickmeshIndex >> 16 & 0xFF;
+ const g = frame.pickmeshIndex >> 8 & 0xFF;
+ const r = frame.pickmeshIndex & 0xFF;
+ frame.pickmeshIndex++;
+ gl.uniform4f(this._uPickColor, r / 255, g / 255, b / 255, a / 255);
+ // Draw (indices bound in prev step)
+ if (geometryState.combined) {
+ if (geometryState.indicesBufCombined) { // Geometry indices into portion of uber-array
+ gl.drawElements(geometryState.primitive, geometryState.indicesBufCombined.numItems, geometryState.indicesBufCombined.itemType, 0);
+ frame.drawElements++;
+ } else {
+ // TODO: drawArrays() with VertexBufs positions
}
- const program = this._program;
- this._uPositionsDecodeMatrix = program.getLocation("positionsDecodeMatrix");
- this._uModelMatrix = program.getLocation("modelMatrix");
- this._uViewMatrix = program.getLocation("viewMatrix");
- this._uProjMatrix = program.getLocation("projMatrix");
- this._uClips = [];
- const clips = mesh.scene._clipsState.clips;
- for (let i = 0, len = clips.length; i < len; i++) {
- this._uClips.push({
- active: program.getLocation("clipActive" + i),
- pos: program.getLocation("clipPos" + i),
- dir: program.getLocation("clipDir" + i)
- });
+ } else {
+ if (geometryState.indicesBuf) {
+ gl.drawElements(geometryState.primitive, geometryState.indicesBuf.numItems, geometryState.indicesBuf.itemType, 0);
+ frame.drawElements++;
+ } else if (geometryState.positions) {
+ gl.drawArrays(gl.TRIANGLES, 0, geometryState.positions.numItems);
}
- this._aPosition = program.getAttribute("position");
- this._uClippable = program.getLocation("clippable");
- this._uPickColor = program.getLocation("pickColor");
- this._lastMaterialId = null;
- this._lastVertexBufsId = null;
- this._lastGeometryId = null;
- };
+ }
+};
- xeogl.renderer.PickMeshRenderer.prototype._bindProgram = function (frame) {
- if (!this._program) {
- this._allocate(mesh);
- }
- const scene = this._scene;
- const gl = scene.canvas.gl;
- const clipsState = scene._clipsState;
- const camera = scene.camera;
- const cameraState = camera._state;
- this._program.bind();
- frame.useProgram++;
- this._lastMaterialId = null;
- this._lastVertexBufsId = null;
- this._lastGeometryId = null;
- gl.uniformMatrix4fv(this._uViewMatrix, false, frame.pickViewMatrix || cameraState.matrix);
- gl.uniformMatrix4fv(this._uProjMatrix, false, frame.pickProjMatrix || camera.project._state.matrix);
- if (clipsState.clips.length > 0) {
- let clipUniforms;
- let uClipActive;
- let clip;
- let uClipPos;
- let uClipDir;
- for (let i = 0, len = this._uClips.length; i < len; i++) {
- clipUniforms = this._uClips[i];
- uClipActive = clipUniforms.active;
- clip = clipsState.clips[i];
- if (uClipActive) {
- gl.uniform1i(uClipActive, clip.active);
- }
- uClipPos = clipUniforms.pos;
- if (uClipPos) {
- gl.uniform3fv(clipUniforms.pos, clip.pos);
- }
- uClipDir = clipUniforms.dir;
- if (uClipDir) {
- gl.uniform3fv(clipUniforms.dir, clip.dir);
- }
+PickMeshRenderer.prototype._allocate = function (mesh) {
+ const gl = mesh.scene.canvas.gl;
+ this._program = new Program(gl, this._shaderSource);
+ if (this._program.errors) {
+ this.errors = this._program.errors;
+ return;
+ }
+ const program = this._program;
+ this._uPositionsDecodeMatrix = program.getLocation("positionsDecodeMatrix");
+ this._uModelMatrix = program.getLocation("modelMatrix");
+ this._uViewMatrix = program.getLocation("viewMatrix");
+ this._uProjMatrix = program.getLocation("projMatrix");
+ this._uClips = [];
+ const clips = mesh.scene._clipsState.clips;
+ for (let i = 0, len = clips.length; i < len; i++) {
+ this._uClips.push({
+ active: program.getLocation("clipActive" + i),
+ pos: program.getLocation("clipPos" + i),
+ dir: program.getLocation("clipDir" + i)
+ });
+ }
+ this._aPosition = program.getAttribute("position");
+ this._uClippable = program.getLocation("clippable");
+ this._uPickColor = program.getLocation("pickColor");
+ this._lastMaterialId = null;
+ this._lastVertexBufsId = null;
+ this._lastGeometryId = null;
+};
+
+PickMeshRenderer.prototype._bindProgram = function (frame) {
+ if (!this._program) {
+ this._allocate(mesh);
+ }
+ const scene = this._scene;
+ const gl = scene.canvas.gl;
+ const clipsState = scene._clipsState;
+ const camera = scene.camera;
+ const cameraState = camera._state;
+ this._program.bind();
+ frame.useProgram++;
+ this._lastMaterialId = null;
+ this._lastVertexBufsId = null;
+ this._lastGeometryId = null;
+ gl.uniformMatrix4fv(this._uViewMatrix, false, frame.pickViewMatrix || cameraState.matrix);
+ gl.uniformMatrix4fv(this._uProjMatrix, false, frame.pickProjMatrix || camera.project._state.matrix);
+ if (clipsState.clips.length > 0) {
+ let clipUniforms;
+ let uClipActive;
+ let clip;
+ let uClipPos;
+ let uClipDir;
+ for (let i = 0, len = this._uClips.length; i < len; i++) {
+ clipUniforms = this._uClips[i];
+ uClipActive = clipUniforms.active;
+ clip = clipsState.clips[i];
+ if (uClipActive) {
+ gl.uniform1i(uClipActive, clip.active);
+ }
+ uClipPos = clipUniforms.pos;
+ if (uClipPos) {
+ gl.uniform3fv(clipUniforms.pos, clip.pos);
+ }
+ uClipDir = clipUniforms.dir;
+ if (uClipDir) {
+ gl.uniform3fv(clipUniforms.dir, clip.dir);
}
}
- };
+ }
+};
-})();
+export{PickMeshRenderer};
diff --git a/src/renderer/pick/pickMeshShaderSource.js b/src/renderer/pick/pickMeshShaderSource.js
index b7a80f7ca..d7b1d4c74 100644
--- a/src/renderer/pick/pickMeshShaderSource.js
+++ b/src/renderer/pick/pickMeshShaderSource.js
@@ -2,107 +2,105 @@
* @author xeolabs / https://github.com/xeolabs
*/
-(function () {
-
- "use strict";
-
- xeogl.renderer.PickMeshShaderSource = function (mesh) {
+class PickMeshShaderSource {
+ constructor(mesh) {
this.vertex = buildVertex(mesh);
this.fragment = buildFragment(mesh);
- };
-
- function buildVertex(mesh) {
- const scene = mesh.scene;
- const clipping = scene._clipsState.clips.length > 0;
- const quantizedGeometry = !!mesh._geometry._state.quantized;
- const billboard = mesh._state.billboard;
- const stationary = mesh._state.stationary;
- const src = [];
- src.push("// mesh picking vertex shader");
- src.push("attribute vec3 position;");
- src.push("uniform mat4 modelMatrix;");
- src.push("uniform mat4 viewMatrix;");
- src.push("uniform mat4 viewNormalMatrix;");
- src.push("uniform mat4 projMatrix;");
- src.push("varying vec4 vViewPosition;");
- if (quantizedGeometry) {
- src.push("uniform mat4 positionsDecodeMatrix;");
- }
- if (clipping) {
- src.push("varying vec4 vWorldPosition;");
- }
- if (billboard === "spherical" || billboard === "cylindrical") {
- src.push("void billboard(inout mat4 mat) {");
- src.push(" mat[0][0] = 1.0;");
- src.push(" mat[0][1] = 0.0;");
- src.push(" mat[0][2] = 0.0;");
- if (billboard === "spherical") {
- src.push(" mat[1][0] = 0.0;");
- src.push(" mat[1][1] = 1.0;");
- src.push(" mat[1][2] = 0.0;");
- }
- src.push(" mat[2][0] = 0.0;");
- src.push(" mat[2][1] = 0.0;");
- src.push(" mat[2][2] =1.0;");
- src.push("}");
- }
- src.push("void main(void) {");
- src.push("vec4 localPosition = vec4(position, 1.0); ");
- if (quantizedGeometry) {
- src.push("localPosition = positionsDecodeMatrix * localPosition;");
- }
- src.push("mat4 viewMatrix2 = viewMatrix;");
- src.push("mat4 modelMatrix2 = modelMatrix;");
- if (stationary) {
- src.push("viewMatrix2[3][0] = viewMatrix2[3][1] = viewMatrix2[3][2] = 0.0;")
- }
- if (billboard === "spherical" || billboard === "cylindrical") {
- src.push("mat4 modelViewMatrix = viewMatrix2 * modelMatrix2;");
- src.push("billboard(modelMatrix2);");
- src.push("billboard(viewMatrix2);");
- }
- src.push(" vec4 worldPosition = modelMatrix2 * localPosition;");
- src.push(" vec4 viewPosition = viewMatrix2 * worldPosition;");
- if (clipping) {
- src.push(" vWorldPosition = worldPosition;");
+ }
+}
+
+function buildVertex(mesh) {
+ const scene = mesh.scene;
+ const clipping = scene._clipsState.clips.length > 0;
+ const quantizedGeometry = !!mesh._geometry._state.quantized;
+ const billboard = mesh._state.billboard;
+ const stationary = mesh._state.stationary;
+ const src = [];
+ src.push("// mesh picking vertex shader");
+ src.push("attribute vec3 position;");
+ src.push("uniform mat4 modelMatrix;");
+ src.push("uniform mat4 viewMatrix;");
+ src.push("uniform mat4 viewNormalMatrix;");
+ src.push("uniform mat4 projMatrix;");
+ src.push("varying vec4 vViewPosition;");
+ if (quantizedGeometry) {
+ src.push("uniform mat4 positionsDecodeMatrix;");
+ }
+ if (clipping) {
+ src.push("varying vec4 vWorldPosition;");
+ }
+ if (billboard === "spherical" || billboard === "cylindrical") {
+ src.push("void billboard(inout mat4 mat) {");
+ src.push(" mat[0][0] = 1.0;");
+ src.push(" mat[0][1] = 0.0;");
+ src.push(" mat[0][2] = 0.0;");
+ if (billboard === "spherical") {
+ src.push(" mat[1][0] = 0.0;");
+ src.push(" mat[1][1] = 1.0;");
+ src.push(" mat[1][2] = 0.0;");
}
- src.push(" gl_Position = projMatrix * viewPosition;");
+ src.push(" mat[2][0] = 0.0;");
+ src.push(" mat[2][1] = 0.0;");
+ src.push(" mat[2][2] =1.0;");
src.push("}");
- return src;
}
+ src.push("void main(void) {");
+ src.push("vec4 localPosition = vec4(position, 1.0); ");
+ if (quantizedGeometry) {
+ src.push("localPosition = positionsDecodeMatrix * localPosition;");
+ }
+ src.push("mat4 viewMatrix2 = viewMatrix;");
+ src.push("mat4 modelMatrix2 = modelMatrix;");
+ if (stationary) {
+ src.push("viewMatrix2[3][0] = viewMatrix2[3][1] = viewMatrix2[3][2] = 0.0;")
+ }
+ if (billboard === "spherical" || billboard === "cylindrical") {
+ src.push("mat4 modelViewMatrix = viewMatrix2 * modelMatrix2;");
+ src.push("billboard(modelMatrix2);");
+ src.push("billboard(viewMatrix2);");
+ }
+ src.push(" vec4 worldPosition = modelMatrix2 * localPosition;");
+ src.push(" vec4 viewPosition = viewMatrix2 * worldPosition;");
+ if (clipping) {
+ src.push(" vWorldPosition = worldPosition;");
+ }
+ src.push(" gl_Position = projMatrix * viewPosition;");
+ src.push("}");
+ return src;
+}
- function buildFragment(mesh) {
- const scene = mesh.scene;
- const clipsState = scene._clipsState;
- const clipping = clipsState.clips.length > 0;
- const src = [];
- src.push("// mesh picking fragment shader");
- src.push("precision lowp float;");
- src.push("uniform vec4 pickColor;");
- if (clipping) {
- src.push("uniform bool clippable;");
- src.push("varying vec4 vWorldPosition;");
- for (var i = 0; i < clipsState.clips.length; i++) {
- src.push("uniform bool clipActive" + i + ";");
- src.push("uniform vec3 clipPos" + i + ";");
- src.push("uniform vec3 clipDir" + i + ";");
- }
+function buildFragment(mesh) {
+ const scene = mesh.scene;
+ const clipsState = scene._clipsState;
+ const clipping = clipsState.clips.length > 0;
+ const src = [];
+ src.push("// mesh picking fragment shader");
+ src.push("precision lowp float;");
+ src.push("uniform vec4 pickColor;");
+ if (clipping) {
+ src.push("uniform bool clippable;");
+ src.push("varying vec4 vWorldPosition;");
+ for (var i = 0; i < clipsState.clips.length; i++) {
+ src.push("uniform bool clipActive" + i + ";");
+ src.push("uniform vec3 clipPos" + i + ";");
+ src.push("uniform vec3 clipDir" + i + ";");
}
- src.push("void main(void) {");
- if (clipping) {
- src.push("if (clippable) {");
- src.push(" float dist = 0.0;");
- for (var i = 0; i < clipsState.clips.length; i++) {
- src.push("if (clipActive" + i + ") {");
- src.push(" dist += clamp(dot(-clipDir" + i + ".xyz, vWorldPosition.xyz - clipPos" + i + ".xyz), 0.0, 1000.0);");
- src.push("}");
- }
- src.push(" if (dist > 0.0) { discard; }");
+ }
+ src.push("void main(void) {");
+ if (clipping) {
+ src.push("if (clippable) {");
+ src.push(" float dist = 0.0;");
+ for (var i = 0; i < clipsState.clips.length; i++) {
+ src.push("if (clipActive" + i + ") {");
+ src.push(" dist += clamp(dot(-clipDir" + i + ".xyz, vWorldPosition.xyz - clipPos" + i + ".xyz), 0.0, 1000.0);");
src.push("}");
}
- src.push(" gl_FragColor = pickColor; ");
+ src.push(" if (dist > 0.0) { discard; }");
src.push("}");
- return src;
}
+ src.push(" gl_FragColor = pickColor; ");
+ src.push("}");
+ return src;
+}
-})();
\ No newline at end of file
+export{PickMeshShaderSource};
\ No newline at end of file
diff --git a/src/renderer/pick/pickTriangleRenderer.js b/src/renderer/pick/pickTriangleRenderer.js
index 00dfff84e..6b4a65b68 100644
--- a/src/renderer/pick/pickTriangleRenderer.js
+++ b/src/renderer/pick/pickTriangleRenderer.js
@@ -2,160 +2,161 @@
* @author xeolabs / https://github.com/xeolabs
*/
-(function () {
+import {PickTriangleShaderSource} from "./pickTriangleShaderSource.js";
+import {Program} from "../program.js";
+import {stats} from "../../stats.js";
- "use strict";
+const PickTriangleRenderer = function (hash, mesh) {
+ this._hash = hash;
+ this._scene = mesh.scene;
+ this._useCount = 0;
+ this._shaderSource = new PickTriangleShaderSource(mesh);
+ this._allocate(mesh);
+};
- xeogl.renderer.PickTriangleRenderer = function (hash, mesh) {
- this._hash = hash;
- this._scene = mesh.scene;
- this._useCount = 0;
- this._shaderSource = new xeogl.renderer.PickTriangleShaderSource(mesh);
- this._allocate(mesh);
- };
-
- const renderers = {};
+const renderers = {};
- xeogl.renderer.PickTriangleRenderer.get = function (mesh) {
- const hash = [
- mesh.scene.canvas.canvas.id,
- mesh.scene._clipsState.getHash(),
- mesh._geometry._state.quantized ? "cp" : "",
- mesh._state.hash
- ].join(";");
- let renderer = renderers[hash];
- if (!renderer) {
- renderer = new xeogl.renderer.PickTriangleRenderer(hash, mesh);
- if (renderer.errors) {
- console.log(renderer.errors.join("\n"));
- return null;
- }
- renderers[hash] = renderer;
- xeogl.stats.memory.programs++;
+PickTriangleRenderer.get = function (mesh) {
+ const hash = [
+ mesh.scene.canvas.canvas.id,
+ mesh.scene._clipsState.getHash(),
+ mesh._geometry._state.quantized ? "cp" : "",
+ mesh._state.hash
+ ].join(";");
+ let renderer = renderers[hash];
+ if (!renderer) {
+ renderer = new PickTriangleRenderer(hash, mesh);
+ if (renderer.errors) {
+ console.log(renderer.errors.join("\n"));
+ return null;
}
- renderer._useCount++;
- return renderer;
- };
+ renderers[hash] = renderer;
+ stats.memory.programs++;
+ }
+ renderer._useCount++;
+ return renderer;
+};
- xeogl.renderer.PickTriangleRenderer.prototype.put = function () {
- if (--this._useCount === 0) {
- if (this._program) {
- this._program.destroy();
- }
- delete renderers[this._hash];
- xeogl.stats.memory.programs--;
+PickTriangleRenderer.prototype.put = function () {
+ if (--this._useCount === 0) {
+ if (this._program) {
+ this._program.destroy();
}
- };
+ delete renderers[this._hash];
+ stats.memory.programs--;
+ }
+};
- xeogl.renderer.PickTriangleRenderer.prototype.webglContextRestored = function () {
- this._program = null;
- };
+PickTriangleRenderer.prototype.webglContextRestored = function () {
+ this._program = null;
+};
- xeogl.renderer.PickTriangleRenderer.prototype.drawMesh = function (frame, mesh) {
- if (!this._program) {
- this._allocate(mesh);
- }
- const scene = this._scene;
- const gl = scene.canvas.gl;
- const clipsState = scene._clipsState;
- const materialState = mesh._material._state;
- const meshState = mesh._state;
- const geometry = mesh._geometry;
- const geometryState = mesh._geometry._state;
- const backfaces = materialState.backfaces;
- const frontface = materialState.frontface;
- const positionsBuf = geometry._getPickTrianglePositions();
- const pickColorsBuf = geometry._getPickTriangleColors();
- const camera = scene.camera;
- const cameraState = camera._state;
- this._program.bind();
- frame.useProgram++;
- gl.uniformMatrix4fv(this._uViewMatrix, false, frame.pickViewMatrix || cameraState.matrix);
- gl.uniformMatrix4fv(this._uProjMatrix, false, frame.pickProjMatrix || camera.project._state.matrix);
- if (clipsState.clips.length > 0) {
- const clips = clipsState.clips;
- let clipUniforms;
- let uClipActive;
- let clip;
- let uClipPos;
- let uClipDir;
- for (let i = 0, len = this._uClips.length; i < len; i++) {
- clipUniforms = this._uClips[i];
- uClipActive = clipUniforms.active;
- clip = clips[i];
- if (uClipActive) {
- gl.uniform1i(uClipActive, clip.active);
- }
- uClipPos = clipUniforms.pos;
- if (uClipPos) {
- gl.uniform3fv(clipUniforms.pos, clip.pos);
- }
- uClipDir = clipUniforms.dir;
- if (uClipDir) {
- gl.uniform3fv(clipUniforms.dir, clip.dir);
- }
+PickTriangleRenderer.prototype.drawMesh = function (frame, mesh) {
+ if (!this._program) {
+ this._allocate(mesh);
+ }
+ const scene = this._scene;
+ const gl = scene.canvas.gl;
+ const clipsState = scene._clipsState;
+ const materialState = mesh._material._state;
+ const meshState = mesh._state;
+ const geometry = mesh._geometry;
+ const geometryState = mesh._geometry._state;
+ const backfaces = materialState.backfaces;
+ const frontface = materialState.frontface;
+ const positionsBuf = geometry._getPickTrianglePositions();
+ const pickColorsBuf = geometry._getPickTriangleColors();
+ const camera = scene.camera;
+ const cameraState = camera._state;
+ this._program.bind();
+ frame.useProgram++;
+ gl.uniformMatrix4fv(this._uViewMatrix, false, frame.pickViewMatrix || cameraState.matrix);
+ gl.uniformMatrix4fv(this._uProjMatrix, false, frame.pickProjMatrix || camera.project._state.matrix);
+ if (clipsState.clips.length > 0) {
+ const clips = clipsState.clips;
+ let clipUniforms;
+ let uClipActive;
+ let clip;
+ let uClipPos;
+ let uClipDir;
+ for (let i = 0, len = this._uClips.length; i < len; i++) {
+ clipUniforms = this._uClips[i];
+ uClipActive = clipUniforms.active;
+ clip = clips[i];
+ if (uClipActive) {
+ gl.uniform1i(uClipActive, clip.active);
}
- }
- if (frame.backfaces !== backfaces) {
- if (backfaces) {
- gl.disable(gl.CULL_FACE);
- } else {
- gl.enable(gl.CULL_FACE);
+ uClipPos = clipUniforms.pos;
+ if (uClipPos) {
+ gl.uniform3fv(clipUniforms.pos, clip.pos);
}
- frame.backfaces = backfaces;
- }
- if (frame.frontface !== frontface) {
- if (frontface) {
- gl.frontFace(gl.CCW);
- } else {
- gl.frontFace(gl.CW);
+ uClipDir = clipUniforms.dir;
+ if (uClipDir) {
+ gl.uniform3fv(clipUniforms.dir, clip.dir);
}
- frame.frontface = frontface;
}
- this._lastMaterialId = materialState.id;
- gl.uniformMatrix4fv(this._uModelMatrix, gl.FALSE, mesh.worldMatrix);
- if (this._uClippable) {
- gl.uniform1i(this._uClippable, mesh._state.clippable);
+ }
+ if (frame.backfaces !== backfaces) {
+ if (backfaces) {
+ gl.disable(gl.CULL_FACE);
+ } else {
+ gl.enable(gl.CULL_FACE);
}
- if (this._uPositionsDecodeMatrix) {
- gl.uniformMatrix4fv(this._uPositionsDecodeMatrix, false, geometryState.positionsDecodeMatrix);
- this._aPosition.bindArrayBuffer(positionsBuf, geometryState.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
+ frame.backfaces = backfaces;
+ }
+ if (frame.frontface !== frontface) {
+ if (frontface) {
+ gl.frontFace(gl.CCW);
} else {
- this._aPosition.bindArrayBuffer(positionsBuf);
+ gl.frontFace(gl.CW);
}
- pickColorsBuf.bind();
- gl.enableVertexAttribArray(this._aColor.location);
- gl.vertexAttribPointer(this._aColor.location, pickColorsBuf.itemSize, pickColorsBuf.itemType, true, 0, 0); // Normalize
- gl.drawArrays(geometryState.primitive, 0, positionsBuf.numItems / 3);
- };
+ frame.frontface = frontface;
+ }
+ this._lastMaterialId = materialState.id;
+ gl.uniformMatrix4fv(this._uModelMatrix, gl.FALSE, mesh.worldMatrix);
+ if (this._uClippable) {
+ gl.uniform1i(this._uClippable, mesh._state.clippable);
+ }
+ if (this._uPositionsDecodeMatrix) {
+ gl.uniformMatrix4fv(this._uPositionsDecodeMatrix, false, geometryState.positionsDecodeMatrix);
+ this._aPosition.bindArrayBuffer(positionsBuf, geometryState.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
+ } else {
+ this._aPosition.bindArrayBuffer(positionsBuf);
+ }
+ pickColorsBuf.bind();
+ gl.enableVertexAttribArray(this._aColor.location);
+ gl.vertexAttribPointer(this._aColor.location, pickColorsBuf.itemSize, pickColorsBuf.itemType, true, 0, 0); // Normalize
+ gl.drawArrays(geometryState.primitive, 0, positionsBuf.numItems / 3);
+};
- xeogl.renderer.PickTriangleRenderer.prototype._allocate = function (mesh) {
- const gl = mesh.scene.canvas.gl;
- this._program = new xeogl.renderer.Program(gl, this._shaderSource);
- this._useCount = 0;
- if (this._program.errors) {
- this.errors = this._program.errors;
- return;
- }
- const program = this._program;
- this._uPositionsDecodeMatrix = program.getLocation("positionsDecodeMatrix");
- this._uModelMatrix = program.getLocation("modelMatrix");
- this._uViewMatrix = program.getLocation("viewMatrix");
- this._uProjMatrix = program.getLocation("projMatrix");
- this._uClips = [];
- const clips = mesh.scene._clipsState.clips;
- for (let i = 0, len = clips.length; i < len; i++) {
- this._uClips.push({
- active: program.getLocation("clipActive" + i),
- pos: program.getLocation("clipPos" + i),
- dir: program.getLocation("clipDir" + i)
- });
- }
- this._aPosition = program.getAttribute("position");
- this._aColor = program.getAttribute("color");
- this._uClippable = program.getLocation("clippable");
- };
-})();
+PickTriangleRenderer.prototype._allocate = function (mesh) {
+ const gl = mesh.scene.canvas.gl;
+ this._program = new Program(gl, this._shaderSource);
+ this._useCount = 0;
+ if (this._program.errors) {
+ this.errors = this._program.errors;
+ return;
+ }
+ const program = this._program;
+ this._uPositionsDecodeMatrix = program.getLocation("positionsDecodeMatrix");
+ this._uModelMatrix = program.getLocation("modelMatrix");
+ this._uViewMatrix = program.getLocation("viewMatrix");
+ this._uProjMatrix = program.getLocation("projMatrix");
+ this._uClips = [];
+ const clips = mesh.scene._clipsState.clips;
+ for (let i = 0, len = clips.length; i < len; i++) {
+ this._uClips.push({
+ active: program.getLocation("clipActive" + i),
+ pos: program.getLocation("clipPos" + i),
+ dir: program.getLocation("clipDir" + i)
+ });
+ }
+ this._aPosition = program.getAttribute("position");
+ this._aColor = program.getAttribute("color");
+ this._uClippable = program.getLocation("clippable");
+};
+
+export{PickTriangleRenderer};
diff --git a/src/renderer/pick/pickTriangleShaderSource.js b/src/renderer/pick/pickTriangleShaderSource.js
index c101c3442..9c52269d0 100644
--- a/src/renderer/pick/pickTriangleShaderSource.js
+++ b/src/renderer/pick/pickTriangleShaderSource.js
@@ -2,83 +2,82 @@
* @author xeolabs / https://github.com/xeolabs
*/
-(function () {
-
- "use strict";
-
- xeogl.renderer.PickTriangleShaderSource = function (mesh) {
+class PickTriangleShaderSource {
+ constructor(mesh) {
this.vertex = buildVertex(mesh);
this.fragment = buildFragment(mesh);
- };
+ }
+}
- function buildVertex(mesh) {
- const scene = mesh.scene;
- const clipping = scene._clipsState.clips.length > 0;
- const quantizedGeometry = !!mesh._geometry._state.quantized;
- const billboard = mesh._state.billboard;
- const stationary = mesh._state.stationary;
- const src = [];
- src.push("// Surface picking vertex shader");
- src.push("attribute vec3 position;");
- src.push("attribute vec4 color;");
- src.push("uniform mat4 modelMatrix;");
- src.push("uniform mat4 viewMatrix;");
- src.push("uniform mat4 projMatrix;");
- if (clipping) {
- src.push("uniform bool clippable;");
- src.push("varying vec4 vWorldPosition;");
- }
- src.push("varying vec4 vColor;");
- if (quantizedGeometry) {
- src.push("uniform mat4 positionsDecodeMatrix;");
- }
- src.push("void main(void) {");
- src.push("vec4 localPosition = vec4(position, 1.0); ");
- if (quantizedGeometry) {
- src.push("localPosition = positionsDecodeMatrix * localPosition;");
- }
- src.push(" vec4 worldPosition = modelMatrix * localPosition; ");
- src.push(" vec4 viewPosition = viewMatrix * worldPosition;");
- if (clipping) {
- src.push(" vWorldPosition = worldPosition;");
- }
- src.push(" vColor = color;");
- src.push(" gl_Position = projMatrix * viewPosition;");
- src.push("}");
- return src;
+function buildVertex(mesh) {
+ const scene = mesh.scene;
+ const clipping = scene._clipsState.clips.length > 0;
+ const quantizedGeometry = !!mesh._geometry._state.quantized;
+ const billboard = mesh._state.billboard;
+ const stationary = mesh._state.stationary;
+ const src = [];
+ src.push("// Surface picking vertex shader");
+ src.push("attribute vec3 position;");
+ src.push("attribute vec4 color;");
+ src.push("uniform mat4 modelMatrix;");
+ src.push("uniform mat4 viewMatrix;");
+ src.push("uniform mat4 projMatrix;");
+ if (clipping) {
+ src.push("uniform bool clippable;");
+ src.push("varying vec4 vWorldPosition;");
+ }
+ src.push("varying vec4 vColor;");
+ if (quantizedGeometry) {
+ src.push("uniform mat4 positionsDecodeMatrix;");
+ }
+ src.push("void main(void) {");
+ src.push("vec4 localPosition = vec4(position, 1.0); ");
+ if (quantizedGeometry) {
+ src.push("localPosition = positionsDecodeMatrix * localPosition;");
}
+ src.push(" vec4 worldPosition = modelMatrix * localPosition; ");
+ src.push(" vec4 viewPosition = viewMatrix * worldPosition;");
+ if (clipping) {
+ src.push(" vWorldPosition = worldPosition;");
+ }
+ src.push(" vColor = color;");
+ src.push(" gl_Position = projMatrix * viewPosition;");
+ src.push("}");
+ return src;
+}
- function buildFragment(mesh) {
- const scene = mesh.scene;
- const clipsState = scene._clipsState;
- const clipping = clipsState.clips.length > 0;
- const src = [];
- src.push("// Surface picking fragment shader");
- src.push("precision lowp float;");
- src.push("varying vec4 vColor;");
- if (clipping) {
- src.push("uniform bool clippable;");
- src.push("varying vec4 vWorldPosition;");
- for (var i = 0; i < clipsState.clips.length; i++) {
- src.push("uniform bool clipActive" + i + ";");
- src.push("uniform vec3 clipPos" + i + ";");
- src.push("uniform vec3 clipDir" + i + ";");
- }
+function buildFragment(mesh) {
+ const scene = mesh.scene;
+ const clipsState = scene._clipsState;
+ const clipping = clipsState.clips.length > 0;
+ const src = [];
+ src.push("// Surface picking fragment shader");
+ src.push("precision lowp float;");
+ src.push("varying vec4 vColor;");
+ if (clipping) {
+ src.push("uniform bool clippable;");
+ src.push("varying vec4 vWorldPosition;");
+ for (var i = 0; i < clipsState.clips.length; i++) {
+ src.push("uniform bool clipActive" + i + ";");
+ src.push("uniform vec3 clipPos" + i + ";");
+ src.push("uniform vec3 clipDir" + i + ";");
}
- src.push("void main(void) {");
- if (clipping) {
- src.push("if (clippable) {");
- src.push(" float dist = 0.0;");
- for (var i = 0; i < clipsState.clips.length; i++) {
- src.push("if (clipActive" + i + ") {");
- src.push(" dist += clamp(dot(-clipDir" + i + ".xyz, vWorldPosition.xyz - clipPos" + i + ".xyz), 0.0, 1000.0);");
- src.push("}");
- }
- src.push(" if (dist > 0.0) { discard; }");
+ }
+ src.push("void main(void) {");
+ if (clipping) {
+ src.push("if (clippable) {");
+ src.push(" float dist = 0.0;");
+ for (var i = 0; i < clipsState.clips.length; i++) {
+ src.push("if (clipActive" + i + ") {");
+ src.push(" dist += clamp(dot(-clipDir" + i + ".xyz, vWorldPosition.xyz - clipPos" + i + ".xyz), 0.0, 1000.0);");
src.push("}");
}
- src.push(" gl_FragColor = vColor;");
+ src.push(" if (dist > 0.0) { discard; }");
src.push("}");
- return src;
}
-})();
\ No newline at end of file
+ src.push(" gl_FragColor = vColor;");
+ src.push("}");
+ return src;
+}
+
+export{PickTriangleShaderSource};
\ No newline at end of file
diff --git a/src/renderer/pick/pickVertexRenderer.js b/src/renderer/pick/pickVertexRenderer.js
index 447cf79b8..cb3b77aec 100644
--- a/src/renderer/pick/pickVertexRenderer.js
+++ b/src/renderer/pick/pickVertexRenderer.js
@@ -2,147 +2,148 @@
* @author xeolabs / https://github.com/xeolabs
*/
-(function () {
+import {PickVertexShaderSource} from "./pickVertexShaderSource.js";
+import {Program} from "../program.js";
+import {stats} from "../../stats.js";
- "use strict";
+const PickVertexRenderer = function (hash, mesh) {
+ const gl = mesh.scene.canvas.gl;
+ this._hash = hash;
+ this._shaderSource = new PickVertexShaderSource(mesh);
+ this._program = new Program(gl, this._shaderSource);
+ this._scene = mesh.scene;
+ this._useCount = 0;
+ if (this._program.errors) {
+ this.errors = this._program.errors;
+ return;
+ }
+ const program = this._program;
+ this._uPositionsDecodeMatrix = program.getLocation("positionsDecodeMatrix");
+ this._uModelMatrix = program.getLocation("modelMatrix");
+ this._uViewMatrix = program.getLocation("viewMatrix");
+ this._uProjMatrix = program.getLocation("projMatrix");
+ this._uClips = [];
+ const clips = mesh.scene._clipsState.clips;
+ for (let i = 0, len = clips.length; i < len; i++) {
+ this._uClips.push({
+ active: program.getLocation("clipActive" + i),
+ pos: program.getLocation("clipPos" + i),
+ dir: program.getLocation("clipDir" + i)
+ });
+ }
+ this._aPosition = program.getAttribute("position");
+ this._aColor = program.getAttribute("color");
+ this._uClippable = program.getLocation("clippable");
+};
- xeogl.renderer.PickVertexRenderer = function (hash, mesh) {
- const gl = mesh.scene.canvas.gl;
- this._hash = hash;
- this._shaderSource = new xeogl.renderer.PickVertexShaderSource(mesh);
- this._program = new xeogl.renderer.Program(gl, this._shaderSource);
- this._scene = mesh.scene;
- this._useCount = 0;
- if (this._program.errors) {
- this.errors = this._program.errors;
- return;
- }
- const program = this._program;
- this._uPositionsDecodeMatrix = program.getLocation("positionsDecodeMatrix");
- this._uModelMatrix = program.getLocation("modelMatrix");
- this._uViewMatrix = program.getLocation("viewMatrix");
- this._uProjMatrix = program.getLocation("projMatrix");
- this._uClips = [];
- const clips = mesh.scene._clipsState.clips;
- for (let i = 0, len = clips.length; i < len; i++) {
- this._uClips.push({
- active: program.getLocation("clipActive" + i),
- pos: program.getLocation("clipPos" + i),
- dir: program.getLocation("clipDir" + i)
- });
- }
- this._aPosition = program.getAttribute("position");
- this._aColor = program.getAttribute("color");
- this._uClippable = program.getLocation("clippable");
- };
-
- const renderers = {};
+const renderers = {};
- xeogl.renderer.PickVertexRenderer.get = function (scene, mesh) {
- const hash = [
- mesh.scene.canvas.canvas.id,
- mesh.scene._clipsState.getHash(),
- mesh._geometry._state.quantized ? "cp" : "",
- mesh._state.hash
- ].join(";");
- let renderer = renderers[hash];
- if (!renderer) {
- renderer = new xeogl.renderer.PickVertexRenderer(hash, mesh);
- if (renderer.errors) {
- console.log(renderer.errors.join("\n"));
- return null;
- }
- renderers[hash] = renderer;
- xeogl.stats.memory.programs++;
+PickVertexRenderer.get = function (scene, mesh) {
+ const hash = [
+ mesh.scene.canvas.canvas.id,
+ mesh.scene._clipsState.getHash(),
+ mesh._geometry._state.quantized ? "cp" : "",
+ mesh._state.hash
+ ].join(";");
+ let renderer = renderers[hash];
+ if (!renderer) {
+ renderer = new PickVertexRenderer(hash, mesh);
+ if (renderer.errors) {
+ console.log(renderer.errors.join("\n"));
+ return null;
}
- renderer._useCount++;
- return renderer;
- };
+ renderers[hash] = renderer;
+ stats.memory.programs++;
+ }
+ renderer._useCount++;
+ return renderer;
+};
- xeogl.renderer.PickVertexRenderer.prototype.put = function () {
- if (--this._useCount === 0) {
- if (this._program) {
- this._program.destroy();
- }
- delete renderers[this._hash];
- xeogl.stats.memory.programs--;
+PickVertexRenderer.prototype.put = function () {
+ if (--this._useCount === 0) {
+ if (this._program) {
+ this._program.destroy();
}
- };
+ delete renderers[this._hash];
+ stats.memory.programs--;
+ }
+};
- xeogl.renderer.PickVertexRenderer.prototype.webglContextRestored = function () {
- this._program = null;
- };
+PickVertexRenderer.prototype.webglContextRestored = function () {
+ this._program = null;
+};
- xeogl.renderer.PickVertexRenderer.prototype._bindProgram = function (frame) {
- const scene = this._scene;
- const gl = scene.canvas.gl;
- const clipsState = scene._clipsState;
- const camera = scene.camera;
- const cameraState = camera._state;
- this._program.bind();
- frame.useProgram++;
- this._lastVertexBufsId = null;
- this._lastGeometryId = null;
- gl.uniformMatrix4fv(this._uViewMatrix, false, cameraState.matrix);
- gl.uniformMatrix4fv(this._uProjMatrix, false, camera.project._state.matrix);
- if (clipsState.clips.length > 0) {
- const clips = clipsState.clips;
- let clipUniforms;
- let uClipActive;
- let clip;
- let uClipPos;
- let uClipDir;
- for (let i = 0, len = this._uClips.length; i < len; i++) {
- clipUniforms = this._uClips[i];
- uClipActive = clipUniforms.active;
- clip = clips[i];
- if (uClipActive) {
- gl.uniform1i(uClipActive, clip.active);
- }
- uClipPos = clipUniforms.pos;
- if (uClipPos) {
- gl.uniform3fv(clipUniforms.pos, clip.pos);
- }
- uClipDir = clipUniforms.dir;
- if (uClipDir) {
- gl.uniform3fv(clipUniforms.dir, clip.dir);
- }
+PickVertexRenderer.prototype._bindProgram = function (frame) {
+ const scene = this._scene;
+ const gl = scene.canvas.gl;
+ const clipsState = scene._clipsState;
+ const camera = scene.camera;
+ const cameraState = camera._state;
+ this._program.bind();
+ frame.useProgram++;
+ this._lastVertexBufsId = null;
+ this._lastGeometryId = null;
+ gl.uniformMatrix4fv(this._uViewMatrix, false, cameraState.matrix);
+ gl.uniformMatrix4fv(this._uProjMatrix, false, camera.project._state.matrix);
+ if (clipsState.clips.length > 0) {
+ const clips = clipsState.clips;
+ let clipUniforms;
+ let uClipActive;
+ let clip;
+ let uClipPos;
+ let uClipDir;
+ for (let i = 0, len = this._uClips.length; i < len; i++) {
+ clipUniforms = this._uClips[i];
+ uClipActive = clipUniforms.active;
+ clip = clips[i];
+ if (uClipActive) {
+ gl.uniform1i(uClipActive, clip.active);
+ }
+ uClipPos = clipUniforms.pos;
+ if (uClipPos) {
+ gl.uniform3fv(clipUniforms.pos, clip.pos);
+ }
+ uClipDir = clipUniforms.dir;
+ if (uClipDir) {
+ gl.uniform3fv(clipUniforms.dir, clip.dir);
}
}
- };
+ }
+};
- xeogl.renderer.PickVertexRenderer.prototype.drawMesh = function (frame, mesh) {
- const scene = this._scene;
- const gl = scene.canvas.gl;
- const geometryState = mesh._geometry._state;
- if (frame.lastProgramId !== this._program.id) {
- frame.lastProgramId = this._program.id;
- this._bindProgram(frame);
- }
- gl.uniformMatrix4fv(this._uModelMatrix, gl.FALSE, mesh.worldMatrix);
- if (this._uClippable) {
- gl.uniform1i(this._uClippable, mesh._state.clippable);
+PickVertexRenderer.prototype.drawMesh = function (frame, mesh) {
+ const scene = this._scene;
+ const gl = scene.canvas.gl;
+ const geometryState = mesh._geometry._state;
+ if (frame.lastProgramId !== this._program.id) {
+ frame.lastProgramId = this._program.id;
+ this._bindProgram(frame);
+ }
+ gl.uniformMatrix4fv(this._uModelMatrix, gl.FALSE, mesh.worldMatrix);
+ if (this._uClippable) {
+ gl.uniform1i(this._uClippable, mesh._state.clippable);
+ }
+ // Bind VBOs
+ if (geometryState.id !== this._lastGeometryId) {
+ const pickPositionsBuf = geometryState.getVertexPickPositions();
+ if (this._uPositionsDecodeMatrix) {
+ gl.uniformMatrix4fv(this._uPositionsDecodeMatrix, false, geometryState.positionsDecodeMatrix);
+ this._aPosition.bindArrayBuffer(pickPositionsBuf, geometryState.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
+ } else {
+ this._aPosition.bindArrayBuffer(pickPositionsBuf);
}
- // Bind VBOs
- if (geometryState.id !== this._lastGeometryId) {
- const pickPositionsBuf = geometryState.getVertexPickPositions();
- if (this._uPositionsDecodeMatrix) {
- gl.uniformMatrix4fv(this._uPositionsDecodeMatrix, false, geometryState.positionsDecodeMatrix);
- this._aPosition.bindArrayBuffer(pickPositionsBuf, geometryState.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
- } else {
- this._aPosition.bindArrayBuffer(pickPositionsBuf);
- }
- const pickColorsBuf = geometryState.getVertexPickColors();
- pickColorsBuf.bind();
- gl.enableVertexAttribArray(this._aColor.location);
- this._gl.vertexAttribPointer(this._aColor.location, pickColorsBuf.itemSize, pickColorsBuf.itemType, true, 0, 0); // Normalize
- this._lastGeometryId = geometryState.id;
- }
- // TODO: load point size
- // FIXME make points
- gl.drawArrays(geometryState.primitive, 0, positions.numItems / 3);
- };
-})();
+ const pickColorsBuf = geometryState.getVertexPickColors();
+ pickColorsBuf.bind();
+ gl.enableVertexAttribArray(this._aColor.location);
+ this._gl.vertexAttribPointer(this._aColor.location, pickColorsBuf.itemSize, pickColorsBuf.itemType, true, 0, 0); // Normalize
+ this._lastGeometryId = geometryState.id;
+ }
+ // TODO: load point size
+ // FIXME make points
+ gl.drawArrays(geometryState.primitive, 0, positions.numItems / 3);
+};
+
+export{PickVertexRenderer};
diff --git a/src/renderer/pick/pickVertexShaderSource.js b/src/renderer/pick/pickVertexShaderSource.js
index 08fd8de99..37cd5120b 100644
--- a/src/renderer/pick/pickVertexShaderSource.js
+++ b/src/renderer/pick/pickVertexShaderSource.js
@@ -2,85 +2,83 @@
* @author xeolabs / https://github.com/xeolabs
*/
-(function () {
-
- "use strict";
-
- xeogl.renderer.PickVertexShaderSource = function (mesh) {
+class PickVertexShaderSource {
+ constructor(mesh) {
this.vertex = buildVertex(mesh);
this.fragment = buildFragment(mesh);
- };
+ }
+}
- function buildVertex(mesh) {
- const scene = mesh.scene;
- const clipping = scene._clipsState.clips.length > 0;
- const quantizedGeometry = !!mesh._geometry._state.quantized;
- const billboard = mesh._state.billboard;
- const stationary = mesh._state.stationary;
- const src = [];
- src.push("// Surface picking vertex shader");
- src.push("attribute vec3 position;");
- src.push("attribute vec4 color;");
- src.push("uniform mat4 modelMatrix;");
- src.push("uniform mat4 viewMatrix;");
- src.push("uniform mat4 projMatrix;");
- if (clipping) {
- src.push("uniform bool clippable;");
- src.push("varying vec4 vWorldPosition;");
- }
- src.push("varying vec4 vColor;");
- if (quantizedGeometry) {
- src.push("uniform mat4 positionsDecodeMatrix;");
- }
- src.push("void main(void) {");
- src.push("vec4 localPosition = vec4(position, 1.0); ");
- if (quantizedGeometry) {
- src.push("localPosition = positionsDecodeMatrix * localPosition;");
- }
- src.push(" vec4 worldPosition = modelMatrix * localPosition; ");
- src.push(" vec4 viewPosition = viewMatrix * worldPosition;");
- if (clipping) {
- src.push(" vWorldPosition = worldPosition;");
- }
- src.push(" vColor = color;");
- src.push(" gl_Position = projMatrix * viewPosition;");
- src.push("}");
- return src;
+function buildVertex(mesh) {
+ const scene = mesh.scene;
+ const clipping = scene._clipsState.clips.length > 0;
+ const quantizedGeometry = !!mesh._geometry._state.quantized;
+ const billboard = mesh._state.billboard;
+ const stationary = mesh._state.stationary;
+ const src = [];
+ src.push("// Surface picking vertex shader");
+ src.push("attribute vec3 position;");
+ src.push("attribute vec4 color;");
+ src.push("uniform mat4 modelMatrix;");
+ src.push("uniform mat4 viewMatrix;");
+ src.push("uniform mat4 projMatrix;");
+ if (clipping) {
+ src.push("uniform bool clippable;");
+ src.push("varying vec4 vWorldPosition;");
+ }
+ src.push("varying vec4 vColor;");
+ if (quantizedGeometry) {
+ src.push("uniform mat4 positionsDecodeMatrix;");
+ }
+ src.push("void main(void) {");
+ src.push("vec4 localPosition = vec4(position, 1.0); ");
+ if (quantizedGeometry) {
+ src.push("localPosition = positionsDecodeMatrix * localPosition;");
+ }
+ src.push(" vec4 worldPosition = modelMatrix * localPosition; ");
+ src.push(" vec4 viewPosition = viewMatrix * worldPosition;");
+ if (clipping) {
+ src.push(" vWorldPosition = worldPosition;");
}
+ src.push(" vColor = color;");
+ src.push(" gl_Position = projMatrix * viewPosition;");
+ src.push("}");
+ return src;
+}
- function buildFragment(mesh) {
- const scene = mesh.scene;
- const clipsState = scene._clipsState;
- const clipping = clipsState.clips.length > 0;
- const src = [];
- src.push("// Surface picking fragment shader");
- src.push("precision lowp float;");
- src.push("varying vec4 vColor;");
- if (clipping) {
- src.push("uniform bool clippable;");
- src.push("varying vec4 vWorldPosition;");
- for (var i = 0; i < clipsState.clips.length; i++) {
- src.push("uniform bool clipActive" + i + ";");
- src.push("uniform vec3 clipPos" + i + ";");
- src.push("uniform vec3 clipDir" + i + ";");
- }
+function buildFragment(mesh) {
+ const scene = mesh.scene;
+ const clipsState = scene._clipsState;
+ const clipping = clipsState.clips.length > 0;
+ const src = [];
+ src.push("// Surface picking fragment shader");
+ src.push("precision lowp float;");
+ src.push("varying vec4 vColor;");
+ if (clipping) {
+ src.push("uniform bool clippable;");
+ src.push("varying vec4 vWorldPosition;");
+ for (var i = 0; i < clipsState.clips.length; i++) {
+ src.push("uniform bool clipActive" + i + ";");
+ src.push("uniform vec3 clipPos" + i + ";");
+ src.push("uniform vec3 clipDir" + i + ";");
}
- src.push("void main(void) {");
- if (clipping) {
- src.push("if (clippable) {");
- src.push(" float dist = 0.0;");
- for (var i = 0; i < clipsState.clips.length; i++) {
- src.push("if (clipActive" + i + ") {");
- src.push(" dist += clamp(dot(-clipDir" + i + ".xyz, vWorldPosition.xyz - clipPos" + i + ".xyz), 0.0, 1000.0);");
- src.push("}");
- }
- src.push(" if (dist > 0.0) { discard; }");
+ }
+ src.push("void main(void) {");
+ if (clipping) {
+ src.push("if (clippable) {");
+ src.push(" float dist = 0.0;");
+ for (var i = 0; i < clipsState.clips.length; i++) {
+ src.push("if (clipActive" + i + ") {");
+ src.push(" dist += clamp(dot(-clipDir" + i + ".xyz, vWorldPosition.xyz - clipPos" + i + ".xyz), 0.0, 1000.0);");
src.push("}");
}
-
- src.push(" gl_FragColor = vColor;");
+ src.push(" if (dist > 0.0) { discard; }");
src.push("}");
- return src;
}
-})();
\ No newline at end of file
+ src.push(" gl_FragColor = vColor;");
+ src.push("}");
+ return src;
+}
+
+export{PickVertexShaderSource};
\ No newline at end of file
diff --git a/src/renderer/program.js b/src/renderer/program.js
index 90bab0787..d06028acc 100644
--- a/src/renderer/program.js
+++ b/src/renderer/program.js
@@ -2,19 +2,39 @@
* @author xeolabs / https://github.com/xeolabs
*/
-(function () {
+import {Map} from "../utils/map.js";
+import {Shader} from "./shader.js";
+import {Sampler} from "./sampler.js";
+import {Attribute} from "./attribute.js";
- "use strict";
+const ids = new Map({});
- const ids = new xeogl.utils.Map({});
+function joinSansComments(srcLines) {
+ const src = [];
+ let line;
+ let n;
+ for (let i = 0, len = srcLines.length; i < len; i++) {
+ line = srcLines[i];
+ n = line.indexOf("/");
+ if (n > 0) {
+ if (line.charAt(n + 1) === "/") {
+ line = line.substring(0, n);
+ }
+ }
+ src.push(line);
+ }
+ return src.join("\n");
+}
+
+class Program {
- xeogl.renderer.Program = function (gl, shaderSource) {
+ constructor(gl, shaderSource) {
this.id = ids.addItem({});
this.source = shaderSource;
this.init(gl);
- };
+ }
- xeogl.renderer.Program.prototype.init = function (gl) {
+ init(gl) {
this.gl = gl;
this.allocated = false;
this.compiled = false;
@@ -24,8 +44,8 @@
this.uniforms = {};
this.samplers = {};
this.attributes = {};
- this._vertexShader = new xeogl.renderer.Shader(gl, gl.VERTEX_SHADER, joinSansComments(this.source.vertex));
- this._fragmentShader = new xeogl.renderer.Shader(gl, gl.FRAGMENT_SHADER, joinSansComments(this.source.fragment));
+ this._vertexShader = new Shader(gl, gl.VERTEX_SHADER, joinSansComments(this.source.vertex));
+ this._fragmentShader = new Shader(gl, gl.FRAGMENT_SHADER, joinSansComments(this.source.fragment));
if (!this._vertexShader.allocated) {
this.errors = ["Vertex shader failed to allocate"].concat(this._vertexShader.errors);
return;
@@ -81,7 +101,7 @@
}
location = gl.getUniformLocation(this.handle, uName);
if ((u.type === gl.SAMPLER_2D) || (u.type === gl.SAMPLER_CUBE) || (u.type === 35682)) {
- this.samplers[uName] = new xeogl.renderer.Sampler(gl, location);
+ this.samplers[uName] = new Sampler(gl, location);
} else {
this.uniforms[uName] = location;
}
@@ -92,51 +112,34 @@
a = gl.getActiveAttrib(this.handle, i);
if (a) {
location = gl.getAttribLocation(this.handle, a.name);
- this.attributes[a.name] = new xeogl.renderer.Attribute(gl, location);
+ this.attributes[a.name] = new Attribute(gl, location);
}
}
this.allocated = true;
- };
-
- function joinSansComments(srcLines) {
- const src = [];
- let line;
- let n;
- for (let i = 0, len = srcLines.length; i < len; i++) {
- line = srcLines[i];
- n = line.indexOf("/");
- if (n > 0) {
- if (line.charAt(n + 1) === "/") {
- line = line.substring(0, n);
- }
- }
- src.push(line);
- }
- return src.join("\n");
}
- xeogl.renderer.Program.prototype.bind = function () {
+ bind() {
if (!this.allocated) {
return;
}
this.gl.useProgram(this.handle);
- };
+ }
- xeogl.renderer.Program.prototype.getLocation = function (name) {
+ getLocation(name) {
if (!this.allocated) {
return;
}
return this.uniforms[name];
- };
+ }
- xeogl.renderer.Program.prototype.getAttribute = function (name) {
+ getAttribute(name) {
if (!this.allocated) {
return;
}
return this.attributes[name];
- };
+ }
- xeogl.renderer.Program.prototype.bindTexture = function (name, texture, unit) {
+ bindTexture(name, texture, unit) {
if (!this.allocated) {
return false;
}
@@ -146,9 +149,9 @@
} else {
return false;
}
- };
+ }
- xeogl.renderer.Program.prototype.destroy = function () {
+ destroy() {
if (!this.allocated) {
return;
}
@@ -161,5 +164,7 @@
this.uniforms = null;
this.samplers = null;
this.allocated = false;
- };
-})();
+ }
+}
+
+export{Program};
\ No newline at end of file
diff --git a/src/renderer/renderBuffer.js b/src/renderer/renderBuffer.js
index 14539855b..5c0ea3c4f 100644
--- a/src/renderer/renderBuffer.js
+++ b/src/renderer/renderBuffer.js
@@ -2,188 +2,191 @@
* @author xeolabs / https://github.com/xeolabs
*/
-xeogl.renderer.RenderBuffer = function (canvas, gl, options) {
-
- options = options || {};
-
- this.gl = gl;
- this.allocated = false;
- this.canvas = canvas;
- this.buffer = null;
- this.bound = false;
- this.size = options.size;
-};
-
-xeogl.renderer.RenderBuffer.prototype.setSize = function (size) {
- this.size = size;
-};
-
-xeogl.renderer.RenderBuffer.prototype.webglContextRestored = function (gl) {
- this.gl = gl;
- this.buffer = null;
- this.allocated = false;
- this.bound = false;
-};
-
-xeogl.renderer.RenderBuffer.prototype.bind = function () {
- this._touch();
- if (this.bound) {
- return;
- }
- const gl = this.gl;
- gl.bindFramebuffer(gl.FRAMEBUFFER, this.buffer.framebuf);
- this.bound = true;
-};
+class RenderBuffer {
-xeogl.renderer.RenderBuffer.prototype._touch = function () {
+ constructor(canvas, gl, options) {
+ options = options || {};
+ this.gl = gl;
+ this.allocated = false;
+ this.canvas = canvas;
+ this.buffer = null;
+ this.bound = false;
+ this.size = options.size;
+ }
- let width;
- let height;
- const gl = this.gl;
+ setSize(size) {
+ this.size = size;
+ }
- if (this.size) {
- width = this.size[0];
- height = this.size[1];
+ webglContextRestored(gl) {
+ this.gl = gl;
+ this.buffer = null;
+ this.allocated = false;
+ this.bound = false;
+ }
- } else {
- width = this.canvas.clientWidth;
- height = this.canvas.clientHeight;
+ bind() {
+ this._touch();
+ if (this.bound) {
+ return;
+ }
+ const gl = this.gl;
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.buffer.framebuf);
+ this.bound = true;
}
- if (this.buffer) {
+ _touch() {
- if (this.buffer.width === width && this.buffer.height === height) {
- return;
+ let width;
+ let height;
+ const gl = this.gl;
+
+ if (this.size) {
+ width = this.size[0];
+ height = this.size[1];
} else {
- gl.deleteTexture(this.buffer.texture);
- gl.deleteFramebuffer(this.buffer.framebuf);
- gl.deleteRenderbuffer(this.buffer.renderbuf);
+ width = this.canvas.clientWidth;
+ height = this.canvas.clientHeight;
}
- }
- // width = 1024;
- // height = 1024;
+ if (this.buffer) {
- // width = 1024;
- // height = 1024;
+ if (this.buffer.width === width && this.buffer.height === height) {
+ return;
- const texture = gl.createTexture();
- gl.bindTexture(gl.TEXTURE_2D, texture);
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+ } else {
+ gl.deleteTexture(this.buffer.texture);
+ gl.deleteFramebuffer(this.buffer.framebuf);
+ gl.deleteRenderbuffer(this.buffer.renderbuf);
+ }
+ }
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+ // width = 1024;
+ // height = 1024;
- const renderbuf = gl.createRenderbuffer();
- gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuf);
- gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
+ // width = 1024;
+ // height = 1024;
- const framebuf = gl.createFramebuffer();
- gl.bindFramebuffer(gl.FRAMEBUFFER, framebuf);
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
- gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuf);
+ const texture = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
- gl.bindTexture(gl.TEXTURE_2D, null);
- gl.bindRenderbuffer(gl.RENDERBUFFER, null);
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- // Verify framebuffer is OK
+ const renderbuf = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuf);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
- gl.bindFramebuffer(gl.FRAMEBUFFER, framebuf);
- if (!gl.isFramebuffer(framebuf)) {
- throw "Invalid framebuffer";
- }
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ const framebuf = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, framebuf);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuf);
- const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ gl.bindRenderbuffer(gl.RENDERBUFFER, null);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- switch (status) {
+ // Verify framebuffer is OK
- case gl.FRAMEBUFFER_COMPLETE:
- break;
+ gl.bindFramebuffer(gl.FRAMEBUFFER, framebuf);
+ if (!gl.isFramebuffer(framebuf)) {
+ throw "Invalid framebuffer";
+ }
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
- throw "Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
+ const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
- case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
- throw "Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
+ switch (status) {
- case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
- throw "Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
+ case gl.FRAMEBUFFER_COMPLETE:
+ break;
- case gl.FRAMEBUFFER_UNSUPPORTED:
- throw "Incomplete framebuffer: FRAMEBUFFER_UNSUPPORTED";
+ case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
+ throw "Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
- default:
- throw "Incomplete framebuffer: " + status;
- }
+ case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
+ throw "Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
- this.buffer = {
- framebuf: framebuf,
- renderbuf: renderbuf,
- texture: texture,
- width: width,
- height: height
- };
+ case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
+ throw "Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
- this.bound = false;
-};
+ case gl.FRAMEBUFFER_UNSUPPORTED:
+ throw "Incomplete framebuffer: FRAMEBUFFER_UNSUPPORTED";
-xeogl.renderer.RenderBuffer.prototype.clear = function () {
- if (!this.bound) {
- throw "Render buffer not bound";
+ default:
+ throw "Incomplete framebuffer: " + status;
+ }
+
+ this.buffer = {
+ framebuf: framebuf,
+ renderbuf: renderbuf,
+ texture: texture,
+ width: width,
+ height: height
+ };
+
+ this.bound = false;
}
- const gl = this.gl;
- gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
-};
-
-xeogl.renderer.RenderBuffer.prototype.read = function (pickX, pickY) {
- const x = pickX;
- const y = this.canvas.height - pickY;
- const pix = new Uint8Array(4);
- const gl = this.gl;
- gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pix);
- return pix;
-};
-
-xeogl.renderer.RenderBuffer.prototype.unbind = function () {
- const gl = this.gl;
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- this.bound = false;
-};
-
-xeogl.renderer.RenderBuffer.prototype.getTexture = function () {
- const self = this;
- return {
- renderBuffer: this,
- bind: function (unit) {
- if (self.buffer && self.buffer.texture) {
- self.gl.activeTexture(self.gl["TEXTURE" + unit]);
- self.gl.bindTexture(self.gl.TEXTURE_2D, self.buffer.texture);
- return true;
- }
- return false;
- },
- unbind: function (unit) {
- if (self.buffer && self.buffer.texture) {
- self.gl.activeTexture(self.gl["TEXTURE" + unit]);
- self.gl.bindTexture(self.gl.TEXTURE_2D, null);
- }
+
+ clear() {
+ if (!this.bound) {
+ throw "Render buffer not bound";
}
- };
-};
+ const gl = this.gl;
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+ }
-xeogl.renderer.RenderBuffer.prototype.destroy = function () {
- if (this.allocated) {
+ read(pickX, pickY) {
+ const x = pickX;
+ const y = this.canvas.height - pickY;
+ const pix = new Uint8Array(4);
const gl = this.gl;
- gl.deleteTexture(this.buffer.texture);
- gl.deleteFramebuffer(this.buffer.framebuf);
- gl.deleteRenderbuffer(this.buffer.renderbuf);
- this.allocated = false;
- this.buffer = null;
+ gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pix);
+ return pix;
+ }
+
+ unbind() {
+ const gl = this.gl;
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
this.bound = false;
}
-};
+
+ getTexture() {
+ const self = this;
+ return {
+ renderBuffer: this,
+ bind: function (unit) {
+ if (self.buffer && self.buffer.texture) {
+ self.gl.activeTexture(self.gl["TEXTURE" + unit]);
+ self.gl.bindTexture(self.gl.TEXTURE_2D, self.buffer.texture);
+ return true;
+ }
+ return false;
+ },
+ unbind: function (unit) {
+ if (self.buffer && self.buffer.texture) {
+ self.gl.activeTexture(self.gl["TEXTURE" + unit]);
+ self.gl.bindTexture(self.gl.TEXTURE_2D, null);
+ }
+ }
+ };
+ }
+
+ destroy() {
+ if (this.allocated) {
+ const gl = this.gl;
+ gl.deleteTexture(this.buffer.texture);
+ gl.deleteFramebuffer(this.buffer.framebuf);
+ gl.deleteRenderbuffer(this.buffer.renderbuf);
+ this.allocated = false;
+ this.buffer = null;
+ this.bound = false;
+ }
+ }
+}
+
+export{RenderBuffer};
\ No newline at end of file
diff --git a/src/renderer/renderer.js b/src/renderer/renderer.js
index 6ee6551f3..d464f23a7 100644
--- a/src/renderer/renderer.js
+++ b/src/renderer/renderer.js
@@ -2,17 +2,20 @@
* @author xeolabs / https://github.com/xeolabs
*/
-xeogl.renderer = xeogl.renderer || {};
+import {Frame} from './frame.js';
+import {RenderBuffer} from './renderBuffer.js';
+import {math} from '../math/math.js';
+import {stats} from './../stats.js';
+import {WEBGL_INFO} from './../webglInfo.js';
+import {Mesh} from '../objects/mesh.js';
-
-xeogl.renderer.Renderer = function (stats, scene, options) {
+const Renderer = function ( scene, options) {
"use strict";
options = options || {};
- stats = stats || {};
- const frame = new xeogl.renderer.Frame();
+ const frame = new Frame();
const canvas = scene.canvas.canvas;
const gl = scene.canvas.gl;
const shadowLightMeshes = {};
@@ -121,7 +124,7 @@ xeogl.renderer.Renderer = function (stats, scene, options) {
stateSortDirty = true;
}
if (stateSortDirty) {
- meshList.sort(xeogl.Mesh._compareState);
+ meshList.sort(Mesh._compareState);
stateSortDirty = false;
imageDirty = true;
}
@@ -145,7 +148,7 @@ xeogl.renderer.Renderer = function (stats, scene, options) {
}
function stateSort() {
- meshList.sort(xeogl.Mesh._compareState);
+ meshList.sort(Mesh._compareState);
}
function drawShadowMaps() {
@@ -245,10 +248,10 @@ xeogl.renderer.Renderer = function (stats, scene, options) {
return function (params) {
- if (xeogl.WEBGL_INFO.SUPPORTED_EXTENSIONS["OES_element_index_uint"]) { // In case context lost/recovered
+ if (WEBGL_INFO.SUPPORTED_EXTENSIONS["OES_element_index_uint"]) { // In case context lost/recovered
gl.getExtension("OES_element_index_uint");
}
- if (xeogl.WEBGL_INFO.SUPPORTED_EXTENSIONS["OES_standard_derivatives"]) { // For normal mapping w/o precomputed tangents
+ if (WEBGL_INFO.SUPPORTED_EXTENSIONS["OES_standard_derivatives"]) { // For normal mapping w/o precomputed tangents
gl.getExtension("OES_standard_derivatives");
}
@@ -700,7 +703,7 @@ xeogl.renderer.Renderer = function (stats, scene, options) {
frameStats.bindTexture = frame.bindTexture;
frameStats.bindArray = frame.bindArray;
- const numTextureUnits = xeogl.WEBGL_INFO.MAX_TEXTURE_UNITS;
+ const numTextureUnits = WEBGL_INFO.MAX_TEXTURE_UNITS;
for (let ii = 0; ii < numTextureUnits; ii++) {
gl.activeTexture(gl.TEXTURE0 + ii);
}
@@ -724,7 +727,6 @@ xeogl.renderer.Renderer = function (stats, scene, options) {
*/
this.pick = (function () {
- const math = xeogl.math;
const tempVec3a = math.vec3();
const tempMat4a = math.mat4();
const up = math.vec3([0, 1, 0]);
@@ -734,7 +736,7 @@ xeogl.renderer.Renderer = function (stats, scene, options) {
update();
- if (xeogl.WEBGL_INFO.SUPPORTED_EXTENSIONS["OES_element_index_uint"]) { // In case context lost/recovered
+ if (WEBGL_INFO.SUPPORTED_EXTENSIONS["OES_element_index_uint"]) { // In case context lost/recovered
gl.getExtension("OES_element_index_uint");
}
@@ -767,7 +769,7 @@ xeogl.renderer.Renderer = function (stats, scene, options) {
canvasY = canvas.clientHeight * 0.5;
}
- pickBuf = pickBuf || new xeogl.renderer.RenderBuffer(canvas, gl);
+ pickBuf = pickBuf || new RenderBuffer(canvas, gl);
pickBuf.bind();
const mesh = pickMesh(canvasX, canvasY, pickViewMatrix, pickProjMatrix, params);
@@ -881,7 +883,7 @@ xeogl.renderer.Renderer = function (stats, scene, options) {
* @param opaqueOnly
*/
this.readPixels = function (pixels, colors, len, opaqueOnly) {
- readPixelBuf = readPixelBuf || (readPixelBuf = new xeogl.renderer.RenderBuffer(canvas, gl));
+ readPixelBuf = readPixelBuf || (readPixelBuf = new RenderBuffer(canvas, gl));
readPixelBuf.bind();
readPixelBuf.clear();
this.render({force: true, opaqueOnly: opaqueOnly});
@@ -914,3 +916,5 @@ xeogl.renderer.Renderer = function (stats, scene, options) {
}
};
};
+
+export{Renderer};
\ No newline at end of file
diff --git a/src/renderer/sampler.js b/src/renderer/sampler.js
index 8c1d86612..ac91ef3c4 100644
--- a/src/renderer/sampler.js
+++ b/src/renderer/sampler.js
@@ -2,12 +2,17 @@
* @author xeolabs / https://github.com/xeolabs
*/
-xeogl.renderer.Sampler = function (gl, location) {
- this.bindTexture = function (texture, unit) {
- if (texture.bind(unit)) {
- gl.uniform1i(location, unit);
- return true;
- }
- return false;
- };
-};
\ No newline at end of file
+class Sampler {
+
+ constructor(gl, location) {
+ this.bindTexture = function (texture, unit) {
+ if (texture.bind(unit)) {
+ gl.uniform1i(location, unit);
+ return true;
+ }
+ return false;
+ };
+ }
+}
+
+export{Sampler};
\ No newline at end of file
diff --git a/src/renderer/shader.js b/src/renderer/shader.js
index dd35eb0ea..b2ed0316e 100644
--- a/src/renderer/shader.js
+++ b/src/renderer/shader.js
@@ -2,43 +2,48 @@
* @author xeolabs / https://github.com/xeolabs
*/
-xeogl.renderer.Shader = function (gl, type, source) {
+class Shader {
- this.allocated = false;
- this.compiled = false;
- this.handle = gl.createShader(type);
+ constructor(gl, type, source) {
- if (!this.handle) {
- this.errors = [
- "Failed to allocate"
- ];
- return;
- }
+ this.allocated = false;
+ this.compiled = false;
+ this.handle = gl.createShader(type);
+
+ if (!this.handle) {
+ this.errors = [
+ "Failed to allocate"
+ ];
+ return;
+ }
- this.allocated = true;
+ this.allocated = true;
- gl.shaderSource(this.handle, source);
- gl.compileShader(this.handle);
+ gl.shaderSource(this.handle, source);
+ gl.compileShader(this.handle);
- this.compiled = gl.getShaderParameter(this.handle, gl.COMPILE_STATUS);
+ this.compiled = gl.getShaderParameter(this.handle, gl.COMPILE_STATUS);
- if (!this.compiled) {
+ if (!this.compiled) {
- if (!gl.isContextLost()) { // Handled explicitly elsewhere, so won't re-handle here
+ if (!gl.isContextLost()) { // Handled explicitly elsewhere, so won't re-handle here
- const lines = source.split("\n");
- const numberedLines = [];
- for (let i = 0; i < lines.length; i++) {
- numberedLines.push((i + 1) + ": " + lines[i] + "\n");
+ const lines = source.split("\n");
+ const numberedLines = [];
+ for (let i = 0; i < lines.length; i++) {
+ numberedLines.push((i + 1) + ": " + lines[i] + "\n");
+ }
+ this.errors = [];
+ this.errors.push("");
+ this.errors.push(gl.getShaderInfoLog(this.handle));
+ this.errors = this.errors.concat(numberedLines.join(""));
}
- this.errors = [];
- this.errors.push("");
- this.errors.push(gl.getShaderInfoLog(this.handle));
- this.errors = this.errors.concat(numberedLines.join(""));
}
}
-};
-xeogl.renderer.Shader.prototype.destroy = function () {
+ destroy() {
+
+ }
+}
-};
\ No newline at end of file
+export{Shader};
\ No newline at end of file
diff --git a/src/renderer/shadow/shadowRenderer.js b/src/renderer/shadow/shadowRenderer.js
index ccd3c1cfd..2ca079bd2 100644
--- a/src/renderer/shadow/shadowRenderer.js
+++ b/src/renderer/shadow/shadowRenderer.js
@@ -2,203 +2,202 @@
* @author xeolabs / https://github.com/xeolabs
*/
-(function () {
+import {ShadowShaderSource} from "./shadowShaderSource.js";
+import {Program} from "../program.js";
- "use strict";
+const ShadowRenderer = function (hash, mesh) {
+ this._hash = hash;
+ this._shaderSource = new ShadowShaderSource(mesh);
+ this._program = new Program(mesh.scene.canvas.gl, this._shaderSource);
+ this._scene = scene;
+ this._useCount = 0;
+ if (this._program.errors) {
+ this.errors = this._program.errors;
+ return;
+ }
+ const program = this._program;
+ this._uPositionsDecodeMatrix = program.getLocation("positionsDecodeMatrix");
+ this._uModelMatrix = program.getLocation("modelMatrix");
+ this._uViewMatrix = program.getLocation("viewMatrix");
+ this._uProjMatrix = program.getLocation("projMatrix");
+ this._uClips = {};
+ const clips = mesh.scene._clipsState.clips;
+ for (let i = 0, len = clips.length; i < len; i++) {
+ this._uClips.push({
+ active: program.getLocation("clipActive" + i),
+ pos: program.getLocation("clipPos" + i),
+ dir: program.getLocation("clipDir" + i)
+ });
+ }
+ this._aPosition = program.getAttribute("position");
+ this._uClippable = program.getLocation("clippable");
+ this._lastMaterialId = null;
+ this._lastVertexBufsId = null;
+ this._lastGeometryId = null;
+};
- xeogl.renderer.ShadowRenderer = function (hash, mesh) {
- this._hash = hash;
- this._shaderSource = new xeogl.renderer.ShadowShaderSource(mesh);
- this._program = new xeogl.renderer.Program(mesh.scene.canvas.gl, this._shaderSource);
- this._scene = scene;
- this._useCount = 0;
- if (this._program.errors) {
- this.errors = this._program.errors;
- return;
- }
- const program = this._program;
- this._uPositionsDecodeMatrix = program.getLocation("positionsDecodeMatrix");
- this._uModelMatrix = program.getLocation("modelMatrix");
- this._uViewMatrix = program.getLocation("viewMatrix");
- this._uProjMatrix = program.getLocation("projMatrix");
- this._uClips = {};
- const clips = mesh.scene._clipsState.clips;
- for (let i = 0, len = clips.length; i < len; i++) {
- this._uClips.push({
- active: program.getLocation("clipActive" + i),
- pos: program.getLocation("clipPos" + i),
- dir: program.getLocation("clipDir" + i)
- });
- }
- this._aPosition = program.getAttribute("position");
- this._uClippable = program.getLocation("clippable");
- this._lastMaterialId = null;
- this._lastVertexBufsId = null;
- this._lastGeometryId = null;
- };
-
- const renderers = {};
+const renderers = {};
- xeogl.renderer.ShadowRenderer.get = function (mesh) {
- const hash = [
- mesh.scene.canvas.canvas.id,
- mesh.scene._clipsState.getHash(),
- mesh._geometry._state.hash,
- mesh._state.hash].join(";");
- let renderer = renderers[hash];
- if (!renderer) {
- renderer = new xeogl.renderer.ShadowRenderer(hash, mesh);
- renderers[hash] = renderer;
- }
- renderer._useCount++;
- return renderer;
- };
+ShadowRenderer.get = function (mesh) {
+ const hash = [
+ mesh.scene.canvas.canvas.id,
+ mesh.scene._clipsState.getHash(),
+ mesh._geometry._state.hash,
+ mesh._state.hash].join(";");
+ let renderer = renderers[hash];
+ if (!renderer) {
+ renderer = new ShadowRenderer(hash, mesh);
+ renderers[hash] = renderer;
+ }
+ renderer._useCount++;
+ return renderer;
+};
- xeogl.renderer.ShadowRenderer.prototype.put = function () {
- if (--this._useCount) {
- this._program.destroy();
- delete renderers[this._hash];
- }
- };
+ShadowRenderer.prototype.put = function () {
+ if (--this._useCount) {
+ this._program.destroy();
+ delete renderers[this._hash];
+ }
+};
- xeogl.renderer.ShadowRenderer.prototype._bindProgram = function (frame) {
- const scene = this._scene;
- const gl = scene.canvas.gl;
- const clipsState = scene._clipsState;
- this._program.bind();
- frame.useProgram++;
- this._lastLightId = null;
- this._lastMaterialId = null;
- this._lastVertexBufsId = null;
- this._lastGeometryId = null;
- if (clipsState.clips.length > 0) {
- let clipUniforms;
- let uClipActive;
- let clip;
- let uClipPos;
- let uClipDir;
- for (let i = 0, len = this._uClips.length; i < len; i++) {
- clipUniforms = this._uClips[i];
- uClipActive = clipUniforms.active;
- clip = clipsState.clips[i];
- if (uClipActive) {
- gl.uniform1i(uClipActive, clip.active);
- }
- uClipPos = clipUniforms.pos;
- if (uClipPos) {
- gl.uniform3fv(clipUniforms.pos, clip.pos);
- }
- uClipDir = clipUniforms.dir;
- if (uClipDir) {
- gl.uniform3fv(clipUniforms.dir, clip.dir);
- }
+ShadowRenderer.prototype._bindProgram = function (frame) {
+ const scene = this._scene;
+ const gl = scene.canvas.gl;
+ const clipsState = scene._clipsState;
+ this._program.bind();
+ frame.useProgram++;
+ this._lastLightId = null;
+ this._lastMaterialId = null;
+ this._lastVertexBufsId = null;
+ this._lastGeometryId = null;
+ if (clipsState.clips.length > 0) {
+ let clipUniforms;
+ let uClipActive;
+ let clip;
+ let uClipPos;
+ let uClipDir;
+ for (let i = 0, len = this._uClips.length; i < len; i++) {
+ clipUniforms = this._uClips[i];
+ uClipActive = clipUniforms.active;
+ clip = clipsState.clips[i];
+ if (uClipActive) {
+ gl.uniform1i(uClipActive, clip.active);
+ }
+ uClipPos = clipUniforms.pos;
+ if (uClipPos) {
+ gl.uniform3fv(clipUniforms.pos, clip.pos);
+ }
+ uClipDir = clipUniforms.dir;
+ if (uClipDir) {
+ gl.uniform3fv(clipUniforms.dir, clip.dir);
}
}
- };
+ }
+};
- xeogl.renderer.ShadowRenderer.prototype.webglContextRestored = function () {
- this._program = null;
- };
+ShadowRenderer.prototype.webglContextRestored = function () {
+ this._program = null;
+};
- xeogl.renderer.ShadowRenderer.prototype.drawMesh = function (frame, mesh, light) {
- const scene = this._scene;
- const gl = scene.canvas.gl;
- const materialState = mesh._material._state;
- const meshState = mesh._state;
- const geometryState = mesh._geometry._state;
- if (frame.lastProgramId !== this._program.id) {
- frame.lastProgramId = this._program.id;
- this._bindProgram(frame);
- }
- frame.textureUnit = 0;
- if (light.id !== this._lastLightId) {
- gl.uniformMatrix4fv(this._uViewMatrix, false, light.getShadowViewMatrix());
- gl.uniformMatrix4fv(this._uProjMatrix, false, light.getShadowProjMatrix());
- this._lastLightId = light.id;
- }
- // gl.uniformMatrix4fv(this._uViewMatrix, false, this._scene.viewTransform.matrix);
- // gl.uniformMatrix4fv(this._uProjMatrix, false, this._scene.projTransform.matrix);
- if (materialState.id !== this._lastMaterialId) {
- const backfaces = materialState.backfaces;
- if (frame.backfaces !== backfaces) {
- if (backfaces) {
- gl.disable(gl.CULL_FACE);
- } else {
- gl.enable(gl.CULL_FACE);
- }
- frame.backfaces = backfaces;
- }
- const frontface = materialState.frontface;
- if (frame.frontface !== frontface) {
- if (frontface) {
- gl.frontFace(gl.CCW);
- } else {
- gl.frontFace(gl.CW);
- }
- frame.frontface = frontface;
- }
- if (frame.lineWidth !== materialState.lineWidth) {
- gl.lineWidth(materialState.lineWidth);
- frame.lineWidth = materialState.lineWidth;
+ShadowRenderer.prototype.drawMesh = function (frame, mesh, light) {
+ const scene = this._scene;
+ const gl = scene.canvas.gl;
+ const materialState = mesh._material._state;
+ const meshState = mesh._state;
+ const geometryState = mesh._geometry._state;
+ if (frame.lastProgramId !== this._program.id) {
+ frame.lastProgramId = this._program.id;
+ this._bindProgram(frame);
+ }
+ frame.textureUnit = 0;
+ if (light.id !== this._lastLightId) {
+ gl.uniformMatrix4fv(this._uViewMatrix, false, light.getShadowViewMatrix());
+ gl.uniformMatrix4fv(this._uProjMatrix, false, light.getShadowProjMatrix());
+ this._lastLightId = light.id;
+ }
+ // gl.uniformMatrix4fv(this._uViewMatrix, false, this._scene.viewTransform.matrix);
+ // gl.uniformMatrix4fv(this._uProjMatrix, false, this._scene.projTransform.matrix);
+ if (materialState.id !== this._lastMaterialId) {
+ const backfaces = materialState.backfaces;
+ if (frame.backfaces !== backfaces) {
+ if (backfaces) {
+ gl.disable(gl.CULL_FACE);
+ } else {
+ gl.enable(gl.CULL_FACE);
}
- if (this._uPointSize) {
- gl.uniform1i(this._uPointSize, materialState.pointSize);
+ frame.backfaces = backfaces;
+ }
+ const frontface = materialState.frontface;
+ if (frame.frontface !== frontface) {
+ if (frontface) {
+ gl.frontFace(gl.CCW);
+ } else {
+ gl.frontFace(gl.CW);
}
- this._lastMaterialId = materialState.id;
+ frame.frontface = frontface;
}
- gl.uniformMatrix4fv(this._uModelMatrix, gl.FALSE, mesh.worldMatrix);
- if (this._uClippable) {
- gl.uniform1i(this._uClippable, mesh._state.clippable);
+ if (frame.lineWidth !== materialState.lineWidth) {
+ gl.lineWidth(materialState.lineWidth);
+ frame.lineWidth = materialState.lineWidth;
}
- if (geometryState.combined) {
- const vertexBufs = mesh.vertexBufs;
- if (vertexBufs.id !== this._lastVertexBufsId) {
- if (vertexBufs.positionsBuf && this._aPosition) {
- this._aPosition.bindArrayBuffer(vertexBufs.positionsBuf, vertexBufs.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
- frame.bindArray++;
- }
- this._lastVertexBufsId = vertexBufs.id;
- }
+ if (this._uPointSize) {
+ gl.uniform1i(this._uPointSize, materialState.pointSize);
}
- if (geometryState.id !== this._lastGeometryId) {
- if (this._uPositionsDecodeMatrix) {
- gl.uniformMatrix4fv(this._uPositionsDecodeMatrix, false, geometryState.positionsDecodeMatrix);
+ this._lastMaterialId = materialState.id;
+ }
+ gl.uniformMatrix4fv(this._uModelMatrix, gl.FALSE, mesh.worldMatrix);
+ if (this._uClippable) {
+ gl.uniform1i(this._uClippable, mesh._state.clippable);
+ }
+ if (geometryState.combined) {
+ const vertexBufs = mesh.vertexBufs;
+ if (vertexBufs.id !== this._lastVertexBufsId) {
+ if (vertexBufs.positionsBuf && this._aPosition) {
+ this._aPosition.bindArrayBuffer(vertexBufs.positionsBuf, vertexBufs.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
+ frame.bindArray++;
}
- if (geometryState.combined) { // VBOs were bound by the preceding VertexBufs chunk
- if (geometryState.indicesBufCombined) {
- geometryState.indicesBufCombined.bind();
- frame.bindArray++;
- }
- } else {
- if (this._aPosition) {
- this._aPosition.bindArrayBuffer(geometryState.positionsBuf, geometryState.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
- frame.bindArray++;
- }
- if (geometryState.indicesBuf) {
- geometryState.indicesBuf.bind();
- frame.bindArray++;
- }
- }
- this._lastGeometryId = geometryState.id;
+ this._lastVertexBufsId = vertexBufs.id;
+ }
+ }
+ if (geometryState.id !== this._lastGeometryId) {
+ if (this._uPositionsDecodeMatrix) {
+ gl.uniformMatrix4fv(this._uPositionsDecodeMatrix, false, geometryState.positionsDecodeMatrix);
}
- if (geometryState.combined) {
+ if (geometryState.combined) { // VBOs were bound by the preceding VertexBufs chunk
if (geometryState.indicesBufCombined) {
- gl.drawElements(geometryState.primitive, geometryState.indicesBufCombined.numItems, geometryState.indicesBufCombined.itemType, 0);
- frame.drawElements++;
- } else {
- // TODO: drawArrays() with VertexBufs positions
+ geometryState.indicesBufCombined.bind();
+ frame.bindArray++;
}
} else {
+ if (this._aPosition) {
+ this._aPosition.bindArrayBuffer(geometryState.positionsBuf, geometryState.quantized ? gl.UNSIGNED_SHORT : gl.FLOAT);
+ frame.bindArray++;
+ }
if (geometryState.indicesBuf) {
- gl.drawElements(geometryState.primitive, geometryState.indicesBuf.numItems, geometryState.indicesBuf.itemType, 0);
- frame.drawElements++;
- } else if (geometryState.positions) {
- gl.drawArrays(gl.TRIANGLES, 0, geometryState.positions.numItems);
- frame.drawArrays++;
+ geometryState.indicesBuf.bind();
+ frame.bindArray++;
}
}
- };
-})();
+ this._lastGeometryId = geometryState.id;
+ }
+ if (geometryState.combined) {
+ if (geometryState.indicesBufCombined) {
+ gl.drawElements(geometryState.primitive, geometryState.indicesBufCombined.numItems, geometryState.indicesBufCombined.itemType, 0);
+ frame.drawElements++;
+ } else {
+ // TODO: drawArrays() with VertexBufs positions
+ }
+ } else {
+ if (geometryState.indicesBuf) {
+ gl.drawElements(geometryState.primitive, geometryState.indicesBuf.numItems, geometryState.indicesBuf.itemType, 0);
+ frame.drawElements++;
+ } else if (geometryState.positions) {
+ gl.drawArrays(gl.TRIANGLES, 0, geometryState.positions.numItems);
+ frame.drawArrays++;
+ }
+ }
+};
+export{ShadowRenderer};
diff --git a/src/renderer/shadow/shadowShaderSource.js b/src/renderer/shadow/shadowShaderSource.js
index 6c3fa8cd3..891132e24 100644
--- a/src/renderer/shadow/shadowShaderSource.js
+++ b/src/renderer/shadow/shadowShaderSource.js
@@ -2,189 +2,187 @@
* @author xeolabs / https://github.com/xeolabs
*/
-(function () {
-
- "use strict";
-
- xeogl.renderer.ShadowShaderSource = function (mesh) {
+class ShadowShaderSource {
+ constructor(mesh) {
this.vertex = buildVertex(mesh);
this.fragment = buildFragment(mesh);
- };
-
- function hasTextures(mesh) {
- if (!mesh._geometry._state.uv) {
- return false;
- }
- const materialState = mesh._material._state;
- return materialState.alphaMap;
}
-
- function buildVertex(mesh) {
+}
- let i;
- let len;
- const lights = scene.lights.lights;
- let light;
- const billboard = mesh._state.billboard;
+function hasTextures(mesh) {
+ if (!mesh._geometry._state.uv) {
+ return false;
+ }
+ const materialState = mesh._material._state;
+ return materialState.alphaMap;
+}
- const src = [];
+function buildVertex(mesh) {
- src.push("// Shadow drawing vertex shader");
+ let i;
+ let len;
+ const lights = scene.lights.lights;
+ let light;
+ const billboard = mesh._state.billboard;
- src.push("attribute vec3 position;");
+ const src = [];
- src.push("uniform mat4 modelMatrix;");
- src.push("uniform mat4 viewMatrix;");
- src.push("uniform mat4 projMatrix;");
+ src.push("// Shadow drawing vertex shader");
- if (cfg.quantizedGeometry) {
- src.push("uniform mat4 positionsDecodeMatrix;");
- }
+ src.push("attribute vec3 position;");
- if (cfg.clipping) {
- src.push("varying vec4 vWorldPosition;");
- }
+ src.push("uniform mat4 modelMatrix;");
+ src.push("uniform mat4 viewMatrix;");
+ src.push("uniform mat4 projMatrix;");
- if (mesh._geometry._state.primitiveName === "points") {
- src.push("uniform float pointSize;");
- }
+ if (cfg.quantizedGeometry) {
+ src.push("uniform mat4 positionsDecodeMatrix;");
+ }
- if (billboard === "spherical" || billboard === "cylindrical") {
- src.push("void billboard(inout mat4 mat) {");
- src.push(" mat[0][0] = 1.0;");
- src.push(" mat[0][1] = 0.0;");
- src.push(" mat[0][2] = 0.0;");
- if (billboard === "spherical") {
- src.push(" mat[1][0] = 0.0;");
- src.push(" mat[1][1] = 1.0;");
- src.push(" mat[1][2] = 0.0;");
- }
- src.push(" mat[2][0] = 0.0;");
- src.push(" mat[2][1] = 0.0;");
- src.push(" mat[2][2] =1.0;");
- src.push("}");
- }
+ if (cfg.clipping) {
+ src.push("varying vec4 vWorldPosition;");
+ }
- src.push("void main(void) {");
+ if (mesh._geometry._state.primitiveName === "points") {
+ src.push("uniform float pointSize;");
+ }
- src.push("vec4 localPosition = vec4(position, 1.0); ");
- src.push("vec4 worldPosition;");
+ if (billboard === "spherical" || billboard === "cylindrical") {
+ src.push("void billboard(inout mat4 mat) {");
+ src.push(" mat[0][0] = 1.0;");
+ src.push(" mat[0][1] = 0.0;");
+ src.push(" mat[0][2] = 0.0;");
+ if (billboard === "spherical") {
+ src.push(" mat[1][0] = 0.0;");
+ src.push(" mat[1][1] = 1.0;");
+ src.push(" mat[1][2] = 0.0;");
+ }
+ src.push(" mat[2][0] = 0.0;");
+ src.push(" mat[2][1] = 0.0;");
+ src.push(" mat[2][2] =1.0;");
+ src.push("}");
+ }
- if (cfg.quantizedGeometry) {
- src.push("localPosition = positionsDecodeMatrix * localPosition;");
- }
+ src.push("void main(void) {");
- src.push("mat4 viewMatrix2 = viewMatrix;");
- src.push("mat4 modelMatrix2 = modelMatrix;");
+ src.push("vec4 localPosition = vec4(position, 1.0); ");
+ src.push("vec4 worldPosition;");
- if (mesh._state.stationary) {
- src.push("viewMatrix2[3][0] = viewMatrix2[3][1] = viewMatrix2[3][2] = 0.0;")
- }
+ if (cfg.quantizedGeometry) {
+ src.push("localPosition = positionsDecodeMatrix * localPosition;");
+ }
- if (billboard === "spherical" || billboard === "cylindrical") {
+ src.push("mat4 viewMatrix2 = viewMatrix;");
+ src.push("mat4 modelMatrix2 = modelMatrix;");
- src.push("mat4 modelViewMatrix = viewMatrix2 * modelMatrix2;");
- src.push("billboard(modelMatrix2);");
- src.push("billboard(viewMatrix2);");
- src.push("billboard(modelViewMatrix);");
+ if (mesh._state.stationary) {
+ src.push("viewMatrix2[3][0] = viewMatrix2[3][1] = viewMatrix2[3][2] = 0.0;")
+ }
- if (cfg.normals) {
- src.push("mat4 modelViewNormalMatrix = viewNormalMatrix2 * modelNormalMatrix2;");
- src.push("billboard(modelNormalMatrix2);");
- src.push("billboard(viewNormalMatrix2);");
- src.push("billboard(modelViewNormalMatrix);");
- }
+ if (billboard === "spherical" || billboard === "cylindrical") {
- src.push("worldPosition = modelMatrix2 * localPosition;");
- src.push("vec4 viewPosition = modelViewMatrix * localPosition;");
+ src.push("mat4 modelViewMatrix = viewMatrix2 * modelMatrix2;");
+ src.push("billboard(modelMatrix2);");
+ src.push("billboard(viewMatrix2);");
+ src.push("billboard(modelViewMatrix);");
- } else {
- src.push("worldPosition = modelMatrix2 * localPosition;");
- src.push("vec4 viewPosition = viewMatrix2 * worldPosition; ");
+ if (cfg.normals) {
+ src.push("mat4 modelViewNormalMatrix = viewNormalMatrix2 * modelNormalMatrix2;");
+ src.push("billboard(modelNormalMatrix2);");
+ src.push("billboard(viewNormalMatrix2);");
+ src.push("billboard(modelViewNormalMatrix);");
}
- if (cfg.clipping) {
- src.push("vWorldPosition = worldPosition;");
- }
+ src.push("worldPosition = modelMatrix2 * localPosition;");
+ src.push("vec4 viewPosition = modelViewMatrix * localPosition;");
- if (mesh._geometry._state.primitiveName === "points") {
- src.push("gl_PointSize = pointSize;");
- }
-
- src.push(" gl_Position = projMatrix * viewPosition;");
+ } else {
+ src.push("worldPosition = modelMatrix2 * localPosition;");
+ src.push("vec4 viewPosition = viewMatrix2 * worldPosition; ");
+ }
- src.push("}");
+ if (cfg.clipping) {
+ src.push("vWorldPosition = worldPosition;");
+ }
- return src;
+ if (mesh._geometry._state.primitiveName === "points") {
+ src.push("gl_PointSize = pointSize;");
}
- function buildFragment(mesh) {
+ src.push(" gl_Position = projMatrix * viewPosition;");
- let i;
- const src = [];
+ src.push("}");
- src.push("// Shadow fragment shader");
+ return src;
+}
- src.push("precision " + getFragmentFloatPrecision(gl) + " float;");
+function buildFragment(mesh) {
- if (cfg.clipping) {
- src.push("varying vec4 vWorldPosition;");
- src.push("uniform bool clippable;");
- for (i = 0; i < scene.clips.clips.length; i++) {
- src.push("uniform bool clipActive" + i + ";");
- src.push("uniform vec3 clipPos" + i + ";");
- src.push("uniform vec3 clipDir" + i + ";");
- }
+ let i;
+ const src = [];
+
+ src.push("// Shadow fragment shader");
+
+ src.push("precision " + getFragmentFloatPrecision(gl) + " float;");
+
+ if (cfg.clipping) {
+ src.push("varying vec4 vWorldPosition;");
+ src.push("uniform bool clippable;");
+ for (i = 0; i < scene.clips.clips.length; i++) {
+ src.push("uniform bool clipActive" + i + ";");
+ src.push("uniform vec3 clipPos" + i + ";");
+ src.push("uniform vec3 clipDir" + i + ";");
}
- src.push("vec4 packDepth (float depth) {");
- src.push(" const vec4 bitShift = vec4(1.0, 256.0, 256.0 * 256.0, 256.0 * 256.0 * 256.0);");
- src.push(" const vec4 bitMask = vec4(1.0/256.0, 1.0/256.0, 1.0/256.0, 0.0);");
- src.push(" vec4 comp = fract(depth * bitShift);");
- src.push(" comp -= comp.gbaa * bitMask;");
- src.push(" return comp;");
- src.push("}");
- src.push("void main(void) {");
- if (cfg.clipping) {
- src.push("if (clippable) {");
- src.push(" float dist = 0.0;");
- for (i = 0; i < scene.clips.clips.length; i++) {
- src.push("if (clipActive" + i + ") {");
- src.push(" dist += clamp(dot(-clipDir" + i + ".xyz, vWorldPosition.xyz - clipPos" + i + ".xyz), 0.0, 1000.0);");
- src.push("}");
- }
- src.push(" if (dist > 0.0) { discard; }");
- if (cfg.solid) {
- src.push(" if (gl_FrontFacing == false) {");
- src.push(" gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);");
- src.push(" return;");
- src.push(" }");
- }
+ }
+ src.push("vec4 packDepth (float depth) {");
+ src.push(" const vec4 bitShift = vec4(1.0, 256.0, 256.0 * 256.0, 256.0 * 256.0 * 256.0);");
+ src.push(" const vec4 bitMask = vec4(1.0/256.0, 1.0/256.0, 1.0/256.0, 0.0);");
+ src.push(" vec4 comp = fract(depth * bitShift);");
+ src.push(" comp -= comp.gbaa * bitMask;");
+ src.push(" return comp;");
+ src.push("}");
+ src.push("void main(void) {");
+ if (cfg.clipping) {
+ src.push("if (clippable) {");
+ src.push(" float dist = 0.0;");
+ for (i = 0; i < scene.clips.clips.length; i++) {
+ src.push("if (clipActive" + i + ") {");
+ src.push(" dist += clamp(dot(-clipDir" + i + ".xyz, vWorldPosition.xyz - clipPos" + i + ".xyz), 0.0, 1000.0);");
src.push("}");
}
- if (mesh._geometry._state.primitiveName === "points") {
- src.push("vec2 cxy = 2.0 * gl_PointCoord - 1.0;");
- src.push("float r = dot(cxy, cxy);");
- src.push("if (r > 1.0) {");
- src.push(" discard;");
- src.push("}");
-
+ src.push(" if (dist > 0.0) { discard; }");
+ if (cfg.solid) {
+ src.push(" if (gl_FrontFacing == false) {");
+ src.push(" gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);");
+ src.push(" return;");
+ src.push(" }");
}
- src.push("gl_FragColor = packDepth(gl_FragCoord.z);");
src.push("}");
- return src;
}
+ if (mesh._geometry._state.primitiveName === "points") {
+ src.push("vec2 cxy = 2.0 * gl_PointCoord - 1.0;");
+ src.push("float r = dot(cxy, cxy);");
+ src.push("if (r > 1.0) {");
+ src.push(" discard;");
+ src.push("}");
- function getFragmentFloatPrecision(gl) {
- if (!gl.getShaderPrecisionFormat) {
- return "mediump";
- }
- if (gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT).precision > 0) {
- return "highp";
- }
- if (gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT).precision > 0) {
- return "mediump";
- }
- return "lowp";
}
+ src.push("gl_FragColor = packDepth(gl_FragCoord.z);");
+ src.push("}");
+ return src;
+}
+
+function getFragmentFloatPrecision(gl) {
+ if (!gl.getShaderPrecisionFormat) {
+ return "mediump";
+ }
+ if (gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT).precision > 0) {
+ return "highp";
+ }
+ if (gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT).precision > 0) {
+ return "mediump";
+ }
+ return "lowp";
+}
-})();
\ No newline at end of file
+export{ShadowShaderSource};
\ No newline at end of file
diff --git a/src/renderer/state.js b/src/renderer/state.js
index e7ae90000..1198850fa 100644
--- a/src/renderer/state.js
+++ b/src/renderer/state.js
@@ -1,14 +1,21 @@
-(function () {
- const ids = new xeogl.utils.Map({});
- xeogl.renderer.State = function (cfg) {
+import {Map} from "../utils/map.js";
+
+const ids = new Map({});
+
+class State {
+
+ constructor(cfg) {
this.id = ids.addItem({});
for (const key in cfg) {
if (cfg.hasOwnProperty(key)) {
this[key] = cfg[key];
}
}
- };
- xeogl.renderer.State.prototype.destroy = function () {
+ }
+
+ destroy() {
ids.removeItem(this.id);
- };
-})();
\ No newline at end of file
+ }
+}
+
+export{State};
\ No newline at end of file
diff --git a/src/renderer/texture2d.js b/src/renderer/texture2d.js
index 9b388e0c1..62a2b83b4 100644
--- a/src/renderer/texture2d.js
+++ b/src/renderer/texture2d.js
@@ -2,19 +2,76 @@
* @author xeolabs / https://github.com/xeolabs
*/
-xeogl.renderer.Texture2D = function (gl, target) {
- this.gl = gl;
- this.target = target || gl.TEXTURE_2D;
- this.texture = gl.createTexture();
- this.setPreloadColor([0,0,0,0]); // Prevents "there is no texture bound to the unit 0" error
- this.allocated = true;
-};
+import {core} from "./../core.js";
+import {utils} from '../utils.js';
+import {webglEnums} from './webglEnums.js';
-xeogl.renderer.Texture2D.prototype.setPreloadColor = (function () {
+function getGLEnum(name, defaultVal) {
+ if (name === undefined) {
+ return defaultVal;
+ }
+ const glName = webglEnums[name];
+ if (glName === undefined) {
+ return defaultVal;
+ }
+ return this.gl[glName];
+}
- const color = new Uint8Array([0, 0, 0, 1]);
+const color = new Uint8Array([0, 0, 0, 1]);
- return function (value) {
+function clampImageSize(image, numPixels) {
+ const n = image.width * image.height;
+ if (n > numPixels) {
+ const ratio = numPixels / n;
+ const width = image.width * ratio;
+ const height = image.height * ratio;
+ const canvas = document.createElement("canvas");
+ canvas.width = nextHighestPowerOfTwo(width);
+ canvas.height = nextHighestPowerOfTwo(height);
+ const ctx = canvas.getContext("2d");
+ ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height);
+ image = canvas;
+ }
+ return image;
+}
+
+function ensureImageSizePowerOfTwo(image) {
+ if (!isPowerOfTwo(image.width) || !isPowerOfTwo(image.height)) {
+ const canvas = document.createElement("canvas");
+ canvas.width = nextHighestPowerOfTwo(image.width);
+ canvas.height = nextHighestPowerOfTwo(image.height);
+ const ctx = canvas.getContext("2d");
+ ctx.drawImage(image,
+ 0, 0, image.width, image.height,
+ 0, 0, canvas.width, canvas.height);
+ image = canvas;
+ }
+ return image;
+}
+
+function isPowerOfTwo(x) {
+ return (x & (x - 1)) === 0;
+}
+
+function nextHighestPowerOfTwo(x) {
+ --x;
+ for (let i = 1; i < 32; i <<= 1) {
+ x = x | x >> i;
+ }
+ return x + 1;
+}
+
+class Texture2D {
+
+ constructor(gl, target) {
+ this.gl = gl;
+ this.target = target || gl.TEXTURE_2D;
+ this.texture = gl.createTexture();
+ this.setPreloadColor([0, 0, 0, 0]); // Prevents "there is no texture bound to the unit 0" error
+ this.allocated = true;
+ }
+
+ setPreloadColor(value) {
if (!value) {
color[0] = 0;
@@ -54,159 +111,107 @@ xeogl.renderer.Texture2D.prototype.setPreloadColor = (function () {
}
gl.bindTexture(this.target, null);
- };
-})();
-
-xeogl.renderer.Texture2D.prototype.setTarget = function (target) {
- this.target = target || this.gl.TEXTURE_2D;
-};
-
-xeogl.renderer.Texture2D.prototype.setImage = function (image, props) {
- const gl = this.gl;
- gl.bindTexture(this.target, this.texture);
- gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, props.flipY);
- if (this.target === gl.TEXTURE_CUBE_MAP) {
- if (xeogl._isArray(image)) {
- const images = image;
- const faces = [
- gl.TEXTURE_CUBE_MAP_POSITIVE_X,
- gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
- gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
- gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
- gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
- gl.TEXTURE_CUBE_MAP_NEGATIVE_Z
- ];
- for (let i = 0, len = faces.length; i < len; i++) {
- gl.texImage2D(faces[i], 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, images[i]);
- }
- }
- } else {
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
- }
- gl.bindTexture(this.target, null);
-};
-
-xeogl.renderer.Texture2D.prototype.setProps = function (props) {
- const gl = this.gl;
- gl.bindTexture(this.target, this.texture);
- if (props.minFilter) {
- const minFilter = this._getGLEnum(props.minFilter);
- if (minFilter) {
- gl.texParameteri(this.target, gl.TEXTURE_MIN_FILTER, minFilter);
- if (minFilter === gl.NEAREST_MIPMAP_NEAREST ||
- minFilter === gl.LINEAR_MIPMAP_NEAREST ||
- minFilter === gl.NEAREST_MIPMAP_LINEAR ||
- minFilter === gl.LINEAR_MIPMAP_LINEAR) {
-
- gl.generateMipmap(this.target);
- }
- }
- }
- if (props.magFilter) {
- const magFilter = this._getGLEnum(props.magFilter);
- if (magFilter) {
- gl.texParameteri(this.target, gl.TEXTURE_MAG_FILTER, magFilter);
- }
- }
- if (props.wrapS) {
- const wrapS = this._getGLEnum(props.wrapS);
- if (wrapS) {
- gl.texParameteri(this.target, gl.TEXTURE_WRAP_S, wrapS);
- }
}
- if (props.wrapT) {
- const wrapT = this._getGLEnum(props.wrapT);
- if (wrapT) {
- gl.texParameteri(this.target, gl.TEXTURE_WRAP_T, wrapT);
- }
- }
- gl.bindTexture(this.target, null);
-};
-xeogl.renderer.Texture2D.prototype._getGLEnum = function (name, defaultVal) {
- if (name === undefined) {
- return defaultVal;
- }
- const glName = xeogl.renderer.webgl.enums[name];
- if (glName === undefined) {
- return defaultVal;
+ setTarget(target) {
+ this.target = target || this.gl.TEXTURE_2D;
}
- return this.gl[glName];
-};
-
-xeogl.renderer.Texture2D.prototype.bind = function (unit) {
- if (!this.allocated) {
- return;
- }
- if (this.texture) {
+ setImage(image, props) {
const gl = this.gl;
- gl.activeTexture(gl["TEXTURE" + unit]);
gl.bindTexture(this.target, this.texture);
- return true;
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, props.flipY);
+ if (this.target === gl.TEXTURE_CUBE_MAP) {
+ if (utils.isArray(image)) {
+ const images = image;
+ const faces = [
+ gl.TEXTURE_CUBE_MAP_POSITIVE_X,
+ gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
+ gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
+ gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
+ gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
+ gl.TEXTURE_CUBE_MAP_NEGATIVE_Z
+ ];
+ for (let i = 0, len = faces.length; i < len; i++) {
+ gl.texImage2D(faces[i], 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, images[i]);
+ }
+ }
+ } else {
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
+ }
+ gl.bindTexture(this.target, null);
}
- return false;
-};
-xeogl.renderer.Texture2D.prototype.unbind = function (unit) {
- if (!this.allocated) {
- return;
- }
- if (this.texture) {
+ setProps(props) {
const gl = this.gl;
- gl.activeTexture(gl["TEXTURE" + unit]);
+ gl.bindTexture(this.target, this.texture);
+ if (props.minFilter) {
+ const minFilter = getGLEnum(props.minFilter);
+ if (minFilter) {
+ gl.texParameteri(this.target, gl.TEXTURE_MIN_FILTER, minFilter);
+ if (minFilter === gl.NEAREST_MIPMAP_NEAREST ||
+ minFilter === gl.LINEAR_MIPMAP_NEAREST ||
+ minFilter === gl.NEAREST_MIPMAP_LINEAR ||
+ minFilter === gl.LINEAR_MIPMAP_LINEAR) {
+
+ gl.generateMipmap(this.target);
+ }
+ }
+ }
+ if (props.magFilter) {
+ const magFilter = getGLEnum(props.magFilter);
+ if (magFilter) {
+ gl.texParameteri(this.target, gl.TEXTURE_MAG_FILTER, magFilter);
+ }
+ }
+ if (props.wrapS) {
+ const wrapS = getGLEnum(props.wrapS);
+ if (wrapS) {
+ gl.texParameteri(this.target, gl.TEXTURE_WRAP_S, wrapS);
+ }
+ }
+ if (props.wrapT) {
+ const wrapT = getGLEnum(props.wrapT);
+ if (wrapT) {
+ gl.texParameteri(this.target, gl.TEXTURE_WRAP_T, wrapT);
+ }
+ }
gl.bindTexture(this.target, null);
}
-};
-xeogl.renderer.Texture2D.prototype.destroy = function () {
- if (!this.allocated) {
- return;
- }
- if (this.texture) {
- this.gl.deleteTexture(this.texture);
- this.texture = null;
+ bind(unit) {
+ if (!this.allocated) {
+ return;
+ }
+ if (this.texture) {
+ const gl = this.gl;
+ gl.activeTexture(gl["TEXTURE" + unit]);
+ gl.bindTexture(this.target, this.texture);
+ return true;
+ }
+ return false;
}
-};
-xeogl.renderer.clampImageSize = function (image, numPixels) {
- const n = image.width * image.height;
- if (n > numPixels) {
- const ratio = numPixels / n;
- const width = image.width * ratio;
- const height = image.height * ratio;
- const canvas = document.createElement("canvas");
- canvas.width = xeogl.renderer.nextHighestPowerOfTwo(width);
- canvas.height = xeogl.renderer.nextHighestPowerOfTwo(height);
- const ctx = canvas.getContext("2d");
- ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height);
- image = canvas;
+ unbind(unit) {
+ if (!this.allocated) {
+ return;
+ }
+ if (this.texture) {
+ const gl = this.gl;
+ gl.activeTexture(gl["TEXTURE" + unit]);
+ gl.bindTexture(this.target, null);
+ }
}
- return image;
-};
-xeogl.renderer.ensureImageSizePowerOfTwo = function (image) {
- if (!xeogl.renderer.isPowerOfTwo(image.width) || !xeogl.renderer.isPowerOfTwo(image.height)) {
- const canvas = document.createElement("canvas");
- canvas.width = xeogl.renderer.nextHighestPowerOfTwo(image.width);
- canvas.height = xeogl.renderer.nextHighestPowerOfTwo(image.height);
- const ctx = canvas.getContext("2d");
- ctx.drawImage(image,
- 0, 0, image.width, image.height,
- 0, 0, canvas.width, canvas.height);
- image = canvas;
+ destroy() {
+ if (!this.allocated) {
+ return;
+ }
+ if (this.texture) {
+ this.gl.deleteTexture(this.texture);
+ this.texture = null;
+ }
}
- return image;
-};
+}
-xeogl.renderer.isPowerOfTwo = function (x) {
- return (x & (x - 1)) === 0;
-};
-
-xeogl.renderer.nextHighestPowerOfTwo = function (x) {
- --x;
- for (let i = 1; i < 32; i <<= 1) {
- x = x | x >> i;
- }
- return x + 1;
-};
+export{Texture2D};
\ No newline at end of file
diff --git a/src/renderer/webgl.js b/src/renderer/webgl.js
deleted file mode 100644
index b59693c9c..000000000
--- a/src/renderer/webgl.js
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * @author xeolabs / https://github.com/xeolabs
- */
-
-xeogl.renderer.webgl = {
- enums: {
- funcAdd: "FUNC_ADD",
- funcSubtract: "FUNC_SUBTRACT",
- funcReverseSubtract: "FUNC_REVERSE_SUBTRACT",
- zero: "ZERO",
- one: "ONE",
- srcColor: "SRC_COLOR",
- oneMinusSrcColor: "ONE_MINUS_SRC_COLOR",
- dstColor: "DST_COLOR",
- oneMinusDstColor: "ONE_MINUS_DST_COLOR",
- srcAlpha: "SRC_ALPHA",
- oneMinusSrcAlpha: "ONE_MINUS_SRC_ALPHA",
- dstAlpha: "DST_ALPHA",
- oneMinusDstAlpha: "ONE_MINUS_DST_ALPHA",
- contantColor: "CONSTANT_COLOR",
- oneMinusConstantColor: "ONE_MINUS_CONSTANT_COLOR",
- constantAlpha: "CONSTANT_ALPHA",
- oneMinusConstantAlpha: "ONE_MINUS_CONSTANT_ALPHA",
- srcAlphaSaturate: "SRC_ALPHA_SATURATE",
- front: "FRONT",
- back: "BACK",
- frontAndBack: "FRONT_AND_BACK",
- never: "NEVER",
- less: "LESS",
- equal: "EQUAL",
- lequal: "LEQUAL",
- greater: "GREATER",
- notequal: "NOTEQUAL",
- gequal: "GEQUAL",
- always: "ALWAYS",
- cw: "CW",
- ccw: "CCW",
- linear: "LINEAR",
- nearest: "NEAREST",
- linearMipmapNearest: "LINEAR_MIPMAP_NEAREST",
- nearestMipmapNearest: "NEAREST_MIPMAP_NEAREST",
- nearestMipmapLinear: "NEAREST_MIPMAP_LINEAR",
- linearMipmapLinear: "LINEAR_MIPMAP_LINEAR",
- repeat: "REPEAT",
- clampToEdge: "CLAMP_TO_EDGE",
- mirroredRepeat: "MIRRORED_REPEAT",
- alpha: "ALPHA",
- rgb: "RGB",
- rgba: "RGBA",
- luminance: "LUMINANCE",
- luminanceAlpha: "LUMINANCE_ALPHA",
- textureBinding2D: "TEXTURE_BINDING_2D",
- textureBindingCubeMap: "TEXTURE_BINDING_CUBE_MAP",
- compareRToTexture: "COMPARE_R_TO_TEXTURE", // Hardware Shadowing Z-depth,
- unsignedByte: "UNSIGNED_BYTE"
- }
-};
diff --git a/src/renderer/webglEnums.js b/src/renderer/webglEnums.js
new file mode 100644
index 000000000..f2572d55c
--- /dev/null
+++ b/src/renderer/webglEnums.js
@@ -0,0 +1,57 @@
+/**
+ * @author xeolabs / https://github.com/xeolabs
+ */
+
+const webglEnums = {
+ funcAdd: "FUNC_ADD",
+ funcSubtract: "FUNC_SUBTRACT",
+ funcReverseSubtract: "FUNC_REVERSE_SUBTRACT",
+ zero: "ZERO",
+ one: "ONE",
+ srcColor: "SRC_COLOR",
+ oneMinusSrcColor: "ONE_MINUS_SRC_COLOR",
+ dstColor: "DST_COLOR",
+ oneMinusDstColor: "ONE_MINUS_DST_COLOR",
+ srcAlpha: "SRC_ALPHA",
+ oneMinusSrcAlpha: "ONE_MINUS_SRC_ALPHA",
+ dstAlpha: "DST_ALPHA",
+ oneMinusDstAlpha: "ONE_MINUS_DST_ALPHA",
+ contantColor: "CONSTANT_COLOR",
+ oneMinusConstantColor: "ONE_MINUS_CONSTANT_COLOR",
+ constantAlpha: "CONSTANT_ALPHA",
+ oneMinusConstantAlpha: "ONE_MINUS_CONSTANT_ALPHA",
+ srcAlphaSaturate: "SRC_ALPHA_SATURATE",
+ front: "FRONT",
+ back: "BACK",
+ frontAndBack: "FRONT_AND_BACK",
+ never: "NEVER",
+ less: "LESS",
+ equal: "EQUAL",
+ lequal: "LEQUAL",
+ greater: "GREATER",
+ notequal: "NOTEQUAL",
+ gequal: "GEQUAL",
+ always: "ALWAYS",
+ cw: "CW",
+ ccw: "CCW",
+ linear: "LINEAR",
+ nearest: "NEAREST",
+ linearMipmapNearest: "LINEAR_MIPMAP_NEAREST",
+ nearestMipmapNearest: "NEAREST_MIPMAP_NEAREST",
+ nearestMipmapLinear: "NEAREST_MIPMAP_LINEAR",
+ linearMipmapLinear: "LINEAR_MIPMAP_LINEAR",
+ repeat: "REPEAT",
+ clampToEdge: "CLAMP_TO_EDGE",
+ mirroredRepeat: "MIRRORED_REPEAT",
+ alpha: "ALPHA",
+ rgb: "RGB",
+ rgba: "RGBA",
+ luminance: "LUMINANCE",
+ luminanceAlpha: "LUMINANCE_ALPHA",
+ textureBinding2D: "TEXTURE_BINDING_2D",
+ textureBindingCubeMap: "TEXTURE_BINDING_CUBE_MAP",
+ compareRToTexture: "COMPARE_R_TO_TEXTURE", // Hardware Shadowing Z-depth,
+ unsignedByte: "UNSIGNED_BYTE"
+};
+
+export {webglEnums};
\ No newline at end of file
diff --git a/src/scene.js b/src/scene.js
index af27ecce6..ef683b1ab 100644
--- a/src/scene.js
+++ b/src/scene.js
@@ -369,1420 +369,1459 @@
@param [cfg.gammaFactor=2.2] {Number} The gamma factor to use when rendering with pre-multiplied gamma.
@extends Component
*/
-(function () {
- "use strict";
+import {core} from "./core.js";
+import {utils} from './utils.js';
+import {math} from './math/math.js';
+import {stats} from './stats.js';
+import {Component} from './component.js';
+import {Canvas} from './canvas/canvas.js';
+import {Renderer} from './renderer/renderer.js';
+import {Input} from './input/input.js';
+import {Viewport} from './viewport/viewport.js';
+import {Camera} from './camera/camera.js';
+import {DirLight} from './lighting/dirLight.js';
+import {BoxGeometry} from './geometry/boxGeometry.js';
+import {PhongMaterial} from './materials/phongMaterial.js';
+import {EmphasisMaterial} from './materials/emphasisMaterial.js';
+import {EdgeMaterial} from './materials/edgeMaterial.js';
+import {OutlineMaterial} from './materials/outlineMaterial.js';
+
+// Cached vars to avoid garbage collection
+
+const localRayOrigin = math.vec3();
+const localRayDir = math.vec3();
+const positionA = math.vec3();
+const positionB = math.vec3();
+const positionC = math.vec3();
+const triangleVertices = math.vec3();
+const position = math.vec4();
+const worldPos = math.vec3();
+const viewPos = math.vec3();
+const bary = math.vec3();
+const normalA = math.vec3();
+const normalB = math.vec3();
+const normalC = math.vec3();
+const uva = math.vec3();
+const uvb = math.vec3();
+const uvc = math.vec3();
+const tempVec4a = math.vec4();
+const tempVec4b = math.vec4();
+const tempVec4c = math.vec4();
+const tempVec3 = math.vec3();
+const tempVec3b = math.vec3();
+const tempVec3c = math.vec3();
+const tempVec3d = math.vec3();
+const tempVec3e = math.vec3();
+const tempVec3f = math.vec3();
+const tempVec3g = math.vec3();
+const tempVec3h = math.vec3();
+const tempVec3i = math.vec3();
+const tempVec3j = math.vec3();
+const tempVec3k = math.vec3();
+
+function getMeshIDMap(scene, meshIds) {
+ const map = {};
+ let meshId;
+ let mesh;
+ for (let i = 0, len = meshIds.length; i < len; i++) {
+ meshId = meshIds[i];
+ mesh = scene.meshes[meshId];
+ if (!mesh) {
+ scene.warn("pick(): Mesh not found: " + meshId);
+ continue;
+ }
+ map[meshId] = true;
+ }
+ return map;
+}
+
+function getMeshIDMapFromentityTypes(scene, entityTypes) {
+ // var objectIds = {};
+ // var entityType;
+ // var mesh;
+ // for (var i = 0, len = entityTypes.length; i < len; i++) {
+ // entityType = entityTypes[i];
+ // mesh = scene.meshes[entityType];
+ // if (!mesh) {
+ // scene.warn("pick(): Mesh not found: " + entityType);
+ // continue;
+ // }
+ // objectIds[mesh._objectId] = true;
+ // }
+ // return objectIds;
+}
- /**
- * Fired whenever a debug message is logged on a component within this Scene.
- * @event log
- * @param {String} value The debug message
- */
+/**
+ * Fired whenever a debug message is logged on a component within this Scene.
+ * @event log
+ * @param {String} value The debug message
+ */
- /**
- * Fired whenever an error is logged on a component within this Scene.
- * @event error
- * @param {String} value The error message
- */
+/**
+ * Fired whenever an error is logged on a component within this Scene.
+ * @event error
+ * @param {String} value The error message
+ */
+
+/**
+ * Fired whenever a warning is logged on a component within this Scene.
+ * @event warn
+ * @param {String} value The warning message
+ */
+class Scene extends Component {
/**
- * Fired whenever a warning is logged on a component within this Scene.
- * @event warn
- * @param {String} value The warning message
+ JavaScript class name for this Component.
+
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
+
+ @property type
+ @type String
+ @final
*/
- xeogl.Scene = xeogl.Component.extend({
+ static get type() {
+ return "xeogl.Scene";
+ }
- type: "xeogl.Scene",
+ init(cfg) {
- _init: function (cfg) {
+ super.init(cfg);
- const self = this;
+ const self = this;
- const transparent = !!cfg.transparent;
+ const transparent = !!cfg.transparent;
- /**
- The number of models currently loading.
+ /**
+ The number of models currently loading.
- @property loading
- @final
- @type {Number}
- */
- this.loading = 0;
+ @property loading
+ @final
+ @type {Number}
+ */
+ this.loading = 0;
- /**
- The epoch time (in milliseconds since 1970) when this Scene was instantiated.
+ /**
+ The epoch time (in milliseconds since 1970) when this Scene was instantiated.
- @property timeCreated
- @final
- @type {Number}
- */
- this.startTime = (new Date()).getTime();
+ @property timeCreated
+ @final
+ @type {Number}
+ */
+ this.startTime = (new Date()).getTime();
- /**
- {{#crossLink "Model"}}{{/crossLink}}s in this Scene, mapped to their IDs.
+ /**
+ {{#crossLink "Model"}}{{/crossLink}}s in this Scene, mapped to their IDs.
- @property models
- @final
- @type {String:xeogl.Model}
- */
- this.models = {};
+ @property models
+ @final
+ @type {String:xeogl.Model}
+ */
+ this.models = {};
- /**
- The {{#crossLink "Object"}}Objects{{/crossLink}} in this Scene, mapped to their IDs.
+ /**
+ The {{#crossLink "Object"}}Objects{{/crossLink}} in this Scene, mapped to their IDs.
- @property objects
- @final
- @type {{String:Object}}
- */
- this.objects = {};
+ @property objects
+ @final
+ @type {{String:Object}}
+ */
+ this.objects = {};
- /**
- {{#crossLink "Object"}}Objects{{/crossLink}} in this Scene that have GUIDs, mapped to their GUIDs.
+ /**
+ {{#crossLink "Object"}}Objects{{/crossLink}} in this Scene that have GUIDs, mapped to their GUIDs.
- Each Object is registered in this map when its {{#crossLink "Object/guid:property"}}{{/crossLink}} is
- assigned a value.
+ Each Object is registered in this map when its {{#crossLink "Object/guid:property"}}{{/crossLink}} is
+ assigned a value.
- @property guidObjects
- @final
- @type {{String:Object}}
- */
- this.guidObjects = {};
+ @property guidObjects
+ @final
+ @type {{String:Object}}
+ */
+ this.guidObjects = {};
- /**
- For each entity type, a map of IDs to {{#crossLink "Object"}}Objects{{/crossLink}} of that entity type.
+ /**
+ For each entity type, a map of IDs to {{#crossLink "Object"}}Objects{{/crossLink}} of that entity type.
- Each Object is registered in this map when its {{#crossLink "Object/entityType:property"}}{{/crossLink}} is
- assigned a value.
+ Each Object is registered in this map when its {{#crossLink "Object/entityType:property"}}{{/crossLink}} is
+ assigned a value.
- @property entityTypes
- @final
- @type {String:{String:xeogl.Component}}
- */
- this.entityTypes = {};
+ @property entityTypes
+ @final
+ @type {String:{String:xeogl.Component}}
+ */
+ this.entityTypes = {};
- /**
- {{#crossLink "Object"}}Objects{{/crossLink}} in this Scene that have entity types, mapped to their IDs.
+ /**
+ {{#crossLink "Object"}}Objects{{/crossLink}} in this Scene that have entity types, mapped to their IDs.
- Each Object is registered in this map when its {{#crossLink "Object/entityType:property"}}{{/crossLink}} is
- assigned a value.
+ Each Object is registered in this map when its {{#crossLink "Object/entityType:property"}}{{/crossLink}} is
+ assigned a value.
- @property entities
- @final
- @type {{String:Object}}
- */
- this.entities = {};
+ @property entities
+ @final
+ @type {{String:Object}}
+ */
+ this.entities = {};
- /**
- Visible entity {{#crossLink "Object"}}Objects{{/crossLink}} within this Scene, mapped to their IDs.
+ /**
+ Visible entity {{#crossLink "Object"}}Objects{{/crossLink}} within this Scene, mapped to their IDs.
- Each Object is registered in this map when its {{#crossLink "Object/visible:property"}}{{/crossLink}} property is true and its
- {{#crossLink "Object/entityType:property"}}{{/crossLink}} is assigned a value.
+ Each Object is registered in this map when its {{#crossLink "Object/visible:property"}}{{/crossLink}} property is true and its
+ {{#crossLink "Object/entityType:property"}}{{/crossLink}} is assigned a value.
- @property visibleEntities
- @final
- @type {{String:Object}}
- */
- this.visibleEntities = {};
+ @property visibleEntities
+ @final
+ @type {{String:Object}}
+ */
+ this.visibleEntities = {};
- /**
- Ghosted entity {{#crossLink "Object"}}Objects{{/crossLink}} within this Scene, mapped to their IDs.
+ /**
+ Ghosted entity {{#crossLink "Object"}}Objects{{/crossLink}} within this Scene, mapped to their IDs.
- Each Object is registered in this map when its {{#crossLink "Object/ghosted:property"}}{{/crossLink}} property is true and its
- {{#crossLink "Object/entityType:property"}}{{/crossLink}} is assigned a value.
+ Each Object is registered in this map when its {{#crossLink "Object/ghosted:property"}}{{/crossLink}} property is true and its
+ {{#crossLink "Object/entityType:property"}}{{/crossLink}} is assigned a value.
- @property ghostedEntities
- @final
- @type {{String:Object}}
- */
- this.ghostedEntities = {};
+ @property ghostedEntities
+ @final
+ @type {{String:Object}}
+ */
+ this.ghostedEntities = {};
- /**
- Highlighted entity {{#crossLink "Object"}}Objects{{/crossLink}} within this Scene, mapped to their IDs.
+ /**
+ Highlighted entity {{#crossLink "Object"}}Objects{{/crossLink}} within this Scene, mapped to their IDs.
- Each Object is registered in this map when its {{#crossLink "Object/highlighted:property"}}{{/crossLink}} property is true and its
- {{#crossLink "Object/entityType:property"}}{{/crossLink}} is assigned a value.
+ Each Object is registered in this map when its {{#crossLink "Object/highlighted:property"}}{{/crossLink}} property is true and its
+ {{#crossLink "Object/entityType:property"}}{{/crossLink}} is assigned a value.
- @property highlightedEntities
- @final
- @type {{String:Object}}
- */
- this.highlightedEntities = {};
+ @property highlightedEntities
+ @final
+ @type {{String:Object}}
+ */
+ this.highlightedEntities = {};
- /**
- Selected entity {{#crossLink "Object"}}Objects{{/crossLink}} within this Scene, mapped to their IDs.
+ /**
+ Selected entity {{#crossLink "Object"}}Objects{{/crossLink}} within this Scene, mapped to their IDs.
- Each Object is registered in this map when its {{#crossLink "Object/selected:property"}}{{/crossLink}} property is true and its
- {{#crossLink "Object/entityType:property"}}{{/crossLink}} is assigned a value.
+ Each Object is registered in this map when its {{#crossLink "Object/selected:property"}}{{/crossLink}} property is true and its
+ {{#crossLink "Object/entityType:property"}}{{/crossLink}} is assigned a value.
- @property selectedEntities
- @final
- @type {{String:Object}}
- */
- this.selectedEntities = {};
+ @property selectedEntities
+ @final
+ @type {{String:Object}}
+ */
+ this.selectedEntities = {};
- // Cached ID arrays, lazy-rebuilt as needed when stale after map updates
+ // Cached ID arrays, lazy-rebuilt as needed when stale after map updates
- /**
- Lazy-regenerated ID lists.
- */
- this._objectGUIDs = null;
- this._entityIds = null;
- this._visibleEntityIds = null;
- this._ghostedEntityIds = null;
- this._highlightedEntityIds = null;
- this._selectedEntityIds = null;
+ /**
+ Lazy-regenerated ID lists.
+ */
+ this._objectGUIDs = null;
+ this._entityIds = null;
+ this._visibleEntityIds = null;
+ this._ghostedEntityIds = null;
+ this._highlightedEntityIds = null;
+ this._selectedEntityIds = null;
- /**
- The {{#crossLink "Mesh"}}Meshes{{/crossLink}} in this Scene, mapped to their IDs.
+ /**
+ The {{#crossLink "Mesh"}}Meshes{{/crossLink}} in this Scene, mapped to their IDs.
- @property meshes
- @final
- @type {String:xeogl.Mesh}
- */
- this.meshes = {};
+ @property meshes
+ @final
+ @type {String:xeogl.Mesh}
+ */
+ this.meshes = {};
- this._needRecompileMeshes = false;
+ this._needRecompileMeshes = false;
- /**
- For each {{#crossLink "Component"}}{{/crossLink}} type, a map of
- IDs to {{#crossLink "Component"}}Components{{/crossLink}} instances of that type.
+ /**
+ For each {{#crossLink "Component"}}{{/crossLink}} type, a map of
+ IDs to {{#crossLink "Component"}}Components{{/crossLink}} instances of that type.
- @property types
- @final
- @type {String:{String:xeogl.Component}}
- */
- this.types = {};
+ @property types
+ @final
+ @type {String:{String:xeogl.Component}}
+ */
+ this.types = {};
- /**
- The {{#crossLink "Component"}}Component{{/crossLink}} within this Scene, mapped to their IDs.
+ /**
+ The {{#crossLink "Component"}}Component{{/crossLink}} within this Scene, mapped to their IDs.
- @property components
- @final
- @type {String:xeogl.Component}
- */
- this.components = {};
+ @property components
+ @final
+ @type {String:xeogl.Component}
+ */
+ this.components = {};
- /**
- The root {{#crossLink "Object"}}Objects{{/crossLink}} in this Scene, mapped to their IDs.
+ /**
+ The root {{#crossLink "Object"}}Objects{{/crossLink}} in this Scene, mapped to their IDs.
- @property rootObjects
- @final
- @type {{String:Object}}
- */
- this.rootObjects = {};
+ @property rootObjects
+ @final
+ @type {{String:Object}}
+ */
+ this.rootObjects = {};
- /**
- The {{#crossLink "Clip"}}Clip{{/crossLink}} components in this Scene, mapped to their IDs.
+ /**
+ The {{#crossLink "Clip"}}Clip{{/crossLink}} components in this Scene, mapped to their IDs.
- @property clips
- @final
- @type {{String:Clip}}
- */
- this.clips = {};
+ @property clips
+ @final
+ @type {{String:Clip}}
+ */
+ this.clips = {};
- /**
- The {{#crossLink "PointLight"}}{{/crossLink}}, {{#crossLink "DirLight"}}{{/crossLink}},
- {{#crossLink "SpotLight"}}{{/crossLink}} and {{#crossLink "AmbientLight"}}{{/crossLink}} components in this Scene, mapped to their IDs.
+ /**
+ The {{#crossLink "PointLight"}}{{/crossLink}}, {{#crossLink "DirLight"}}{{/crossLink}},
+ {{#crossLink "SpotLight"}}{{/crossLink}} and {{#crossLink "AmbientLight"}}{{/crossLink}} components in this Scene, mapped to their IDs.
- @property lights
- @final
- @type {{String:Object}}
- */
- this.lights = {};
+ @property lights
+ @final
+ @type {{String:Object}}
+ */
+ this.lights = {};
- /**
- The {{#crossLink "LightMap"}}{{/crossLink}} components in this Scene, mapped to their IDs.
+ /**
+ The {{#crossLink "LightMap"}}{{/crossLink}} components in this Scene, mapped to their IDs.
- @property lightMaps
- @final
- @type {{String:LightMap}}
- */
- this.lightMaps = {};
+ @property lightMaps
+ @final
+ @type {{String:LightMap}}
+ */
+ this.lightMaps = {};
- /**
- The {{#crossLink "ReflectionMap"}}{{/crossLink}} components in this Scene, mapped to their IDs.
+ /**
+ The {{#crossLink "ReflectionMap"}}{{/crossLink}} components in this Scene, mapped to their IDs.
- @property reflectionMaps
- @final
- @type {{String:ReflectionMap}}
- */
- this.reflectionMaps = {};
+ @property reflectionMaps
+ @final
+ @type {{String:ReflectionMap}}
+ */
+ this.reflectionMaps = {};
- /**
- Manages the HTML5 canvas for this Scene.
- @final
- @property canvas
- @type {Canvas}
- */
- this.canvas = new xeogl.Canvas(this, {
- canvas: cfg.canvas, // Can be canvas ID, canvas element, or null
- transparent: transparent,
- backgroundColor: cfg.backgroundColor,
- backgroundImage: cfg.backgroundImage,
- webgl2: cfg.webgl2 !== false,
- contextAttr: cfg.contextAttr || {},
- simulateWebGLContextLost: cfg.simulateWebGLContextLost
- });
+ /**
+ Manages the HTML5 canvas for this Scene.
+ @final
+ @property canvas
+ @type {Canvas}
+ */
+ this.canvas = new Canvas(this, {
+ canvas: cfg.canvas, // Can be canvas ID, canvas element, or null
+ transparent: transparent,
+ backgroundColor: cfg.backgroundColor,
+ backgroundImage: cfg.backgroundImage,
+ webgl2: cfg.webgl2 !== false,
+ contextAttr: cfg.contextAttr || {},
+ simulateWebGLContextLost: cfg.simulateWebGLContextLost
+ });
- // Redraw as canvas resized
- this.canvas.on("boundary", function () {
- self._renderer.imageDirty();
- });
+ // Redraw as canvas resized
+ this.canvas.on("boundary", function () {
+ self._renderer.imageDirty();
+ });
- this.canvas.on("webglContextFailed", function () {
- alert("xeogl failed to find WebGL!");
- });
+ this.canvas.on("webglContextFailed", function () {
+ alert("xeogl failed to find WebGL!");
+ });
- this._renderer = new xeogl.renderer.Renderer(xeogl.stats, this, {
- transparent: transparent
- });
+ this._renderer = new Renderer(this, {
+ transparent: transparent
+ });
- this._clipsState = new (function () {
+ this._clipsState = new (function () {
- this.clips = [];
+ this.clips = [];
- let hash = null;
+ let hash = null;
- this.getHash = function () {
- if (hash) {
- return hash;
- }
- const clips = this.clips;
- if (clips.length === 0) {
- return this.hash = ";";
- }
- let clip;
- const hashParts = [];
- for (let i = 0, len = clips.length; i < len; i++) {
- clip = clips[i];
- hashParts.push("cp");
- }
- hashParts.push(";");
- hash = hashParts.join("");
+ this.getHash = function () {
+ if (hash) {
return hash;
- };
-
- this.addClip = function (clip) {
- this.clips.push(clip);
- hash = null;
- };
-
- this.removeClip = function (clip) {
- for (let i = 0, len = this.clips.length; i < len; i++) {
- if (this.clips[i].id === clip.id) {
- this.clips.splice(i, 1);
- hash = null;
- return;
- }
+ }
+ const clips = this.clips;
+ if (clips.length === 0) {
+ return this.hash = ";";
+ }
+ let clip;
+ const hashParts = [];
+ for (let i = 0, len = clips.length; i < len; i++) {
+ clip = clips[i];
+ hashParts.push("cp");
+ }
+ hashParts.push(";");
+ hash = hashParts.join("");
+ return hash;
+ };
+
+ this.addClip = function (clip) {
+ this.clips.push(clip);
+ hash = null;
+ };
+
+ this.removeClip = function (clip) {
+ for (let i = 0, len = this.clips.length; i < len; i++) {
+ if (this.clips[i].id === clip.id) {
+ this.clips.splice(i, 1);
+ hash = null;
+ return;
}
- };
- })();
+ }
+ };
+ })();
- this._lightsState = new (function () {
+ this._lightsState = new (function () {
- const DEFAULT_AMBIENT = xeogl.math.vec3([0, 0, 0]);
- const ambientColor = xeogl.math.vec3();
+ const DEFAULT_AMBIENT = math.vec3([0, 0, 0]);
+ const ambientColor = math.vec3();
- this.lights = [];
- this.reflectionMaps = [];
- this.lightMaps = [];
+ this.lights = [];
+ this.reflectionMaps = [];
+ this.lightMaps = [];
- let hash = null;
- let ambientLight = null;
+ let hash = null;
+ let ambientLight = null;
- this.getHash = function () {
- if (hash) {
- return hash;
+ this.getHash = function () {
+ if (hash) {
+ return hash;
+ }
+ const hashParts = [];
+ const lights = this.lights;
+ let light;
+ for (let i = 0, len = lights.length; i < len; i++) {
+ light = lights[i];
+ hashParts.push("/");
+ hashParts.push(light.type);
+ hashParts.push((light.space === "world") ? "w" : "v");
+ if (light.shadow) {
+ hashParts.push("sh");
}
- const hashParts = [];
- const lights = this.lights;
- let light;
- for (let i = 0, len = lights.length; i < len; i++) {
- light = lights[i];
- hashParts.push("/");
- hashParts.push(light.type);
- hashParts.push((light.space === "world") ? "w" : "v");
- if (light.shadow) {
- hashParts.push("sh");
+ }
+ if (this.lightMaps.length > 0) {
+ hashParts.push("/lm");
+ }
+ if (this.reflectionMaps.length > 0) {
+ hashParts.push("/rm");
+ }
+ hashParts.push(";");
+ hash = hashParts.join("");
+ return hash;
+ };
+
+ this.addLight = function (state) {
+ this.lights.push(state);
+ ambientLight = null;
+ hash = null;
+ };
+
+ this.removeLight = function (state) {
+ for (let i = 0, len = this.lights.length; i < len; i++) {
+ const light = this.lights[i];
+ if (light.id === state.id) {
+ this.lights.splice(i, 1);
+ if (ambientLight && ambientLight.id === state.id) {
+ ambientLight = null;
}
+ hash = null;
+ return;
}
- if (this.lightMaps.length > 0) {
- hashParts.push("/lm");
- }
- if (this.reflectionMaps.length > 0) {
- hashParts.push("/rm");
+ }
+ };
+
+ this.addReflectionMap = function (state) {
+ this.reflectionMaps.push(state);
+ hash = null;
+ };
+
+ this.removeReflectionMap = function (state) {
+ for (let i = 0, len = this.reflectionMaps.length; i < len; i++) {
+ if (this.reflectionMaps[i].id === state.id) {
+ this.reflectionMaps.splice(i, 1);
+ hash = null;
+ return;
}
- hashParts.push(";");
- hash = hashParts.join("");
- return hash;
- };
+ }
+ };
+
+ this.addLightMap = function (state) {
+ this.lightMaps.push(state);
+ hash = null;
+ };
- this.addLight = function (state) {
- this.lights.push(state);
- ambientLight = null;
- hash = null;
- };
+ this.removeLightMap = function (state) {
+ for (let i = 0, len = this.lightMaps.length; i < len; i++) {
+ if (this.lightMaps[i].id === state.id) {
+ this.lightMaps.splice(i, 1);
+ hash = null;
+ return;
+ }
+ }
+ };
- this.removeLight = function (state) {
+ this.getAmbientColor = function () {
+ if (!ambientLight) {
for (let i = 0, len = this.lights.length; i < len; i++) {
const light = this.lights[i];
- if (light.id === state.id) {
- this.lights.splice(i, 1);
- if (ambientLight && ambientLight.id === state.id) {
- ambientLight = null;
- }
- hash = null;
- return;
- }
- }
- };
-
- this.addReflectionMap = function (state) {
- this.reflectionMaps.push(state);
- hash = null;
- };
-
- this.removeReflectionMap = function (state) {
- for (let i = 0, len = this.reflectionMaps.length; i < len; i++) {
- if (this.reflectionMaps[i].id === state.id) {
- this.reflectionMaps.splice(i, 1);
- hash = null;
- return;
+ if (light.type === "ambient") {
+ ambientLight = light;
+ break;
}
}
- };
-
- this.addLightMap = function (state) {
- this.lightMaps.push(state);
- hash = null;
- };
-
- this.removeLightMap = function (state) {
- for (let i = 0, len = this.lightMaps.length; i < len; i++) {
- if (this.lightMaps[i].id === state.id) {
- this.lightMaps.splice(i, 1);
- hash = null;
- return;
- }
- }
- };
-
- this.getAmbientColor = function () {
- if (!ambientLight) {
- for (let i = 0, len = this.lights.length; i < len; i++) {
- const light = this.lights[i];
- if (light.type === "ambient") {
- ambientLight = light;
- break;
- }
- }
- }
- if (ambientLight) {
- const color = ambientLight.color;
- const intensity = ambientLight.intensity;
- ambientColor[0] = color[0] * intensity;
- ambientColor[1] = color[1] * intensity;
- ambientColor[2] = color[2] * intensity;
- return ambientColor;
- } else {
- return DEFAULT_AMBIENT;
- }
- };
+ }
+ if (ambientLight) {
+ const color = ambientLight.color;
+ const intensity = ambientLight.intensity;
+ ambientColor[0] = color[0] * intensity;
+ ambientColor[1] = color[1] * intensity;
+ ambientColor[2] = color[2] * intensity;
+ return ambientColor;
+ } else {
+ return DEFAULT_AMBIENT;
+ }
+ };
- })();
- /**
- Publishes input events that occur on this Scene's canvas.
+ })();
- @final
- @property input
- @type {Input}
- @final
- */
- this.input = new xeogl.Input(this, {
- element: this.canvas.canvas
- });
+ /**
+ Publishes input events that occur on this Scene's canvas.
- // Register Scene on engine
- // Do this BEFORE we add components below
- xeogl._addScene(this);
-
- // Add components specified as JSON
-
- const componentJSONs = cfg.components;
-
- if (componentJSONs) {
- let componentJSON;
- let type;
- let constr;
- for (let i = 0, len = componentJSONs.length; i < len; i++) {
- componentJSON = componentJSONs[i];
- type = componentJSON.type;
- if (type) {
- constr = window[type];
- if (constr) {
- new constr(this, componentJSON);
- }
+ @final
+ @property input
+ @type {Input}
+ @final
+ */
+ this.input = new Input(this, {
+ element: this.canvas.canvas
+ });
+
+ // Register Scene on engine
+ // Do this BEFORE we add components below
+ utils.addScene(this);
+
+ // Add components specified as JSON
+
+ const componentJSONs = cfg.components;
+
+ if (componentJSONs) {
+ let componentJSON;
+ let type;
+ let constr;
+ for (let i = 0, len = componentJSONs.length; i < len; i++) {
+ componentJSON = componentJSONs[i];
+ type = componentJSON.type;
+ if (type) {
+ constr = window[type];
+ if (constr) {
+ new constr(this, componentJSON);
}
}
}
+ }
- // Init default components
+ // Init default components
- this._initDefaults();
+ this._initDefaults();
- // Global components
+ // Global components
- this._viewport = new xeogl.Viewport(this, {
- id: "default.viewport",
- autoBoundary: true
- });
-
- this._camera = new xeogl.Camera(this, {
- id: "default.camera"
- });
+ this._viewport = new Viewport(this, {
+ id: "default.viewport",
+ autoBoundary: true
+ });
- // Default lights
+ this._camera = new Camera(this, {
+ id: "default.camera"
+ });
- new xeogl.DirLight(this, {
- dir: [0.8, -0.6, -0.8],
- color: [1.0, 1.0, 1.0],
- intensity: 1.0,
- space: "view"
- });
+ // Default lights
- new xeogl.DirLight(this, {
- dir: [-0.8, -0.4, -0.4],
- color: [1.0, 1.0, 1.0],
- intensity: 1.0,
- space: "view"
- });
+ new DirLight(this, {
+ dir: [0.8, -0.6, -0.8],
+ color: [1.0, 1.0, 1.0],
+ intensity: 1.0,
+ space: "view"
+ });
- new xeogl.DirLight(this, {
- dir: [0.2, -0.8, 0.8],
- color: [0.6, 0.6, 0.6],
- intensity: 1.0,
- space: "view"
- });
+ new DirLight(this, {
+ dir: [-0.8, -0.4, -0.4],
+ color: [1.0, 1.0, 1.0],
+ intensity: 1.0,
+ space: "view"
+ });
- // Plug global components into renderer
+ new DirLight(this, {
+ dir: [0.2, -0.8, 0.8],
+ color: [0.6, 0.6, 0.6],
+ intensity: 1.0,
+ space: "view"
+ });
- const viewport = this._viewport;
- const renderer = this._renderer;
- const camera = this._camera;
+ // Plug global components into renderer
- camera.on("dirty", function () {
- renderer.imageDirty();
- });
+ const viewport = this._viewport;
+ const renderer = this._renderer;
+ const camera = this._camera;
- this.ticksPerRender = cfg.ticksPerRender;
- this.passes = cfg.passes;
- this.clearEachPass = cfg.clearEachPass;
- this.gammaInput = cfg.gammaInput;
- this.gammaOutput = cfg.gammaOutput;
- this.gammaFactor = cfg.gammaFactor;
- },
+ camera.on("dirty", function () {
+ renderer.imageDirty();
+ });
- _initDefaults: function () {
+ this.ticksPerRender = cfg.ticksPerRender;
+ this.passes = cfg.passes;
+ this.clearEachPass = cfg.clearEachPass;
+ this.gammaInput = cfg.gammaInput;
+ this.gammaOutput = cfg.gammaOutput;
+ this.gammaFactor = cfg.gammaFactor;
+ }
- // Call this Scene's property accessors to lazy-init their properties
+ _initDefaults() {
- let dummy; // Keeps Codacy happy
+ // Call this Scene's property accessors to lazy-init their properties
- dummy = this.geometry;
- dummy = this.material;
- dummy = this.ghostMaterial;
- dummy = this.outlineMaterial;
- },
+ let dummy; // Keeps Codacy happy
- _addComponent: function (c) {
- if (c.id) { // Manual ID
- if (this.components[c.id]) {
- this.error("Component " + xeogl._inQuotes(c.id) + " already exists in Scene - ignoring ID, will randomly-generate instead");
- c.id = null;
- }
- }
- if (!c.id) { // Auto ID
- if (window.nextID === undefined) {
- window.nextID = 0;
- }
- //c.id = xeogl.math.createUUID();
- c.id = "_" + window.nextID++;
- while (this.components[c.id]) {
- c.id = xeogl.math.createUUID();
- }
- }
- this.components[c.id] = c;
- // Register for class type
- const type = c.type;
- let types = this.types[c.type];
- if (!types) {
- types = this.types[type] = {};
- }
- types[c.id] = c;
- },
-
- _removeComponent: function (c) {
- delete this.components[c.id];
- const types = this.types[c.type];
- if (types) {
- delete types[c.id];
- if (xeogl._isEmptyObject(types)) {
- delete this.types[c.type];
- }
- }
- },
-
- // Methods below are called by various component types to register themselves on their
- // Scene. Violates Hollywood Principle, where we could just filter on type in _addComponent,
- // but this is faster than checking the type of each component in such a filter.
-
- _clipCreated: function (clip) {
- this.clips[clip.id] = clip;
- this.scene._clipsState.addClip(clip._state);
- this._needRecompileMeshes = true;
- },
-
- _lightCreated: function (light) {
- this.lights[light.id] = light;
- this.scene._lightsState.addLight(light._state);
- this._needRecompileMeshes = true;
- },
-
- _lightMapCreated: function (lightMap) {
- this.lightMaps[lightMap.id] = lightMap;
- this.scene._lightsState.addLightMap(lightMap._state);
- this._needRecompileMeshes = true;
- },
-
- _reflectionMapCreated: function (reflectionMap) {
- this.reflectionMaps[reflectionMap.id] = reflectionMap;
- this.scene._lightsState.addReflectionMap(reflectionMap._state);
- this._needRecompileMeshes = true;
- },
-
- _objectCreated: function (object) {
- this.objects[object.id] = object;
- if (object.guid) {
- this.guidObjects[object.id] = object;
- this._objectGUIDs = null; // To lazy-rebuild
- }
- if (!object.parent) {
- this.rootObjects[object.id] = object; // TODO: What about when a root Object is added as child to another?
- }
- xeogl.stats.components.objects++;
- },
-
- _meshCreated: function (mesh) {
- this.meshes[mesh.id] = mesh;
- xeogl.stats.components.meshes++;
- },
-
- _modelCreated: function (model) {
- this.models[model.id] = model;
- xeogl.stats.components.models++;
- },
-
- _clipDestroyed: function (clip) {
- delete this.clips[clip.id];
- this.scene._clipsState.removeClip(clip._state);
- this._needRecompileMeshes = true;
- },
-
- _lightDestroyed: function (light) {
- delete this.lights[light.id];
- this.scene._lightsState.removeLight(light._state);
- this._needRecompileMeshes = true;
- },
-
- _lightMapDestroyed: function (lightMap) {
- delete this.lightMaps[lightMap.id];
- this.scene._lightsState.removeLightMap(lightMap._state);
- this._needRecompileMeshes = true;
- },
-
- _reflectionMapDestroyed: function (reflectionMap) {
- delete this.reflectionMaps[reflectionMap.id];
- this.scene._lightsState.removeReflectionMap(reflectionMap._state);
- this._needRecompileMeshes = true;
- },
-
- _objectDestroyed: function (object) {
- delete this.objects[object.id];
- if (object.guid) {
- delete this.guidObjects[object.guid];
- this._objectGUIDs = null; // To lazy-rebuild
- }
- if (!object.parent) {
- delete this.rootObjects[object.id];
- }
- xeogl.stats.components.objects--;
- },
-
- _meshDestroyed: function (mesh) {
- xeogl.stats.components.meshes--;
- delete this.meshes[mesh.id];
- xeogl.stats.components.meshes--;
- },
-
- _modelDestroyed: function (model) {
- this.models[model.id] = model;
- xeogl.stats.components.models++;
- },
-
- _entityTypeAssigned: function (object, newEntityType) {
- this.entities[object.id] = object;
- let objectsOfType = this.entityTypes[newEntityType];
- if (!objectsOfType) {
- objectsOfType = {};
- this.entityTypes[newEntityType] = objectsOfType;
- }
- objectsOfType[object.id] = object;
- this._entityIds = null; // Lazy regenerate
- this._entityTypeIds = null; // Lazy regenerate
- },
-
- _entityTypeRemoved: function (object, oldEntityType) {
- delete this.entities[object.id];
- const objectsOfType = this.entityTypes[oldEntityType];
- if (objectsOfType) {
- delete objectsOfType[object.id];
- }
- this._entityIds = null; // Lazy regenerate
- this._entityTypeIds = null; // Lazy regenerate
- },
+ dummy = this.geometry;
+ dummy = this.material;
+ dummy = this.ghostMaterial;
+ dummy = this.outlineMaterial;
+ }
- _entityVisibilityUpdated: function (object, visible) {
- if (visible) {
- this.visibleEntities[object.id] = object;
- } else {
- delete this.visibleEntities[object.id];
+ _addComponent(component) {
+ if (component.id) { // Manual ID
+ if (this.components[component.id]) {
+ this.error("Component " + utils.inQuotes(component.id) + " already exists in Scene - ignoring ID, will randomly-generate instead");
+ component.id = null;
}
- this._visibleEntityIds = null; // Lazy regenerate
- },
-
- _entityGhostedUpdated: function (object, ghosted) {
- if (ghosted) {
- this.ghostedEntities[object.id] = object;
- } else {
- delete this.ghostedEntities[object.id];
+ }
+ if (!component.id) { // Auto ID
+ if (window.nextID === undefined) {
+ window.nextID = 0;
}
- this._ghostedEntityIds = null; // Lazy regenerate
- },
-
- _entityHighlightedUpdated: function (object, highlighted) {
- if (highlighted) {
- this.highlightedEntities[object.id] = object;
- } else {
- delete this.highlightedEntities[object.id];
+ //component.id = math.createUUID();
+ component.id = "_" + window.nextID++;
+ while (this.components[component.id]) {
+ component.id = math.createUUID();
}
- this._highlightedEntityIds = null; // Lazy regenerate
- },
-
- _entitySelectedUpdated: function (object, selected) {
- if (selected) {
- this.selectedEntities[object.id] = object;
- } else {
- delete this.selectedEntities[object.id];
+ }
+ this.components[component.id] = component;
+ // Register for class type
+ const type = component.type;
+ let types = this.types[component.type];
+ if (!types) {
+ types = this.types[type] = {};
+ }
+ types[component.id] = component;
+ }
+
+ _removeComponent(component) {
+ delete this.components[component.id];
+ const types = this.types[component.type];
+ if (types) {
+ delete types[component.id];
+ if (utils.isEmptyObject(types)) {
+ delete this.types[component.type];
}
- this._selectedEntityIds = null; // Lazy regenerate
- },
-
- _webglContextLost: function () {
- // this.loading++;
- this.canvas.spinner.processes++;
- for (const id in this.components) {
- if (this.components.hasOwnProperty(id)) {
- const c = this.components[id];
- if (c._webglContextLost) {
- c._webglContextLost();
- }
+ }
+ }
+
+ // Methods below are called by various component types to register themselves on their
+ // Scene. Violates Hollywood Principle, where we could just filter on type in _addComponent,
+ // but this is faster than checking the type of each component in such a filter.
+
+ _clipCreated(clip) {
+ this.clips[clip.id] = clip;
+ this.scene._clipsState.addClip(clip._state);
+ this._needRecompileMeshes = true;
+ }
+
+ _lightCreated(light) {
+ this.lights[light.id] = light;
+ this.scene._lightsState.addLight(light._state);
+ this._needRecompileMeshes = true;
+ }
+
+ _lightMapCreated(lightMap) {
+ this.lightMaps[lightMap.id] = lightMap;
+ this.scene._lightsState.addLightMap(lightMap._state);
+ this._needRecompileMeshes = true;
+ }
+
+ _reflectionMapCreated(reflectionMap) {
+ this.reflectionMaps[reflectionMap.id] = reflectionMap;
+ this.scene._lightsState.addReflectionMap(reflectionMap._state);
+ this._needRecompileMeshes = true;
+ }
+
+ _objectCreated(object) {
+ this.objects[object.id] = object;
+ if (object.guid) {
+ this.guidObjects[object.id] = object;
+ this._objectGUIDs = null; // To lazy-rebuild
+ }
+ if (!object.parent) {
+ this.rootObjects[object.id] = object; // TODO: What about when a root Object is added as child to another?
+ }
+ stats.components.objects++;
+ }
+
+ _meshCreated(mesh) {
+ this.meshes[mesh.id] = mesh;
+ stats.components.meshes++;
+ }
+
+ _modelCreated(model) {
+ this.models[model.id] = model;
+ stats.components.models++;
+ }
+
+ _clipDestroyed(clip) {
+ delete this.clips[clip.id];
+ this.scene._clipsState.removeClip(clip._state);
+ this._needRecompileMeshes = true;
+ }
+
+ _lightDestroyed(light) {
+ delete this.lights[light.id];
+ this.scene._lightsState.removeLight(light._state);
+ this._needRecompileMeshes = true;
+ }
+
+ _lightMapDestroyed(lightMap) {
+ delete this.lightMaps[lightMap.id];
+ this.scene._lightsState.removeLightMap(lightMap._state);
+ this._needRecompileMeshes = true;
+ }
+
+ _reflectionMapDestroyed(reflectionMap) {
+ delete this.reflectionMaps[reflectionMap.id];
+ this.scene._lightsState.removeReflectionMap(reflectionMap._state);
+ this._needRecompileMeshes = true;
+ }
+
+ _objectDestroyed(object) {
+ delete this.objects[object.id];
+ if (object.guid) {
+ delete this.guidObjects[object.guid];
+ this._objectGUIDs = null; // To lazy-rebuild
+ }
+ if (!object.parent) {
+ delete this.rootObjects[object.id];
+ }
+ stats.components.objects--;
+ }
+
+ _meshDestroyed(mesh) {
+ stats.components.meshes--;
+ delete this.meshes[mesh.id];
+ stats.components.meshes--;
+ }
+
+ _modelDestroyed(model) {
+ this.models[model.id] = model;
+ stats.components.models++;
+ }
+
+ _entityTypeAssigned(object, newEntityType) {
+ this.entities[object.id] = object;
+ let objectsOfType = this.entityTypes[newEntityType];
+ if (!objectsOfType) {
+ objectsOfType = {};
+ this.entityTypes[newEntityType] = objectsOfType;
+ }
+ objectsOfType[object.id] = object;
+ this._entityIds = null; // Lazy regenerate
+ this._entityTypeIds = null; // Lazy regenerate
+ }
+
+ _entityTypeRemoved(object, oldEntityType) {
+ delete this.entities[object.id];
+ const objectsOfType = this.entityTypes[oldEntityType];
+ if (objectsOfType) {
+ delete objectsOfType[object.id];
+ }
+ this._entityIds = null; // Lazy regenerate
+ this._entityTypeIds = null; // Lazy regenerate
+ }
+
+ _entityVisibilityUpdated(object, visible) {
+ if (visible) {
+ this.visibleEntities[object.id] = object;
+ } else {
+ delete this.visibleEntities[object.id];
+ }
+ this._visibleEntityIds = null; // Lazy regenerate
+ }
+
+ _entityGhostedUpdated(object, ghosted) {
+ if (ghosted) {
+ this.ghostedEntities[object.id] = object;
+ } else {
+ delete this.ghostedEntities[object.id];
+ }
+ this._ghostedEntityIds = null; // Lazy regenerate
+ }
+
+ _entityHighlightedUpdated(object, highlighted) {
+ if (highlighted) {
+ this.highlightedEntities[object.id] = object;
+ } else {
+ delete this.highlightedEntities[object.id];
+ }
+ this._highlightedEntityIds = null; // Lazy regenerate
+ }
+
+ _entitySelectedUpdated(object, selected) {
+ if (selected) {
+ this.selectedEntities[object.id] = object;
+ } else {
+ delete this.selectedEntities[object.id];
+ }
+ this._selectedEntityIds = null; // Lazy regenerate
+ }
+
+ _webglContextLost() {
+ // this.loading++;
+ this.canvas.spinner.processes++;
+ for (const id in this.components) {
+ if (this.components.hasOwnProperty(id)) {
+ const component = this.components[id];
+ if (component._webglContextLost) {
+ component._webglContextLost();
}
}
- this._renderer.webglContextLost();
- },
-
- _webglContextRestored: function () {
- const gl = this.canvas.gl;
- for (const id in this.components) {
- if (this.components.hasOwnProperty(id)) {
- const c = this.components[id];
- if (c._webglContextRestored) {
- c._webglContextRestored(gl);
- }
+ }
+ this._renderer.webglContextLost();
+ }
+
+ _webglContextRestored() {
+ const gl = this.canvas.gl;
+ for (const id in this.components) {
+ if (this.components.hasOwnProperty(id)) {
+ const component = this.components[id];
+ if (component._webglContextRestored) {
+ component._webglContextRestored(gl);
}
}
- this._renderer.webglContextRestored(gl);
- //this.loading--;
- this.canvas.spinner.processes--;
- },
+ }
+ this._renderer.webglContextRestored(gl);
+ //this.loading--;
+ this.canvas.spinner.processes--;
+ }
- /**
- * Renders a single frame of this Scene.
- *
- * The Scene will periodically render itself after any updates, but you can call this method to force a render
- * if required. This method is typically used when we want to synchronously take a snapshot of the canvas and
- * need everything rendered right at that moment.
- *
- * @method render
- * @param {Boolean} [forceRender=false] Forces a render when true, otherwise only renders if something has changed in this Scene
- * since the last render.
- */
- render: (function () {
+ /**
+ * Renders a single frame of this Scene.
+ *
+ * The Scene will periodically render itself after any updates, but you can call this method to force a render
+ * if required. This method is typically used when we want to synchronously take a snapshot of the canvas and
+ * need everything rendered right at that moment.
+ *
+ * @method render
+ * @param {Boolean} [forceRender=false] Forces a render when true, otherwise only renders if something has changed in this Scene
+ * since the last render.
+ */
+ render(forceRender) {
- const renderEvent = {
- sceneId: null,
- pass: 0
- };
+ const renderEvent = {
+ sceneId: null,
+ pass: 0
+ };
- return function (forceRender) {
- if (this._needRecompileMeshes) {
- this._recompileMeshes();
- this._needRecompileMeshes = false;
- }
+ if (this._needRecompileMeshes) {
+ this._recompileMeshes();
+ this._needRecompileMeshes = false;
+ }
- if (this.loading > 0 || this.canvas.spinner.processes > 0) {
- this.canvas.canvas.style.opacity = 0.0;
- return;
- }
+ if (this.loading > 0 || this.canvas.spinner.processes > 0) {
+ this.canvas.canvas.style.opacity = 0.0;
+ return;
+ }
- let opacity = Number.parseFloat(this.canvas.canvas.style.opacity);
- if (opacity < 1.0) {
- opacity += 0.1;
- this.canvas.canvas.style.opacity = opacity;
- }
+ let opacity = Number.parseFloat(this.canvas.canvas.style.opacity);
+ if (opacity < 1.0) {
+ opacity += 0.1;
+ this.canvas.canvas.style.opacity = opacity;
+ }
- renderEvent.sceneId = this.id;
+ renderEvent.sceneId = this.id;
- const passes = this._passes;
- const clearEachPass = this._clearEachPass;
- let pass;
- let clear;
+ const passes = this._passes;
+ const clearEachPass = this._clearEachPass;
+ let pass;
+ let clear;
- for (pass = 0; pass < passes; pass++) {
+ for (pass = 0; pass < passes; pass++) {
- renderEvent.pass = pass;
+ renderEvent.pass = pass;
- /**
- * Fired when about to render a frame for a Scene.
- *
- * @event rendering
- * @param {String} sceneID The ID of this Scene.
- * @param {Number} pass Index of the pass we are about to render (see {{#crossLink "Scene/passes:property"}}{{/crossLink}}).
- */
- this.fire("rendering", renderEvent, true);
+ /**
+ * Fired when about to render a frame for a Scene.
+ *
+ * @event rendering
+ * @param {String} sceneID The ID of this Scene.
+ * @param {Number} pass Index of the pass we are about to render (see {{#crossLink "Scene/passes:property"}}{{/crossLink}}).
+ */
+ this.fire("rendering", renderEvent, true);
- clear = clearEachPass || (pass === 0);
+ clear = clearEachPass || (pass === 0);
- this._renderer.render({pass: pass, clear: clear, force: forceRender});
+ this._renderer.render({pass: pass, clear: clear, force: forceRender});
- /**
- * Fired when we have just rendered a frame for a Scene.
- *
- * @event rendering
- * @param {String} sceneID The ID of this Scene.
- * @param {Number} pass Index of the pass we rendered (see {{#crossLink "Scene/passes:property"}}{{/crossLink}}).
- */
- this.fire("rendered", renderEvent, true);
- }
+ /**
+ * Fired when we have just rendered a frame for a Scene.
+ *
+ * @event rendering
+ * @param {String} sceneID The ID of this Scene.
+ * @param {Number} pass Index of the pass we rendered (see {{#crossLink "Scene/passes:property"}}{{/crossLink}}).
+ */
+ this.fire("rendered", renderEvent, true);
+ }
- this._saveAmbientColor();
- };
- })(),
+ this._saveAmbientColor();
+ }
- _recompileMeshes: function () {
- for (const id in this.meshes) {
- if (this.meshes.hasOwnProperty(id)) {
- this.meshes[id]._compile();
- }
+ _recompileMeshes() {
+ for (const id in this.meshes) {
+ if (this.meshes.hasOwnProperty(id)) {
+ this.meshes[id]._compile();
}
- },
-
- _saveAmbientColor: function () {
- const canvas = this.canvas;
- if (!canvas.transparent && !canvas.backgroundImage && !canvas.backgroundColor) {
- const ambientColor = this._lightsState.getAmbientColor();
- if (!this._lastAmbientColor ||
- this._lastAmbientColor[0] !== ambientColor[0] ||
- this._lastAmbientColor[1] !== ambientColor[1] ||
- this._lastAmbientColor[2] !== ambientColor[2] ||
- this._lastAmbientColor[3] !== ambientColor[3]) {
- canvas.backgroundColor = ambientColor;
- if (!this._lastAmbientColor) {
- this._lastAmbientColor = xeogl.math.vec4([0, 0, 0, 1]);
- }
- this._lastAmbientColor.set(ambientColor);
+ }
+ }
+
+ _saveAmbientColor() {
+ const canvas = this.canvas;
+ if (!canvas.transparent && !canvas.backgroundImage && !canvas.backgroundColor) {
+ const ambientColor = this._lightsState.getAmbientColor();
+ if (!this._lastAmbientColor ||
+ this._lastAmbientColor[0] !== ambientColor[0] ||
+ this._lastAmbientColor[1] !== ambientColor[1] ||
+ this._lastAmbientColor[2] !== ambientColor[2] ||
+ this._lastAmbientColor[3] !== ambientColor[3]) {
+ canvas.backgroundColor = ambientColor;
+ if (!this._lastAmbientColor) {
+ this._lastAmbientColor = math.vec4([0, 0, 0, 1]);
}
- } else {
- this._lastAmbientColor = null;
+ this._lastAmbientColor.set(ambientColor);
}
- },
+ } else {
+ this._lastAmbientColor = null;
+ }
+ }
- _props: {
+ /**
+ Convenience array of entity type IDs in {{#crossLink "Scene/entityTypes:property"}}{{/crossLink}}.
+ @property entityTypeIds
+ @final
+ @type {Array of String}
+ */
+ get objectGUIDs() {
+ if (!this._objectGUIDs) {
+ this._objectGUIDs = Object.keys(this.guidObjects);
+ }
+ return this._objectGUIDs;
+ }
- /**
- Convenience array of entity type IDs in {{#crossLink "Scene/entityTypes:property"}}{{/crossLink}}.
- @property entityTypeIds
- @final
- @type {Array of String}
- */
- objectGUIDs: {
- get: function () {
- if (!this._objectGUIDs) {
- this._objectGUIDs = Object.keys(this.guidObjects);
- }
- return this._objectGUIDs;
- }
- },
+ /**
+ Convenience array of entity type IDs in {{#crossLink "Scene/entityTypes:property"}}{{/crossLink}}.
+ @property entityTypeIds
+ @final
+ @type {Array of String}
+ */
+ get entityTypeIds() {
+ if (!this._entityTypeIds) {
+ this._entityTypeIds = Object.keys(this.entityTypes);
+ }
+ return this._entityTypeIds;
+ }
- /**
- Convenience array of entity type IDs in {{#crossLink "Scene/entityTypes:property"}}{{/crossLink}}.
- @property entityTypeIds
- @final
- @type {Array of String}
- */
- entityTypeIds: {
- get: function () {
- if (!this._entityTypeIds) {
- this._entityTypeIds = Object.keys(this.entityTypes);
- }
- return this._entityTypeIds;
- }
- },
+ /**
+ Convenience array of IDs in {{#crossLink "Scene/entities:property"}}{{/crossLink}}.
+ @property entityIds
+ @final
+ @type {Array of String}
+ */
+ get entityIds() {
+ if (!this._entityIds) {
+ this._entityIds = Object.keys(this.entities);
+ }
+ return this._entityIds;
+ }
- /**
- Convenience array of IDs in {{#crossLink "Scene/entities:property"}}{{/crossLink}}.
- @property entityIds
- @final
- @type {Array of String}
- */
- entityIds: {
- get: function () {
- if (!this._entityIds) {
- this._entityIds = Object.keys(this.entities);
- }
- return this._entityIds;
- }
- },
+ /**
+ Convenience array of IDs in {{#crossLink "Scene/visibleEntities:property"}}{{/crossLink}}.
+ @property visibleEntityIds
+ @final
+ @type {Array of String}
+ */
+ get visibleEntityIds() {
+ if (!this._visibleEntityIds) {
+ this._visibleEntityIds = Object.keys(this.visibleEntities);
+ }
+ return this._visibleEntityIds;
+ }
- /**
- Convenience array of IDs in {{#crossLink "Scene/visibleEntities:property"}}{{/crossLink}}.
- @property visibleEntityIds
- @final
- @type {Array of String}
- */
- visibleEntityIds: {
- get: function () {
- if (!this._visibleEntityIds) {
- this._visibleEntityIds = Object.keys(this.visibleEntities);
- }
- return this._visibleEntityIds;
- }
- },
+ /**
+ Convenience array of IDs in {{#crossLink "Scene/ghostedEntities:property"}}{{/crossLink}}.
+ @property ghostedEntityIds
+ @final
+ @type {Array of String}
+ */
+ get ghostedEntityIds() {
+ if (!this._ghostedEntityIds) {
+ this._ghostedEntityIds = Object.keys(this.ghostedEntities);
+ }
+ return this._ghostedEntityIds;
+ }
- /**
- Convenience array of IDs in {{#crossLink "Scene/ghostedEntities:property"}}{{/crossLink}}.
- @property ghostedEntityIds
- @final
- @type {Array of String}
- */
- ghostedEntityIds: {
- get: function () {
- if (!this._ghostedEntityIds) {
- this._ghostedEntityIds = Object.keys(this.ghostedEntities);
- }
- return this._ghostedEntityIds;
- }
- },
+ /**
+ Convenience array of IDs in {{#crossLink "Scene/highlightedEntities:property"}}{{/crossLink}}.
+ @property highlightedEntityIds
+ @final
+ @type {Array of String}
+ */
+ get highlightedEntityIds() {
+ if (!this._highlightedEntityIds) {
+ this._highlightedEntityIds = Object.keys(this.highlightedEntities);
+ }
+ return this._highlightedEntityIds;
+ }
- /**
- Convenience array of IDs in {{#crossLink "Scene/highlightedEntities:property"}}{{/crossLink}}.
- @property highlightedEntityIds
- @final
- @type {Array of String}
- */
- highlightedEntityIds: {
- get: function () {
- if (!this._highlightedEntityIds) {
- this._highlightedEntityIds = Object.keys(this.highlightedEntities);
- }
- return this._highlightedEntityIds;
- }
- },
+ /**
+ Convenience array of IDs in {{#crossLink "Scene/selectedEntities:property"}}{{/crossLink}}.
+ @property selectedEntityIds
+ @final
+ @type {Array of String}
+ */
+ get selectedEntityIds() {
+ if (!this._selectedEntityIds) {
+ this._selectedEntityIds = Object.keys(this.selectedEntities);
+ }
+ return this._selectedEntityIds;
+ }
- /**
- Convenience array of IDs in {{#crossLink "Scene/selectedEntities:property"}}{{/crossLink}}.
- @property selectedEntityIds
- @final
- @type {Array of String}
- */
- selectedEntityIds: {
- get: function () {
- if (!this._selectedEntityIds) {
- this._selectedEntityIds = Object.keys(this.selectedEntities);
- }
- return this._selectedEntityIds;
- }
- },
+ /**
+ The number of {{#crossLink "Scene/tick:property"}}{{/crossLink}} that happen between each render or this Scene.
- /**
- The number of {{#crossLink "Scene/tick:property"}}{{/crossLink}} that happen between each render or this Scene.
+ @property ticksPerRender
+ @default 1
+ @type Number
+ */
+ set ticksPerRender(value) {
+ if (value === undefined || value === null) {
+ value = 1;
+ } else if (!utils.isNumeric(value) || value <= 0) {
+ this.error("Unsupported value for 'ticksPerRender': '" + value +
+ "' - should be an integer greater than zero.");
+ value = 1;
+ }
+ if (value === this._ticksPerRender) {
+ return;
+ }
+ this._ticksPerRender = value;
+ }
- @property ticksPerRender
- @default 1
- @type Number
- */
- ticksPerRender: {
- set: function (value) {
- if (value === undefined || value === null) {
- value = 1;
- } else if (!xeogl._isNumeric(value) || value <= 0) {
- this.error("Unsupported value for 'ticksPerRender': '" + value +
- "' - should be an integer greater than zero.");
- value = 1;
- }
- if (value === this._ticksPerRender) {
- return;
- }
- this._ticksPerRender = value;
- },
- get: function () {
- return this._ticksPerRender;
- }
- },
+ get ticksPerRender() {
+ return this._ticksPerRender;
+ }
- /**
- The number of times this Scene renders per frame.
+ /**
+ The number of times this Scene renders per frame.
- @property passes
- @default 1
- @type Number
- */
- passes: {
- set: function (value) {
- if (value === undefined || value === null) {
- value = 1;
- } else if (!xeogl._isNumeric(value) || value <= 0) {
- this.error("Unsupported value for 'passes': '" + value +
- "' - should be an integer greater than zero.");
- value = 1;
- }
- if (value === this._passes) {
- return;
- }
- this._passes = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._passes;
- }
- },
+ @property passes
+ @default 1
+ @type Number
+ */
+ set passes(value) {
+ if (value === undefined || value === null) {
+ value = 1;
+ } else if (!utils.isNumeric(value) || value <= 0) {
+ this.error("Unsupported value for 'passes': '" + value +
+ "' - should be an integer greater than zero.");
+ value = 1;
+ }
+ if (value === this._passes) {
+ return;
+ }
+ this._passes = value;
+ this._renderer.imageDirty();
+ }
- /**
- When doing multiple passes per frame, specifies whether to clear the
- canvas before each pass (true) or just before the first pass (false).
+ get passes() {
+ return this._passes;
+ }
- @property clearEachPass
- @default false
- @type Boolean
- */
- clearEachPass: {
- set: function (value) {
- value = !!value;
- if (value === this._clearEachPass) {
- return;
- }
- this._clearEachPass = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._clearEachPass;
- }
- },
+ /**
+ When doing multiple passes per frame, specifies whether to clear the
+ canvas before each pass (true) or just before the first pass (false).
- /**
- When true, expects all textures and colors are premultiplied gamma.
+ @property clearEachPass
+ @default false
+ @type Boolean
+ */
+ set clearEachPass(value) {
+ value = !!value;
+ if (value === this._clearEachPass) {
+ return;
+ }
+ this._clearEachPass = value;
+ this._renderer.imageDirty();
+ }
- @property gammaInput
- @default false
- @type Boolean
- */
- gammaInput: {
- set: function (value) {
- value = value !== false;
- if (value === this._renderer.gammaInput) {
- return;
- }
- this._renderer.gammaInput = value;
- this._needRecompileMeshes = true;
- },
- get: function () {
- return this._renderer.gammaInput;
- }
- },
+ get clearEachPass() {
+ return this._clearEachPass;
+ }
- /**
- Whether or not to render pixels with pre-multiplied gama.
+ /**
+ When true, expects all textures and colors are premultiplied gamma.
- @property gammaOutput
- @default true
- @type Boolean
- */
- gammaOutput: {
- set: function (value) {
- value = value !== false;
- if (value === this._renderer.gammaOutput) {
- return;
- }
- this._renderer.gammaOutput = value;
- this._needRecompileMeshes = true;
- },
- get: function () {
- return this._renderer.gammaOutput;
- }
- },
+ @property gammaInput
+ @default false
+ @type Boolean
+ */
+ set gammaInput(value) {
+ value = value !== false;
+ if (value === this._renderer.gammaInput) {
+ return;
+ }
+ this._renderer.gammaInput = value;
+ this._needRecompileMeshes = true;
+ }
+
+ get gammaInput() {
+ return this._renderer.gammaInput;
+ }
+
+ /**
+ Whether or not to render pixels with pre-multiplied gama.
+
+ @property gammaOutput
+ @default true
+ @type Boolean
+ */
+ set gammaOutput(value) {
+ value = value !== false;
+ if (value === this._renderer.gammaOutput) {
+ return;
+ }
+ this._renderer.gammaOutput = value;
+ this._needRecompileMeshes = true;
+ }
+
+ get gammaOutput() {
+ return this._renderer.gammaOutput;
+ }
+
+ /**
+ The gamma factor to use when {{#crossLink "Scene/property:gammaOutput"}}{{/crossLink}} is set true.
- /**
- The gamma factor to use when {{#crossLink "Scene/property:gammaOutput"}}{{/crossLink}} is set true.
+ @property gammaOutput
+ @default 1.0
+ @type Number
+ */
+ set gammaFactor(value) {
+ value = (value === undefined || value === null) ? 2.2 : value;
+ if (value === this._renderer.gammaFactor) {
+ return;
+ }
+ this._renderer.gammaFactor = value;
+ this._renderer.imageDirty();
+ }
- @property gammaOutput
- @default 1.0
- @type Number
- */
- gammaFactor: {
- set: function (value) {
- value = (value === undefined || value === null) ? 2.2 : value;
- if (value === this._renderer.gammaFactor) {
- return;
- }
- this._renderer.gammaFactor = value;
- this._renderer.imageDirty();
- },
- get: function () {
- return this._renderer.gammaFactor;
- }
- },
+ get gammaFactor() {
+ return this._renderer.gammaFactor;
+ }
- /**
- The default geometry for this Scene, which is a {{#crossLink "BoxGeometry"}}BoxGeometry{{/crossLink}}.
+ /**
+ The default geometry for this Scene, which is a {{#crossLink "BoxGeometry"}}BoxGeometry{{/crossLink}}.
- This {{#crossLink "BoxGeometry"}}BoxGeometry{{/crossLink}} has an {{#crossLink "Component/id:property"}}id{{/crossLink}} equal to "default.geometry".
+ This {{#crossLink "BoxGeometry"}}BoxGeometry{{/crossLink}} has an {{#crossLink "Component/id:property"}}id{{/crossLink}} equal to "default.geometry".
- {{#crossLink "Mesh"}}Meshes{{/crossLink}} in this Scene are attached to this
- {{#crossLink "Geometry"}}Geometry{{/crossLink}} by default.
- @property geometry
- @final
- @type BoxGeometry
- */
- geometry: {
- get: function () {
- return this.components["default.geometry"] ||
- new xeogl.BoxGeometry(this, {
- id: "default.geometry",
- isDefault: true
- });
- }
- },
+ {{#crossLink "Mesh"}}Meshes{{/crossLink}} in this Scene are attached to this
+ {{#crossLink "Geometry"}}Geometry{{/crossLink}} by default.
+ @property geometry
+ @final
+ @type BoxGeometry
+ */
+ get geometry() {
+ return this.components["default.geometry"] ||
+ new BoxGeometry(this, {
+ id: "default.geometry",
+ isDefault: true
+ });
+ }
- /**
- The default drawing material for this Scene, which is a {{#crossLink "PhongMaterial"}}PhongMaterial{{/crossLink}}.
+ /**
+ The default drawing material for this Scene, which is a {{#crossLink "PhongMaterial"}}PhongMaterial{{/crossLink}}.
- This {{#crossLink "PhongMaterial"}}PhongMaterial{{/crossLink}} has
- an {{#crossLink "Component/id:property"}}id{{/crossLink}} equal to "default.material", with all
- other properties initialised to their default values.
+ This {{#crossLink "PhongMaterial"}}PhongMaterial{{/crossLink}} has
+ an {{#crossLink "Component/id:property"}}id{{/crossLink}} equal to "default.material", with all
+ other properties initialised to their default values.
- {{#crossLink "Mesh"}}Meshes{{/crossLink}} in this Scene are attached to this
- {{#crossLink "PhongMaterial"}}PhongMaterial{{/crossLink}} by default.
- @property material
- @final
- @type PhongMaterial
- */
- material: {
- get: function () {
- return this.components["default.material"] ||
- new xeogl.PhongMaterial(this, {
- id: "default.material",
- isDefault: true,
- emissive: [0.4, 0.4, 0.4] // Visible by default on geometry without normals
- });
- }
- },
+ {{#crossLink "Mesh"}}Meshes{{/crossLink}} in this Scene are attached to this
+ {{#crossLink "PhongMaterial"}}PhongMaterial{{/crossLink}} by default.
+ @property material
+ @final
+ @type PhongMaterial
+ */
+ get material() {
+ return this.components["default.material"] || new PhongMaterial(this, {
+ id: "default.material",
+ isDefault: true,
+ emissive: [0.4, 0.4, 0.4] // Visible by default on geometry without normals
+ });
+ }
- /**
- The Scene's default {{#crossLink "EmphasisMaterial"}}EmphasisMaterial{{/crossLink}} for the appearance of {{#crossLink "Meshes"}}Meshes{{/crossLink}} when they are ghosted.
+ /**
+ The Scene's default {{#crossLink "EmphasisMaterial"}}EmphasisMaterial{{/crossLink}} for the appearance of {{#crossLink "Meshes"}}Meshes{{/crossLink}} when they are ghosted.
- This {{#crossLink "EmphasisMaterial"}}EmphasisMaterial{{/crossLink}} has
- an {{#crossLink "Component/id:property"}}id{{/crossLink}} equal to "default.ghostMaterial", with all
- other properties initialised to their default values.
+ This {{#crossLink "EmphasisMaterial"}}EmphasisMaterial{{/crossLink}} has
+ an {{#crossLink "Component/id:property"}}id{{/crossLink}} equal to "default.ghostMaterial", with all
+ other properties initialised to their default values.
- {{#crossLink "Mesh"}}Meshes{{/crossLink}} in this Scene are attached to this
- {{#crossLink "EmphasisMaterial"}}EmphasisMaterial{{/crossLink}} by default.
- @property ghostMaterial
- @final
- @type EmphasisMaterial
- */
- ghostMaterial: {
- get: function () {
- return this.components["default.ghostMaterial"] ||
- new xeogl.EmphasisMaterial(this, {
- id: "default.ghostMaterial",
- preset: "sepia",
- isDefault: true
- });
- }
- },
+ {{#crossLink "Mesh"}}Meshes{{/crossLink}} in this Scene are attached to this
+ {{#crossLink "EmphasisMaterial"}}EmphasisMaterial{{/crossLink}} by default.
+ @property ghostMaterial
+ @final
+ @type EmphasisMaterial
+ */
+ get ghostMaterial() {
+ return this.components["default.ghostMaterial"] || new EmphasisMaterial(this, {
+ id: "default.ghostMaterial",
+ preset: "sepia",
+ isDefault: true
+ });
+ }
- /**
- The Scene's default {{#crossLink "EmphasisMaterial"}}EmphasisMaterial{{/crossLink}} for the appearance of {{#crossLink "Meshes"}}Meshes{{/crossLink}} when they are highlighted.
+ /**
+ The Scene's default {{#crossLink "EmphasisMaterial"}}EmphasisMaterial{{/crossLink}} for the appearance of {{#crossLink "Meshes"}}Meshes{{/crossLink}} when they are highlighted.
- This {{#crossLink "HighlightMaterial"}}HighlightMaterial{{/crossLink}} has
- an {{#crossLink "Component/id:property"}}id{{/crossLink}} equal to "default.highlightMaterial", with all
- other properties initialised to their default values.
+ This {{#crossLink "HighlightMaterial"}}HighlightMaterial{{/crossLink}} has
+ an {{#crossLink "Component/id:property"}}id{{/crossLink}} equal to "default.highlightMaterial", with all
+ other properties initialised to their default values.
- {{#crossLink "Mesh"}}Meshes{{/crossLink}} in this Scene are attached to this
- {{#crossLink "HighlightMaterial"}}HighlightMaterial{{/crossLink}} by default.
- @property highlightMaterial
- @final
- @type HighlightMaterial
- */
- highlightMaterial: {
- get: function () {
- return this.components["default.highlightMaterial"] ||
- new xeogl.EmphasisMaterial(this, {
- id: "default.highlightMaterial",
- preset: "yellowHighlight",
- isDefault: true
- });
- }
- },
+ {{#crossLink "Mesh"}}Meshes{{/crossLink}} in this Scene are attached to this
+ {{#crossLink "HighlightMaterial"}}HighlightMaterial{{/crossLink}} by default.
+ @property highlightMaterial
+ @final
+ @type HighlightMaterial
+ */
+ get highlightMaterial() {
+ return this.components["default.highlightMaterial"] || new EmphasisMaterial(this, {
+ id: "default.highlightMaterial",
+ preset: "yellowHighlight",
+ isDefault: true
+ });
+ }
- /**
- The Scene's default {{#crossLink "EmphasisMaterial"}}EmphasisMaterial{{/crossLink}} for the appearance of {{#crossLink "Meshes"}}Meshes{{/crossLink}} when they are selected.
+ /**
+ The Scene's default {{#crossLink "EmphasisMaterial"}}EmphasisMaterial{{/crossLink}} for the appearance of {{#crossLink "Meshes"}}Meshes{{/crossLink}} when they are selected.
- This {{#crossLink "SelectedMaterial"}}SelectedMaterial{{/crossLink}} has
- an {{#crossLink "Component/id:property"}}id{{/crossLink}} equal to "default.selectedMaterial", with all
- other properties initialised to their default values.
+ This {{#crossLink "SelectedMaterial"}}SelectedMaterial{{/crossLink}} has
+ an {{#crossLink "Component/id:property"}}id{{/crossLink}} equal to "default.selectedMaterial", with all
+ other properties initialised to their default values.
- {{#crossLink "Mesh"}}Meshes{{/crossLink}} in this Scene are attached to this
- {{#crossLink "SelectedMaterial"}}SelectedMaterial{{/crossLink}} by default.
- @property selectedMaterial
- @final
- @type SelectedMaterial
- */
- selectedMaterial: {
- get: function () {
- return this.components["default.selectedMaterial"] ||
- new xeogl.EmphasisMaterial(this, {
- id: "default.selectedMaterial",
- preset: "greenSelected",
- isDefault: true
- });
- }
- },
+ {{#crossLink "Mesh"}}Meshes{{/crossLink}} in this Scene are attached to this
+ {{#crossLink "SelectedMaterial"}}SelectedMaterial{{/crossLink}} by default.
+ @property selectedMaterial
+ @final
+ @type SelectedMaterial
+ */
+ get selectedMaterial() {
+ return this.components["default.selectedMaterial"] || new EmphasisMaterial(this, {
+ id: "default.selectedMaterial",
+ preset: "greenSelected",
+ isDefault: true
+ });
+ }
- /**
- The Scene's default {{#crossLink "EdgeMaterial"}}EmphasisMaterial{{/crossLink}} for the appearance of {{#crossLink "Meshes"}}Meshes{{/crossLink}} when edges are emphasized.
+ /**
+ The Scene's default {{#crossLink "EdgeMaterial"}}EmphasisMaterial{{/crossLink}} for the appearance of {{#crossLink "Meshes"}}Meshes{{/crossLink}} when edges are emphasized.
- This {{#crossLink "EdgeMaterial"}}EdgeMaterial{{/crossLink}} has
- an {{#crossLink "Component/id:property"}}id{{/crossLink}} equal to "default.edgeMaterial", with all
- other properties initialised to their default values.
+ This {{#crossLink "EdgeMaterial"}}EdgeMaterial{{/crossLink}} has
+ an {{#crossLink "Component/id:property"}}id{{/crossLink}} equal to "default.edgeMaterial", with all
+ other properties initialised to their default values.
- {{#crossLink "Mesh"}}Meshes{{/crossLink}} in this Scene are attached to this
- {{#crossLink "EdgeMaterial"}}EdgeMaterial{{/crossLink}} by default.
- @property edgeMaterial
- @final
- @type EdgeMaterial
- */
- edgeMaterial: {
- get: function () {
- return this.components["default.edgeMaterial"] ||
- new xeogl.EdgeMaterial(this, {
- id: "default.edgeMaterial",
- preset: "default",
- edgeColor: [0.0, 0.0, 0.0],
- edgeAlpha: 1.0,
- edgeWidth: 1,
- isDefault: true
- });
- }
- },
+ {{#crossLink "Mesh"}}Meshes{{/crossLink}} in this Scene are attached to this
+ {{#crossLink "EdgeMaterial"}}EdgeMaterial{{/crossLink}} by default.
+ @property edgeMaterial
+ @final
+ @type EdgeMaterial
+ */
+ get edgeMaterial() {
+ return this.components["default.edgeMaterial"] || new EdgeMaterial(this, {
+ id: "default.edgeMaterial",
+ preset: "default",
+ edgeColor: [0.0, 0.0, 0.0],
+ edgeAlpha: 1.0,
+ edgeWidth: 1,
+ isDefault: true
+ });
+ }
- /**
- The Scene's default {{#crossLink "OutlineMaterial"}}OutlineMaterial{{/crossLink}} for the appearance of {{#crossLink "Meshes"}}Meshes{{/crossLink}} when they are outlined.
+ /**
+ The Scene's default {{#crossLink "OutlineMaterial"}}OutlineMaterial{{/crossLink}} for the appearance of {{#crossLink "Meshes"}}Meshes{{/crossLink}} when they are outlined.
- This {{#crossLink "OutlineMaterial"}}OutlineMaterial{{/crossLink}} has
- an {{#crossLink "Component/id:property"}}id{{/crossLink}} equal to "default.outlineMaterial", with all
- other properties initialised to their default values.
+ This {{#crossLink "OutlineMaterial"}}OutlineMaterial{{/crossLink}} has
+ an {{#crossLink "Component/id:property"}}id{{/crossLink}} equal to "default.outlineMaterial", with all
+ other properties initialised to their default values.
- {{#crossLink "Mesh"}}Meshes{{/crossLink}} in this Scene are attached to this
- {{#crossLink "OutlineMaterial"}}OutlineMaterial{{/crossLink}} by default.
- @property outlineMaterial
- @final
- @type OutlineMaterial
- */
- outlineMaterial: {
- get: function () {
- return this.components["default.outlineMaterial"] ||
- new xeogl.OutlineMaterial(this, {
- id: "default.outlineMaterial",
- isDefault: true
- });
- }
- },
+ {{#crossLink "Mesh"}}Meshes{{/crossLink}} in this Scene are attached to this
+ {{#crossLink "OutlineMaterial"}}OutlineMaterial{{/crossLink}} by default.
+ @property outlineMaterial
+ @final
+ @type OutlineMaterial
+ */
+ get outlineMaterial() {
+ return this.components["default.outlineMaterial"] || new OutlineMaterial(this, {
+ id: "default.outlineMaterial",
+ isDefault: true
+ });
+ }
- /**
- The {{#crossLink "Viewport"}}{{/crossLink}} belonging to this Scene.
+ /**
+ The {{#crossLink "Viewport"}}{{/crossLink}} belonging to this Scene.
- @property viewport
- @final
- @type Viewport
- */
- viewport: {
- get: function () {
- return this._viewport;
- }
- },
+ @property viewport
+ @final
+ @type Viewport
+ */
+ get viewport() {
+ return this._viewport;
+ }
- /**
- The {{#crossLink "Camera"}}Camera{{/crossLink}} belonging to this Scene.
+ /**
+ The {{#crossLink "Camera"}}Camera{{/crossLink}} belonging to this Scene.
- @property camera
- @final
- @type Camera
- */
- camera: {
- get: function () {
- return this._camera;
- }
- },
+ @property camera
+ @final
+ @type Camera
+ */
+ get camera() {
+ return this._camera;
+ }
- /**
- World-space 3D center of this Scene.
+ /**
+ World-space 3D center of this Scene.
- @property center
- @final
- @type {Float32Array}
- */
- center: {
- get: function () {
- if (this._aabbDirty || !this._center) {
- if (!this._center || !this._center) {
- this._center = xeogl.math.vec3();
- }
- const aabb = this.aabb;
- this._center[0] = (aabb[0] + aabb[3] ) / 2;
- this._center[1] = (aabb[1] + aabb[4] ) / 2;
- this._center[2] = (aabb[2] + aabb[5] ) / 2;
- }
- return this._center;
- }
- },
+ @property center
+ @final
+ @type {Float32Array}
+ */
+ get center() {
+ if (this._aabbDirty || !this._center) {
+ if (!this._center || !this._center) {
+ this._center = math.vec3();
+ }
+ const aabb = this.aabb;
+ this._center[0] = (aabb[0] + aabb[3] ) / 2;
+ this._center[1] = (aabb[1] + aabb[4] ) / 2;
+ this._center[2] = (aabb[2] + aabb[5] ) / 2;
+ }
+ return this._center;
+ }
- /**
- World-space axis-aligned 3D boundary (AABB) of this Scene.
+ /**
+ World-space axis-aligned 3D boundary (AABB) of this Scene.
- The AABB is represented by a six-element Float32Array containing the min/max extents of the
- axis-aligned volume, ie. ````[xmin, ymin,zmin,xmax,ymax, zmax]````.
+ The AABB is represented by a six-element Float32Array containing the min/max extents of the
+ axis-aligned volume, ie. ````[xmin, ymin,zmin,xmax,ymax, zmax]````.
- @property aabb
- @final
- @type {Float32Array}
- */
- aabb: {
- get: function () {
- // console.log("get aabb")
- if (this._aabbDirty) {
- if (!this._aabb) {
- this._aabb = xeogl.math.AABB3();
- }
- let xmin = xeogl.math.MAX_DOUBLE;
- let ymin = xeogl.math.MAX_DOUBLE;
- let zmin = xeogl.math.MAX_DOUBLE;
- let xmax = -xeogl.math.MAX_DOUBLE;
- let ymax = -xeogl.math.MAX_DOUBLE;
- let zmax = -xeogl.math.MAX_DOUBLE;
- let aabb;
- const meshes = this.meshes;
- let mesh;
- for (const meshId in meshes) {
- if (meshes.hasOwnProperty(meshId)) {
- mesh = meshes[meshId];
- if (!mesh.collidable) {
- continue;
- }
- aabb = mesh.aabb;
- if (aabb[0] < xmin) {
- xmin = aabb[0];
- }
- if (aabb[1] < ymin) {
- ymin = aabb[1];
- }
- if (aabb[2] < zmin) {
- zmin = aabb[2];
- }
- if (aabb[3] > xmax) {
- xmax = aabb[3];
- }
- if (aabb[4] > ymax) {
- ymax = aabb[4];
- }
- if (aabb[5] > zmax) {
- zmax = aabb[5];
- }
- }
- }
- this._aabb[0] = xmin;
- this._aabb[1] = ymin;
- this._aabb[2] = zmin;
- this._aabb[3] = xmax;
- this._aabb[4] = ymax;
- this._aabb[5] = zmax;
- this._aabbDirty = false;
+ @property aabb
+ @final
+ @type {Float32Array}
+ */
+ get aabb() {
+ if (this._aabbDirty) {
+ if (!this._aabb) {
+ this._aabb = math.AABB3();
+ }
+ let xmin = math.MAX_DOUBLE;
+ let ymin = math.MAX_DOUBLE;
+ let zmin = math.MAX_DOUBLE;
+ let xmax = -math.MAX_DOUBLE;
+ let ymax = -math.MAX_DOUBLE;
+ let zmax = -math.MAX_DOUBLE;
+ let aabb;
+ const meshes = this.meshes;
+ let mesh;
+ for (const meshId in meshes) {
+ if (meshes.hasOwnProperty(meshId)) {
+ mesh = meshes[meshId];
+ if (!mesh.collidable) {
+ continue;
+ }
+ aabb = mesh.aabb;
+ if (aabb[0] < xmin) {
+ xmin = aabb[0];
+ }
+ if (aabb[1] < ymin) {
+ ymin = aabb[1];
+ }
+ if (aabb[2] < zmin) {
+ zmin = aabb[2];
+ }
+ if (aabb[3] > xmax) {
+ xmax = aabb[3];
+ }
+ if (aabb[4] > ymax) {
+ ymax = aabb[4];
+ }
+ if (aabb[5] > zmax) {
+ zmax = aabb[5];
}
- return this._aabb;
}
}
- },
+ this._aabb[0] = xmin;
+ this._aabb[1] = ymin;
+ this._aabb[2] = zmin;
+ this._aabb[3] = xmax;
+ this._aabb[4] = ymax;
+ this._aabb[5] = zmax;
+ this._aabbDirty = false;
+ }
+ return this._aabb;
+ }
- _setBoundaryDirty: function () {
- //if (!this._aabbDirty) {
- this._aabbDirty = true;
- this.fire("boundary");
- // }
- },
+ _setBoundaryDirty() {
+ //if (!this._aabbDirty) {
+ this._aabbDirty = true;
+ this.fire("boundary");
+ // }
+ }
- /**
- Attempts to pick an {{#crossLink "Mesh"}}Mesh{{/crossLink}} in this Scene.
+ /**
+ Attempts to pick an {{#crossLink "Mesh"}}Mesh{{/crossLink}} in this Scene.
- Ignores {{#crossLink "Mesh"}}Meshes{{/crossLink}} with {{#crossLink "Mesh/pickable:property"}}pickable{{/crossLink}}
- set *false*.
+ Ignores {{#crossLink "Mesh"}}Meshes{{/crossLink}} with {{#crossLink "Mesh/pickable:property"}}pickable{{/crossLink}}
+ set *false*.
- When a {{#crossLink "Mesh"}}{{/crossLink}} is picked, fires a "pick" event on the {{#crossLink "Mesh"}}{{/crossLink}}
- with the hit result as parameters.
+ When a {{#crossLink "Mesh"}}{{/crossLink}} is picked, fires a "pick" event on the {{#crossLink "Mesh"}}{{/crossLink}}
+ with the hit result as parameters.
- Picking the {{#crossLink "Mesh"}}{{/crossLink}} at the given canvas coordinates:
+ Picking the {{#crossLink "Mesh"}}{{/crossLink}} at the given canvas coordinates:
- ````javascript
- var hit = scene.pick({
+ ````javascript
+ var hit = scene.pick({
canvasPos: [23, 131]
});
- if (hit) { // Picked a Mesh
+ if (hit) { // Picked a Mesh
var mesh = hit.mesh;
}
- ````
+ ````
- **Usage:**
+ **Usage:**
- Picking the {{#crossLink "Mesh"}}{{/crossLink}} that intersects a ray cast through the canvas:
+ Picking the {{#crossLink "Mesh"}}{{/crossLink}} that intersects a ray cast through the canvas:
- ````javascript
- var hit = scene.pick({
+ ````javascript
+ var hit = scene.pick({
pickSurface: true,
canvasPos: [23, 131]
});
- if (hit) { // Picked a Mesh
+ if (hit) { // Picked a Mesh
var mesh = hit.mesh;
@@ -1798,18 +1837,18 @@
var normal = hit.normal; // Float32Array containing the interpolated normal vector at the picked position on the triangle
var uv = hit.uv; // Float32Array containing the interpolated UV coordinates at the picked position on the triangle
}
- ````
+ ````
- Picking the {{#crossLink "Mesh"}}{{/crossLink}} that intersects an arbitrarily-aligned World-space ray:
+ Picking the {{#crossLink "Mesh"}}{{/crossLink}} that intersects an arbitrarily-aligned World-space ray:
- ````javascript
- var hit = scene.pick({
+ ````javascript
+ var hit = scene.pick({
pickSurface: true, // Picking with arbitrarily-positioned ray
origin: [0,0,-5], // Ray origin
direction: [0,0,1] // Ray direction
});
- if (hit) { // Picked a Mesh with the ray
+ if (hit) { // Picked a Mesh with the ray
var mesh = hit.mesh;
@@ -1825,380 +1864,316 @@
var origin = hit.origin; // Float32Array containing the World-space ray origin
var direction = hit.direction; // Float32Array containing the World-space ray direction
}
- ````
- @method pick
-
- @param {*} params Picking parameters.
- @param {Boolean} [params.pickSurface=false] Whether to find the picked position on the surface of the Mesh.
- @param {Float32Array} [params.canvasPos] Canvas-space coordinates. When ray-picking, this will override the
- **origin** and ** direction** parameters and will cause the ray to be fired through the canvas at this position,
- directly along the negative View-space Z-axis.
- @param {Float32Array} [params.origin] World-space ray origin when ray-picking. Ignored when canvasPos given.
- @param {Float32Array} [params.direction] World-space ray direction when ray-picking. Also indicates the length of the ray. Ignored when canvasPos given.
- @param {Array} [params.includeMeshes] IDs of {{#crossLink "Mesh"}}Meshes{{/crossLink}} to restrict picking to. When given, ignores {{#crossLink "Mesh"}}Meshes{{/crossLink}} whose IDs are not in this list.
- @param {Array} [params.excludeMeshes] IDs of {{#crossLink "Mesh"}}Meshes{{/crossLink}} to ignore. When given, will pick *through* these {{#crossLink "Mesh"}}Meshes{{/crossLink}}, as if they were not there.
- @returns {*} Hit record, returned when an {{#crossLink "Mesh"}}{{/crossLink}} is picked, else null. See
- method comments for description.
- */
- pick: (function () {
-
- // Cached vectors to avoid garbage collection
-
- const math = xeogl.math;
-
- const localRayOrigin = math.vec3();
- const localRayDir = math.vec3();
-
- const a = math.vec3();
- const b = math.vec3();
- const c = math.vec3();
-
- const triangleVertices = math.vec3();
- const position = math.vec4();
- const worldPos = math.vec3();
- const viewPos = math.vec3();
- const bary = math.vec3();
-
- const na = math.vec3();
- const nb = math.vec3();
- const nc = math.vec3();
-
- const uva = math.vec3();
- const uvb = math.vec3();
- const uvc = math.vec3();
-
- const tempVec4a = math.vec4();
- const tempVec4b = math.vec4();
- const tempVec4c = math.vec4();
-
- const tempVec3 = math.vec3();
- const tempVec3b = math.vec3();
- const tempVec3c = math.vec3();
- const tempVec3d = math.vec3();
- const tempVec3e = math.vec3();
- const tempVec3f = math.vec3();
- const tempVec3g = math.vec3();
- const tempVec3h = math.vec3();
- const tempVec3i = math.vec3();
- const tempVec3j = math.vec3();
- const tempVec3k = math.vec3();
-
- function getMeshIDMap(scene, meshIds) {
- const map = {};
- let meshId;
- let mesh;
- for (let i = 0, len = meshIds.length; i < len; i++) {
- meshId = meshIds[i];
- mesh = scene.meshes[meshId];
- if (!mesh) {
- scene.warn("pick(): Mesh not found: " + meshId);
- continue;
- }
- map[meshId] = true;
- }
- return map;
- }
-
- function getMeshIDMapFromentityTypes(scene, entityTypes) {
- // var objectIds = {};
- // var entityType;
- // var mesh;
- // for (var i = 0, len = entityTypes.length; i < len; i++) {
- // entityType = entityTypes[i];
- // mesh = scene.meshes[entityType];
- // if (!mesh) {
- // scene.warn("pick(): Mesh not found: " + entityType);
- // continue;
- // }
- // objectIds[mesh._objectId] = true;
- // }
- // return objectIds;
- }
-
- return function (params) {
-
- if (this.canvas.boundary[2] === 0 || this.canvas.boundary[3] === 0) {
- this.error("Picking not allowed while canvas has zero width or height");
- return null;
- }
+ ````
+ @method pick
+
+ @param {*} params Picking parameters.
+ @param {Boolean} [params.pickSurface=false] Whether to find the picked position on the surface of the Mesh.
+ @param {Float32Array} [params.canvasPos] Canvas-space coordinates. When ray-picking, this will override the
+ **origin** and ** direction** parameters and will cause the ray to be fired through the canvas at this position,
+ directly along the negative View-space Z-axis.
+ @param {Float32Array} [params.origin] World-space ray origin when ray-picking. Ignored when canvasPos given.
+ @param {Float32Array} [params.direction] World-space ray direction when ray-picking. Also indicates the length of the ray. Ignored when canvasPos given.
+ @param {Array} [params.includeMeshes] IDs of {{#crossLink "Mesh"}}Meshes{{/crossLink}} to restrict picking to. When given, ignores {{#crossLink "Mesh"}}Meshes{{/crossLink}} whose IDs are not in this list.
+ @param {Array} [params.excludeMeshes] IDs of {{#crossLink "Mesh"}}Meshes{{/crossLink}} to ignore. When given, will pick *through* these {{#crossLink "Mesh"}}Meshes{{/crossLink}}, as if they were not there.
+ @returns {*} Hit record, returned when an {{#crossLink "Mesh"}}{{/crossLink}} is picked, else null. See
+ method comments for description.
+ */
+ pick(params) {
- params = params || {};
+ if (this.canvas.boundary[2] === 0 || this.canvas.boundary[3] === 0) {
+ this.error("Picking not allowed while canvas has zero width or height");
+ return null;
+ }
- params.pickSurface = params.pickSurface || params.rayPick; // Backwards compatibility
+ params = params || {};
- if (!params.canvasPos && (!params.origin || !params.direction)) {
- this.warn("picking without canvasPos or ray origin and direction");
- }
+ params.pickSurface = params.pickSurface || params.rayPick; // Backwards compatibility
- const includeMeshes = params.includeMeshes || params.include; // Backwards compat
- if (includeMeshes) {
- params.includeMeshIds = getMeshIDMap(this, includeMeshes);
- }
+ if (!params.canvasPos && (!params.origin || !params.direction)) {
+ this.warn("picking without canvasPos or ray origin and direction");
+ }
- const excludeMeshes = params.excludeMeshes || params.exclude; // Backwards compat
- if (excludeMeshes) {
- params.excludeMeshIds = getMeshIDMap(this, excludeMeshes);
- }
+ const includeMeshes = params.includeMeshes || params.include; // Backwards compat
+ if (includeMeshes) {
+ params.includeMeshIds = getMeshIDMap(this, includeMeshes);
+ }
- // if (params.includeEntityTypes) {
- // params.includeObjects = getMeshIDMapFromEntityTypes(this, params.includeEntityTypes);
- // }
- //
- // if (params.excludeEntityTypes) {
- // params.excludeObjects = getMeshIDMapFromEntityTypes(this, params.excludeEntityTypes);
- // }
+ const excludeMeshes = params.excludeMeshes || params.exclude; // Backwards compat
+ if (excludeMeshes) {
+ params.excludeMeshIds = getMeshIDMap(this, excludeMeshes);
+ }
- const hit = this._renderer.pick(params);
+ // if (params.includeEntityTypes) {
+ // params.includeObjects = getMeshIDMapFromEntityTypes(this, params.includeEntityTypes);
+ // }
+ //
+ // if (params.excludeEntityTypes) {
+ // params.excludeObjects = getMeshIDMapFromEntityTypes(this, params.excludeEntityTypes);
+ // }
- if (hit) {
+ const hit = this._renderer.pick(params);
- hit.object = hit.mesh; // Backwards compat
+ if (hit) {
- if (params.pickSurface) {
+ hit.object = hit.mesh; // Backwards compat
- if (hit.primIndex !== undefined && hit.primIndex > -1) {
+ if (params.pickSurface) {
- const geometry = hit.mesh.geometry._state;
+ if (hit.primIndex !== undefined && hit.primIndex > -1) {
- if (geometry.primitiveName === "triangles") {
+ const geometry = hit.mesh.geometry._state;
- // Triangle picked; this only happens when the
- // Mesh has a Geometry that has primitives of type "triangle"
+ if (geometry.primitiveName === "triangles") {
- hit.primitive = "triangle";
+ // Triangle picked; this only happens when the
+ // Mesh has a Geometry that has primitives of type "triangle"
- // Get the World-space positions of the triangle's vertices
+ hit.primitive = "triangle";
- const i = hit.primIndex; // Indicates the first triangle index in the indices array
+ // Get the World-space positions of the triangle's vertices
- const indices = geometry.indices; // Indices into geometry arrays, not into shared VertexBufs
- const positions = geometry.positions;
+ const i = hit.primIndex; // Indicates the first triangle index in the indices array
- let ia3;
- let ib3;
- let ic3;
+ const indices = geometry.indices; // Indices into geometry arrays, not into shared VertexBufs
+ const positions = geometry.positions;
- if (indices) {
+ let ia3;
+ let ib3;
+ let ic3;
- var ia = indices[i + 0];
- var ib = indices[i + 1];
- var ic = indices[i + 2];
+ if (indices) {
- triangleVertices[0] = ia;
- triangleVertices[1] = ib;
- triangleVertices[2] = ic;
+ var ia = indices[i + 0];
+ var ib = indices[i + 1];
+ var ic = indices[i + 2];
- hit.indices = triangleVertices;
+ triangleVertices[0] = ia;
+ triangleVertices[1] = ib;
+ triangleVertices[2] = ic;
- ia3 = ia * 3;
- ib3 = ib * 3;
- ic3 = ic * 3;
+ hit.indices = triangleVertices;
- } else {
+ ia3 = ia * 3;
+ ib3 = ib * 3;
+ ic3 = ic * 3;
- ia3 = i * 3;
- ib3 = ia3 + 3;
- ic3 = ib3 + 3;
- }
+ } else {
- a[0] = positions[ia3 + 0];
- a[1] = positions[ia3 + 1];
- a[2] = positions[ia3 + 2];
+ ia3 = i * 3;
+ ib3 = ia3 + 3;
+ ic3 = ib3 + 3;
+ }
- b[0] = positions[ib3 + 0];
- b[1] = positions[ib3 + 1];
- b[2] = positions[ib3 + 2];
+ positionA[0] = positions[ia3 + 0];
+ positionA[1] = positions[ia3 + 1];
+ positionA[2] = positions[ia3 + 2];
- c[0] = positions[ic3 + 0];
- c[1] = positions[ic3 + 1];
- c[2] = positions[ic3 + 2];
+ positionB[0] = positions[ib3 + 0];
+ positionB[1] = positions[ib3 + 1];
+ positionB[2] = positions[ib3 + 2];
- if (geometry.quantized) {
+ positionC[0] = positions[ic3 + 0];
+ positionC[1] = positions[ic3 + 1];
+ positionC[2] = positions[ic3 + 2];
- // Decompress vertex positions
+ if (geometry.quantized) {
- const positionsDecodeMatrix = geometry.positionsDecodeMatrix;
- if (positionsDecodeMatrix) {
- math.decompressPosition(a, positionsDecodeMatrix, a);
- math.decompressPosition(b, positionsDecodeMatrix, b);
- math.decompressPosition(c, positionsDecodeMatrix, c);
- }
- }
+ // Decompress vertex positions
- // Attempt to ray-pick the triangle in local space
+ const positionsDecodeMatrix = geometry.positionsDecodeMatrix;
+ if (positionsDecodeMatrix) {
+ math.decompressPosition(positionA, positionsDecodeMatrix, positionA);
+ math.decompressPosition(positionB, positionsDecodeMatrix, positionB);
+ math.decompressPosition(positionC, positionsDecodeMatrix, positionC);
+ }
+ }
- let canvasPos;
+ // Attempt to ray-pick the triangle in local space
- if (params.canvasPos) {
- canvasPos = params.canvasPos;
- hit.canvasPos = params.canvasPos;
- math.canvasPosToLocalRay(this.camera, hit.mesh, canvasPos, localRayOrigin, localRayDir);
+ let canvasPos;
- } else if (params.origin && params.direction) {
- math.worldRayToLocalRay(hit.mesh, params.origin, params.direction, localRayOrigin, localRayDir);
- }
+ if (params.canvasPos) {
+ canvasPos = params.canvasPos;
+ hit.canvasPos = params.canvasPos;
+ math.canvasPosToLocalRay(this.camera, hit.mesh, canvasPos, localRayOrigin, localRayDir);
- math.normalizeVec3(localRayDir);
- math.rayPlaneIntersect(localRayOrigin, localRayDir, a, b, c, position);
+ } else if (params.origin && params.direction) {
+ math.worldRayToLocalRay(hit.mesh, params.origin, params.direction, localRayOrigin, localRayDir);
+ }
- // Get Local-space cartesian coordinates of the ray-triangle intersection
+ math.normalizeVec3(localRayDir);
+ math.rayPlaneIntersect(localRayOrigin, localRayDir, positionA, positionB, positionC, position);
- hit.localPos = position;
- hit.position = position;
+ // Get Local-space cartesian coordinates of the ray-triangle intersection
- // Get interpolated World-space coordinates
+ hit.localPos = position;
+ hit.position = position;
- // Need to transform homogeneous coords
+ // Get interpolated World-space coordinates
- tempVec4a[0] = position[0];
- tempVec4a[1] = position[1];
- tempVec4a[2] = position[2];
- tempVec4a[3] = 1;
+ // Need to transform homogeneous coords
- // Get World-space cartesian coordinates of the ray-triangle intersection
+ tempVec4a[0] = position[0];
+ tempVec4a[1] = position[1];
+ tempVec4a[2] = position[2];
+ tempVec4a[3] = 1;
- math.transformVec4(hit.mesh.worldMatrix, tempVec4a, tempVec4b);
+ // Get World-space cartesian coordinates of the ray-triangle intersection
- worldPos[0] = tempVec4b[0];
- worldPos[1] = tempVec4b[1];
- worldPos[2] = tempVec4b[2];
+ math.transformVec4(hit.mesh.worldMatrix, tempVec4a, tempVec4b);
- hit.worldPos = worldPos;
+ worldPos[0] = tempVec4b[0];
+ worldPos[1] = tempVec4b[1];
+ worldPos[2] = tempVec4b[2];
- // Get View-space cartesian coordinates of the ray-triangle intersection
+ hit.worldPos = worldPos;
- math.transformVec4(hit.mesh.scene.camera.matrix, tempVec4b, tempVec4c);
+ // Get View-space cartesian coordinates of the ray-triangle intersection
- viewPos[0] = tempVec4c[0];
- viewPos[1] = tempVec4c[1];
- viewPos[2] = tempVec4c[2];
+ math.transformVec4(hit.mesh.scene.camera.matrix, tempVec4b, tempVec4c);
- hit.viewPos = viewPos;
+ viewPos[0] = tempVec4c[0];
+ viewPos[1] = tempVec4c[1];
+ viewPos[2] = tempVec4c[2];
- // Get barycentric coordinates of the ray-triangle intersection
+ hit.viewPos = viewPos;
- math.cartesianToBarycentric(position, a, b, c, bary);
+ // Get barycentric coordinates of the ray-triangle intersection
- hit.bary = bary;
+ math.cartesianToBarycentric(position, positionA, positionB, positionC, bary);
- // Get interpolated normal vector
+ hit.bary = bary;
- const normals = geometry.normals;
+ // Get interpolated normal vector
- if (normals) {
+ const normals = geometry.normals;
- if (geometry.quantized) {
+ if (normals) {
- // Decompress vertex normals
+ if (geometry.quantized) {
- const ia2 = ia * 2;
- const ib2 = ib * 2;
- const ic2 = ic * 2;
+ // Decompress vertex normals
- math.octDecodeVec2(normals.subarray(ia2, ia2 + 2), na);
- math.octDecodeVec2(normals.subarray(ib2, ib2 + 2), nb);
- math.octDecodeVec2(normals.subarray(ic2, ic2 + 2), nc);
+ const ia2 = ia * 2;
+ const ib2 = ib * 2;
+ const ic2 = ic * 2;
- } else {
+ math.octDecodeVec2(normals.subarray(ia2, ia2 + 2), normalA);
+ math.octDecodeVec2(normals.subarray(ib2, ib2 + 2), normalB);
+ math.octDecodeVec2(normals.subarray(ic2, ic2 + 2), normalC);
- na[0] = normals[ia3];
- na[1] = normals[ia3 + 1];
- na[2] = normals[ia3 + 2];
+ } else {
- nb[0] = normals[ib3];
- nb[1] = normals[ib3 + 1];
- nb[2] = normals[ib3 + 2];
+ normalA[0] = normals[ia3];
+ normalA[1] = normals[ia3 + 1];
+ normalA[2] = normals[ia3 + 2];
- nc[0] = normals[ic3];
- nc[1] = normals[ic3 + 1];
- nc[2] = normals[ic3 + 2];
- }
+ normalB[0] = normals[ib3];
+ normalB[1] = normals[ib3 + 1];
+ normalB[2] = normals[ib3 + 2];
- const normal = math.addVec3(math.addVec3(
- math.mulVec3Scalar(na, bary[0], tempVec3),
- math.mulVec3Scalar(nb, bary[1], tempVec3b), tempVec3c),
- math.mulVec3Scalar(nc, bary[2], tempVec3d), tempVec3e);
+ normalC[0] = normals[ic3];
+ normalC[1] = normals[ic3 + 1];
+ normalC[2] = normals[ic3 + 2];
+ }
- hit.normal = math.transformVec3(hit.mesh.worldNormalMatrix, normal, tempVec3f);
- }
+ const normal = math.addVec3(math.addVec3(
+ math.mulVec3Scalar(normalA, bary[0], tempVec3),
+ math.mulVec3Scalar(normalB, bary[1], tempVec3b), tempVec3c),
+ math.mulVec3Scalar(normalC, bary[2], tempVec3d), tempVec3e);
- // Get interpolated UV coordinates
+ hit.normal = math.transformVec3(hit.mesh.worldNormalMatrix, normal, tempVec3f);
+ }
- const uvs = geometry.uv;
+ // Get interpolated UV coordinates
- if (uvs) {
+ const uvs = geometry.uv;
- uva[0] = uvs[(ia * 2)];
- uva[1] = uvs[(ia * 2) + 1];
+ if (uvs) {
- uvb[0] = uvs[(ib * 2)];
- uvb[1] = uvs[(ib * 2) + 1];
+ uva[0] = uvs[(ia * 2)];
+ uva[1] = uvs[(ia * 2) + 1];
- uvc[0] = uvs[(ic * 2)];
- uvc[1] = uvs[(ic * 2) + 1];
+ uvb[0] = uvs[(ib * 2)];
+ uvb[1] = uvs[(ib * 2) + 1];
- if (geometry.quantized) {
+ uvc[0] = uvs[(ic * 2)];
+ uvc[1] = uvs[(ic * 2) + 1];
- // Decompress vertex UVs
+ if (geometry.quantized) {
- const uvDecodeMatrix = geometry.uvDecodeMatrix;
- if (uvDecodeMatrix) {
- math.decompressUV(uva, uvDecodeMatrix, uva);
- math.decompressUV(uvb, uvDecodeMatrix, uvb);
- math.decompressUV(uvc, uvDecodeMatrix, uvc);
- }
- }
+ // Decompress vertex UVs
- hit.uv = math.addVec3(
- math.addVec3(
- math.mulVec2Scalar(uva, bary[0], tempVec3g),
- math.mulVec2Scalar(uvb, bary[1], tempVec3h), tempVec3i),
- math.mulVec2Scalar(uvc, bary[2], tempVec3j), tempVec3k);
+ const uvDecodeMatrix = geometry.uvDecodeMatrix;
+ if (uvDecodeMatrix) {
+ math.decompressUV(uva, uvDecodeMatrix, uva);
+ math.decompressUV(uvb, uvDecodeMatrix, uvb);
+ math.decompressUV(uvc, uvDecodeMatrix, uvc);
}
}
+
+ hit.uv = math.addVec3(
+ math.addVec3(
+ math.mulVec2Scalar(uva, bary[0], tempVec3g),
+ math.mulVec2Scalar(uvb, bary[1], tempVec3h), tempVec3i),
+ math.mulVec2Scalar(uvc, bary[2], tempVec3j), tempVec3k);
}
}
+ }
+ }
- hit.mesh.fire("picked", hit);
+ hit.mesh.fire("picked", hit);
- return hit;
- }
- };
- })(),
+ return hit;
+ }
+ }
- /**
- Returns the collective axis-aligned bounding box of the {{#crossLink "Object"}}Objects{{/crossLink}}, specified by their IDs, GUIDs and/or entity types.
+ /**
+ Returns the collective axis-aligned bounding box of the {{#crossLink "Object"}}Objects{{/crossLink}}, specified by their IDs, GUIDs and/or entity types.
- When no arguments are given, returns the total boundary of all objects in the scene.
+ When no arguments are given, returns the total boundary of all objects in the scene.
- Only {{#crossLink "Mesh"}}Meshes{{/crossLink}} with {{#crossLink "Mesh/collidable:property"}}collidable{{/crossLink}}
- set ````true```` are included in the boundary.
+ Only {{#crossLink "Mesh"}}Meshes{{/crossLink}} with {{#crossLink "Mesh/collidable:property"}}collidable{{/crossLink}}
+ set ````true```` are included in the boundary.
- ## Usage
+ ## Usage
- ````JavaScript
- scene.getAABB(); // Gets collective boundary of all objects in the scene
- scene.getAABB("saw"); // Gets collective boundary of all objects in saw model
- scene.getAABB(["saw", "gearbox"]); // Gets collective boundary of all objects in saw and gearbox models
- scene.getAABB("saw#0.1"); // Get boundary of an object in the saw model
- scene.getAABB(["saw#0.1", "saw#0.2"]); // Get collective boundary of two objects in saw model
- scene.getAABB(["saw#0.1", "surface", "support"]); // Get collective boundary an object, and all objects of the given two entity classes.
- ````
+ ````JavaScript
+ scene.getAABB(); // Gets collective boundary of all objects in the scene
+ scene.getAABB("saw"); // Gets collective boundary of all objects in saw model
+ scene.getAABB(["saw", "gearbox"]); // Gets collective boundary of all objects in saw and gearbox models
+ scene.getAABB("saw#0.1"); // Get boundary of an object in the saw model
+ scene.getAABB(["saw#0.1", "saw#0.2"]); // Get collective boundary of two objects in saw model
+ scene.getAABB(["saw#0.1", "surface", "support"]); // Get collective boundary an object, and all objects of the given two entity classes.
+ ````
- @method getAABB
- @param {String|String[]} target {Array} Array of {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
- @returns {[Number, Number, Number, Number, Number, Number]} An axis-aligned World-space bounding box, given as elements ````[xmin, ymin, zmin, xmax, ymax, zmax]````.
- */
- getAABB: (function () {
- let xmin = 100000;
- let ymin = 100000;
- let zmin = 100000;
- let xmax = -100000;
- let ymax = -100000;
- let zmax = -100000;
- let valid;
-
- function callback(object) {
+ @method getAABB
+ @param {String|String[]} target {Array} Array of {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
+ @returns {[Number, Number, Number, Number, Number, Number]} An axis-aligned World-space bounding box, given as elements ````[xmin, ymin, zmin, xmax, ymax, zmax]````.
+ */
+ getAABB(target) {
+ if (target === undefined) {
+ return this.aabb;
+ }
+ if (utils.isString(target)) {
+ const object = this.objects[target];
+ if (object) {
+ return object.aabb;
+ }
+ target = [target]; // Must be an entity type
+ }
+ if (target.length === 0) {
+ return this.aabb;
+ }
+ let xmin = 100000;
+ let ymin = 100000;
+ let zmin = 100000;
+ let xmax = -100000;
+ let ymax = -100000;
+ let zmax = -100000;
+ let valid;
+ this.withObjects(target, object => {
const aabb = object.aabb;
if (aabb[0] < xmin) {
xmin = aabb[0];
@@ -2220,419 +2195,329 @@
}
valid = true;
}
+ );
+ if (valid) {
+ const aabb2 = new math.AABB3();
+ aabb2[0] = xmin;
+ aabb2[1] = ymin;
+ aabb2[2] = zmin;
+ aabb2[3] = xmax;
+ aabb2[4] = ymax;
+ aabb2[5] = zmax;
+ return aabb2;
+ } else {
+ return this.aabb; // Scene AABB
+ }
+ }
- return function (target) {
- if (target === undefined) {
- return this.aabb;
- }
- if (xeogl._isString(target)) {
- const object = this.objects[target];
- if (object) {
- return object.aabb;
- }
- target = [target]; // Must be an entity type
- }
- if (target.length === 0) {
- return this.aabb;
- }
- xmin = 100000;
- ymin = 100000;
- zmin = 100000;
- xmax = -100000;
- ymax = -100000;
- zmax = -100000;
- this.withObjects(target, callback);
- if (valid) {
- const aabb2 = new xeogl.math.AABB3();
- aabb2[0] = xmin;
- aabb2[1] = ymin;
- aabb2[2] = zmin;
- aabb2[3] = xmax;
- aabb2[4] = ymax;
- aabb2[5] = zmax;
- return aabb2;
- } else {
- return this.aabb; // Scene AABB
- }
- };
- })(),
-
- /**
- Resets this Scene to its default state.
-
- References to any components in this Scene will become invalid.
-
- @method clear
- */
- clear: function () { // FIXME: should only clear user-created components
- for (const id in this.components) {
- if (this.components.hasOwnProperty(id)) {
- // Each component fires "destroyed" as it is destroyed,
- // which this Scene handles by removing the component
- this.components[id].destroy();
- }
- }
- // Reinitialise defaults
- this._initDefaults();
- },
-
- /**
- Convenience method that destroys all light sources.
-
- Removes all {{#crossLink "AmbientLight"}}AmbientLights{{/crossLink}}, {{#crossLink "PointLight"}}PointLights{{/crossLink}},
- {{#crossLink "DirLight"}}DirLights{{/crossLink}} and {{#crossLink "SpotLight"}}SpotLights{{/crossLink}}.
-
- @method clearLights
- */
- clearLights: function () {
- const ids = Object.keys(this.lights);
- for (let i = 0, len = ids.length; i < len; i++) {
- this.lights[ids[i]].destroy();
- }
- },
-
- /**
- Convenience method that destroys all {{#crossLink "Clip"}}Clips{{/crossLink}}.
-
- @method clearClips
- */
- clearClips: function () {
- const ids = Object.keys(this.clips);
- for (let i = 0, len = ids.length; i < len; i++) {
- this.clips[ids[i]].destroy();
- }
- },
-
- /**
- Shows or hides a batch of {{#crossLink "Object"}}Objects{{/crossLink}}, specified by their IDs, GUIDs and/or entity types.
-
- Each Object indicates its visibility status in its {{#crossLink "Object/visibility:property"}}{{/crossLink}} property.
-
- Each visible Object is registered in the {{#crossLink "Scene"}}{{/crossLink}}'s
- {{#crossLink "Scene/visibleEntities:property"}}{{/crossLink}} map while its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
- is assigned a value.
-
- @method setVisible
- @param ids {Array} Array of {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
- @param visible {Boolean} The new visibility state.
- @returns {Boolean} True if any {{#crossLink "Object"}}Objects{{/crossLink}} changed visibility, else false if all updates were redundant and not applied.
- */
- setVisible: (function () {
- let newValue;
-
- function callback(object) {
- const changed = (object.visible != newValue);
- object.visible = newValue;
- return changed;
- }
-
- return function (ids, visible) {
- newValue = visible;
- return this.withObjects(ids, callback);
- };
- })(),
-
- /**
- Culls or unculls a batch of {{#crossLink "Object"}}Objects{{/crossLink}}, specified by their IDs, GUIDs and/or entity types.
-
- Each Object indicates its culled status in its {{#crossLink "Object/visibility:property"}}{{/crossLink}} property.
+ /**
+ Resets this Scene to its default state.
- @method setVisible
- @param ids {Array} Array of {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
- @param visible {Boolean} The new cull state.
- @returns {Boolean} True if any {{#crossLink "Object"}}Objects{{/crossLink}} changed culled state, else false if all updates were redundant and not applied.
- */
- setCulled: (function () {
- let newValue;
+ References to any components in this Scene will become invalid.
- function callback(object) {
- const changed = (object.culled != newValue);
- object.culled = newValue;
- return changed;
+ @method clear
+ */
+ clear() { // FIXME: should only clear user-created components
+ for (const id in this.components) {
+ if (this.components.hasOwnProperty(id)) {
+ // Each component fires "destroyed" as it is destroyed,
+ // which this Scene handles by removing the component
+ this.components[id].destroy();
}
+ }
+ // Reinitialise defaults
+ this._initDefaults();
+ }
- return function (ids, culled) {
- newValue = culled;
- return this.withObjects(ids, callback);
- };
- })(),
-
- /**
- Selects or de-selects a batch of {{#crossLink "Object"}}Objects{{/crossLink}}, specified by their IDs, GUIDs and/or entity types.
-
- Each Object indicates its selected status in its {{#crossLink "Object/selected:property"}}{{/crossLink}} property.
-
- Each selected Object is registered in the {{#crossLink "Scene"}}{{/crossLink}}'s
- {{#crossLink "Scene/selectedEntities:property"}}{{/crossLink}} map while its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
- is assigned a value.
+ /**
+ Convenience method that destroys all light sources.
- @method setSelected
- @param ids {Array} Array of {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
- @param selected {Boolean} Whether to select or deselect.
- @returns {Boolean} True if any {{#crossLink "Object"}}Objects{{/crossLink}} changed selection state, else false if all updates were redundant and not applied.
- */
- setSelected: (function () {
- let newValue;
+ Removes all {{#crossLink "AmbientLight"}}AmbientLights{{/crossLink}}, {{#crossLink "PointLight"}}PointLights{{/crossLink}},
+ {{#crossLink "DirLight"}}DirLights{{/crossLink}} and {{#crossLink "SpotLight"}}SpotLights{{/crossLink}}.
- function callback(object) {
- const changed = (object.selected != newValue);
- object.selected = newValue;
- return changed;
- }
+ @method clearLights
+ */
+ clearLights() {
+ const ids = Object.keys(this.lights);
+ for (let i = 0, len = ids.length; i < len; i++) {
+ this.lights[ids[i]].destroy();
+ }
+ }
- return function (ids, selected) {
- newValue = selected;
- return this.withObjects(ids, callback);
- };
- })(),
+ /**
+ Convenience method that destroys all {{#crossLink "Clip"}}Clips{{/crossLink}}.
- /**
- Highlights or de-highlights a batch of {{#crossLink "Object"}}Objects{{/crossLink}}, specified by their IDs, GUIDs and/or entity types.
+ @method clearClips
+ */
+ clearClips() {
+ const ids = Object.keys(this.clips);
+ for (let i = 0, len = ids.length; i < len; i++) {
+ this.clips[ids[i]].destroy();
+ }
+ }
- Each Object indicates its highlight status in its {{#crossLink "Object/highlighted:property"}}{{/crossLink}} property.
+ /**
+ Shows or hides a batch of {{#crossLink "Object"}}Objects{{/crossLink}}, specified by their IDs, GUIDs and/or entity types.
- Each highlighted Object is registered in the {{#crossLink "Scene"}}{{/crossLink}}'s
- {{#crossLink "Scene/highlightedEntities:property"}}{{/crossLink}} map while its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
- is assigned a value.
+ Each Object indicates its visibility status in its {{#crossLink "Object/visibility:property"}}{{/crossLink}} property.
- @method setHighlighted
- @param ids {Array} Array of {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
- @param highlighted {Boolean} Whether to highlight or un-highlight.
- @returns {Boolean} True if any {{#crossLink "Object"}}Objects{{/crossLink}} changed highlighted state, else false if all updates were redundant and not applied.
- */
- setHighlighted: (function () {
- let newValue;
+ Each visible Object is registered in the {{#crossLink "Scene"}}{{/crossLink}}'s
+ {{#crossLink "Scene/visibleEntities:property"}}{{/crossLink}} map while its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
+ is assigned a value.
- function callback(object) {
- const changed = (object.highlighted != newValue);
- object.highlighted = newValue;
- return changed;
- }
+ @method setVisible
+ @param ids {Array} Array of {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
+ @param visible {Boolean} The new visibility state.
+ @returns {Boolean} True if any {{#crossLink "Object"}}Objects{{/crossLink}} changed visibility, else false if all updates were redundant and not applied.
+ */
+ setVisible(ids, visible) {
+ return this.withObjects(ids, object => {
+ const changed = (object.visible !== visible);
+ object.visible = visible;
+ return changed;
+ });
+ }
- return function (ids, highlighted) {
- newValue = highlighted;
- return this.withObjects(ids, callback);
- };
- })(),
+ /**
+ Culls or unculls a batch of {{#crossLink "Object"}}Objects{{/crossLink}}, specified by their IDs, GUIDs and/or entity types.
- /**
- Ghosts or un-ghosts a batch of {{#crossLink "Object"}}Objects{{/crossLink}}, specified by their IDs, GUIDs and/or entity types.
+ Each Object indicates its culled status in its {{#crossLink "Object/visibility:property"}}{{/crossLink}} property.
- Each Object indicates its ghosted status in its {{#crossLink "Object/ghosted:property"}}{{/crossLink}} property.
+ @method setVisible
+ @param ids {Array} Array of {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
+ @param culled {Boolean} The new cull state.
+ @returns {Boolean} True if any {{#crossLink "Object"}}Objects{{/crossLink}} changed culled state, else false if all updates were redundant and not applied.
+ */
+ setCulled(ids, culled) {
+ return this.withObjects(ids, object => {
+ const changed = (object.culled !== culled);
+ object.culled = culled;
+ return changed;
+ });
+ }
- Each ghosted Object is registered in the {{#crossLink "Scene"}}{{/crossLink}}'s
- {{#crossLink "Scene/ghostedEntities:property"}}{{/crossLink}} map when its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
- is assigned a value.
+ /**
+ Selects or de-selects a batch of {{#crossLink "Object"}}Objects{{/crossLink}}, specified by their IDs, GUIDs and/or entity types.
- @method setGhosted
- @param ids {Array} Array of {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
- @param ghosted {Float32Array} Whether to ghost or un-ghost.
- @returns {Boolean} True if any {{#crossLink "Object"}}Objects{{/crossLink}} changed ghosted state, else false if all updates were redundant and not applied.
- */
- setGhosted: (function () {
- let newValue;
+ Each Object indicates its selected status in its {{#crossLink "Object/selected:property"}}{{/crossLink}} property.
- function callback(object) {
- const changed = (object.ghosted != newValue);
- object.ghosted = newValue;
- return changed;
- }
+ Each selected Object is registered in the {{#crossLink "Scene"}}{{/crossLink}}'s
+ {{#crossLink "Scene/selectedEntities:property"}}{{/crossLink}} map while its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
+ is assigned a value.
- return function (ids, ghosted) {
- newValue = ghosted;
- return this.withObjects(ids, callback);
- };
- })(),
+ @method setSelected
+ @param ids {Array} Array of {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
+ @param selected {Boolean} Whether to select or deselect.
+ @returns {Boolean} True if any {{#crossLink "Object"}}Objects{{/crossLink}} changed selection state, else false if all updates were redundant and not applied.
+ */
+ setSelected(ids, selected) {
+ return this.withObjects(ids, object => {
+ const changed = (object.selected !== selected);
+ object.selected = selected;
+ return changed;
+ });
+ }
- /**
- Shows or hides wireeframe edges for batch of {{#crossLink "Object"}}Objects{{/crossLink}}, specified by their IDs, GUIDs and/or entity types.
+ /**
+ Highlights or de-highlights a batch of {{#crossLink "Object"}}Objects{{/crossLink}}, specified by their IDs, GUIDs and/or entity types.
- @method setEdges
- @param ids {Array} Array of {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
- @param edges {Float32Array} Whether to show or hide edges.
- @returns {Boolean} True if any {{#crossLink "Object"}}Objects{{/crossLink}} changed edges state, else false if all updates were redundant and not applied.
- */
- setEdges: (function () {
- let newValue;
+ Each Object indicates its highlight status in its {{#crossLink "Object/highlighted:property"}}{{/crossLink}} property.
- function callback(object) {
- const changed = (object.edges != newValue);
- object.edges = newValue;
- return changed;
- }
+ Each highlighted Object is registered in the {{#crossLink "Scene"}}{{/crossLink}}'s
+ {{#crossLink "Scene/highlightedEntities:property"}}{{/crossLink}} map while its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
+ is assigned a value.
- return function (ids, edges) {
- newValue = edges;
- return this.withObjects(ids, callback);
- };
- })(),
+ @method setHighlighted
+ @param ids {Array} Array of {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
+ @param highlighted {Boolean} Whether to highlight or un-highlight.
+ @returns {Boolean} True if any {{#crossLink "Object"}}Objects{{/crossLink}} changed highlighted state, else false if all updates were redundant and not applied.
+ */
+ setHighlighted(ids, highlighted) {
+ return this.withObjects(ids, object => {
+ const changed = (object.highlighted !== highlighted);
+ object.highlighted = highlighted;
+ return changed;
+ });
+ }
- /**
- Shows or hides an outline around a batch of {{#crossLink "Object"}}Objects{{/crossLink}}, specified by their IDs, GUIDs and/or entity types.
+ /**
+ Ghosts or un-ghosts a batch of {{#crossLink "Object"}}Objects{{/crossLink}}, specified by their IDs, GUIDs and/or entity types.
- Each Object indicates its outlined status in its {{#crossLink "Object/outlined:property"}}{{/crossLink}} property.
+ Each Object indicates its ghosted status in its {{#crossLink "Object/ghosted:property"}}{{/crossLink}} property.
- Each outlined Object is registered in the {{#crossLink "Scene"}}{{/crossLink}}'s
- {{#crossLink "Scene/outlinedEntities:property"}}{{/crossLink}} map when its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
- is assigned a value.
+ Each ghosted Object is registered in the {{#crossLink "Scene"}}{{/crossLink}}'s
+ {{#crossLink "Scene/ghostedEntities:property"}}{{/crossLink}} map when its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
+ is assigned a value.
- @method setOutlined
- @param ids {Array} Array of {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
- @param outlined {Float32Array} Whether to show or hide the outline.
- @returns {Boolean} True if any {{#crossLink "Object"}}Objects{{/crossLink}} changed outlined state, else false if all updates were redundant and not applied.
- */
- setOutlined: (function () {
- // var newValue;
- //
- // function callback(object) {
- // var changed = (object.outlined != newValue);
- // object.outlined = newValue;
- // return changed;
- // }
- //
- // return function (ids, outlined) {
- // newValue = outlined;
- // return this.withObjects(ids, callback);
- // };
- })(),
+ @method setGhosted
+ @param ids {Array} Array of {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
+ @param ghosted {Float32Array} Whether to ghost or un-ghost.
+ @returns {Boolean} True if any {{#crossLink "Object"}}Objects{{/crossLink}} changed ghosted state, else false if all updates were redundant and not applied.
+ */
+ setGhosted(ids, ghosted) {
+ return this.withObjects(ids, object => {
+ const changed = (object.ghosted !== ghosted);
+ object.ghosted = ghosted;
+ return changed;
+ });
+ }
- /**
- Colorizes a batch of {{#crossLink "Object"}}Objects{{/crossLink}}, specified by their IDs, GUIDs and/or entity types.
+ /**
+ Shows or hides wireeframe edges for batch of {{#crossLink "Object"}}Objects{{/crossLink}}, specified by their IDs, GUIDs and/or entity types.
- @method setColorize
- @param ids {Array} Array of {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
- @param [colorize=(1,1,1)] Float32Array RGB colorize factors, multiplied by the rendered pixel colors.
- */
- setColorize: (function () {
- let newValue;
+ @method setEdges
+ @param ids {Array} Array of {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
+ @param edges {Float32Array} Whether to show or hide edges.
+ @returns {Boolean} True if any {{#crossLink "Object"}}Objects{{/crossLink}} changed edges state, else false if all updates were redundant and not applied.
+ */
+ setEdges(ids, edges) {
+ return this.withObjects(ids, object => {
+ const changed = (object.edges !== edges);
+ object.edges = edges;
+ return changed;
+ });
+ }
- function callback(object) {
- object.colorize = newValue;
- }
+ /**
+ Shows or hides an outline around a batch of {{#crossLink "Object"}}Objects{{/crossLink}}, specified by their IDs, GUIDs and/or entity types.
- return function (ids, colorize) {
- newValue = colorize;
- this.withObjects(ids, callback);
- };
- })(),
+ Each Object indicates its outlined status in its {{#crossLink "Object/outlined:property"}}{{/crossLink}} property.
- /**
- Updates opacities of a batch of {{#crossLink "Object"}}Objects{{/crossLink}}, specified by their IDs, GUIDs and/or entity types.
+ Each outlined Object is registered in the {{#crossLink "Scene"}}{{/crossLink}}'s
+ {{#crossLink "Scene/outlinedEntities:property"}}{{/crossLink}} map when its {{#crossLink "Object/entityType:property"}}{{/crossLink}}
+ is assigned a value.
- @method setOpacity
- @param ids {Array} Array of {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
- @param [opacity=1] Number Opacity factor in range ````[0..1]````, multiplies by the rendered pixel alphas.
- */
- setOpacity: (function () {
- let newValue;
+ @method setOutlined
+ @param ids {Array} Array of {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
+ @param outlined {Float32Array} Whether to show or hide the outline.
+ @returns {Boolean} True if any {{#crossLink "Object"}}Objects{{/crossLink}} changed outlined state, else false if all updates were redundant and not applied.
+ */
+ setOutlined(ids, outlined) {
+ return this.withObjects(ids, object => {
+ const changed = (object.outlined !== outlined);
+ object.outlined = outlined;
+ return changed;
+ });
+ }
- function callback(object) {
- object.opacity = newValue;
- }
+ /**
+ Colorizes a batch of {{#crossLink "Object"}}Objects{{/crossLink}}, specified by their IDs, GUIDs and/or entity types.
- return function (ids, opacity) {
- newValue = opacity;
- this.withObjects(ids, callback);
- };
- })(),
+ @method setColorize
+ @param ids {Array} Array of {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
+ @param [colorize=(1,1,1)] Float32Array RGB colorize factors, multiplied by the rendered pixel colors.
+ */
+ setColorize(ids, colorize) {
+ return this.withObjects(ids, object => {
+ object.colorize = colorize;
+ });
+ }
- /**
- Sets a batch of {{#crossLink "Object"}}Objects{{/crossLink}} pickable or unpickable, specified by their IDs, GUIDs and/or entity types.
+ /**
+ Updates opacities of a batch of {{#crossLink "Object"}}Objects{{/crossLink}}, specified by their IDs, GUIDs and/or entity types.
- Picking is done via calls to {{#crossLink "Scene/pick:method"}}Scene#pick(){{/crossLink}}.
+ @method setOpacity
+ @param ids {Array} Array of {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
+ @param [opacity=1] Number Opacity factor in range ````[0..1]````, multiplies by the rendered pixel alphas.
+ */
+ setOpacity(ids, opacity) {
+ return this.withObjects(ids, object => {
+ object.opacity = opacity;
+ });
+ }
- @method setPickable
- @param ids {Array} Array of {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
- @param pickable {Float32Array} Whether to ghost or un-ghost.
- @returns {Boolean} True if any {{#crossLink "Object"}}Objects{{/crossLink}} changed pickable state, else false if all updates were redundant and not applied.
- */
- setPickable: (function () {
- let newValue;
+ /**
+ Sets a batch of {{#crossLink "Object"}}Objects{{/crossLink}} pickable or unpickable, specified by their IDs, GUIDs and/or entity types.
- function callback(object) {
- const changed = (object.pickable != newValue);
- object.pickable = newValue;
- return changed;
- }
+ Picking is done via calls to {{#crossLink "Scene/pick:method"}}Scene#pick(){{/crossLink}}.
- return function (ids, pickable) {
- newValue = pickable;
- return this.withObjects(ids, callback);
- };
- })(),
+ @method setPickable
+ @param ids {Array} Array of {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
+ @param pickable {Float32Array} Whether to ghost or un-ghost.
+ @returns {Boolean} True if any {{#crossLink "Object"}}Objects{{/crossLink}} changed pickable state, else false if all updates were redundant and not applied.
+ */
+ setPickable(ids, pickable) {
+ return this.withObjects(ids, object => {
+ const changed = (object.pickable !== pickable);
+ object.pickable = pickable;
+ return changed;
+ });
+ }
- /**
- Iterates with a callback over {{#crossLink "Object"}}Objects{{/crossLink}}, specified by their IDs, GUIDs and/or entity types.
+ /**
+ Iterates with a callback over {{#crossLink "Object"}}Objects{{/crossLink}}, specified by their IDs, GUIDs and/or entity types.
- @method withObjects
- @param ids {String|Array} One or more {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
- @param callback {Function} The callback, which takes each object as its argument.
- */
- withObjects: function (ids, callback) {
- if (xeogl._isString(ids)) {
- ids = [ids];
- }
- let changed = false;
- for (let i = 0, len = ids.length; i < len; i++) {
- const id = ids[i];
- let object = this.objects[id];
+ @method withObjects
+ @param ids {String|Array} One or more {{#crossLink "Object"}}{{/crossLink}} IDs, GUIDs or entity types.
+ @param callback {Function} The callback, which takes each object as its argument.
+ */
+ withObjects(ids, callback) {
+ if (utils.isString(ids)) {
+ ids = [ids];
+ }
+ let changed = false;
+ for (let i = 0, len = ids.length; i < len; i++) {
+ const id = ids[i];
+ let object = this.objects[id];
+ if (object) {
+ changed = callback(object) || changed;
+ } else {
+ object = this.guidObjects[id];
if (object) {
changed = callback(object) || changed;
} else {
- object = this.guidObjects[id];
- if (object) {
- changed = callback(object) || changed;
- } else {
- const objects = this.entityTypes[id];
- if (objects) {
- for (const objectId in objects) {
- if (objects.hasOwnProperty(objectId)) {
- changed = callback(objects[objectId]) || changed;
- }
+ const objects = this.entityTypes[id];
+ if (objects) {
+ for (const objectId in objects) {
+ if (objects.hasOwnProperty(objectId)) {
+ changed = callback(objects[objectId]) || changed;
}
}
}
}
}
- return changed;
- },
-
- _destroy: function () {
-
- this.clear();
-
- this.canvas.gl = null;
-
- // Memory leak prevention
- this.models = null;
- this.objects = null;
- this.guidObjects = null;
- this.entityTypes = null;
- this.entities = null;
- this.visibleEntities = null;
- this.ghostedEntities = null;
- this.highlightedEntities = null;
- this.selectedEntities = null;
- this.clips = null;
- this.lights = null;
- this.lightMaps = null;
- this.reflectionMaps = null;
- this._objectGUIDs = null;
- this._entityIds = null;
- this._visibleEntityIds = null;
- this._ghostedEntityIds = null;
- this._highlightedEntityIds = null;
- this._selectedEntityIds = null;
- this.meshes = null;
- this.types = null;
- this.components = null;
- this.rootObjects = null;
- this.canvas = null;
- this._renderer = null;
- this.input = null;
- this._viewport = null;
- this._camera = null;
}
- });
-
-})();
+ return changed;
+ }
+
+ destroy() {
+
+ super.destroy();
+
+ this.clear();
+
+ this.canvas.gl = null;
+
+ // Memory leak prevention
+ this.models = null;
+ this.objects = null;
+ this.guidObjects = null;
+ this.entityTypes = null;
+ this.entities = null;
+ this.visibleEntities = null;
+ this.ghostedEntities = null;
+ this.highlightedEntities = null;
+ this.selectedEntities = null;
+ this.clips = null;
+ this.lights = null;
+ this.lightMaps = null;
+ this.reflectionMaps = null;
+ this._objectGUIDs = null;
+ this._entityIds = null;
+ this._visibleEntityIds = null;
+ this._ghostedEntityIds = null;
+ this._highlightedEntityIds = null;
+ this._selectedEntityIds = null;
+ this.meshes = null;
+ this.types = null;
+ this.components = null;
+ this.rootObjects = null;
+ this.canvas = null;
+ this._renderer = null;
+ this.input = null;
+ this._viewport = null;
+ this._camera = null;
+ }
+}
+
+export {Scene};
diff --git a/src/stats.js b/src/stats.js
new file mode 100644
index 000000000..67cab8d28
--- /dev/null
+++ b/src/stats.js
@@ -0,0 +1,50 @@
+const stats = {
+ build: {
+ version: "0.8"
+ },
+ client: {
+ browser: (navigator && navigator.userAgent) ? navigator.userAgent : "n/a"
+ },
+
+ // TODO: replace 'canvas' with 'pixels'
+ //canvas: {
+ // width: 0,
+ // height: 0
+ //},
+ components: {
+ scenes: 0,
+ models: 0,
+ meshes: 0,
+ objects: 0
+ },
+ memory: {
+
+ // Note that these counts will include any positions, colors,
+ // normals and indices that xeogl internally creates on-demand
+ // to support color-index triangle picking.
+
+ meshes: 0,
+ positions: 0,
+ colors: 0,
+ normals: 0,
+ uvs: 0,
+ indices: 0,
+ textures: 0,
+ transforms: 0,
+ materials: 0,
+ programs: 0
+ },
+ frame: {
+ frameCount: 0,
+ fps: 0,
+ useProgram: 0,
+ bindTexture: 0,
+ bindArray: 0,
+ drawElements: 0,
+ drawArrays: 0,
+ tasksRun: 0,
+ tasksScheduled: 0
+ }
+};
+
+export {stats};
\ No newline at end of file
diff --git a/src/tasks.js b/src/tasks.js
new file mode 100644
index 000000000..55f964adb
--- /dev/null
+++ b/src/tasks.js
@@ -0,0 +1,47 @@
+import {Queue} from './utils/queue.js';
+
+const taskQueue = new Queue(); // Task queue, which is pumped on each frame; tasks are pushed to it with calls to xeogl.schedule
+
+const tasks = {
+
+ /**
+ Schedule a task for xeogl to run at the next frame.
+
+ Internally, this pushes the task to a FIFO queue. Within each frame interval, xeogl processes the queue
+ for a certain period of time, popping tasks and running them. After each frame interval, tasks that did not
+ get a chance to run during the task are left in the queue to be run next time.
+
+ @method scheduleTask
+ @param {Function} callback Callback that runs the task.
+ @param {Object} [scope] Scope for the callback.
+ */
+ scheduleTask(callback, scope) {
+ taskQueue.push(callback);
+ taskQueue.push(scope);
+ },
+
+ runTasks(until) { // Pops and processes tasks in the queue, until the given number of milliseconds has elapsed.
+ let time = (new Date()).getTime();
+ let callback;
+ let scope;
+ let tasksRun = 0;
+ while (taskQueue.length > 0 && time < until) {
+ callback = taskQueue.shift();
+ scope = taskQueue.shift();
+ if (scope) {
+ callback.call(scope);
+ } else {
+ callback();
+ }
+ time = (new Date()).getTime();
+ tasksRun++;
+ }
+ return tasksRun;
+ },
+
+ getNumTasks() {
+ return taskQueue.length;
+ }
+};
+
+export {tasks};
\ No newline at end of file
diff --git a/src/utils.js b/src/utils.js
new file mode 100644
index 000000000..2d3702413
--- /dev/null
+++ b/src/utils.js
@@ -0,0 +1,192 @@
+var utils = {
+
+ /**
+ Tests if the given object is an array
+ @private
+ */
+ isArray: function (testMesh) {
+ return testMesh && !(testMesh.propertyIsEnumerable('length')) && typeof testMesh === 'object' && typeof testMesh.length === 'number';
+ },
+
+ /**
+ Tests if the given value is a string
+ @param value
+ @returns {boolean}
+ @private
+ */
+ isString: function (value) {
+ return (typeof value === 'string' || value instanceof String);
+ },
+
+ /**
+ Tests if the given value is a number
+ @param value
+ @returns {boolean}
+ @private
+ */
+ isNumeric: function (value) {
+ return !isNaN(parseFloat(value)) && isFinite(value);
+ },
+
+ /**
+ Tests if the given value is an ID
+ @param value
+ @returns {boolean}
+ @private
+ */
+ isID: function (value) {
+ return utils._isString(value) || utils._isNumeric(value);
+ },
+
+ /**
+ Tests if the given components are the same, where the components can be either IDs or instances.
+ @param c1
+ @param c2
+ @returns {boolean}
+ @private
+ */
+ isSameComponent: function (c1, c2) {
+ if (!c1 || !c2) {
+ return false;
+ }
+ const id1 = (utils._isNumeric(c1) || utils._isString(c1)) ? `${c1}` : c1.id;
+ const id2 = (utils._isNumeric(c2) || utils._isString(c2)) ? `${c2}` : c2.id;
+ return id1 === id2;
+ },
+
+ /**
+ Tests if the given value is a function
+ @param value
+ @returns {boolean}
+ @private
+ */
+ isFunction: function (value) {
+ return (typeof value === "function");
+ },
+
+ /**
+ Tests if the given value is a JavaScript JSON object, eg, ````{ foo: "bar" }````.
+ @param value
+ @returns {boolean}
+ @private
+ */
+ isObject: function () {
+ const objectConstructor = {}.constructor;
+ return value => !!value && value.constructor === objectConstructor;
+ },
+
+ /**
+ Tests if the given component type is a subtype of another component supertype.
+ @param {String} type
+ @param {String} [superType="xeogl.Component"]
+ @returns {boolean}
+ @private
+ */
+ isComponentType: function (type, superType = "xeogl.Component") {
+ if (type === superType) {
+ return true;
+ }
+ const types = utils._superTypes[type];
+ if (!types) {
+ return false;
+ }
+ for (let i = types.length - 1; i >= 0; i--) {
+ if (types[i] === superType) {
+ return true;
+ }
+ }
+ return false;
+ },
+
+ /** Returns a shallow copy
+ */
+ copy: function (o) {
+ return utils._apply(o, {});
+ },
+
+ /** Add properties of o to o2, overwriting them on o2 if already there
+ */
+ apply: function (o, o2) {
+ for (const name in o) {
+ if (o.hasOwnProperty(name)) {
+ o2[name] = o[name];
+ }
+ }
+ return o2;
+ },
+
+ /**
+ Add non-null/defined properties of o to o2
+ @private
+ */
+ apply2: function (o, o2) {
+ for (const name in o) {
+ if (o.hasOwnProperty(name)) {
+ if (o[name] !== undefined && o[name] !== null) {
+ o2[name] = o[name];
+ }
+ }
+ }
+ return o2;
+ },
+
+ /**
+ Add properties of o to o2 where undefined or null on o2
+ @private
+ */
+ applyIf: function (o, o2) {
+ for (const name in o) {
+ if (o.hasOwnProperty(name)) {
+ if (o2[name] === undefined || o2[name] === null) {
+ o2[name] = o[name];
+ }
+ }
+ }
+ return o2;
+ },
+
+ /**
+ Returns true if the given map is empty.
+ @param obj
+ @returns {boolean}
+ @private
+ */
+ isEmptyObject: function (obj) {
+ for (const name in obj) {
+ if (obj.hasOwnProperty(name)) {
+ return false;
+ }
+ }
+ return true;
+ },
+
+ /**
+ Returns the given ID as a string, in quotes if the ID was a string to begin with.
+
+ This is useful for logging IDs.
+
+ @param {Number| String} id The ID
+ @returns {String}
+ @private
+ */
+ inQuotes: function (id) {
+ return utils._isNumeric(id) ? (`${id}`) : (`'${id}'`);
+ },
+
+ /**
+ Returns the concatenation of two typed arrays.
+ @param a
+ @param b
+ @returns {*|a}
+ @private
+ */
+ concat: function (a, b) {
+ const c = new a.constructor(a.length + b.length);
+ c.set(a);
+ c.set(b, a.length);
+ return c;
+ },
+};
+
+export {utils};
+
diff --git a/src/_utils/map.js b/src/utils/map.js
similarity index 62%
rename from src/_utils/map.js
rename to src/utils/map.js
index 89e4dec63..4c198b9f1 100644
--- a/src/_utils/map.js
+++ b/src/utils/map.js
@@ -1,15 +1,9 @@
-xeogl.utils = xeogl.utils || {};
+class Map {
-/**
- * Generic map of IDs to items - can generate own IDs or accept given IDs. IDs should be strings in order to not
- * clash with internally generated IDs, which are numbers.
- */
-xeogl.utils.Map = function (items, baseId) {
-
- this.items = items || [];
-
- baseId = baseId || 0;
- let lastUniqueId = baseId + 1;
+ constructor(items, baseId) {
+ this.items = items || [];
+ this._lastUniqueId = (baseId || 0) + 1;
+ }
/**
* Usage:
@@ -17,7 +11,7 @@ xeogl.utils.Map = function (items, baseId) {
* id = myMap.addItem("foo") // ID internally generated
* id = myMap.addItem("foo", "bar") // ID is "foo"
*/
- this.addItem = function () {
+ addItem() {
let item;
if (arguments.length === 2) {
const id = arguments[0];
@@ -31,18 +25,20 @@ xeogl.utils.Map = function (items, baseId) {
} else {
item = arguments[0] || {};
while (true) {
- const findId = lastUniqueId++;
+ const findId = this._lastUniqueId++;
if (!this.items[findId]) {
this.items[findId] = item;
return findId;
}
}
}
- };
+ }
- this.removeItem = function (id) {
+ removeItem(id) {
const item = this.items[id];
delete this.items[id];
return item;
- };
-};
+ }
+}
+
+export {Map};
diff --git a/src/utils/queue.js b/src/utils/queue.js
new file mode 100644
index 000000000..eb390ca7e
--- /dev/null
+++ b/src/utils/queue.js
@@ -0,0 +1,51 @@
+// Fast queue that avoids using potentially inefficient array .shift() calls
+// Based on https://github.com/creationix/fastqueue
+
+class Queue {
+
+ constructor() {
+
+ this._head = [];
+ this._headLength = 0;
+ this._tail = [];
+ this._index = 0;
+ this._length = 0;
+ }
+
+ shift() {
+ if (this._index >= this._headLength) {
+ const t = this._head;
+ t.length = 0;
+ this._head = this._tail;
+ this._tail = t;
+ this._index = 0;
+ this._headLength = this._head.length;
+ if (!this._headLength) {
+ return;
+ }
+ }
+ const value = this._head[this._index];
+ if (this._index < 0) {
+ delete this._head[this._index++];
+ }
+ else {
+ this._head[this._index++] = undefined;
+ }
+ this._length--;
+ return value;
+ }
+
+ push(item) {
+ this._length++;
+ this._tail.push(item);
+ return this;
+ };
+
+ unshift(item) {
+ this._head[--this._index] = item;
+ this._length++;
+ return this;
+ }
+}
+
+export {Queue};
\ No newline at end of file
diff --git a/src/viewport/viewport.js b/src/viewport/viewport.js
index 531599e24..261c475a3 100644
--- a/src/viewport/viewport.js
+++ b/src/viewport/viewport.js
@@ -53,8 +53,7 @@
@module xeogl
@submodule rendering
@constructor
- @param [scene] {Scene} Parent {{#crossLink "Scene"}}{{/crossLink}}, creates this Viewport within the
- default {{#crossLink "Scene"}}{{/crossLink}} when omitted.
+ @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] {*} Viewport configuration
@param [cfg.id] {String} Optional ID, unique among all components in the parent
{{#crossLink "Scene"}}Scene{{/crossLink}}, generated automatically when omitted.
@@ -67,146 +66,156 @@
@extends Component
*/
-(function () {
+import {core} from "./../core.js";
+import {Component} from '../component.js';
+import {State} from '../renderer/state.js';
- "use strict";
+class Viewport extends Component {
- xeogl.Viewport = xeogl.Component.extend({
+ /**
+ JavaScript class name for this Component.
- type: "xeogl.Viewport",
+ For example: "xeogl.AmbientLight", "xeogl.ColorTarget", "xeogl.Lights" etc.
- _init: function (cfg) {
+ @property type
+ @type String
+ @final
+ */
+ static get type() {
+ return "xeogl.Viewport";
+ }
- this._state = new xeogl.renderer.State({
- boundary: [0, 0, 100, 100]
- });
+ init(cfg) {
- this.boundary = cfg.boundary;
- this.autoBoundary = cfg.autoBoundary;
- },
+ super.init(cfg);
- _props: {
+ this._state = new State({
+ boundary: [0, 0, 100, 100]
+ });
- /**
- The canvas-space boundary of this Viewport, indicated as [min, max, width, height].
+ this.boundary = cfg.boundary;
+ this.autoBoundary = cfg.autoBoundary;
+ }
- Defaults to the size of the parent
- {{#crossLink "Scene"}}Scene's{{/crossLink}} {{#crossLink "Canvas"}}{{/crossLink}}.
- Ignores attempts to set value when {{#crossLink "Viewport/autoBoundary:property"}}{{/crossLink}} is ````true````.
+ /**
+ The canvas-space boundary of this Viewport, indicated as [min, max, width, height].
- Fires a {{#crossLink "Viewport/boundary:event"}}{{/crossLink}} event on change.
+ Defaults to the size of the parent
+ {{#crossLink "Scene"}}Scene's{{/crossLink}} {{#crossLink "Canvas"}}{{/crossLink}}.
- @property boundary
- @default [size of Scene Canvas]
- @type {Array of Number}
- */
- boundary: {
+ Ignores attempts to set value when {{#crossLink "Viewport/autoBoundary:property"}}{{/crossLink}} is ````true````.
- set: function (value) {
+ Fires a {{#crossLink "Viewport/boundary:event"}}{{/crossLink}} event on change.
- if (this._autoBoundary) {
- return;
- }
+ @property boundary
+ @default [size of Scene Canvas]
+ @type {Array of Number}
+ */
+ set boundary(value) {
- if (!value) {
+ if (this._autoBoundary) {
+ return;
+ }
- const canvasBoundary = this.scene.canvas.boundary;
+ if (!value) {
- const width = canvasBoundary[2];
- const height = canvasBoundary[3];
+ const canvasBoundary = this.scene.canvas.boundary;
- value = [0, 0, width, height];
- }
+ const width = canvasBoundary[2];
+ const height = canvasBoundary[3];
- this._state.boundary = value;
+ value = [0, 0, width, height];
+ }
- this._renderer.imageDirty();
+ this._state.boundary = value;
- /**
- Fired whenever this Viewport's {{#crossLink "Viewport/boundary:property"}}{{/crossLink}} property changes.
+ this._renderer.imageDirty();
- @event boundary
- @param value {Boolean} The property's new value
- */
- this.fire("boundary", this._state.boundary);
- },
+ /**
+ Fired whenever this Viewport's {{#crossLink "Viewport/boundary:property"}}{{/crossLink}} property changes.
- get: function () {
- return this._state.boundary;
- }
- },
+ @event boundary
+ @param value {Boolean} The property's new value
+ */
+ this.fire("boundary", this._state.boundary);
+ }
- /**
- Indicates whether this Viewport's {{#crossLink "Viewport/boundary:property"}}{{/crossLink}} automatically
- synchronizes with the size of the parent {{#crossLink "Scene"}}Scene's{{/crossLink}} {{#crossLink "Canvas"}}{{/crossLink}}.
+ get boundary() {
+ return this._state.boundary;
+ }
- When set true, then this Viewport will fire a {{#crossLink "Viewport/boundary/event"}}{{/crossLink}} whenever
- the {{#crossLink "Canvas"}}{{/crossLink}} resizes. Also fires that event as soon as this ````autoBoundary````
- property is changed.
+ /**
+ Indicates whether this Viewport's {{#crossLink "Viewport/boundary:property"}}{{/crossLink}} automatically
+ synchronizes with the size of the parent {{#crossLink "Scene"}}Scene's{{/crossLink}} {{#crossLink "Canvas"}}{{/crossLink}}.
- Fires a {{#crossLink "Viewport/autoBoundary:event"}}{{/crossLink}} event on change.
+ When set true, then this Viewport will fire a {{#crossLink "Viewport/boundary/event"}}{{/crossLink}} whenever
+ the {{#crossLink "Canvas"}}{{/crossLink}} resizes. Also fires that event as soon as this ````autoBoundary````
+ property is changed.
- @property autoBoundary
- @default false
- @type Boolean
- */
- autoBoundary: {
+ Fires a {{#crossLink "Viewport/autoBoundary:event"}}{{/crossLink}} event on change.
- set: function (value) {
+ @property autoBoundary
+ @default false
+ @type Boolean
+ */
+ set autoBoundary(value) {
- value = !!value;
+ value = !!value;
+
+ if (value === this._autoBoundary) {
+ return;
+ }
- if (value === this._autoBoundary) {
- return;
- }
+ this._autoBoundary = value;
- this._autoBoundary = value;
+ if (this._autoBoundary) {
+ this._onCanvasSize = this.scene.canvas.on("boundary",
+ function (boundary) {
- if (this._autoBoundary) {
- this._onCanvasSize = this.scene.canvas.on("boundary",
- function (boundary) {
+ const width = boundary[2];
+ const height = boundary[3];
- const width = boundary[2];
- const height = boundary[3];
+ this._state.boundary = [0, 0, width, height];
- this._state.boundary = [0, 0, width, height];
+ this._renderer.imageDirty();
- this._renderer.imageDirty();
+ /**
+ Fired whenever this Viewport's {{#crossLink "Viewport/boundary:property"}}{{/crossLink}} property changes.
- /**
- Fired whenever this Viewport's {{#crossLink "Viewport/boundary:property"}}{{/crossLink}} property changes.
+ @event boundary
+ @param value {Boolean} The property's new value
+ */
+ this.fire("boundary", this._state.boundary);
- @event boundary
- @param value {Boolean} The property's new value
- */
- this.fire("boundary", this._state.boundary);
+ }, this);
- }, this);
+ } else if (this._onCanvasSize) {
+ this.scene.canvas.off(this._onCanvasSize);
+ this._onCanvasSize = null;
+ }
- } else if (this._onCanvasSize) {
- this.scene.canvas.off(this._onCanvasSize);
- this._onCanvasSize = null;
- }
+ /**
+ Fired whenever this Viewport's {{#crossLink "autoBoundary/autoBoundary:property"}}{{/crossLink}} property changes.
- /**
- Fired whenever this Viewport's {{#crossLink "autoBoundary/autoBoundary:property"}}{{/crossLink}} property changes.
+ @event autoBoundary
+ @param value The property's new value
+ */
+ this.fire("autoBoundary", this._autoBoundary);
+ }
- @event autoBoundary
- @param value The property's new value
- */
- this.fire("autoBoundary", this._autoBoundary);
- },
+ get autoBoundary() {
+ return this._autoBoundary;
+ }
- get: function () {
- return this._autoBoundary;
- }
- }
- },
+ _getState() {
+ return this._state;
+ }
- _getState: function () {
- return this._state;
- }
- });
+ destroy() {
+ super.destroy();
+ this._state.destroy();
+ }
+}
-})();
+export{Viewport};
\ No newline at end of file
diff --git a/src/webglInfo.js b/src/webglInfo.js
new file mode 100644
index 000000000..93752a20b
--- /dev/null
+++ b/src/webglInfo.js
@@ -0,0 +1,43 @@
+const WEBGL_INFO = {
+ WEBGL: false,
+ SUPPORTED_EXTENSIONS: {}
+};
+
+const canvas = document.createElement("canvas");
+
+if (canvas) {
+
+ const gl = canvas.getContext("webgl", {antialias: true}) || canvas.getContext("experimental-webgl", {antialias: true});
+
+ WEBGL_INFO.WEBGL = !!gl;
+
+ if (WEBGL_INFO.WEBGL) {
+ WEBGL_INFO.ANTIALIAS = gl.getContextAttributes().antialias;
+ if (gl.getShaderPrecisionFormat) {
+ if (gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT).precision > 0) {
+ WEBGL_INFO.FS_MAX_FLOAT_PRECISION = "highp";
+ } else if (gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT).precision > 0) {
+ WEBGL_INFO.FS_MAX_FLOAT_PRECISION = "mediump";
+ } else {
+ WEBGL_INFO.FS_MAX_FLOAT_PRECISION = "lowp";
+ }
+ } else {
+ WEBGL_INFO.FS_MAX_FLOAT_PRECISION = "mediump";
+ }
+ WEBGL_INFO.DEPTH_BUFFER_BITS = gl.getParameter(gl.DEPTH_BITS);
+ WEBGL_INFO.MAX_TEXTURE_SIZE = gl.getParameter(gl.MAX_TEXTURE_SIZE);
+ WEBGL_INFO.MAX_CUBE_MAP_SIZE = gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE);
+ WEBGL_INFO.MAX_RENDERBUFFER_SIZE = gl.getParameter(gl.MAX_RENDERBUFFER_SIZE);
+ WEBGL_INFO.MAX_TEXTURE_UNITS = gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS);
+ WEBGL_INFO.MAX_TEXTURE_IMAGE_UNITS = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ WEBGL_INFO.MAX_VERTEX_ATTRIBS = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
+ WEBGL_INFO.MAX_VERTEX_UNIFORM_VECTORS = gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS);
+ WEBGL_INFO.MAX_FRAGMENT_UNIFORM_VECTORS = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS);
+ WEBGL_INFO.MAX_VARYING_VECTORS = gl.getParameter(gl.MAX_VARYING_VECTORS);
+ gl.getSupportedExtensions().forEach(function (ext) {
+ WEBGL_INFO.SUPPORTED_EXTENSIONS[ext] = true;
+ });
+ }
+}
+
+export {WEBGL_INFO};
\ No newline at end of file
diff --git a/src/xeogl.js b/src/xeogl.js
index a19a229dc..53b59a306 100644
--- a/src/xeogl.js
+++ b/src/xeogl.js
@@ -6,727 +6,99 @@
@static
@author xeolabs / http://xeolabs.com/
*/
-(function () {
- "use strict";
- // Fast queue that avoids using potentially inefficient array .shift() calls
- // Based on https://github.com/creationix/fastqueue
- const Queue = function () {
-
- let head = [];
- let headLength = 0;
- let tail = [];
- let index = 0;
- this.length = 0;
-
- this.shift = function () {
- if (index >= headLength) {
- const t = head;
- t.length = 0;
- head = tail;
- tail = t;
- index = 0;
- headLength = head.length;
- if (!headLength) {
- return;
- }
- }
- const value = head[index];
- if (index < 0) {
- delete head[index++];
- }
- else {
- head[index++] = undefined;
- }
- this.length--;
- return value;
- };
-
- this.push = function (item) {
- this.length++;
- tail.push(item);
- return this;
- };
-
- this.unshift = function (item) {
- head[--index] = item;
- this.length++;
- return this;
- };
- };
-
- const xeogl = function () {
-
- this._debug = {
- forceHighShaderPrecision: false
- };
-
- /**
- * Semantic version number. The value for this is set by an expression that's concatenated to
- * the end of the built binary by the xeogl build script.
- * @property version
- * @namespace xeogl
- * @type {String}
- */
- this.version = null;
-
- /**
- * Information about available WebGL support
- */
- this.WEBGL_INFO = (function () {
- const info = {
- WEBGL: false
- };
-
- const canvas = document.createElement("canvas");
-
- if (!canvas) {
- return info;
- }
-
- const gl = canvas.getContext("webgl", {antialias: true}) || canvas.getContext("experimental-webgl", {antialias: true});
-
- info.WEBGL = !!gl;
-
- if (!info.WEBGL) {
- return info;
- }
-
- info.ANTIALIAS = gl.getContextAttributes().antialias;
-
- if (gl.getShaderPrecisionFormat) {
- if (gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT).precision > 0) {
- info.FS_MAX_FLOAT_PRECISION = "highp";
- } else if (gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT).precision > 0) {
- info.FS_MAX_FLOAT_PRECISION = "mediump";
- } else {
- info.FS_MAX_FLOAT_PRECISION = "lowp";
- }
- } else {
- info.FS_MAX_FLOAT_PRECISION = "mediump";
- }
-
- info.DEPTH_BUFFER_BITS = gl.getParameter(gl.DEPTH_BITS);
- info.MAX_TEXTURE_SIZE = gl.getParameter(gl.MAX_TEXTURE_SIZE);
- info.MAX_CUBE_MAP_SIZE = gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE);
- info.MAX_RENDERBUFFER_SIZE = gl.getParameter(gl.MAX_RENDERBUFFER_SIZE);
- info.MAX_TEXTURE_UNITS = gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS);
- info.MAX_TEXTURE_IMAGE_UNITS = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
- info.MAX_VERTEX_ATTRIBS = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
- info.MAX_VERTEX_UNIFORM_VECTORS = gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS);
- info.MAX_FRAGMENT_UNIFORM_VECTORS = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS);
- info.MAX_VARYING_VECTORS = gl.getParameter(gl.MAX_VARYING_VECTORS);
-
- info.SUPPORTED_EXTENSIONS = {};
-
- gl.getSupportedExtensions().forEach(function (ext) {
- info.SUPPORTED_EXTENSIONS[ext] = true;
- });
-
- return info;
- })();
-
- /**
- * Tracks statistics within xeogl, such as numbers of
- * scenes, textures, geometries etc.
- * @final
- * @property stats
- * @type {*}
- */
- this.stats = {
- build: {
- version: xeogl.version
- },
- client: {
- browser: (navigator && navigator.userAgent) ? navigator.userAgent : "n/a"
- },
-
- // TODO: replace 'canvas' with 'pixels'
- //canvas: {
- // width: 0,
- // height: 0
- //},
- components: {
- scenes: 0,
- models: 0,
- meshes: 0,
- objects: 0
- },
- memory: {
-
- // Note that these counts will include any positions, colors,
- // normals and indices that xeogl internally creates on-demand
- // to support color-index triangle picking.
-
- meshes: 0,
- positions: 0,
- colors: 0,
- normals: 0,
- uvs: 0,
- indices: 0,
- textures: 0,
- transforms: 0,
- materials: 0,
- programs: 0
- },
- frame: {
- frameCount: 0,
- fps: 0,
- useProgram: 0,
- bindTexture: 0,
- bindArray: 0,
- drawElements: 0,
- drawArrays: 0,
- tasksRun: 0,
- tasksScheduled: 0
- }
- };
-
- // Ensures unique scene IDs
- // Lazy-instantiated because its class is on the
- // namespace of this object, and so won't be defined yet
- this._sceneIDMap = null;
-
- // Default singleton Scene, lazy-initialized in getter
- this._scene = null;
-
- /**
- * Existing {{#crossLink "Scene"}}Scene{{/crossLink}}s , mapped to their IDs
- * @property scenes
- * @namespace xeogl
- * @type {{String:xeogl.Scene}}
- */
- this.scenes = {};
-
- // Used for throttling FPS for each Scene
- this._scenesRenderInfo = {};
-
- /**
- * For each component type, a list of its supertypes, ordered upwards in the hierarchy.
- * @type {{}}
- * @private
- */
- this._superTypes = {};
-
- // Task queue, which is pumped on each frame;
- // tasks are pushed to it with calls to xeogl.schedule
-
- this._taskQueue = new Queue();
-
- //-----------------------------------------------------------------------
- // Game loop
- //
- // https://developer.mozilla.org/en-US/docs/Games/Anatomy
- //
- // http://gameprogrammingpatterns.com/game-loop.html
- //-----------------------------------------------------------------------
-
- const self = this;
-
- (function () {
-
- const tickEvent = {
- sceneId: null,
- time: null,
- startTime: null,
- prevTime: null,
- deltaTime: null
- };
-
- // Hoisted vars
-
- const taskBudget = 10; // Millisecs we're allowed to spend on tasks in each frame
- let frameTime;
- let lastFrameTime = 0;
- let elapsedFrameTime;
- let newFPS;
- const fpsSamples = [];
- const numFPSSamples = 30;
- let totalFPS = 0;
- let updateTime;
- let id;
- let scene;
-
- const frame = function () {
-
- frameTime = Date.now();
-
- // Moving average of FPS
-
- if (lastFrameTime > 0) {
- elapsedFrameTime = frameTime - lastFrameTime;
- newFPS = 1000 / elapsedFrameTime;
- totalFPS += newFPS;
- fpsSamples.push(newFPS);
- if (fpsSamples.length >= numFPSSamples) {
- totalFPS -= fpsSamples.shift();
- }
- self.stats.frame.fps = Math.round(totalFPS / fpsSamples.length);
-
- }
-
- update();
-
- render();
-
- lastFrameTime = frameTime;
-
- window.requestAnimationFrame(frame);
- };
-
-
- function update() {
-
- updateTime = Date.now();
-
- // Process as many enqueued tasks as we can
- // within the per-frame task budget
-
- const tasksRun = self._runScheduledTasks(updateTime + taskBudget);
- const tasksScheduled = self._taskQueue.length;
-
- self.stats.frame.tasksRun = tasksRun;
- self.stats.frame.tasksScheduled = tasksScheduled;
- self.stats.frame.tasksBudget = taskBudget;
-
- tickEvent.time = updateTime;
-
- // Fire a "tick" event at the scene, which will in turn cause
- // all sorts of scene components to schedule more tasks
-
- for (id in self.scenes) {
- if (self.scenes.hasOwnProperty(id)) {
-
- scene = self.scenes[id];
-
- // Fire the tick event at the scene
-
- tickEvent.sceneId = id;
- tickEvent.startTime = scene.startTime;
- tickEvent.deltaTime = tickEvent.prevTime != null ? tickEvent.time - tickEvent.prevTime : 0;
-
- /**
- * Fired on each game loop iteration.
- *
- * @event tick
- * @param {String} sceneID The ID of this Scene.
- * @param {Number} startTime The time in seconds since 1970 that this Scene was instantiated.
- * @param {Number} time The time in seconds since 1970 of this "tick" event.
- * @param {Number} prevTime The time of the previous "tick" event from this Scene.
- * @param {Number} deltaTime The time in seconds since the previous "tick" event from this Scene.
- */
- scene.fire("tick", tickEvent, true);
- }
- }
-
- tickEvent.prevTime = updateTime;
- }
-
- function render() {
-
- const scenes = self.scenes;
- const scenesRenderInfo = self._scenesRenderInfo;
- let scene;
- let renderInfo;
- let ticksPerRender;
-
- const forceRender = false;
- for (id in scenes) {
- if (scenes.hasOwnProperty(id)) {
-
- scene = scenes[id];
- renderInfo = scenesRenderInfo[id];
-
- ticksPerRender = scene.ticksPerRender;
-
- if (renderInfo.ticksPerRender !== ticksPerRender) {
- renderInfo.ticksPerRender = ticksPerRender;
- renderInfo.renderCountdown = ticksPerRender;
- }
-
- if (--renderInfo.renderCountdown === 0) {
- scene.render(forceRender);
- renderInfo.renderCountdown = ticksPerRender;
- }
- }
- }
- }
-
- window.requestAnimationFrame(frame);
-
- })();
- };
-
- xeogl.prototype = {
-
- constructor: xeogl,
-
- /**
- The default {{#crossLink "Scene"}}Scene{{/crossLink}}.
-
- Components created without an explicit parent {{#crossLink "Scene"}}Scene{{/crossLink}} will be created within this
- {{#crossLink "Scene"}}Scene{{/crossLink}} by default.
-
- xeogl creates the default {{#crossLink "Scene"}}Scene{{/crossLink}} as soon as you either
- reference this property for the first time, or create your first {{#crossLink "Mesh"}}Mesh{{/crossLink}} without
- a specified {{#crossLink "Scene"}}Scene{{/crossLink}}.
-
- @property scene
- @namespace xeogl
- @final
- @type Scene
- */
- get scene() {
-
- // xeogl.Scene constructor will call this._addScene
- // to register itself on xeogl
-
- return this._scene || (this._scene = new window.xeogl.Scene({
- id: "default.scene"
- }));
- },
-
- set scene(value) {
- this._scene = value;
- },
-
- /**
- * Registers a scene on xeogl.
- * This is called within the xeogl.Scene constructor.
- *
- * @method _addScene
- * @param {Scene} scene The scene
- * @private
- */
- _addScene: function (scene) {
-
- this._sceneIDMap = this._sceneIDMap || new window.xeogl.utils.Map();
-
- if (scene.id) {
-
- // User-supplied ID
-
- if (this.scenes[scene.id]) {
- console.error("[ERROR] Scene " + xeogl._inQuotes(scene.id) + " already exists");
- return;
- }
-
- } else {
-
- // Auto-generated ID
-
- scene.id = this._sceneIDMap.addItem({});
- }
-
- this.scenes[scene.id] = scene;
-
- const ticksPerRender = scene.ticksPerRender;
-
- this._scenesRenderInfo[scene.id] = {
- ticksPerRender: ticksPerRender,
- renderCountdown: ticksPerRender
- };
-
- this.stats.components.scenes++;
-
- const self = this;
-
- // Unregister destroyed scenes
-
- scene.on("destroyed", function () {
-
- self._sceneIDMap.removeItem(scene.id);
-
- delete self.scenes[scene.id];
- delete self._scenesRenderInfo[scene.id];
-
- self.stats.components.scenes--;
- });
- },
-
- /**
- * Schedule a task for xeogl to run at the next frame.
- *
- * Internally, this pushes the task to a FIFO queue. Within each frame interval, xeogl processes the queue
- * for a certain period of time, popping tasks and running them. After each frame interval, tasks that did not
- * get a chance to run during the task are left in the queue to be run next time.
- *
- * @method scheduleTask
- * @param {Function} callback Callback that runs the task.
- * @param {Object} [scope] Scope for the callback.
- */
- scheduleTask: function (callback, scope) {
- this._taskQueue.push(callback);
- this._taskQueue.push(scope);
- },
-
- deferTask: function (callback, scope) {
- if (scope) {
- callback.call(scope);
- } else {
- callback();
- }
- },
-
- // Pops and propcesses tasks in the queue, until the
- // given number of milliseconds has elapsed.
- _runScheduledTasks: function (until) {
-
- let time = (new Date()).getTime();
- const taskQueue = this._taskQueue;
- let callback;
- let scope;
- let tasksRun = 0;
-
- while (taskQueue.length > 0 && time < until) {
- callback = taskQueue.shift();
- scope = taskQueue.shift();
- if (scope) {
- callback.call(scope);
- } else {
- callback();
- }
- time = (new Date()).getTime();
- tasksRun++;
- }
-
- return tasksRun;
- },
-
- /**
- * Destroys all user-created {{#crossLink "Scene"}}Scenes{{/crossLink}} and
- * clears the default {{#crossLink "Scene"}}Scene{{/crossLink}}.
- *
- * @method clear
- * @demo foo
- */
- clear: function () {
-
- let scene;
-
- for (const id in this.scenes) {
- if (this.scenes.hasOwnProperty(id)) {
-
- scene = this.scenes[id];
-
- // Only clear the default Scene
- // but destroy all the others
-
- if (id === "default.scene") {
- scene.clear();
- } else {
- scene.destroy();
- }
- }
- }
- this.scenes = {};
- },
-
- /**
- * Tests if the given object is an array
- * @private
- */
- _isArray: function (testMesh) {
- return testMesh && !(testMesh.propertyIsEnumerable('length')) && typeof testMesh === 'object' && typeof testMesh.length === 'number';
- },
-
- /**
- * Tests if the given value is a string
- * @param value
- * @returns {boolean}
- * @private
- */
- _isString: function (value) {
- return (typeof value === 'string' || value instanceof String);
- },
-
- /**
- * Tests if the given value is a number
- * @param value
- * @returns {boolean}
- * @private
- */
- _isNumeric: function (value) {
- return !isNaN(parseFloat(value)) && isFinite(value);
- },
-
- /**
- * Tests if the given value is an ID
- * @param value
- * @returns {boolean}
- * @private
- */
- _isID: function (value) {
- return xeogl.prototype._isString(value) || xeogl.prototype._isNumeric(value);
- },
-
- /**
- * Tests if the given components are the same, where the components can be either IDs or instances.
- * @param c1
- * @param c2
- * @returns {boolean}
- * @private
- */
- _isSameComponent: function (c1, c2) {
-
- if (!c1 || !c2) {
- return false;
- }
-
- const id1 = (xeogl.prototype._isNumeric(c1) || xeogl.prototype._isString(c1)) ? "" + c1 : c1.id;
- const id2 = (xeogl.prototype._isNumeric(c2) || xeogl.prototype._isString(c2)) ? "" + c2 : c2.id;
-
- return id1 === id2;
- },
-
- /**
- * Tests if the given value is a function
- * @param value
- * @returns {boolean}
- * @private
- */
- _isFunction: function (value) {
- return (typeof value === "function");
- },
-
- /**
- * Tests if the given value is a JavaScript JSON object, eg, ````{ foo: "bar" }````.
- * @param value
- * @returns {boolean}
- * @private
- */
- _isObject: (function () {
- const objectConstructor = {}.constructor;
- return function (value) {
- return (!!value && value.constructor === objectConstructor);
- };
- })(),
-
- /**
- * Tests if the given component type is a subtype of another component supertype.
- * @param {String} type
- * @param {String} [superType="xeogl.Component"]
- * @returns {boolean}
- * @private
- */
- _isComponentType: function (type, superType) {
-
- superType = superType || "xeogl.Component";
-
- if (type === superType) {
- return true;
- }
-
- const superTypes = this._superTypes[type];
-
- if (!superTypes) {
- return false;
- }
-
- for (let i = superTypes.length - 1; i >= 0; i--) {
- if (superTypes[i] === superType) {
- return true;
- }
- }
-
- return false;
- },
-
- /** Returns a shallow copy
- */
- _copy: function (o) {
- return this._apply(o, {});
- },
-
- /** Add properties of o to o2, overwriting them on o2 if already there
- */
- _apply: function (o, o2) {
- for (const name in o) {
- if (o.hasOwnProperty(name)) {
- o2[name] = o[name];
- }
- }
- return o2;
- },
-
- /**
- * Add non-null/defined properties of o to o2
- * @private
- */
- _apply2: function (o, o2) {
- for (const name in o) {
- if (o.hasOwnProperty(name)) {
- if (o[name] !== undefined && o[name] !== null) {
- o2[name] = o[name];
- }
- }
- }
- return o2;
- },
-
- /**
- * Add properties of o to o2 where undefined or null on o2
- * @private
- */
- _applyIf: function (o, o2) {
- for (const name in o) {
- if (o.hasOwnProperty(name)) {
- if (o2[name] === undefined || o2[name] === null) {
- o2[name] = o[name];
- }
- }
- }
- return o2;
- },
-
- /**
- * Returns true if the given map is empty.
- * @param obj
- * @returns {boolean}
- * @private
- */
- _isEmptyObject: function (obj) {
- for (const name in obj) {
- if (obj.hasOwnProperty(name)) {
- return false;
- }
- }
- return true;
- },
-
- /**
- * Returns the given ID as a string, in quotes if the ID was a string to begin with.
- *
- * This is useful for logging IDs.
- *
- * @param {Number| String} id The ID
- * @returns {String}
- * @private
- */
- _inQuotes: function (id) {
- return this._isNumeric(id) ? ("" + id) : ("'" + id + "'");
- },
-
- /**
- * Returns the concatenation of two typed arrays.
- * @param a
- * @param b
- * @returns {*|a}
- * @private
- */
- _concat: function (a, b) {
- const c = new a.constructor(a.length + b.length);
- c.set(a);
- c.set(b, a.length);
- return c;
- }
- };
-
- // Have a lower-case xeogl namespace as well,
- // just because it's easier to type when live-coding
-
- window.xeogl = window.xeogl = new xeogl();
-
-})
-();
+import {core} from "./core.js";
+export {WEBGL_INFO} from "./webglInfo.js";
+export {stats} from "./stats.js";
+export {math} from "./math/math.js";
+// export {Component} from "./component.js";
+// export {CameraFlightAnimation} from './animation/cameraFlightAnimation.js';
+// export {Canvas} from "./canvas/canvas.js";
+// export {Spinner} from "./canvas/spinner.js";
+// export {Clip} from "./clipping/clip.js";
+// export {CameraControl} from "./controls/cameraControl.js";
+// export {Geometry} from "./geometry/geometry.js";
+// export {BoxGeometry} from "./geometry/boxGeometry.js";
+// export {TorusGeometry} from "./geometry/torusGeometry.js";
+// export {SphereGeometry} from "./geometry/sphereGeometry.js";
+// export {OBBGeometry} from "./geometry/obbGeometry.js";
+// export {AABBGeometry} from "./geometry/aabbGeometry.js";
+// // export {PathGeometry} from "./geometry/pathGeometry.js";
+// export {CylinderGeometry} from "./geometry/cylinderGeometry.js";
+// export {PlaneGeometry} from "./geometry/planeGeometry.js";
+// export {Input} from "./input/input.js";
+// export {AmbientLight} from "./lighting/ambientLight.js";
+// export {DirLight} from "./lighting/dirLight.js";
+// export {PointLight} from "./lighting/pointLight.js";
+// export {SpotLight} from "./lighting/spotLight.js";
+// export {CubeTexture} from "./lighting/cubeTexture.js";
+// export {LightMap} from "./lighting/lightMap.js";
+// export {ReflectionMap} from "./lighting/reflectionMap.js";
+// export {Shadow} from "./lighting/shadow.js";
+// export {Model} from "./models/model.js";
+// export {Material} from "./materials/material.js";
+// export {PhongMaterial} from "./materials/phongMaterial.js";
+// export {LambertMaterial} from "./materials/lambertMaterial.js";
+// export {SpecularMaterial} from "./materials/specularMaterial.js";
+// export {MetallicMaterial} from "./materials/metallicMaterial.js";
+// export {EmphasisMaterial} from "./materials/emphasisMaterial.js";
+// export {EdgeMaterial} from "./materials/edgeMaterial.js";
+// export {OutlineMaterial} from "./materials/outlineMaterial.js";
+// export {Texture} from "./materials/texture.js";
+// export {Fresnel} from "./materials/fresnel.js";
+// export {Viewport} from "./viewport/viewport.js";
+// export {Camera} from "./camera/camera.js";
+// export {Frustum} from "./camera/frustum.js";
+// export {Ortho} from "./camera/ortho.js";
+// export {Perspective} from "./camera/perspective.js";
+// export {CustomProjection} from "./camera/customProjection.js"
+// export {Scene} from "./scene.js";
+
+//
+// xeogl.WEBGL_INFO = WEBGL_INFO;
+// xeogl.stats = stats;
+// xeogl.math = math;
+// xeogl.Component = Component;
+// xeogl.CameraFlightAnimation = CameraFlightAnimation;
+// // xeogl.Canvas = Canvas;
+// // xeogl.Spinner = Spinner;
+// // xeogl.Clip = Clip;
+// // xeogl.CameraControl = CameraControl;
+// // xeogl.Geometry = Geometry;
+// // xeogl.BoxGeometry = BoxGeometry;
+// // xeogl.TorusGeometry = TorusGeometry;
+// // xeogl.SphereGeometry = SphereGeometry;
+// // xeogl.OBBGeometry = OBBGeometry;
+// // xeogl.AABBGeometry = AABBGeometry;
+// // xeogl.CylinderGeometry = CylinderGeometry;
+// // xeogl.PlaneGeometry = PlaneGeometry;
+// // xeogl.Input = Input;
+// // xeogl.AmbientLight = AmbientLight;
+// // xeogl.DirLight = DirLight;
+// // xeogl.PointLight = PointLight;
+// // xeogl.SpotLight = SpotLight;
+// // xeogl.CubeTexture = CubeTexture;
+// // xeogl.LightMap = LightMap;
+// // xeogl.ReflectionMap = ReflectionMap;
+// // xeogl.Shadow = Shadow;
+// // xeogl.Model = Model;
+// // xeogl.Material = Material;
+// // xeogl.PhongMaterial = PhongMaterial;
+// // xeogl.MetallicMaterial = MetallicMaterial;
+// // xeogl.SpecularMaterial = SpecularMaterial;
+// // xeogl.LambertMaterial = LambertMaterial;
+// // xeogl.EmphasisMaterial = EmphasisMaterial;
+// // xeogl.EdgeMaterial = EdgeMaterial;
+// // xeogl.OutlineMaterial = OutlineMaterial;
+// // xeogl.Texture = Texture;
+// // xeogl.Fresnel = Fresnel;
+// // xeogl.Viewport = Viewport;
+// // xeogl.Camera = Camera;
+// // xeogl.Frustum = Frustum;
+// // xeogl.Ortho = Ortho;
+// // xeogl.Perspective = Perspective;
+// // xeogl.CustomProjection = CustomProjection;
+// // xeogl.Scene = Scene;
+//
+// export default xeogl;
diff --git a/website/index.html b/website/index.html
index 79dbe6d6c..eb97ef907 100644
--- a/website/index.html
+++ b/website/index.html
@@ -208,6 +208,7 @@ Features
3D engine:
- Uses WebGL for rendering
+ - Written in ECMAScript 6
- Component-based 3D scene representation
- No external dependencies; library and tool agnostic
- Optimized for high-detail visualization