From f4e52d7cfa606df2fe0f133a500adfc267080453 Mon Sep 17 00:00:00 2001 From: pissang Date: Wed, 8 Mar 2017 13:01:30 +0800 Subject: [PATCH] Dump 0.3.0 --- dist/qtek.amd.js | 29345 ----------------- dist/qtek.amd.min.js | 12 - dist/qtek.js | 64142 +++++++++++++++++++----------------- dist/qtek.min.js | 25 +- package.json | 2 +- src/picking/RayPicking.js | 17 +- tests/js/config.js | 12 +- 7 files changed, 34349 insertions(+), 59206 deletions(-) delete mode 100644 dist/qtek.amd.js delete mode 100644 dist/qtek.amd.min.js diff --git a/dist/qtek.amd.js b/dist/qtek.amd.js deleted file mode 100644 index 8a8dd3452..000000000 --- a/dist/qtek.amd.js +++ /dev/null @@ -1,29345 +0,0 @@ - -define('qtek/qtek.amd',[],function() { - return { - version: '0.2.1' - }; -}); -define('qtek', ['qtek/qtek.amd'], function (main) { return main; }); - -define('qtek/core/mixin/derive',['require'],function(require) { - - - - /** - * Extend a sub class from base class - * @param {object|Function} makeDefaultOpt default option of this sub class, method of the sub can use this.xxx to access this option - * @param {Function} [initialize] Initialize after the sub class is instantiated - * @param {Object} [proto] Prototype methods/properties of the sub class - * @memberOf qtek.core.mixin.derive. - * @return {Function} - */ - function derive(makeDefaultOpt, initialize/*optional*/, proto/*optional*/) { - - if (typeof initialize == 'object') { - proto = initialize; - initialize = null; - } - - var _super = this; - - var propList; - if (!(makeDefaultOpt instanceof Function)) { - // Optimize the property iterate if it have been fixed - propList = []; - for (var propName in makeDefaultOpt) { - if (makeDefaultOpt.hasOwnProperty(propName)) { - propList.push(propName); - } - } - } - - var sub = function(options) { - - // call super constructor - _super.apply(this, arguments); - - if (makeDefaultOpt instanceof Function) { - // Invoke makeDefaultOpt each time if it is a function, So we can make sure each - // property in the object will not be shared by mutiple instances - extend(this, makeDefaultOpt.call(this)); - } else { - extendWithPropList(this, makeDefaultOpt, propList); - } - - if (this.constructor === sub) { - // Initialize function will be called in the order of inherit - var initializers = sub.__initializers__; - for (var i = 0; i < initializers.length; i++) { - initializers[i].apply(this, arguments); - } - } - }; - // save super constructor - sub.__super__ = _super; - // Initialize function will be called after all the super constructor is called - if (!_super.__initializers__) { - sub.__initializers__ = []; - } else { - sub.__initializers__ = _super.__initializers__.slice(); - } - if (initialize) { - sub.__initializers__.push(initialize); - } - - var Ctor = function() {}; - Ctor.prototype = _super.prototype; - sub.prototype = new Ctor(); - sub.prototype.constructor = sub; - extend(sub.prototype, proto); - - // extend the derive method as a static method; - sub.derive = _super.derive; - - return sub; - } - - function extend(target, source) { - if (!source) { - return; - } - for (var name in source) { - if (source.hasOwnProperty(name)) { - target[name] = source[name]; - } - } - } - - function extendWithPropList(target, source, propList) { - for (var i = 0; i < propList.length; i++) { - var propName = propList[i]; - target[propName] = source[propName]; - } - } - - /** - * @alias qtek.core.mixin.derive - * @mixin - */ - return { - derive : derive - }; -}); -define('qtek/core/mixin/notifier',[],function() { - - function Handler(action, context) { - this.action = action; - this.context = context; - } - /** - * @mixin - * @alias qtek.core.mixin.notifier - */ - var notifier = { - /** - * Trigger event - * @param {string} name - */ - trigger: function(name) { - if (!this.hasOwnProperty('__handlers__')) { - return; - } - if (!this.__handlers__.hasOwnProperty(name)) { - return; - } - - var hdls = this.__handlers__[name]; - var l = hdls.length, i = -1, args = arguments; - // Optimize advise from backbone - switch (args.length) { - case 1: - while (++i < l) { - hdls[i].action.call(hdls[i].context); - } - return; - case 2: - while (++i < l) { - hdls[i].action.call(hdls[i].context, args[1]); - } - return; - case 3: - while (++i < l) { - hdls[i].action.call(hdls[i].context, args[1], args[2]); - } - return; - case 4: - while (++i < l) { - hdls[i].action.call(hdls[i].context, args[1], args[2], args[3]); - } - return; - case 5: - while (++i < l) { - hdls[i].action.call(hdls[i].context, args[1], args[2], args[3], args[4]); - } - return; - default: - while (++i < l) { - hdls[i].action.apply(hdls[i].context, Array.prototype.slice.call(args, 1)); - } - return; - } - }, - /** - * Register event handler - * @param {string} name - * @param {Function} action - * @param {Object} [context] - * @chainable - */ - on: function(name, action, context) { - if (!name || !action) { - return; - } - var handlers = this.__handlers__ || (this.__handlers__={}); - if (! handlers[name]) { - handlers[name] = []; - } else { - if (this.has(name, action)) { - return; - } - } - var handler = new Handler(action, context || this); - handlers[name].push(handler); - - return this; - }, - - /** - * Register event, event will only be triggered once and then removed - * @param {string} name - * @param {Function} action - * @param {Object} [context] - * @chainable - */ - once: function(name, action, context) { - if (!name || !action) { - return; - } - var self = this; - function wrapper() { - self.off(name, wrapper); - action.apply(this, arguments); - } - return this.on(name, wrapper, context); - }, - - /** - * Alias of once('before' + name) - * @param {string} name - * @param {Function} action - * @param {Object} [context] - * @chainable - */ - before: function(name, action, context) { - if (!name || !action) { - return; - } - name = 'before' + name; - return this.on(name, action, context); - }, - - /** - * Alias of once('after' + name) - * @param {string} name - * @param {Function} action - * @param {Object} [context] - * @chainable - */ - after: function(name, action, context) { - if (!name || !action) { - return; - } - name = 'after' + name; - return this.on(name, action, context); - }, - - /** - * Alias of on('success') - * @param {Function} action - * @param {Object} [context] - * @chainable - */ - success: function(action, context) { - return this.once('success', action, context); - }, - - /** - * Alias of on('error') - * @param {Function} action - * @param {Object} [context] - * @chainable - */ - error: function(action, context) { - return this.once('error', action, context); - }, - - /** - * Alias of on('success') - * @param {Function} action - * @param {Object} [context] - * @chainable - */ - off: function(name, action) { - - var handlers = this.__handlers__ || (this.__handlers__={}); - - if (!action) { - handlers[name] = []; - return; - } - if (handlers[name]) { - var hdls = handlers[name]; - var retains = []; - for (var i = 0; i < hdls.length; i++) { - if (action && hdls[i].action !== action) { - retains.push(hdls[i]); - } - } - handlers[name] = retains; - } - - return this; - }, - - /** - * If registered the event handler - * @param {string} name - * @param {Function} action - * @return {boolean} - */ - has: function(name, action) { - var handlers = this.__handlers__; - - if (! handlers || - ! handlers[name]) { - return false; - } - var hdls = handlers[name]; - for (var i = 0; i < hdls.length; i++) { - if (hdls[i].action === action) { - return true; - } - } - } - }; - - return notifier; -}); -define('qtek/core/util',['require'],function(require){ - - - - var guid = 0; - - /** - * Util functions - * @namespace qtek.core.util - */ - var util = { - - /** - * Generate GUID - * @return {number} - * @memberOf qtek.core.util - */ - genGUID: function() { - return ++guid; - }, - /** - * Relative path to absolute path - * @param {string} path - * @param {string} basePath - * @return {string} - * @memberOf qtek.core.util - */ - relative2absolute: function(path, basePath) { - if (!basePath || path.match(/^\//)) { - return path; - } - var pathParts = path.split('/'); - var basePathParts = basePath.split('/'); - - var item = pathParts[0]; - while(item === '.' || item === '..') { - if (item === '..') { - basePathParts.pop(); - } - pathParts.shift(); - item = pathParts[0]; - } - return basePathParts.join('/') + '/' + pathParts.join('/'); - }, - - /** - * Extend target with source - * @param {Object} target - * @param {Object} source - * @return {Object} - * @memberOf qtek.core.util - */ - extend: function(target, source) { - if (source) { - for (var name in source) { - if (source.hasOwnProperty(name)) { - target[name] = source[name]; - } - } - } - return target; - }, - - /** - * Extend properties to target if not exist. - * @param {Object} target - * @param {Object} source - * @return {Object} - * @memberOf qtek.core.util - */ - defaults: function(target, source) { - if (source) { - for (var propName in source) { - if (target[propName] === undefined) { - target[propName] = source[propName]; - } - } - } - return target; - }, - /** - * Extend properties with a given property list to avoid for..in.. iteration. - * @param {Object} target - * @param {Object} source - * @param {Array.} propList - * @return {Object} - * @memberOf qtek.core.util - */ - extendWithPropList: function(target, source, propList) { - if (source) { - for (var i = 0; i < propList.length; i++) { - var propName = propList[i]; - target[propName] = source[propName]; - } - } - return target; - }, - /** - * Extend properties to target if not exist. With a given property list avoid for..in.. iteration. - * @param {Object} target - * @param {Object} source - * @param {Array.} propList - * @return {Object} - * @memberOf qtek.core.util - */ - defaultsWithPropList: function(target, source, propList) { - if (source) { - for (var i = 0; i < propList.length; i++) { - var propName = propList[i]; - if (target[propName] === undefined) { - target[propName] = source[propName]; - } - } - } - return target; - }, - /** - * @param {Object|Array} obj - * @param {Function} iterator - * @param {Object} [context] - * @memberOf qtek.core.util - */ - each: function(obj, iterator, context) { - if (!(obj && iterator)) { - return; - } - if (obj.forEach) { - obj.forEach(iterator, context); - } else if (obj.length === + obj.length) { - for (var i = 0, len = obj.length; i < len; i++) { - iterator.call(context, obj[i], i, obj); - } - } else { - for (var key in obj) { - if (obj.hasOwnProperty(key)) { - iterator.call(context, obj[key], key, obj); - } - } - } - }, - - /** - * Is object ? - * @param {} obj - * @return {boolean} - * @memberOf qtek.core.util - */ - isObject: function(obj) { - return obj === Object(obj); - }, - - /** - * Is array ? - * @param {} obj - * @return {boolean} - * @memberOf qtek.core.util - */ - isArray: function(obj) { - return obj instanceof Array; - }, - - /** - * Is array like, which have a length property - * @param {} obj - * @return {boolean} - * @memberOf qtek.core.util - */ - isArrayLike: function(obj) { - if (!obj) { - return false; - } else { - return obj.length === + obj.length; - } - }, - - /** - * @param {} obj - * @return {} - * @memberOf qtek.core.util - */ - clone: function(obj) { - if (!util.isObject(obj)) { - return obj; - } else if (util.isArray(obj)) { - return obj.slice(); - } else if (util.isArrayLike(obj)) { // is typed array - var ret = new obj.constructor(obj.length); - for (var i = 0; i < obj.length; i++) { - ret[i] = obj[i]; - } - return ret; - } else { - return util.extend({}, obj); - } - } - }; - - return util; -}); -define('qtek/core/Base',['require','./mixin/derive','./mixin/notifier','./util'],function(require){ - - - - var deriveMixin = require('./mixin/derive'); - var notifierMixin = require('./mixin/notifier'); - var util = require('./util'); - - /** - * Base class of all objects - * @constructor - * @alias qtek.core.Base - * @mixes qtek.core.mixin.notifier - */ - var Base = function() { - /** - * @type {number} - */ - this.__GUID__ = util.genGUID(); - }; - - Base.__initializers__ = [ - function(opts) { - util.extend(this, opts); - } - ]; - - util.extend(Base, deriveMixin); - util.extend(Base.prototype, notifierMixin); - - return Base; -}); -/** - * @fileoverview gl-matrix - High performance matrix and vector operations - * @author Brandon Jones - * @author Colin MacKenzie IV - * @version 2.2.0 - */ - -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - - -(function(_global) { - - - var shim = {}; - if (typeof(exports) === 'undefined') { - if(typeof define == 'function' && typeof define.amd == 'object' && define.amd) { - shim.exports = {}; - define('qtek/dep/glmatrix',[],function() { - return shim.exports; - }); - } else { - // gl-matrix lives in a browser, define its namespaces in global - shim.exports = typeof(window) !== 'undefined' ? window : _global; - } - } - else { - // gl-matrix lives in commonjs, define its namespaces in exports - shim.exports = exports; - } - - (function(exports) { - /* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - - -if(!GLMAT_EPSILON) { - var GLMAT_EPSILON = 0.000001; -} - -if(!GLMAT_ARRAY_TYPE) { - var GLMAT_ARRAY_TYPE = (typeof Float32Array !== 'undefined') ? Float32Array : Array; -} - -if(!GLMAT_RANDOM) { - var GLMAT_RANDOM = Math.random; -} - -/** - * @class Common utilities - * @name glMatrix - */ -var glMatrix = {}; - -/** - * Sets the type of array used when creating new vectors and matricies - * - * @param {Type} type Array type, such as Float32Array or Array - */ -glMatrix.setMatrixArrayType = function(type) { - GLMAT_ARRAY_TYPE = type; -} - -if(typeof(exports) !== 'undefined') { - exports.glMatrix = glMatrix; -} - -var degree = Math.PI / 180; - -/** -* Convert Degree To Radian -* -* @param {Number} Angle in Degrees -*/ -glMatrix.toRadian = function(a){ - return a * degree; -} -; -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -/** - * @class 2 Dimensional Vector - * @name vec2 - */ - -var vec2 = {}; - -/** - * Creates a new, empty vec2 - * - * @returns {vec2} a new 2D vector - */ -vec2.create = function() { - var out = new GLMAT_ARRAY_TYPE(2); - out[0] = 0; - out[1] = 0; - return out; -}; - -/** - * Creates a new vec2 initialized with values from an existing vector - * - * @param {vec2} a vector to clone - * @returns {vec2} a new 2D vector - */ -vec2.clone = function(a) { - var out = new GLMAT_ARRAY_TYPE(2); - out[0] = a[0]; - out[1] = a[1]; - return out; -}; - -/** - * Creates a new vec2 initialized with the given values - * - * @param {Number} x X component - * @param {Number} y Y component - * @returns {vec2} a new 2D vector - */ -vec2.fromValues = function(x, y) { - var out = new GLMAT_ARRAY_TYPE(2); - out[0] = x; - out[1] = y; - return out; -}; - -/** - * Copy the values from one vec2 to another - * - * @param {vec2} out the receiving vector - * @param {vec2} a the source vector - * @returns {vec2} out - */ -vec2.copy = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - return out; -}; - -/** - * Set the components of a vec2 to the given values - * - * @param {vec2} out the receiving vector - * @param {Number} x X component - * @param {Number} y Y component - * @returns {vec2} out - */ -vec2.set = function(out, x, y) { - out[0] = x; - out[1] = y; - return out; -}; - -/** - * Adds two vec2's - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {vec2} out - */ -vec2.add = function(out, a, b) { - out[0] = a[0] + b[0]; - out[1] = a[1] + b[1]; - return out; -}; - -/** - * Subtracts vector b from vector a - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {vec2} out - */ -vec2.subtract = function(out, a, b) { - out[0] = a[0] - b[0]; - out[1] = a[1] - b[1]; - return out; -}; - -/** - * Alias for {@link vec2.subtract} - * @function - */ -vec2.sub = vec2.subtract; - -/** - * Multiplies two vec2's - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {vec2} out - */ -vec2.multiply = function(out, a, b) { - out[0] = a[0] * b[0]; - out[1] = a[1] * b[1]; - return out; -}; - -/** - * Alias for {@link vec2.multiply} - * @function - */ -vec2.mul = vec2.multiply; - -/** - * Divides two vec2's - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {vec2} out - */ -vec2.divide = function(out, a, b) { - out[0] = a[0] / b[0]; - out[1] = a[1] / b[1]; - return out; -}; - -/** - * Alias for {@link vec2.divide} - * @function - */ -vec2.div = vec2.divide; - -/** - * Returns the minimum of two vec2's - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {vec2} out - */ -vec2.min = function(out, a, b) { - out[0] = Math.min(a[0], b[0]); - out[1] = Math.min(a[1], b[1]); - return out; -}; - -/** - * Returns the maximum of two vec2's - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {vec2} out - */ -vec2.max = function(out, a, b) { - out[0] = Math.max(a[0], b[0]); - out[1] = Math.max(a[1], b[1]); - return out; -}; - -/** - * Scales a vec2 by a scalar number - * - * @param {vec2} out the receiving vector - * @param {vec2} a the vector to scale - * @param {Number} b amount to scale the vector by - * @returns {vec2} out - */ -vec2.scale = function(out, a, b) { - out[0] = a[0] * b; - out[1] = a[1] * b; - return out; -}; - -/** - * Adds two vec2's after scaling the second operand by a scalar value - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @param {Number} scale the amount to scale b by before adding - * @returns {vec2} out - */ -vec2.scaleAndAdd = function(out, a, b, scale) { - out[0] = a[0] + (b[0] * scale); - out[1] = a[1] + (b[1] * scale); - return out; -}; - -/** - * Calculates the euclidian distance between two vec2's - * - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {Number} distance between a and b - */ -vec2.distance = function(a, b) { - var x = b[0] - a[0], - y = b[1] - a[1]; - return Math.sqrt(x*x + y*y); -}; - -/** - * Alias for {@link vec2.distance} - * @function - */ -vec2.dist = vec2.distance; - -/** - * Calculates the squared euclidian distance between two vec2's - * - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {Number} squared distance between a and b - */ -vec2.squaredDistance = function(a, b) { - var x = b[0] - a[0], - y = b[1] - a[1]; - return x*x + y*y; -}; - -/** - * Alias for {@link vec2.squaredDistance} - * @function - */ -vec2.sqrDist = vec2.squaredDistance; - -/** - * Calculates the length of a vec2 - * - * @param {vec2} a vector to calculate length of - * @returns {Number} length of a - */ -vec2.length = function (a) { - var x = a[0], - y = a[1]; - return Math.sqrt(x*x + y*y); -}; - -/** - * Alias for {@link vec2.length} - * @function - */ -vec2.len = vec2.length; - -/** - * Calculates the squared length of a vec2 - * - * @param {vec2} a vector to calculate squared length of - * @returns {Number} squared length of a - */ -vec2.squaredLength = function (a) { - var x = a[0], - y = a[1]; - return x*x + y*y; -}; - -/** - * Alias for {@link vec2.squaredLength} - * @function - */ -vec2.sqrLen = vec2.squaredLength; - -/** - * Negates the components of a vec2 - * - * @param {vec2} out the receiving vector - * @param {vec2} a vector to negate - * @returns {vec2} out - */ -vec2.negate = function(out, a) { - out[0] = -a[0]; - out[1] = -a[1]; - return out; -}; - -/** - * Normalize a vec2 - * - * @param {vec2} out the receiving vector - * @param {vec2} a vector to normalize - * @returns {vec2} out - */ -vec2.normalize = function(out, a) { - var x = a[0], - y = a[1]; - var len = x*x + y*y; - if (len > 0) { - //TODO: evaluate use of glm_invsqrt here? - len = 1 / Math.sqrt(len); - out[0] = a[0] * len; - out[1] = a[1] * len; - } - return out; -}; - -/** - * Calculates the dot product of two vec2's - * - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {Number} dot product of a and b - */ -vec2.dot = function (a, b) { - return a[0] * b[0] + a[1] * b[1]; -}; - -/** - * Computes the cross product of two vec2's - * Note that the cross product must by definition produce a 3D vector - * - * @param {vec3} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {vec3} out - */ -vec2.cross = function(out, a, b) { - var z = a[0] * b[1] - a[1] * b[0]; - out[0] = out[1] = 0; - out[2] = z; - return out; -}; - -/** - * Performs a linear interpolation between two vec2's - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @param {Number} t interpolation amount between the two inputs - * @returns {vec2} out - */ -vec2.lerp = function (out, a, b, t) { - var ax = a[0], - ay = a[1]; - out[0] = ax + t * (b[0] - ax); - out[1] = ay + t * (b[1] - ay); - return out; -}; - -/** - * Generates a random vector with the given scale - * - * @param {vec2} out the receiving vector - * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned - * @returns {vec2} out - */ -vec2.random = function (out, scale) { - scale = scale || 1.0; - var r = GLMAT_RANDOM() * 2.0 * Math.PI; - out[0] = Math.cos(r) * scale; - out[1] = Math.sin(r) * scale; - return out; -}; - -/** - * Transforms the vec2 with a mat2 - * - * @param {vec2} out the receiving vector - * @param {vec2} a the vector to transform - * @param {mat2} m matrix to transform with - * @returns {vec2} out - */ -vec2.transformMat2 = function(out, a, m) { - var x = a[0], - y = a[1]; - out[0] = m[0] * x + m[2] * y; - out[1] = m[1] * x + m[3] * y; - return out; -}; - -/** - * Transforms the vec2 with a mat2d - * - * @param {vec2} out the receiving vector - * @param {vec2} a the vector to transform - * @param {mat2d} m matrix to transform with - * @returns {vec2} out - */ -vec2.transformMat2d = function(out, a, m) { - var x = a[0], - y = a[1]; - out[0] = m[0] * x + m[2] * y + m[4]; - out[1] = m[1] * x + m[3] * y + m[5]; - return out; -}; - -/** - * Transforms the vec2 with a mat3 - * 3rd vector component is implicitly '1' - * - * @param {vec2} out the receiving vector - * @param {vec2} a the vector to transform - * @param {mat3} m matrix to transform with - * @returns {vec2} out - */ -vec2.transformMat3 = function(out, a, m) { - var x = a[0], - y = a[1]; - out[0] = m[0] * x + m[3] * y + m[6]; - out[1] = m[1] * x + m[4] * y + m[7]; - return out; -}; - -/** - * Transforms the vec2 with a mat4 - * 3rd vector component is implicitly '0' - * 4th vector component is implicitly '1' - * - * @param {vec2} out the receiving vector - * @param {vec2} a the vector to transform - * @param {mat4} m matrix to transform with - * @returns {vec2} out - */ -vec2.transformMat4 = function(out, a, m) { - var x = a[0], - y = a[1]; - out[0] = m[0] * x + m[4] * y + m[12]; - out[1] = m[1] * x + m[5] * y + m[13]; - return out; -}; - -/** - * Perform some operation over an array of vec2s. - * - * @param {Array} a the array of vectors to iterate over - * @param {Number} stride Number of elements between the start of each vec2. If 0 assumes tightly packed - * @param {Number} offset Number of elements to skip at the beginning of the array - * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array - * @param {Function} fn Function to call for each vector in the array - * @param {Object} [arg] additional argument to pass to fn - * @returns {Array} a - * @function - */ -vec2.forEach = (function() { - var vec = vec2.create(); - - return function(a, stride, offset, count, fn, arg) { - var i, l; - if(!stride) { - stride = 2; - } - - if(!offset) { - offset = 0; - } - - if(count) { - l = Math.min((count * stride) + offset, a.length); - } else { - l = a.length; - } - - for(i = offset; i < l; i += stride) { - vec[0] = a[i]; vec[1] = a[i+1]; - fn(vec, vec, arg); - a[i] = vec[0]; a[i+1] = vec[1]; - } - - return a; - }; -})(); - -/** - * Returns a string representation of a vector - * - * @param {vec2} vec vector to represent as a string - * @returns {String} string representation of the vector - */ -vec2.str = function (a) { - return 'vec2(' + a[0] + ', ' + a[1] + ')'; -}; - -if(typeof(exports) !== 'undefined') { - exports.vec2 = vec2; -} -; -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -/** - * @class 3 Dimensional Vector - * @name vec3 - */ - -var vec3 = {}; - -/** - * Creates a new, empty vec3 - * - * @returns {vec3} a new 3D vector - */ -vec3.create = function() { - var out = new GLMAT_ARRAY_TYPE(3); - out[0] = 0; - out[1] = 0; - out[2] = 0; - return out; -}; - -/** - * Creates a new vec3 initialized with values from an existing vector - * - * @param {vec3} a vector to clone - * @returns {vec3} a new 3D vector - */ -vec3.clone = function(a) { - var out = new GLMAT_ARRAY_TYPE(3); - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - return out; -}; - -/** - * Creates a new vec3 initialized with the given values - * - * @param {Number} x X component - * @param {Number} y Y component - * @param {Number} z Z component - * @returns {vec3} a new 3D vector - */ -vec3.fromValues = function(x, y, z) { - var out = new GLMAT_ARRAY_TYPE(3); - out[0] = x; - out[1] = y; - out[2] = z; - return out; -}; - -/** - * Copy the values from one vec3 to another - * - * @param {vec3} out the receiving vector - * @param {vec3} a the source vector - * @returns {vec3} out - */ -vec3.copy = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - return out; -}; - -/** - * Set the components of a vec3 to the given values - * - * @param {vec3} out the receiving vector - * @param {Number} x X component - * @param {Number} y Y component - * @param {Number} z Z component - * @returns {vec3} out - */ -vec3.set = function(out, x, y, z) { - out[0] = x; - out[1] = y; - out[2] = z; - return out; -}; - -/** - * Adds two vec3's - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {vec3} out - */ -vec3.add = function(out, a, b) { - out[0] = a[0] + b[0]; - out[1] = a[1] + b[1]; - out[2] = a[2] + b[2]; - return out; -}; - -/** - * Subtracts vector b from vector a - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {vec3} out - */ -vec3.subtract = function(out, a, b) { - out[0] = a[0] - b[0]; - out[1] = a[1] - b[1]; - out[2] = a[2] - b[2]; - return out; -}; - -/** - * Alias for {@link vec3.subtract} - * @function - */ -vec3.sub = vec3.subtract; - -/** - * Multiplies two vec3's - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {vec3} out - */ -vec3.multiply = function(out, a, b) { - out[0] = a[0] * b[0]; - out[1] = a[1] * b[1]; - out[2] = a[2] * b[2]; - return out; -}; - -/** - * Alias for {@link vec3.multiply} - * @function - */ -vec3.mul = vec3.multiply; - -/** - * Divides two vec3's - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {vec3} out - */ -vec3.divide = function(out, a, b) { - out[0] = a[0] / b[0]; - out[1] = a[1] / b[1]; - out[2] = a[2] / b[2]; - return out; -}; - -/** - * Alias for {@link vec3.divide} - * @function - */ -vec3.div = vec3.divide; - -/** - * Returns the minimum of two vec3's - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {vec3} out - */ -vec3.min = function(out, a, b) { - out[0] = Math.min(a[0], b[0]); - out[1] = Math.min(a[1], b[1]); - out[2] = Math.min(a[2], b[2]); - return out; -}; - -/** - * Returns the maximum of two vec3's - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {vec3} out - */ -vec3.max = function(out, a, b) { - out[0] = Math.max(a[0], b[0]); - out[1] = Math.max(a[1], b[1]); - out[2] = Math.max(a[2], b[2]); - return out; -}; - -/** - * Scales a vec3 by a scalar number - * - * @param {vec3} out the receiving vector - * @param {vec3} a the vector to scale - * @param {Number} b amount to scale the vector by - * @returns {vec3} out - */ -vec3.scale = function(out, a, b) { - out[0] = a[0] * b; - out[1] = a[1] * b; - out[2] = a[2] * b; - return out; -}; - -/** - * Adds two vec3's after scaling the second operand by a scalar value - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @param {Number} scale the amount to scale b by before adding - * @returns {vec3} out - */ -vec3.scaleAndAdd = function(out, a, b, scale) { - out[0] = a[0] + (b[0] * scale); - out[1] = a[1] + (b[1] * scale); - out[2] = a[2] + (b[2] * scale); - return out; -}; - -/** - * Calculates the euclidian distance between two vec3's - * - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {Number} distance between a and b - */ -vec3.distance = function(a, b) { - var x = b[0] - a[0], - y = b[1] - a[1], - z = b[2] - a[2]; - return Math.sqrt(x*x + y*y + z*z); -}; - -/** - * Alias for {@link vec3.distance} - * @function - */ -vec3.dist = vec3.distance; - -/** - * Calculates the squared euclidian distance between two vec3's - * - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {Number} squared distance between a and b - */ -vec3.squaredDistance = function(a, b) { - var x = b[0] - a[0], - y = b[1] - a[1], - z = b[2] - a[2]; - return x*x + y*y + z*z; -}; - -/** - * Alias for {@link vec3.squaredDistance} - * @function - */ -vec3.sqrDist = vec3.squaredDistance; - -/** - * Calculates the length of a vec3 - * - * @param {vec3} a vector to calculate length of - * @returns {Number} length of a - */ -vec3.length = function (a) { - var x = a[0], - y = a[1], - z = a[2]; - return Math.sqrt(x*x + y*y + z*z); -}; - -/** - * Alias for {@link vec3.length} - * @function - */ -vec3.len = vec3.length; - -/** - * Calculates the squared length of a vec3 - * - * @param {vec3} a vector to calculate squared length of - * @returns {Number} squared length of a - */ -vec3.squaredLength = function (a) { - var x = a[0], - y = a[1], - z = a[2]; - return x*x + y*y + z*z; -}; - -/** - * Alias for {@link vec3.squaredLength} - * @function - */ -vec3.sqrLen = vec3.squaredLength; - -/** - * Negates the components of a vec3 - * - * @param {vec3} out the receiving vector - * @param {vec3} a vector to negate - * @returns {vec3} out - */ -vec3.negate = function(out, a) { - out[0] = -a[0]; - out[1] = -a[1]; - out[2] = -a[2]; - return out; -}; - -/** - * Normalize a vec3 - * - * @param {vec3} out the receiving vector - * @param {vec3} a vector to normalize - * @returns {vec3} out - */ -vec3.normalize = function(out, a) { - var x = a[0], - y = a[1], - z = a[2]; - var len = x*x + y*y + z*z; - if (len > 0) { - //TODO: evaluate use of glm_invsqrt here? - len = 1 / Math.sqrt(len); - out[0] = a[0] * len; - out[1] = a[1] * len; - out[2] = a[2] * len; - } - return out; -}; - -/** - * Calculates the dot product of two vec3's - * - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {Number} dot product of a and b - */ -vec3.dot = function (a, b) { - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; -}; - -/** - * Computes the cross product of two vec3's - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {vec3} out - */ -vec3.cross = function(out, a, b) { - var ax = a[0], ay = a[1], az = a[2], - bx = b[0], by = b[1], bz = b[2]; - - out[0] = ay * bz - az * by; - out[1] = az * bx - ax * bz; - out[2] = ax * by - ay * bx; - return out; -}; - -/** - * Performs a linear interpolation between two vec3's - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @param {Number} t interpolation amount between the two inputs - * @returns {vec3} out - */ -vec3.lerp = function (out, a, b, t) { - var ax = a[0], - ay = a[1], - az = a[2]; - out[0] = ax + t * (b[0] - ax); - out[1] = ay + t * (b[1] - ay); - out[2] = az + t * (b[2] - az); - return out; -}; - -/** - * Generates a random vector with the given scale - * - * @param {vec3} out the receiving vector - * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned - * @returns {vec3} out - */ -vec3.random = function (out, scale) { - scale = scale || 1.0; - - var r = GLMAT_RANDOM() * 2.0 * Math.PI; - var z = (GLMAT_RANDOM() * 2.0) - 1.0; - var zScale = Math.sqrt(1.0-z*z) * scale; - - out[0] = Math.cos(r) * zScale; - out[1] = Math.sin(r) * zScale; - out[2] = z * scale; - return out; -}; - -/** - * Transforms the vec3 with a mat4. - * 4th vector component is implicitly '1' - * - * @param {vec3} out the receiving vector - * @param {vec3} a the vector to transform - * @param {mat4} m matrix to transform with - * @returns {vec3} out - */ -vec3.transformMat4 = function(out, a, m) { - var x = a[0], y = a[1], z = a[2]; - out[0] = m[0] * x + m[4] * y + m[8] * z + m[12]; - out[1] = m[1] * x + m[5] * y + m[9] * z + m[13]; - out[2] = m[2] * x + m[6] * y + m[10] * z + m[14]; - return out; -}; - -/** - * Transforms the vec3 with a mat3. - * - * @param {vec3} out the receiving vector - * @param {vec3} a the vector to transform - * @param {mat4} m the 3x3 matrix to transform with - * @returns {vec3} out - */ -vec3.transformMat3 = function(out, a, m) { - var x = a[0], y = a[1], z = a[2]; - out[0] = x * m[0] + y * m[3] + z * m[6]; - out[1] = x * m[1] + y * m[4] + z * m[7]; - out[2] = x * m[2] + y * m[5] + z * m[8]; - return out; -}; - -/** - * Transforms the vec3 with a quat - * - * @param {vec3} out the receiving vector - * @param {vec3} a the vector to transform - * @param {quat} q quaternion to transform with - * @returns {vec3} out - */ -vec3.transformQuat = function(out, a, q) { - // benchmarks: http://jsperf.com/quaternion-transform-vec3-implementations - - var x = a[0], y = a[1], z = a[2], - qx = q[0], qy = q[1], qz = q[2], qw = q[3], - - // calculate quat * vec - ix = qw * x + qy * z - qz * y, - iy = qw * y + qz * x - qx * z, - iz = qw * z + qx * y - qy * x, - iw = -qx * x - qy * y - qz * z; - - // calculate result * inverse quat - out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; - out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; - out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; - return out; -}; - -/** - * Perform some operation over an array of vec3s. - * - * @param {Array} a the array of vectors to iterate over - * @param {Number} stride Number of elements between the start of each vec3. If 0 assumes tightly packed - * @param {Number} offset Number of elements to skip at the beginning of the array - * @param {Number} count Number of vec3s to iterate over. If 0 iterates over entire array - * @param {Function} fn Function to call for each vector in the array - * @param {Object} [arg] additional argument to pass to fn - * @returns {Array} a - * @function - */ -vec3.forEach = (function() { - var vec = vec3.create(); - - return function(a, stride, offset, count, fn, arg) { - var i, l; - if(!stride) { - stride = 3; - } - - if(!offset) { - offset = 0; - } - - if(count) { - l = Math.min((count * stride) + offset, a.length); - } else { - l = a.length; - } - - for(i = offset; i < l; i += stride) { - vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; - fn(vec, vec, arg); - a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; - } - - return a; - }; -})(); - -/** - * Returns a string representation of a vector - * - * @param {vec3} vec vector to represent as a string - * @returns {String} string representation of the vector - */ -vec3.str = function (a) { - return 'vec3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ')'; -}; - -if(typeof(exports) !== 'undefined') { - exports.vec3 = vec3; -} -; -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -/** - * @class 4 Dimensional Vector - * @name vec4 - */ - -var vec4 = {}; - -/** - * Creates a new, empty vec4 - * - * @returns {vec4} a new 4D vector - */ -vec4.create = function() { - var out = new GLMAT_ARRAY_TYPE(4); - out[0] = 0; - out[1] = 0; - out[2] = 0; - out[3] = 0; - return out; -}; - -/** - * Creates a new vec4 initialized with values from an existing vector - * - * @param {vec4} a vector to clone - * @returns {vec4} a new 4D vector - */ -vec4.clone = function(a) { - var out = new GLMAT_ARRAY_TYPE(4); - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - return out; -}; - -/** - * Creates a new vec4 initialized with the given values - * - * @param {Number} x X component - * @param {Number} y Y component - * @param {Number} z Z component - * @param {Number} w W component - * @returns {vec4} a new 4D vector - */ -vec4.fromValues = function(x, y, z, w) { - var out = new GLMAT_ARRAY_TYPE(4); - out[0] = x; - out[1] = y; - out[2] = z; - out[3] = w; - return out; -}; - -/** - * Copy the values from one vec4 to another - * - * @param {vec4} out the receiving vector - * @param {vec4} a the source vector - * @returns {vec4} out - */ -vec4.copy = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - return out; -}; - -/** - * Set the components of a vec4 to the given values - * - * @param {vec4} out the receiving vector - * @param {Number} x X component - * @param {Number} y Y component - * @param {Number} z Z component - * @param {Number} w W component - * @returns {vec4} out - */ -vec4.set = function(out, x, y, z, w) { - out[0] = x; - out[1] = y; - out[2] = z; - out[3] = w; - return out; -}; - -/** - * Adds two vec4's - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {vec4} out - */ -vec4.add = function(out, a, b) { - out[0] = a[0] + b[0]; - out[1] = a[1] + b[1]; - out[2] = a[2] + b[2]; - out[3] = a[3] + b[3]; - return out; -}; - -/** - * Subtracts vector b from vector a - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {vec4} out - */ -vec4.subtract = function(out, a, b) { - out[0] = a[0] - b[0]; - out[1] = a[1] - b[1]; - out[2] = a[2] - b[2]; - out[3] = a[3] - b[3]; - return out; -}; - -/** - * Alias for {@link vec4.subtract} - * @function - */ -vec4.sub = vec4.subtract; - -/** - * Multiplies two vec4's - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {vec4} out - */ -vec4.multiply = function(out, a, b) { - out[0] = a[0] * b[0]; - out[1] = a[1] * b[1]; - out[2] = a[2] * b[2]; - out[3] = a[3] * b[3]; - return out; -}; - -/** - * Alias for {@link vec4.multiply} - * @function - */ -vec4.mul = vec4.multiply; - -/** - * Divides two vec4's - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {vec4} out - */ -vec4.divide = function(out, a, b) { - out[0] = a[0] / b[0]; - out[1] = a[1] / b[1]; - out[2] = a[2] / b[2]; - out[3] = a[3] / b[3]; - return out; -}; - -/** - * Alias for {@link vec4.divide} - * @function - */ -vec4.div = vec4.divide; - -/** - * Returns the minimum of two vec4's - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {vec4} out - */ -vec4.min = function(out, a, b) { - out[0] = Math.min(a[0], b[0]); - out[1] = Math.min(a[1], b[1]); - out[2] = Math.min(a[2], b[2]); - out[3] = Math.min(a[3], b[3]); - return out; -}; - -/** - * Returns the maximum of two vec4's - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {vec4} out - */ -vec4.max = function(out, a, b) { - out[0] = Math.max(a[0], b[0]); - out[1] = Math.max(a[1], b[1]); - out[2] = Math.max(a[2], b[2]); - out[3] = Math.max(a[3], b[3]); - return out; -}; - -/** - * Scales a vec4 by a scalar number - * - * @param {vec4} out the receiving vector - * @param {vec4} a the vector to scale - * @param {Number} b amount to scale the vector by - * @returns {vec4} out - */ -vec4.scale = function(out, a, b) { - out[0] = a[0] * b; - out[1] = a[1] * b; - out[2] = a[2] * b; - out[3] = a[3] * b; - return out; -}; - -/** - * Adds two vec4's after scaling the second operand by a scalar value - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @param {Number} scale the amount to scale b by before adding - * @returns {vec4} out - */ -vec4.scaleAndAdd = function(out, a, b, scale) { - out[0] = a[0] + (b[0] * scale); - out[1] = a[1] + (b[1] * scale); - out[2] = a[2] + (b[2] * scale); - out[3] = a[3] + (b[3] * scale); - return out; -}; - -/** - * Calculates the euclidian distance between two vec4's - * - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {Number} distance between a and b - */ -vec4.distance = function(a, b) { - var x = b[0] - a[0], - y = b[1] - a[1], - z = b[2] - a[2], - w = b[3] - a[3]; - return Math.sqrt(x*x + y*y + z*z + w*w); -}; - -/** - * Alias for {@link vec4.distance} - * @function - */ -vec4.dist = vec4.distance; - -/** - * Calculates the squared euclidian distance between two vec4's - * - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {Number} squared distance between a and b - */ -vec4.squaredDistance = function(a, b) { - var x = b[0] - a[0], - y = b[1] - a[1], - z = b[2] - a[2], - w = b[3] - a[3]; - return x*x + y*y + z*z + w*w; -}; - -/** - * Alias for {@link vec4.squaredDistance} - * @function - */ -vec4.sqrDist = vec4.squaredDistance; - -/** - * Calculates the length of a vec4 - * - * @param {vec4} a vector to calculate length of - * @returns {Number} length of a - */ -vec4.length = function (a) { - var x = a[0], - y = a[1], - z = a[2], - w = a[3]; - return Math.sqrt(x*x + y*y + z*z + w*w); -}; - -/** - * Alias for {@link vec4.length} - * @function - */ -vec4.len = vec4.length; - -/** - * Calculates the squared length of a vec4 - * - * @param {vec4} a vector to calculate squared length of - * @returns {Number} squared length of a - */ -vec4.squaredLength = function (a) { - var x = a[0], - y = a[1], - z = a[2], - w = a[3]; - return x*x + y*y + z*z + w*w; -}; - -/** - * Alias for {@link vec4.squaredLength} - * @function - */ -vec4.sqrLen = vec4.squaredLength; - -/** - * Negates the components of a vec4 - * - * @param {vec4} out the receiving vector - * @param {vec4} a vector to negate - * @returns {vec4} out - */ -vec4.negate = function(out, a) { - out[0] = -a[0]; - out[1] = -a[1]; - out[2] = -a[2]; - out[3] = -a[3]; - return out; -}; - -/** - * Normalize a vec4 - * - * @param {vec4} out the receiving vector - * @param {vec4} a vector to normalize - * @returns {vec4} out - */ -vec4.normalize = function(out, a) { - var x = a[0], - y = a[1], - z = a[2], - w = a[3]; - var len = x*x + y*y + z*z + w*w; - if (len > 0) { - len = 1 / Math.sqrt(len); - out[0] = a[0] * len; - out[1] = a[1] * len; - out[2] = a[2] * len; - out[3] = a[3] * len; - } - return out; -}; - -/** - * Calculates the dot product of two vec4's - * - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {Number} dot product of a and b - */ -vec4.dot = function (a, b) { - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; -}; - -/** - * Performs a linear interpolation between two vec4's - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @param {Number} t interpolation amount between the two inputs - * @returns {vec4} out - */ -vec4.lerp = function (out, a, b, t) { - var ax = a[0], - ay = a[1], - az = a[2], - aw = a[3]; - out[0] = ax + t * (b[0] - ax); - out[1] = ay + t * (b[1] - ay); - out[2] = az + t * (b[2] - az); - out[3] = aw + t * (b[3] - aw); - return out; -}; - -/** - * Generates a random vector with the given scale - * - * @param {vec4} out the receiving vector - * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned - * @returns {vec4} out - */ -vec4.random = function (out, scale) { - scale = scale || 1.0; - - //TODO: This is a pretty awful way of doing this. Find something better. - out[0] = GLMAT_RANDOM(); - out[1] = GLMAT_RANDOM(); - out[2] = GLMAT_RANDOM(); - out[3] = GLMAT_RANDOM(); - vec4.normalize(out, out); - vec4.scale(out, out, scale); - return out; -}; - -/** - * Transforms the vec4 with a mat4. - * - * @param {vec4} out the receiving vector - * @param {vec4} a the vector to transform - * @param {mat4} m matrix to transform with - * @returns {vec4} out - */ -vec4.transformMat4 = function(out, a, m) { - var x = a[0], y = a[1], z = a[2], w = a[3]; - out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w; - out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w; - out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w; - out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w; - return out; -}; - -/** - * Transforms the vec4 with a quat - * - * @param {vec4} out the receiving vector - * @param {vec4} a the vector to transform - * @param {quat} q quaternion to transform with - * @returns {vec4} out - */ -vec4.transformQuat = function(out, a, q) { - var x = a[0], y = a[1], z = a[2], - qx = q[0], qy = q[1], qz = q[2], qw = q[3], - - // calculate quat * vec - ix = qw * x + qy * z - qz * y, - iy = qw * y + qz * x - qx * z, - iz = qw * z + qx * y - qy * x, - iw = -qx * x - qy * y - qz * z; - - // calculate result * inverse quat - out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; - out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; - out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; - return out; -}; - -/** - * Perform some operation over an array of vec4s. - * - * @param {Array} a the array of vectors to iterate over - * @param {Number} stride Number of elements between the start of each vec4. If 0 assumes tightly packed - * @param {Number} offset Number of elements to skip at the beginning of the array - * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array - * @param {Function} fn Function to call for each vector in the array - * @param {Object} [arg] additional argument to pass to fn - * @returns {Array} a - * @function - */ -vec4.forEach = (function() { - var vec = vec4.create(); - - return function(a, stride, offset, count, fn, arg) { - var i, l; - if(!stride) { - stride = 4; - } - - if(!offset) { - offset = 0; - } - - if(count) { - l = Math.min((count * stride) + offset, a.length); - } else { - l = a.length; - } - - for(i = offset; i < l; i += stride) { - vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; vec[3] = a[i+3]; - fn(vec, vec, arg); - a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; a[i+3] = vec[3]; - } - - return a; - }; -})(); - -/** - * Returns a string representation of a vector - * - * @param {vec4} vec vector to represent as a string - * @returns {String} string representation of the vector - */ -vec4.str = function (a) { - return 'vec4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; -}; - -if(typeof(exports) !== 'undefined') { - exports.vec4 = vec4; -} -; -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -/** - * @class 2x2 Matrix - * @name mat2 - */ - -var mat2 = {}; - -/** - * Creates a new identity mat2 - * - * @returns {mat2} a new 2x2 matrix - */ -mat2.create = function() { - var out = new GLMAT_ARRAY_TYPE(4); - out[0] = 1; - out[1] = 0; - out[2] = 0; - out[3] = 1; - return out; -}; - -/** - * Creates a new mat2 initialized with values from an existing matrix - * - * @param {mat2} a matrix to clone - * @returns {mat2} a new 2x2 matrix - */ -mat2.clone = function(a) { - var out = new GLMAT_ARRAY_TYPE(4); - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - return out; -}; - -/** - * Copy the values from one mat2 to another - * - * @param {mat2} out the receiving matrix - * @param {mat2} a the source matrix - * @returns {mat2} out - */ -mat2.copy = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - return out; -}; - -/** - * Set a mat2 to the identity matrix - * - * @param {mat2} out the receiving matrix - * @returns {mat2} out - */ -mat2.identity = function(out) { - out[0] = 1; - out[1] = 0; - out[2] = 0; - out[3] = 1; - return out; -}; - -/** - * Transpose the values of a mat2 - * - * @param {mat2} out the receiving matrix - * @param {mat2} a the source matrix - * @returns {mat2} out - */ -mat2.transpose = function(out, a) { - // If we are transposing ourselves we can skip a few steps but have to cache some values - if (out === a) { - var a1 = a[1]; - out[1] = a[2]; - out[2] = a1; - } else { - out[0] = a[0]; - out[1] = a[2]; - out[2] = a[1]; - out[3] = a[3]; - } - - return out; -}; - -/** - * Inverts a mat2 - * - * @param {mat2} out the receiving matrix - * @param {mat2} a the source matrix - * @returns {mat2} out - */ -mat2.invert = function(out, a) { - var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], - - // Calculate the determinant - det = a0 * a3 - a2 * a1; - - if (!det) { - return null; - } - det = 1.0 / det; - - out[0] = a3 * det; - out[1] = -a1 * det; - out[2] = -a2 * det; - out[3] = a0 * det; - - return out; -}; - -/** - * Calculates the adjugate of a mat2 - * - * @param {mat2} out the receiving matrix - * @param {mat2} a the source matrix - * @returns {mat2} out - */ -mat2.adjoint = function(out, a) { - // Caching this value is nessecary if out == a - var a0 = a[0]; - out[0] = a[3]; - out[1] = -a[1]; - out[2] = -a[2]; - out[3] = a0; - - return out; -}; - -/** - * Calculates the determinant of a mat2 - * - * @param {mat2} a the source matrix - * @returns {Number} determinant of a - */ -mat2.determinant = function (a) { - return a[0] * a[3] - a[2] * a[1]; -}; - -/** - * Multiplies two mat2's - * - * @param {mat2} out the receiving matrix - * @param {mat2} a the first operand - * @param {mat2} b the second operand - * @returns {mat2} out - */ -mat2.multiply = function (out, a, b) { - var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3]; - var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; - out[0] = a0 * b0 + a1 * b2; - out[1] = a0 * b1 + a1 * b3; - out[2] = a2 * b0 + a3 * b2; - out[3] = a2 * b1 + a3 * b3; - return out; -}; - -/** - * Alias for {@link mat2.multiply} - * @function - */ -mat2.mul = mat2.multiply; - -/** - * Rotates a mat2 by the given angle - * - * @param {mat2} out the receiving matrix - * @param {mat2} a the matrix to rotate - * @param {Number} rad the angle to rotate the matrix by - * @returns {mat2} out - */ -mat2.rotate = function (out, a, rad) { - var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], - s = Math.sin(rad), - c = Math.cos(rad); - out[0] = a0 * c + a1 * s; - out[1] = a0 * -s + a1 * c; - out[2] = a2 * c + a3 * s; - out[3] = a2 * -s + a3 * c; - return out; -}; - -/** - * Scales the mat2 by the dimensions in the given vec2 - * - * @param {mat2} out the receiving matrix - * @param {mat2} a the matrix to rotate - * @param {vec2} v the vec2 to scale the matrix by - * @returns {mat2} out - **/ -mat2.scale = function(out, a, v) { - var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], - v0 = v[0], v1 = v[1]; - out[0] = a0 * v0; - out[1] = a1 * v1; - out[2] = a2 * v0; - out[3] = a3 * v1; - return out; -}; - -/** - * Returns a string representation of a mat2 - * - * @param {mat2} mat matrix to represent as a string - * @returns {String} string representation of the matrix - */ -mat2.str = function (a) { - return 'mat2(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; -}; - -if(typeof(exports) !== 'undefined') { - exports.mat2 = mat2; -} -; -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -/** - * @class 2x3 Matrix - * @name mat2d - * - * @description - * A mat2d contains six elements defined as: - *
- * [a, b,
- *  c, d,
- *  tx,ty]
- * 
- * This is a short form for the 3x3 matrix: - *
- * [a, b, 0
- *  c, d, 0
- *  tx,ty,1]
- * 
- * The last column is ignored so the array is shorter and operations are faster. - */ - -var mat2d = {}; - -/** - * Creates a new identity mat2d - * - * @returns {mat2d} a new 2x3 matrix - */ -mat2d.create = function() { - var out = new GLMAT_ARRAY_TYPE(6); - out[0] = 1; - out[1] = 0; - out[2] = 0; - out[3] = 1; - out[4] = 0; - out[5] = 0; - return out; -}; - -/** - * Creates a new mat2d initialized with values from an existing matrix - * - * @param {mat2d} a matrix to clone - * @returns {mat2d} a new 2x3 matrix - */ -mat2d.clone = function(a) { - var out = new GLMAT_ARRAY_TYPE(6); - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - out[4] = a[4]; - out[5] = a[5]; - return out; -}; - -/** - * Copy the values from one mat2d to another - * - * @param {mat2d} out the receiving matrix - * @param {mat2d} a the source matrix - * @returns {mat2d} out - */ -mat2d.copy = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - out[4] = a[4]; - out[5] = a[5]; - return out; -}; - -/** - * Set a mat2d to the identity matrix - * - * @param {mat2d} out the receiving matrix - * @returns {mat2d} out - */ -mat2d.identity = function(out) { - out[0] = 1; - out[1] = 0; - out[2] = 0; - out[3] = 1; - out[4] = 0; - out[5] = 0; - return out; -}; - -/** - * Inverts a mat2d - * - * @param {mat2d} out the receiving matrix - * @param {mat2d} a the source matrix - * @returns {mat2d} out - */ -mat2d.invert = function(out, a) { - var aa = a[0], ab = a[1], ac = a[2], ad = a[3], - atx = a[4], aty = a[5]; - - var det = aa * ad - ab * ac; - if(!det){ - return null; - } - det = 1.0 / det; - - out[0] = ad * det; - out[1] = -ab * det; - out[2] = -ac * det; - out[3] = aa * det; - out[4] = (ac * aty - ad * atx) * det; - out[5] = (ab * atx - aa * aty) * det; - return out; -}; - -/** - * Calculates the determinant of a mat2d - * - * @param {mat2d} a the source matrix - * @returns {Number} determinant of a - */ -mat2d.determinant = function (a) { - return a[0] * a[3] - a[1] * a[2]; -}; - -/** - * Multiplies two mat2d's - * - * @param {mat2d} out the receiving matrix - * @param {mat2d} a the first operand - * @param {mat2d} b the second operand - * @returns {mat2d} out - */ -mat2d.multiply = function (out, a, b) { - var aa = a[0], ab = a[1], ac = a[2], ad = a[3], - atx = a[4], aty = a[5], - ba = b[0], bb = b[1], bc = b[2], bd = b[3], - btx = b[4], bty = b[5]; - - out[0] = aa*ba + ab*bc; - out[1] = aa*bb + ab*bd; - out[2] = ac*ba + ad*bc; - out[3] = ac*bb + ad*bd; - out[4] = ba*atx + bc*aty + btx; - out[5] = bb*atx + bd*aty + bty; - return out; -}; - -/** - * Alias for {@link mat2d.multiply} - * @function - */ -mat2d.mul = mat2d.multiply; - - -/** - * Rotates a mat2d by the given angle - * - * @param {mat2d} out the receiving matrix - * @param {mat2d} a the matrix to rotate - * @param {Number} rad the angle to rotate the matrix by - * @returns {mat2d} out - */ -mat2d.rotate = function (out, a, rad) { - var aa = a[0], - ab = a[1], - ac = a[2], - ad = a[3], - atx = a[4], - aty = a[5], - st = Math.sin(rad), - ct = Math.cos(rad); - - out[0] = aa*ct + ab*st; - out[1] = -aa*st + ab*ct; - out[2] = ac*ct + ad*st; - out[3] = -ac*st + ct*ad; - out[4] = ct*atx + st*aty; - out[5] = ct*aty - st*atx; - return out; -}; - -/** - * Scales the mat2d by the dimensions in the given vec2 - * - * @param {mat2d} out the receiving matrix - * @param {mat2d} a the matrix to translate - * @param {vec2} v the vec2 to scale the matrix by - * @returns {mat2d} out - **/ -mat2d.scale = function(out, a, v) { - var vx = v[0], vy = v[1]; - out[0] = a[0] * vx; - out[1] = a[1] * vy; - out[2] = a[2] * vx; - out[3] = a[3] * vy; - out[4] = a[4] * vx; - out[5] = a[5] * vy; - return out; -}; - -/** - * Translates the mat2d by the dimensions in the given vec2 - * - * @param {mat2d} out the receiving matrix - * @param {mat2d} a the matrix to translate - * @param {vec2} v the vec2 to translate the matrix by - * @returns {mat2d} out - **/ -mat2d.translate = function(out, a, v) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - out[4] = a[4] + v[0]; - out[5] = a[5] + v[1]; - return out; -}; - -/** - * Returns a string representation of a mat2d - * - * @param {mat2d} a matrix to represent as a string - * @returns {String} string representation of the matrix - */ -mat2d.str = function (a) { - return 'mat2d(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + - a[3] + ', ' + a[4] + ', ' + a[5] + ')'; -}; - -if(typeof(exports) !== 'undefined') { - exports.mat2d = mat2d; -} -; -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -/** - * @class 3x3 Matrix - * @name mat3 - */ - -var mat3 = {}; - -/** - * Creates a new identity mat3 - * - * @returns {mat3} a new 3x3 matrix - */ -mat3.create = function() { - var out = new GLMAT_ARRAY_TYPE(9); - out[0] = 1; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 1; - out[5] = 0; - out[6] = 0; - out[7] = 0; - out[8] = 1; - return out; -}; - -/** - * Copies the upper-left 3x3 values into the given mat3. - * - * @param {mat3} out the receiving 3x3 matrix - * @param {mat4} a the source 4x4 matrix - * @returns {mat3} out - */ -mat3.fromMat4 = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[4]; - out[4] = a[5]; - out[5] = a[6]; - out[6] = a[8]; - out[7] = a[9]; - out[8] = a[10]; - return out; -}; - -/** - * Creates a new mat3 initialized with values from an existing matrix - * - * @param {mat3} a matrix to clone - * @returns {mat3} a new 3x3 matrix - */ -mat3.clone = function(a) { - var out = new GLMAT_ARRAY_TYPE(9); - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - out[4] = a[4]; - out[5] = a[5]; - out[6] = a[6]; - out[7] = a[7]; - out[8] = a[8]; - return out; -}; - -/** - * Copy the values from one mat3 to another - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the source matrix - * @returns {mat3} out - */ -mat3.copy = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - out[4] = a[4]; - out[5] = a[5]; - out[6] = a[6]; - out[7] = a[7]; - out[8] = a[8]; - return out; -}; - -/** - * Set a mat3 to the identity matrix - * - * @param {mat3} out the receiving matrix - * @returns {mat3} out - */ -mat3.identity = function(out) { - out[0] = 1; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 1; - out[5] = 0; - out[6] = 0; - out[7] = 0; - out[8] = 1; - return out; -}; - -/** - * Transpose the values of a mat3 - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the source matrix - * @returns {mat3} out - */ -mat3.transpose = function(out, a) { - // If we are transposing ourselves we can skip a few steps but have to cache some values - if (out === a) { - var a01 = a[1], a02 = a[2], a12 = a[5]; - out[1] = a[3]; - out[2] = a[6]; - out[3] = a01; - out[5] = a[7]; - out[6] = a02; - out[7] = a12; - } else { - out[0] = a[0]; - out[1] = a[3]; - out[2] = a[6]; - out[3] = a[1]; - out[4] = a[4]; - out[5] = a[7]; - out[6] = a[2]; - out[7] = a[5]; - out[8] = a[8]; - } - - return out; -}; - -/** - * Inverts a mat3 - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the source matrix - * @returns {mat3} out - */ -mat3.invert = function(out, a) { - var a00 = a[0], a01 = a[1], a02 = a[2], - a10 = a[3], a11 = a[4], a12 = a[5], - a20 = a[6], a21 = a[7], a22 = a[8], - - b01 = a22 * a11 - a12 * a21, - b11 = -a22 * a10 + a12 * a20, - b21 = a21 * a10 - a11 * a20, - - // Calculate the determinant - det = a00 * b01 + a01 * b11 + a02 * b21; - - if (!det) { - return null; - } - det = 1.0 / det; - - out[0] = b01 * det; - out[1] = (-a22 * a01 + a02 * a21) * det; - out[2] = (a12 * a01 - a02 * a11) * det; - out[3] = b11 * det; - out[4] = (a22 * a00 - a02 * a20) * det; - out[5] = (-a12 * a00 + a02 * a10) * det; - out[6] = b21 * det; - out[7] = (-a21 * a00 + a01 * a20) * det; - out[8] = (a11 * a00 - a01 * a10) * det; - return out; -}; - -/** - * Calculates the adjugate of a mat3 - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the source matrix - * @returns {mat3} out - */ -mat3.adjoint = function(out, a) { - var a00 = a[0], a01 = a[1], a02 = a[2], - a10 = a[3], a11 = a[4], a12 = a[5], - a20 = a[6], a21 = a[7], a22 = a[8]; - - out[0] = (a11 * a22 - a12 * a21); - out[1] = (a02 * a21 - a01 * a22); - out[2] = (a01 * a12 - a02 * a11); - out[3] = (a12 * a20 - a10 * a22); - out[4] = (a00 * a22 - a02 * a20); - out[5] = (a02 * a10 - a00 * a12); - out[6] = (a10 * a21 - a11 * a20); - out[7] = (a01 * a20 - a00 * a21); - out[8] = (a00 * a11 - a01 * a10); - return out; -}; - -/** - * Calculates the determinant of a mat3 - * - * @param {mat3} a the source matrix - * @returns {Number} determinant of a - */ -mat3.determinant = function (a) { - var a00 = a[0], a01 = a[1], a02 = a[2], - a10 = a[3], a11 = a[4], a12 = a[5], - a20 = a[6], a21 = a[7], a22 = a[8]; - - return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20); -}; - -/** - * Multiplies two mat3's - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the first operand - * @param {mat3} b the second operand - * @returns {mat3} out - */ -mat3.multiply = function (out, a, b) { - var a00 = a[0], a01 = a[1], a02 = a[2], - a10 = a[3], a11 = a[4], a12 = a[5], - a20 = a[6], a21 = a[7], a22 = a[8], - - b00 = b[0], b01 = b[1], b02 = b[2], - b10 = b[3], b11 = b[4], b12 = b[5], - b20 = b[6], b21 = b[7], b22 = b[8]; - - out[0] = b00 * a00 + b01 * a10 + b02 * a20; - out[1] = b00 * a01 + b01 * a11 + b02 * a21; - out[2] = b00 * a02 + b01 * a12 + b02 * a22; - - out[3] = b10 * a00 + b11 * a10 + b12 * a20; - out[4] = b10 * a01 + b11 * a11 + b12 * a21; - out[5] = b10 * a02 + b11 * a12 + b12 * a22; - - out[6] = b20 * a00 + b21 * a10 + b22 * a20; - out[7] = b20 * a01 + b21 * a11 + b22 * a21; - out[8] = b20 * a02 + b21 * a12 + b22 * a22; - return out; -}; - -/** - * Alias for {@link mat3.multiply} - * @function - */ -mat3.mul = mat3.multiply; - -/** - * Translate a mat3 by the given vector - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the matrix to translate - * @param {vec2} v vector to translate by - * @returns {mat3} out - */ -mat3.translate = function(out, a, v) { - var a00 = a[0], a01 = a[1], a02 = a[2], - a10 = a[3], a11 = a[4], a12 = a[5], - a20 = a[6], a21 = a[7], a22 = a[8], - x = v[0], y = v[1]; - - out[0] = a00; - out[1] = a01; - out[2] = a02; - - out[3] = a10; - out[4] = a11; - out[5] = a12; - - out[6] = x * a00 + y * a10 + a20; - out[7] = x * a01 + y * a11 + a21; - out[8] = x * a02 + y * a12 + a22; - return out; -}; - -/** - * Rotates a mat3 by the given angle - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the matrix to rotate - * @param {Number} rad the angle to rotate the matrix by - * @returns {mat3} out - */ -mat3.rotate = function (out, a, rad) { - var a00 = a[0], a01 = a[1], a02 = a[2], - a10 = a[3], a11 = a[4], a12 = a[5], - a20 = a[6], a21 = a[7], a22 = a[8], - - s = Math.sin(rad), - c = Math.cos(rad); - - out[0] = c * a00 + s * a10; - out[1] = c * a01 + s * a11; - out[2] = c * a02 + s * a12; - - out[3] = c * a10 - s * a00; - out[4] = c * a11 - s * a01; - out[5] = c * a12 - s * a02; - - out[6] = a20; - out[7] = a21; - out[8] = a22; - return out; -}; - -/** - * Scales the mat3 by the dimensions in the given vec2 - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the matrix to rotate - * @param {vec2} v the vec2 to scale the matrix by - * @returns {mat3} out - **/ -mat3.scale = function(out, a, v) { - var x = v[0], y = v[1]; - - out[0] = x * a[0]; - out[1] = x * a[1]; - out[2] = x * a[2]; - - out[3] = y * a[3]; - out[4] = y * a[4]; - out[5] = y * a[5]; - - out[6] = a[6]; - out[7] = a[7]; - out[8] = a[8]; - return out; -}; - -/** - * Copies the values from a mat2d into a mat3 - * - * @param {mat3} out the receiving matrix - * @param {mat2d} a the matrix to copy - * @returns {mat3} out - **/ -mat3.fromMat2d = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = 0; - - out[3] = a[2]; - out[4] = a[3]; - out[5] = 0; - - out[6] = a[4]; - out[7] = a[5]; - out[8] = 1; - return out; -}; - -/** -* Calculates a 3x3 matrix from the given quaternion -* -* @param {mat3} out mat3 receiving operation result -* @param {quat} q Quaternion to create matrix from -* -* @returns {mat3} out -*/ -mat3.fromQuat = function (out, q) { - var x = q[0], y = q[1], z = q[2], w = q[3], - x2 = x + x, - y2 = y + y, - z2 = z + z, - - xx = x * x2, - yx = y * x2, - yy = y * y2, - zx = z * x2, - zy = z * y2, - zz = z * z2, - wx = w * x2, - wy = w * y2, - wz = w * z2; - - out[0] = 1 - yy - zz; - out[3] = yx - wz; - out[6] = zx + wy; - - out[1] = yx + wz; - out[4] = 1 - xx - zz; - out[7] = zy - wx; - - out[2] = zx - wy; - out[5] = zy + wx; - out[8] = 1 - xx - yy; - - return out; -}; - -/** -* Calculates a 3x3 normal matrix (transpose inverse) from the 4x4 matrix -* -* @param {mat3} out mat3 receiving operation result -* @param {mat4} a Mat4 to derive the normal matrix from -* -* @returns {mat3} out -*/ -mat3.normalFromMat4 = function (out, a) { - var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], - a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], - a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], - a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], - - b00 = a00 * a11 - a01 * a10, - b01 = a00 * a12 - a02 * a10, - b02 = a00 * a13 - a03 * a10, - b03 = a01 * a12 - a02 * a11, - b04 = a01 * a13 - a03 * a11, - b05 = a02 * a13 - a03 * a12, - b06 = a20 * a31 - a21 * a30, - b07 = a20 * a32 - a22 * a30, - b08 = a20 * a33 - a23 * a30, - b09 = a21 * a32 - a22 * a31, - b10 = a21 * a33 - a23 * a31, - b11 = a22 * a33 - a23 * a32, - - // Calculate the determinant - det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; - - if (!det) { - return null; - } - det = 1.0 / det; - - out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; - out[1] = (a12 * b08 - a10 * b11 - a13 * b07) * det; - out[2] = (a10 * b10 - a11 * b08 + a13 * b06) * det; - - out[3] = (a02 * b10 - a01 * b11 - a03 * b09) * det; - out[4] = (a00 * b11 - a02 * b08 + a03 * b07) * det; - out[5] = (a01 * b08 - a00 * b10 - a03 * b06) * det; - - out[6] = (a31 * b05 - a32 * b04 + a33 * b03) * det; - out[7] = (a32 * b02 - a30 * b05 - a33 * b01) * det; - out[8] = (a30 * b04 - a31 * b02 + a33 * b00) * det; - - return out; -}; - -/** - * Returns a string representation of a mat3 - * - * @param {mat3} mat matrix to represent as a string - * @returns {String} string representation of the matrix - */ -mat3.str = function (a) { - return 'mat3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + - a[3] + ', ' + a[4] + ', ' + a[5] + ', ' + - a[6] + ', ' + a[7] + ', ' + a[8] + ')'; -}; - -if(typeof(exports) !== 'undefined') { - exports.mat3 = mat3; -} -; -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -/** - * @class 4x4 Matrix - * @name mat4 - */ - -var mat4 = {}; - -/** - * Creates a new identity mat4 - * - * @returns {mat4} a new 4x4 matrix - */ -mat4.create = function() { - var out = new GLMAT_ARRAY_TYPE(16); - out[0] = 1; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 0; - out[5] = 1; - out[6] = 0; - out[7] = 0; - out[8] = 0; - out[9] = 0; - out[10] = 1; - out[11] = 0; - out[12] = 0; - out[13] = 0; - out[14] = 0; - out[15] = 1; - return out; -}; - -/** - * Creates a new mat4 initialized with values from an existing matrix - * - * @param {mat4} a matrix to clone - * @returns {mat4} a new 4x4 matrix - */ -mat4.clone = function(a) { - var out = new GLMAT_ARRAY_TYPE(16); - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - out[4] = a[4]; - out[5] = a[5]; - out[6] = a[6]; - out[7] = a[7]; - out[8] = a[8]; - out[9] = a[9]; - out[10] = a[10]; - out[11] = a[11]; - out[12] = a[12]; - out[13] = a[13]; - out[14] = a[14]; - out[15] = a[15]; - return out; -}; - -/** - * Copy the values from one mat4 to another - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the source matrix - * @returns {mat4} out - */ -mat4.copy = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - out[4] = a[4]; - out[5] = a[5]; - out[6] = a[6]; - out[7] = a[7]; - out[8] = a[8]; - out[9] = a[9]; - out[10] = a[10]; - out[11] = a[11]; - out[12] = a[12]; - out[13] = a[13]; - out[14] = a[14]; - out[15] = a[15]; - return out; -}; - -/** - * Set a mat4 to the identity matrix - * - * @param {mat4} out the receiving matrix - * @returns {mat4} out - */ -mat4.identity = function(out) { - out[0] = 1; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 0; - out[5] = 1; - out[6] = 0; - out[7] = 0; - out[8] = 0; - out[9] = 0; - out[10] = 1; - out[11] = 0; - out[12] = 0; - out[13] = 0; - out[14] = 0; - out[15] = 1; - return out; -}; - -/** - * Transpose the values of a mat4 - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the source matrix - * @returns {mat4} out - */ -mat4.transpose = function(out, a) { - // If we are transposing ourselves we can skip a few steps but have to cache some values - if (out === a) { - var a01 = a[1], a02 = a[2], a03 = a[3], - a12 = a[6], a13 = a[7], - a23 = a[11]; - - out[1] = a[4]; - out[2] = a[8]; - out[3] = a[12]; - out[4] = a01; - out[6] = a[9]; - out[7] = a[13]; - out[8] = a02; - out[9] = a12; - out[11] = a[14]; - out[12] = a03; - out[13] = a13; - out[14] = a23; - } else { - out[0] = a[0]; - out[1] = a[4]; - out[2] = a[8]; - out[3] = a[12]; - out[4] = a[1]; - out[5] = a[5]; - out[6] = a[9]; - out[7] = a[13]; - out[8] = a[2]; - out[9] = a[6]; - out[10] = a[10]; - out[11] = a[14]; - out[12] = a[3]; - out[13] = a[7]; - out[14] = a[11]; - out[15] = a[15]; - } - - return out; -}; - -/** - * Inverts a mat4 - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the source matrix - * @returns {mat4} out - */ -mat4.invert = function(out, a) { - var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], - a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], - a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], - a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], - - b00 = a00 * a11 - a01 * a10, - b01 = a00 * a12 - a02 * a10, - b02 = a00 * a13 - a03 * a10, - b03 = a01 * a12 - a02 * a11, - b04 = a01 * a13 - a03 * a11, - b05 = a02 * a13 - a03 * a12, - b06 = a20 * a31 - a21 * a30, - b07 = a20 * a32 - a22 * a30, - b08 = a20 * a33 - a23 * a30, - b09 = a21 * a32 - a22 * a31, - b10 = a21 * a33 - a23 * a31, - b11 = a22 * a33 - a23 * a32, - - // Calculate the determinant - det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; - - if (!det) { - return null; - } - det = 1.0 / det; - - out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; - out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; - out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; - out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; - out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; - out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; - out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; - out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; - out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; - out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; - out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; - out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; - out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; - out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; - out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; - out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; - - return out; -}; - -/** - * Calculates the adjugate of a mat4 - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the source matrix - * @returns {mat4} out - */ -mat4.adjoint = function(out, a) { - var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], - a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], - a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], - a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; - - out[0] = (a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22)); - out[1] = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22)); - out[2] = (a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12)); - out[3] = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12)); - out[4] = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22)); - out[5] = (a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22)); - out[6] = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12)); - out[7] = (a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12)); - out[8] = (a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21)); - out[9] = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21)); - out[10] = (a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11)); - out[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11)); - out[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21)); - out[13] = (a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21)); - out[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11)); - out[15] = (a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11)); - return out; -}; - -/** - * Calculates the determinant of a mat4 - * - * @param {mat4} a the source matrix - * @returns {Number} determinant of a - */ -mat4.determinant = function (a) { - var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], - a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], - a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], - a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], - - b00 = a00 * a11 - a01 * a10, - b01 = a00 * a12 - a02 * a10, - b02 = a00 * a13 - a03 * a10, - b03 = a01 * a12 - a02 * a11, - b04 = a01 * a13 - a03 * a11, - b05 = a02 * a13 - a03 * a12, - b06 = a20 * a31 - a21 * a30, - b07 = a20 * a32 - a22 * a30, - b08 = a20 * a33 - a23 * a30, - b09 = a21 * a32 - a22 * a31, - b10 = a21 * a33 - a23 * a31, - b11 = a22 * a33 - a23 * a32; - - // Calculate the determinant - return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; -}; - -/** - * Multiplies two mat4's - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the first operand - * @param {mat4} b the second operand - * @returns {mat4} out - */ -mat4.multiply = function (out, a, b) { - var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], - a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], - a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], - a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; - - // Cache only the current line of the second matrix - var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; - out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; - out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; - out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; - out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; - - b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; - out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; - out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; - out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; - out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; - - b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; - out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; - out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; - out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; - out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; - - b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; - out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; - out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; - out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; - out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; - return out; -}; - -/** - * Alias for {@link mat4.multiply} - * @function - */ -mat4.mul = mat4.multiply; - -/** - * Translate a mat4 by the given vector - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the matrix to translate - * @param {vec3} v vector to translate by - * @returns {mat4} out - */ -mat4.translate = function (out, a, v) { - var x = v[0], y = v[1], z = v[2], - a00, a01, a02, a03, - a10, a11, a12, a13, - a20, a21, a22, a23, - a30, a31, a32, a33; - - a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; - a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; - a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; - a30 = a[12]; a31 = a[13]; a32 = a[14]; a33 = a[15]; - - out[0] = a00 + a03*x; - out[1] = a01 + a03*y; - out[2] = a02 + a03*z; - out[3] = a03; - - out[4] = a10 + a13*x; - out[5] = a11 + a13*y; - out[6] = a12 + a13*z; - out[7] = a13; - - out[8] = a20 + a23*x; - out[9] = a21 + a23*y; - out[10] = a22 + a23*z; - out[11] = a23; - out[12] = a30 + a33*x; - out[13] = a31 + a33*y; - out[14] = a32 + a33*z; - out[15] = a33; - - return out; -}; -/** - * Scales the mat4 by the dimensions in the given vec3 - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the matrix to scale - * @param {vec3} v the vec3 to scale the matrix by - * @returns {mat4} out - **/ -mat4.scale = function(out, a, v) { - var x = v[0], y = v[1], z = v[2]; - - out[0] = a[0] * x; - out[1] = a[1] * x; - out[2] = a[2] * x; - out[3] = a[3] * x; - out[4] = a[4] * y; - out[5] = a[5] * y; - out[6] = a[6] * y; - out[7] = a[7] * y; - out[8] = a[8] * z; - out[9] = a[9] * z; - out[10] = a[10] * z; - out[11] = a[11] * z; - out[12] = a[12]; - out[13] = a[13]; - out[14] = a[14]; - out[15] = a[15]; - return out; -}; - -/** - * Rotates a mat4 by the given angle - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the matrix to rotate - * @param {Number} rad the angle to rotate the matrix by - * @param {vec3} axis the axis to rotate around - * @returns {mat4} out - */ -mat4.rotate = function (out, a, rad, axis) { - var x = axis[0], y = axis[1], z = axis[2], - len = Math.sqrt(x * x + y * y + z * z), - s, c, t, - a00, a01, a02, a03, - a10, a11, a12, a13, - a20, a21, a22, a23, - b00, b01, b02, - b10, b11, b12, - b20, b21, b22; - - if (Math.abs(len) < GLMAT_EPSILON) { return null; } - - len = 1 / len; - x *= len; - y *= len; - z *= len; - - s = Math.sin(rad); - c = Math.cos(rad); - t = 1 - c; - - a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; - a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; - a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; - - // Construct the elements of the rotation matrix - b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; - b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; - b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; - - // Perform rotation-specific matrix multiplication - out[0] = a00 * b00 + a10 * b01 + a20 * b02; - out[1] = a01 * b00 + a11 * b01 + a21 * b02; - out[2] = a02 * b00 + a12 * b01 + a22 * b02; - out[3] = a03 * b00 + a13 * b01 + a23 * b02; - out[4] = a00 * b10 + a10 * b11 + a20 * b12; - out[5] = a01 * b10 + a11 * b11 + a21 * b12; - out[6] = a02 * b10 + a12 * b11 + a22 * b12; - out[7] = a03 * b10 + a13 * b11 + a23 * b12; - out[8] = a00 * b20 + a10 * b21 + a20 * b22; - out[9] = a01 * b20 + a11 * b21 + a21 * b22; - out[10] = a02 * b20 + a12 * b21 + a22 * b22; - out[11] = a03 * b20 + a13 * b21 + a23 * b22; - - if (a !== out) { // If the source and destination differ, copy the unchanged last row - out[12] = a[12]; - out[13] = a[13]; - out[14] = a[14]; - out[15] = a[15]; - } - return out; -}; - -/** - * Rotates a matrix by the given angle around the X axis - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the matrix to rotate - * @param {Number} rad the angle to rotate the matrix by - * @returns {mat4} out - */ -mat4.rotateX = function (out, a, rad) { - var s = Math.sin(rad), - c = Math.cos(rad), - a10 = a[4], - a11 = a[5], - a12 = a[6], - a13 = a[7], - a20 = a[8], - a21 = a[9], - a22 = a[10], - a23 = a[11]; - - if (a !== out) { // If the source and destination differ, copy the unchanged rows - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - out[12] = a[12]; - out[13] = a[13]; - out[14] = a[14]; - out[15] = a[15]; - } - - // Perform axis-specific matrix multiplication - out[4] = a10 * c + a20 * s; - out[5] = a11 * c + a21 * s; - out[6] = a12 * c + a22 * s; - out[7] = a13 * c + a23 * s; - out[8] = a20 * c - a10 * s; - out[9] = a21 * c - a11 * s; - out[10] = a22 * c - a12 * s; - out[11] = a23 * c - a13 * s; - return out; -}; - -/** - * Rotates a matrix by the given angle around the Y axis - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the matrix to rotate - * @param {Number} rad the angle to rotate the matrix by - * @returns {mat4} out - */ -mat4.rotateY = function (out, a, rad) { - var s = Math.sin(rad), - c = Math.cos(rad), - a00 = a[0], - a01 = a[1], - a02 = a[2], - a03 = a[3], - a20 = a[8], - a21 = a[9], - a22 = a[10], - a23 = a[11]; - - if (a !== out) { // If the source and destination differ, copy the unchanged rows - out[4] = a[4]; - out[5] = a[5]; - out[6] = a[6]; - out[7] = a[7]; - out[12] = a[12]; - out[13] = a[13]; - out[14] = a[14]; - out[15] = a[15]; - } - - // Perform axis-specific matrix multiplication - out[0] = a00 * c - a20 * s; - out[1] = a01 * c - a21 * s; - out[2] = a02 * c - a22 * s; - out[3] = a03 * c - a23 * s; - out[8] = a00 * s + a20 * c; - out[9] = a01 * s + a21 * c; - out[10] = a02 * s + a22 * c; - out[11] = a03 * s + a23 * c; - return out; -}; - -/** - * Rotates a matrix by the given angle around the Z axis - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the matrix to rotate - * @param {Number} rad the angle to rotate the matrix by - * @returns {mat4} out - */ -mat4.rotateZ = function (out, a, rad) { - var s = Math.sin(rad), - c = Math.cos(rad), - a00 = a[0], - a01 = a[1], - a02 = a[2], - a03 = a[3], - a10 = a[4], - a11 = a[5], - a12 = a[6], - a13 = a[7]; - - if (a !== out) { // If the source and destination differ, copy the unchanged last row - out[8] = a[8]; - out[9] = a[9]; - out[10] = a[10]; - out[11] = a[11]; - out[12] = a[12]; - out[13] = a[13]; - out[14] = a[14]; - out[15] = a[15]; - } - - // Perform axis-specific matrix multiplication - out[0] = a00 * c + a10 * s; - out[1] = a01 * c + a11 * s; - out[2] = a02 * c + a12 * s; - out[3] = a03 * c + a13 * s; - out[4] = a10 * c - a00 * s; - out[5] = a11 * c - a01 * s; - out[6] = a12 * c - a02 * s; - out[7] = a13 * c - a03 * s; - return out; -}; - -/** - * Creates a matrix from a quaternion rotation and vector translation - * This is equivalent to (but much faster than): - * - * mat4.identity(dest); - * mat4.translate(dest, vec); - * var quatMat = mat4.create(); - * quat4.toMat4(quat, quatMat); - * mat4.multiply(dest, quatMat); - * - * @param {mat4} out mat4 receiving operation result - * @param {quat4} q Rotation quaternion - * @param {vec3} v Translation vector - * @returns {mat4} out - */ -mat4.fromRotationTranslation = function (out, q, v) { - // Quaternion math - var x = q[0], y = q[1], z = q[2], w = q[3], - x2 = x + x, - y2 = y + y, - z2 = z + z, - - xx = x * x2, - xy = x * y2, - xz = x * z2, - yy = y * y2, - yz = y * z2, - zz = z * z2, - wx = w * x2, - wy = w * y2, - wz = w * z2; - - out[0] = 1 - (yy + zz); - out[1] = xy + wz; - out[2] = xz - wy; - out[3] = 0; - out[4] = xy - wz; - out[5] = 1 - (xx + zz); - out[6] = yz + wx; - out[7] = 0; - out[8] = xz + wy; - out[9] = yz - wx; - out[10] = 1 - (xx + yy); - out[11] = 0; - out[12] = v[0]; - out[13] = v[1]; - out[14] = v[2]; - out[15] = 1; - - return out; -}; - -mat4.fromQuat = function (out, q) { - var x = q[0], y = q[1], z = q[2], w = q[3], - x2 = x + x, - y2 = y + y, - z2 = z + z, - - xx = x * x2, - yx = y * x2, - yy = y * y2, - zx = z * x2, - zy = z * y2, - zz = z * z2, - wx = w * x2, - wy = w * y2, - wz = w * z2; - - out[0] = 1 - yy - zz; - out[1] = yx + wz; - out[2] = zx - wy; - out[3] = 0; - - out[4] = yx - wz; - out[5] = 1 - xx - zz; - out[6] = zy + wx; - out[7] = 0; - - out[8] = zx + wy; - out[9] = zy - wx; - out[10] = 1 - xx - yy; - out[11] = 0; - - out[12] = 0; - out[13] = 0; - out[14] = 0; - out[15] = 1; - - return out; -}; - -/** - * Generates a frustum matrix with the given bounds - * - * @param {mat4} out mat4 frustum matrix will be written into - * @param {Number} left Left bound of the frustum - * @param {Number} right Right bound of the frustum - * @param {Number} bottom Bottom bound of the frustum - * @param {Number} top Top bound of the frustum - * @param {Number} near Near bound of the frustum - * @param {Number} far Far bound of the frustum - * @returns {mat4} out - */ -mat4.frustum = function (out, left, right, bottom, top, near, far) { - var rl = 1 / (right - left), - tb = 1 / (top - bottom), - nf = 1 / (near - far); - out[0] = (near * 2) * rl; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 0; - out[5] = (near * 2) * tb; - out[6] = 0; - out[7] = 0; - out[8] = (right + left) * rl; - out[9] = (top + bottom) * tb; - out[10] = (far + near) * nf; - out[11] = -1; - out[12] = 0; - out[13] = 0; - out[14] = (far * near * 2) * nf; - out[15] = 0; - return out; -}; - -/** - * Generates a perspective projection matrix with the given bounds - * - * @param {mat4} out mat4 frustum matrix will be written into - * @param {number} fovy Vertical field of view in radians - * @param {number} aspect Aspect ratio. typically viewport width/height - * @param {number} near Near bound of the frustum - * @param {number} far Far bound of the frustum - * @returns {mat4} out - */ -mat4.perspective = function (out, fovy, aspect, near, far) { - var f = 1.0 / Math.tan(fovy / 2), - nf = 1 / (near - far); - out[0] = f / aspect; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 0; - out[5] = f; - out[6] = 0; - out[7] = 0; - out[8] = 0; - out[9] = 0; - out[10] = (far + near) * nf; - out[11] = -1; - out[12] = 0; - out[13] = 0; - out[14] = (2 * far * near) * nf; - out[15] = 0; - return out; -}; - -/** - * Generates a orthogonal projection matrix with the given bounds - * - * @param {mat4} out mat4 frustum matrix will be written into - * @param {number} left Left bound of the frustum - * @param {number} right Right bound of the frustum - * @param {number} bottom Bottom bound of the frustum - * @param {number} top Top bound of the frustum - * @param {number} near Near bound of the frustum - * @param {number} far Far bound of the frustum - * @returns {mat4} out - */ -mat4.ortho = function (out, left, right, bottom, top, near, far) { - var lr = 1 / (left - right), - bt = 1 / (bottom - top), - nf = 1 / (near - far); - out[0] = -2 * lr; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 0; - out[5] = -2 * bt; - out[6] = 0; - out[7] = 0; - out[8] = 0; - out[9] = 0; - out[10] = 2 * nf; - out[11] = 0; - out[12] = (left + right) * lr; - out[13] = (top + bottom) * bt; - out[14] = (far + near) * nf; - out[15] = 1; - return out; -}; - -/** - * Generates a look-at matrix with the given eye position, focal point, and up axis - * - * @param {mat4} out mat4 frustum matrix will be written into - * @param {vec3} eye Position of the viewer - * @param {vec3} center Point the viewer is looking at - * @param {vec3} up vec3 pointing up - * @returns {mat4} out - */ -mat4.lookAt = function (out, eye, center, up) { - var x0, x1, x2, y0, y1, y2, z0, z1, z2, len, - eyex = eye[0], - eyey = eye[1], - eyez = eye[2], - upx = up[0], - upy = up[1], - upz = up[2], - centerx = center[0], - centery = center[1], - centerz = center[2]; - - if (Math.abs(eyex - centerx) < GLMAT_EPSILON && - Math.abs(eyey - centery) < GLMAT_EPSILON && - Math.abs(eyez - centerz) < GLMAT_EPSILON) { - return mat4.identity(out); - } - - z0 = eyex - centerx; - z1 = eyey - centery; - z2 = eyez - centerz; - - len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); - z0 *= len; - z1 *= len; - z2 *= len; - - 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; - } - - 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; - } - - out[0] = x0; - out[1] = y0; - out[2] = z0; - out[3] = 0; - out[4] = x1; - out[5] = y1; - out[6] = z1; - out[7] = 0; - out[8] = x2; - out[9] = y2; - out[10] = z2; - out[11] = 0; - out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); - out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); - out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); - out[15] = 1; - - return out; -}; - -/** - * Returns a string representation of a mat4 - * - * @param {mat4} mat matrix to represent as a string - * @returns {String} string representation of the matrix - */ -mat4.str = function (a) { - return 'mat4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ', ' + - a[4] + ', ' + a[5] + ', ' + a[6] + ', ' + a[7] + ', ' + - a[8] + ', ' + a[9] + ', ' + a[10] + ', ' + a[11] + ', ' + - a[12] + ', ' + a[13] + ', ' + a[14] + ', ' + a[15] + ')'; -}; - -if(typeof(exports) !== 'undefined') { - exports.mat4 = mat4; -} -; -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -/** - * @class Quaternion - * @name quat - */ - -var quat = {}; - -/** - * Creates a new identity quat - * - * @returns {quat} a new quaternion - */ -quat.create = function() { - var out = new GLMAT_ARRAY_TYPE(4); - out[0] = 0; - out[1] = 0; - out[2] = 0; - out[3] = 1; - return out; -}; - -/** - * Sets a quaternion to represent the shortest rotation from one - * vector to another. - * - * Both vectors are assumed to be unit length. - * - * @param {quat} out the receiving quaternion. - * @param {vec3} a the initial vector - * @param {vec3} b the destination vector - * @returns {quat} out - */ -quat.rotationTo = (function() { - var tmpvec3 = vec3.create(); - var xUnitVec3 = vec3.fromValues(1,0,0); - var yUnitVec3 = vec3.fromValues(0,1,0); - - return function(out, a, b) { - var dot = vec3.dot(a, b); - if (dot < -0.999999) { - vec3.cross(tmpvec3, xUnitVec3, a); - if (vec3.length(tmpvec3) < 0.000001) - vec3.cross(tmpvec3, yUnitVec3, a); - vec3.normalize(tmpvec3, tmpvec3); - quat.setAxisAngle(out, tmpvec3, Math.PI); - return out; - } else if (dot > 0.999999) { - out[0] = 0; - out[1] = 0; - out[2] = 0; - out[3] = 1; - return out; - } else { - vec3.cross(tmpvec3, a, b); - out[0] = tmpvec3[0]; - out[1] = tmpvec3[1]; - out[2] = tmpvec3[2]; - out[3] = 1 + dot; - return quat.normalize(out, out); - } - }; -})(); - -/** - * Sets the specified quaternion with values corresponding to the given - * axes. Each axis is a vec3 and is expected to be unit length and - * perpendicular to all other specified axes. - * - * @param {vec3} view the vector representing the viewing direction - * @param {vec3} right the vector representing the local "right" direction - * @param {vec3} up the vector representing the local "up" direction - * @returns {quat} out - */ -quat.setAxes = (function() { - var matr = mat3.create(); - - return function(out, view, right, up) { - matr[0] = right[0]; - matr[3] = right[1]; - matr[6] = right[2]; - - matr[1] = up[0]; - matr[4] = up[1]; - matr[7] = up[2]; - - matr[2] = -view[0]; - matr[5] = -view[1]; - matr[8] = -view[2]; - - return quat.normalize(out, quat.fromMat3(out, matr)); - }; -})(); - -/** - * Creates a new quat initialized with values from an existing quaternion - * - * @param {quat} a quaternion to clone - * @returns {quat} a new quaternion - * @function - */ -quat.clone = vec4.clone; - -/** - * Creates a new quat initialized with the given values - * - * @param {Number} x X component - * @param {Number} y Y component - * @param {Number} z Z component - * @param {Number} w W component - * @returns {quat} a new quaternion - * @function - */ -quat.fromValues = vec4.fromValues; - -/** - * Copy the values from one quat to another - * - * @param {quat} out the receiving quaternion - * @param {quat} a the source quaternion - * @returns {quat} out - * @function - */ -quat.copy = vec4.copy; - -/** - * Set the components of a quat to the given values - * - * @param {quat} out the receiving quaternion - * @param {Number} x X component - * @param {Number} y Y component - * @param {Number} z Z component - * @param {Number} w W component - * @returns {quat} out - * @function - */ -quat.set = vec4.set; - -/** - * Set a quat to the identity quaternion - * - * @param {quat} out the receiving quaternion - * @returns {quat} out - */ -quat.identity = function(out) { - out[0] = 0; - out[1] = 0; - out[2] = 0; - out[3] = 1; - return out; -}; - -/** - * Sets a quat from the given angle and rotation axis, - * then returns it. - * - * @param {quat} out the receiving quaternion - * @param {vec3} axis the axis around which to rotate - * @param {Number} rad the angle in radians - * @returns {quat} out - **/ -quat.setAxisAngle = function(out, axis, rad) { - rad = rad * 0.5; - var s = Math.sin(rad); - out[0] = s * axis[0]; - out[1] = s * axis[1]; - out[2] = s * axis[2]; - out[3] = Math.cos(rad); - return out; -}; - -/** - * Adds two quat's - * - * @param {quat} out the receiving quaternion - * @param {quat} a the first operand - * @param {quat} b the second operand - * @returns {quat} out - * @function - */ -quat.add = vec4.add; - -/** - * Multiplies two quat's - * - * @param {quat} out the receiving quaternion - * @param {quat} a the first operand - * @param {quat} b the second operand - * @returns {quat} out - */ -quat.multiply = function(out, a, b) { - var ax = a[0], ay = a[1], az = a[2], aw = a[3], - bx = b[0], by = b[1], bz = b[2], bw = b[3]; - - out[0] = ax * bw + aw * bx + ay * bz - az * by; - out[1] = ay * bw + aw * by + az * bx - ax * bz; - out[2] = az * bw + aw * bz + ax * by - ay * bx; - out[3] = aw * bw - ax * bx - ay * by - az * bz; - return out; -}; - -/** - * Alias for {@link quat.multiply} - * @function - */ -quat.mul = quat.multiply; - -/** - * Scales a quat by a scalar number - * - * @param {quat} out the receiving vector - * @param {quat} a the vector to scale - * @param {Number} b amount to scale the vector by - * @returns {quat} out - * @function - */ -quat.scale = vec4.scale; - -/** - * Rotates a quaternion by the given angle about the X axis - * - * @param {quat} out quat receiving operation result - * @param {quat} a quat to rotate - * @param {number} rad angle (in radians) to rotate - * @returns {quat} out - */ -quat.rotateX = function (out, a, rad) { - rad *= 0.5; - - var ax = a[0], ay = a[1], az = a[2], aw = a[3], - bx = Math.sin(rad), bw = Math.cos(rad); - - out[0] = ax * bw + aw * bx; - out[1] = ay * bw + az * bx; - out[2] = az * bw - ay * bx; - out[3] = aw * bw - ax * bx; - return out; -}; - -/** - * Rotates a quaternion by the given angle about the Y axis - * - * @param {quat} out quat receiving operation result - * @param {quat} a quat to rotate - * @param {number} rad angle (in radians) to rotate - * @returns {quat} out - */ -quat.rotateY = function (out, a, rad) { - rad *= 0.5; - - var ax = a[0], ay = a[1], az = a[2], aw = a[3], - by = Math.sin(rad), bw = Math.cos(rad); - - out[0] = ax * bw - az * by; - out[1] = ay * bw + aw * by; - out[2] = az * bw + ax * by; - out[3] = aw * bw - ay * by; - return out; -}; - -/** - * Rotates a quaternion by the given angle about the Z axis - * - * @param {quat} out quat receiving operation result - * @param {quat} a quat to rotate - * @param {number} rad angle (in radians) to rotate - * @returns {quat} out - */ -quat.rotateZ = function (out, a, rad) { - rad *= 0.5; - - var ax = a[0], ay = a[1], az = a[2], aw = a[3], - bz = Math.sin(rad), bw = Math.cos(rad); - - out[0] = ax * bw + ay * bz; - out[1] = ay * bw - ax * bz; - out[2] = az * bw + aw * bz; - out[3] = aw * bw - az * bz; - return out; -}; - -/** - * Calculates the W component of a quat from the X, Y, and Z components. - * Assumes that quaternion is 1 unit in length. - * Any existing W component will be ignored. - * - * @param {quat} out the receiving quaternion - * @param {quat} a quat to calculate W component of - * @returns {quat} out - */ -quat.calculateW = function (out, a) { - var x = a[0], y = a[1], z = a[2]; - - out[0] = x; - out[1] = y; - out[2] = z; - out[3] = -Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z)); - return out; -}; - -/** - * Calculates the dot product of two quat's - * - * @param {quat} a the first operand - * @param {quat} b the second operand - * @returns {Number} dot product of a and b - * @function - */ -quat.dot = vec4.dot; - -/** - * Performs a linear interpolation between two quat's - * - * @param {quat} out the receiving quaternion - * @param {quat} a the first operand - * @param {quat} b the second operand - * @param {Number} t interpolation amount between the two inputs - * @returns {quat} out - * @function - */ -quat.lerp = vec4.lerp; - -/** - * Performs a spherical linear interpolation between two quat - * - * @param {quat} out the receiving quaternion - * @param {quat} a the first operand - * @param {quat} b the second operand - * @param {Number} t interpolation amount between the two inputs - * @returns {quat} out - */ -quat.slerp = function (out, a, b, t) { - // benchmarks: - // http://jsperf.com/quaternion-slerp-implementations - - var ax = a[0], ay = a[1], az = a[2], aw = a[3], - bx = b[0], by = b[1], bz = b[2], bw = b[3]; - - var omega, cosom, sinom, scale0, scale1; - - // calc cosine - cosom = ax * bx + ay * by + az * bz + aw * bw; - // adjust signs (if necessary) - if ( cosom < 0.0 ) { - cosom = -cosom; - bx = - bx; - by = - by; - bz = - bz; - bw = - bw; - } - // calculate coefficients - if ( (1.0 - cosom) > 0.000001 ) { - // standard case (slerp) - omega = Math.acos(cosom); - sinom = Math.sin(omega); - scale0 = Math.sin((1.0 - t) * omega) / sinom; - scale1 = Math.sin(t * omega) / sinom; - } else { - // "from" and "to" quaternions are very close - // ... so we can do a linear interpolation - scale0 = 1.0 - t; - scale1 = t; - } - // calculate final values - out[0] = scale0 * ax + scale1 * bx; - out[1] = scale0 * ay + scale1 * by; - out[2] = scale0 * az + scale1 * bz; - out[3] = scale0 * aw + scale1 * bw; - - return out; -}; - -/** - * Calculates the inverse of a quat - * - * @param {quat} out the receiving quaternion - * @param {quat} a quat to calculate inverse of - * @returns {quat} out - */ -quat.invert = function(out, a) { - var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], - dot = a0*a0 + a1*a1 + a2*a2 + a3*a3, - invDot = dot ? 1.0/dot : 0; - - // TODO: Would be faster to return [0,0,0,0] immediately if dot == 0 - - out[0] = -a0*invDot; - out[1] = -a1*invDot; - out[2] = -a2*invDot; - out[3] = a3*invDot; - return out; -}; - -/** - * Calculates the conjugate of a quat - * If the quaternion is normalized, this function is faster than quat.inverse and produces the same result. - * - * @param {quat} out the receiving quaternion - * @param {quat} a quat to calculate conjugate of - * @returns {quat} out - */ -quat.conjugate = function (out, a) { - out[0] = -a[0]; - out[1] = -a[1]; - out[2] = -a[2]; - out[3] = a[3]; - return out; -}; - -/** - * Calculates the length of a quat - * - * @param {quat} a vector to calculate length of - * @returns {Number} length of a - * @function - */ -quat.length = vec4.length; - -/** - * Alias for {@link quat.length} - * @function - */ -quat.len = quat.length; - -/** - * Calculates the squared length of a quat - * - * @param {quat} a vector to calculate squared length of - * @returns {Number} squared length of a - * @function - */ -quat.squaredLength = vec4.squaredLength; - -/** - * Alias for {@link quat.squaredLength} - * @function - */ -quat.sqrLen = quat.squaredLength; - -/** - * Normalize a quat - * - * @param {quat} out the receiving quaternion - * @param {quat} a quaternion to normalize - * @returns {quat} out - * @function - */ -quat.normalize = vec4.normalize; - -/** - * Creates a quaternion from the given 3x3 rotation matrix. - * - * NOTE: The resultant quaternion is not normalized, so you should be sure - * to renormalize the quaternion yourself where necessary. - * - * @param {quat} out the receiving quaternion - * @param {mat3} m rotation matrix - * @returns {quat} out - * @function - */ -quat.fromMat3 = function(out, m) { - // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes - // article "Quaternion Calculus and Fast Animation". - var fTrace = m[0] + m[4] + m[8]; - var fRoot; - - if ( fTrace > 0.0 ) { - // |w| > 1/2, may as well choose w > 1/2 - fRoot = Math.sqrt(fTrace + 1.0); // 2w - out[3] = 0.5 * fRoot; - fRoot = 0.5/fRoot; // 1/(4w) - out[0] = (m[7]-m[5])*fRoot; - out[1] = (m[2]-m[6])*fRoot; - out[2] = (m[3]-m[1])*fRoot; - } else { - // |w| <= 1/2 - var i = 0; - if ( m[4] > m[0] ) - i = 1; - if ( m[8] > m[i*3+i] ) - i = 2; - var j = (i+1)%3; - var k = (i+2)%3; - - fRoot = Math.sqrt(m[i*3+i]-m[j*3+j]-m[k*3+k] + 1.0); - out[i] = 0.5 * fRoot; - fRoot = 0.5 / fRoot; - out[3] = (m[k*3+j] - m[j*3+k]) * fRoot; - out[j] = (m[j*3+i] + m[i*3+j]) * fRoot; - out[k] = (m[k*3+i] + m[i*3+k]) * fRoot; - } - - return out; -}; - -/** - * Returns a string representation of a quatenion - * - * @param {quat} vec vector to represent as a string - * @returns {String} string representation of the vector - */ -quat.str = function (a) { - return 'quat(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; -}; - -if(typeof(exports) !== 'undefined') { - exports.quat = quat; -} -; - - - - - - - - - - - - - - })(shim.exports); -})(this); - -define('qtek/math/Vector3',['require','../dep/glmatrix'],function(require) { - - - - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - - /** - * @constructor - * @alias qtek.math.Vector3 - * @param {number} x - * @param {number} y - * @param {number} z - */ - var Vector3 = function(x, y, z) { - - x = x || 0; - y = y || 0; - z = z || 0; - - /** - * Storage of Vector3, read and write of x, y, z will change the values in _array - * All methods also operate on the _array instead of x, y, z components - * @type {Float32Array} - */ - this._array = vec3.fromValues(x, y, z); - - /** - * Dirty flag is used by the Node to determine - * if the matrix is updated to latest - * @type {boolean} - */ - this._dirty = true; - }; - - Vector3.prototype= { - - constructor : Vector3, - - /** - * Add b to self - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - add : function(b) { - vec3.add(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Set x, y and z components - * @param {number} x - * @param {number} y - * @param {number} z - * @return {qtek.math.Vector3} - */ - set : function(x, y, z) { - this._array[0] = x; - this._array[1] = y; - this._array[2] = z; - this._dirty = true; - return this; - }, - - /** - * Set x, y and z components from array - * @param {Float32Array|number[]} arr - * @return {qtek.math.Vector3} - */ - setArray : function(arr) { - this._array[0] = arr[0]; - this._array[1] = arr[1]; - this._array[2] = arr[2]; - - this._dirty = true; - return this; - }, - - /** - * Clone a new Vector3 - * @return {qtek.math.Vector3} - */ - clone : function() { - return new Vector3(this.x, this.y, this.z); - }, - - /** - * Copy from b - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - copy : function(b) { - vec3.copy(this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Cross product of self and b, written to a Vector3 out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - cross : function(a, b) { - vec3.cross(this._array, a._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Alias for distance - * @param {qtek.math.Vector3} b - * @return {number} - */ - dist : function(b) { - return vec3.dist(this._array, b._array); - }, - - /** - * Distance between self and b - * @param {qtek.math.Vector3} b - * @return {number} - */ - distance : function(b) { - return vec3.distance(this._array, b._array); - }, - - /** - * Alias for divide - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - div : function(b) { - vec3.div(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Divide self by b - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - divide : function(b) { - vec3.divide(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Dot product of self and b - * @param {qtek.math.Vector3} b - * @return {number} - */ - dot : function(b) { - return vec3.dot(this._array, b._array); - }, - - /** - * Alias of length - * @return {number} - */ - len : function() { - return vec3.len(this._array); - }, - - /** - * Calculate the length - * @return {number} - */ - length : function() { - return vec3.length(this._array); - }, - /** - * Linear interpolation between a and b - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @param {number} t - * @return {qtek.math.Vector3} - */ - lerp : function(a, b, t) { - vec3.lerp(this._array, a._array, b._array, t); - this._dirty = true; - return this; - }, - - /** - * Minimum of self and b - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - min : function(b) { - vec3.min(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Maximum of self and b - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - max : function(b) { - vec3.max(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Alias for multiply - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - mul : function(b) { - vec3.mul(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Mutiply self and b - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - multiply : function(b) { - vec3.multiply(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Negate self - * @return {qtek.math.Vector3} - */ - negate : function() { - vec3.negate(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Normalize self - * @return {qtek.math.Vector3} - */ - normalize : function() { - vec3.normalize(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Generate random x, y, z components with a given scale - * @param {number} scale - * @return {qtek.math.Vector3} - */ - random : function(scale) { - vec3.random(this._array, scale); - this._dirty = true; - return this; - }, - - /** - * Scale self - * @param {number} scale - * @return {qtek.math.Vector3} - */ - scale : function(s) { - vec3.scale(this._array, this._array, s); - this._dirty = true; - return this; - }, - - /** - * Scale b and add to self - * @param {qtek.math.Vector3} b - * @param {number} scale - * @return {qtek.math.Vector3} - */ - scaleAndAdd : function(b, s) { - vec3.scaleAndAdd(this._array, this._array, b._array, s); - this._dirty = true; - return this; - }, - - /** - * Alias for squaredDistance - * @param {qtek.math.Vector3} b - * @return {number} - */ - sqrDist : function(b) { - return vec3.sqrDist(this._array, b._array); - }, - - /** - * Squared distance between self and b - * @param {qtek.math.Vector3} b - * @return {number} - */ - squaredDistance : function(b) { - return vec3.squaredDistance(this._array, b._array); - }, - - /** - * Alias for squaredLength - * @return {number} - */ - sqrLen : function() { - return vec3.sqrLen(this._array); - }, - - /** - * Squared length of self - * @return {number} - */ - squaredLength : function() { - return vec3.squaredLength(this._array); - }, - - /** - * Alias for subtract - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - sub : function(b) { - vec3.sub(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Subtract b from self - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - subtract : function(b) { - vec3.subtract(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Transform self with a Matrix3 m - * @param {qtek.math.Matrix3} m - * @return {qtek.math.Vector3} - */ - transformMat3 : function(m) { - vec3.transformMat3(this._array, this._array, m._array); - this._dirty = true; - return this; - }, - - /** - * Transform self with a Matrix4 m - * @param {qtek.math.Matrix4} m - * @return {qtek.math.Vector3} - */ - transformMat4 : function(m) { - vec3.transformMat4(this._array, this._array, m._array); - this._dirty = true; - return this; - }, - /** - * Transform self with a Quaternion q - * @param {qtek.math.Quaternion} q - * @return {qtek.math.Vector3} - */ - transformQuat : function(q) { - vec3.transformQuat(this._array, this._array, q._array); - this._dirty = true; - return this; - }, - - /** - * Trasnform self into projection space with m - * @param {qtek.math.Matrix4} m - * @return {qtek.math.Vector3} - */ - applyProjection : function(m) { - var v = this._array; - m = m._array; - - // Perspective projection - if (m[15] === 0) { - var w = -1 / v[2]; - v[0] = m[0] * v[0] * w; - v[1] = m[5] * v[1] * w; - v[2] = (m[10] * v[2] + m[14]) * w; - } else { - v[0] = m[0] * v[0] + m[12]; - v[1] = m[5] * v[1] + m[13]; - v[2] = m[10] * v[2] + m[14]; - } - this._dirty = true; - - return this; - }, - - eulerFromQuaternion : function(q, order) { - Vector3.eulerFromQuaternion(this, q, order); - }, - - toString : function() { - return '[' + Array.prototype.join.call(this._array, ',') + ']'; - }, - }; - - // Getter and Setter - if (Object.defineProperty) { - - var proto = Vector3.prototype; - /** - * @name x - * @type {number} - * @memberOf qtek.math.Vector3 - * @instance - */ - Object.defineProperty(proto, 'x', { - get: function () { - return this._array[0]; - }, - set: function (value) { - this._array[0] = value; - this._dirty = true; - } - }); - - /** - * @name y - * @type {number} - * @memberOf qtek.math.Vector3 - * @instance - */ - Object.defineProperty(proto, 'y', { - get: function () { - return this._array[1]; - }, - set: function (value) { - this._array[1] = value; - this._dirty = true; - } - }); - - /** - * @name z - * @type {number} - * @memberOf qtek.math.Vector3 - * @instance - */ - Object.defineProperty(proto, 'z', { - get: function () { - return this._array[2]; - }, - set: function (value) { - this._array[2] = value; - this._dirty = true; - } - }); - } - - - // Supply methods that are not in place - - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - Vector3.add = function(out, a, b) { - vec3.add(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector3} out - * @param {number} x - * @param {number} y - * @param {number} z - * @return {qtek.math.Vector3} - */ - Vector3.set = function(out, x, y, z) { - vec3.set(out._array, x, y, z); - out._dirty = true; - }; - - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - Vector3.copy = function(out, b) { - vec3.copy(out._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - Vector3.cross = function(out, a, b) { - vec3.cross(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {number} - */ - Vector3.dist = function(a, b) { - return vec3.distance(a._array, b._array); - }; - - /** - * @method - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {number} - */ - Vector3.distance = Vector3.dist; - - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - Vector3.div = function(out, a, b) { - vec3.divide(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @method - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - Vector3.divide = Vector3.div; - - /** - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {number} - */ - Vector3.dot = function(a, b) { - return vec3.dot(a._array, b._array); - }; - - /** - * @param {qtek.math.Vector3} a - * @return {number} - */ - Vector3.len = function(b) { - return vec3.length(b._array); - }; - - // Vector3.length = Vector3.len; - - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @param {number} t - * @return {qtek.math.Vector3} - */ - Vector3.lerp = function(out, a, b, t) { - vec3.lerp(out._array, a._array, b._array, t); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - Vector3.min = function(out, a, b) { - vec3.min(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - Vector3.max = function(out, a, b) { - vec3.max(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - Vector3.mul = function(out, a, b) { - vec3.multiply(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - /** - * @method - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - Vector3.multiply = Vector3.mul; - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @return {qtek.math.Vector3} - */ - Vector3.negate = function(out, a) { - vec3.negate(out._array, a._array); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @return {qtek.math.Vector3} - */ - Vector3.normalize = function(out, a) { - vec3.normalize(out._array, a._array); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector3} out - * @param {number} scale - * @return {qtek.math.Vector3} - */ - Vector3.random = function(out, scale) { - vec3.random(out._array, scale); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {number} scale - * @return {qtek.math.Vector3} - */ - Vector3.scale = function(out, a, scale) { - vec3.scale(out._array, a._array, scale); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @param {number} scale - * @return {qtek.math.Vector3} - */ - Vector3.scaleAndAdd = function(out, a, b, scale) { - vec3.scaleAndAdd(out._array, a._array, b._array, scale); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {number} - */ - Vector3.sqrDist = function(a, b) { - return vec3.sqrDist(a._array, b._array); - }; - /** - * @method - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {number} - */ - Vector3.squaredDistance = Vector3.sqrDist; - /** - * @param {qtek.math.Vector3} a - * @return {number} - */ - Vector3.sqrLen = function(a) { - return vec3.sqrLen(a._array); - }; - /** - * @method - * @param {qtek.math.Vector3} a - * @return {number} - */ - Vector3.squaredLength = Vector3.sqrLen; - - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - Vector3.sub = function(out, a, b) { - vec3.subtract(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - /** - * @method - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - Vector3.subtract = Vector3.sub; - - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {Matrix3} m - * @return {qtek.math.Vector3} - */ - Vector3.transformMat3 = function(out, a, m) { - vec3.transformMat3(out._array, a._array, m._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Matrix4} m - * @return {qtek.math.Vector3} - */ - Vector3.transformMat4 = function(out, a, m) { - vec3.transformMat4(out._array, a._array, m._array); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Quaternion} q - * @return {qtek.math.Vector3} - */ - Vector3.transformQuat = function(out, a, q) { - vec3.transformQuat(out._array, a._array, q._array); - out._dirty = true; - return out; - }; - - function clamp(val, min, max) { - return val < min ? min : (val > max ? max : val); - }; - /** - * Convert quaternion to euler angle - * Quaternion must be normalized - * From three.js - */ - Vector3.eulerFromQuaternion = function (v, q, order) { - v = v._array; - q = q._array; - var x = q[0], y = q[1], z = q[2], w = q[3]; - var x2 = x * x; - var y2 = y * y; - var z2 = z * z; - var w2 = w * w; - var atan2 = Math.atan2; - var asin = Math.asin; - switch (order && order.toUpperCase()) { - case 'YXZ': - v[0] = asin(clamp(2 * (x * w - y * z), - 1, 1)); - v[1] = atan2(2 * (x * z + y * w), (w2 - x2 - y2 + z2)); - v[2] = atan2(2 * (x * y + z * w), (w2 - x2 + y2 - z2)); - break; - case 'ZXY': - v[0] = asin(clamp(2 * (x * w + y * z), - 1, 1)); - v[1] = atan2(2 * (y * w - z * x), (w2 - x2 - y2 + z2)); - v[2] = atan2(2 * (z * w - x * y), (w2 - x2 + y2 - z2)); - break; - case 'ZYX': - v[0] = atan2(2 * (x * w + z * y), (w2 - x2 - y2 + z2)); - v[1] = asin(clamp(2 * (y * w - x * z), - 1, 1)); - v[2] = atan2(2 * (x * y + z * w), (w2 + x2 - y2 - z2)); - break; - case 'YZX': - v[0] = atan2(2 * (x * w - z * y), (w2 - x2 + y2 - z2)); - v[1] = atan2(2 * (y * w - x * z), (w2 + x2 - y2 - z2)); - v[2] = asin(clamp(2 * (x * y + z * w), - 1, 1)); - break; - case 'XZY': - v[0] = atan2(2 * (x * w + y * z), (w2 - x2 + y2 - z2)); - v[1] = atan2(2 * (x * z + y * w), (w2 + x2 - y2 - z2)); - v[2] = asin(clamp(2 * (z * w - x * y), - 1, 1)); - break; - case 'XYZ': - default: - v[0] = atan2(2 * (x * w - y * z), (w2 - x2 - y2 + z2)); - v[1] = asin(clamp(2 * (x * z + y * w), - 1, 1)); - v[2] = atan2(2 * (z * w - x * y), (w2 + x2 - y2 - z2)); - break; - } - v._dirty = true; - return v; - }; - - /** - * @type {qtek.math.Vector3} - */ - Vector3.POSITIVE_X = new Vector3(1, 0, 0); - /** - * @type {qtek.math.Vector3} - */ - Vector3.NEGATIVE_X = new Vector3(-1, 0, 0); - /** - * @type {qtek.math.Vector3} - */ - Vector3.POSITIVE_Y = new Vector3(0, 1, 0); - /** - * @type {qtek.math.Vector3} - */ - Vector3.NEGATIVE_Y = new Vector3(0, -1, 0); - /** - * @type {qtek.math.Vector3} - */ - Vector3.POSITIVE_Z = new Vector3(0, 0, 1); - /** - * @type {qtek.math.Vector3} - */ - Vector3.NEGATIVE_Z = new Vector3(0, 0, -1); - /** - * @type {qtek.math.Vector3} - */ - Vector3.UP = new Vector3(0, 1, 0); - /** - * @type {qtek.math.Vector3} - */ - Vector3.ZERO = new Vector3(0, 0, 0); - - return Vector3; -}); -define('qtek/math/Quaternion',['require','../dep/glmatrix'],function(require) { - - - - var glMatrix = require('../dep/glmatrix'); - var quat = glMatrix.quat; - - /** - * @constructor - * @alias qtek.math.Quaternion - * @param {number} x - * @param {number} y - * @param {number} z - * @param {number} w - */ - var Quaternion = function(x, y, z, w) { - - x = x || 0; - y = y || 0; - z = z || 0; - w = w === undefined ? 1 : w; - - /** - * Storage of Quaternion, read and write of x, y, z, w will change the values in _array - * All methods also operate on the _array instead of x, y, z, w components - * @type {Float32Array} - */ - this._array = quat.fromValues(x, y, z, w); - - /** - * Dirty flag is used by the Node to determine - * if the matrix is updated to latest - * @type {boolean} - */ - this._dirty = true; - }; - - Quaternion.prototype = { - - constructor: Quaternion, - - /** - * Add b to self - * @param {qtek.math.Quaternion} b - * @return {qtek.math.Quaternion} - */ - add: function(b) { - quat.add( this._array, this._array, b._array ); - this._dirty = true; - return this; - }, - - /** - * Calculate the w component from x, y, z component - * @return {qtek.math.Quaternion} - */ - calculateW: function() { - quat.calculateW(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Set x, y and z components - * @param {number} x - * @param {number} y - * @param {number} z - * @param {number} w - * @return {qtek.math.Quaternion} - */ - set: function(x, y, z, w) { - this._array[0] = x; - this._array[1] = y; - this._array[2] = z; - this._array[3] = w; - this._dirty = true; - return this; - }, - - /** - * Set x, y, z and w components from array - * @param {Float32Array|number[]} arr - * @return {qtek.math.Quaternion} - */ - setArray: function(arr) { - this._array[0] = arr[0]; - this._array[1] = arr[1]; - this._array[2] = arr[2]; - this._array[3] = arr[3]; - - this._dirty = true; - return this; - }, - - /** - * Clone a new Quaternion - * @return {qtek.math.Quaternion} - */ - clone: function() { - return new Quaternion( this.x, this.y, this.z, this.w ); - }, - - /** - * Calculates the conjugate of self If the quaternion is normalized, - * this function is faster than invert and produces the same result. - * - * @return {qtek.math.Quaternion} - */ - conjugate: function() { - quat.conjugate(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Copy from b - * @param {qtek.math.Quaternion} b - * @return {qtek.math.Quaternion} - */ - copy: function(b) { - quat.copy(this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Dot product of self and b - * @param {qtek.math.Quaternion} b - * @return {number} - */ - dot: function(b) { - return quat.dot(this._array, b._array); - }, - - /** - * Set from the given 3x3 rotation matrix - * @param {qtek.math.Matrix3} m - * @return {qtek.math.Quaternion} - */ - fromMat3: function(m) { - quat.fromMat3(this._array, m._array); - this._dirty = true; - return this; - }, - - /** - * Set from the given 4x4 rotation matrix - * The 4th column and 4th row will be droped - * @param {qtek.math.Matrix4} m - * @return {qtek.math.Quaternion} - */ - fromMat4: (function() { - var mat3 = glMatrix.mat3; - var m3 = mat3.create(); - return function(m) { - mat3.fromMat4(m3, m._array); - // TODO Not like mat4, mat3 in glmatrix seems to be row-based - mat3.transpose(m3, m3); - quat.fromMat3(this._array, m3); - this._dirty = true; - return this; - }; - })(), - - /** - * Set to identity quaternion - * @return {qtek.math.Quaternion} - */ - identity: function() { - quat.identity(this._array); - this._dirty = true; - return this; - }, - /** - * Invert self - * @return {qtek.math.Quaternion} - */ - invert: function() { - quat.invert(this._array, this._array); - this._dirty = true; - return this; - }, - /** - * Alias of length - * @return {number} - */ - len: function() { - return quat.len(this._array); - }, - - /** - * Calculate the length - * @return {number} - */ - length: function() { - return quat.length(this._array); - }, - - /** - * Linear interpolation between a and b - * @param {qtek.math.Quaternion} a - * @param {qtek.math.Quaternion} b - * @param {number} t - * @return {qtek.math.Quaternion} - */ - lerp: function(a, b, t) { - quat.lerp(this._array, a._array, b._array, t); - this._dirty = true; - return this; - }, - - /** - * Alias for multiply - * @param {qtek.math.Quaternion} b - * @return {qtek.math.Quaternion} - */ - mul: function(b) { - quat.mul(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Alias for multiplyLeft - * @param {qtek.math.Quaternion} a - * @return {qtek.math.Quaternion} - */ - mulLeft: function(a) { - quat.multiply(this._array, a._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Mutiply self and b - * @param {qtek.math.Quaternion} b - * @return {qtek.math.Quaternion} - */ - multiply: function(b) { - quat.multiply(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Mutiply a and self - * Quaternion mutiply is not commutative, so the result of mutiplyLeft is different with multiply. - * @param {qtek.math.Quaternion} a - * @return {qtek.math.Quaternion} - */ - multiplyLeft: function(a) { - quat.multiply(this._array, a._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Normalize self - * @return {qtek.math.Quaternion} - */ - normalize: function() { - quat.normalize(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Rotate self by a given radian about X axis - * @param {number} rad - * @return {qtek.math.Quaternion} - */ - rotateX: function(rad) { - quat.rotateX(this._array, this._array, rad); - this._dirty = true; - return this; - }, - - /** - * Rotate self by a given radian about Y axis - * @param {number} rad - * @return {qtek.math.Quaternion} - */ - rotateY: function(rad) { - quat.rotateY(this._array, this._array, rad); - this._dirty = true; - return this; - }, - - /** - * Rotate self by a given radian about Z axis - * @param {number} rad - * @return {qtek.math.Quaternion} - */ - rotateZ: function(rad) { - quat.rotateZ(this._array, this._array, rad); - this._dirty = true; - return this; - }, - - /** - * Sets self to represent the shortest rotation from Vector3 a to Vector3 b. - * a and b needs to be normalized - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Quaternion} - */ - rotationTo: function(a, b) { - quat.rotationTo(this._array, a._array, b._array); - this._dirty = true; - return this; - }, - /** - * Sets self with values corresponding to the given axes - * @param {qtek.math.Vector3} view - * @param {qtek.math.Vector3} right - * @param {qtek.math.Vector3} up - * @return {qtek.math.Quaternion} - */ - setAxes: function(view, right, up) { - quat.setAxes(this._array, view._array, right._array, up._array); - this._dirty = true; - return this; - }, - - /** - * Sets self with a rotation axis and rotation angle - * @param {qtek.math.Vector3} axis - * @param {number} rad - * @return {qtek.math.Quaternion} - */ - setAxisAngle: function(axis, rad) { - quat.setAxisAngle(this._array, axis._array, rad); - this._dirty = true; - return this; - }, - /** - * Perform spherical linear interpolation between a and b - * @param {qtek.math.Quaternion} a - * @param {qtek.math.Quaternion} b - * @param {number} t - * @return {qtek.math.Quaternion} - */ - slerp: function(a, b, t) { - quat.slerp(this._array, a._array, b._array, t); - this._dirty = true; - return this; - }, - - /** - * Alias for squaredLength - * @return {number} - */ - sqrLen: function() { - return quat.sqrLen(this._array); - }, - - /** - * Squared length of self - * @return {number} - */ - squaredLength: function() { - return quat.squaredLength(this._array); - }, - - // Set quaternion from euler angle - setFromEuler: function(v) { - - }, - - toString: function() { - return '[' + Array.prototype.join.call(this._array, ',') + ']'; - } - }; - - // Getter and Setter - if (Object.defineProperty) { - - var proto = Quaternion.prototype; - /** - * @name x - * @type {number} - * @memberOf qtek.math.Quaternion - * @instance - */ - Object.defineProperty(proto, 'x', { - get: function () { - return this._array[0]; - }, - set: function (value) { - this._array[0] = value; - this._dirty = true; - } - }); - - /** - * @name y - * @type {number} - * @memberOf qtek.math.Quaternion - * @instance - */ - Object.defineProperty(proto, 'y', { - get: function () { - return this._array[1]; - }, - set: function (value) { - this._array[1] = value; - this._dirty = true; - } - }); - - /** - * @name z - * @type {number} - * @memberOf qtek.math.Quaternion - * @instance - */ - Object.defineProperty(proto, 'z', { - get: function () { - return this._array[2]; - }, - set: function (value) { - this._array[2] = value; - this._dirty = true; - } - }); - - /** - * @name w - * @type {number} - * @memberOf qtek.math.Quaternion - * @instance - */ - Object.defineProperty(proto, 'w', { - get: function () { - return this._array[3]; - }, - set: function (value) { - this._array[3] = value; - this._dirty = true; - } - }); - } - - // Supply methods that are not in place - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @param {qtek.math.Quaternion} b - * @return {qtek.math.Quaternion} - */ - Quaternion.add = function(out, a, b) { - quat.add(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {number} x - * @param {number} y - * @param {number} z - * @param {number} w - * @return {qtek.math.Quaternion} - */ - Quaternion.set = function(out, x, y, z, w) { - quat.set(out._array, x, y, z, w); - out._dirty = true; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} b - * @return {qtek.math.Quaternion} - */ - Quaternion.copy = function(out, b) { - quat.copy(out._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @return {qtek.math.Quaternion} - */ - Quaternion.calculateW = function(out, a) { - quat.calculateW(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @return {qtek.math.Quaternion} - */ - Quaternion.conjugate = function(out, a) { - quat.conjugate(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @return {qtek.math.Quaternion} - */ - Quaternion.identity = function(out) { - quat.identity(out._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @return {qtek.math.Quaternion} - */ - Quaternion.invert = function(out, a) { - quat.invert(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} a - * @param {qtek.math.Quaternion} b - * @return {number} - */ - Quaternion.dot = function(a, b) { - return quat.dot(a._array, b._array); - }; - - /** - * @param {qtek.math.Quaternion} a - * @return {number} - */ - Quaternion.len = function(a) { - return quat.length(a._array); - }; - - // Quaternion.length = Quaternion.len; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @param {qtek.math.Quaternion} b - * @param {number} t - * @return {qtek.math.Quaternion} - */ - Quaternion.lerp = function(out, a, b, t) { - quat.lerp(out._array, a._array, b._array, t); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @param {qtek.math.Quaternion} b - * @param {number} t - * @return {qtek.math.Quaternion} - */ - Quaternion.slerp = function(out, a, b, t) { - quat.slerp(out._array, a._array, b._array, t); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @param {qtek.math.Quaternion} b - * @return {qtek.math.Quaternion} - */ - Quaternion.mul = function(out, a, b) { - quat.multiply(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @method - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @param {qtek.math.Quaternion} b - * @return {qtek.math.Quaternion} - */ - Quaternion.multiply = Quaternion.mul; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @param {number} rad - * @return {qtek.math.Quaternion} - */ - Quaternion.rotateX = function(out, a, rad) { - quat.rotateX(out._array, a._array, rad); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @param {number} rad - * @return {qtek.math.Quaternion} - */ - Quaternion.rotateY = function(out, a, rad) { - quat.rotateY(out._array, a._array, rad); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @param {number} rad - * @return {qtek.math.Quaternion} - */ - Quaternion.rotateZ = function(out, a, rad) { - quat.rotateZ(out._array, a._array, rad); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Vector3} axis - * @param {number} rad - * @return {qtek.math.Quaternion} - */ - Quaternion.setAxisAngle = function(out, axis, rad) { - quat.setAxisAngle(out._array, axis._array, rad); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @return {qtek.math.Quaternion} - */ - Quaternion.normalize = function(out, a) { - quat.normalize(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} a - * @return {number} - */ - Quaternion.sqrLen = function(a) { - return quat.sqrLen(a._array); - }; - - /** - * @method - * @param {qtek.math.Quaternion} a - * @return {number} - */ - Quaternion.squaredLength = Quaternion.sqrLen; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Matrix3} m - * @return {qtek.math.Quaternion} - */ - Quaternion.fromMat3 = function(out, m) { - quat.fromMat3(out._array, m._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Vector3} view - * @param {qtek.math.Vector3} right - * @param {qtek.math.Vector3} up - * @return {qtek.math.Quaternion} - */ - Quaternion.setAxes = function(out, view, right, up) { - quat.setAxes(out._array, view._array, right._array, up._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Quaternion} - */ - Quaternion.rotationTo = function(out, a, b) { - quat.rotationTo(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - return Quaternion; -}); -define('qtek/math/Matrix4',['require','../dep/glmatrix','./Vector3'],function(require) { - - - - var glMatrix = require('../dep/glmatrix'); - var Vector3 = require('./Vector3'); - var mat4 = glMatrix.mat4; - var vec3 = glMatrix.vec3; - var mat3 = glMatrix.mat3; - var quat = glMatrix.quat; - - function makeProperty(n) { - return { - set: function(value) { - this._array[n] = value; - this._dirty = true; - }, - get: function() { - return this._array[n]; - } - }; - } - - /** - * @constructor - * @alias qtek.math.Matrix4 - */ - var Matrix4 = function() { - - this._axisX = new Vector3(); - this._axisY = new Vector3(); - this._axisZ = new Vector3(); - - /** - * Storage of Matrix4 - * @type {Float32Array} - */ - this._array = mat4.create(); - - /** - * @type {boolean} - */ - this._dirty = true; - }; - - Matrix4.prototype = { - - constructor: Matrix4, - - /** - * Calculate the adjugate of self, in-place - * @return {qtek.math.Matrix4} - */ - adjoint: function() { - mat4.adjoint(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Clone a new Matrix4 - * @return {qtek.math.Matrix4} - */ - clone: function() { - return (new Matrix4()).copy(this); - }, - - /** - * Copy from b - * @param {qtek.math.Matrix4} b - * @return {qtek.math.Matrix4} - */ - copy: function(a) { - mat4.copy(this._array, a._array); - this._dirty = true; - return this; - }, - - /** - * Calculate matrix determinant - * @return {number} - */ - determinant: function() { - return mat4.determinant(this._array); - }, - - /** - * Set upper 3x3 part from quaternion - * @param {qtek.math.Quaternion} q - * @return {qtek.math.Matrix4} - */ - fromQuat: function(q) { - mat4.fromQuat(this._array, q._array); - this._dirty = true; - return this; - }, - - /** - * Set from a quaternion rotation and a vector translation - * @param {qtek.math.Quaternion} q - * @param {qtek.math.Vector3} v - * @return {qtek.math.Matrix4} - */ - fromRotationTranslation: function(q, v) { - mat4.fromRotationTranslation(this._array, q._array, v._array); - this._dirty = true; - return this; - }, - - /** - * Set from Matrix2d, it is used when converting a 2d shape to 3d space. - * In 3d space it is equivalent to ranslate on xy plane and rotate about z axis - * @param {qtek.math.Matrix2d} m2d - * @return {qtek.math.Matrix4} - */ - fromMat2d: function(m2d) { - Matrix4.fromMat2d(this, m2d); - return this; - }, - - /** - * Set from frustum bounds - * @param {number} left - * @param {number} right - * @param {number} bottom - * @param {number} top - * @param {number} near - * @param {number} far - * @return {qtek.math.Matrix4} - */ - frustum: function(left, right, bottom, top, near, far) { - mat4.frustum(this._array, left, right, bottom, top, near, far); - this._dirty = true; - return this; - }, - - /** - * Set to a identity matrix - * @return {qtek.math.Matrix4} - */ - identity: function() { - mat4.identity(this._array); - this._dirty = true; - return this; - }, - - /** - * Invert self - * @return {qtek.math.Matrix4} - */ - invert: function() { - mat4.invert(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Set as a matrix with the given eye position, focal point, and up axis - * @param {qtek.math.Vector3} eye - * @param {qtek.math.Vector3} center - * @param {qtek.math.Vector3} up - * @return {qtek.math.Matrix4} - */ - lookAt: function(eye, center, up) { - mat4.lookAt(this._array, eye._array, center._array, up._array); - this._dirty = true; - return this; - }, - - /** - * Alias for mutiply - * @param {qtek.math.Matrix4} b - * @return {qtek.math.Matrix4} - */ - mul: function(b) { - mat4.mul(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Alias for multiplyLeft - * @param {qtek.math.Matrix4} a - * @return {qtek.math.Matrix4} - */ - mulLeft: function(a) { - mat4.mul(this._array, a._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Multiply self and b - * @param {qtek.math.Matrix4} b - * @return {qtek.math.Matrix4} - */ - multiply: function(b) { - mat4.multiply(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Multiply a and self, a is on the left - * @param {qtek.math.Matrix3} a - * @return {qtek.math.Matrix3} - */ - multiplyLeft: function(a) { - mat4.multiply(this._array, a._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Set as a orthographic projection matrix - * @param {number} left - * @param {number} right - * @param {number} bottom - * @param {number} top - * @param {number} near - * @param {number} far - * @return {qtek.math.Matrix4} - */ - ortho: function(left, right, bottom, top, near, far) { - mat4.ortho(this._array, left, right, bottom, top, near, far); - this._dirty = true; - return this; - }, - /** - * Set as a perspective projection matrix - * @param {number} fovy - * @param {number} aspect - * @param {number} near - * @param {number} far - * @return {qtek.math.Matrix4} - */ - perspective: function(fovy, aspect, near, far) { - mat4.perspective(this._array, fovy, aspect, near, far); - this._dirty = true; - return this; - }, - - /** - * Rotate self by rad about axis - * @param {number} rad - * @param {qtek.math.Vector3} axis - * @return {qtek.math.Matrix4} - */ - rotate: function(rad, axis) { - mat4.rotate(this._array, this._array, rad, axis._array); - this._dirty = true; - return this; - }, - - /** - * Rotate self by a given radian about X axis - * @param {number} rad - * @return {qtek.math.Matrix4} - */ - rotateX: function(rad) { - mat4.rotateX(this._array, this._array, rad); - this._dirty = true; - return this; - }, - - /** - * Rotate self by a given radian about Y axis - * @param {number} rad - * @return {qtek.math.Matrix4} - */ - rotateY: function(rad) { - mat4.rotateY(this._array, this._array, rad); - this._dirty = true; - return this; - }, - - /** - * Rotate self by a given radian about Z axis - * @param {number} rad - * @return {qtek.math.Matrix4} - */ - rotateZ: function(rad) { - mat4.rotateZ(this._array, this._array, rad); - this._dirty = true; - return this; - }, - - /** - * Scale self by s - * @param {qtek.math.Vector3} s - * @return {qtek.math.Matrix4} - */ - scale: function(v) { - mat4.scale(this._array, this._array, v._array); - this._dirty = true; - return this; - }, - - /** - * Translate self by v - * @param {qtek.math.Vector3} v - * @return {qtek.math.Matrix4} - */ - translate: function(v) { - mat4.translate(this._array, this._array, v._array); - this._dirty = true; - return this; - }, - - /** - * Transpose self, in-place. - * @return {qtek.math.Matrix2} - */ - transpose: function() { - mat4.transpose(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Decompose a matrix to SRT - * @param {qtek.math.Vector3} [scale] - * @param {qtek.math.Quaternion} rotation - * @param {qtek.math.Vector} position - * @see http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.matrix.decompose.aspx - */ - decomposeMatrix: (function() { - - var x = vec3.create(); - var y = vec3.create(); - var z = vec3.create(); - - var m3 = mat3.create(); - - return function(scale, rotation, position) { - - var el = this._array; - vec3.set(x, el[0], el[1], el[2]); - vec3.set(y, el[4], el[5], el[6]); - vec3.set(z, el[8], el[9], el[10]); - - var sx = vec3.length(x); - var sy = vec3.length(y); - var sz = vec3.length(z); - if (scale) { - scale.x = sx; - scale.y = sy; - scale.z = sz; - scale._dirty = true; - } - - position.set(el[12], el[13], el[14]); - - mat3.fromMat4(m3, el); - // Not like mat4, mat3 in glmatrix seems to be row-based - mat3.transpose(m3, m3); - - m3[0] /= sx; - m3[1] /= sx; - m3[2] /= sx; - - m3[3] /= sy; - m3[4] /= sy; - m3[5] /= sy; - - m3[6] /= sz; - m3[7] /= sz; - m3[8] /= sz; - - quat.fromMat3(rotation._array, m3); - quat.normalize(rotation._array, rotation._array); - - rotation._dirty = true; - position._dirty = true; - }; - })(), - - toString: function() { - return '[' + Array.prototype.join.call(this._array, ',') + ']'; - } - }; - - if (Object.defineProperty) { - var proto = Matrix4.prototype; - /** - * Z Axis of local transform - * @name z - * @type {qtek.math.Vector3} - * @memberOf qtek.math.Matrix4 - * @instance - */ - Object.defineProperty(proto, 'z', { - get: function () { - var el = this._array; - this._axisZ.set(el[8], el[9], el[10]); - return this._axisZ; - }, - set: function (v) { - // TODO Here has a problem - // If only set an item of vector will not work - var el = this._array; - v = v._array; - el[8] = v[0]; - el[9] = v[1]; - el[10] = v[2]; - - this._dirty = true; - } - }); - - /** - * Y Axis of local transform - * @name y - * @type {qtek.math.Vector3} - * @memberOf qtek.math.Matrix4 - * @instance - */ - Object.defineProperty(proto, 'y', { - get: function () { - var el = this._array; - this._axisY.set(el[4], el[5], el[6]); - return this._axisY; - }, - set: function (v) { - var el = this._array; - v = v._array; - el[4] = v[0]; - el[5] = v[1]; - el[6] = v[2]; - - this._dirty = true; - } - }); - - /** - * X Axis of local transform - * @name x - * @type {qtek.math.Vector3} - * @memberOf qtek.math.Matrix4 - * @instance - */ - Object.defineProperty(proto, 'x', { - get: function () { - var el = this._array; - this._axisX.set(el[0], el[1], el[2]); - return this._axisX; - }, - set: function (v) { - var el = this._array; - v = v._array; - el[0] = v[0]; - el[1] = v[1]; - el[2] = v[2]; - - this._dirty = true; - } - }) - } - - // Object.defineProperty(Matrix4.prototype, 'm00', makeProperty(0)); - // Object.defineProperty(Matrix4.prototype, 'm01', makeProperty(1)); - // Object.defineProperty(Matrix4.prototype, 'm02', makeProperty(2)); - // Object.defineProperty(Matrix4.prototype, 'm03', makeProperty(3)); - // Object.defineProperty(Matrix4.prototype, 'm10', makeProperty(4)); - // Object.defineProperty(Matrix4.prototype, 'm11', makeProperty(5)); - // Object.defineProperty(Matrix4.prototype, 'm12', makeProperty(6)); - // Object.defineProperty(Matrix4.prototype, 'm13', makeProperty(7)); - // Object.defineProperty(Matrix4.prototype, 'm20', makeProperty(8)); - // Object.defineProperty(Matrix4.prototype, 'm21', makeProperty(9)); - // Object.defineProperty(Matrix4.prototype, 'm22', makeProperty(10)); - // Object.defineProperty(Matrix4.prototype, 'm23', makeProperty(11)); - // Object.defineProperty(Matrix4.prototype, 'm30', makeProperty(12)); - // Object.defineProperty(Matrix4.prototype, 'm31', makeProperty(13)); - // Object.defineProperty(Matrix4.prototype, 'm32', makeProperty(14)); - // Object.defineProperty(Matrix4.prototype, 'm33', makeProperty(15)); - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @return {qtek.math.Matrix4} - */ - Matrix4.adjoint = function(out, a) { - mat4.adjoint(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @return {qtek.math.Matrix4} - */ - Matrix4.copy = function(out, a) { - mat4.copy(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} a - * @return {number} - */ - Matrix4.determinant = function(a) { - return mat4.determinant(a._array); - }; - - /** - * @param {qtek.math.Matrix4} out - * @return {qtek.math.Matrix4} - */ - Matrix4.identity = function(out) { - mat4.identity(out._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {number} left - * @param {number} right - * @param {number} bottom - * @param {number} top - * @param {number} near - * @param {number} far - * @return {qtek.math.Matrix4} - */ - Matrix4.ortho = function(out, left, right, bottom, top, near, far) { - mat4.ortho(out._array, left, right, bottom, top, near, far); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {number} fovy - * @param {number} aspect - * @param {number} near - * @param {number} far - * @return {qtek.math.Matrix4} - */ - Matrix4.perspective = function(out, fovy, aspect, near, far) { - mat4.perspective(out._array, fovy, aspect, near, far); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Vector3} eye - * @param {qtek.math.Vector3} center - * @param {qtek.math.Vector3} up - * @return {qtek.math.Matrix4} - */ - Matrix4.lookAt = function(out, eye, center, up) { - mat4.lookAt(out._array, eye._array, center._array, up._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @return {qtek.math.Matrix4} - */ - Matrix4.invert = function(out, a) { - mat4.invert(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @param {qtek.math.Matrix4} b - * @return {qtek.math.Matrix4} - */ - Matrix4.mul = function(out, a, b) { - mat4.mul(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @method - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @param {qtek.math.Matrix4} b - * @return {qtek.math.Matrix4} - */ - Matrix4.multiply = Matrix4.mul; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Quaternion} q - * @return {qtek.math.Matrix4} - */ - Matrix4.fromQuat = function(out, q) { - mat4.fromQuat(out._array, q._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Quaternion} q - * @param {qtek.math.Vector3} v - * @return {qtek.math.Matrix4} - */ - Matrix4.fromRotationTranslation = function(out, q, v) { - mat4.fromRotationTranslation(out._array, q._array, v._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} m4 - * @param {qtek.math.Matrix2d} m2d - * @return {qtek.math.Matrix4} - */ - Matrix4.fromMat2d = function(m4, m2d) { - m4._dirty = true; - var m2d = m2d._array; - var m4 = m4._array; - - m4[0] = m2d[0]; - m4[4] = m2d[2]; - m4[12] = m2d[4]; - - m4[1] = m2d[1]; - m4[5] = m2d[3]; - m4[13] = m2d[5]; - - return m4; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @param {number} rad - * @param {qtek.math.Vector3} axis - * @return {qtek.math.Matrix4} - */ - Matrix4.rotate = function(out, a, rad, axis) { - mat4.rotate(out._array, a._array, rad, axis._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @param {number} rad - * @return {qtek.math.Matrix4} - */ - Matrix4.rotateX = function(out, a, rad) { - mat4.rotateX(out._array, a._array, rad); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @param {number} rad - * @return {qtek.math.Matrix4} - */ - Matrix4.rotateY = function(out, a, rad) { - mat4.rotateY(out._array, a._array, rad); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @param {number} rad - * @return {qtek.math.Matrix4} - */ - Matrix4.rotateZ = function(out, a, rad) { - mat4.rotateZ(out._array, a._array, rad); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @param {qtek.math.Vector3} v - * @return {qtek.math.Matrix4} - */ - Matrix4.scale = function(out, a, v) { - mat4.scale(out._array, a._array, v._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @return {qtek.math.Matrix4} - */ - Matrix4.transpose = function(out, a) { - mat4.transpose(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @param {qtek.math.Vector3} v - * @return {qtek.math.Matrix4} - */ - Matrix4.translate = function(out, a, v) { - mat4.translate(out._array, a._array, v._array); - out._dirty = true; - return out; - }; - - return Matrix4; -}); -define('qtek/Node',['require','./core/Base','./math/Vector3','./math/Quaternion','./math/Matrix4','./dep/glmatrix'],function(require) { - - - - var Base = require('./core/Base'); - var Vector3 = require('./math/Vector3'); - var Quaternion = require('./math/Quaternion'); - var Matrix4 = require('./math/Matrix4'); - var glMatrix = require('./dep/glmatrix'); - var mat4 = glMatrix.mat4; - - var nameId = 0; - - /** - * @constructor qtek.Node - * @extends qtek.core.Base - */ - var Node = Base.derive( - /** @lends qtek.Node# */ - { - /** - * Scene node name - * @type {string} - */ - name: '', - - /** - * Position relative to its parent node. aka translation. - * @type {qtek.math.Vector3} - */ - position: null, - - /** - * Rotation relative to its parent node. Represented by a quaternion - * @type {qtek.math.Quaternion} - */ - rotation: null, - - /** - * Scale relative to its parent node - * @type {qtek.math.Vector3} - */ - scale: null, - - /** - * Affine transform matrix relative to its root scene. - * @type {qtek.math.Matrix4} - */ - worldTransform: null, - - /** - * Affine transform matrix relative to its parent node. - * Composite with position, rotation and scale. - * @type {qtek.math.Matrix4} - */ - localTransform: null, - - /** - * If the local transform is update from SRT(scale, rotation, translation, which is position here) each frame - * @type {boolean} - */ - autoUpdateLocalTransform: true, - - /** - * Parent of current scene node - * @type {?qtek.Node} - * @private - */ - _parent: null, - /** - * The root scene mounted. Null if it is a isolated node - * @type {?qtek.Scene} - * @private - */ - _scene: null, - - _needsUpdateWorldTransform: true, - - _inIterating: false, - - // Depth for transparent queue sorting - __depth: 0 - - }, function() { - - if (!this.name) { - this.name = 'NODE_' + (nameId++); - } - - if (!this.position) { - this.position = new Vector3(); - } - if (!this.rotation) { - this.rotation = new Quaternion(); - } - if (!this.scale) { - this.scale = new Vector3(1, 1, 1); - } - - this.worldTransform = new Matrix4(); - this.localTransform = new Matrix4(); - - this._children = []; - - }, - /**@lends qtek.Node.prototype. */ - { - - /** - * If node and its chilren visible - * @type {boolean} - * @memberOf qtek.Node - * @instance - */ - visible: true, - - /** - * Return true if it is a renderable scene node, like Mesh and ParticleSystem - * @return {boolean} - */ - isRenderable: function() { - return false; - }, - - /** - * Set the name of the scene node - * @param {string} name - */ - setName: function(name) { - if (this._scene) { - delete this._scene._nodeRepository[this.name]; - this._scene._nodeRepository[name] = this; - } - this.name = name; - }, - - /** - * Add a child node - * @param {qtek.Node} node - */ - add: function(node) { - if (this._inIterating) { - console.warn('Add operation can cause unpredictable error when in iterating'); - } - if (node._parent === this) { - return; - } - if (node._parent) { - node._parent.remove(node); - } - node._parent = this; - this._children.push(node); - - if (this._scene && this._scene !== node.scene) { - node.traverse(this._addSelfToScene, this); - } - }, - - /** - * Remove the given child scene node - * @param {qtek.Node} node - */ - remove: function(node) { - if (this._inIterating) { - console.warn('Remove operation can cause unpredictable error when in iterating'); - } - - var idx = this._children.indexOf(node); - if (idx < 0) { - return; - } - - this._children.splice(idx, 1); - node._parent = null; - - if (this._scene) { - node.traverse(this._removeSelfFromScene, this); - } - }, - - /** - * Get the scene mounted - * @return {qtek.Scene} - */ - getScene: function () { - return this._scene; - }, - - /** - * Get parent node - * @return {qtek.Scene} - */ - getParent: function () { - return this._parent; - }, - - _removeSelfFromScene: function(descendant) { - descendant._scene.removeFromScene(descendant); - descendant._scene = null; - }, - - _addSelfToScene: function(descendant) { - this._scene.addToScene(descendant); - descendant._scene = this._scene; - }, - - /** - * Return true if it is ancestor of the given scene node - * @param {qtek.Node} node - */ - isAncestor: function(node) { - var parent = node._parent; - while(parent) { - if (parent === this) { - return true; - } - parent = parent._parent; - } - return false; - }, - - /** - * Get a new created array of all its children nodes - * @return {qtek.Node[]} - */ - children: function() { - return this._children.slice(); - }, - - childAt: function(idx) { - return this._children[idx]; - }, - - /** - * Get first child have the given name - * @param {string} name - * @return {qtek.Node} - */ - getChildByName: function(name) { - for (var i = 0; i < this._children.length; i++) { - if (this._children[i].name === name) { - return this._children[i]; - } - } - }, - - /** - * Get first descendant have the given name - * @param {string} name - * @return {qtek.Node} - */ - getDescendantByName: function(name) { - for (var i = 0; i < this._children.length; i++) { - var child = this._children[i]; - if (child.name === name) { - return child; - } else { - var res = child.getDescendantByName(name); - if (res) { - return res; - } - } - } - }, - - /** - * Query descendant node by path - * @param {string} path - * @return {qtek.Node} - */ - queryNode: function (path) { - if (!path) { - return; - } - // TODO Name have slash ? - var pathArr = path.split('/'); - var current = this; - for (var i = 0; i < pathArr.length; i++) { - var name = pathArr[i]; - // Skip empty - if (!name) { - continue; - } - var found = false; - for (var j = 0; j < current._children.length; j++) { - var child = current._children[j]; - if (child.name === name) { - current = child; - found = true; - break; - } - } - // Early return if not found - if (!found) { - return; - } - } - - return current; - }, - - /** - * Get query path, relative to rootNode(default is scene) - * @return {string} - */ - getPath: function (rootNode) { - if (!this._parent) { - return '/'; - } - - var current = this._parent; - var path = this.name; - while (current._parent) { - path = current.name + '/' + path; - if (current._parent == rootNode) { - break; - } - current = current._parent; - } - if (!current._parent && rootNode) { - return null; - } - return path; - }, - - /** - * Depth first traverse all its descendant scene nodes and - * @param {Function} callback - * @param {Node} [context] - * @param {Function} [ctor] - */ - traverse: function(callback, context, ctor) { - - this._inIterating = true; - - if (ctor === undefined || this.constructor === ctor) { - callback.call(context, this); - } - var _children = this._children; - for(var i = 0, len = _children.length; i < len; i++) { - _children[i].traverse(callback, context, ctor); - } - - this._inIterating = false; - }, - - /** - * Set the local transform and decompose to SRT - * @param {qtek.math.Matrix4} matrix - */ - setLocalTransform: function(matrix) { - mat4.copy(this.localTransform._array, matrix._array); - this.decomposeLocalTransform(); - }, - - /** - * Decompose the local transform to SRT - */ - decomposeLocalTransform: function(keepScale) { - var scale = !keepScale ? this.scale: null; - this.localTransform.decomposeMatrix(scale, this.rotation, this.position); - }, - - /** - * Set the world transform and decompose to SRT - * @param {qtek.math.Matrix4} matrix - */ - setWorldTransform: function(matrix) { - mat4.copy(this.worldTransform._array, matrix._array); - this.decomposeWorldTransform(); - }, - - /** - * Decompose the world transform to SRT - * @method - */ - decomposeWorldTransform: (function() { - - var tmp = mat4.create(); - - return function(keepScale) { - // Assume world transform is updated - if (this._parent) { - mat4.invert(tmp, this._parent.worldTransform._array); - mat4.multiply(this.localTransform._array, tmp, this.worldTransform._array); - } else { - mat4.copy(this.localTransform._array, this.worldTransform._array); - } - var scale = !keepScale ? this.scale: null; - this.localTransform.decomposeMatrix(scale, this.rotation, this.position); - }; - })(), - - /** - * Update local transform from SRT - * Notice that local transform will not be updated if _dirty mark of position, rotation, scale is all false - */ - updateLocalTransform: function() { - var position = this.position; - var rotation = this.rotation; - var scale = this.scale; - - if (position._dirty || scale._dirty || rotation._dirty) { - var m = this.localTransform._array; - - // Transform order, scale->rotation->position - mat4.fromRotationTranslation(m, rotation._array, position._array); - - mat4.scale(m, m, scale._array); - - rotation._dirty = false; - scale._dirty = false; - position._dirty = false; - - this._needsUpdateWorldTransform = true; - } - }, - - /** - * Update world transform, assume its parent world transform have been updated - */ - updateWorldTransform: function() { - if (this._parent) { - mat4.multiply( - this.worldTransform._array, - this._parent.worldTransform._array, - this.localTransform._array - ); - } else { - mat4.copy( - this.worldTransform._array, this.localTransform._array - ); - } - }, - - /** - * Update local transform and world transform recursively - * @param {boolean} forceUpdateWorld - */ - update: function(forceUpdateWorld) { - if (this.autoUpdateLocalTransform) { - this.updateLocalTransform(); - } else { - // Transform is manually setted - forceUpdateWorld = true; - } - - if (forceUpdateWorld || this._needsUpdateWorldTransform) { - this.updateWorldTransform(); - forceUpdateWorld = true; - this._needsUpdateWorldTransform = false; - } - - for(var i = 0, len = this._children.length; i < len; i++) { - this._children[i].update(forceUpdateWorld); - } - }, - - /** - * Get world position, extracted from world transform - * @param {math.Vector3} [out] - * @return {math.Vector3} - */ - getWorldPosition: function(out) { - var m = this.worldTransform._array; - if (out) { - out._array[0] = m[12]; - out._array[1] = m[13]; - out._array[2] = m[14]; - return out; - } else { - return new Vector3(m[12], m[13], m[14]); - } - }, - - /** - * Clone a new node - * @return {Node} - */ - clone: function() { - var node = new this.constructor(); - node.setName(this.name); - node.position.copy(this.position); - node.rotation.copy(this.rotation); - node.scale.copy(this.scale); - - for (var i = 0; i < this._children.length; i++) { - node.add(this._children[i].clone()); - } - return node; - }, - - /** - * Rotate the node around a axis by angle degrees, axis passes through point - * @param {math.Vector3} point Center point - * @param {math.Vector3} axis Center axis - * @param {number} angle Rotation angle - * @see http://docs.unity3d.com/Documentation/ScriptReference/Transform.RotateAround.html - * @method - */ - rotateAround: (function() { - var v = new Vector3(); - var RTMatrix = new Matrix4(); - - // TODO improve performance - return function(point, axis, angle) { - - v.copy(this.position).subtract(point); - - this.localTransform.identity(); - // parent node - this.localTransform.translate(point); - this.localTransform.rotate(angle, axis); - - RTMatrix.fromRotationTranslation(this.rotation, v); - this.localTransform.multiply(RTMatrix); - this.localTransform.scale(this.scale); - - this.decomposeLocalTransform(); - this._needsUpdateWorldTransform = true; - }; - })(), - - /** - * @param {math.Vector3} target - * @param {math.Vector3} [up] - * @see http://www.opengl.org/sdk/docs/man2/xhtml/gluLookAt.xml - * @method - */ - lookAt: (function() { - var m = new Matrix4(); - return function(target, up) { - m.lookAt(this.position, target, up || this.localTransform.y).invert(); - m.decomposeMatrix(null, this.rotation, this.position); - }; - })() - }); - - return Node; -}); -define('qtek/math/BoundingBox',['require','./Vector3','../dep/glmatrix'],function(require) { - - - - var Vector3 = require('./Vector3'); - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - - var vec3TransformMat4 = vec3.transformMat4; - var vec3Copy = vec3.copy; - var vec3Set = vec3.set; - - /** - * Axis aligned bounding box - * @constructor - * @alias qtek.math.BoundingBox - * @param {qtek.math.Vector3} [min] - * @param {qtek.math.Vector3} [max] - */ - var BoundingBox = function(min, max) { - - /** - * Minimum coords of bounding box - * @type {qtek.math.Vector3} - */ - this.min = min || new Vector3(Infinity, Infinity, Infinity); - - /** - * Maximum coords of bounding box - * @type {qtek.math.Vector3} - */ - this.max = max || new Vector3(-Infinity, -Infinity, -Infinity); - - // Cube vertices - var vertices = []; - for (var i = 0; i < 8; i++) { - vertices[i] = vec3.fromValues(0, 0, 0); - } - - /** - * Eight coords of bounding box - * @type {Float32Array[]} - */ - this.vertices = vertices; - }; - - BoundingBox.prototype = { - - constructor: BoundingBox, - /** - * Update min and max coords from a vertices array - * @param {array} vertices - */ - updateFromVertices: function(vertices) { - if (vertices.length > 0) { - var _min = this.min._array; - var _max = this.max._array; - vec3Copy(_min, vertices[0]); - vec3Copy(_max, vertices[0]); - for (var i = 1; i < vertices.length; i++) { - var vertex = vertices[i]; - - if (vertex[0] < _min[0]) { _min[0] = vertex[0]; } - if (vertex[1] < _min[1]) { _min[1] = vertex[1]; } - if (vertex[2] < _min[2]) { _min[2] = vertex[2]; } - - if (vertex[0] > _max[0]) { _max[0] = vertex[0]; } - if (vertex[1] > _max[1]) { _max[1] = vertex[1]; } - if (vertex[2] > _max[2]) { _max[2] = vertex[2]; } - } - this.min._dirty = true; - this.max._dirty = true; - } - }, - - /** - * Union operation with another bounding box - * @param {qtek.math.BoundingBox} bbox - */ - union: function(bbox) { - vec3.min(this.min._array, this.min._array, bbox.min._array); - vec3.max(this.max._array, this.max._array, bbox.max._array); - this.min._dirty = true; - this.max._dirty = true; - }, - - /** - * If intersect with another bounding box - * @param {qtek.math.BoundingBox} bbox - * @return {boolean} - */ - intersectBoundingBox: function(bbox) { - var _min = this.min._array; - var _max = this.max._array; - - var _min2 = bbox.min._array; - var _max2 = bbox.max._array; - - return ! (_min[0] > _max2[0] || _min[1] > _max2[1] || _min[2] > _max2[2] - || _max[0] < _min2[0] || _max[1] < _min2[1] || _max[2] < _min2[2]); - }, - - /** - * Apply an affine transform matrix to the bounding box - * @param {qtek.math.Matrix4} matrix - */ - applyTransform: function(matrix) { - if (this.min._dirty || this.max._dirty) { - this.updateVertices(); - this.min._dirty = false; - this.max._dirty = false; - } - - var m4 = matrix._array; - var _min = this.min._array; - var _max = this.max._array; - var vertices = this.vertices; - - var v = vertices[0]; - vec3TransformMat4(v, v, m4); - vec3Copy(_min, v); - vec3Copy(_max, v); - - for (var i = 1; i < 8; i++) { - v = vertices[i]; - vec3TransformMat4(v, v, m4); - - if (v[0] < _min[0]) { _min[0] = v[0]; } - if (v[1] < _min[1]) { _min[1] = v[1]; } - if (v[2] < _min[2]) { _min[2] = v[2]; } - - if (v[0] > _max[0]) { _max[0] = v[0]; } - if (v[1] > _max[1]) { _max[1] = v[1]; } - if (v[2] > _max[2]) { _max[2] = v[2]; } - } - - this.min._dirty = true; - this.max._dirty = true; - }, - - /** - * Apply a projection matrix to the bounding box - * @param {qtek.math.Matrix4} matrix - */ - applyProjection: function(matrix) { - if (this.min._dirty || this.max._dirty) { - this.updateVertices(); - this.min._dirty = false; - this.max._dirty = false; - } - - var m = matrix._array; - // min in min z - var v1 = this.vertices[0]; - // max in min z - var v2 = this.vertices[3]; - // max in max z - var v3 = this.vertices[7]; - - var _min = this.min._array; - var _max = this.max._array; - - if (m[15] === 1) { // Orthographic projection - _min[0] = m[0] * v1[0] + m[12]; - _min[1] = m[5] * v1[1] + m[13]; - _max[2] = m[10] * v1[2] + m[14]; - - _max[0] = m[0] * v3[0] + m[12]; - _max[1] = m[5] * v3[1] + m[13]; - _min[2] = m[10] * v3[2] + m[14]; - } else { - var w = -1 / v1[2]; - _min[0] = m[0] * v1[0] * w; - _min[1] = m[5] * v1[1] * w; - _max[2] = (m[10] * v1[2] + m[14]) * w; - - w = -1 / v2[2]; - _max[0] = m[0] * v2[0] * w; - _max[1] = m[5] * v2[1] * w; - - w = -1 / v3[2]; - _min[2] = (m[10] * v3[2] + m[14]) * w; - } - this.min._dirty = true; - this.max._dirty = true; - }, - - updateVertices: function() { - var min = this.min._array; - var max = this.max._array; - var vertices = this.vertices; - //--- min z - // min x - vec3Set(vertices[0], min[0], min[1], min[2]); - vec3Set(vertices[1], min[0], max[1], min[2]); - // max x - vec3Set(vertices[2], max[0], min[1], min[2]); - vec3Set(vertices[3], max[0], max[1], min[2]); - - //-- max z - vec3Set(vertices[4], min[0], min[1], max[2]); - vec3Set(vertices[5], min[0], max[1], max[2]); - vec3Set(vertices[6], max[0], min[1], max[2]); - vec3Set(vertices[7], max[0], max[1], max[2]); - }, - /** - * Copy values from another bounding box - * @param {qtek.math.BoundingBox} bbox - */ - copy: function(bbox) { - vec3Copy(this.min._array, bbox.min._array); - vec3Copy(this.max._array, bbox.max._array); - this.min._dirty = true; - this.max._dirty = true; - }, - - /** - * Clone a new bounding box - * @return {qtek.math.BoundingBox} - */ - clone: function() { - var boundingBox = new BoundingBox(); - boundingBox.copy(this); - return boundingBox; - } - }; - - return BoundingBox; -}); -define('qtek/math/Plane',['require','./Vector3','../dep/glmatrix'],function(require) { - - - - var Vector3 = require('./Vector3'); - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - var mat4 = glMatrix.mat4; - var vec4 = glMatrix.vec4; - - /** - * @constructor - * @alias qtek.math.Plane - * @param {qtek.math.Vector3} [normal] - * @param {number} [distance] - */ - var Plane = function(normal, distance) { - /** - * Normal of the plane - * @type {qtek.math.Vector3} - */ - this.normal = normal || new Vector3(0, 1, 0); - - /** - * Constant of the plane equation, used as distance to the origin - * @type {number} - */ - this.distance = distance || 0; - }; - - Plane.prototype = { - - constructor: Plane, - - /** - * Distance from given point to plane - * @param {qtek.math.Vector3} point - * @return {number} - */ - distanceToPoint: function(point) { - return vec3.dot(point._array, this.normal._array) - this.distance; - }, - - /** - * Calculate the projection on the plane of point - * @param {qtek.math.Vector3} point - * @param {qtek.math.Vector3} out - * @return {qtek.math.Vector3} - */ - projectPoint: function(point, out) { - if (!out) { - out = new Vector3(); - } - var d = this.distanceToPoint(point); - vec3.scaleAndAdd(out._array, point._array, this.normal._array, -d); - out._dirty = true; - return out; - }, - - /** - * Normalize the plane's normal and calculate distance - */ - normalize: function() { - var invLen = 1 / vec3.len(this.normal._array); - vec3.scale(this.normal._array, invLen); - this.distance *= invLen; - }, - - /** - * If the plane intersect a frustum - * @param {qtek.math.Frustum} Frustum - * @return {boolean} - */ - intersectFrustum: function(frustum) { - // Check if all coords of frustum is on plane all under plane - var coords = frustum.vertices; - var normal = this.normal._array; - var onPlane = vec3.dot(coords[0]._array, normal) > this.distance; - for (var i = 1; i < 8; i++) { - if ((vec3.dot(coords[i]._array, normal) > this.distance) != onPlane) { - return true; - } - } - }, - - /** - * Calculate the intersection point between plane and a given line - * @method - * @param {qtek.math.Vector3} start start point of line - * @param {qtek.math.Vector3} end end point of line - * @param {qtek.math.Vector3} [out] - * @return {qtek.math.Vector3} - */ - intersectLine: (function() { - var rd = vec3.create(); - return function(start, end, out) { - var d0 = this.distanceToPoint(start); - var d1 = this.distanceToPoint(end); - if ((d0 > 0 && d1 > 0) || (d0 < 0 && d1 < 0)) { - return null; - } - // Ray intersection - var pn = this.normal._array; - var d = this.distance; - var ro = start._array; - // direction - vec3.sub(rd, end._array, start._array); - vec3.normalize(rd, rd); - - var divider = vec3.dot(pn, rd); - // ray is parallel to the plane - if (divider === 0) { - return null; - } - if (!out) { - out = new Vector3(); - } - var t = (vec3.dot(pn, ro) - d) / divider; - vec3.scaleAndAdd(out._array, ro, rd, -t); - out._dirty = true; - return out; - }; - })(), - - /** - * Apply an affine transform matrix to plane - * @method - * @return {qtek.math.Matrix4} - */ - applyTransform: (function() { - var inverseTranspose = mat4.create(); - var normalv4 = vec4.create(); - var pointv4 = vec4.create(); - pointv4[3] = 1; - return function(m4) { - m4 = m4._array; - // Transform point on plane - vec3.scale(pointv4, this.normal._array, this.distance); - vec4.transformMat4(pointv4, pointv4, m4); - this.distance = vec3.dot(pointv4, this.normal._array); - // Transform plane normal - mat4.invert(inverseTranspose, m4); - mat4.transpose(inverseTranspose, inverseTranspose); - normalv4[3] = 0; - vec3.copy(normalv4, this.normal._array); - vec4.transformMat4(normalv4, normalv4, inverseTranspose); - vec3.copy(this.normal._array, normalv4); - }; - })(), - - /** - * Copy from another plane - * @param {qtek.math.Vector3} plane - */ - copy: function(plane) { - vec3.copy(this.normal._array, plane.normal._array); - this.normal._dirty = true; - this.distance = plane.distance; - }, - - /** - * Clone a new plane - * @return {qtek.math.Plane} - */ - clone: function() { - var plane = new Plane(); - plane.copy(this); - return plane; - } - }; - - return Plane; -}); -define('qtek/math/Frustum',['require','./Vector3','./BoundingBox','./Plane','../dep/glmatrix'],function(require) { - - - - var Vector3 = require('./Vector3'); - var BoundingBox = require('./BoundingBox'); - var Plane = require('./Plane'); - - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - - /** - * @constructor - * @alias qtek.math.Frustum - */ - var Frustum = function() { - - /** - * Eight planes to enclose the frustum - * @type {qtek.math.Plane[]} - */ - this.planes = []; - - for (var i = 0; i < 6; i++) { - this.planes.push(new Plane()); - } - - /** - * Bounding box of frustum - * @type {qtek.math.BoundingBox} - */ - this.boundingBox = new BoundingBox(); - - /** - * Eight vertices of frustum - * @type {Float32Array[]} - */ - this.vertices = []; - for (var i = 0; i < 8; i++) { - this.vertices[i] = vec3.fromValues(0, 0, 0); - } - }; - - Frustum.prototype = { - - // http://web.archive.org/web/20120531231005/http://crazyjoke.free.fr/doc/3D/plane%20extraction.pdf - /** - * Set frustum from a projection matrix - * @param {qtek.math.Matrix4} projectionMatrix - */ - setFromProjection: function(projectionMatrix) { - - var planes = this.planes; - var m = projectionMatrix._array; - var m0 = m[0], m1 = m[1], m2 = m[2], m3 = m[3]; - var m4 = m[4], m5 = m[5], m6 = m[6], m7 = m[7]; - var m8 = m[8], m9 = m[9], m10 = m[10], m11 = m[11]; - var m12 = m[12], m13 = m[13], m14 = m[14], m15 = m[15]; - - // Update planes - vec3.set(planes[0].normal._array, m3 - m0, m7 - m4, m11 - m8); - planes[0].distance = -(m15 - m12); - planes[0].normalize(); - - vec3.set(planes[1].normal._array, m3 + m0, m7 + m4, m11 + m8); - planes[1].distance = -(m15 + m12); - planes[1].normalize(); - - vec3.set(planes[2].normal._array, m3 + m1, m7 + m5, m11 + m9); - planes[2].distance = -(m15 + m13); - planes[2].normalize(); - - vec3.set(planes[3].normal._array, m3 - m1, m7 - m5, m11 - m9); - planes[3].distance = -(m15 - m13); - planes[3].normalize(); - - vec3.set(planes[4].normal._array, m3 - m2, m7 - m6, m11 - m10); - planes[4].distance = -(m15 - m14); - planes[4].normalize(); - - vec3.set(planes[5].normal._array, m3 + m2, m7 + m6, m11 + m10); - planes[5].distance = -(m15 + m14); - planes[5].normalize(); - - // Perspective projection - if (m15 === 0) { - var aspect = m5 / m0; - var zNear = -m14 / (m10 - 1); - var zFar = -m14 / (m10 + 1); - var farY = -zFar / m5; - var nearY = -zNear / m5; - // Update bounding box - this.boundingBox.min.set(-farY * aspect, -farY, zFar); - this.boundingBox.max.set(farY * aspect, farY, zNear); - // update vertices - var vertices = this.vertices; - //--- min z - // min x - vec3.set(vertices[0], -farY * aspect, -farY, zFar); - vec3.set(vertices[1], -farY * aspect, farY, zFar); - // max x - vec3.set(vertices[2], farY * aspect, -farY, zFar); - vec3.set(vertices[3], farY * aspect, farY, zFar); - //-- max z - vec3.set(vertices[4], -nearY * aspect, -nearY, zNear); - vec3.set(vertices[5], -nearY * aspect, nearY, zNear); - vec3.set(vertices[6], nearY * aspect, -nearY, zNear); - vec3.set(vertices[7], nearY * aspect, nearY, zNear); - } else { // Orthographic projection - var left = (-1 - m12) / m0; - var right = (1 - m12) / m0; - var top = (1 - m13) / m5; - var bottom = (-1 - m13) / m5; - var near = (-1 - m14) / m10; - var far = (1 - m14) / m10; - - this.boundingBox.min.set(left, bottom, far); - this.boundingBox.max.set(right, top, near); - // Copy the vertices from bounding box directly - for (var i = 0; i < 8; i++) { - vec3.copy(this.vertices[i], this.boundingBox.vertices[i]); - } - } - }, - - /** - * Apply a affine transform matrix and set to the given bounding box - * @method - * @param {qtek.math.BoundingBox} - * @param {qtek.math.Matrix4} - * @return {qtek.math.BoundingBox} - */ - getTransformedBoundingBox: (function() { - - var tmpVec3 = vec3.create(); - - return function(bbox, matrix) { - var vertices = this.vertices; - - var m4 = matrix._array; - var _min = bbox.min._array; - var _max = bbox.max._array; - var v = vertices[0]; - vec3.transformMat4(tmpVec3, v, m4); - vec3.copy(_min, tmpVec3); - vec3.copy(_max, tmpVec3); - - for (var i = 1; i < 8; i++) { - v = vertices[i]; - vec3.transformMat4(tmpVec3, v, m4); - - _min[0] = Math.min(tmpVec3[0], _min[0]); - _min[1] = Math.min(tmpVec3[1], _min[1]); - _min[2] = Math.min(tmpVec3[2], _min[2]); - - _max[0] = Math.max(tmpVec3[0], _max[0]); - _max[1] = Math.max(tmpVec3[1], _max[1]); - _max[2] = Math.max(tmpVec3[2], _max[2]); - } - - bbox.min._dirty = true; - bbox.max._dirty = true; - - return bbox; - }; - }) () - }; - return Frustum; -}); -define('qtek/math/Ray',['require','./Vector3','../dep/glmatrix'],function(require) { - - - - var Vector3 = require('./Vector3'); - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - - var EPSILON = 1e-5; - - /** - * @constructor - * @alias qtek.math.Ray - * @param {qtek.math.Vector3} [origin] - * @param {qtek.math.Vector3} [direction] - */ - var Ray = function(origin, direction) { - /** - * @type {qtek.math.Vector3} - */ - this.origin = origin || new Vector3(); - /** - * @type {qtek.math.Vector3} - */ - this.direction = direction || new Vector3(); - }; - - Ray.prototype = { - - constructor: Ray, - - // http://www.siggraph.org/education/materials/HyperGraph/raytrace/rayplane_intersection.htm - /** - * Calculate intersection point between ray and a give plane - * @param {qtek.math.Plane} plane - * @param {qtek.math.Vector3} [out] - * @return {qtek.math.Vector3} - */ - intersectPlane: function(plane, out) { - var pn = plane.normal._array; - var d = plane.distance; - var ro = this.origin._array; - var rd = this.direction._array; - - var divider = vec3.dot(pn, rd); - // ray is parallel to the plane - if (divider === 0) { - return null; - } - if (!out) { - out = new Vector3(); - } - var t = (vec3.dot(pn, ro) - d) / divider; - vec3.scaleAndAdd(out._array, ro, rd, -t); - out._dirty = true; - return out; - }, - - /** - * Mirror the ray against plane - * @param {qtek.math.Plane} plane - */ - mirrorAgainstPlane: function(plane) { - // Distance to plane - var d = vec3.dot(plane.normal._array, this.direction._array); - vec3.scaleAndAdd(this.direction._array, this.direction._array, plane.normal._array, -d * 2); - this.direction._dirty = true; - }, - - distanceToPoint: (function () { - var v = vec3.create(); - return function (point) { - vec3.sub(v, point, this.origin._array); - // Distance from projection point to origin - var b = vec3.dot(v, this.direction._array); - if (b < 0) { - return vec3.distance(this.origin._array, point); - } - // Squared distance from center to origin - var c2 = vec3.lenSquared(v); - // Squared distance from center to projection point - return Math.sqrt(c2 - b * b); - }; - })(), - - /** - * Calculate intersection point between ray and sphere - * @param {qtek.math.Vector3} center - * @param {number} radius - * @param {qtek.math.Vector3} out - * @return {qtek.math.Vector3} - */ - intersectSphere: (function () { - var v = vec3.create(); - return function (center, radius, out) { - var origin = this.origin._array; - var direction = this.direction._array; - center = center._array; - vec3.sub(v, center, origin); - // Distance from projection point to origin - var b = vec3.dot(v, direction); - // Squared distance from center to origin - var c2 = vec3.squaredLength(v); - // Squared distance from center to projection point - var d2 = c2 - b * b; - - var r2 = radius * radius; - // No intersection - if (d2 > r2) { - return; - } - - var a = Math.sqrt(r2 - d2); - // First intersect point - var t0 = b - a; - // Second intersect point - var t1 = b + a; - - if (!out) { - out = new Vector3(); - } - if (t0 < 0) { - if (t1 < 0) { - return null; - } else { - vec3.scaleAndAdd(out._array, origin, direction, t1); - return out; - } - } else { - vec3.scaleAndAdd(out._array, origin, direction, t0); - return out; - } - }; - })(), - - // http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/ - /** - * Calculate intersection point between ray and bounding box - * @param {qtek.math.BoundingBox} bbox - * @param {qtek.math.Vector3} - * @return {qtek.math.Vector3} - */ - intersectBoundingBox: function(bbox, out) { - var dir = this.direction._array; - var origin = this.origin._array; - var min = bbox.min._array; - var max = bbox.max._array; - - var invdirx = 1 / dir[0]; - var invdiry = 1 / dir[1]; - var invdirz = 1 / dir[2]; - - var tmin, tmax, tymin, tymax, tzmin, tzmax; - if (invdirx >= 0) { - tmin = (min[0] - origin[0]) * invdirx; - tmax = (max[0] - origin[0]) * invdirx; - } else { - tmax = (min[0] - origin[0]) * invdirx; - tmin = (max[0] - origin[0]) * invdirx; - } - if (invdiry >= 0) { - tymin = (min[1] - origin[1]) * invdiry; - tymax = (max[1] - origin[1]) * invdiry; - } else { - tymax = (min[1] - origin[1]) * invdiry; - tymin = (max[1] - origin[1]) * invdiry; - } - - if ((tmin > tymax) || (tymin > tmax)) { - return null; - } - - if (tymin > tmin || tmin !== tmin) { - tmin = tymin; - } - if (tymax < tmax || tmax !== tmax) { - tmax = tymax; - } - - if (invdirz >= 0) { - tzmin = (min[2] - origin[2]) * invdirz; - tzmax = (max[2] - origin[2]) * invdirz; - } else { - tzmax = (min[2] - origin[2]) * invdirz; - tzmin = (max[2] - origin[2]) * invdirz; - } - - if ((tmin > tzmax) || (tzmin > tmax)) { - return null; - } - - if (tzmin > tmin || tmin !== tmin) { - tmin = tzmin; - } - if (tzmax < tmax || tmax !== tmax) { - tmax = tzmax; - } - if (tmax < 0) { - return null; - } - - var t = tmin >= 0 ? tmin : tmax; - - if (!out) { - out = new Vector3(); - } - vec3.scaleAndAdd(out._array, origin, dir, t); - return out; - }, - - // http://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm - /** - * Calculate intersection point between ray and three triangle vertices - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @param {qtek.math.Vector3} c - * @param {boolean} singleSided, CW triangle will be ignored - * @param {qtek.math.Vector3} [out] - * @param {qtek.math.Vector3} [barycenteric] barycentric coords - * @return {qtek.math.Vector3} - */ - intersectTriangle: (function() { - - var eBA = vec3.create(); - var eCA = vec3.create(); - var AO = vec3.create(); - var vCross = vec3.create(); - - return function(a, b, c, singleSided, out, barycenteric) { - var dir = this.direction._array; - var origin = this.origin._array; - a = a._array; - b = b._array; - c = c._array; - - vec3.sub(eBA, b, a); - vec3.sub(eCA, c, a); - - vec3.cross(vCross, eCA, dir); - - var det = vec3.dot(eBA, vCross); - - if (singleSided) { - if (det > -EPSILON) { - return null; - } - } - else { - if (det > -EPSILON && det < EPSILON) { - return null; - } - } - - vec3.sub(AO, origin, a); - var u = vec3.dot(vCross, AO) / det; - if (u < 0 || u > 1) { - return null; - } - - vec3.cross(vCross, eBA, AO); - var v = vec3.dot(dir, vCross) / det; - - if (v < 0 || v > 1 || (u + v > 1)) { - return null; - } - - vec3.cross(vCross, eBA, eCA); - var t = -vec3.dot(AO, vCross) / det; - - if (t < 0) { - return null; - } - - if (!out) { - out = new Vector3(); - } - if (barycenteric) { - Vector3.set(barycenteric, (1 - u - v), u, v); - } - vec3.scaleAndAdd(out._array, origin, dir, t); - - return out; - }; - })(), - - /** - * Apply an affine transform matrix to the ray - * @return {qtek.math.Matrix4} matrix - */ - applyTransform: function(matrix) { - Vector3.add(this.direction, this.direction, this.origin); - Vector3.transformMat4(this.origin, this.origin, matrix); - Vector3.transformMat4(this.direction, this.direction, matrix); - - Vector3.sub(this.direction, this.direction, this.origin); - Vector3.normalize(this.direction, this.direction); - }, - - /** - * Copy values from another ray - * @param {qtek.math.Ray} ray - */ - copy: function(ray) { - Vector3.copy(this.origin, ray.origin); - Vector3.copy(this.direction, ray.direction); - }, - - /** - * Clone a new ray - * @return {qtek.math.Ray} - */ - clone: function() { - var ray = new Ray(); - ray.copy(this); - return ray; - } - }; - - return Ray; -}); -define('qtek/Camera',['require','./Node','./math/Matrix4','./math/Frustum','./math/BoundingBox','./math/Ray','./dep/glmatrix'],function(require) { - - - - var Node = require('./Node'); - var Matrix4 = require('./math/Matrix4'); - var Frustum = require('./math/Frustum'); - var BoundingBox = require('./math/BoundingBox'); - var Ray = require('./math/Ray'); - - var glMatrix = require('./dep/glmatrix'); - var mat4 = glMatrix.mat4; - var vec3 = glMatrix.vec3; - var vec4 = glMatrix.vec4; - - /** - * @constructor qtek.Camera - * @extends qtek.Node - */ - var Camera = Node.derive(function() { - return /** @lends qtek.Camera# */ { - /** - * Camera projection matrix - * @type {qtek.math.Matrix4} - */ - projectionMatrix: new Matrix4(), - - /** - * Inverse of camera projection matrix - * @type {qtek.math.Matrix4} - */ - invProjectionMatrix: new Matrix4(), - - /** - * View matrix, equal to inverse of camera's world matrix - * @type {qtek.math.Matrix4} - */ - viewMatrix: new Matrix4(), - - /** - * Camera frustum in view space - * @type {qtek.math.Frustum} - */ - frustum: new Frustum(), - - /** - * Scene bounding box in view space. - * Used when camera needs to adujst the near and far plane automatically - * so that the view frustum contains the visible objects as tightly as possible. - * Notice: - * It is updated after rendering (in the step of frustum culling passingly). So may be not so accurate, but saves a lot of calculation - * - * @type {qtek.math.BoundingBox} - */ - //TODO In case of one camera to multiple scenes - sceneBoundingBoxLastFrame: new BoundingBox() - }; - }, function() { - this.update(true); - }, - /** @lends qtek.Camera.prototype */ - { - - update: function(force) { - Node.prototype.update.call(this, force); - mat4.invert(this.viewMatrix._array, this.worldTransform._array); - - this.updateProjectionMatrix(); - mat4.invert(this.invProjectionMatrix._array, this.projectionMatrix._array); - - this.frustum.setFromProjection(this.projectionMatrix); - }, - /** - * Update projection matrix, called after update - */ - updateProjectionMatrix: function(){}, - - /** - * Cast a picking ray from camera near plane to far plane - * @method - * @param {qtek.math.Vector2} ndc - * @param {qtek.math.Ray} [out] - * @return {qtek.math.Ray} - */ - castRay: (function() { - var v4 = vec4.create(); - return function(ndc, out) { - var ray = out !== undefined ? out : new Ray(); - var x = ndc._array[0]; - var y = ndc._array[1]; - vec4.set(v4, x, y, -1, 1); - vec4.transformMat4(v4, v4, this.invProjectionMatrix._array); - vec4.transformMat4(v4, v4, this.worldTransform._array); - vec3.scale(ray.origin._array, v4, 1 / v4[3]); - - vec4.set(v4, x, y, 1, 1); - vec4.transformMat4(v4, v4, this.invProjectionMatrix._array); - vec4.transformMat4(v4, v4, this.worldTransform._array); - vec3.scale(v4, v4, 1 / v4[3]); - vec3.sub(ray.direction._array, v4, ray.origin._array); - - vec3.normalize(ray.direction._array, ray.direction._array); - ray.direction._dirty = true; - ray.origin._dirty = true; - - return ray; - }; - })() - - /** - * @method - * @name clone - * @return {qtek.Camera} - * @memberOf qtek.Camera.prototype - */ - }); - - return Camera; -}); -/** - * @namespace qtek.core.glenum - * @see http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14 - */ -define('qtek/core/glenum',[],function() { - -return { - /* ClearBufferMask */ - DEPTH_BUFFER_BIT : 0x00000100, - STENCIL_BUFFER_BIT : 0x00000400, - COLOR_BUFFER_BIT : 0x00004000, - - /* BeginMode */ - POINTS : 0x0000, - LINES : 0x0001, - LINE_LOOP : 0x0002, - LINE_STRIP : 0x0003, - TRIANGLES : 0x0004, - TRIANGLE_STRIP : 0x0005, - TRIANGLE_FAN : 0x0006, - - /* AlphaFunction (not supported in ES20) */ - /* NEVER */ - /* LESS */ - /* EQUAL */ - /* LEQUAL */ - /* GREATER */ - /* NOTEQUAL */ - /* GEQUAL */ - /* ALWAYS */ - - /* BlendingFactorDest */ - ZERO : 0, - ONE : 1, - SRC_COLOR : 0x0300, - ONE_MINUS_SRC_COLOR : 0x0301, - SRC_ALPHA : 0x0302, - ONE_MINUS_SRC_ALPHA : 0x0303, - DST_ALPHA : 0x0304, - ONE_MINUS_DST_ALPHA : 0x0305, - - /* BlendingFactorSrc */ - /* ZERO */ - /* ONE */ - DST_COLOR : 0x0306, - ONE_MINUS_DST_COLOR : 0x0307, - SRC_ALPHA_SATURATE : 0x0308, - /* SRC_ALPHA */ - /* ONE_MINUS_SRC_ALPHA */ - /* DST_ALPHA */ - /* ONE_MINUS_DST_ALPHA */ - - /* BlendEquationSeparate */ - FUNC_ADD : 0x8006, - BLEND_EQUATION : 0x8009, - BLEND_EQUATION_RGB : 0x8009, /* same as BLEND_EQUATION */ - BLEND_EQUATION_ALPHA : 0x883D, - - /* BlendSubtract */ - FUNC_SUBTRACT : 0x800A, - FUNC_REVERSE_SUBTRACT : 0x800B, - - /* Separate Blend Functions */ - BLEND_DST_RGB : 0x80C8, - BLEND_SRC_RGB : 0x80C9, - BLEND_DST_ALPHA : 0x80CA, - BLEND_SRC_ALPHA : 0x80CB, - CONSTANT_COLOR : 0x8001, - ONE_MINUS_CONSTANT_COLOR : 0x8002, - CONSTANT_ALPHA : 0x8003, - ONE_MINUS_CONSTANT_ALPHA : 0x8004, - BLEND_COLOR : 0x8005, - - /* Buffer Objects */ - ARRAY_BUFFER : 0x8892, - ELEMENT_ARRAY_BUFFER : 0x8893, - ARRAY_BUFFER_BINDING : 0x8894, - ELEMENT_ARRAY_BUFFER_BINDING : 0x8895, - - STREAM_DRAW : 0x88E0, - STATIC_DRAW : 0x88E4, - DYNAMIC_DRAW : 0x88E8, - - BUFFER_SIZE : 0x8764, - BUFFER_USAGE : 0x8765, - - CURRENT_VERTEX_ATTRIB : 0x8626, - - /* CullFaceMode */ - FRONT : 0x0404, - BACK : 0x0405, - FRONT_AND_BACK : 0x0408, - - /* DepthFunction */ - /* NEVER */ - /* LESS */ - /* EQUAL */ - /* LEQUAL */ - /* GREATER */ - /* NOTEQUAL */ - /* GEQUAL */ - /* ALWAYS */ - - /* EnableCap */ - /* TEXTURE_2D */ - CULL_FACE : 0x0B44, - BLEND : 0x0BE2, - DITHER : 0x0BD0, - STENCIL_TEST : 0x0B90, - DEPTH_TEST : 0x0B71, - SCISSOR_TEST : 0x0C11, - POLYGON_OFFSET_FILL : 0x8037, - SAMPLE_ALPHA_TO_COVERAGE : 0x809E, - SAMPLE_COVERAGE : 0x80A0, - - /* ErrorCode */ - NO_ERROR : 0, - INVALID_ENUM : 0x0500, - INVALID_VALUE : 0x0501, - INVALID_OPERATION : 0x0502, - OUT_OF_MEMORY : 0x0505, - - /* FrontFaceDirection */ - CW : 0x0900, - CCW : 0x0901, - - /* GetPName */ - LINE_WIDTH : 0x0B21, - ALIASED_POINT_SIZE_RANGE : 0x846D, - ALIASED_LINE_WIDTH_RANGE : 0x846E, - CULL_FACE_MODE : 0x0B45, - FRONT_FACE : 0x0B46, - DEPTH_RANGE : 0x0B70, - DEPTH_WRITEMASK : 0x0B72, - DEPTH_CLEAR_VALUE : 0x0B73, - DEPTH_FUNC : 0x0B74, - STENCIL_CLEAR_VALUE : 0x0B91, - STENCIL_FUNC : 0x0B92, - STENCIL_FAIL : 0x0B94, - STENCIL_PASS_DEPTH_FAIL : 0x0B95, - STENCIL_PASS_DEPTH_PASS : 0x0B96, - STENCIL_REF : 0x0B97, - STENCIL_VALUE_MASK : 0x0B93, - STENCIL_WRITEMASK : 0x0B98, - STENCIL_BACK_FUNC : 0x8800, - STENCIL_BACK_FAIL : 0x8801, - STENCIL_BACK_PASS_DEPTH_FAIL : 0x8802, - STENCIL_BACK_PASS_DEPTH_PASS : 0x8803, - STENCIL_BACK_REF : 0x8CA3, - STENCIL_BACK_VALUE_MASK : 0x8CA4, - STENCIL_BACK_WRITEMASK : 0x8CA5, - VIEWPORT : 0x0BA2, - SCISSOR_BOX : 0x0C10, - /* SCISSOR_TEST */ - COLOR_CLEAR_VALUE : 0x0C22, - COLOR_WRITEMASK : 0x0C23, - UNPACK_ALIGNMENT : 0x0CF5, - PACK_ALIGNMENT : 0x0D05, - MAX_TEXTURE_SIZE : 0x0D33, - MAX_VIEWPORT_DIMS : 0x0D3A, - SUBPIXEL_BITS : 0x0D50, - RED_BITS : 0x0D52, - GREEN_BITS : 0x0D53, - BLUE_BITS : 0x0D54, - ALPHA_BITS : 0x0D55, - DEPTH_BITS : 0x0D56, - STENCIL_BITS : 0x0D57, - POLYGON_OFFSET_UNITS : 0x2A00, - /* POLYGON_OFFSET_FILL */ - POLYGON_OFFSET_FACTOR : 0x8038, - TEXTURE_BINDING_2D : 0x8069, - SAMPLE_BUFFERS : 0x80A8, - SAMPLES : 0x80A9, - SAMPLE_COVERAGE_VALUE : 0x80AA, - SAMPLE_COVERAGE_INVERT : 0x80AB, - - /* GetTextureParameter */ - /* TEXTURE_MAG_FILTER */ - /* TEXTURE_MIN_FILTER */ - /* TEXTURE_WRAP_S */ - /* TEXTURE_WRAP_T */ - - COMPRESSED_TEXTURE_FORMATS : 0x86A3, - - /* HintMode */ - DONT_CARE : 0x1100, - FASTEST : 0x1101, - NICEST : 0x1102, - - /* HintTarget */ - GENERATE_MIPMAP_HINT : 0x8192, - - /* DataType */ - BYTE : 0x1400, - UNSIGNED_BYTE : 0x1401, - SHORT : 0x1402, - UNSIGNED_SHORT : 0x1403, - INT : 0x1404, - UNSIGNED_INT : 0x1405, - FLOAT : 0x1406, - - /* PixelFormat */ - DEPTH_COMPONENT : 0x1902, - ALPHA : 0x1906, - RGB : 0x1907, - RGBA : 0x1908, - LUMINANCE : 0x1909, - LUMINANCE_ALPHA : 0x190A, - - /* PixelType */ - /* UNSIGNED_BYTE */ - UNSIGNED_SHORT_4_4_4_4 : 0x8033, - UNSIGNED_SHORT_5_5_5_1 : 0x8034, - UNSIGNED_SHORT_5_6_5 : 0x8363, - - /* Shaders */ - FRAGMENT_SHADER : 0x8B30, - VERTEX_SHADER : 0x8B31, - MAX_VERTEX_ATTRIBS : 0x8869, - MAX_VERTEX_UNIFORM_VECTORS : 0x8DFB, - MAX_VARYING_VECTORS : 0x8DFC, - MAX_COMBINED_TEXTURE_IMAGE_UNITS : 0x8B4D, - MAX_VERTEX_TEXTURE_IMAGE_UNITS : 0x8B4C, - MAX_TEXTURE_IMAGE_UNITS : 0x8872, - MAX_FRAGMENT_UNIFORM_VECTORS : 0x8DFD, - SHADER_TYPE : 0x8B4F, - DELETE_STATUS : 0x8B80, - LINK_STATUS : 0x8B82, - VALIDATE_STATUS : 0x8B83, - ATTACHED_SHADERS : 0x8B85, - ACTIVE_UNIFORMS : 0x8B86, - ACTIVE_ATTRIBUTES : 0x8B89, - SHADING_LANGUAGE_VERSION : 0x8B8C, - CURRENT_PROGRAM : 0x8B8D, - - /* StencilFunction */ - NEVER : 0x0200, - LESS : 0x0201, - EQUAL : 0x0202, - LEQUAL : 0x0203, - GREATER : 0x0204, - NOTEQUAL : 0x0205, - GEQUAL : 0x0206, - ALWAYS : 0x0207, - - /* StencilOp */ - /* ZERO */ - KEEP : 0x1E00, - REPLACE : 0x1E01, - INCR : 0x1E02, - DECR : 0x1E03, - INVERT : 0x150A, - INCR_WRAP : 0x8507, - DECR_WRAP : 0x8508, - - /* StringName */ - VENDOR : 0x1F00, - RENDERER : 0x1F01, - VERSION : 0x1F02, - - /* TextureMagFilter */ - NEAREST : 0x2600, - LINEAR : 0x2601, - - /* TextureMinFilter */ - /* NEAREST */ - /* LINEAR */ - NEAREST_MIPMAP_NEAREST : 0x2700, - LINEAR_MIPMAP_NEAREST : 0x2701, - NEAREST_MIPMAP_LINEAR : 0x2702, - LINEAR_MIPMAP_LINEAR : 0x2703, - - /* TextureParameterName */ - TEXTURE_MAG_FILTER : 0x2800, - TEXTURE_MIN_FILTER : 0x2801, - TEXTURE_WRAP_S : 0x2802, - TEXTURE_WRAP_T : 0x2803, - - /* TextureTarget */ - TEXTURE_2D : 0x0DE1, - TEXTURE : 0x1702, - - TEXTURE_CUBE_MAP : 0x8513, - TEXTURE_BINDING_CUBE_MAP : 0x8514, - TEXTURE_CUBE_MAP_POSITIVE_X : 0x8515, - TEXTURE_CUBE_MAP_NEGATIVE_X : 0x8516, - TEXTURE_CUBE_MAP_POSITIVE_Y : 0x8517, - TEXTURE_CUBE_MAP_NEGATIVE_Y : 0x8518, - TEXTURE_CUBE_MAP_POSITIVE_Z : 0x8519, - TEXTURE_CUBE_MAP_NEGATIVE_Z : 0x851A, - MAX_CUBE_MAP_TEXTURE_SIZE : 0x851C, - - /* TextureUnit */ - TEXTURE0 : 0x84C0, - TEXTURE1 : 0x84C1, - TEXTURE2 : 0x84C2, - TEXTURE3 : 0x84C3, - TEXTURE4 : 0x84C4, - TEXTURE5 : 0x84C5, - TEXTURE6 : 0x84C6, - TEXTURE7 : 0x84C7, - TEXTURE8 : 0x84C8, - TEXTURE9 : 0x84C9, - TEXTURE10 : 0x84CA, - TEXTURE11 : 0x84CB, - TEXTURE12 : 0x84CC, - TEXTURE13 : 0x84CD, - TEXTURE14 : 0x84CE, - TEXTURE15 : 0x84CF, - TEXTURE16 : 0x84D0, - TEXTURE17 : 0x84D1, - TEXTURE18 : 0x84D2, - TEXTURE19 : 0x84D3, - TEXTURE20 : 0x84D4, - TEXTURE21 : 0x84D5, - TEXTURE22 : 0x84D6, - TEXTURE23 : 0x84D7, - TEXTURE24 : 0x84D8, - TEXTURE25 : 0x84D9, - TEXTURE26 : 0x84DA, - TEXTURE27 : 0x84DB, - TEXTURE28 : 0x84DC, - TEXTURE29 : 0x84DD, - TEXTURE30 : 0x84DE, - TEXTURE31 : 0x84DF, - ACTIVE_TEXTURE : 0x84E0, - - /* TextureWrapMode */ - REPEAT : 0x2901, - CLAMP_TO_EDGE : 0x812F, - MIRRORED_REPEAT : 0x8370, - - /* Uniform Types */ - FLOAT_VEC2 : 0x8B50, - FLOAT_VEC3 : 0x8B51, - FLOAT_VEC4 : 0x8B52, - INT_VEC2 : 0x8B53, - INT_VEC3 : 0x8B54, - INT_VEC4 : 0x8B55, - BOOL : 0x8B56, - BOOL_VEC2 : 0x8B57, - BOOL_VEC3 : 0x8B58, - BOOL_VEC4 : 0x8B59, - FLOAT_MAT2 : 0x8B5A, - FLOAT_MAT3 : 0x8B5B, - FLOAT_MAT4 : 0x8B5C, - SAMPLER_2D : 0x8B5E, - SAMPLER_CUBE : 0x8B60, - - /* Vertex Arrays */ - VERTEX_ATTRIB_ARRAY_ENABLED : 0x8622, - VERTEX_ATTRIB_ARRAY_SIZE : 0x8623, - VERTEX_ATTRIB_ARRAY_STRIDE : 0x8624, - VERTEX_ATTRIB_ARRAY_TYPE : 0x8625, - VERTEX_ATTRIB_ARRAY_NORMALIZED : 0x886A, - VERTEX_ATTRIB_ARRAY_POINTER : 0x8645, - VERTEX_ATTRIB_ARRAY_BUFFER_BINDING : 0x889F, - - /* Shader Source */ - COMPILE_STATUS : 0x8B81, - - /* Shader Precision-Specified Types */ - LOW_FLOAT : 0x8DF0, - MEDIUM_FLOAT : 0x8DF1, - HIGH_FLOAT : 0x8DF2, - LOW_INT : 0x8DF3, - MEDIUM_INT : 0x8DF4, - HIGH_INT : 0x8DF5, - - /* Framebuffer Object. */ - FRAMEBUFFER : 0x8D40, - RENDERBUFFER : 0x8D41, - - RGBA4 : 0x8056, - RGB5_A1 : 0x8057, - RGB565 : 0x8D62, - DEPTH_COMPONENT16 : 0x81A5, - STENCIL_INDEX : 0x1901, - STENCIL_INDEX8 : 0x8D48, - DEPTH_STENCIL : 0x84F9, - - RENDERBUFFER_WIDTH : 0x8D42, - RENDERBUFFER_HEIGHT : 0x8D43, - RENDERBUFFER_INTERNAL_FORMAT : 0x8D44, - RENDERBUFFER_RED_SIZE : 0x8D50, - RENDERBUFFER_GREEN_SIZE : 0x8D51, - RENDERBUFFER_BLUE_SIZE : 0x8D52, - RENDERBUFFER_ALPHA_SIZE : 0x8D53, - RENDERBUFFER_DEPTH_SIZE : 0x8D54, - RENDERBUFFER_STENCIL_SIZE : 0x8D55, - - FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE : 0x8CD0, - FRAMEBUFFER_ATTACHMENT_OBJECT_NAME : 0x8CD1, - FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL : 0x8CD2, - FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE : 0x8CD3, - - COLOR_ATTACHMENT0 : 0x8CE0, - DEPTH_ATTACHMENT : 0x8D00, - STENCIL_ATTACHMENT : 0x8D20, - DEPTH_STENCIL_ATTACHMENT : 0x821A, - - NONE : 0, - - FRAMEBUFFER_COMPLETE : 0x8CD5, - FRAMEBUFFER_INCOMPLETE_ATTACHMENT : 0x8CD6, - FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT : 0x8CD7, - FRAMEBUFFER_INCOMPLETE_DIMENSIONS : 0x8CD9, - FRAMEBUFFER_UNSUPPORTED : 0x8CDD, - - FRAMEBUFFER_BINDING : 0x8CA6, - RENDERBUFFER_BINDING : 0x8CA7, - MAX_RENDERBUFFER_SIZE : 0x84E8, - - INVALID_FRAMEBUFFER_OPERATION : 0x0506, - - /* WebGL-specific enums */ - UNPACK_FLIP_Y_WEBGL : 0x9240, - UNPACK_PREMULTIPLY_ALPHA_WEBGL : 0x9241, - CONTEXT_LOST_WEBGL : 0x9242, - UNPACK_COLORSPACE_CONVERSION_WEBGL : 0x9243, - BROWSER_DEFAULT_WEBGL : 0x9244, -}; -}); -define('qtek/core/Cache',[],function() { - - - - var Cache = function() { - - this._contextId = 0; - - this._caches = []; - - this._context = {}; - }; - - Cache.prototype = { - - use: function(contextId, documentSchema) { - - if (! this._caches[contextId]) { - this._caches[contextId] = {}; - - if (documentSchema) { - this._caches[contextId] = documentSchema(); - } - } - this._contextId = contextId; - - this._context = this._caches[contextId]; - }, - - put: function(key, value) { - this._context[key] = value; - }, - - get: function(key) { - return this._context[key]; - }, - - dirty: function(field) { - field = field || ''; - var key = '__dirty__' + field; - this.put(key, true); - }, - - dirtyAll: function(field) { - field = field || ''; - var key = '__dirty__' + field; - for (var i = 0; i < this._caches.length; i++) { - if (this._caches[i]) { - this._caches[i][key] = true; - } - } - }, - - fresh: function(field) { - field = field || ''; - var key = '__dirty__' + field; - this.put(key, false); - }, - - freshAll: function(field) { - field = field || ''; - var key = '__dirty__' + field; - for (var i = 0; i < this._caches.length; i++) { - if (this._caches[i]) { - this._caches[i][key] = false; - } - } - }, - - isDirty: function(field) { - field = field || ''; - var key = '__dirty__' + field; - return !this._context.hasOwnProperty(key) - || this._context[key] === true; - }, - - deleteContext: function(contextId) { - delete this._caches[contextId]; - this._context = {}; - }, - - 'delete': function(key) { - delete this._context[key]; - }, - - clearAll: function() { - this._caches = {}; - }, - - getContext: function() { - return this._context; - }, - - miss: function(key) { - return ! this._context.hasOwnProperty(key); - } - }; - - Cache.prototype.constructor = Cache; - - return Cache; - -}); -define('qtek/Geometry',['require','./core/Base','./core/glenum','./core/Cache','./dep/glmatrix'],function(require) { - - - - var Base = require('./core/Base'); - var glenum = require('./core/glenum'); - var Cache = require('./core/Cache'); - var glmatrix = require('./dep/glmatrix'); - var vec2 = glmatrix.vec2; - var vec3 = glmatrix.vec3; - var vec4 = glmatrix.vec4; - - // PENDING put the buffer data in attribute ? - function Attribute(name, type, size, semantic, isDynamic) { - this.name = name; - this.type = type; - this.size = size; - if (semantic) { - this.semantic = semantic; - } - if (isDynamic) { - this._isDynamic = true; - this.value = []; - } else { - this._isDynamic = false; - this.value = null; - } - - // Init getter setter - switch (size) { - case 1: - this.get = function (idx) { - return this.value[idx]; - }; - this.set = function (idx, value) { - this.value[idx] = value; - }; - break; - case 2: - if (isDynamic) { - this.get = function (idx, out) { - out = out._array || out; - var item = this.value[idx]; - if (item) { - vec2.copy(out, item); - } - return out; - }; - this.set = function (idx, val) { - val = val._array || val; - var item = this.value[idx]; - if (!item) { - item = this.value[idx] = vec2.create(); - } - vec2.copy(item, val); - }; - } else { - this.get = function (idx, out) { - out = out._array || out; - out[0] = this.value[idx * 2]; - out[1] = this.value[idx * 2 + 1]; - return out; - }; - this.set = function (idx, val) { - val = val._array || val; - this.value[idx * 2] = val[0]; - this.value[idx * 2 + 1] = val[1]; - }; - } - break; - case 3: - if (isDynamic) { - this.get = function (idx, out) { - out = out._array || out; - var item = this.value[idx]; - if (item) { - vec3.copy(out, item); - } - return out; - }; - this.set = function (idx, val) { - val = val._array || val; - var item = this.value[idx]; - if (!item) { - item = this.value[idx] = vec3.create(); - } - vec3.copy(item, val); - }; - } else { - this.get = function (idx, out) { - out = out._array || out; - out[0] = this.value[idx * 3]; - out[1] = this.value[idx * 3 + 1]; - out[2] = this.value[idx * 3 + 2]; - return out; - }; - this.set = function (idx, val) { - val = val._array || val; - this.value[idx * 3] = val[0]; - this.value[idx * 3 + 1] = val[1]; - this.value[idx * 3 + 2] = val[2]; - }; - } - break; - case 4: - if (isDynamic) { - this.get = function (idx, out) { - out = out._array || out; - var item = this.value[idx]; - if (item) { - vec4.copy(out, item); - } - return out; - }; - this.set = function (idx, val) { - val = val._array || val; - var item = this.value[idx]; - if (!item) { - item = this.value[idx] = vec4.create(); - } - vec4.copy(item, val); - }; - } else { - this.get = function (idx, out) { - out = out._array || out; - out[0] = this.value[idx * 4]; - out[1] = this.value[idx * 4 + 1]; - out[2] = this.value[idx * 4 + 2]; - out[3] = this.value[idx * 4 + 3]; - return out; - }; - this.set = function (idx, val) { - val = val._array || val; - this.value[idx * 4] = val[0]; - this.value[idx * 4 + 1] = val[1]; - this.value[idx * 4 + 2] = val[2]; - this.value[idx * 4 + 3] = val[3]; - }; - } - break; - } - } - - Attribute.prototype.init = function(nVertex) { - if (!this._isDynamic) { - if (!this.value || this.value.length != nVertex * this.size) { - var ArrayConstructor; - switch(this.type) { - case 'byte': - ArrayConstructor = Int8Array; - break; - case 'ubyte': - ArrayConstructor = Uint8Array; - break; - case 'short': - ArrayConstructor = Int16Array; - break; - case 'ushort': - ArrayConstructor = Uint16Array; - break; - default: - ArrayConstructor = Float32Array; - break; - } - this.value = new ArrayConstructor(nVertex * this.size); - } - } else { - console.warn('Dynamic geometry not support init method'); - } - }; - - Attribute.prototype.clone = function(copyValue) { - var ret = new Attribute(this.name, this.type, this.size, this.semantic, this._isDynamic); - if (copyValue) { - console.warn('todo'); - } - - return ret; - }; - - - function AttributeBuffer(name, type, buffer, size, semantic) { - this.name = name; - this.type = type; - this.buffer = buffer; - this.size = size; - this.semantic = semantic; - - // To be set in mesh - // symbol in the shader - this.symbol = ''; - } - - function IndicesBuffer(buffer) { - this.buffer = buffer; - this.count = 0; - } - - function notImplementedWarn() { - console.warn('Geometry doesn\'t implement this method, use DynamicGeometry or StaticGeometry instead'); - } - - /** - * @constructor qtek.Geometry - * @extends qtek.core.Base - */ - var Geometry = Base.derive( - /** @lends qtek.Geometry# */ - { - /** - * @type {qtek.math.BoundingBox} - */ - boundingBox : null, - - /** - * Vertex attributes - * @type {Object} - */ - attributes : {}, - - faces : null, - - /** - * Is vertices data dynamically updated - * @type {boolean} - */ - dynamic: false, - - /** - * @type {boolean} - */ - useFace : true - - }, function() { - // Use cache - this._cache = new Cache(); - - this._attributeList = Object.keys(this.attributes); - }, - /** @lends qtek.Geometry.prototype */ - { - /** - * User defined ray picking algorithm instead of default - * triangle ray intersection - * @type {Function} - */ - pickByRay: null, - - /** - * Main attribute will be used to count vertex number - * @type {string} - */ - mainAttribute: 'position', - /** - * Mark attributes in geometry is dirty - * @method - */ - dirty: notImplementedWarn, - /** - * Create a new attribute - * @method - * @param {string} name - * @param {string} type - * @param {number} size - * @param {string} [semantic] - */ - createAttribute: notImplementedWarn, - /** - * Remove attribute - * @method - * @param {string} name - */ - removeAttribute: notImplementedWarn, - /** - * @method - * @return {number} - */ - getVertexNumber: notImplementedWarn, - /** - * @method - * @return {number} - */ - getFaceNumber: notImplementedWarn, - /** - * @method - * @param {number} idx - * @param {qtek.math.Vector3} out - * @return {qtek.math.Vector3} - */ - getFace: notImplementedWarn, - /** - * @method - * @return {boolean} - */ - isUseFace: notImplementedWarn, - - getEnabledAttributes: notImplementedWarn, - getBufferChunks: notImplementedWarn, - - /** - * @method - */ - generateVertexNormals: notImplementedWarn, - /** - * @method - */ - generateFaceNormals: notImplementedWarn, - /** - * @method - * @return {boolean} - */ - isUniqueVertex: notImplementedWarn, - /** - * @method - */ - generateUniqueVertex: notImplementedWarn, - /** - * @method - */ - generateTangents: notImplementedWarn, - /** - * @method - */ - generateBarycentric: notImplementedWarn, - /** - * @method - * @param {qtek.math.Matrix4} matrix - */ - applyTransform: notImplementedWarn, - /** - * @method - * @param {WebGLRenderingContext} gl - */ - dispose: notImplementedWarn - }); - - Geometry.STATIC_DRAW = glenum.STATIC_DRAW; - Geometry.DYNAMIC_DRAW = glenum.DYNAMIC_DRAW; - Geometry.STREAM_DRAW = glenum.STREAM_DRAW; - - Geometry.AttributeBuffer = AttributeBuffer; - Geometry.IndicesBuffer = IndicesBuffer; - Geometry.Attribute = Attribute; - - return Geometry; -}); -/** - * @namespace qtek.core.glinfo - * @see http://www.khronos.org/registry/webgl/extensions/ - */ -define('qtek/core/glinfo',[],function() { - - - - var EXTENSION_LIST = [ - 'OES_texture_float', - 'OES_texture_half_float', - 'OES_texture_float_linear', - 'OES_texture_half_float_linear', - 'OES_standard_derivatives', - 'OES_vertex_array_object', - 'OES_element_index_uint', - 'WEBGL_compressed_texture_s3tc', - 'WEBGL_depth_texture', - 'EXT_texture_filter_anisotropic', - 'EXT_shader_texture_lod', - 'WEBGL_draw_buffers' - ]; - - var PARAMETER_NAMES = [ - 'MAX_TEXTURE_SIZE', - 'MAX_CUBE_MAP_TEXTURE_SIZE' - ] - - var extensions = {}; - var parameters = {}; - - var glinfo = { - /** - * Initialize all extensions and parameters in context - * @param {WebGLRenderingContext} _gl - * @memberOf qtek.core.glinfo - */ - initialize: function(_gl) { - var glid = _gl.__GLID__; - if (extensions[glid]) { - return; - } - extensions[glid] = {}; - parameters[glid] = {}; - // Get webgl extension - for (var i = 0; i < EXTENSION_LIST.length; i++) { - var extName = EXTENSION_LIST[i]; - - this._createExtension(_gl, extName); - } - // Get parameters - for (var i = 0; i < PARAMETER_NAMES.length; i++) { - var name = PARAMETER_NAMES[i]; - parameters[glid][name] = _gl.getParameter(_gl[name]); - } - }, - - /** - * Get extension - * @param {WebGLRenderingContext} _gl - * @param {string} name - Extension name, vendorless - * @return {WebGLExtension} - * @memberOf qtek.core.glinfo - */ - getExtension: function(_gl, name) { - var glid = _gl.__GLID__; - if (extensions[glid]) { - if (typeof(extensions[glid][name]) == 'undefined') { - this._createExtension(_gl, name); - } - return extensions[glid][name]; - } - }, - - /** - * Get parameter - * @param {WebGLRenderingContext} _gl - * @param {string} name Parameter name - * @return {*} - */ - getParameter: function(_gl, name) { - var glid = _gl.__GLID__; - if (parameters[glid]) { - return parameters[glid][name]; - } - }, - - /** - * Dispose context - * @param {WebGLRenderingContext} _gl - * @memberOf qtek.core.glinfo - */ - dispose: function(_gl) { - delete extensions[_gl.__GLID__]; - delete parameters[_gl.__GLID__]; - }, - - _createExtension: function(_gl, name) { - var ext = _gl.getExtension(name); - if (!ext) { - ext = _gl.getExtension('MOZ_' + name); - } - if (!ext) { - ext = _gl.getExtension('WEBKIT_' + name); - } - - extensions[_gl.__GLID__][name] = ext; - } - }; - - return glinfo; -}); -/** - * - * PENDING: use perfermance hint and remove the array after the data is transfered? - * static draw & dynamic draw? - */ -define('qtek/DynamicGeometry',['require','./Geometry','./math/BoundingBox','./core/glenum','./core/glinfo','./dep/glmatrix'],function(require) { - - - - var Geometry = require('./Geometry'); - var BoundingBox = require('./math/BoundingBox'); - var glenum = require('./core/glenum'); - var glinfo = require('./core/glinfo'); - - var glMatrix = require('./dep/glmatrix'); - var vec3 = glMatrix.vec3; - var mat4 = glMatrix.mat4; - - var arrSlice = Array.prototype.slice; - /** - * @constructor qtek.DynamicGeometry - * @extends qtek.Geometry - */ - var DynamicGeometry = Geometry.derive(function() { - return /** @lends qtek.DynamicGeometry# */ { - attributes: { - position: new Geometry.Attribute('position', 'float', 3, 'POSITION', true), - texcoord0: new Geometry.Attribute('texcoord0', 'float', 2, 'TEXCOORD_0', true), - texcoord1: new Geometry.Attribute('texcoord1', 'float', 2, 'TEXCOORD_1', true), - normal: new Geometry.Attribute('normal', 'float', 3, 'NORMAL', true), - tangent: new Geometry.Attribute('tangent', 'float', 4, 'TANGENT', true), - color: new Geometry.Attribute('color', 'float', 4, 'COLOR', true), - // Skinning attributes - // Each vertex can be bind to 4 bones, because the - // sum of weights is 1, so the weights is stored in vec3 and the last - // can be calculated by 1-w.x-w.y-w.z - weight: new Geometry.Attribute('weight', 'float', 3, 'WEIGHT', true), - joint: new Geometry.Attribute('joint', 'float', 4, 'JOINT', true), - // For wireframe display - // http://codeflow.org/entries/2012/aug/02/easy-wireframe-display-with-barycentric-coordinates/ - barycentric: new Geometry.Attribute('barycentric', 'float', 3, null, true) - }, - - dynamic: true, - - hint: glenum.DYNAMIC_DRAW, - - // Face is list of triangles, each face - // is an array of the vertex indices of triangle - - /** - * @type {array} - */ - faces: [], - - _enabledAttributes: null, - - // Typed Array of each geometry chunk - // [{ - // attributeArrays:{ - // position: TypedArray - // }, - // indicesArray: null - // }] - _arrayChunks: [] - }; - }, - /** @lends qtek.DynamicGeometry.prototype */ - { - updateBoundingBox: function() { - if (!this.boundingBox) { - this.boundingBox = new BoundingBox(); - } - this.boundingBox.updateFromVertices(this.attributes.position.value); - }, - // Overwrite the dirty method - dirty: function(field) { - if (!field) { - this.dirty('indices'); - for (var name in this.attributes) { - this.dirty(name); - } - return; - } - this._cache.dirtyAll(field); - - this._cache.dirtyAll(); - - this._enabledAttributes = null; - }, - - getVertexNumber: function() { - var mainAttribute = this.attributes[this.mainAttribute]; - if (!mainAttribute || !mainAttribute.value) { - return 0; - } - return mainAttribute.value.length; - }, - - getFaceNumber: function() { - return this.faces.length; - }, - - getFace: function (idx, out) { - if (idx < this.getFaceNumber() && idx >= 0) { - if (!out) { - out = vec3.create(); - } - vec3.copy(out, this.faces[idx]); - - return out; - } - }, - - isUseFace: function() { - return this.useFace && (this.faces.length > 0); - }, - - isSplitted: function() { - return this.getVertexNumber() > 0xffff; - }, - - createAttribute: function(name, type, size, semantic) { - var attrib = new Geometry.Attribute(name, type, size, semantic, true); - this.attributes[name] = attrib; - this._attributeList.push(name); - return attrib; - }, - - removeAttribute: function(name) { - var idx = this._attributeList.indexOf(name); - if (idx >= 0) { - this._attributeList.splice(idx, 1); - delete this.attributes[name]; - return true; - } - return false; - }, - - /** - * Get enabled attributes map. - * Attribute that has same vertex number with position is treated as an enabled attribute - * @return {Object} - */ - getEnabledAttributes: function() { - // Cache - if (this._enabledAttributes) { - return this._enabledAttributes; - } - - var result = {}; - var nVertex = this.getVertexNumber(); - - for (var i = 0; i < this._attributeList.length; i++) { - var name = this._attributeList[i]; - var attrib = this.attributes[name]; - if (attrib.value.length) { - if (attrib.value.length === nVertex) { - result[name] = attrib; - } - } - } - - this._enabledAttributes = result; - - return result; - }, - - _getDirtyAttributes: function() { - - var attributes = this.getEnabledAttributes(); - - if (this._cache.miss('chunks')) { - return attributes; - } else { - var result = {}; - var noDirtyAttributes = true; - for (var name in attributes) { - if (this._cache.isDirty(name)) { - result[name] = attributes[name]; - noDirtyAttributes = false; - } - } - if (! noDirtyAttributes) { - return result; - } - } - }, - - getChunkNumber: function() { - return this._arrayChunks.length; - }, - - getBufferChunks: function(_gl) { - - this._cache.use(_gl.__GLID__); - - if (this._cache.isDirty()) { - var dirtyAttributes = this._getDirtyAttributes(); - - var isFacesDirty = this._cache.isDirty('indices'); - isFacesDirty = isFacesDirty && this.isUseFace(); - - if (dirtyAttributes) { - this._updateAttributesAndIndicesArrays( - dirtyAttributes, isFacesDirty, - glinfo.getExtension(_gl, 'OES_element_index_uint') != null - ); - this._updateBuffer(_gl, dirtyAttributes, isFacesDirty); - - for (var name in dirtyAttributes) { - this._cache.fresh(name); - } - this._cache.fresh('indices'); - this._cache.fresh(); - } - } - return this._cache.get('chunks'); - }, - - _updateAttributesAndIndicesArrays: function(attributes, isFacesDirty, useUintExtension) { - - var self = this; - var nVertex = this.getVertexNumber(); - - var verticesReorganizedMap = []; - var reorganizedFaces = []; - - var ArrayConstructors = {}; - for (var name in attributes) { - // Type can be byte, ubyte, short, ushort, float - switch(type) { - case 'byte': - ArrayConstructors[name] = Int8Array; - break; - case 'ubyte': - ArrayConstructors[name] = Uint8Array; - break; - case 'short': - ArrayConstructors[name] = Int16Array; - break; - case 'ushort': - ArrayConstructors[name] = Uint16Array; - break; - default: - ArrayConstructors[name] = Float32Array; - break; - } - } - - var newChunk = function(chunkIdx) { - if (self._arrayChunks[chunkIdx]) { - return self._arrayChunks[chunkIdx]; - } - var chunk = { - attributeArrays: {}, - indicesArray: null - }; - - for (var name in attributes) { - chunk.attributeArrays[name] = null; - } - - for (var i = 0; i < nVertex; i++) { - verticesReorganizedMap[i] = -1; - } - - self._arrayChunks.push(chunk); - return chunk; - }; - - var attribNameList = Object.keys(attributes); - // Split large geometry into chunks because index buffer - // only can use uint16 which means each draw call can only - // have at most 65535 vertex data - // But now most browsers support OES_element_index_uint extension - if ( - nVertex > 0xffff && this.isUseFace() && !useUintExtension - ) { - var chunkIdx = 0; - var currentChunk; - - var chunkFaceStart = [0]; - var vertexUseCount = []; - - for (i = 0; i < nVertex; i++) { - vertexUseCount[i] = -1; - verticesReorganizedMap[i] = -1; - } - if (isFacesDirty) { - for (i = 0; i < this.faces.length; i++) { - reorganizedFaces[i] = [0, 0, 0]; - } - } - - currentChunk = newChunk(chunkIdx); - - var vertexCount = 0; - for (var i = 0; i < this.faces.length; i++) { - var face = this.faces[i]; - var reorganizedFace = reorganizedFaces[i]; - - // newChunk - if (vertexCount+3 > 0xffff) { - chunkIdx++; - chunkFaceStart[chunkIdx] = i; - vertexCount = 0; - currentChunk = newChunk(chunkIdx); - } - - for (var f = 0; f < 3; f++) { - var ii = face[f]; - var isNew = verticesReorganizedMap[ii] === -1; - - for (var k = 0; k < attribNameList.length; k++) { - var name = attribNameList[k]; - var attribArray = currentChunk.attributeArrays[name]; - var values = attributes[name].value; - var size = attributes[name].size; - if (! attribArray) { - // Here use array to put data temporary because i can't predict - // the size of chunk precisely. - attribArray = currentChunk.attributeArrays[name] = []; - } - if (isNew) { - if (size === 1) { - attribArray[vertexCount] = values[ii]; - } - for (var j = 0; j < size; j++) { - attribArray[vertexCount * size + j] = values[ii][j]; - } - } - } - if (isNew) { - verticesReorganizedMap[ii] = vertexCount; - reorganizedFace[f] = vertexCount; - vertexCount++; - } else { - reorganizedFace[f] = verticesReorganizedMap[ii]; - } - } - } - //Create typedArray from existed array - for (var c = 0; c < this._arrayChunks.length; c++) { - var chunk = this._arrayChunks[c]; - for (var name in chunk.attributeArrays) { - var array = chunk.attributeArrays[name]; - if (array instanceof Array) { - chunk.attributeArrays[name] = new ArrayConstructors[name](array); - } - } - } - - if (isFacesDirty) { - var chunkStart, chunkEnd, cursor, chunk; - for (var c = 0; c < this._arrayChunks.length; c++) { - chunkStart = chunkFaceStart[c]; - chunkEnd = chunkFaceStart[c+1] || this.faces.length; - cursor = 0; - chunk = this._arrayChunks[c]; - var indicesArray = chunk.indicesArray; - if (! indicesArray) { - indicesArray = chunk.indicesArray = new Uint16Array((chunkEnd-chunkStart)*3); - } - - for (var i = chunkStart; i < chunkEnd; i++) { - indicesArray[cursor++] = reorganizedFaces[i][0]; - indicesArray[cursor++] = reorganizedFaces[i][1]; - indicesArray[cursor++] = reorganizedFaces[i][2]; - } - } - } - } else { - var chunk = newChunk(0); - // Use faces - if (isFacesDirty) { - var indicesArray = chunk.indicesArray; - var nFace = this.faces.length; - if (!indicesArray || (nFace * 3 !== indicesArray.length)) { - var ArrayCtor = nVertex > 0xffff ? Uint32Array : Uint16Array; - indicesArray = chunk.indicesArray = new ArrayCtor(this.faces.length * 3); - } - var cursor = 0; - for (var i = 0; i < nFace; i++) { - indicesArray[cursor++] = this.faces[i][0]; - indicesArray[cursor++] = this.faces[i][1]; - indicesArray[cursor++] = this.faces[i][2]; - } - } - for (var name in attributes) { - var values = attributes[name].value; - var type = attributes[name].type; - var size = attributes[name].size; - var attribArray = chunk.attributeArrays[name]; - - var arrSize = nVertex * size; - if (! attribArray || attribArray.length !== arrSize) { - attribArray = new ArrayConstructors[name](arrSize); - chunk.attributeArrays[name] = attribArray; - } - - if (size === 1) { - for (var i = 0; i < values.length; i++) { - attribArray[i] = values[i]; - } - } else { - var cursor = 0; - for (var i = 0; i < values.length; i++) { - for (var j = 0; j < size; j++) { - attribArray[cursor++] = values[i][j]; - } - } - } - } - } - }, - - _updateBuffer: function(_gl, dirtyAttributes, isFacesDirty) { - var chunks = this._cache.get('chunks'); - var firstUpdate = false; - if (! chunks) { - chunks = []; - // Intialize - for (var i = 0; i < this._arrayChunks.length; i++) { - chunks[i] = { - attributeBuffers: [], - indicesBuffer: null - }; - } - this._cache.put('chunks', chunks); - firstUpdate = true; - } - for (var cc = 0; cc < this._arrayChunks.length; cc++) { - var chunk = chunks[cc]; - if (! chunk) { - chunk = chunks[cc] = { - attributeBuffers: [], - indicesBuffer: null - }; - } - var attributeBuffers = chunk.attributeBuffers; - var indicesBuffer = chunk.indicesBuffer; - - var arrayChunk = this._arrayChunks[cc]; - var attributeArrays = arrayChunk.attributeArrays; - var indicesArray = arrayChunk.indicesArray; - - var count = 0; - var prevSearchIdx = 0; - for (var name in dirtyAttributes) { - var attribute = dirtyAttributes[name]; - var type = attribute.type; - var semantic = attribute.semantic; - var size = attribute.size; - - var bufferInfo; - if (!firstUpdate) { - for (var i = prevSearchIdx; i < attributeBuffers.length; i++) { - if (attributeBuffers[i].name === name) { - bufferInfo = attributeBuffers[i]; - prevSearchIdx = i + 1; - break; - } - } - if (!bufferInfo) { - for (var i = prevSearchIdx - 1; i >= 0; i--) { - if (attributeBuffers[i].name === name) { - bufferInfo = attributeBuffers[i]; - prevSearchIdx = i; - break; - } - } - } - } - - var buffer; - if (bufferInfo) { - buffer = bufferInfo.buffer; - } else { - buffer = _gl.createBuffer(); - } - //TODO: Use BufferSubData? - _gl.bindBuffer(_gl.ARRAY_BUFFER, buffer); - _gl.bufferData(_gl.ARRAY_BUFFER, attributeArrays[name], this.hint); - - attributeBuffers[count++] = new Geometry.AttributeBuffer(name, type, buffer, size, semantic); - } - attributeBuffers.length = count; - - if (isFacesDirty) { - if (! indicesBuffer) { - indicesBuffer = new Geometry.IndicesBuffer(_gl.createBuffer()); - chunk.indicesBuffer = indicesBuffer; - } - indicesBuffer.count = indicesArray.length; - _gl.bindBuffer(_gl.ELEMENT_ARRAY_BUFFER, indicesBuffer.buffer); - _gl.bufferData(_gl.ELEMENT_ARRAY_BUFFER, indicesArray, this.hint); - } - } - }, - - generateVertexNormals: function() { - var faces = this.faces; - var len = faces.length; - var positions = this.attributes.position.value; - var normals = this.attributes.normal.value; - var normal = vec3.create(); - - var v21 = vec3.create(), v32 = vec3.create(); - - for (var i = 0; i < normals.length; i++) { - vec3.set(normals[i], 0.0, 0.0, 0.0); - } - for (var i = normals.length; i < positions.length; i++) { - //Use array instead of Float32Array - normals[i] = [0.0, 0.0, 0.0]; - } - - for (var f = 0; f < len; f++) { - - var face = faces[f]; - var i1 = face[0]; - var i2 = face[1]; - var i3 = face[2]; - var p1 = positions[i1]; - var p2 = positions[i2]; - var p3 = positions[i3]; - - vec3.sub(v21, p1, p2); - vec3.sub(v32, p2, p3); - vec3.cross(normal, v21, v32); - // Weighted by the triangle area - vec3.add(normals[i1], normals[i1], normal); - vec3.add(normals[i2], normals[i2], normal); - vec3.add(normals[i3], normals[i3], normal); - } - for (var i = 0; i < normals.length; i++) { - vec3.normalize(normals[i], normals[i]); - } - }, - - generateFaceNormals: function() { - if (! this.isUniqueVertex()) { - this.generateUniqueVertex(); - } - - var faces = this.faces; - var len = faces.length; - var positions = this.attributes.position.value; - var normals = this.attributes.normal.value; - var normal = vec3.create(); - - var v21 = vec3.create(), v32 = vec3.create(); - - var isCopy = normals.length === positions.length; - - for (var i = 0; i < len; i++) { - var face = faces[i]; - var i1 = face[0]; - var i2 = face[1]; - var i3 = face[2]; - var p1 = positions[i1]; - var p2 = positions[i2]; - var p3 = positions[i3]; - - vec3.sub(v21, p1, p2); - vec3.sub(v32, p2, p3); - vec3.cross(normal, v21, v32); - - if (isCopy) { - vec3.copy(normals[i1], normal); - vec3.copy(normals[i2], normal); - vec3.copy(normals[i3], normal); - } else { - normals[i1] = normals[i2] = normals[i3] = arrSlice.call(normal); - } - } - }, - // 'Mathmatics for 3D programming and computer graphics, third edition' - // section 7.8.2 - // http://www.crytek.com/download/Triangle_mesh_tangent_space_calculation.pdf - generateTangents: function() { - - var texcoords = this.attributes.texcoord0.value; - var positions = this.attributes.position.value; - var tangents = this.attributes.tangent.value; - var normals = this.attributes.normal.value; - - var tan1 = []; - var tan2 = []; - var nVertex = this.getVertexNumber(); - for (var i = 0; i < nVertex; i++) { - tan1[i] = [0.0, 0.0, 0.0]; - tan2[i] = [0.0, 0.0, 0.0]; - } - - var sdir = [0.0, 0.0, 0.0]; - var tdir = [0.0, 0.0, 0.0]; - for (var i = 0; i < this.faces.length; i++) { - var face = this.faces[i], - i1 = face[0], - i2 = face[1], - i3 = face[2], - - st1 = texcoords[i1], - st2 = texcoords[i2], - st3 = texcoords[i3], - - p1 = positions[i1], - p2 = positions[i2], - p3 = positions[i3]; - - var x1 = p2[0] - p1[0], - x2 = p3[0] - p1[0], - y1 = p2[1] - p1[1], - y2 = p3[1] - p1[1], - z1 = p2[2] - p1[2], - z2 = p3[2] - p1[2]; - - var s1 = st2[0] - st1[0], - s2 = st3[0] - st1[0], - t1 = st2[1] - st1[1], - t2 = st3[1] - st1[1]; - - var r = 1.0 / (s1 * t2 - t1 * s2); - sdir[0] = (t2 * x1 - t1 * x2) * r; - sdir[1] = (t2 * y1 - t1 * y2) * r; - sdir[2] = (t2 * z1 - t1 * z2) * r; - - tdir[0] = (s1 * x2 - s2 * x1) * r; - tdir[1] = (s1 * y2 - s2 * y1) * r; - tdir[2] = (s1 * z2 - s2 * z1) * r; - - vec3.add(tan1[i1], tan1[i1], sdir); - vec3.add(tan1[i2], tan1[i2], sdir); - vec3.add(tan1[i3], tan1[i3], sdir); - vec3.add(tan2[i1], tan2[i1], tdir); - vec3.add(tan2[i2], tan2[i2], tdir); - vec3.add(tan2[i3], tan2[i3], tdir); - } - var tmp = [0, 0, 0, 0]; - var nCrossT = [0, 0, 0]; - for (var i = 0; i < nVertex; i++) { - var n = normals[i]; - var t = tan1[i]; - - // Gram-Schmidt orthogonalize - vec3.scale(tmp, n, vec3.dot(n, t)); - vec3.sub(tmp, t, tmp); - vec3.normalize(tmp, tmp); - // Calculate handedness. - vec3.cross(nCrossT, n, t); - tmp[3] = vec3.dot(nCrossT, tan2[i]) < 0.0 ? -1.0 : 1.0; - tangents[i] = tmp.slice(); - } - }, - - isUniqueVertex: function() { - if (this.isUseFace()) { - return this.getVertexNumber() === this.faces.length * 3; - } else { - return true; - } - }, - - generateUniqueVertex: function() { - - var vertexUseCount = []; - // Intialize with empty value, read undefined value from array - // is slow - // http://jsperf.com/undefined-array-read - for (var i = 0; i < this.getVertexNumber(); i++) { - vertexUseCount[i] = 0; - } - - var cursor = this.getVertexNumber(); - var attributes = this.getEnabledAttributes(); - var faces = this.faces; - - var attributeNameList = Object.keys(attributes); - - for (var i = 0; i < faces.length; i++) { - var face = faces[i]; - for (var j = 0; j < 3; j++) { - var ii = face[j]; - if (vertexUseCount[ii] > 0) { - for (var a = 0; a < attributeNameList.length; a++) { - var name = attributeNameList[a]; - var array = attributes[name].value; - var size = attributes[name].size; - if (size === 1) { - array.push(array[ii]); - } else { - array.push(arrSlice.call(array[ii])); - } - } - face[j] = cursor; - cursor++; - } - vertexUseCount[ii]++; - } - } - - this.dirty(); - }, - - // http://codeflow.org/entries/2012/aug/02/easy-wireframe-display-with-barycentric-coordinates/ - // http://en.wikipedia.org/wiki/Barycentric_coordinate_system_(mathematics) - generateBarycentric: (function() { - var a = [1, 0, 0]; - var b = [0, 0, 1]; - var c = [0, 1, 0]; - return function() { - - if (! this.isUniqueVertex()) { - this.generateUniqueVertex(); - } - - var array = this.attributes.barycentric.value; - // Already existed; - if (array.length == this.faces.length * 3) { - return; - } - var i1, i2, i3, face; - for (var i = 0; i < this.faces.length; i++) { - face = this.faces[i]; - i1 = face[0]; - i2 = face[1]; - i3 = face[2]; - array[i1] = a; - array[i2] = b; - array[i3] = c; - } - }; - })(), - - convertToStatic: function(geometry, useUintExtension) { - this._updateAttributesAndIndicesArrays(this.getEnabledAttributes(), true, useUintExtension); - - if (this._arrayChunks.length > 1) { - console.warn('Large geometry will discard chunks when convert to StaticGeometry'); - } - else if (this._arrayChunks.length === 0) { - return geometry; - } - var chunk = this._arrayChunks[0]; - - var attributes = this.getEnabledAttributes(); - for (var name in attributes) { - var attrib = attributes[name]; - var geoAttrib = geometry.attributes[name]; - if (!geoAttrib) { - geoAttrib = geometry.attributes[name] = { - type: attrib.type, - size: attrib.size, - value: null - }; - if (attrib.semantic) { - geoAttrib.semantic = attrib.semantic; - } - } - geoAttrib.value = chunk.attributeArrays[name]; - } - geometry.faces = chunk.indicesArray; - - if (this.boundingBox) { - geometry.boundingBox = new BoundingBox(); - geometry.boundingBox.min.copy(this.boundingBox.min); - geometry.boundingBox.max.copy(this.boundingBox.max); - } - // PENDING copy buffer ? - return geometry; - }, - - applyTransform: function(matrix) { - - var positions = this.attributes.position.value; - var normals = this.attributes.normal.value; - var tangents = this.attributes.tangent.value; - - matrix = matrix._array; - for (var i = 0; i < positions.length; i++) { - vec3.transformMat4(positions[i], positions[i], matrix); - } - // Normal Matrix - var inverseTransposeMatrix = mat4.create(); - mat4.invert(inverseTransposeMatrix, matrix); - mat4.transpose(inverseTransposeMatrix, inverseTransposeMatrix); - - for (var i = 0; i < normals.length; i++) { - vec3.transformMat4(normals[i], normals[i], inverseTransposeMatrix); - } - - for (var i = 0; i < tangents.length; i++) { - vec3.transformMat4(tangents[i], tangents[i], inverseTransposeMatrix); - } - - if (this.boundingBox) { - this.updateBoundingBox(); - } - }, - - dispose: function(_gl) { - this._cache.use(_gl.__GLID__); - var chunks = this._cache.get('chunks'); - if (chunks) { - for (var c = 0; c < chunks.length; c++) { - var chunk = chunks[c]; - for (var k = 0; k < chunk.attributeBuffers.length; k++) { - var attribs = chunk.attributeBuffers[k]; - _gl.deleteBuffer(attribs.buffer); - } - } - } - this._cache.deleteContext(_gl.__GLID__); - } - }); - - return DynamicGeometry; -}); -/** - * Base class for all textures like compressed texture, texture2d, texturecube - * TODO mapping - */ -define('qtek/Texture',['require','./core/Base','./core/glenum','./core/Cache'],function(require) { - - - - var Base = require('./core/Base'); - var glenum = require('./core/glenum'); - var Cache = require('./core/Cache'); - - /** - * @constructor qtek.Texture - * @extends qtek.core.Base - */ - var Texture = Base.derive( - /** @lends qtek.Texture# */ - { - /** - * Texture width, only needed when the texture is used as a render target - * @type {number} - */ - width: 512, - /** - * Texture height, only needed when the texture is used as a render target - * @type {number} - */ - height: 512, - /** - * Texel data type - * @type {number} - */ - type: glenum.UNSIGNED_BYTE, - /** - * Format of texel data - * @type {number} - */ - format: glenum.RGBA, - /** - * @type {number} - */ - wrapS: glenum.CLAMP_TO_EDGE, - /** - * @type {number} - */ - wrapT: glenum.CLAMP_TO_EDGE, - /** - * @type {number} - */ - minFilter: glenum.LINEAR_MIPMAP_LINEAR, - /** - * @type {number} - */ - magFilter: glenum.LINEAR, - /** - * @type {boolean} - */ - useMipmap: true, - - /** - * Anisotropic filtering, enabled if value is larger than 1 - * @see http://blog.tojicode.com/2012/03/anisotropic-filtering-in-webgl.html - * @type {number} - */ - anisotropic: 1, - // pixelStorei parameters - // http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml - /** - * @type {boolean} - */ - flipY: true, - /** - * @type {number} - */ - unpackAlignment: 4, - /** - * @type {boolean} - */ - premultiplyAlpha: false, - - /** - * Dynamic option for texture like video - * @type {boolean} - */ - dynamic: false, - - NPOT: false - }, function() { - this._cache = new Cache(); - }, - /** @lends qtek.Texture.prototype */ - { - - getWebGLTexture: function(_gl) { - var cache = this._cache; - cache.use(_gl.__GLID__); - - if (cache.miss('webgl_texture')) { - // In a new gl context, create new texture and set dirty true - cache.put('webgl_texture', _gl.createTexture()); - } - if (this.dynamic) { - this.update(_gl); - } - else if (cache.isDirty()) { - this.update(_gl); - cache.fresh(); - } - - return cache.get('webgl_texture'); - }, - - bind: function() {}, - unbind: function() {}, - - /** - * Mark texture is dirty and update in the next frame - */ - dirty: function() { - this._cache.dirtyAll(); - }, - - update: function(_gl) {}, - - // Update the common parameters of texture - beforeUpdate: function(_gl) { - _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, this.flipY); - _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this.premultiplyAlpha); - _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, this.unpackAlignment); - - this.fallBack(); - }, - - fallBack: function() { - // Use of none-power of two texture - // http://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences - - var isPowerOfTwo = this.isPowerOfTwo(); - - if (this.format === glenum.DEPTH_COMPONENT) { - this.useMipmap = false; - } - - if (! isPowerOfTwo || ! this.useMipmap) { - // none-power of two flag - this.NPOT = true; - // Save the original value for restore - this._minFilterOriginal = this.minFilter; - this._magFilterOriginal = this.magFilter; - this._wrapSOriginal = this.wrapS; - this._wrapTOriginal = this.wrapT; - - if (this.minFilter == glenum.NEAREST_MIPMAP_NEAREST || - this.minFilter == glenum.NEAREST_MIPMAP_LINEAR) { - this.minFilter = glenum.NEAREST; - } else if ( - this.minFilter == glenum.LINEAR_MIPMAP_LINEAR || - this.minFilter == glenum.LINEAR_MIPMAP_NEAREST - ) { - this.minFilter = glenum.LINEAR; - } - - this.wrapS = glenum.CLAMP_TO_EDGE; - this.wrapT = glenum.CLAMP_TO_EDGE; - } else { - this.NPOT = false; - if (this._minFilterOriginal) { - this.minFilter = this._minFilterOriginal; - } - if (this._magFilterOriginal) { - this.magFilter = this._magFilterOriginal; - } - if (this._wrapSOriginal) { - this.wrapS = this._wrapSOriginal; - } - if (this._wrapTOriginal) { - this.wrapT = this._wrapTOriginal; - } - } - - }, - - nextHighestPowerOfTwo: function(x) { - --x; - for (var i = 1; i < 32; i <<= 1) { - x = x | x >> i; - } - return x + 1; - }, - /** - * @param {WebGLRenderingContext} _gl - */ - dispose: function(_gl) { - var cache = this._cache; - cache.use(_gl.__GLID__); - - var webglTexture = cache.get('webgl_texture'); - if (webglTexture){ - _gl.deleteTexture(webglTexture); - } - cache.deleteContext(_gl.__GLID__); - }, - /** - * Test if image of texture is valid and loaded. - * @return {boolean} - */ - isRenderable: function() {}, - - isPowerOfTwo: function() {} - }); - - /* DataType */ - Texture.BYTE = glenum.BYTE; - Texture.UNSIGNED_BYTE = glenum.UNSIGNED_BYTE; - Texture.SHORT = glenum.SHORT; - Texture.UNSIGNED_SHORT = glenum.UNSIGNED_SHORT; - Texture.INT = glenum.INT; - Texture.UNSIGNED_INT = glenum.UNSIGNED_INT; - Texture.FLOAT = glenum.FLOAT; - Texture.HALF_FLOAT = 0x8D61; - - /* PixelFormat */ - Texture.DEPTH_COMPONENT = glenum.DEPTH_COMPONENT; - Texture.ALPHA = glenum.ALPHA; - Texture.RGB = glenum.RGB; - Texture.RGBA = glenum.RGBA; - Texture.LUMINANCE = glenum.LUMINANCE; - Texture.LUMINANCE_ALPHA = glenum.LUMINANCE_ALPHA; - - /* Compressed Texture */ - Texture.COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0; - Texture.COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1; - Texture.COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2; - Texture.COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3; - - /* TextureMagFilter */ - Texture.NEAREST = glenum.NEAREST; - Texture.LINEAR = glenum.LINEAR; - - /* TextureMinFilter */ - /* NEAREST */ - /* LINEAR */ - Texture.NEAREST_MIPMAP_NEAREST = glenum.NEAREST_MIPMAP_NEAREST; - Texture.LINEAR_MIPMAP_NEAREST = glenum.LINEAR_MIPMAP_NEAREST; - Texture.NEAREST_MIPMAP_LINEAR = glenum.NEAREST_MIPMAP_LINEAR; - Texture.LINEAR_MIPMAP_LINEAR = glenum.LINEAR_MIPMAP_LINEAR; - - /* TextureParameterName */ - Texture.TEXTURE_MAG_FILTER = glenum.TEXTURE_MAG_FILTER; - Texture.TEXTURE_MIN_FILTER = glenum.TEXTURE_MIN_FILTER; - - /* TextureWrapMode */ - Texture.REPEAT = glenum.REPEAT; - Texture.CLAMP_TO_EDGE = glenum.CLAMP_TO_EDGE; - Texture.MIRRORED_REPEAT = glenum.MIRRORED_REPEAT; - - - return Texture; -}); -define('qtek/TextureCube',['require','./Texture','./core/glinfo','./core/glenum','./core/util'],function(require) { - - var Texture = require('./Texture'); - var glinfo = require('./core/glinfo'); - var glenum = require('./core/glenum'); - var util = require('./core/util'); - - var targetList = ['px', 'nx', 'py', 'ny', 'pz', 'nz']; - - /** - * @constructor qtek.TextureCube - * @extends qtek.Texture - * - * @example - * ... - * var mat = new qtek.Material({ - * shader: qtek.shader.library.get('buildin.phong', 'environmentMap') - * }); - * var envMap = new qtek.TextureCube(); - * envMap.load({ - * 'px': 'assets/textures/sky/px.jpg', - * 'nx': 'assets/textures/sky/nx.jpg' - * 'py': 'assets/textures/sky/py.jpg' - * 'ny': 'assets/textures/sky/ny.jpg' - * 'pz': 'assets/textures/sky/pz.jpg' - * 'nz': 'assets/textures/sky/nz.jpg' - * }); - * mat.set('environmentMap', envMap); - * ... - * envMap.success(function() { - * // Wait for the sky texture loaded - * animation.on('frame', function(frameTime) { - * renderer.render(scene, camera); - * }); - * }); - */ - var TextureCube = Texture.derive(function() { - return /** @lends qtek.TextureCube# */{ - /** - * @type {Object} - * @property {HTMLImageElement|HTMLCanvasElemnet} px - * @property {HTMLImageElement|HTMLCanvasElemnet} nx - * @property {HTMLImageElement|HTMLCanvasElemnet} py - * @property {HTMLImageElement|HTMLCanvasElemnet} ny - * @property {HTMLImageElement|HTMLCanvasElemnet} pz - * @property {HTMLImageElement|HTMLCanvasElemnet} nz - */ - image: { - px: null, - nx: null, - py: null, - ny: null, - pz: null, - nz: null - }, - /** - * @type {Object} - * @property {Uint8Array} px - * @property {Uint8Array} nx - * @property {Uint8Array} py - * @property {Uint8Array} ny - * @property {Uint8Array} pz - * @property {Uint8Array} nz - */ - pixels: { - px: null, - nx: null, - py: null, - ny: null, - pz: null, - nz: null - }, - - /** - * @type {Array.} - */ - mipmaps: [] - }; - }, { - update: function(_gl) { - - _gl.bindTexture(_gl.TEXTURE_CUBE_MAP, this._cache.get('webgl_texture')); - - this.beforeUpdate(_gl); - - var glFormat = this.format; - var glType = this.type; - - _gl.texParameteri(_gl.TEXTURE_CUBE_MAP, _gl.TEXTURE_WRAP_S, this.wrapS); - _gl.texParameteri(_gl.TEXTURE_CUBE_MAP, _gl.TEXTURE_WRAP_T, this.wrapT); - - _gl.texParameteri(_gl.TEXTURE_CUBE_MAP, _gl.TEXTURE_MAG_FILTER, this.magFilter); - _gl.texParameteri(_gl.TEXTURE_CUBE_MAP, _gl.TEXTURE_MIN_FILTER, this.minFilter); - - var anisotropicExt = glinfo.getExtension(_gl, 'EXT_texture_filter_anisotropic'); - if (anisotropicExt && this.anisotropic > 1) { - _gl.texParameterf(_gl.TEXTURE_CUBE_MAP, anisotropicExt.TEXTURE_MAX_ANISOTROPY_EXT, this.anisotropic); - } - - // Fallback to float type if browser don't have half float extension - if (glType === 36193) { - var halfFloatExt = glinfo.getExtension(_gl, 'OES_texture_half_float'); - if (!halfFloatExt) { - glType = glenum.FLOAT; - } - } - - if (this.mipmaps.length) { - var width = this.width; - var height = this.height; - for (var i = 0; i < this.mipmaps.length; i++) { - var mipmap = this.mipmaps[i]; - this._updateTextureData(_gl, mipmap, i, width, height, glFormat, glType); - width /= 2; - height /= 2; - } - } - else { - this._updateTextureData(_gl, this, 0, this.width, this.height, glFormat, glType); - - if (!this.NPOT && this.useMipmap) { - _gl.generateMipmap(_gl.TEXTURE_CUBE_MAP); - } - } - - _gl.bindTexture(_gl.TEXTURE_CUBE_MAP, null); - }, - - _updateTextureData: function (_gl, data, level, width, height, glFormat, glType) { - for (var i = 0; i < 6; i++) { - var target = targetList[i]; - var img = data.image && data.image[target]; - if (img) { - _gl.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, level, glFormat, glFormat, glType, img); - } - else { - _gl.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, level, glFormat, width, height, 0, glFormat, glType, data.pixels && data.pixels[target]); - } - } - }, - - /** - * @param {WebGLRenderingContext} _gl - * @memberOf qtek.TextureCube.prototype - */ - generateMipmap: function(_gl) { - if (this.useMipmap && !this.NPOT) { - _gl.bindTexture(_gl.TEXTURE_CUBE_MAP, this._cache.get('webgl_texture')); - _gl.generateMipmap(_gl.TEXTURE_CUBE_MAP); - } - }, - - bind: function(_gl) { - - _gl.bindTexture(_gl.TEXTURE_CUBE_MAP, this.getWebGLTexture(_gl)); - }, - - unbind: function(_gl) { - _gl.bindTexture(_gl.TEXTURE_CUBE_MAP, null); - }, - - // Overwrite the isPowerOfTwo method - isPowerOfTwo: function() { - if (this.image.px) { - return isPowerOfTwo(this.image.px.width) - && isPowerOfTwo(this.image.px.height); - } else { - return isPowerOfTwo(this.width) - && isPowerOfTwo(this.height); - } - - function isPowerOfTwo(value) { - return (value & (value-1)) === 0; - } - }, - - isRenderable: function() { - if (this.image.px) { - return isImageRenderable(this.image.px) - && isImageRenderable(this.image.nx) - && isImageRenderable(this.image.py) - && isImageRenderable(this.image.ny) - && isImageRenderable(this.image.pz) - && isImageRenderable(this.image.nz); - } else { - return this.width && this.height; - } - }, - - load: function(imageList) { - var loading = 0; - var self = this; - util.each(imageList, function(src, target){ - var image = new Image(); - image.onload = function() { - loading --; - if (loading === 0){ - self.dirty(); - self.trigger('success', self); - } - image.onload = null; - }; - image.onerror = function() { - loading --; - image.onerror = null; - }; - - loading++; - image.src = src; - self.image[target] = image; - }); - - return this; - } - }); - - function isImageRenderable(image) { - return image.nodeName === 'CANVAS' || - image.complete; - } - - return TextureCube; -}); -define('qtek/FrameBuffer',['require','./core/Base','./TextureCube','./core/glinfo','./core/glenum','./core/Cache'],function(require) { - - - - var Base = require('./core/Base'); - var TextureCube = require('./TextureCube'); - var glinfo = require('./core/glinfo'); - var glenum = require('./core/glenum'); - var Cache = require('./core/Cache'); - - /** - * @constructor qtek.FrameBuffer - * @extends qtek.core.Base - */ - var FrameBuffer = Base.derive( - /** @lends qtek.FrameBuffer# */ - { - /** - * If use depth buffer - * @type {boolean} - */ - depthBuffer: true, - - //Save attached texture and target - _attachedTextures: null, - - _width: 0, - _height: 0, - - _binded: false, - }, function() { - // Use cache - this._cache = new Cache(); - - this._attachedTextures = {}; - }, - - /**@lends qtek.FrameBuffer.prototype. */ - { - - /** - * Resize framebuffer. - * It is not recommanded use this methods to change the framebuffer size because the size will still be changed when attaching a new texture - * @param {number} width - * @param {number} height - */ - resize: function(width, height) { - this._width = width; - this._height = height; - }, - - /** - * Bind the framebuffer to given renderer before rendering - * @param {qtek.Renderer} renderer - */ - bind: function(renderer) { - - var _gl = renderer.gl; - - if (!this._binded) { - _gl.bindFramebuffer(_gl.FRAMEBUFFER, this.getFrameBuffer(_gl)); - this._binded = true; - } - var cache = this._cache; - - cache.put('viewport', renderer.viewport); - renderer.setViewport(0, 0, this._width, this._height, 1); - if (! cache.get('depthtexture_attached') && this.depthBuffer) { - // Create a new render buffer - if (cache.miss('renderbuffer')) { - cache.put('renderbuffer', _gl.createRenderbuffer()); - } - var width = this._width; - var height = this._height; - var renderbuffer = cache.get('renderbuffer'); - - if (width !== cache.get('renderbuffer_width') - || height !== cache.get('renderbuffer_height')) { - _gl.bindRenderbuffer(_gl.RENDERBUFFER, renderbuffer); - _gl.renderbufferStorage(_gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, width, height); - cache.put('renderbuffer_width', width); - cache.put('renderbuffer_height', height); - _gl.bindRenderbuffer(_gl.RENDERBUFFER, null); - } - if (! cache.get('renderbuffer_attached')) { - _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer); - cache.put('renderbuffer_attached', true); - } - } - }, - /** - * Unbind the frame buffer after rendering - * @param {qtek.Renderer} renderer - */ - unbind: function(renderer) { - var _gl = renderer.gl; - - _gl.bindFramebuffer(_gl.FRAMEBUFFER, null); - this._binded = false; - - this._cache.use(_gl.__GLID__); - var viewport = this._cache.get('viewport'); - // Reset viewport; - if (viewport) { - renderer.setViewport( - viewport.x, viewport.y, viewport.width, viewport.height - ); - } - - // Because the data of texture is changed over time, - // Here update the mipmaps of texture each time after rendered; - for (var attachment in this._attachedTextures) { - var texture = this._attachedTextures[attachment]; - if (! texture.NPOT && texture.useMipmap) { - var target = texture instanceof TextureCube ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; - _gl.bindTexture(target, texture.getWebGLTexture(_gl)); - _gl.generateMipmap(target); - _gl.bindTexture(target, null); - } - } - }, - - getFrameBuffer: function(_gl) { - - this._cache.use(_gl.__GLID__); - - if (this._cache.miss('framebuffer')) { - this._cache.put('framebuffer', _gl.createFramebuffer()); - } - - return this._cache.get('framebuffer'); - }, - - /** - * Attach a texture(RTT) to the framebuffer - * @param {WebGLRenderingContext} _gl - * @param {qtek.Texture} texture - * @param {number} [attachment=gl.COLOR_ATTACHMENT0] - * @param {number} [target=gl.TEXTURE_2D] - * @param {number} [mipmapLevel=0] - */ - attach: function(_gl, texture, attachment, target, mipmapLevel) { - - if (! texture.width) { - throw new Error('The texture attached to color buffer is not a valid.'); - } - - if (!this._binded) { - _gl.bindFramebuffer(_gl.FRAMEBUFFER, this.getFrameBuffer(_gl)); - this._binded = true; - } - - this._width = texture.width; - this._height = texture.height; - - // If the depth_texture extension is enabled, developers - // Can attach a depth texture to the depth buffer - // http://blog.tojicode.com/2012/07/using-webgldepthtexture.html - attachment = attachment || _gl.COLOR_ATTACHMENT0; - target = target || _gl.TEXTURE_2D; - mipmapLevel = mipmapLevel || 0; - - if (attachment === _gl.DEPTH_ATTACHMENT) { - - var extension = glinfo.getExtension(_gl, 'WEBGL_depth_texture'); - - if (!extension) { - console.error(' Depth texture is not supported by the browser'); - return; - } - if (texture.format !== glenum.DEPTH_COMPONENT) { - console.error('The texture attached to depth buffer is not a valid.'); - return; - } - this._cache.put('renderbuffer_attached', false); - this._cache.put('depthtexture_attached', true); - } - - this._attachedTextures[attachment] = texture; - - _gl.framebufferTexture2D(_gl.FRAMEBUFFER, attachment, target, texture.getWebGLTexture(_gl), mipmapLevel); - }, - // TODO - detach: function() {}, - /** - * Dispose - * @param {WebGLRenderingContext} _gl - */ - dispose: function(_gl) { - this._cache.use(_gl.__GLID__); - - var renderBuffer = this._cache.get('renderbuffer'); - if (renderBuffer) { - _gl.deleteRenderbuffer(renderBuffer); - } - var frameBuffer = this._cache.get('framebuffer'); - if (frameBuffer) { - _gl.deleteFramebuffer(frameBuffer); - } - - // Clear cache for reusing - this._attachedTextures = {}; - this._width = this._height = 0; - - this._cache.deleteContext(_gl.__GLID__); - } - }); - - FrameBuffer.COLOR_ATTACHMENT0 = glenum.COLOR_ATTACHMENT0; - FrameBuffer.DEPTH_ATTACHMENT = glenum.DEPTH_ATTACHMENT; - FrameBuffer.STENCIL_ATTACHMENT = glenum.STENCIL_ATTACHMENT; - FrameBuffer.DEPTH_STENCIL_ATTACHMENT = glenum.DEPTH_STENCIL_ATTACHMENT; - - return FrameBuffer; -}); -define('qtek/Joint',['require','./core/Base'],function(require) { - - - - var Base = require('./core/Base'); - - /** - * @constructor qtek.Joint - * @extends qtek.core.Base - */ - var Joint = Base.derive( - /** @lends qtek.Joint# */ - { - // https://github.com/KhronosGroup/glTF/issues/193#issuecomment-29216576 - /** - * Joint name - * @type {string} - */ - name: '', - /** - * Index of joint in the skeleton - * @type {number} - */ - index: -1, - /** - * Index of parent joint index, -1 if it is a root joint - * @type {number} - */ - parentIndex: -1, - - /** - * Scene node attached to - * @type {qtek.Node} - */ - node: null, - - /** - * Root scene node of the skeleton, which parent node is null or don't have a joint - * @type {qtek.Node} - */ - rootNode: null - }); - - return Joint; -}); -/** - * Mainly do the parse and compile of shader string - * Support shader code chunk import and export - * Support shader semantics - * http://www.nvidia.com/object/using_sas.html - * https://github.com/KhronosGroup/collada2json/issues/45 - * - */ -define('qtek/Shader',['require','./core/Base','./core/util','./core/Cache','./dep/glmatrix'],function(require) { - - - - var Base = require('./core/Base'); - var util = require('./core/util'); - var Cache = require('./core/Cache'); - var glMatrix = require('./dep/glmatrix'); - var mat2 = glMatrix.mat2; - var mat3 = glMatrix.mat3; - var mat4 = glMatrix.mat4; - - var uniformRegex = /uniform\s+(bool|float|int|vec2|vec3|vec4|ivec2|ivec3|ivec4|mat2|mat3|mat4|sampler2D|samplerCube)\s+([\w\,]+)?(\[.*?\])?\s*(:\s*([\S\s]+?))?;/g; - var attributeRegex = /attribute\s+(float|int|vec2|vec3|vec4)\s+(\w*)\s*(:\s*(\w+))?;/g; - var defineRegex = /#define\s+(\w+)?(\s+[\w-.]+)?\s*\n/g; - - var uniformTypeMap = { - 'bool': '1i', - 'int': '1i', - 'sampler2D': 't', - 'samplerCube': 't', - 'float': '1f', - 'vec2': '2f', - 'vec3': '3f', - 'vec4': '4f', - 'ivec2': '2i', - 'ivec3': '3i', - 'ivec4': '4i', - 'mat2': 'm2', - 'mat3': 'm3', - 'mat4': 'm4' - }; - - var uniformValueConstructor = { - 'bool': function() {return true;}, - 'int': function() {return 0;}, - 'float': function() {return 0;}, - 'sampler2D': function() {return null;}, - 'samplerCube': function() {return null;}, - - 'vec2': function() {return [0, 0];}, - 'vec3': function() {return [0, 0, 0];}, - 'vec4': function() {return [0, 0, 0, 0];}, - - 'ivec2': function() {return [0, 0];}, - 'ivec3': function() {return [0, 0, 0];}, - 'ivec4': function() {return [0, 0, 0, 0];}, - - 'mat2': function() {return mat2.create();}, - 'mat3': function() {return mat3.create();}, - 'mat4': function() {return mat4.create();}, - - 'array': function() {return [];} - }; - - var attribSemantics = [ - 'POSITION', - 'NORMAL', - 'BINORMAL', - 'TANGENT', - 'TEXCOORD', - 'TEXCOORD_0', - 'TEXCOORD_1', - 'COLOR', - // Skinning - // https://github.com/KhronosGroup/glTF/blob/master/specification/README.md#semantics - 'JOINT', - 'WEIGHT', - 'SKIN_MATRIX' - ]; - var matrixSemantics = [ - 'WORLD', - 'VIEW', - 'PROJECTION', - 'WORLDVIEW', - 'VIEWPROJECTION', - 'WORLDVIEWPROJECTION', - 'WORLDINVERSE', - 'VIEWINVERSE', - 'PROJECTIONINVERSE', - 'WORLDVIEWINVERSE', - 'VIEWPROJECTIONINVERSE', - 'WORLDVIEWPROJECTIONINVERSE', - 'WORLDTRANSPOSE', - 'VIEWTRANSPOSE', - 'PROJECTIONTRANSPOSE', - 'WORLDVIEWTRANSPOSE', - 'VIEWPROJECTIONTRANSPOSE', - 'WORLDVIEWPROJECTIONTRANSPOSE', - 'WORLDINVERSETRANSPOSE', - 'VIEWINVERSETRANSPOSE', - 'PROJECTIONINVERSETRANSPOSE', - 'WORLDVIEWINVERSETRANSPOSE', - 'VIEWPROJECTIONINVERSETRANSPOSE', - 'WORLDVIEWPROJECTIONINVERSETRANSPOSE' - ]; - - // Enable attribute operation is global to all programs - // Here saved the list of all enabled attribute index - // http://www.mjbshaw.com/2013/03/webgl-fixing-invalidoperation.html - var enabledAttributeList = {}; - - var SHADER_STATE_TO_ENABLE = 1; - var SHADER_STATE_KEEP_ENABLE = 2; - var SHADER_STATE_PENDING = 3; - - /** - * @constructor qtek.Shader - * @extends qtek.core.Base - * - * @example - * // Create a phong shader - * var shader = new qtek.Shader({ - * vertex: qtek.Shader.source('buildin.phong.vertex'), - * fragment: qtek.Shader.source('buildin.phong.fragment') - * }); - * // Enable diffuse texture - * shader.enableTexture('diffuseMap'); - * // Use alpha channel in diffuse texture - * shader.define('fragment', 'DIFFUSEMAP_ALPHA_ALPHA'); - */ - var Shader = Base.derive(function() { - return /** @lends qtek.Shader# */ { - /** - * Vertex shader code - * @type {string} - */ - vertex: '', - - /** - * Fragment shader code - * @type {string} - */ - fragment: '', - - - precision: 'mediump', - // Properties follow will be generated by the program - attribSemantics: {}, - matrixSemantics: {}, - matrixSemanticKeys: [], - - uniformTemplates: {}, - attributeTemplates: {}, - - /** - * Custom defined values in the vertex shader - * @type {Object} - */ - vertexDefines: {}, - /** - * Custom defined values in the vertex shader - * @type {Object} - */ - fragmentDefines: {}, - - // Glue code - // Defines the each type light number in the scene - // AMBIENT_LIGHT - // POINT_LIGHT - // SPOT_LIGHT - // AREA_LIGHT - lightNumber: {}, - - _attacheMaterialNumber: 0, - - _uniformList: [], - // { - // enabled: true - // shaderType: "vertex", - // } - _textureStatus: {}, - - _vertexProcessed: '', - _fragmentProcessed: '', - - _currentLocationsMap: {} - }; - }, function() { - - this._cache = new Cache(); - - this._updateShaderString(); - }, - /** @lends qtek.Shader.prototype */ - { - /** - * Set vertex shader code - * @param {string} str - */ - setVertex: function(str) { - this.vertex = str; - this._updateShaderString(); - this.dirty(); - }, - - /** - * Set fragment shader code - * @param {string} str - */ - setFragment: function(str) { - this.fragment = str; - this._updateShaderString(); - this.dirty(); - }, - - /** - * Bind shader program - * Return true or error msg if error happened - * @param {WebGLRenderingContext} _gl - */ - bind: function(_gl) { - this._cache.use(_gl.__GLID__, getCacheSchema); - - this._currentLocationsMap = this._cache.get('locations'); - - if (this._cache.isDirty()) { - this._updateShaderString(); - var errMsg = this._buildProgram(_gl, this._vertexProcessed, this._fragmentProcessed); - this._cache.fresh(); - - if (errMsg) { - return errMsg; - } - } - - _gl.useProgram(this._cache.get('program')); - }, - - /** - * Mark dirty and update program in next frame - */ - dirty: function() { - this._cache.dirtyAll(); - for (var i = 0; i < this._cache._caches.length; i++) { - if (this._cache._caches[i]) { - var context = this._cache._caches[i]; - context['locations'] = {}; - context['attriblocations'] = {}; - } - } - }, - - _updateShaderString: function() { - - if (this.vertex !== this._vertexPrev || - this.fragment !== this._fragmentPrev) { - - this._parseImport(); - - this.attribSemantics = {}; - this.matrixSemantics = {}; - this._textureStatus = {}; - - this._parseUniforms(); - this._parseAttributes(); - this._parseDefines(); - - this._vertexPrev = this.vertex; - this._fragmentPrev = this.fragment; - } - this._addDefine(); - }, - - /** - * Add a #define micro in shader code - * @param {string} shaderType Can be vertex, fragment or both - * @param {string} symbol - * @param {number} [val] - */ - define: function(shaderType, symbol, val) { - val = val !== undefined ? val : null; - if (shaderType == 'vertex' || shaderType == 'both') { - if (this.vertexDefines[symbol] !== val) { - this.vertexDefines[symbol] = val; - // Mark as dirty - this.dirty(); - } - } - if (shaderType == 'fragment' || shaderType == 'both') { - if (this.fragmentDefines[symbol] !== val) { - this.fragmentDefines[symbol] = val; - if (shaderType !== 'both') { - this.dirty(); - } - } - } - }, - - /** - * @param {string} shaderType Can be vertex, fragment or both - * @param {string} symbol - */ - unDefine: function(shaderType, symbol) { - if (shaderType == 'vertex' || shaderType == 'both') { - if (this.isDefined('vertex', symbol)) { - delete this.vertexDefines[symbol]; - // Mark as dirty - this.dirty(); - } - } - if (shaderType == 'fragment' || shaderType == 'both') { - if (this.isDefined('fragment', symbol)) { - delete this.fragmentDefines[symbol]; - if (shaderType !== 'both') { - this.dirty(); - } - } - } - }, - - /** - * @param {string} shaderType Can be vertex, fragment or both - * @param {string} symbol - */ - isDefined: function(shaderType, symbol) { - switch(shaderType) { - case 'vertex': - return this.vertexDefines[symbol] !== undefined; - case 'fragment': - return this.fragmentDefines[symbol] !== undefined; - } - }, - /** - * @param {string} shaderType Can be vertex, fragment or both - * @param {string} symbol - */ - getDefine: function(shaderType, symbol) { - switch(shaderType) { - case 'vertex': - return this.vertexDefines[symbol]; - case 'fragment': - return this.fragmentDefines[symbol]; - } - }, - /** - * Enable a texture, actually it will add a #define micro in the shader code - * For example, if texture symbol is diffuseMap, it will add a line `#define DIFFUSEMAP_ENABLED` in the shader code - * @param {string} symbol - */ - enableTexture: function(symbol) { - var status = this._textureStatus[symbol]; - if (status) { - var isEnabled = status.enabled; - if (!isEnabled) { - status.enabled = true; - this.dirty(); - } - } - }, - /** - * Enable all textures used in the shader - */ - enableTexturesAll: function() { - for (var symbol in this._textureStatus) { - this._textureStatus[symbol].enabled = true; - } - - this.dirty(); - }, - /** - * Disable a texture, it remove a #define micro in the shader - * @param {string} symbol - */ - disableTexture: function(symbol) { - var status = this._textureStatus[symbol]; - if (status) { - var isDisabled = ! status.enabled; - if (!isDisabled) { - status.enabled = false; - this.dirty(); - } - } - }, - /** - * Disable all textures used in the shader - */ - disableTexturesAll: function() { - for (var symbol in this._textureStatus) { - this._textureStatus[symbol].enabled = false; - } - - this.dirty(); - }, - /** - * @param {string} symbol - * @return {boolean} - */ - isTextureEnabled: function(symbol) { - return this._textureStatus[symbol].enabled; - }, - - getEnabledTextures: function () { - var enabledTextures = []; - for (var symbol in this._textureStatus) { - if (this._textureStatus[symbol].enabled) { - enabledTextures.push(symbol); - } - } - return enabledTextures; - }, - - hasUniform: function(symbol) { - var location = this._currentLocationsMap[symbol]; - return location !== null && location !== undefined; - }, - - setUniform: function(_gl, type, symbol, value) { - var locationMap = this._currentLocationsMap; - var location = locationMap[symbol]; - // Uniform is not existed in the shader - if (location === null || location === undefined) { - return false; - } - switch (type) { - case 'm4': - // The matrix must be created by glmatrix and can pass it directly. - _gl.uniformMatrix4fv(location, false, value); - break; - case '2i': - _gl.uniform2i(location, value[0], value[1]); - break; - case '2f': - _gl.uniform2f(location, value[0], value[1]); - break; - case '3i': - _gl.uniform3i(location, value[0], value[1], value[2]); - break; - case '3f': - _gl.uniform3f(location, value[0], value[1], value[2]); - break; - case '4i': - _gl.uniform4i(location, value[0], value[1], value[2], value[3]); - break; - case '4f': - _gl.uniform4f(location, value[0], value[1], value[2], value[3]); - break; - case '1i': - _gl.uniform1i(location, value); - break; - case '1f': - _gl.uniform1f(location, value); - break; - case '1fv': - _gl.uniform1fv(location, value); - break; - case '1iv': - _gl.uniform1iv(location, value); - break; - case '2iv': - _gl.uniform2iv(location, value); - break; - case '2fv': - _gl.uniform2fv(location, value); - break; - case '3iv': - _gl.uniform3iv(location, value); - break; - case '3fv': - _gl.uniform3fv(location, value); - break; - case '4iv': - _gl.uniform4iv(location, value); - break; - case '4fv': - _gl.uniform4fv(location, value); - break; - case 'm2': - case 'm2v': - _gl.uniformMatrix2fv(location, false, value); - break; - case 'm3': - case 'm3v': - _gl.uniformMatrix3fv(location, false, value); - break; - case 'm4v': - if (value instanceof Array) { - var array = new Float32Array(value.length * 16); - var cursor = 0; - for (var i = 0; i < value.length; i++) { - var item = value[i]; - for (var j = 0; j < 16; j++) { - array[cursor++] = item[j]; - } - } - _gl.uniformMatrix4fv(location, false, array); - // Raw value - } else if (value instanceof Float32Array) { // ArrayBufferView - _gl.uniformMatrix4fv(location, false, value); - } - break; - } - return true; - }, - - setUniformBySemantic: function(_gl, semantic, val) { - var semanticInfo = this.attribSemantics[semantic]; - if (semanticInfo) { - return this.setUniform(_gl, semanticInfo.type, semanticInfo.symbol, val); - } - return false; - }, - - // Enable the attributes passed in and disable the rest - // Example Usage: - // enableAttributes(_gl, ["position", "texcoords"]) - enableAttributes: function(_gl, attribList, vao) { - - var program = this._cache.get('program'); - - var locationMap = this._cache.get('attriblocations'); - - var enabledAttributeListInContext; - if (vao) { - enabledAttributeListInContext = vao.__enabledAttributeList; - } else { - enabledAttributeListInContext = enabledAttributeList[_gl.__GLID__]; - } - if (! enabledAttributeListInContext) { - // In vertex array object context - // PENDING Each vao object needs to enable attributes again? - if (vao) { - enabledAttributeListInContext - = vao.__enabledAttributeList - = []; - } else { - enabledAttributeListInContext - = enabledAttributeList[_gl.__GLID__] - = []; - } - } - var locationList = []; - for (var i = 0; i < attribList.length; i++) { - var symbol = attribList[i]; - if (!this.attributeTemplates[symbol]) { - locationList[i] = -1; - continue; - } - var location = locationMap[symbol]; - if (location === undefined) { - location = _gl.getAttribLocation(program, symbol); - // Attrib location is a number from 0 to ... - if (location === -1) { - locationList[i] = -1; - continue; - } - locationMap[symbol] = location; - } - locationList[i] = location; - - if (!enabledAttributeListInContext[location]) { - enabledAttributeListInContext[location] = SHADER_STATE_TO_ENABLE; - } else { - enabledAttributeListInContext[location] = SHADER_STATE_KEEP_ENABLE; - } - } - - for (var i = 0; i < enabledAttributeListInContext.length; i++) { - switch(enabledAttributeListInContext[i]){ - case SHADER_STATE_TO_ENABLE: - _gl.enableVertexAttribArray(i); - enabledAttributeListInContext[i] = SHADER_STATE_PENDING; - break; - case SHADER_STATE_KEEP_ENABLE: - enabledAttributeListInContext[i] = SHADER_STATE_PENDING; - break; - // Expired - case SHADER_STATE_PENDING: - _gl.disableVertexAttribArray(i); - enabledAttributeListInContext[i] = 0; - break; - } - } - - return locationList; - }, - - _parseImport: function() { - - this._vertexProcessedNoDefine = Shader.parseImport(this.vertex); - this._fragmentProcessedNoDefine = Shader.parseImport(this.fragment); - - }, - - _addDefine: function() { - - // Add defines - // VERTEX - var defineStr = []; - for (var lightType in this.lightNumber) { - var count = this.lightNumber[lightType]; - if (count > 0) { - defineStr.push('#define ' + lightType.toUpperCase() + '_NUMBER ' + count); - } - } - for (var symbol in this._textureStatus) { - var status = this._textureStatus[symbol]; - if (status.enabled) { - defineStr.push('#define ' + symbol.toUpperCase() + '_ENABLED'); - } - } - // Custom Defines - for (var symbol in this.vertexDefines) { - var value = this.vertexDefines[symbol]; - if (value === null) { - defineStr.push('#define ' + symbol); - }else{ - defineStr.push('#define ' + symbol + ' ' + value.toString()); - } - } - this._vertexProcessed = defineStr.join('\n') + '\n' + this._vertexProcessedNoDefine; - - // FRAGMENT - defineStr = []; - for (var lightType in this.lightNumber) { - var count = this.lightNumber[lightType]; - if (count > 0) { - defineStr.push('#define ' + lightType.toUpperCase() + '_NUMBER ' + count); - } - } - for (var symbol in this._textureStatus) { - var status = this._textureStatus[symbol]; - if (status.enabled) { - defineStr.push('#define ' + symbol.toUpperCase() + '_ENABLED'); - } - } - // Custom Defines - for (var symbol in this.fragmentDefines) { - var value = this.fragmentDefines[symbol]; - if (value === null) { - defineStr.push('#define ' + symbol); - }else{ - defineStr.push('#define ' + symbol + ' ' + value.toString()); - } - } - var code = defineStr.join('\n') + '\n' + this._fragmentProcessedNoDefine; - - // Add precision - this._fragmentProcessed = ['precision', this.precision, 'float'].join(' ') + ';\n' + code; - }, - - _parseUniforms: function() { - var uniforms = {}; - var self = this; - var shaderType = 'vertex'; - this._uniformList = []; - - this._vertexProcessedNoDefine = this._vertexProcessedNoDefine.replace(uniformRegex, _uniformParser); - shaderType = 'fragment'; - this._fragmentProcessedNoDefine = this._fragmentProcessedNoDefine.replace(uniformRegex, _uniformParser); - - self.matrixSemanticKeys = Object.keys(this.matrixSemantics); - - function _uniformParser(str, type, symbol, isArray, semanticWrapper, semantic) { - if (type && symbol) { - var uniformType = uniformTypeMap[type]; - var isConfigurable = true; - var defaultValueFunc; - if (uniformType) { - self._uniformList.push(symbol); - if (type === 'sampler2D' || type === 'samplerCube') { - // Texture is default disabled - self._textureStatus[symbol] = { - enabled: false, - shaderType: shaderType - }; - } - if (isArray) { - uniformType += 'v'; - } - if (semantic) { - // This case is only for SKIN_MATRIX - // TODO - if (attribSemantics.indexOf(semantic) >= 0) { - self.attribSemantics[semantic] = { - symbol: symbol, - type: uniformType - }; - isConfigurable = false; - } else if (matrixSemantics.indexOf(semantic) >= 0) { - var isTranspose = false; - var semanticNoTranspose = semantic; - if (semantic.match(/TRANSPOSE$/)) { - isTranspose = true; - semanticNoTranspose = semantic.slice(0, -9); - } - self.matrixSemantics[semantic] = { - symbol: symbol, - type: uniformType, - isTranspose: isTranspose, - semanticNoTranspose: semanticNoTranspose - }; - isConfigurable = false; - } else { - // The uniform is not configurable, which means it will not appear - // in the material uniform properties - if (semantic === 'unconfigurable') { - isConfigurable = false; - } else { - // Uniform have a defalut value, like - // uniform vec3 color: [1, 1, 1]; - defaultValueFunc = self._parseDefaultValue(type, semantic); - if (!defaultValueFunc) { - throw new Error('Unkown semantic "' + semantic + '"'); - } - else { - semantic = ''; - } - } - } - } - if (isConfigurable) { - uniforms[symbol] = { - type: uniformType, - value: isArray ? uniformValueConstructor['array'] : (defaultValueFunc || uniformValueConstructor[type]), - semantic: semantic || null - }; - } - } - return ['uniform', type, symbol, isArray].join(' ') + ';\n'; - } - } - - this.uniformTemplates = uniforms; - }, - - _parseDefaultValue: function(type, str) { - var arrayRegex = /\[\s*(.*)\s*\]/; - if (type === 'vec2' || type === 'vec3' || type === 'vec4') { - var arrayStr = arrayRegex.exec(str)[1]; - if (arrayStr) { - var arr = arrayStr.split(/\s*,\s*/); - return function() { - return new Float32Array(arr); - }; - } else { - // Invalid value - return; - } - } else if (type === 'bool') { - return function() { - return str.toLowerCase() === 'true' ? true : false; - }; - } else if (type === 'float') { - return function() { - return parseFloat(str); - }; - } - }, - - // Create a new uniform instance for material - createUniforms: function() { - var uniforms = {}; - - for (var symbol in this.uniformTemplates){ - var uniformTpl = this.uniformTemplates[symbol]; - uniforms[symbol] = { - type: uniformTpl.type, - value: uniformTpl.value() - }; - } - - return uniforms; - }, - - // Attached to material - attached: function () { - this._attacheMaterialNumber++; - }, - - // Detached to material - detached: function () { - this._attacheMaterialNumber--; - }, - - isAttachedToAny: function () { - return this._attacheMaterialNumber !== 0; - }, - - _parseAttributes: function() { - var attributes = {}; - var self = this; - this._vertexProcessedNoDefine = this._vertexProcessedNoDefine.replace( - attributeRegex, _attributeParser - ); - - function _attributeParser(str, type, symbol, semanticWrapper, semantic) { - if (type && symbol) { - var size = 1; - switch (type) { - case 'vec4': - size = 4; - break; - case 'vec3': - size = 3; - break; - case 'vec2': - size = 2; - break; - case 'float': - size = 1; - break; - } - - attributes[symbol] = { - // Can only be float - type: 'float', - size: size, - semantic: semantic || null - }; - - if (semantic) { - if (attribSemantics.indexOf(semantic) < 0) { - throw new Error('Unkown semantic "' + semantic + '"'); - }else{ - self.attribSemantics[semantic] = { - symbol: symbol, - type: type - }; - } - } - } - - return ['attribute', type, symbol].join(' ') + ';\n'; - } - - this.attributeTemplates = attributes; - }, - - _parseDefines: function() { - var self = this; - var shaderType = 'vertex'; - this._vertexProcessedNoDefine = this._vertexProcessedNoDefine.replace(defineRegex, _defineParser); - shaderType = 'fragment'; - this._fragmentProcessedNoDefine = this._fragmentProcessedNoDefine.replace(defineRegex, _defineParser); - - function _defineParser(str, symbol, value) { - var defines = shaderType === 'vertex' ? self.vertexDefines : self.fragmentDefines; - if (!defines[symbol]) { // Haven't been defined by user - if (value == 'false') { - defines[symbol] = false; - } else if (value == 'true') { - defines[symbol] = true; - } else { - defines[symbol] = value ? parseFloat(value) : null; - } - } - return ''; - } - }, - - // Return true or error msg if error happened - _buildProgram: function(_gl, vertexShaderString, fragmentShaderString) { - - if (this._cache.get('program')) { - _gl.deleteProgram(this._cache.get('program')); - } - var program = _gl.createProgram(); - - var vertexShader = _gl.createShader(_gl.VERTEX_SHADER); - _gl.shaderSource(vertexShader, vertexShaderString); - _gl.compileShader(vertexShader); - - var fragmentShader = _gl.createShader(_gl.FRAGMENT_SHADER); - _gl.shaderSource(fragmentShader, fragmentShaderString); - _gl.compileShader(fragmentShader); - - var msg = this._checkShaderErrorMsg(_gl, vertexShader, vertexShaderString); - if (msg) { - return msg; - } - msg = this._checkShaderErrorMsg(_gl, fragmentShader, fragmentShaderString); - if (msg) { - return msg; - } - - _gl.attachShader(program, vertexShader); - _gl.attachShader(program, fragmentShader); - // Force the position bind to location 0; - if (this.attribSemantics['POSITION']) { - _gl.bindAttribLocation(program, 0, this.attribSemantics['POSITION'].symbol); - } else { - // Else choose an attribute and bind to location 0; - var keys = Object.keys(this.attributeTemplates); - _gl.bindAttribLocation(program, 0, keys[0]); - } - - _gl.linkProgram(program); - - if (!_gl.getProgramParameter(program, _gl.LINK_STATUS)) { - return 'Could not link program\n' + 'VALIDATE_STATUS: ' + _gl.getProgramParameter(program, _gl.VALIDATE_STATUS) + ', gl error [' + _gl.getError() + ']'; - } - - // Cache uniform locations - for (var i = 0; i < this._uniformList.length; i++) { - var uniformSymbol = this._uniformList[i]; - var locationMap = this._cache.get('locations'); - locationMap[uniformSymbol] = _gl.getUniformLocation(program, uniformSymbol); - } - - _gl.deleteShader(vertexShader); - _gl.deleteShader(fragmentShader); - - this._cache.put('program', program); - }, - - // Return true or error msg if error happened - _checkShaderErrorMsg: function(_gl, shader, shaderString) { - if (!_gl.getShaderParameter(shader, _gl.COMPILE_STATUS)) { - return [_gl.getShaderInfoLog(shader), addLineNumbers(shaderString)].join('\n'); - } - }, - - /** - * Clone a new shader - * @return {qtek.Shader} - */ - clone: function() { - var shader = new Shader({ - vertex: this.vertex, - fragment: this.fragment, - vertexDefines: util.clone(this.vertexDefines), - fragmentDefines: util.clone(this.fragmentDefines) - }); - for (var name in this._textureStatus) { - shader._textureStatus[name] = util.clone(this._textureStatus[name]); - } - return shader; - }, - /** - * @param {WebGLRenderingContext} _gl - */ - dispose: function(_gl) { - this._cache.use(_gl.__GLID__); - var program = this._cache.get('program'); - if (program) { - _gl.deleteProgram(program); - } - this._cache.deleteContext(_gl.__GLID__); - this._locations = {}; - } - }); - - function getCacheSchema() { - return { - locations: {}, - attriblocations: {} - }; - } - - // some util functions - function addLineNumbers(string) { - var chunks = string.split('\n'); - for (var i = 0, il = chunks.length; i < il; i ++) { - // Chrome reports shader errors on lines - // starting counting from 1 - chunks[i] = (i + 1) + ': ' + chunks[i]; - } - return chunks.join('\n'); - } - - var importRegex = /(@import)\s*([0-9a-zA-Z_\-\.]*)/g; - Shader.parseImport = function(shaderStr) { - shaderStr = shaderStr.replace(importRegex, function(str, importSymbol, importName) { - var str = Shader.source(importName); - if (str) { - // Recursively parse - return Shader.parseImport(str); - } else { - console.warn('Shader chunk "' + importName + '" not existed in library'); - return ''; - } - }); - return shaderStr; - }; - - var exportRegex = /(@export)\s*([0-9a-zA-Z_\-\.]*)\s*\n([\s\S]*?)@end/g; - - /** - * Import shader source - * @param {string} shaderStr - * @memberOf qtek.Shader - */ - Shader['import'] = function(shaderStr) { - shaderStr.replace(exportRegex, function(str, exportSymbol, exportName, code) { - var code = code.replace(/(^[\s\t\xa0\u3000]+)|([\u3000\xa0\s\t]+\x24)/g, ''); - if (code) { - var parts = exportName.split('.'); - var obj = Shader.codes; - var i = 0; - var key; - while (i < parts.length - 1) { - key = parts[i++]; - if (!obj[key]) { - obj[key] = {}; - } - obj = obj[key]; - } - key = parts[i]; - obj[key] = code; - } - return code; - }); - }; - - /** - * Library to store all the loaded shader codes - * @type {Object} - * @readOnly - * @memberOf qtek.Shader - */ - Shader.codes = {}; - - /** - * Get shader source - * @param {string} name - * @return {string} - * @memberOf qtek.Shader - */ - Shader.source = function(name) { - var parts = name.split('.'); - var obj = Shader.codes; - var i = 0; - while(obj && i < parts.length) { - var key = parts[i++]; - obj = obj[key]; - } - if (!obj) { - console.warn('Shader "' + name + '" not existed in library'); - return; - } - return obj; - }; - - return Shader; -}); -define('qtek/light/light.essl',[],function () { return '@export buildin.header.directional_light\nuniform vec3 directionalLightDirection[ DIRECTIONAL_LIGHT_NUMBER ] : unconfigurable;\nuniform vec3 directionalLightColor[ DIRECTIONAL_LIGHT_NUMBER ] : unconfigurable;\n@end\n\n@export buildin.header.ambient_light\nuniform vec3 ambientLightColor[ AMBIENT_LIGHT_NUMBER ] : unconfigurable;\n@end\n\n@export buildin.header.point_light\nuniform vec3 pointLightPosition[ POINT_LIGHT_NUMBER ] : unconfigurable;\nuniform float pointLightRange[ POINT_LIGHT_NUMBER ] : unconfigurable;\nuniform vec3 pointLightColor[ POINT_LIGHT_NUMBER ] : unconfigurable;\n@end\n\n@export buildin.header.spot_light\nuniform vec3 spotLightPosition[SPOT_LIGHT_NUMBER] : unconfigurable;\nuniform vec3 spotLightDirection[SPOT_LIGHT_NUMBER] : unconfigurable;\nuniform float spotLightRange[SPOT_LIGHT_NUMBER] : unconfigurable;\nuniform float spotLightUmbraAngleCosine[SPOT_LIGHT_NUMBER] : unconfigurable;\nuniform float spotLightPenumbraAngleCosine[SPOT_LIGHT_NUMBER] : unconfigurable;\nuniform float spotLightFalloffFactor[SPOT_LIGHT_NUMBER] : unconfigurable;\nuniform vec3 spotLightColor[SPOT_LIGHT_NUMBER] : unconfigurable;\n@end';}); - -define('qtek/Light',['require','./Node','./Shader','./light/light.essl'],function(require){ - - - - var Node = require('./Node'); - var Shader = require('./Shader'); - - /** - * @constructor qtek.Light - * @extends qtek.Node - */ - var Light = Node.derive(function(){ - return /** @lends qtek.Light# */ { - /** - * Light RGB color - * @type {number[]} - */ - color: [1, 1, 1], - - /** - * Light intensity - * @type {number} - */ - intensity: 1.0, - - // Config for shadow map - /** - * If light cast shadow - * @type {boolean} - */ - castShadow: true, - - /** - * Shadow map size - * @type {number} - */ - shadowResolution: 512 - }; - }, - /** @lends qtek.Light.prototype. */ - { - /** - * Light type - * @type {string} - * @memberOf qtek.Light# - */ - type: '', - - /** - * @return {qtek.Light} - * @memberOf qtek.Light.prototype - */ - clone: function() { - var light = Node.prototype.clone.call(this); - light.color = Array.prototype.slice.call(this.color); - light.intensity = this.intensity; - light.castShadow = this.castShadow; - light.shadowResolution = this.shadowResolution; - - return light; - } - }); - - Shader['import'](require('./light/light.essl')); - - return Light; -}); -define('qtek/Material',['require','./core/Base','./Texture'],function(require) { - - - - var Base = require('./core/Base'); - var Texture = require('./Texture'); - - /** - * #constructor qtek.Material - * @extends qtek.core.Base - */ - var Material = Base.derive( - /** @lends qtek.Material# */ - { - /** - * @type {string} - */ - name: '', - - /** - * @type {Object} - */ - uniforms: null, - - /** - * @type {qtek.Shader} - */ - shader: null, - - /** - * @type {boolean} - */ - depthTest: true, - - /** - * @type {boolean} - */ - depthMask: true, - - /** - * @type {boolean} - */ - transparent: false, - /** - * Blend func is a callback function when the material - * have custom blending - * The gl context will be the only argument passed in tho the - * blend function - * Detail of blend function in WebGL: - * http://www.khronos.org/registry/gles/specs/2.0/es_full_spec_2.0.25.pdf - * - * Example : - * function(_gl) { - * _gl.blendEquation(_gl.FUNC_ADD); - * _gl.blendFunc(_gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA); - * } - */ - blend: null, - - // shadowTransparentMap : null - - _enabledUniforms: null, - }, function() { - if (!this.name) { - this.name = 'MATERIAL_' + this.__GUID__; - } - if (this.shader) { - this.attachShader(this.shader); - } - if (! this.uniforms) { - this.uniforms = {}; - } - }, - /** @lends qtek.Material.prototype */ - { - - bind: function(_gl, prevMaterial) { - - var slot = 0; - - var sameShader = prevMaterial && prevMaterial.shader === this.shader; - // Set uniforms - for (var u = 0; u < this._enabledUniforms.length; u++) { - var symbol = this._enabledUniforms[u]; - var uniform = this.uniforms[symbol]; - // When binding two materials with the same shader - // Many uniforms will be be set twice even if they have the same value - // So add a evaluation to see if the uniform is really needed to be set - // - // FIXME Small possibility enabledUniforms are not the same - if (sameShader) { - if (prevMaterial.uniforms[symbol].value === uniform.value) { - continue; - } - } - - if (uniform.value === undefined) { - console.warn('Uniform value "' + symbol + '" is undefined'); - continue; - } - else if (uniform.value === null) { - // if (uniform.type == 't') { - // // PENDING - // _gl.bindTexture(_gl.TEXTURE_2D, null); - // _gl.bindTexture(_gl.TEXTURE_CUBE, null); - // } - continue; - } - else if (uniform.value instanceof Array - && ! uniform.value.length) { - continue; - } - else if (uniform.value instanceof Texture) { - var res = this.shader.setUniform(_gl, '1i', symbol, slot); - if (!res) { // Texture is not enabled - continue; - } - var texture = uniform.value; - _gl.activeTexture(_gl.TEXTURE0 + slot); - // Maybe texture is not loaded yet; - if (texture.isRenderable()) { - texture.bind(_gl); - } else { - // Bind texture to null - texture.unbind(_gl); - } - - slot++; - } - else if (uniform.value instanceof Array) { - if (uniform.value.length === 0) { - continue; - } - // Texture Array - var exampleValue = uniform.value[0]; - - if (exampleValue instanceof Texture) { - if (!this.shader.hasUniform(symbol)) { - continue; - } - - var arr = []; - for (var i = 0; i < uniform.value.length; i++) { - var texture = uniform.value[i]; - _gl.activeTexture(_gl.TEXTURE0 + slot); - // Maybe texture is not loaded yet; - if (texture.isRenderable()) { - texture.bind(_gl); - } else { - texture.unbind(_gl); - } - - arr.push(slot++); - } - - this.shader.setUniform(_gl, '1iv', symbol, arr); - } else { - this.shader.setUniform(_gl, uniform.type, symbol, uniform.value); - } - } - else{ - this.shader.setUniform(_gl, uniform.type, symbol, uniform.value); - } - } - }, - - /** - * @param {string} symbol - * @param {number|array|qtek.Texture|ArrayBufferView} value - */ - setUniform: function(symbol, value) { - var uniform = this.uniforms[symbol]; - if (uniform) { - uniform.value = value; - } - }, - - /** - * @param {Object} obj - */ - setUniforms: function(obj) { - for (var key in obj) { - var val = obj[key]; - this.setUniform(key, val); - } - }, - - /** - * Enable a uniform - * It only have effect on the uniform exists in shader. - * @param {string} symbol - */ - enableUniform: function(symbol) { - if (this.uniforms[symbol] && !this.isUniformEnabled(symbol)) { - this._enabledUniforms.push(symbol); - } - }, - - /** - * Disable a uniform - * It will not affect the uniform state in the shader. Because the shader uniforms is parsed from shader code with naive regex. When using micro to disable some uniforms in the shader. It will still try to set these uniforms in each rendering pass. We can disable these uniforms manually if we need this bit performance improvement. Mostly we can simply ignore it. - * @param {string} symbol - */ - disableUniform: function(symbol) { - var idx = this._enabledUniforms.indexOf(symbol); - if (idx >= 0) { - this._enabledUniforms.splice(idx, 1); - } - }, - - /** - * @param {string} symbol - * @return {boolean} - */ - isUniformEnabled: function(symbol) { - return this._enabledUniforms.indexOf(symbol) >= 0; - }, - - /** - * Alias of setUniform and setUniforms - * @param {object|string} symbol - * @param {number|array|qtek.Texture|ArrayBufferView} [value] - */ - set: function(symbol, value) { - if (typeof(symbol) === 'object') { - for (var key in symbol) { - var val = symbol[key]; - this.set(key, val); - } - } else { - var uniform = this.uniforms[symbol]; - if (uniform) { - uniform.value = value; - } - } - }, - /** - * Get uniform value - * @param {string} symbol - * @return {number|array|qtek.Texture|ArrayBufferView} - */ - get: function(symbol) { - var uniform = this.uniforms[symbol]; - if (uniform) { - return uniform.value; - } - }, - /** - * Attach a shader instance - * @param {qtek.Shader} shader - * @param {boolean} keepUniform If try to keep uniform value - */ - attachShader: function(shader, keepUniform) { - if (this.shader) { - this.shader.detached(); - } - - var originalUniforms = this.uniforms; - this.uniforms = shader.createUniforms(); - this.shader = shader; - - this._enabledUniforms = Object.keys(this.uniforms); - - if (keepUniform) { - for (var symbol in originalUniforms) { - if (this.uniforms[symbol]) { - this.uniforms[symbol].value = originalUniforms[symbol].value; - } - } - } - - shader.attached(); - }, - - /** - * Detach a shader instance - */ - detachShader: function() { - this.shader.detached(); - this.shader = null; - this.uniforms = {}; - }, - - /** - * Clone a new material and keep uniforms, shader will not be cloned - * @return {qtek.Material} - */ - clone: function () { - var material = new Material({ - name: this.name, - shader: this.shader - }); - for (var symbol in this.uniforms) { - material.uniforms[symbol].value = this.uniforms[symbol].value; - } - material.depthTest = this.depthTest; - material.depthMask = this.depthMask; - material.transparent = this.transparent; - material.blend = this.blend; - - return material; - }, - - /** - * Dispose material, if material shader is not attached to any other materials - * Shader will also be disposed - * @param {WebGLRenderingContext} gl - * @param {boolean} [disposeTexture=false] If dispose the textures used in the material - */ - dispose: function(_gl, disposeTexture) { - if (disposeTexture) { - for (var name in this.uniforms) { - var val = this.uniforms[name].value; - if (!val ) { - continue; - } - if (val instanceof Texture) { - val.dispose(_gl); - } - else if (val instanceof Array) { - for (var i = 0; i < val.length; i++) { - if (val[i] instanceof Texture) { - val[i].dispose(_gl); - } - } - } - } - } - var shader = this.shader; - if (shader) { - this.detachShader(); - if (!shader.isAttachedToAny()) { - shader.dispose(_gl); - } - } - } - }); - - return Material; -}); -define('qtek/Renderable',['require','./Node','./core/glenum','./core/glinfo','./DynamicGeometry'],function(require) { - - - - var Node = require('./Node'); - var glenum = require('./core/glenum'); - var glinfo = require('./core/glinfo'); - var DynamicGeometry = require('./DynamicGeometry'); - - // Cache - var prevDrawID = 0; - var prevDrawIndicesBuffer = null; - var prevDrawIsUseFace = true; - - var currentDrawID; - - var RenderInfo = function() { - this.faceNumber = 0; - this.vertexNumber = 0; - this.drawCallNumber = 0; - }; - - function VertexArrayObject( - availableAttributes, - availableAttributeSymbols, - indicesBuffer - ) { - this.availableAttributes = availableAttributes; - this.availableAttributeSymbols = availableAttributeSymbols; - this.indicesBuffer = indicesBuffer; - - this.vao = null; - } - /** - * @constructor qtek.Renderable - * @extends qtek.Node - */ - var Renderable = Node.derive( - /** @lends qtek.Renderable# */ - { - /** - * @type {qtek.Material} - */ - material: null, - - /** - * @type {qtek.Geometry} - */ - geometry: null, - - /** - * @type {number} - */ - mode: glenum.TRIANGLES, - - _drawCache: null, - - _renderInfo: null - }, function() { - this._drawCache = {}; - this._renderInfo = new RenderInfo(); - }, - /** @lends qtek.Renderable.prototype */ - { - - /** - * Used when mode is LINES, LINE_STRIP or LINE_LOOP - * @type {number} - */ - lineWidth: 1, - - /** - * @type {boolean} - */ - culling: true, - /** - * @type {number} - */ - cullFace: glenum.BACK, - /** - * @type {number} - */ - frontFace: glenum.CCW, - - /** - * Software frustum culling - * @type {boolean} - */ - frustumCulling: true, - /** - * @type {boolean} - */ - receiveShadow: true, - /** - * @type {boolean} - */ - castShadow: true, - /** - * @type {boolean} - */ - ignorePicking: false, - - /** - * @return {boolean} - */ - isRenderable: function() { - return this.geometry && this.material && this.material.shader && this.visible; - }, - - /** - * @param {WebGLRenderingContext} _gl - * @param {qtek.Material} [globalMaterial] - * @return {Object} - */ - render: function(_gl, globalMaterial) { - var material = globalMaterial || this.material; - var shader = material.shader; - var geometry = this.geometry; - - var glDrawMode = this.mode; - - var nVertex = geometry.getVertexNumber(); - var isUseFace = geometry.isUseFace(); - - var uintExt = glinfo.getExtension(_gl, 'OES_element_index_uint'); - var useUintExt = uintExt && nVertex > 0xffff; - var indicesType = useUintExt ? _gl.UNSIGNED_INT : _gl.UNSIGNED_SHORT; - - var vaoExt = glinfo.getExtension(_gl, 'OES_vertex_array_object'); - - var isStatic = !geometry.dynamic; - - var renderInfo = this._renderInfo; - renderInfo.vertexNumber = nVertex; - renderInfo.faceNumber = 0; - renderInfo.drawCallNumber = 0; - // Draw each chunk - var drawHashChanged = false; - // Hash with shader id in case previous material has less attributes than next material - currentDrawID = _gl.__GLID__ + '-' + geometry.__GUID__ + '-' + shader.__GUID__; - - if (currentDrawID !== prevDrawID) { - drawHashChanged = true; - } else { - // The cache will be invalid in the following cases - // 1. Geometry is splitted to multiple chunks - // 2. VAO is enabled and is binded to null after render - // 3. Geometry needs update - if ( - ((geometry instanceof DynamicGeometry) && (nVertex > 0xffff && !uintExt) && isUseFace) - || (vaoExt && isStatic) - || geometry._cache.isDirty() - ) { - drawHashChanged = true; - } - } - prevDrawID = currentDrawID; - - if (!drawHashChanged) { - // Direct draw - if (prevDrawIsUseFace) { - _gl.drawElements(glDrawMode, prevDrawIndicesBuffer.count, indicesType, 0); - renderInfo.faceNumber = prevDrawIndicesBuffer.count / 3; - } - else { - // FIXME Use vertex number in buffer - // getVertexNumber may get the wrong value when geometry forget to mark dirty after update - _gl.drawArrays(glDrawMode, 0, nVertex); - } - renderInfo.drawCallNumber = 1; - } else { - // Use the cache of static geometry - var vaoList = this._drawCache[currentDrawID]; - if (!vaoList) { - var chunks = geometry.getBufferChunks(_gl); - if (!chunks) { // Empty mesh - return; - } - vaoList = []; - for (var c = 0; c < chunks.length; c++) { - var chunk = chunks[c]; - var attributeBuffers = chunk.attributeBuffers; - var indicesBuffer = chunk.indicesBuffer; - - var availableAttributes = []; - var availableAttributeSymbols = []; - for (var a = 0; a < attributeBuffers.length; a++) { - var attributeBufferInfo = attributeBuffers[a]; - var name = attributeBufferInfo.name; - var semantic = attributeBufferInfo.semantic; - var symbol; - if (semantic) { - var semanticInfo = shader.attribSemantics[semantic]; - symbol = semanticInfo && semanticInfo.symbol; - } else { - symbol = name; - } - if (symbol && shader.attributeTemplates[symbol]) { - availableAttributes.push(attributeBufferInfo); - availableAttributeSymbols.push(symbol); - } - } - - var vao = new VertexArrayObject( - availableAttributes, - availableAttributeSymbols, - indicesBuffer - ); - vaoList.push(vao); - } - if (isStatic) { - this._drawCache[currentDrawID] = vaoList; - } - } - - for (var i = 0; i < vaoList.length; i++) { - var vao = vaoList[i]; - var needsBindAttributes = true; - - // Create vertex object array cost a lot - // So we don't use it on the dynamic object - if (vaoExt && isStatic) { - // Use vertex array object - // http://blog.tojicode.com/2012/10/oesvertexarrayobject-extension.html - if (vao.vao == null) { - vao.vao = vaoExt.createVertexArrayOES(); - } else { - needsBindAttributes = false; - } - vaoExt.bindVertexArrayOES(vao.vao); - } - - var availableAttributes = vao.availableAttributes; - var indicesBuffer = vao.indicesBuffer; - - if (needsBindAttributes) { - var locationList = shader.enableAttributes(_gl, vao.availableAttributeSymbols, (vaoExt && isStatic && vao.vao)); - // Setting attributes; - for (var a = 0; a < availableAttributes.length; a++) { - var location = locationList[a]; - if (location === -1) { - continue; - } - var attributeBufferInfo = availableAttributes[a]; - var buffer = attributeBufferInfo.buffer; - var size = attributeBufferInfo.size; - var glType; - switch (attributeBufferInfo.type) { - case 'float': - glType = _gl.FLOAT; - break; - case 'byte': - glType = _gl.BYTE; - break; - case 'ubyte': - glType = _gl.UNSIGNED_BYTE; - break; - case 'short': - glType = _gl.SHORT; - break; - case 'ushort': - glType = _gl.UNSIGNED_SHORT; - break; - default: - glType = _gl.FLOAT; - break; - } - - _gl.bindBuffer(_gl.ARRAY_BUFFER, buffer); - _gl.vertexAttribPointer(location, size, glType, false, 0, 0); - } - } - if ( - glDrawMode == glenum.LINES || - glDrawMode == glenum.LINE_STRIP || - glDrawMode == glenum.LINE_LOOP - ) { - _gl.lineWidth(this.lineWidth); - } - - prevDrawIndicesBuffer = indicesBuffer; - prevDrawIsUseFace = geometry.isUseFace(); - //Do drawing - if (prevDrawIsUseFace) { - if (needsBindAttributes) { - _gl.bindBuffer(_gl.ELEMENT_ARRAY_BUFFER, indicesBuffer.buffer); - } - _gl.drawElements(glDrawMode, indicesBuffer.count, indicesType, 0); - renderInfo.faceNumber += indicesBuffer.count / 3; - } else { - _gl.drawArrays(glDrawMode, 0, nVertex); - } - - if (vaoExt && isStatic) { - vaoExt.bindVertexArrayOES(null); - } - - renderInfo.drawCallNumber++; - } - } - - return renderInfo; - }, - - /** - * Clone a new renderable - * @method - * @return {qtek.Renderable} - */ - clone: (function() { - var properties = [ - 'castShadow', 'receiveShadow', - 'mode', 'culling', 'cullFace', 'frontFace', - 'frustumCulling' - ]; - return function() { - var renderable = Node.prototype.clone.call(this); - - renderable.geometry = this.geometry; - renderable.material = this.material; - - for (var i = 0; i < properties.length; i++) { - var name = properties[i]; - // Try not to overwrite the prototype property - if (renderable[name] !== this[name]) { - renderable[name] = this[name]; - } - } - - return renderable; - }; - })() - }); - - Renderable.beforeFrame = function() { - prevDrawID = 0; - }; - - // Enums - Renderable.POINTS = glenum.POINTS; - Renderable.LINES = glenum.LINES; - Renderable.LINE_LOOP = glenum.LINE_LOOP; - Renderable.LINE_STRIP = glenum.LINE_STRIP; - Renderable.TRIANGLES = glenum.TRIANGLES; - Renderable.TRIANGLE_STRIP = glenum.TRIANGLE_STRIP; - Renderable.TRIANGLE_FAN = glenum.TRIANGLE_FAN; - - Renderable.BACK = glenum.BACK; - Renderable.FRONT = glenum.FRONT; - Renderable.FRONT_AND_BACK = glenum.FRONT_AND_BACK; - Renderable.CW = glenum.CW; - Renderable.CCW = glenum.CCW; - - Renderable.RenderInfo = RenderInfo; - - return Renderable; -}); -define('qtek/Mesh',['require','./Renderable','./core/glenum'],function(require) { - - - - var Renderable = require('./Renderable'); - var glenum = require('./core/glenum'); - - /** - * @constructor qtek.Mesh - * @extends qtek.Renderable - */ - var Mesh = Renderable.derive( - /** @lends qtek.Mesh# */ - { - /** - * Used when it is a skinned mesh - * @type {qtek.Skeleton} - */ - skeleton: null, - /** - * Joints indices Meshes can share the one skeleton instance and each mesh can use one part of joints. Joints indices indicate the index of joint in the skeleton instance - * @type {number[]} - */ - joints: null - - }, function() { - if (!this.joints) { - this.joints = []; - } - }, { - render: function(_gl, globalMaterial) { - var material = globalMaterial || this.material; - // Set pose matrices of skinned mesh - if (this.skeleton) { - var skinMatricesArray = this.skeleton.getSubSkinMatrices(this.__GUID__, this.joints); - material.shader.setUniformBySemantic(_gl, 'SKIN_MATRIX', skinMatricesArray); - } - - return Renderable.prototype.render.call(this, _gl, globalMaterial); - } - }); - - // Enums - Mesh.POINTS = glenum.POINTS; - Mesh.LINES = glenum.LINES; - Mesh.LINE_LOOP = glenum.LINE_LOOP; - Mesh.LINE_STRIP = glenum.LINE_STRIP; - Mesh.TRIANGLES = glenum.TRIANGLES; - Mesh.TRIANGLE_STRIP = glenum.TRIANGLE_STRIP; - Mesh.TRIANGLE_FAN = glenum.TRIANGLE_FAN; - - Mesh.BACK = glenum.BACK; - Mesh.FRONT = glenum.FRONT; - Mesh.FRONT_AND_BACK = glenum.FRONT_AND_BACK; - Mesh.CW = glenum.CW; - Mesh.CCW = glenum.CCW; - - return Mesh; -}); -/** - * @export{Object} library - */ -define('qtek/shader/library',['require','../Shader','../core/util'],function(require) { - - var Shader = require('../Shader'); - var util = require('../core/util'); - - var _library = {}; - - /** - * @export qtek.shader.library~Libaray - */ - function ShaderLibrary () { - this._pool = {}; - } - - /** - * ### Builin shaders - * + buildin.standard - * + buildin.basic - * + buildin.lambert - * + buildin.phong - * + buildin.wireframe - * - * @namespace qtek.shader.library - */ - /** - * - * Get shader from library. use shader name and option as hash key. - * - * @param {string} name - * @param {Object|string|Array.} [option] - * @return {qtek.Shader} - * - * @example - * qtek.shader.library.get('buildin.phong', 'diffuseMap', 'normalMap'); - * qtek.shader.library.get('buildin.phong', ['diffuseMap', 'normalMap']); - * qtek.shader.library.get('buildin.phong', { - * textures: ['diffuseMap'], - * vertexDefines: {}, - * fragmentDefines: {} - */ - ShaderLibrary.prototype.get = function(name, option) { - var enabledTextures = []; - var vertexDefines = {}; - var fragmentDefines = {}; - if (typeof(option) === 'string') { - enabledTextures = Array.prototype.slice.call(arguments, 1); - } - else if (Object.prototype.toString.call(option) == '[object Object]') { - enabledTextures = option.textures || []; - vertexDefines = option.vertexDefines || {}; - fragmentDefines = option.fragmentDefines || {}; - } - else if(option instanceof Array) { - enabledTextures = option; - } - var vertexDefineKeys = Object.keys(vertexDefines); - var fragmentDefineKeys = Object.keys(fragmentDefines); - enabledTextures.sort(); - vertexDefineKeys.sort(); - fragmentDefineKeys.sort(); - - var keyArr = [name]; - keyArr = keyArr.concat(enabledTextures); - for (var i = 0; i < vertexDefineKeys.length; i++) { - keyArr.push(vertexDefines[vertexDefineKeys[i]]); - } - for (var i = 0; i < fragmentDefineKeys.length; i++) { - keyArr.push(fragmentDefines[fragmentDefineKeys[i]]); - } - var key = keyArr.join('_'); - - if (this._pool[key]) { - return this._pool[key]; - } else { - var source = _library[name]; - if (!source) { - console.error('Shader "' + name + '"' + ' is not in the library'); - return; - } - var shader = new Shader({ - 'vertex': source.vertex, - 'fragment': source.fragment - }); - for (var i = 0; i < enabledTextures.length; i++) { - shader.enableTexture(enabledTextures[i]); - } - for (var name in vertexDefines) { - shader.define('vertex', name, vertexDefines[name]); - } - for (var name in fragmentDefines) { - shader.define('fragment', name, fragmentDefines[name]); - } - this._pool[key] = shader; - return shader; - } - }; - - /** - * Clear shaders - */ - ShaderLibrary.prototype.clear = function() { - this._pool = {}; - }; - - /** - * @memberOf qtek.shader.library - * @param {string} name - * @param {string} vertex - Vertex shader code - * @param {string} fragment - Fragment shader code - */ - function template(name, vertex, fragment) { - _library[name] = { - vertex: vertex, - fragment: fragment - }; - } - - var defaultLibrary = new ShaderLibrary(); - - return { - createLibrary: function () { - return new ShaderLibrary(); - }, - get: function () { - return defaultLibrary.get.apply(defaultLibrary, arguments); - }, - template: template, - clear: function () { - return defaultLibrary.clear(); - } - }; -}); -define('qtek/math/Vector2',['require','../dep/glmatrix'],function(require) { - - - - var glMatrix = require('../dep/glmatrix'); - var vec2 = glMatrix.vec2; - - /** - * @constructor - * @alias qtek.math.Vector2 - * @param {number} x - * @param {number} y - */ - var Vector2 = function(x, y) { - - x = x || 0; - y = y || 0; - - /** - * Storage of Vector2, read and write of x, y will change the values in _array - * All methods also operate on the _array instead of x, y components - * @type {Float32Array} - */ - this._array = vec2.fromValues(x, y); - - /** - * Dirty flag is used by the Node to determine - * if the matrix is updated to latest - * @type {boolean} - */ - this._dirty = true; - }; - - Vector2.prototype = { - - constructor: Vector2, - - /** - * Add b to self - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - add: function(b) { - vec2.add(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Set x and y components - * @param {number} x - * @param {number} y - * @return {qtek.math.Vector2} - */ - set: function(x, y) { - this._array[0] = x; - this._array[1] = y; - this._dirty = true; - return this; - }, - - /** - * Set x and y components from array - * @param {Float32Array|number[]} arr - * @return {qtek.math.Vector2} - */ - setArray: function(arr) { - this._array[0] = arr[0]; - this._array[1] = arr[1]; - - this._dirty = true; - return this; - }, - - /** - * Clone a new Vector2 - * @return {qtek.math.Vector2} - */ - clone: function() { - return new Vector2(this.x, this.y); - }, - - /** - * Copy x, y from b - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - copy: function(b) { - vec2.copy(this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Cross product of self and b, written to a Vector3 out - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - cross: function(out, b) { - vec2.cross(out._array, this._array, b._array); - out._dirty = true; - return this; - }, - - /** - * Alias for distance - * @param {qtek.math.Vector2} b - * @return {number} - */ - dist: function(b) { - return vec2.dist(this._array, b._array); - }, - - /** - * Distance between self and b - * @param {qtek.math.Vector2} b - * @return {number} - */ - distance: function(b) { - return vec2.distance(this._array, b._array); - }, - - /** - * Alias for divide - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - div: function(b) { - vec2.div(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Divide self by b - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - divide: function(b) { - vec2.divide(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Dot product of self and b - * @param {qtek.math.Vector2} b - * @return {number} - */ - dot: function(b) { - return vec2.dot(this._array, b._array); - }, - - /** - * Alias of length - * @return {number} - */ - len: function() { - return vec2.len(this._array); - }, - - /** - * Calculate the length - * @return {number} - */ - length: function() { - return vec2.length(this._array); - }, - - /** - * Linear interpolation between a and b - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @param {number} t - * @return {qtek.math.Vector2} - */ - lerp: function(a, b, t) { - vec2.lerp(this._array, a._array, b._array, t); - this._dirty = true; - return this; - }, - - /** - * Minimum of self and b - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - min: function(b) { - vec2.min(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Maximum of self and b - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - max: function(b) { - vec2.max(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Alias for multiply - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - mul: function(b) { - vec2.mul(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Mutiply self and b - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - multiply: function(b) { - vec2.multiply(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Negate self - * @return {qtek.math.Vector2} - */ - negate: function() { - vec2.negate(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Normalize self - * @return {qtek.math.Vector2} - */ - normalize: function() { - vec2.normalize(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Generate random x, y components with a given scale - * @param {number} scale - * @return {qtek.math.Vector2} - */ - random: function(scale) { - vec2.random(this._array, scale); - this._dirty = true; - return this; - }, - - /** - * Scale self - * @param {number} scale - * @return {qtek.math.Vector2} - */ - scale: function(s) { - vec2.scale(this._array, this._array, s); - this._dirty = true; - return this; - }, - - /** - * Scale b and add to self - * @param {qtek.math.Vector2} b - * @param {number} scale - * @return {qtek.math.Vector2} - */ - scaleAndAdd: function(b, s) { - vec2.scaleAndAdd(this._array, this._array, b._array, s); - this._dirty = true; - return this; - }, - - /** - * Alias for squaredDistance - * @param {qtek.math.Vector2} b - * @return {number} - */ - sqrDist: function(b) { - return vec2.sqrDist(this._array, b._array); - }, - - /** - * Squared distance between self and b - * @param {qtek.math.Vector2} b - * @return {number} - */ - squaredDistance: function(b) { - return vec2.squaredDistance(this._array, b._array); - }, - - /** - * Alias for squaredLength - * @return {number} - */ - sqrLen: function() { - return vec2.sqrLen(this._array); - }, - - /** - * Squared length of self - * @return {number} - */ - squaredLength: function() { - return vec2.squaredLength(this._array); - }, - - /** - * Alias for subtract - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - sub: function(b) { - vec2.sub(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Subtract b from self - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - subtract: function(b) { - vec2.subtract(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Transform self with a Matrix2 m - * @param {qtek.math.Matrix2} m - * @return {qtek.math.Vector2} - */ - transformMat2: function(m) { - vec2.transformMat2(this._array, this._array, m._array); - this._dirty = true; - return this; - }, - - /** - * Transform self with a Matrix2d m - * @param {qtek.math.Matrix2d} m - * @return {qtek.math.Vector2} - */ - transformMat2d: function(m) { - vec2.transformMat2d(this._array, this._array, m._array); - this._dirty = true; - return this; - }, - - /** - * Transform self with a Matrix3 m - * @param {qtek.math.Matrix3} m - * @return {qtek.math.Vector2} - */ - transformMat3: function(m) { - vec2.transformMat3(this._array, this._array, m._array); - this._dirty = true; - return this; - }, - - /** - * Transform self with a Matrix4 m - * @param {qtek.math.Matrix4} m - * @return {qtek.math.Vector2} - */ - transformMat4: function(m) { - vec2.transformMat4(this._array, this._array, m._array); - this._dirty = true; - return this; - }, - - toString: function() { - return '[' + Array.prototype.join.call(this._array, ',') + ']'; - }, - }; - - // Getter and Setter - if (Object.defineProperty) { - - var proto = Vector2.prototype; - /** - * @name x - * @type {number} - * @memberOf qtek.math.Vector2 - * @instance - */ - Object.defineProperty(proto, 'x', { - get: function () { - return this._array[0]; - }, - set: function (value) { - this._array[0] = value; - this._dirty = true; - } - }); - - /** - * @name y - * @type {number} - * @memberOf qtek.math.Vector2 - * @instance - */ - Object.defineProperty(proto, 'y', { - get: function () { - return this._array[1]; - }, - set: function (value) { - this._array[1] = value; - this._dirty = true; - } - }); - } - - // Supply methods that are not in place - - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - Vector2.add = function(out, a, b) { - vec2.add(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector2} out - * @param {number} x - * @param {number} y - * @return {qtek.math.Vector2} - */ - Vector2.set = function(out, x, y) { - vec2.set(out._array, x, y); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - Vector2.copy = function(out, b) { - vec2.copy(out._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - Vector2.cross = function(out, a, b) { - vec2.cross(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {number} - */ - Vector2.dist = function(a, b) { - return vec2.distance(a._array, b._array); - }; - /** - * @method - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {number} - */ - Vector2.distance = Vector2.dist; - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - Vector2.div = function(out, a, b) { - vec2.divide(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - /** - * @method - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - Vector2.divide = Vector2.div; - /** - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {number} - */ - Vector2.dot = function(a, b) { - return vec2.dot(a._array, b._array); - }; - - /** - * @param {qtek.math.Vector2} a - * @return {number} - */ - Vector2.len = function(b) { - return vec2.length(b._array); - }; - - // Vector2.length = Vector2.len; - - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @param {number} t - * @return {qtek.math.Vector2} - */ - Vector2.lerp = function(out, a, b, t) { - vec2.lerp(out._array, a._array, b._array, t); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - Vector2.min = function(out, a, b) { - vec2.min(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - Vector2.max = function(out, a, b) { - vec2.max(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - Vector2.mul = function(out, a, b) { - vec2.multiply(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - /** - * @method - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - Vector2.multiply = Vector2.mul; - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @return {qtek.math.Vector2} - */ - Vector2.negate = function(out, a) { - vec2.negate(out._array, a._array); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @return {qtek.math.Vector2} - */ - Vector2.normalize = function(out, a) { - vec2.normalize(out._array, a._array); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector2} out - * @param {number} scale - * @return {qtek.math.Vector2} - */ - Vector2.random = function(out, scale) { - vec2.random(out._array, scale); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {number} scale - * @return {qtek.math.Vector2} - */ - Vector2.scale = function(out, a, scale) { - vec2.scale(out._array, a._array, scale); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @param {number} scale - * @return {qtek.math.Vector2} - */ - Vector2.scaleAndAdd = function(out, a, b, scale) { - vec2.scaleAndAdd(out._array, a._array, b._array, scale); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {number} - */ - Vector2.sqrDist = function(a, b) { - return vec2.sqrDist(a._array, b._array); - }; - /** - * @method - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {number} - */ - Vector2.squaredDistance = Vector2.sqrDist; - - /** - * @param {qtek.math.Vector2} a - * @return {number} - */ - Vector2.sqrLen = function(a) { - return vec2.sqrLen(a._array); - }; - /** - * @method - * @param {qtek.math.Vector2} a - * @return {number} - */ - Vector2.squaredLength = Vector2.sqrLen; - - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - Vector2.sub = function(out, a, b) { - vec2.subtract(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - /** - * @method - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - Vector2.subtract = Vector2.sub; - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Matrix2} m - * @return {qtek.math.Vector2} - */ - Vector2.transformMat2 = function(out, a, m) { - vec2.transformMat2(out._array, a._array, m._array); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Matrix2d} m - * @return {qtek.math.Vector2} - */ - Vector2.transformMat2d = function(out, a, m) { - vec2.transformMat2d(out._array, a._array, m._array); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {Matrix3} m - * @return {qtek.math.Vector2} - */ - Vector2.transformMat3 = function(out, a, m) { - vec2.transformMat3(out._array, a._array, m._array); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Matrix4} m - * @return {qtek.math.Vector2} - */ - Vector2.transformMat4 = function(out, a, m) { - vec2.transformMat4(out._array, a._array, m._array); - out._dirty = true; - return out; - }; - - return Vector2; - -}); -// TODO Resources like shader, texture, geometry reference management -// Trace and find out which shader, texture, geometry can be destroyed -define('qtek/Renderer',['require','./core/Base','./Texture','./core/glinfo','./core/glenum','./math/BoundingBox','./math/Matrix4','./shader/library','./Material','./math/Vector2','./dep/glmatrix'],function(require) { - - - - var Base = require('./core/Base'); - var Texture = require('./Texture'); - var glinfo = require('./core/glinfo'); - var glenum = require('./core/glenum'); - var BoundingBox = require('./math/BoundingBox'); - var Matrix4 = require('./math/Matrix4'); - var shaderLibrary = require('./shader/library'); - var Material = require('./Material'); - var Vector2 = require('./math/Vector2'); - - var glMatrix = require('./dep/glmatrix'); - var mat4 = glMatrix.mat4; - var vec3 = glMatrix.vec3; - - var glid = 0; - - var errorShader = {}; - - /** - * @constructor qtek.Renderer - */ - var Renderer = Base.derive(function() { - return /** @lends qtek.Renderer# */ { - - /** - * @type {HTMLCanvasElement} - * @readonly - */ - canvas: null, - - /** - * Canvas width, set by resize method - * @type {number} - * @private - */ - width: 100, - - /** - * Canvas width, set by resize method - * @type {number} - * @private - */ - height: 100, - - /** - * Device pixel ratio, set by setDevicePixelRatio method - * Specially for high defination display - * @see http://www.khronos.org/webgl/wiki/HandlingHighDPI - * @type {number} - * @private - */ - devicePixelRatio: window.devicePixelRatio || 1.0, - - /** - * Clear color - * @type {number[]} - */ - color: [0.0, 0.0, 0.0, 0.0], - - /** - * Default: - * _gl.COLOR_BUFFER_BIT | _gl.DEPTH_BUFFER_BIT | _gl.STENCIL_BUFFER_BIT - * @type {number} - */ - clear: 17664, - - // Settings when getting context - // http://www.khronos.org/registry/webgl/specs/latest/#2.4 - - /** - * If enable alpha, default true - * @type {boolean} - */ - alhpa: true, - /** - * If enable depth buffer, default true - * @type {boolean} - */ - depth: true, - /** - * If enable stencil buffer, default false - * @type {boolean} - */ - stencil: false, - /** - * If enable antialias, default true - * @type {boolean} - */ - antialias: true, - /** - * If enable premultiplied alpha, default true - * @type {boolean} - */ - premultipliedAlpha: true, - /** - * If preserve drawing buffer, default false - * @type {boolean} - */ - preserveDrawingBuffer: false, - /** - * If throw context error, usually turned on in debug mode - * @type {boolean} - */ - throwError: true, - /** - * WebGL Context created from given canvas - * @type {WebGLRenderingContext} - */ - gl: null, - /** - * Renderer viewport, read-only, can be set by setViewport method - * @type {Object} - */ - viewport: {}, - - _viewportSettings: [], - _clearSettings: [], - - _sceneRendering: null - }; - }, function() { - - if (!this.canvas) { - this.canvas = document.createElement('canvas'); - this.canvas.width = this.width; - this.canvas.height = this.height; - } - try { - var opts = { - alhpa: this.alhpa, - depth: this.depth, - stencil: this.stencil, - antialias: this.antialias, - premultipliedAlpha: this.premultipliedAlpha, - preserveDrawingBuffer: this.preserveDrawingBuffer - }; - - this.gl = this.canvas.getContext('webgl', opts) - || this.canvas.getContext('experimental-webgl', opts); - - if (!this.gl) { - throw new Error(); - } - - this.gl.__GLID__ = glid++; - - this.width = this.canvas.width; - this.height = this.canvas.height; - this.resize(this.width, this.height); - - glinfo.initialize(this.gl); - } catch(e) { - throw 'Error creating WebGL Context ' + e; - } - }, - /** @lends qtek.Renderer.prototype. **/ - { - /** - * Resize the canvas - * @param {number} width - * @param {number} height - */ - resize: function(width, height) { - var canvas = this.canvas; - // http://www.khronos.org/webgl/wiki/HandlingHighDPI - // set the display size of the canvas. - if (typeof(width) !== 'undefined') { - canvas.style.width = width + 'px'; - canvas.style.height = height + 'px'; - // set the size of the drawingBuffer - canvas.width = width * this.devicePixelRatio; - canvas.height = height * this.devicePixelRatio; - - this.width = width; - this.height = height; - } else { - this.width = canvas.width / this.devicePixelRatio; - this.height = canvas.height / this.devicePixelRatio; - } - - this.setViewport(0, 0, width, height); - }, - - /** - * Get renderer width - * @return {number} - */ - getWidth: function () { - return this.width; - }, - - /** - * Get renderer height - * @return {number} - */ - getHeight: function () { - return this.height; - }, - - /** - * Set devicePixelRatio - * @param {number} devicePixelRatio - */ - setDevicePixelRatio: function(devicePixelRatio) { - this.devicePixelRatio = devicePixelRatio; - this.resize(this.width, this.height); - }, - - /** - * Get devicePixelRatio - * @param {number} devicePixelRatio - */ - getDevicePixelRatio: function () { - return this.devicePixelRatio; - }, - - /** - * Get WebGL extionsion - * @return {[type]} [description] - */ - getExtension: function (name) { - return glinfo.getExtension(this.gl, name); - }, - - /** - * Set rendering viewport - * @param {number|Object} x - * @param {number} [y] - * @param {number} [width] - * @param {number} [height] - * @param {number} [devicePixelRatio] - * Defaultly use the renderere devicePixelRatio - * It needs to be 1 when setViewport is called by frameBuffer - */ - setViewport: function(x, y, width, height, dpr) { - - if (typeof(x) === 'object') { - var obj = x; - x = obj.x; - y = obj.y; - width = obj.width; - height = obj.height; - } - dpr = dpr || this.devicePixelRatio; - - this.gl.viewport( - x * dpr, y * dpr, width * dpr, height * dpr - ); - - this.viewport = { - x: x, - y: y, - width: width, - height: height - }; - }, - - /** - * Push current viewport into a stack - */ - saveViewport: function() { - this._viewportSettings.push(this.viewport); - }, - - /** - * Pop viewport from stack, restore in the renderer - */ - restoreViewport: function() { - if (this._viewportSettings.length > 0) { - this.setViewport(this._viewportSettings.pop()); - } - }, - - /** - * Push current clear into a stack - */ - saveClear: function() { - this._clearSettings.push(this.clear); - }, - - /** - * Pop clear from stack, restore in the renderer - */ - restoreClear: function() { - if (this._clearSettings.length > 0) { - this.clear = this._clearSettings.pop(); - } - }, - /** - * Render the scene in camera to the screen or binded offline framebuffer - * @param {qtek.Scene} scene - * @param {qtek.Camera} camera - * @param {boolean} [notUpdateScene] If not call the scene.update methods in the rendering, default true - * @param {boolean} [preZ] If use preZ optimization, default false - * @return {IRenderInfo} - */ - render: function(scene, camera, notUpdateScene, preZ) { - var _gl = this.gl; - - this._sceneRendering = scene; - - var color = this.color; - - if (this.clear) { - // Must set depth and color mask true before clear - _gl.colorMask(true, true, true, true); - _gl.depthMask(true); - - _gl.clearColor(color[0], color[1], color[2], color[3]); - _gl.clear(this.clear); - } - - // If the scene have been updated in the prepass like shadow map - // There is no need to update it again - if (!notUpdateScene) { - scene.update(false); - } - // Update if camera not mounted on the scene - if (!camera.getScene()) { - camera.update(true); - } - - var opaqueQueue = scene.opaqueQueue; - var transparentQueue = scene.transparentQueue; - var sceneMaterial = scene.material; - - scene.trigger('beforerender', this, scene, camera); - // Sort render queue - // Calculate the object depth - if (transparentQueue.length > 0) { - var worldViewMat = mat4.create(); - var posViewSpace = vec3.create(); - for (var i = 0; i < transparentQueue.length; i++) { - var node = transparentQueue[i]; - mat4.multiply(worldViewMat, camera.viewMatrix._array, node.worldTransform._array); - vec3.transformMat4(posViewSpace, node.position._array, worldViewMat); - node.__depth = posViewSpace[2]; - } - } - opaqueQueue.sort(Renderer.opaqueSortFunc); - transparentQueue.sort(Renderer.transparentSortFunc); - - // Render Opaque queue - scene.trigger('beforerender:opaque', this, opaqueQueue); - - // Reset the scene bounding box; - camera.sceneBoundingBoxLastFrame.min.set(Infinity, Infinity, Infinity); - camera.sceneBoundingBoxLastFrame.max.set(-Infinity, -Infinity, -Infinity); - - _gl.disable(_gl.BLEND); - _gl.enable(_gl.DEPTH_TEST); - var opaqueRenderInfo = this.renderQueue(opaqueQueue, camera, sceneMaterial, preZ); - - scene.trigger('afterrender:opaque', this, opaqueQueue, opaqueRenderInfo); - scene.trigger('beforerender:transparent', this, transparentQueue); - - // Render Transparent Queue - _gl.enable(_gl.BLEND); - var transparentRenderInfo = this.renderQueue(transparentQueue, camera, sceneMaterial); - - scene.trigger('afterrender:transparent', this, transparentQueue, transparentRenderInfo); - var renderInfo = {}; - for (var name in opaqueRenderInfo) { - renderInfo[name] = opaqueRenderInfo[name] + transparentRenderInfo[name]; - } - - scene.trigger('afterrender', this, scene, camera, renderInfo); - - // Cleanup - this._sceneRendering = null; - return renderInfo; - }, - - /** - * Render a single renderable list in camera in sequence - * @param {qtek.Renderable[]} queue List of all renderables. - * Best to be sorted by Renderer.opaqueSortFunc or Renderer.transparentSortFunc - * @param {qtek.Camera} camera - * @param {qtek.Material} [globalMaterial] globalMaterial will override the material of each renderable - * @param {boolean} [preZ] If use preZ optimization, default false - * @return {IRenderInfo} - */ - renderQueue: function(queue, camera, globalMaterial, preZ) { - var renderInfo = { - faceNumber: 0, - vertexNumber: 0, - drawCallNumber: 0, - meshNumber: queue.length, - renderedMeshNumber: 0 - }; - - // Calculate view and projection matrix - mat4.copy(matrices.VIEW, camera.viewMatrix._array); - mat4.copy(matrices.PROJECTION, camera.projectionMatrix._array); - mat4.multiply(matrices.VIEWPROJECTION, camera.projectionMatrix._array, matrices.VIEW); - mat4.copy(matrices.VIEWINVERSE, camera.worldTransform._array); - mat4.invert(matrices.PROJECTIONINVERSE, matrices.PROJECTION); - mat4.invert(matrices.VIEWPROJECTIONINVERSE, matrices.VIEWPROJECTION); - - var _gl = this.gl; - var scene = this._sceneRendering; - - var prevMaterial; - var prevShader; - - // Status - var depthTest, depthMask; - var culling, cullFace, frontFace; - - var culledRenderQueue; - if (preZ) { - var preZPassMaterial = new Material({ - shader: shaderLibrary.get('buildin.prez') - }); - var preZPassShader = preZPassMaterial.shader; - - culledRenderQueue = []; - preZPassShader.bind(_gl); - _gl.colorMask(false, false, false, false); - _gl.depthMask(true); - _gl.enable(_gl.DEPTH_TEST); - for (var i = 0; i < queue.length; i++) { - var renderable = queue[i]; - var worldM = renderable.worldTransform._array; - var geometry = renderable.geometry; - mat4.multiply(matrices.WORLDVIEW, matrices.VIEW , worldM); - mat4.multiply(matrices.WORLDVIEWPROJECTION, matrices.VIEWPROJECTION , worldM); - - if (geometry.boundingBox) { - if (this.isFrustumCulled( - renderable, camera, matrices.WORLDVIEW, matrices.PROJECTION - )) { - continue; - } - } - if (renderable.skeleton) { // Skip skinned mesh - continue; - } - if (renderable.cullFace !== cullFace) { - cullFace = renderable.cullFace; - _gl.cullFace(cullFace); - } - if (renderable.frontFace !== frontFace) { - frontFace = renderable.frontFace; - _gl.frontFace(frontFace); - } - if (renderable.culling !== culling) { - culling = renderable.culling; - culling ? _gl.enable(_gl.CULL_FACE) : _gl.disable(_gl.CULL_FACE); - } - - var semanticInfo = preZPassShader.matrixSemantics.WORLDVIEWPROJECTION; - preZPassShader.setUniform(_gl, semanticInfo.type, semanticInfo.symbol, matrices.WORLDVIEWPROJECTION); - renderable.render(_gl, preZPassMaterial); - culledRenderQueue.push(renderable); - } - _gl.depthFunc(_gl.LEQUAL); - _gl.colorMask(true, true, true, true); - _gl.depthMask(false); - } else { - culledRenderQueue = queue; - } - - culling = null; - cullFace = null; - frontFace = null; - - for (var i =0; i < culledRenderQueue.length; i++) { - var renderable = culledRenderQueue[i]; - var material = globalMaterial || renderable.material; - var shader = material.shader; - var geometry = renderable.geometry; - - var worldM = renderable.worldTransform._array; - // All matrices ralated to world matrix will be updated on demand; - mat4.copy(matrices.WORLD, worldM); - mat4.multiply(matrices.WORLDVIEW, matrices.VIEW , worldM); - mat4.multiply(matrices.WORLDVIEWPROJECTION, matrices.VIEWPROJECTION , worldM); - if (shader.matrixSemantics.WORLDINVERSE || - shader.matrixSemantics.WORLDINVERSETRANSPOSE) { - mat4.invert(matrices.WORLDINVERSE, worldM); - } - if (shader.matrixSemantics.WORLDVIEWINVERSE || - shader.matrixSemantics.WORLDVIEWINVERSETRANSPOSE) { - mat4.invert(matrices.WORLDVIEWINVERSE, matrices.WORLDVIEW); - } - if (shader.matrixSemantics.WORLDVIEWPROJECTIONINVERSE || - shader.matrixSemantics.WORLDVIEWPROJECTIONINVERSETRANSPOSE) { - mat4.invert(matrices.WORLDVIEWPROJECTIONINVERSE, matrices.WORLDVIEWPROJECTION); - } - if (geometry.boundingBox && ! preZ) { - if (this.isFrustumCulled( - renderable, camera, matrices.WORLDVIEW, matrices.PROJECTION - )) { - continue; - } - } - - if (prevShader !== shader) { - // Set lights number - if (scene && scene.isShaderLightNumberChanged(shader)) { - scene.setShaderLightNumber(shader); - } - - var errMsg = shader.bind(_gl); - if (errMsg) { - - if (errorShader[shader.__GUID__]) { - continue; - } - errorShader[shader.__GUID__] = true; - - if (this.throwError) { - throw new Error(errMsg); - } else { - this.trigger('error', errMsg); - } - } - - // Set lights uniforms - // TODO needs optimized - if (scene) { - scene.setLightUniforms(shader, _gl); - } - prevShader = shader; - } - if (prevMaterial !== material) { - if (!preZ) { - if (material.depthTest !== depthTest) { - material.depthTest ? - _gl.enable(_gl.DEPTH_TEST) : - _gl.disable(_gl.DEPTH_TEST); - depthTest = material.depthTest; - } - if (material.depthMask !== depthMask) { - _gl.depthMask(material.depthMask); - depthMask = material.depthMask; - } - } - material.bind(_gl, prevMaterial); - prevMaterial = material; - - // TODO cache blending - if (material.transparent) { - if (material.blend) { - material.blend(_gl); - } else { // Default blend function - _gl.blendEquationSeparate(_gl.FUNC_ADD, _gl.FUNC_ADD); - _gl.blendFuncSeparate(_gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA, _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA); - } - } - } - - var matrixSemanticKeys = shader.matrixSemanticKeys; - for (var k = 0; k < matrixSemanticKeys.length; k++) { - var semantic = matrixSemanticKeys[k]; - var semanticInfo = shader.matrixSemantics[semantic]; - var matrix = matrices[semantic]; - if (semanticInfo.isTranspose) { - var matrixNoTranspose = matrices[semanticInfo.semanticNoTranspose]; - mat4.transpose(matrix, matrixNoTranspose); - } - shader.setUniform(_gl, semanticInfo.type, semanticInfo.symbol, matrix); - } - - if (renderable.cullFace !== cullFace) { - cullFace = renderable.cullFace; - _gl.cullFace(cullFace); - } - if (renderable.frontFace !== frontFace) { - frontFace = renderable.frontFace; - _gl.frontFace(frontFace); - } - if (renderable.culling !== culling) { - culling = renderable.culling; - culling ? _gl.enable(_gl.CULL_FACE) : _gl.disable(_gl.CULL_FACE); - } - - var objectRenderInfo = renderable.render(_gl, globalMaterial); - - if (objectRenderInfo) { - renderInfo.faceNumber += objectRenderInfo.faceNumber; - renderInfo.vertexNumber += objectRenderInfo.vertexNumber; - renderInfo.drawCallNumber += objectRenderInfo.drawCallNumber; - renderInfo.renderedMeshNumber ++; - } - } - - if (preZ) { - // default depth func - _gl.depthFunc(_gl.LESS); - } - - return renderInfo; - }, - - /** - * Evaluate if an scene object is culled by camera frustum - * - * Object can be a renderable or a light - * - * @param {qtek.Node} Scene object - * @param {qtek.Camera} camera - * @param {Array.} worldViewMat represented with array - * @param {Array.} projectionMat represented with array - */ - isFrustumCulled: (function() { - // Frustum culling - // http://www.cse.chalmers.se/~uffe/vfc_bbox.pdf - var cullingBoundingBox = new BoundingBox(); - var cullingMatrix = new Matrix4(); - return function(object, camera, worldViewMat, projectionMat) { - // Bounding box can be a property of object(like light) or renderable.geometry - var geoBBox = object.boundingBox || object.geometry.boundingBox; - cullingMatrix._array = worldViewMat; - cullingBoundingBox.copy(geoBBox); - cullingBoundingBox.applyTransform(cullingMatrix); - - // Passingly update the scene bounding box - // FIXME exclude very large mesh like ground plane or terrain ? - // FIXME Only rendererable which cast shadow ? - if (object.isRenderable() && object.castShadow) { - camera.sceneBoundingBoxLastFrame.union(cullingBoundingBox); - } - - if (object.frustumCulling) { - if (!cullingBoundingBox.intersectBoundingBox(camera.frustum.boundingBox)) { - return true; - } - - cullingMatrix._array = projectionMat; - if ( - cullingBoundingBox.max._array[2] > 0 && - cullingBoundingBox.min._array[2] < 0 - ) { - // Clip in the near plane - cullingBoundingBox.max._array[2] = -1e-20; - } - - cullingBoundingBox.applyProjection(cullingMatrix); - - var min = cullingBoundingBox.min._array; - var max = cullingBoundingBox.max._array; - - if ( - max[0] < -1 || min[0] > 1 - || max[1] < -1 || min[1] > 1 - || max[2] < -1 || min[2] > 1 - ) { - return true; - } - } - return false; - }; - })(), - - /** - * Dispose given scene, including all geometris, textures and shaders in the scene - * @param {qtek.Scene} scene - */ - disposeScene: function(scene) { - this.disposeNode(scene, true, true); - scene.dispose(); - }, - - /** - * Dispose given node, including all geometries, textures and shaders attached on it or its descendant - * @param {qtek.Node} node - * @param {boolean} [disposeGeometry=false] If dispose the geometries used in the descendant mesh - * @param {boolean} [disposeTexture=false] If dispose the textures used in the descendant mesh - */ - disposeNode: function(root, disposeGeometry, disposeTexture) { - var materials = {}; - var _gl = this.gl; - // Dettached from parent - if (root.getParent()) { - root.getParent().remove(root); - } - root.traverse(function(node) { - if (node.geometry && disposeGeometry) { - node.geometry.dispose(_gl); - } - if (node.material) { - materials[node.material.__GUID__] = node.material; - } - // Particle system need to dispose - if (node.dispose) { - node.dispose(_gl); - } - }); - for (var guid in materials) { - var mat = materials[guid]; - mat.dispose(_gl, disposeTexture); - } - }, - - /** - * Dispose given shader - * @param {qtek.Shader} shader - */ - disposeShader: function(shader) { - shader.dispose(this.gl); - }, - - /** - * Dispose given geometry - * @param {qtek.Geometry} geometry - */ - disposeGeometry: function(geometry) { - geometry.dispose(this.gl); - }, - - /** - * Dispose given texture - * @param {qtek.Texture} texture - */ - disposeTexture: function(texture) { - texture.dispose(this.gl); - }, - - /** - * Dispose given frame buffer - * @param {qtek.FrameBuffer} frameBuffer - */ - disposeFrameBuffer: function(frameBuffer) { - frameBuffer.dispose(this.gl); - }, - - /** - * Dispose renderer - */ - dispose: function() { - glinfo.dispose(this.gl); - }, - - /** - * Convert screen coords to normalized device coordinates(NDC) - * Screen coords can get from mouse event, it is positioned relative to canvas element - * NDC can be used in ray casting with Camera.prototype.castRay methods - * - * @param {number} x - * @param {number} y - * @param {qtek.math.Vector2} [out] - * @return {qtek.math.Vector2} - */ - screenToNdc: function(x, y, out) { - if (!out) { - out = new Vector2(); - } - // Invert y; - y = this.height - y; - - var viewport = this.viewport; - var arr = out._array; - arr[0] = (x - viewport.x) / viewport.width; - arr[0] = arr[0] * 2 - 1; - arr[1] = (y - viewport.y) / viewport.height; - arr[1] = arr[1] * 2 - 1; - - return out; - } - }); - - /** - * Opaque renderables compare function - * @param {qtek.Renderable} x - * @param {qtek.Renderable} y - * @return {boolean} - * @static - */ - Renderer.opaqueSortFunc = function(x, y) { - // Priority shader -> material -> geometry - if (x.material.shader === y.material.shader) { - if (x.material === y.material) { - return x.geometry.__GUID__ - y.geometry.__GUID__; - } - return x.material.__GUID__ - y.material.__GUID__; - } - return x.material.shader.__GUID__ - y.material.shader.__GUID__; - }; - - /** - * Transparent renderables compare function - * @param {qtek.Renderable} a - * @param {qtek.Renderable} b - * @return {boolean} - * @static - */ - Renderer.transparentSortFunc = function(x, y) { - // Priority depth -> shader -> material -> geometry - if (x.__depth === y.__depth) { - if (x.material.shader === y.material.shader) { - if (x.material === y.material) { - return x.geometry.__GUID__ - y.geometry.__GUID__; - } - return x.material.__GUID__ - y.material.__GUID__; - } - return x.material.shader.__GUID__ - y.material.shader.__GUID__; - } - // Depth is negative - // So farther object has smaller depth value - return x.__depth - y.__depth; - }; - - // Temporary variables - var matrices = { - WORLD: mat4.create(), - VIEW: mat4.create(), - PROJECTION: mat4.create(), - WORLDVIEW: mat4.create(), - VIEWPROJECTION: mat4.create(), - WORLDVIEWPROJECTION: mat4.create(), - - WORLDINVERSE: mat4.create(), - VIEWINVERSE: mat4.create(), - PROJECTIONINVERSE: mat4.create(), - WORLDVIEWINVERSE: mat4.create(), - VIEWPROJECTIONINVERSE: mat4.create(), - WORLDVIEWPROJECTIONINVERSE: mat4.create(), - - WORLDTRANSPOSE: mat4.create(), - VIEWTRANSPOSE: mat4.create(), - PROJECTIONTRANSPOSE: mat4.create(), - WORLDVIEWTRANSPOSE: mat4.create(), - VIEWPROJECTIONTRANSPOSE: mat4.create(), - WORLDVIEWPROJECTIONTRANSPOSE: mat4.create(), - WORLDINVERSETRANSPOSE: mat4.create(), - VIEWINVERSETRANSPOSE: mat4.create(), - PROJECTIONINVERSETRANSPOSE: mat4.create(), - WORLDVIEWINVERSETRANSPOSE: mat4.create(), - VIEWPROJECTIONINVERSETRANSPOSE: mat4.create(), - WORLDVIEWPROJECTIONINVERSETRANSPOSE: mat4.create() - }; - - Renderer.COLOR_BUFFER_BIT = glenum.COLOR_BUFFER_BIT; - Renderer.DEPTH_BUFFER_BIT = glenum.DEPTH_BUFFER_BIT; - Renderer.STENCIL_BUFFER_BIT = glenum.STENCIL_BUFFER_BIT; - - return Renderer; -}); -define('qtek/Scene',['require','./Node','./Light'],function(require) { - - - - var Node = require('./Node'); - var Light = require('./Light'); - - /** - * @constructor qtek.Scene - * @extends qtek.Node - */ - var Scene = Node.derive(function() { - return /** @lends qtek.Scene# */ { - /** - * Global material of scene - * @type {Material} - */ - material: null, - - /** - * @type {boolean} - */ - autoUpdate: true, - - /** - * Opaque renderable list, it will be updated automatically - * @type {Renderable[]} - * @readonly - */ - opaqueQueue: [], - - /** - * Opaque renderable list, it will be updated automatically - * @type {Renderable[]} - * @readonly - */ - transparentQueue: [], - - lights: [], - - // Properties to save the light information in the scene - // Will be set in the render function - _lightUniforms: {}, - - _lightNumber: { - 'POINT_LIGHT': 0, - 'DIRECTIONAL_LIGHT': 0, - 'SPOT_LIGHT': 0, - 'AMBIENT_LIGHT': 0 - }, - - _opaqueObjectCount: 0, - _transparentObjectCount: 0, - - _nodeRepository: {} - }; - }, function() { - this._scene = this; - }, - /** @lends qtek.Scene.prototype. */ - { - /** - * Add node to scene - * @param {Node} node - */ - addToScene: function(node) { - if (node.name) { - this._nodeRepository[node.name] = node; - } - }, - - /** - * Remove node from scene - * @param {Node} node - */ - removeFromScene: function(node) { - if (node.name) { - delete this._nodeRepository[node.name]; - } - }, - - /** - * Get node by name - * @param {string} name - * @return {Node} - * @DEPRECATED - */ - getNode: function(name) { - return this._nodeRepository[name]; - }, - - /** - * Clone a new scene node recursively, including material, skeleton. - * Shader and geometry instances will not been cloned - * @param {qtek.Node} node - * @return {qtek.Node} - */ - cloneNode: function (node) { - var newNode = node.clone(); - var materialsMap = {}; - - var cloneSkeleton = function (current, currentNew) { - if (current.skeleton) { - currentNew.skeleton = current.skeleton.clone(node, newNode); - currentNew.joints = current.joints.slice(); - } - if (current.material) { - materialsMap[current.material.__GUID__] = { - oldMat: current.material - }; - } - for (var i = 0; i < current._children.length; i++) { - cloneSkeleton(current._children[i], currentNew._children[i]); - } - }; - - cloneSkeleton(node, newNode); - - for (var guid in materialsMap) { - materialsMap[guid].newMat = materialsMap[guid].oldMat.clone(); - } - - // Replace material - newNode.traverse(function (current) { - if (current.material) { - current.material = materialsMap[current.material.__GUID__].newMat; - } - }); - - return newNode; - }, - - - /** - * Scene update - * @param {boolean} force - * @param {boolean} notUpdateLights - * Useful in deferred pipeline - */ - update: function(force, notUpdateLights) { - if (!(this.autoUpdate || force)) { - return; - } - Node.prototype.update.call(this, force); - - var lights = this.lights; - var sceneMaterialTransparent = this.material && this.material.transparent; - - this._opaqueObjectCount = 0; - this._transparentObjectCount = 0; - - lights.length = 0; - - this._updateRenderQueue(this, sceneMaterialTransparent); - - this.opaqueQueue.length = this._opaqueObjectCount; - this.transparentQueue.length = this._transparentObjectCount; - - // reset - if (!notUpdateLights) { - for (var type in this._lightNumber) { - this._lightNumber[type] = 0; - } - for (var i = 0; i < lights.length; i++) { - var light = lights[i]; - this._lightNumber[light.type]++; - } - this._updateLightUniforms(); - } - }, - - // Traverse the scene and add the renderable - // object to the render queue - _updateRenderQueue: function(parent, sceneMaterialTransparent) { - if (!parent.visible) { - return; - } - - for (var i = 0; i < parent._children.length; i++) { - var child = parent._children[i]; - - if (child instanceof Light) { - this.lights.push(child); - } - if (child.isRenderable()) { - if (child.material.transparent || sceneMaterialTransparent) { - this.transparentQueue[this._transparentObjectCount++] = child; - } else { - this.opaqueQueue[this._opaqueObjectCount++] = child; - } - } - if (child._children.length > 0) { - this._updateRenderQueue(child); - } - } - }, - - _updateLightUniforms: function() { - var lights = this.lights; - // Put the light cast shadow before the light not cast shadow - lights.sort(lightSortFunc); - - var lightUniforms = this._lightUniforms; - for (var symbol in lightUniforms) { - lightUniforms[symbol].value.length = 0; - } - for (var i = 0; i < lights.length; i++) { - - var light = lights[i]; - - for (symbol in light.uniformTemplates) { - - var uniformTpl = light.uniformTemplates[symbol]; - if (! lightUniforms[symbol]) { - lightUniforms[symbol] = { - type: '', - value: [] - }; - } - var value = uniformTpl.value(light); - var lu = lightUniforms[symbol]; - lu.type = uniformTpl.type + 'v'; - switch (uniformTpl.type) { - case '1i': - case '1f': - lu.value.push(value); - break; - case '2f': - case '3f': - case '4f': - for (var j =0; j < value.length; j++) { - lu.value.push(value[j]); - } - break; - default: - console.error('Unkown light uniform type '+uniformTpl.type); - } - } - } - }, - - isShaderLightNumberChanged: function(shader) { - return shader.lightNumber.POINT_LIGHT !== this._lightNumber.POINT_LIGHT - || shader.lightNumber.DIRECTIONAL_LIGHT !== this._lightNumber.DIRECTIONAL_LIGHT - || shader.lightNumber.SPOT_LIGHT !== this._lightNumber.SPOT_LIGHT - || shader.lightNumber.AMBIENT_LIGHT !== this._lightNumber.AMBIENT_LIGHT; - }, - - setShaderLightNumber: function(shader) { - for (var type in this._lightNumber) { - shader.lightNumber[type] = this._lightNumber[type]; - } - shader.dirty(); - }, - - setLightUniforms: function(shader, _gl) { - for (var symbol in this._lightUniforms) { - var lu = this._lightUniforms[symbol]; - shader.setUniform(_gl, lu.type, symbol, lu.value); - } - }, - - /** - * Dispose self, clear all the scene objects - * But resources of gl like texuture, shader will not be disposed. - * Mostly you should use disposeScene method in Renderer to do dispose. - */ - dispose: function() { - this.material = null; - this.opaqueQueue = []; - this.transparentQueue = []; - - this.lights = []; - - this._lightUniforms = {}; - - this._lightNumber = {}; - this._nodeRepository = {}; - } - }); - - function lightSortFunc(a, b) { - if (b.castShadow && !a.castShadow) { - return true; - } - } - - return Scene; -}); -define('qtek/Skeleton',['require','./core/Base','./Joint','./dep/glmatrix'],function(require) { - - - - var Base = require('./core/Base'); - var Joint = require('./Joint'); - - var glMatrix = require('./dep/glmatrix'); - var quat = glMatrix.quat; - var vec3 = glMatrix.vec3; - var mat4 = glMatrix.mat4; - - /** - * @constructor qtek.Skeleton - */ - var Skeleton = Base.derive(function() { - return /** @lends qtek.Skeleton# */{ - /** - * @type {string} - */ - name: '', - - /** - * root joints - * @type {Array.} - */ - roots: [], - - /** - * joints - * @type {Array.} - */ - joints: [], - - _clips: [], - - // Matrix to joint space (relative to root joint) - _invBindPoseMatricesArray: null, - - // Use subarray instead of copy back each time computing matrix - // http://jsperf.com/subarray-vs-copy-for-array-transform/5 - _jointMatricesSubArrays: [], - - // jointMatrix * currentPoseMatrix - // worldTransform is relative to the root bone - // still in model space not world space - _skinMatricesArray: null, - - _skinMatricesSubArrays: [], - - _subSkinMatricesArray: {} - }; - }, - /** @lends qtek.Skeleton.prototype */ - { - /** - * Update joints hierarchy - */ - updateHierarchy: function() { - this.roots = []; - var joints = this.joints; - for (var i = 0; i < joints.length; i++) { - var joint = joints[i]; - if (joint.parentIndex >= 0) { - var parent = joints[joint.parentIndex].node; - parent.add(joint.node); - }else{ - this.roots.push(joint); - } - } - }, - - /** - * Add a skinning clip and create a map between clip and skeleton - * @param {qtek.animation.SkinningClip} clip - * @param {Object} [mapRule] Map between joint name in skeleton and joint name in clip - */ - addClip: function(clip, mapRule) { - // Clip have been exists in - for (var i = 0; i < this._clips.length; i++) { - if (this._clips[i].clip === clip) { - return; - } - } - // Map the joint index in skeleton to joint pose index in clip - var maps = []; - for (var i = 0; i < this.joints.length; i++) { - maps[i] = -1; - } - // Create avatar - for (var i = 0; i < clip.jointClips.length; i++) { - for (var j = 0; j < this.joints.length; j++) { - var joint = this.joints[j]; - var jointPose = clip.jointClips[i]; - var jointName = joint.name; - if (mapRule) { - jointName = mapRule[jointName]; - } - if (jointPose.name === jointName) { - maps[j] = i; - break; - } - } - } - - this._clips.push({ - maps: maps, - clip: clip - }); - - return this._clips.length - 1; - }, - - /** - * @param {qtek.animation.SkinningClip} clip - */ - removeClip: function(clip) { - var idx = -1; - for (var i = 0; i < this._clips.length; i++) { - if (this._clips[i].clip === clip) { - idx = i; - break; - } - } - if (idx > 0) { - this._clips.splice(idx, 1); - } - }, - /** - * Remove all clips - */ - removeClipsAll: function() { - this._clips = []; - }, - - /** - * Get clip by index - * @param {number} index - */ - getClip: function(index) { - if (this._clips[index]) { - return this._clips[index].clip; - } - }, - - /** - * @return {number} - */ - getClipNumber: function() { - return this._clips.length; - }, - - /** - * Calculate joint matrices from node transform - * @method - */ - updateJointMatrices: (function() { - - var m4 = mat4.create(); - - return function() { - for (var i = 0; i < this.roots.length; i++) { - this.roots[i].node.update(true); - } - this._invBindPoseMatricesArray = new Float32Array(this.joints.length * 16); - this._skinMatricesArray = new Float32Array(this.joints.length * 16); - - for (var i = 0; i < this.joints.length; i++) { - var joint = this.joints[i]; - // Joint space is relative to root joint's parent, if have - // !!Parent node and joint node must all be updated - if (joint.rootNode && joint.rootNode.getParent()) { - mat4.invert(m4, joint.rootNode.getParent().worldTransform._array); - mat4.multiply( - m4, - m4, - joint.node.worldTransform._array - ); - mat4.invert(m4, m4); - } else { - mat4.copy(m4, joint.node.worldTransform._array); - mat4.invert(m4, m4); - } - - var offset = i * 16; - for (var j = 0; j < 16; j++) { - this._invBindPoseMatricesArray[offset + j] = m4[j]; - } - } - - this.updateMatricesSubArrays(); - }; - })(), - - updateMatricesSubArrays: function() { - for (var i = 0; i < this.joints.length; i++) { - this._jointMatricesSubArrays[i] = this._invBindPoseMatricesArray.subarray(i * 16, (i+1) * 16); - this._skinMatricesSubArrays[i] = this._skinMatricesArray.subarray(i * 16, (i+1) * 16); - } - }, - - /** - * Update skinning matrices - */ - update: (function() { - var m4 = mat4.create(); - return function() { - for (var i = 0; i < this.roots.length; i++) { - this.roots[i].node.update(true); - } - - for (var i = 0; i < this.joints.length; i++) { - var joint = this.joints[i]; - mat4.multiply( - this._skinMatricesSubArrays[i], - joint.node.worldTransform._array, - this._jointMatricesSubArrays[i] - ); - - // Joint space is relative to root joint's parent, if have - // PENDING - if (joint.rootNode && joint.rootNode.getParent()) { - mat4.invert(m4, joint.rootNode.getParent().worldTransform._array); - mat4.multiply( - this._skinMatricesSubArrays[i], - m4, - this._skinMatricesSubArrays[i] - ); - } - } - }; - })(), - - getSubSkinMatrices: function(meshId, joints) { - var subArray = this._subSkinMatricesArray[meshId]; - if (!subArray) { - subArray - = this._subSkinMatricesArray[meshId] - = new Float32Array(joints.length * 16); - } - var cursor = 0; - for (var i = 0; i < joints.length; i++) { - var idx = joints[i]; - for (var j = 0; j < 16; j++) { - subArray[cursor++] = this._skinMatricesArray[idx * 16 + j]; - } - } - return subArray; - }, - - /** - * Set pose and update skinning matrices - * @param {number} clipIndex - */ - setPose: function(clipIndex) { - if (!this._clips[clipIndex]) { - return; - } - - var clip = this._clips[clipIndex].clip; - var maps = this._clips[clipIndex].maps; - - for (var i = 0; i < this.joints.length; i++) { - var joint = this.joints[i]; - if (maps[i] === -1) { - continue; - } - var pose = clip.jointClips[maps[i]]; - - vec3.copy(joint.node.position._array, pose.position); - quat.copy(joint.node.rotation._array, pose.rotation); - vec3.copy(joint.node.scale._array, pose.scale); - - joint.node.position._dirty = true; - joint.node.rotation._dirty = true; - joint.node.scale._dirty = true; - } - this.update(); - }, - - clone: function (rootNode, newRootNode) { - var skeleton = new Skeleton(); - skeleton.name = this.name; - - for (var i = 0; i < this.joints.length; i++) { - var newJoint = new Joint(); - newJoint.name = this.joints[i].name; - newJoint.index = this.joints[i].index; - newJoint.parentIndex = this.joints[i].parentIndex; - - var path = this.joints[i].node.getPath(rootNode); - var rootNodePath = this.joints[i].rootNode.getPath(rootNode); - - if (path != null && rootNodePath != null) { - newJoint.node = newRootNode.queryNode(path); - newJoint.rootNode = newRootNode.queryNode(rootNodePath); - } else { - // PENDING - console.warn('Something wrong in clone, may be the skeleton root nodes is not mounted on the cloned root node.') - } - skeleton.joints.push(newJoint); - } - for (var i = 0; i < this.roots.length; i++) { - skeleton.roots.push(skeleton.joints[this.roots[i].index]); - } - - if (this._invBindPoseMatricesArray) { - var len = this._invBindPoseMatricesArray.length; - skeleton._invBindPoseMatricesArray = new Float32Array(len); - for (var i = 0; i < len; i++) { - skeleton._invBindPoseMatricesArray[i] = this._invBindPoseMatricesArray[i]; - } - - skeleton._skinMatricesArray = new Float32Array(len); - - skeleton.updateMatricesSubArrays(); - } - - skeleton.update(); - - return skeleton; - } - }); - - return Skeleton; -}); -/** - * StaticGeometry can not be changed once they've been setup - */ -define('qtek/StaticGeometry',['require','./Geometry','./math/BoundingBox','./dep/glmatrix','./core/glenum'],function(require) { - - - - var Geometry = require('./Geometry'); - var BoundingBox = require('./math/BoundingBox'); - var glMatrix = require('./dep/glmatrix'); - var glenum = require('./core/glenum'); - var mat4 = glMatrix.mat4; - var vec3 = glMatrix.vec3; - - /** - * @constructor qtek.StaticGeometry - * @extends qtek.Geometry - */ - var StaticGeometry = Geometry.derive(function() { - return /** @lends qtek.StaticGeometry# */ { - attributes: { - position: new Geometry.Attribute('position', 'float', 3, 'POSITION', false), - texcoord0: new Geometry.Attribute('texcoord0', 'float', 2, 'TEXCOORD_0', false), - texcoord1: new Geometry.Attribute('texcoord1', 'float', 2, 'TEXCOORD_1', false), - normal: new Geometry.Attribute('normal', 'float', 3, 'NORMAL', false), - tangent: new Geometry.Attribute('tangent', 'float', 4, 'TANGENT', false), - color: new Geometry.Attribute('color', 'float', 4, 'COLOR', false), - // Skinning attributes - // Each vertex can be bind to 4 bones, because the - // sum of weights is 1, so the weights is stored in vec3 and the last - // can be calculated by 1-w.x-w.y-w.z - weight: new Geometry.Attribute('weight', 'float', 3, 'WEIGHT', false), - joint: new Geometry.Attribute('joint', 'float', 4, 'JOINT', false), - // For wireframe display - // http://codeflow.org/entries/2012/aug/02/easy-wireframe-display-with-barycentric-coordinates/ - barycentric: new Geometry.Attribute('barycentric', 'float', 3, null, false), - }, - - hint: glenum.STATIC_DRAW, - - /** - * @type {Uint16Array} - */ - faces: null, - - _normalType: 'vertex', - - _enabledAttributes: null - }; - }, - /** @lends qtek.StaticGeometry.prototype */ - { - dirty: function() { - this._cache.dirtyAll(); - this._enabledAttributes = null; - }, - - getVertexNumber: function() { - var mainAttribute = this.attributes[this.mainAttribute]; - if (!mainAttribute || !mainAttribute.value) { - return 0; - } - return mainAttribute.value.length / mainAttribute.size; - }, - - getFaceNumber: function() { - if (!this.faces) { - return 0; - } else { - return this.faces.length / 3; - } - }, - - getFace: function (idx, out) { - if (idx < this.getFaceNumber() && idx >= 0) { - if (!out) { - out = vec3.create(); - } - out[0] = this.faces[idx * 3]; - out[1] = this.faces[idx * 3 + 1]; - out[2] = this.faces[idx * 3 + 2]; - return out; - } - }, - - isUseFace: function() { - return this.useFace && (this.faces != null); - }, - - createAttribute: function(name, type, size, semantic) { - var attrib = new Geometry.Attribute(name, type, size, semantic, false); - this.attributes[name] = attrib; - this._attributeList.push(name); - return attrib; - }, - - removeAttribute: function(name) { - var idx = this._attributeList.indexOf(name); - if (idx >= 0) { - this._attributeList.splice(idx, 1); - delete this.attributes[name]; - return true; - } - return false; - }, - - /** - * Get enabled attributes name list - * Attribute which has the same vertex number with position is treated as a enabled attribute - * @return {string[]} - */ - getEnabledAttributes: function() { - // Cache - if (this._enabledAttributes) { - return this._enabledAttributes; - } - - var result = []; - var nVertex = this.getVertexNumber(); - - for (var i = 0; i < this._attributeList.length; i++) { - var name = this._attributeList[i]; - var attrib = this.attributes[name]; - if (attrib.value) { - if (attrib.value.length === nVertex * attrib.size) { - result.push(name); - } - } - } - - this._enabledAttributes = result; - - return result; - }, - - getBufferChunks: function(_gl) { - this._cache.use(_gl.__GLID__); - if (this._cache.isDirty()) { - this._updateBuffer(_gl); - this._cache.fresh(); - } - return this._cache.get('chunks'); - }, - - _updateBuffer: function(_gl) { - var chunks = this._cache.get('chunks'); - var firstUpdate = false; - if (! chunks) { - chunks = []; - // Intialize - chunks[0] = { - attributeBuffers: [], - indicesBuffer: null - }; - this._cache.put('chunks', chunks); - firstUpdate = true; - } - var chunk = chunks[0]; - var attributeBuffers = chunk.attributeBuffers; - var indicesBuffer = chunk.indicesBuffer; - - var attributeList = this.getEnabledAttributes(); - var prevSearchIdx = 0; - var count = 0; - for (var k = 0; k < attributeList.length; k++) { - var name = attributeList[k]; - var attribute = this.attributes[name]; - - var bufferInfo; - - if (!firstUpdate) { - // Search for created buffer - for (var i = prevSearchIdx; i < attributeBuffers.length; i++) { - if (attributeBuffers[i].name === name) { - bufferInfo = attributeBuffers[i]; - prevSearchIdx = i + 1; - break; - } - } - if (!bufferInfo) { - for (var i = prevSearchIdx - 1; i >= 0; i--) { - if (attributeBuffers[i].name === name) { - bufferInfo = attributeBuffers[i]; - prevSearchIdx = i; - break; - } - } - } - } - var buffer; - if (bufferInfo) { - buffer = bufferInfo.buffer; - } else { - buffer = _gl.createBuffer(); - } - //TODO: Use BufferSubData? - _gl.bindBuffer(_gl.ARRAY_BUFFER, buffer); - _gl.bufferData(_gl.ARRAY_BUFFER, attribute.value, this.hint); - - attributeBuffers[count++] = new Geometry.AttributeBuffer(name, attribute.type, buffer, attribute.size, attribute.semantic); - } - attributeBuffers.length = count; - - if (this.isUseFace()) { - if (!indicesBuffer) { - indicesBuffer = new Geometry.IndicesBuffer(_gl.createBuffer()); - chunk.indicesBuffer = indicesBuffer; - } - indicesBuffer.count = this.faces.length; - _gl.bindBuffer(_gl.ELEMENT_ARRAY_BUFFER, indicesBuffer.buffer); - _gl.bufferData(_gl.ELEMENT_ARRAY_BUFFER, this.faces, this.hint); - } - }, - - generateVertexNormals: function() { - var faces = this.faces; - var positions = this.attributes.position.value; - var normals = this.attributes.normal.value; - - if (!normals || normals.length !== positions.length) { - normals = this.attributes.normal.value = new Float32Array(positions.length); - } else { - // Reset - for (var i = 0; i < normals.length; i++) { - normals[i] = 0; - } - } - - var p1 = vec3.create(); - var p2 = vec3.create(); - var p3 = vec3.create(); - - var v21 = vec3.create(); - var v32 = vec3.create(); - - var n = vec3.create(); - - for (var f = 0; f < faces.length;) { - var i1 = faces[f++]; - var i2 = faces[f++]; - var i3 = faces[f++]; - - vec3.set(p1, positions[i1*3], positions[i1*3+1], positions[i1*3+2]); - vec3.set(p2, positions[i2*3], positions[i2*3+1], positions[i2*3+2]); - vec3.set(p3, positions[i3*3], positions[i3*3+1], positions[i3*3+2]); - - vec3.sub(v21, p1, p2); - vec3.sub(v32, p2, p3); - vec3.cross(n, v21, v32); - // Weighted by the triangle area - for (var i = 0; i < 3; i++) { - normals[i1*3+i] = normals[i1*3+i] + n[i]; - normals[i2*3+i] = normals[i2*3+i] + n[i]; - normals[i3*3+i] = normals[i3*3+i] + n[i]; - } - } - - for (var i = 0; i < normals.length;) { - vec3.set(n, normals[i], normals[i+1], normals[i+2]); - vec3.normalize(n, n); - normals[i++] = n[0]; - normals[i++] = n[1]; - normals[i++] = n[2]; - } - }, - - generateFaceNormals: function() { - if (!this.isUniqueVertex()) { - this.generateUniqueVertex(); - } - - var faces = this.faces; - var positions = this.attributes.position.value; - var normals = this.attributes.normal.value; - - var p1 = vec3.create(); - var p2 = vec3.create(); - var p3 = vec3.create(); - - var v21 = vec3.create(); - var v32 = vec3.create(); - var n = vec3.create(); - - if (!normals) { - normals = this.attributes.position.value = new Float32Array(positions.length); - } - for (var f = 0; f < faces.length;) { - var i1 = faces[f++]; - var i2 = faces[f++]; - var i3 = faces[f++]; - - vec3.set(p1, positions[i1*3], positions[i1*3+1], positions[i1*3+2]); - vec3.set(p2, positions[i2*3], positions[i2*3+1], positions[i2*3+2]); - vec3.set(p3, positions[i3*3], positions[i3*3+1], positions[i3*3+2]); - - vec3.sub(v21, p1, p2); - vec3.sub(v32, p2, p3); - vec3.cross(n, v21, v32); - - vec3.normalize(n, n); - - for (var i = 0; i < 3; i++) { - normals[i1*3+i] = n[i]; - normals[i2*3+i] = n[i]; - normals[i3*3+i] = n[i]; - } - } - }, - - generateTangents: function() { - var nVertex = this.getVertexNumber(); - if (!this.attributes.tangent.value) { - this.attributes.tangent.value = new Float32Array(nVertex * 4); - } - var texcoords = this.attributes.texcoord0.value; - var positions = this.attributes.position.value; - var tangents = this.attributes.tangent.value; - var normals = this.attributes.normal.value; - - var tan1 = []; - var tan2 = []; - for (var i = 0; i < nVertex; i++) { - tan1[i] = [0.0, 0.0, 0.0]; - tan2[i] = [0.0, 0.0, 0.0]; - } - - var sdir = [0.0, 0.0, 0.0]; - var tdir = [0.0, 0.0, 0.0]; - for (var i = 0; i < this.faces.length;) { - var i1 = this.faces[i++], - i2 = this.faces[i++], - i3 = this.faces[i++], - - st1s = texcoords[i1 * 2], - st2s = texcoords[i2 * 2], - st3s = texcoords[i3 * 2], - st1t = texcoords[i1 * 2 + 1], - st2t = texcoords[i2 * 2 + 1], - st3t = texcoords[i3 * 2 + 1], - - p1x = positions[i1 * 3], - p2x = positions[i2 * 3], - p3x = positions[i3 * 3], - p1y = positions[i1 * 3 + 1], - p2y = positions[i2 * 3 + 1], - p3y = positions[i3 * 3 + 1], - p1z = positions[i1 * 3 + 2], - p2z = positions[i2 * 3 + 2], - p3z = positions[i3 * 3 + 2]; - - var x1 = p2x - p1x, - x2 = p3x - p1x, - y1 = p2y - p1y, - y2 = p3y - p1y, - z1 = p2z - p1z, - z2 = p3z - p1z; - - var s1 = st2s - st1s, - s2 = st3s - st1s, - t1 = st2t - st1t, - t2 = st3t - st1t; - - var r = 1.0 / (s1 * t2 - t1 * s2); - sdir[0] = (t2 * x1 - t1 * x2) * r; - sdir[1] = (t2 * y1 - t1 * y2) * r; - sdir[2] = (t2 * z1 - t1 * z2) * r; - - tdir[0] = (s1 * x2 - s2 * x1) * r; - tdir[1] = (s1 * y2 - s2 * y1) * r; - tdir[2] = (s1 * z2 - s2 * z1) * r; - - vec3.add(tan1[i1], tan1[i1], sdir); - vec3.add(tan1[i2], tan1[i2], sdir); - vec3.add(tan1[i3], tan1[i3], sdir); - vec3.add(tan2[i1], tan2[i1], tdir); - vec3.add(tan2[i2], tan2[i2], tdir); - vec3.add(tan2[i3], tan2[i3], tdir); - } - var tmp = vec3.create(); - var nCrossT = vec3.create(); - var n = vec3.create(); - for (var i = 0; i < nVertex; i++) { - n[0] = normals[i * 3]; - n[1] = normals[i * 3 + 1]; - n[2] = normals[i * 3 + 2]; - var t = tan1[i]; - - // Gram-Schmidt orthogonalize - vec3.scale(tmp, n, vec3.dot(n, t)); - vec3.sub(tmp, t, tmp); - vec3.normalize(tmp, tmp); - // Calculate handedness. - vec3.cross(nCrossT, n, t); - tangents[i * 4] = tmp[0]; - tangents[i * 4 + 1] = tmp[1]; - tangents[i * 4 + 2] = tmp[2]; - tangents[i * 4 + 3] = vec3.dot(nCrossT, tan2[i]) < 0.0 ? -1.0 : 1.0; - } - }, - - isUniqueVertex: function() { - if (this.isUseFace()) { - return this.getVertexNumber() === this.faces.length; - } else { - return true; - } - }, - - generateUniqueVertex: function() { - var vertexUseCount = []; - - for (var i = 0, len = this.getVertexNumber(); i < len; i++) { - vertexUseCount[i] = 0; - } - - var cursor = this.getVertexNumber(); - var attributes = this.attributes; - var faces = this.faces; - - var attributeNameList = this.getEnabledAttributes(); - - for (var a = 0; a < attributeNameList.length; a++) { - var name = attributeNameList[a]; - var expandedArray = new Float32Array(this.faces.length * attributes[name].size); - var len = attributes[name].value.length; - for (var i = 0; i < len; i++) { - expandedArray[i] = attributes[name].value[i]; - } - attributes[name].value = expandedArray; - } - - for (var i = 0; i < faces.length; i++) { - var ii = faces[i]; - if (vertexUseCount[ii] > 0) { - for (var a = 0; a < attributeNameList.length; a++) { - var name = attributeNameList[a]; - var array = attributes[name].value; - var size = attributes[name].size; - - for (var k = 0; k < size; k++) { - array[cursor * size + k] = array[ii * size + k]; - } - } - faces[i] = cursor; - cursor++; - } - vertexUseCount[ii]++; - } - }, - - generateBarycentric: function() { - - if (! this.isUniqueVertex()) { - this.generateUniqueVertex(); - } - - var array = this.attributes.barycentric.value; - // Already existed; - if (array && array.length === this.faces.length * 3) { - return; - } - array = this.attributes.barycentric.value = new Float32Array(this.faces.length * 3); - for (var i = 0; i < this.faces.length;) { - for (var j = 0; j < 3; j++) { - var ii = this.faces[i++]; - array[ii + j] = 1; - } - } - }, - - convertToDynamic: function(geometry) { - for (var i = 0; i < this.faces.length; i+=3) { - geometry.faces.push(this.face.subarray(i, i + 3)); - } - - var attributes = this.getEnabledAttributes(); - for (var name in attributes) { - var attrib = attributes[name]; - var geoAttrib = geometry.attributes[name]; - if (!geoAttrib) { - geoAttrib = geometry.attributes[name] = { - type: attrib.type, - size: attrib.size, - value: [] - }; - if (attrib.semantic) { - geoAttrib.semantic = attrib.semantic; - } - } - for (var i = 0; i < attrib.value.length; i+= attrib.size) { - if (attrib.size === 1) { - geoAttrib.value.push(attrib.array[i]); - } else { - geoAttrib.value.push(attrib.subarray(i, i + attrib.size)); - } - } - } - - if (this.boundingBox) { - geometry.boundingBox = new BoundingBox(); - geometry.boundingBox.min.copy(this.boundingBox.min); - geometry.boundingBox.max.copy(this.boundingBox.max); - } - // PENDING copy buffer ? - - return geometry; - }, - - applyTransform: function(matrix) { - - var positions = this.attributes.position.value; - var normals = this.attributes.normal.value; - var tangents = this.attributes.tangent.value; - - matrix = matrix._array; - // Normal Matrix - var inverseTransposeMatrix = mat4.create(); - mat4.invert(inverseTransposeMatrix, matrix); - mat4.transpose(inverseTransposeMatrix, inverseTransposeMatrix); - - vec3.forEach(positions, 3, 0, null, vec3.transformMat4, matrix); - if (normals) { - vec3.forEach(normals, 3, 0, null, vec3.transformMat4, inverseTransposeMatrix); - } - if (tangents) { - vec3.forEach(tangents, 4, 0, null, vec3.transformMat4, inverseTransposeMatrix); - } - - if (this.boundingBox) { - this.updateBoundingBox(); - } - }, - - dispose: function(_gl) { - this._cache.use(_gl.__GLID__); - var chunks = this._cache.get('chunks'); - if (chunks) { - for (var c = 0; c < chunks.length; c++) { - var chunk = chunks[c]; - - for (var k = 0; k < chunk.attributeBuffers.length; k++) { - var attribs = chunk.attributeBuffers[k]; - _gl.deleteBuffer(attribs.buffer); - } - } - } - this._cache.deleteContext(_gl.__GLID__); - } - }); - - return StaticGeometry; -}); -define('qtek/Texture2D',['require','./Texture','./core/glinfo','./core/glenum'],function(require) { - - var Texture = require('./Texture'); - var glinfo = require('./core/glinfo'); - var glenum = require('./core/glenum'); - - /** - * @constructor qtek.Texture2D - * @extends qtek.Texture - * - * @example - * ... - * var mat = new qtek.Material({ - * shader: qtek.shader.library.get('buildin.phong', 'diffuseMap') - * }); - * var diffuseMap = new qtek.Texture2D(); - * diffuseMap.load('assets/textures/diffuse.jpg'); - * mat.set('diffuseMap', diffuseMap); - * ... - * diffuseMap.success(function() { - * // Wait for the diffuse texture loaded - * animation.on('frame', function(frameTime) { - * renderer.render(scene, camera); - * }); - * }); - */ - var Texture2D = Texture.derive(function() { - return /** @lends qtek.Texture2D# */ { - /** - * @type {HTMLImageElement|HTMLCanvasElemnet} - */ - image: null, - /** - * @type {Uint8Array|Float32Array} - */ - pixels: null, - /** - * @type {Array.} - * @example - * [{ - * image: mipmap0, - * pixels: null - * }, { - * image: mipmap1, - * pixels: null - * }, ....] - */ - mipmaps: [] - }; - }, { - update: function(_gl) { - - _gl.bindTexture(_gl.TEXTURE_2D, this._cache.get('webgl_texture')); - - this.beforeUpdate( _gl); - - var glFormat = this.format; - var glType = this.type; - - _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, this.wrapS); - _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, this.wrapT); - - _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, this.magFilter); - _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, this.minFilter); - - var anisotropicExt = glinfo.getExtension(_gl, 'EXT_texture_filter_anisotropic'); - if (anisotropicExt && this.anisotropic > 1) { - _gl.texParameterf(_gl.TEXTURE_2D, anisotropicExt.TEXTURE_MAX_ANISOTROPY_EXT, this.anisotropic); - } - - // Fallback to float type if browser don't have half float extension - if (glType === 36193) { - var halfFloatExt = glinfo.getExtension(_gl, 'OES_texture_half_float'); - if (!halfFloatExt) { - glType = glenum.FLOAT; - } - } - - if (this.mipmaps.length) { - var width = this.width; - var height = this.height; - for (var i = 0; i < this.mipmaps.length; i++) { - var mipmap = this.mipmaps[i]; - this._updateTextureData(_gl, mipmap, i, width, height, glFormat, glType); - width /= 2; - height /= 2; - } - } - else { - this._updateTextureData(_gl, this, 0, this.width, this.height, glFormat, glType); - - if (this.useMipmap && !this.NPOT) { - _gl.generateMipmap(_gl.TEXTURE_2D); - } - } - - _gl.bindTexture(_gl.TEXTURE_2D, null); - }, - - _updateTextureData: function (_gl, data, level, width, height, glFormat, glType) { - if (data.image) { - _gl.texImage2D(_gl.TEXTURE_2D, level, glFormat, glFormat, glType, data.image); - } - else { - // Can be used as a blank texture when writing render to texture(RTT) - if ( - glFormat <= Texture.COMPRESSED_RGBA_S3TC_DXT5_EXT - && glFormat >= Texture.COMPRESSED_RGB_S3TC_DXT1_EXT - ) { - _gl.compressedTexImage2D(_gl.TEXTURE_2D, level, glFormat, width, height, 0, data.pixels); - } else { - _gl.texImage2D(_gl.TEXTURE_2D, level, glFormat, width, height, 0, glFormat, glType, data.pixels); - } - } - }, - - /** - * @param {WebGLRenderingContext} _gl - * @memberOf qtek.Texture2D.prototype - */ - generateMipmap: function(_gl) { - if (this.useMipmap && !this.NPOT) { - _gl.bindTexture(_gl.TEXTURE_2D, this._cache.get('webgl_texture')); - _gl.generateMipmap(_gl.TEXTURE_2D); - } - }, - - isPowerOfTwo: function() { - var width; - var height; - if (this.image) { - width = this.image.width; - height = this.image.height; - } else { - width = this.width; - height = this.height; - } - return (width & (width-1)) === 0 - && (height & (height-1)) === 0; - }, - - isRenderable: function() { - if (this.image) { - return this.image.nodeName === 'CANVAS' - || this.image.complete; - } else { - return this.width && this.height; - } - }, - - bind: function(_gl) { - _gl.bindTexture(_gl.TEXTURE_2D, this.getWebGLTexture(_gl)); - }, - - unbind: function(_gl) { - _gl.bindTexture(_gl.TEXTURE_2D, null); - }, - - load: function(src) { - var image = new Image(); - var self = this; - image.onload = function() { - self.dirty(); - self.trigger('success', self); - image.onload = null; - }; - image.onerror = function() { - self.trigger('error', self); - image.onerror = null; - }; - - image.src = src; - this.image = image; - - return this; - } - }); - - return Texture2D; -}); -// 缓动函数来自 https://github.com/sole/tween.js/blob/master/src/Tween.js -define('qtek/animation/easing',[],function() { - - /** - * @namespace qtek.animation.easing - */ - var easing = { - /** - * @alias qtek.animation.easing.Linear - * @param {number} k - * @return {number} - */ - Linear: function(k) { - return k; - }, - /** - * @alias qtek.animation.easing.QuadraticIn - * @param {number} k - * @return {number} - */ - QuadraticIn: function(k) { - return k * k; - }, - /** - * @alias qtek.animation.easing.QuadraticOut - * @param {number} k - * @return {number} - */ - QuadraticOut: function(k) { - return k * (2 - k); - }, - /** - * @alias qtek.animation.easing.QuadraticInOut - * @param {number} k - * @return {number} - */ - QuadraticInOut: function(k) { - if ((k *= 2) < 1) { - return 0.5 * k * k; - } - return - 0.5 * (--k * (k - 2) - 1); - }, - /** - * @alias qtek.animation.easing.CubicIn - * @param {number} k - * @return {number} - */ - CubicIn: function(k) { - return k * k * k; - }, - /** - * @alias qtek.animation.easing.CubicOut - * @param {number} k - * @return {number} - */ - CubicOut: function(k) { - return --k * k * k + 1; - }, - /** - * @alias qtek.animation.easing.CubicInOut - * @param {number} k - * @return {number} - */ - CubicInOut: function(k) { - if ((k *= 2) < 1) { - return 0.5 * k * k * k; - } - return 0.5 * ((k -= 2) * k * k + 2); - }, - /** - * @alias qtek.animation.easing.QuarticIn - * @param {number} k - * @return {number} - */ - QuarticIn: function(k) { - return k * k * k * k; - }, - /** - * @alias qtek.animation.easing.QuarticOut - * @param {number} k - * @return {number} - */ - QuarticOut: function(k) { - return 1 - (--k * k * k * k); - }, - /** - * @alias qtek.animation.easing.QuarticInOut - * @param {number} k - * @return {number} - */ - QuarticInOut: function(k) { - if ((k *= 2) < 1) { - return 0.5 * k * k * k * k; - } - return - 0.5 * ((k -= 2) * k * k * k - 2); - }, - /** - * @alias qtek.animation.easing.QuinticIn - * @param {number} k - * @return {number} - */ - QuinticIn: function(k) { - return k * k * k * k * k; - }, - /** - * @alias qtek.animation.easing.QuinticOut - * @param {number} k - * @return {number} - */ - QuinticOut: function(k) { - return --k * k * k * k * k + 1; - }, - /** - * @alias qtek.animation.easing.QuinticInOut - * @param {number} k - * @return {number} - */ - QuinticInOut: function(k) { - if ((k *= 2) < 1) { - return 0.5 * k * k * k * k * k; - } - return 0.5 * ((k -= 2) * k * k * k * k + 2); - }, - /** - * @alias qtek.animation.easing.SinusoidalIn - * @param {number} k - * @return {number} - */ - SinusoidalIn: function(k) { - return 1 - Math.cos(k * Math.PI / 2); - }, - /** - * @alias qtek.animation.easing.SinusoidalOut - * @param {number} k - * @return {number} - */ - SinusoidalOut: function(k) { - return Math.sin(k * Math.PI / 2); - }, - /** - * @alias qtek.animation.easing.SinusoidalInOut - * @param {number} k - * @return {number} - */ - SinusoidalInOut: function(k) { - return 0.5 * (1 - Math.cos(Math.PI * k)); - }, - /** - * @alias qtek.animation.easing.ExponentialIn - * @param {number} k - * @return {number} - */ - ExponentialIn: function(k) { - return k === 0 ? 0 : Math.pow(1024, k - 1); - }, - /** - * @alias qtek.animation.easing.ExponentialOut - * @param {number} k - * @return {number} - */ - ExponentialOut: function(k) { - return k === 1 ? 1 : 1 - Math.pow(2, - 10 * k); - }, - /** - * @alias qtek.animation.easing.ExponentialInOut - * @param {number} k - * @return {number} - */ - ExponentialInOut: function(k) { - if (k === 0) { - return 0; - } - if (k === 1) { - return 1; - } - if ((k *= 2) < 1) { - return 0.5 * Math.pow(1024, k - 1); - } - return 0.5 * (- Math.pow(2, - 10 * (k - 1)) + 2); - }, - /** - * @alias qtek.animation.easing.CircularIn - * @param {number} k - * @return {number} - */ - CircularIn: function(k) { - return 1 - Math.sqrt(1 - k * k); - }, - /** - * @alias qtek.animation.easing.CircularOut - * @param {number} k - * @return {number} - */ - CircularOut: function(k) { - return Math.sqrt(1 - (--k * k)); - }, - /** - * @alias qtek.animation.easing.CircularInOut - * @param {number} k - * @return {number} - */ - CircularInOut: function(k) { - if ((k *= 2) < 1) { - return - 0.5 * (Math.sqrt(1 - k * k) - 1); - } - return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1); - }, - /** - * @alias qtek.animation.easing.ElasticIn - * @param {number} k - * @return {number} - */ - ElasticIn: function(k) { - var s, a = 0.1, p = 0.4; - if (k === 0) { - return 0; - } - if (k === 1) { - return 1; - } - if (!a || a < 1) { - a = 1; s = p / 4; - }else{ - s = p * Math.asin(1 / a) / (2 * Math.PI); - } - return - (a * Math.pow(2, 10 * (k -= 1)) * - Math.sin((k - s) * (2 * Math.PI) / p)); - }, - /** - * @alias qtek.animation.easing.ElasticOut - * @param {number} k - * @return {number} - */ - ElasticOut: function(k) { - var s, a = 0.1, p = 0.4; - if (k === 0) { - return 0; - } - if (k === 1) { - return 1; - } - if (!a || a < 1) { - a = 1; s = p / 4; - } - else{ - s = p * Math.asin(1 / a) / (2 * Math.PI); - } - return (a * Math.pow(2, - 10 * k) * - Math.sin((k - s) * (2 * Math.PI) / p) + 1); - }, - /** - * @alias qtek.animation.easing.ElasticInOut - * @param {number} k - * @return {number} - */ - ElasticInOut: function(k) { - var s, a = 0.1, p = 0.4; - if (k === 0) { - return 0; - } - if (k === 1) { - return 1; - } - if (!a || a < 1) { - a = 1; s = p / 4; - } - else{ - s = p * Math.asin(1 / a) / (2 * Math.PI); - } - if ((k *= 2) < 1) { - return - 0.5 * (a * Math.pow(2, 10 * (k -= 1)) - * Math.sin((k - s) * (2 * Math.PI) / p)); - } - return a * Math.pow(2, -10 * (k -= 1)) - * Math.sin((k - s) * (2 * Math.PI) / p) * 0.5 + 1; - - }, - /** - * @alias qtek.animation.easing.BackIn - * @param {number} k - * @return {number} - */ - BackIn: function(k) { - var s = 1.70158; - return k * k * ((s + 1) * k - s); - }, - /** - * @alias qtek.animation.easing.BackOut - * @param {number} k - * @return {number} - */ - BackOut: function(k) { - var s = 1.70158; - return --k * k * ((s + 1) * k + s) + 1; - }, - /** - * @alias qtek.animation.easing.BackInOut - * @param {number} k - * @return {number} - */ - BackInOut: function(k) { - var s = 1.70158 * 1.525; - if ((k *= 2) < 1) { - return 0.5 * (k * k * ((s + 1) * k - s)); - } - return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2); - }, - /** - * @alias qtek.animation.easing.BounceIn - * @param {number} k - * @return {number} - */ - BounceIn: function(k) { - return 1 - easing.BounceOut(1 - k); - }, - /** - * @alias qtek.animation.easing.BounceOut - * @param {number} k - * @return {number} - */ - BounceOut: function(k) { - if (k < (1 / 2.75)) { - return 7.5625 * k * k; - } - else if (k < (2 / 2.75)) { - return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75; - } else if (k < (2.5 / 2.75)) { - return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375; - } else { - return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375; - } - }, - /** - * @alias qtek.animation.easing.BounceInOut - * @param {number} k - * @return {number} - */ - BounceInOut: function(k) { - if (k < 0.5) { - return easing.BounceIn(k * 2) * 0.5; - } - return easing.BounceOut(k * 2 - 1) * 0.5 + 0.5; - } - }; - - return easing; -}); - - -define('qtek/animation/Clip',['require','./easing'],function(require) { - - - - var Easing = require('./easing'); - - /** - * @constructor - * @alias qtek.animation.Clip - * @param {Object} [opts] - * @param {Object} [opts.target] - * @param {number} [opts.life] - * @param {number} [opts.delay] - * @param {number} [opts.gap] - * @param {number} [opts.playbackRatio] - * @param {boolean|number} [opts.loop] If loop is a number, it indicate the loop count of animation - * @param {string|Function} [opts.easing] - * @param {Function} [opts.onframe] - * @param {Function} [opts.ondestroy] - * @param {Function} [opts.onrestart] - */ - var Clip = function(opts) { - - opts = opts || {}; - - /** - * @type {string} - */ - this.name = opts.name || ''; - - /** - * @type {Object} - */ - this.target = opts.target; - - if (typeof(opts.life) !== 'undefined') { - /** - * @type {number} - */ - this.life = opts.life; - } - if (typeof(opts.delay) !== 'undefined') { - /** - * @type {number} - */ - this.delay = opts.delay; - } - if (typeof(opts.gap) !== 'undefined') { - /** - * @type {number} - */ - this.gap = opts.gap; - } - - if (typeof(opts.playbackRatio) !== 'undefined') { - /** - * @type {number} - */ - this.playbackRatio = opts.playbackRatio; - } else { - this.playbackRatio = 1; - } - - this._currentTime = new Date().getTime(); - - this._startTime = this._currentTime + this.delay; - - this._elapsedTime = 0; - - this._loop = opts.loop === undefined ? false : opts.loop; - this.setLoop(this._loop); - - if (typeof(opts.easing) !== 'undefined') { - this.setEasing(opts.easing); - } - - if (typeof(opts.onframe) !== 'undefined') { - /** - * @type {Function} - */ - this.onframe = opts.onframe; - } - - if (typeof(opts.ondestroy) !== 'undefined') { - /** - * @type {Function} - */ - this.ondestroy = opts.ondestroy; - } - - if (typeof(opts.onrestart) !== 'undefined') { - /** - * @type {Function} - */ - this.onrestart = opts.onrestart; - } - - }; - - Clip.prototype = { - - gap: 0, - - life: 0, - - delay: 0, - - /** - * @param {number|boolean} loop - */ - setLoop: function(loop) { - this._loop = loop; - if (loop) { - if (typeof(loop) == 'number') { - this._loopRemained = loop; - } else { - this._loopRemained = 1e8; - } - } - }, - - /** - * @param {string|function} easing - */ - setEasing: function(easing) { - if (typeof(easing) === 'string') { - easing = Easing[easing]; - } - this.easing = easing; - }, - - /** - * @param {number} time - * @return {string} - */ - step: function(time) { - if (time < this._startTime) { - this._currentTime = time; - return; - } - - this._elapse(time); - - var percent = this._elapsedTime / this.life; - - if (percent < 0) { - return; - } - if (percent > 1) { - percent = 1; - } - - var schedule; - if (this.easing) { - schedule = this.easing(percent); - }else{ - schedule = percent; - } - this.fire('frame', schedule); - - if (percent == 1) { - if (this._loop && this._loopRemained > 0) { - this._restartInLoop(); - this._loopRemained--; - return 'restart'; - } else { - // Mark this clip to be deleted - // In the animation.update - this._needsRemove = true; - - return 'destroy'; - } - } else { - return null; - } - }, - - /** - * @param {number} time - * @return {string} - */ - setTime: function(time) { - return this.step(time + this._startTime); - }, - - restart: function() { - // If user leave the page for a while, when he gets back - // All clips may be expired and all start from the beginning value(position) - // It is clearly wrong, so we use remainder to add a offset - var time = new Date().getTime(); - this._elapse(time); - - var remainder = this._elapsedTime % this.life; - this._startTime = time - remainder + this.delay; - this._elapsedTime = 0; - this._currentTime = time - remainder; - - this._needsRemove = false; - }, - - _restartInLoop: function () { - var time = new Date().getTime(); - this._startTime = time + this.gap; - this._currentTime = time; - this._elapsedTime = 0; - }, - - _elapse: function(time) { - this._elapsedTime += (time - this._currentTime) * this.playbackRatio; - this._currentTime = time; - }, - - fire: function(eventType, arg) { - var eventName = 'on' + eventType; - if (this[eventName]) { - this[eventName](this.target, arg); - } - }, - - clone: function () { - var clip = new this.constructor(); - clip.name = this.name; - clip._loop = this._loop; - clip._loopRemained = this._loopRemained; - - clip.life = this.life; - clip.gap = this.gap; - clip.delay = this.delay; - - return clip; - } - }; - Clip.prototype.constructor = Clip; - - return Clip; -}); -define('qtek/animation/Animation',['require','./Clip','../core/Base'],function(require) { - - - - var Clip = require('./Clip'); - var Base = require('../core/Base'); - - var requestAnimationFrame = window.requestAnimationFrame - || window.msRequestAnimationFrame - || window.mozRequestAnimationFrame - || window.webkitRequestAnimationFrame - || function(func){setTimeout(func, 16);}; - - var arraySlice = Array.prototype.slice; - - /** - * Animation is global timeline that schedule all clips. each frame animation will set the time of clips to current and update the states of clips - * @constructor qtek.animation.Animation - * @extends qtek.core.Base - * - * @example - * var animation = new qtek.animation.Animation(); - * var node = new qtek.Node(); - * animation.animate(node.position) - * .when(1000, { - * x: 500, - * y: 500 - * }) - * .when(2000, { - * x: 100, - * y: 100 - * }) - * .when(3000, { - * z: 10 - * }) - * .start('spline'); - */ - var Animation = Base.derive(function() { - return /** @lends qtek.animation.Animation# */{ - /** - * stage is an object with render method, each frame if there exists any animating clips, stage.render will be called - * @type {Object} - */ - stage: null, - - _clips: [], - - _running: false, - - _time: 0 - }; - }, - /** @lends qtek.animation.Animation.prototype */ - { - /** - * @param {qtek.animation.Clip} clip - */ - addClip: function(clip) { - this._clips.push(clip); - }, - /** - * @param {qtek.animation.Clip} clip - */ - removeClip: function(clip) { - var idx = this._clips.indexOf(clip); - this._clips.splice(idx, 1); - }, - - _update: function() { - - var time = new Date().getTime(); - var delta = time - this._time; - var clips = this._clips; - var len = clips.length; - - var deferredEvents = []; - var deferredClips = []; - for (var i = 0; i < len; i++) { - var clip = clips[i]; - var e = clip.step(time); - // Throw out the events need to be called after - // stage.render, like destroy - if (e) { - deferredEvents.push(e); - deferredClips.push(clip); - } - } - - // Remove the finished clip - for (var i = 0; i < len;) { - if (clips[i]._needsRemove) { - clips[i] = clips[len-1]; - clips.pop(); - len--; - } else { - i++; - } - } - - len = deferredEvents.length; - for (var i = 0; i < len; i++) { - deferredClips[i].fire(deferredEvents[i]); - } - - this._time = time; - - this.trigger('frame', delta); - - if (this.stage && this.stage.render) { - this.stage.render(); - } - }, - /** - * Start running animation - */ - start: function() { - var self = this; - - this._running = true; - this._time = new Date().getTime(); - - function step() { - if (self._running) { - - requestAnimationFrame(step); - - self._update(); - } - } - - requestAnimationFrame(step); - }, - /** - * Stop running animation - */ - stop: function() { - this._running = false; - }, - /** - * Remove all clips - */ - removeClipsAll: function() { - this._clips = []; - }, - /** - * Create a deferred animating object - * @param {Object} target - * @param {Object} [options] - * @param {boolean} [options.loop] - * @param {Function} [options.getter] - * @param {Function} [options.setter] - * @param {Function} [options.interpolater] - * @return {qtek.animation.Animation._Deferred} - */ - animate: function(target, options) { - options = options || {}; - var deferred = new Deferred( - target, - options.loop, - options.getter, - options.setter, - options.interpolater - ); - deferred.animation = this; - return deferred; - } - }); - - function _defaultGetter(target, key) { - return target[key]; - } - function _defaultSetter(target, key, value) { - target[key] = value; - } - - function _interpolateNumber(p0, p1, percent) { - return (p1 - p0) * percent + p0; - } - - function _interpolateArray(p0, p1, percent, out, arrDim) { - var len = p0.length; - if (arrDim == 1) { - for (var i = 0; i < len; i++) { - out[i] = _interpolateNumber(p0[i], p1[i], percent); - } - } else { - var len2 = p0[0].length; - for (var i = 0; i < len; i++) { - for (var j = 0; j < len2; j++) { - out[i][j] = _interpolateNumber( - p0[i][j], p1[i][j], percent - ); - } - } - } - } - - function _isArrayLike(data) { - if (typeof(data) == 'undefined') { - return false; - } else if (typeof(data) == 'string') { - return false; - } else { - return typeof(data.length) == 'number'; - } - } - - function _cloneValue(value) { - if (_isArrayLike(value)) { - var len = value.length; - if (_isArrayLike(value[0])) { - var ret = []; - for (var i = 0; i < len; i++) { - ret.push(arraySlice.call(value[i])); - } - return ret; - } else { - return arraySlice.call(value); - } - } else { - return value; - } - } - - function _catmullRomInterpolateArray( - p0, p1, p2, p3, t, t2, t3, out, arrDim - ) { - var len = p0.length; - if (arrDim == 1) { - for (var i = 0; i < len; i++) { - out[i] = _catmullRomInterpolate( - p0[i], p1[i], p2[i], p3[i], t, t2, t3 - ); - } - } else { - var len2 = p0[0].length; - for (var i = 0; i < len; i++) { - for (var j = 0; j < len2; j++) { - out[i][j] = _catmullRomInterpolate( - p0[i][j], p1[i][j], p2[i][j], p3[i][j], - t, t2, t3 - ); - } - } - } - } - - function _catmullRomInterpolate(p0, p1, p2, p3, t, t2, t3) { - var v0 = (p2 - p0) * 0.5; - var v1 = (p3 - p1) * 0.5; - return (2 * (p1 - p2) + v0 + v1) * t3 - + (- 3 * (p1 - p2) - 2 * v0 - v1) * t2 - + v0 * t + p1; - } - - /** - * @description Deferred object can only be created by Animation.prototype.animate method. - * After created, we can use {@link qtek.animation.Animation._Deferred#when} to add all keyframes and {@link qtek.animation.Animation._Deferred#start} it. - * Clips will be automatically created and added to the animation instance which created this deferred object. - * - * @constructor qtek.animation.Animation._Deferred - * - * @param {Object} target - * @param {boolean} loop - * @param {Function} getter - * @param {Function} setter - * @param {Function} interpolater - */ - function Deferred(target, loop, getter, setter, interpolater) { - this._tracks = {}; - this._target = target; - - this._loop = loop || false; - - this._getter = getter || _defaultGetter; - this._setter = setter || _defaultSetter; - - this._interpolater = interpolater || null; - - this._clipCount = 0; - - this._delay = 0; - - this._doneList = []; - - this._onframeList = []; - - this._clipList = []; - } - - Deferred.prototype = { - - constructor: Deferred, - - /** - * @param {number} time Keyframe time using millisecond - * @param {Object} props A key-value object. Value can be number, 1d and 2d array - * @return {qtek.animation.Animation._Deferred} - * @memberOf qtek.animation.Animation._Deferred.prototype - */ - when: function(time, props) { - for (var propName in props) { - if (! this._tracks[propName]) { - this._tracks[propName] = []; - // If time is 0 - // Then props is given initialize value - // Else - // Initialize value from current prop value - if (time !== 0) { - this._tracks[propName].push({ - time: 0, - value: _cloneValue( - this._getter(this._target, propName) - ) - }); - } - } - this._tracks[propName].push({ - time: parseInt(time), - value: props[propName] - }); - } - return this; - }, - /** - * callback when running animation - * @param {Function} callback callback have two args, animating target and current percent - * @return {qtek.animation.Animation._Deferred} - * @memberOf qtek.animation.Animation._Deferred.prototype - */ - during: function(callback) { - this._onframeList.push(callback); - return this; - }, - /** - * Start the animation - * @param {string|function} easing - * @return {qtek.animation.Animation._Deferred} - * @memberOf qtek.animation.Animation._Deferred.prototype - */ - start: function(easing) { - - var self = this; - var setter = this._setter; - var getter = this._getter; - var interpolater = this._interpolater; - var onFrameListLen = self._onframeList.length; - var useSpline = easing === 'spline'; - - var ondestroy = function() { - self._clipCount--; - if (self._clipCount === 0) { - // Clear all tracks - self._tracks = {}; - - var len = self._doneList.length; - for (var i = 0; i < len; i++) { - self._doneList[i].call(self); - } - } - }; - - var createTrackClip = function(keyframes, propName) { - var trackLen = keyframes.length; - if (!trackLen) { - return; - } - // Guess data type - var firstVal = keyframes[0].value; - var isValueArray = _isArrayLike(firstVal); - - // For vertices morphing - var arrDim = ( - isValueArray - && _isArrayLike(firstVal[0]) - ) - ? 2 : 1; - // Sort keyframe as ascending - keyframes.sort(function(a, b) { - return a.time - b.time; - }); - - var trackMaxTime = keyframes[trackLen - 1].time; - // Percents of each keyframe - var kfPercents = []; - // Value of each keyframe - var kfValues = []; - for (var i = 0; i < trackLen; i++) { - kfPercents.push(keyframes[i].time / trackMaxTime); - kfValues.push(keyframes[i].value); - } - - // Cache the key of last frame to speed up when - // animation playback is sequency - var cacheKey = 0; - var cachePercent = 0; - var start; - var i, w; - var p0, p1, p2, p3; - - var onframe = function(target, percent) { - // Find the range keyframes - // kf1-----kf2---------current--------kf3 - // find kf2(i) and kf3(i+1) and do interpolation - if (percent < cachePercent) { - // Start from next key - start = Math.min(cacheKey + 1, trackLen - 1); - for (i = start; i >= 0; i--) { - if (kfPercents[i] <= percent) { - break; - } - } - i = Math.min(i, trackLen-2); - } else { - for (i = cacheKey; i < trackLen; i++) { - if (kfPercents[i] > percent) { - break; - } - } - i = Math.min(i-1, trackLen-2); - } - cacheKey = i; - cachePercent = percent; - - var range = (kfPercents[i+1] - kfPercents[i]); - if (range === 0) { - return; - } else { - w = (percent - kfPercents[i]) / range; - } - if (useSpline) { - p1 = kfValues[i]; - p0 = kfValues[i === 0 ? i : i - 1]; - p2 = kfValues[i > trackLen - 2 ? trackLen - 1 : i + 1]; - p3 = kfValues[i > trackLen - 3 ? trackLen - 1 : i + 2]; - if (interpolater) { - setter( - target, - propName, - interpolater( - getter(target, propName), - p0, p1, p2, p3, w - ) - ); - } else if (isValueArray) { - _catmullRomInterpolateArray( - p0, p1, p2, p3, w, w*w, w*w*w, - getter(target, propName), - arrDim - ); - } else { - setter( - target, - propName, - _catmullRomInterpolate(p0, p1, p2, p3, w, w*w, w*w*w) - ); - } - } else { - if (interpolater) { - setter( - target, - propName, - interpolater( - getter(target, propName), - kfValues[i], - kfValues[i + 1], - w - ) - ); - } - else if (isValueArray) { - _interpolateArray( - kfValues[i], kfValues[i+1], w, - getter(target, propName), - arrDim - ); - } else { - setter( - target, - propName, - _interpolateNumber(kfValues[i], kfValues[i+1], w) - ); - } - } - - for (i = 0; i < onFrameListLen; i++) { - self._onframeList[i](target, percent); - } - }; - - var clip = new Clip({ - target: self._target, - life: trackMaxTime, - loop: self._loop, - delay: self._delay, - onframe: onframe, - ondestroy: ondestroy - }); - - if (easing && easing !== 'spline') { - clip.setEasing(easing); - } - self._clipList.push(clip); - self._clipCount++; - self.animation.addClip(clip); - }; - - for (var propName in this._tracks) { - createTrackClip(this._tracks[propName], propName); - } - return this; - }, - /** - * Stop the animation - * @memberOf qtek.animation.Animation._Deferred.prototype - */ - stop: function() { - for (var i = 0; i < this._clipList.length; i++) { - var clip = this._clipList[i]; - this.animation.removeClip(clip); - } - this._clipList = []; - }, - /** - * Delay given milliseconds - * @param {number} time - * @return {qtek.animation.Animation._Deferred} - * @memberOf qtek.animation.Animation._Deferred.prototype - */ - delay: function(time){ - this._delay = time; - return this; - }, - /** - * Callback after animation finished - * @param {Function} func - * @return {qtek.animation.Animation._Deferred} - * @memberOf qtek.animation.Animation._Deferred.prototype - */ - done: function(func) { - if (func) { - this._doneList.push(func); - } - return this; - }, - /** - * Get all clips created in start method. - * @return {qtek.animation.Clip[]} - * @memberOf qtek.animation.Animation._Deferred.prototype - */ - getClips: function() { - return this._clipList; - } - }; - - Animation._Deferred = Deferred; - - return Animation; -}); - -// 1D Blend clip of blend tree -// http://docs.unity3d.com/Documentation/Manual/1DBlending.html -define('qtek/animation/Blend1DClip',['require','./Clip'],function(require) { - - - - var Clip = require('./Clip'); - - var clipSortFunc = function(a, b) { - return a.position < b.position; - }; - - /** - * @typedef {Object} qtek.animation.Blend1DClip.IClipInput - * @property {number} position - * @property {qtek.animation.Clip} clip - * @property {number} offset - */ - - /** - * 1d blending node in animation blend tree. - * output clip must have blend1D and copy method - * @constructor - * @alias qtek.animation.Blend1DClip - * @extends qtek.animation.Clip - * - * @param {Object} [opts] - * @param {string} [opts.name] - * @param {Object} [opts.target] - * @param {number} [opts.life] - * @param {number} [opts.delay] - * @param {number} [opts.gap] - * @param {number} [opts.playbackRatio] - * @param {boolean|number} [opts.loop] If loop is a number, it indicate the loop count of animation - * @param {string|Function} [opts.easing] - * @param {Function} [opts.onframe] - * @param {Function} [opts.ondestroy] - * @param {Function} [opts.onrestart] - * @param {object[]} [opts.inputs] - * @param {number} [opts.position] - * @param {qtek.animation.Clip} [opts.output] - */ - var Blend1DClip = function(opts) { - - opts = opts || {}; - - Clip.call(this, opts); - /** - * Output clip must have blend1D and copy method - * @type {qtek.animation.Clip} - */ - this.output = opts.output || null; - /** - * @type {qtek.animation.Blend1DClip.IClipInput[]} - */ - this.inputs = opts.inputs || []; - /** - * @type {number} - */ - this.position = 0; - - this._cacheKey = 0; - this._cachePosition = -Infinity; - - this.inputs.sort(clipSortFunc); - }; - - Blend1DClip.prototype = new Clip(); - Blend1DClip.prototype.constructor = Blend1DClip; - - /** - * @param {number} position - * @param {qtek.animation.Clip} inputClip - * @param {number} [offset] - * @return {qtek.animation.Blend1DClip.IClipInput} - */ - Blend1DClip.prototype.addInput = function(position, inputClip, offset) { - var obj = { - position: position, - clip: inputClip, - offset: offset || 0 - }; - this.life = Math.max(inputClip.life, this.life); - - if (!this.inputs.length) { - this.inputs.push(obj); - return obj; - } - var len = this.inputs.length; - if (this.inputs[0].position > position) { - this.inputs.unshift(obj); - } else if (this.inputs[len - 1].position <= position) { - this.inputs.push(obj); - } else { - var key = this._findKey(position); - this.inputs.splice(key, obj); - } - - return obj; - }; - - Blend1DClip.prototype.step = function(time) { - - var ret = Clip.prototype.step.call(this, time); - - if (ret !== 'destroy') { - this.setTime(this._elapsedTime); - } - - return ret; - }; - - Blend1DClip.prototype.setTime = function(time) { - var position = this.position; - var inputs = this.inputs; - var len = inputs.length; - var min = inputs[0].position; - var max = inputs[len-1].position; - - if (position <= min || position >= max) { - var in0 = position <= min ? inputs[0] : inputs[len-1]; - var clip = in0.clip; - var offset = in0.offset; - clip.setTime((time + offset) % clip.life); - // Input clip is a blend clip - // PENDING - if (clip.output instanceof Clip) { - this.output.copy(clip.output); - } else { - this.output.copy(clip); - } - } else { - var key = this._findKey(position); - var in1 = inputs[key]; - var in2 = inputs[key + 1]; - var clip1 = in1.clip; - var clip2 = in2.clip; - // Set time on input clips - clip1.setTime((time + in1.offset) % clip1.life); - clip2.setTime((time + in2.offset) % clip2.life); - - var w = (this.position - in1.position) / (in2.position - in1.position); - - var c1 = clip1.output instanceof Clip ? clip1.output : clip1; - var c2 = clip2.output instanceof Clip ? clip2.output : clip2; - this.output.blend1D(c1, c2, w); - } - }; - - /** - * Clone a new Blend1D clip - * @param {boolean} cloneInputs True if clone the input clips - * @return {qtek.animation.Blend1DClip} - */ - Blend1DClip.prototype.clone = function (cloneInputs) { - var clip = Clip.prototype.clone.call(this); - clip.output = this.output.clone(); - for (var i = 0; i < this.inputs.length; i++) { - var inputClip = cloneInputs ? this.inputs[i].clip.clone(true) : this.inputs[i].clip; - clip.addInput(this.inputs[i].position, inputClip, this.inputs[i].offset); - } - return clip; - }; - - // Find the key where position in range [inputs[key].position, inputs[key+1].position) - Blend1DClip.prototype._findKey = function(position) { - var key = -1; - var inputs = this.inputs; - var len = inputs.length; - if (this._cachePosition < position) { - for (var i = this._cacheKey; i < len-1; i++) { - if (position >= inputs[i].position && position < inputs[i+1].position) { - key = i; - } - } - } else { - var s = Math.min(len-2, this._cacheKey); - for (var i = s; i >= 0; i--) { - if (position >= inputs[i].position && position < inputs[i+1].position) { - key = i; - } - } - } - if (key >= 0) { - this._cacheKey = key; - this._cachePosition = position; - } - - return key; - }; - - return Blend1DClip; -}); -// Delaunay Triangulation -// Modified from https://github.com/ironwallaby/delaunay - -define('qtek/util/delaunay',['require'],function(require) { - - - - function appendSupertriangleVertices(vertices) { - var xmin = Number.POSITIVE_INFINITY, - ymin = Number.POSITIVE_INFINITY, - xmax = Number.NEGATIVE_INFINITY, - ymax = Number.NEGATIVE_INFINITY, - i, dx, dy, dmax, xmid, ymid; - - for(i = vertices.length; i--; ) { - if(vertices[i][0] < xmin) xmin = vertices[i][0]; - if(vertices[i][0] > xmax) xmax = vertices[i][0]; - if(vertices[i][1] < ymin) ymin = vertices[i][1]; - if(vertices[i][1] > ymax) ymax = vertices[i][1]; - } - - dx = xmax - xmin; - dy = ymax - ymin; - dmax = Math.max(dx, dy); - xmid = xmin + dx * 0.5; - ymid = ymin + dy * 0.5; - - vertices.push( - [xmid - 20 * dmax, ymid - dmax], - [xmid , ymid + 20 * dmax], - [xmid + 20 * dmax, ymid - dmax] - ); - } - - function triangle(vertices, i, j, k) { - var a = vertices[i], - b = vertices[j], - c = vertices[k], - A = b[0] - a[0], - B = b[1] - a[1], - C = c[0] - a[0], - D = c[1] - a[1], - E = A * (a[0] + b[0]) + B * (a[1] + b[1]), - F = C * (a[0] + c[0]) + D * (a[1] + c[1]), - G = 2 * (A * (c[1] - b[1]) - B * (c[0] - b[0])), - minx, miny, dx, dy, x, y; - - /* If the points of the triangle are collinear, then just find the - * extremes and use the midpoint as the center of the circumcircle. */ - if (Math.abs(G) < 0.000001) { - minx = Math.min(a[0], b[0], c[0]); - miny = Math.min(a[1], b[1], c[1]); - dx = (Math.max(a[0], b[0], c[0]) - minx) * 0.5; - dy = (Math.max(a[1], b[1], c[1]) - miny) * 0.5; - x = minx + dx; - y = miny + dy; - } - else { - x = (D*E - B*F) / G; - y = (A*F - C*E) / G; - dx = x - a[0]; - dy = y - a[1]; - } - - return {i: i, j: j, k: k, x: x, y: y, r: dx * dx + dy * dy}; - } - - function dedup(edges) { - var j = edges.length, - a, b, i, m, n; - - outer: while (j) { - b = edges[--j]; - a = edges[--j]; - i = j; - while (i) { - n = edges[--i] - m = edges[--i] - if ((a === m && b === n) || (a === n && b === m)) { - edges.splice(j, 2); - edges.splice(i, 2); - j -= 2; - continue outer; - } - } - } - } - - var delaunay = { - triangulate: function(vertices, key) { - var n = vertices.length, - i, j, indices, open, closed, edges, dx, dy, a, b, c; - - /* Bail if there aren't enough vertices to form any triangles. */ - if (n < 3) { - return []; - } - - /* Slice out the actual vertices from the passed objects. (Duplicate the - * array even if we don't, though, since we need to make a supertriangle - * later on!) */ - vertices = vertices.slice(0); - - if (key) { - for (i = n; i--; ) { - vertices[i] = vertices[i][key]; - } - } - - /* Make an array of indices into the vertex array, sorted by the vertices' - * x-position. */ - indices = new Array(n); - - for (i = n; i--; ) { - indices[i] = i; - } - - indices.sort(function(i, j) { return vertices[j][0] - vertices[i][0]; }); - - /* Next, find the vertices of the supertriangle (which contains all other - * triangles), and append them onto the end of a (copy of) the vertex - * array. */ - appendSupertriangleVertices(vertices); - - /* Initialize the open list (containing the supertriangle and nothing else) - * and the closed list (which is empty since we havn't processed any - * triangles yet). */ - open = [triangle(vertices, n + 0, n + 1, n + 2)]; - closed = []; - edges = []; - - /* Incrementally add each vertex to the mesh. */ - for (i = indices.length; i--; ) { - c = indices[i]; - edges.length = 0; - - /* For each open triangle, check to see if the current point is - * inside it's circumcircle. If it is, remove the triangle and add - * it's edges to an edge list. */ - for (j = open.length; j--; ) { - /* If this point is to the right of this triangle's circumcircle, - * then this triangle should never get checked again. Remove it - * from the open list, add it to the closed list, and skip. */ - dx = vertices[c][0] - open[j].x; - if (dx > 0.0 && dx * dx > open[j].r) { - closed.push(open[j]); - open.splice(j, 1); - continue; - } - - /* If we're outside the circumcircle, skip this triangle. */ - dy = vertices[c][1] - open[j].y; - if (dx * dx + dy * dy > open[j].r) { - continue; - } - - /* Remove the triangle and add it's edges to the edge list. */ - edges.push( - open[j].i, open[j].j, - open[j].j, open[j].k, - open[j].k, open[j].i - ); - open.splice(j, 1); - } - - /* Remove any doubled edges. */ - dedup(edges); - - /* Add a new triangle for each edge. */ - for(j = edges.length; j; ) { - b = edges[--j]; - a = edges[--j]; - open.push(triangle(vertices, a, b, c)); - } - } - - /* Copy any remaining open triangles to the closed list, and then - * remove any triangles that share a vertex with the supertriangle, building - * a list of triplets that represent triangles. */ - for (i = open.length; i--; ) { - closed.push(open[i]); - } - open.length = 0; - - for(i = closed.length; i--; ) { - if(closed[i].i < n && closed[i].j < n && closed[i].k < n) { - var i1 = closed[i].i, - i2 = closed[i].j, - i3 = closed[i].k; - var tri = { - indices : [i1, i2, i3], - vertices : [vertices[i1], vertices[i2], vertices[i3]] - }; - open.push(tri); - } - } - - /* Yay, we're done! */ - return open; - }, - - contains: function(tri, p) { - /* Bounding box test first, for quick rejections. */ - if((p[0] < tri[0][0] && p[0] < tri[1][0] && p[0] < tri[2][0]) || - (p[0] > tri[0][0] && p[0] > tri[1][0] && p[0] > tri[2][0]) || - (p[1] < tri[0][1] && p[1] < tri[1][1] && p[1] < tri[2][1]) || - (p[1] > tri[0][1] && p[1] > tri[1][1] && p[1] > tri[2][1])) { - - return null; - } - - var a = tri[1][0] - tri[0][0], - b = tri[2][0] - tri[0][0], - c = tri[1][1] - tri[0][1], - d = tri[2][1] - tri[0][1], - i = a * d - b * c; - - /* Degenerate tri. */ - if(i === 0.0) { - return null; - } - - var u = (d * (p[0] - tri[0][0]) - b * (p[1] - tri[0][1])) / i, - v = (a * (p[1] - tri[0][1]) - c * (p[0] - tri[0][0])) / i; - - /* If we're outside the tri, fail. */ - if(u < 0.0 || v < 0.0 || (u + v) > 1.0) { - return null; - } - - // normalize - // u = Math.max(0.0, u); - // v = Math.max(0.0, v); - // var s = u + v; - // if (s > 1.0) { - // u = u / s; - // v = v / s; - // } - return [u, v]; - } - } - - return delaunay; -}); -// 2D Blend clip of blend tree -// http://docs.unity3d.com/Documentation/Manual/2DBlending.html -define('qtek/animation/Blend2DClip',['require','./Clip','../util/delaunay','../math/Vector2'],function(require) { - - - - var Clip = require('./Clip'); - - var delaunay = require('../util/delaunay'); - var Vector2 = require('../math/Vector2'); - - /** - * @typedef {Object} qtek.animation.Blend2DClip.IClipInput - * @property {qtek.math.Vector2} position - * @property {qtek.animation.Clip} clip - * @property {number} offset - */ - - /** - * 2d blending node in animation blend tree. - * output clip must have blend2D method - * @constructor - * @alias qtek.animation.Blend2DClip - * @extends qtek.animation.Clip - * - * @param {Object} [opts] - * @param {string} [opts.name] - * @param {Object} [opts.target] - * @param {number} [opts.life] - * @param {number} [opts.delay] - * @param {number} [opts.gap] - * @param {number} [opts.playbackRatio] - * @param {boolean|number} [opts.loop] If loop is a number, it indicate the loop count of animation - * @param {string|Function} [opts.easing] - * @param {Function} [opts.onframe] - * @param {Function} [opts.ondestroy] - * @param {Function} [opts.onrestart] - * @param {object[]} [opts.inputs] - * @param {qtek.math.Vector2} [opts.position] - * @param {qtek.animation.Clip} [opts.output] - */ - var Blend2DClip = function(opts) { - - opts = opts || {}; - - Clip.call(this, opts); - /** - * Output clip must have blend2D method - * @type {qtek.animation.Clip} - */ - this.output = opts.output || null; - /** - * @type {qtek.animation.Blend2DClip.IClipInput[]} - */ - this.inputs = opts.inputs || []; - /** - * @type {qtek.math.Vector2} - */ - this.position = new Vector2(); - - this._cacheTriangle = null; - - this._triangles = []; - - this._updateTriangles(); - }; - - Blend2DClip.prototype = new Clip(); - Blend2DClip.prototype.constructor = Blend2DClip; - /** - * @param {qtek.math.Vector2} position - * @param {qtek.animation.Clip} inputClip - * @param {number} [offset] - * @return {qtek.animation.Blend2DClip.IClipInput} - */ - Blend2DClip.prototype.addInput = function(position, inputClip, offset) { - var obj = { - position : position, - clip : inputClip, - offset : offset || 0 - }; - this.inputs.push(obj); - this.life = Math.max(inputClip.life, this.life); - // TODO Change to incrementally adding - this._updateTriangles(); - - return obj; - }; - - // Delaunay triangulate - Blend2DClip.prototype._updateTriangles = function() { - var inputs = this.inputs.map(function(a) { - return a.position; - }); - this._triangles = delaunay.triangulate(inputs, '_array'); - }; - - Blend2DClip.prototype.step = function(time) { - - var ret = Clip.prototype.step.call(this, time); - - if (ret !== 'destroy') { - this.setTime(this._elapsedTime); - } - - return ret; - }; - - Blend2DClip.prototype.setTime = function(time) { - var res = this._findTriangle(this.position); - if (!res) { - return; - } - // In Barycentric - var a = res[1]; // Percent of clip2 - var b = res[2]; // Percent of clip3 - - var tri = res[0]; - - var in1 = this.inputs[tri.indices[0]]; - var in2 = this.inputs[tri.indices[1]]; - var in3 = this.inputs[tri.indices[2]]; - var clip1 = in1.clip; - var clip2 = in2.clip; - var clip3 = in3.clip; - - clip1.setTime((time + in1.offset) % clip1.life); - clip2.setTime((time + in2.offset) % clip2.life); - clip3.setTime((time + in3.offset) % clip3.life); - - var c1 = clip1.output instanceof Clip ? clip1.output : clip1; - var c2 = clip2.output instanceof Clip ? clip2.output : clip2; - var c3 = clip3.output instanceof Clip ? clip3.output : clip3; - - this.output.blend2D(c1, c2, c3, a, b); - }; - - /** - * Clone a new Blend2D clip - * @param {boolean} cloneInputs True if clone the input clips - * @return {qtek.animation.Blend2DClip} - */ - Blend2DClip.prototype.clone = function (cloneInputs) { - var clip = Clip.prototype.clone.call(this); - clip.output = this.output.clone(); - for (var i = 0; i < this.inputs.length; i++) { - var inputClip = cloneInputs ? this.inputs[i].clip.clone(true) : this.inputs[i].clip; - clip.addInput(this.inputs[i].position, inputClip, this.inputs[i].offset); - } - return clip; - }; - - Blend2DClip.prototype._findTriangle = function(position) { - if (this._cacheTriangle) { - var res = delaunay.contains(this._cacheTriangle.vertices, position._array); - if (res) { - return [this._cacheTriangle, res[0], res[1]]; - } - } - for (var i = 0; i < this._triangles.length; i++) { - var tri = this._triangles[i]; - var res = delaunay.contains(tri.vertices, this.position._array); - if (res) { - this._cacheTriangle = tri; - return [tri, res[0], res[1]]; - } - } - }; - - return Blend2DClip; -}); -define('qtek/animation/TransformClip',['require','./Clip','../dep/glmatrix'],function(require) { - - - - var Clip = require('./Clip'); - - var glMatrix = require('../dep/glmatrix'); - var quat = glMatrix.quat; - var vec3 = glMatrix.vec3; - - function keyframeSort(a, b) { - return a.time - b.time; - } - - /** - * @constructor - * @alias qtek.animation.TransformClip - * @extends qtek.animation.Clip - * - * @param {Object} [opts] - * @param {string} [opts.name] - * @param {Object} [opts.target] - * @param {number} [opts.life] - * @param {number} [opts.delay] - * @param {number} [opts.gap] - * @param {number} [opts.playbackRatio] - * @param {boolean|number} [opts.loop] If loop is a number, it indicate the loop count of animation - * @param {string|Function} [opts.easing] - * @param {Function} [opts.onframe] - * @param {Function} [opts.ondestroy] - * @param {Function} [opts.onrestart] - * @param {object[]} [opts.keyFrames] - */ - var TransformClip = function(opts) { - - opts = opts || {}; - - Clip.call(this, opts); - - //[{ - // time: //ms - // position: // optional - // rotation: // optional - // scale: // optional - //}] - this.keyFrames = []; - if (opts.keyFrames) { - this.addKeyFrames(opts.keyFrames); - } - - /** - * @type {Float32Array} - */ - this.position = vec3.create(); - /** - * Rotation is represented by a quaternion - * @type {Float32Array} - */ - this.rotation = quat.create(); - /** - * @type {Float32Array} - */ - this.scale = vec3.fromValues(1, 1, 1); - - this._cacheKey = 0; - this._cacheTime = 0; - }; - - TransformClip.prototype = Object.create(Clip.prototype); - - TransformClip.prototype.constructor = TransformClip; - - TransformClip.prototype.step = function(time) { - - var ret = Clip.prototype.step.call(this, time); - - if (ret !== 'destroy') { - this.setTime(this._elapsedTime); - } - - return ret; - }; - - TransformClip.prototype.setTime = function(time) { - this._interpolateField(time, 'position'); - this._interpolateField(time, 'rotation'); - this._interpolateField(time, 'scale'); - }; - /** - * Add a key frame - * @param {Object} kf - */ - TransformClip.prototype.addKeyFrame = function(kf) { - for (var i = 0; i < this.keyFrames.length - 1; i++) { - var prevFrame = this.keyFrames[i]; - var nextFrame = this.keyFrames[i+1]; - if (prevFrame.time <= kf.time && nextFrame.time >= kf.time) { - this.keyFrames.splice(i, 0, kf); - return i; - } - } - - this.life = kf.time; - this.keyFrames.push(kf); - }; - - /** - * Add keyframes - * @param {object[]} kfs - */ - TransformClip.prototype.addKeyFrames = function(kfs) { - for (var i = 0; i < kfs.length; i++) { - this.keyFrames.push(kfs[i]); - } - - this.keyFrames.sort(keyframeSort); - - this.life = this.keyFrames[this.keyFrames.length - 1].time; - }; - - TransformClip.prototype._interpolateField = function(time, fieldName) { - var kfs = this.keyFrames; - var len = kfs.length; - var start; - var end; - - if (!kfs.length) { - return; - } - if (time < kfs[0].time || time > kfs[kfs.length-1].time) { - return; - } - if (time < this._cacheTime) { - var s = this._cacheKey >= len-1 ? len-1 : this._cacheKey+1; - for (var i = s; i >= 0; i--) { - if (kfs[i].time <= time && kfs[i][fieldName]) { - start = kfs[i]; - this._cacheKey = i; - this._cacheTime = time; - } else if (kfs[i][fieldName]) { - end = kfs[i]; - break; - } - } - } else { - for (var i = this._cacheKey; i < len; i++) { - if (kfs[i].time <= time && kfs[i][fieldName]) { - start = kfs[i]; - this._cacheKey = i; - this._cacheTime = time; - } else if (kfs[i][fieldName]) { - end = kfs[i]; - break; - } - } - } - - if (start && end) { - var percent = (time-start.time) / (end.time-start.time); - percent = Math.max(Math.min(percent, 1), 0); - if (fieldName === 'rotation') { - quat.slerp(this[fieldName], start[fieldName], end[fieldName], percent); - } else { - vec3.lerp(this[fieldName], start[fieldName], end[fieldName], percent); - } - } else { - this._cacheKey = 0; - this._cacheTime = 0; - } - }; - /** - * 1D blending between two clips - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 - * @param {number} w - */ - TransformClip.prototype.blend1D = function(c1, c2, w) { - vec3.lerp(this.position, c1.position, c2.position, w); - vec3.lerp(this.scale, c1.scale, c2.scale, w); - quat.slerp(this.rotation, c1.rotation, c2.rotation, w); - }; - - /** - * 2D blending between three clips - * @method - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c3 - * @param {number} f - * @param {number} g - */ - TransformClip.prototype.blend2D = (function() { - var q1 = quat.create(); - var q2 = quat.create(); - return function(c1, c2, c3, f, g) { - var a = 1 - f - g; - - this.position[0] = c1.position[0] * a + c2.position[0] * f + c3.position[0] * g; - this.position[1] = c1.position[1] * a + c2.position[1] * f + c3.position[1] * g; - this.position[2] = c1.position[2] * a + c2.position[2] * f + c3.position[2] * g; - - this.scale[0] = c1.scale[0] * a + c2.scale[0] * f + c3.scale[0] * g; - this.scale[1] = c1.scale[1] * a + c2.scale[1] * f + c3.scale[1] * g; - this.scale[2] = c1.scale[2] * a + c2.scale[2] * f + c3.scale[2] * g; - - // http://msdn.microsoft.com/en-us/library/windows/desktop/bb205403(v=vs.85).aspx - // http://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.quaternion.xmquaternionbarycentric(v=vs.85).aspx - var s = f + g; - if (s === 0) { - quat.copy(this.rotation, c1.rotation); - } else { - quat.slerp(q1, c1.rotation, c2.rotation, s); - quat.slerp(q2, c1.rotation, c3.rotation, s); - quat.slerp(this.rotation, q1, q2, g / s); - } - }; - })(); - - /** - * Additive blending between two clips - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 - */ - TransformClip.prototype.additiveBlend = function(c1, c2) { - vec3.add(this.position, c1.position, c2.position); - vec3.add(this.scale, c1.scale, c2.scale); - quat.multiply(this.rotation, c2.rotation, c1.rotation); - }; - - /** - * Subtractive blending between two clips - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 - */ - TransformClip.prototype.subtractiveBlend = function(c1, c2) { - vec3.sub(this.position, c1.position, c2.position); - vec3.sub(this.scale, c1.scale, c2.scale); - quat.invert(this.rotation, c2.rotation); - quat.multiply(this.rotation, this.rotation, c1.rotation); - }; - - /** - * @param {number} startTime - * @param {number} endTime - * @param {boolean} isLoop - */ - TransformClip.prototype.getSubClip = function(startTime, endTime) { - // TODO - console.warn('TODO'); - }; - - /** - * Clone a new TransformClip - * @return {qtek.animation.TransformClip} - */ - TransformClip.prototype.clone = function () { - var clip = Clip.prototype.clone.call(this); - clip.keyFrames = this.keyFrames; - - vec3.copy(clip.position, this.position); - quat.copy(clip.rotation, this.rotation); - vec3.copy(clip.scale, this.scale); - - return clip; - }; - - - return TransformClip; -}); -// Sampler clip is especially for the animation sampler in glTF -// Use Typed Array can reduce a lot of heap memory -define('qtek/animation/SamplerClip',['require','./Clip','./TransformClip','../dep/glmatrix'],function(require) { - - - - var Clip = require('./Clip'); - var TransformClip = require('./TransformClip'); - - var glMatrix = require('../dep/glmatrix'); - var quat = glMatrix.quat; - var vec3 = glMatrix.vec3; - - // lerp function with offset in large array - function vec3lerp(out, a, b, t, oa, ob) { - var ax = a[oa]; - var ay = a[oa + 1]; - var az = a[oa + 2]; - out[0] = ax + t * (b[ob] - ax); - out[1] = ay + t * (b[ob + 1] - ay); - out[2] = az + t * (b[ob + 2] - az); - - return out; - } - - function quatSlerp(out, a, b, t, oa, ob) { - // benchmarks: - // http://jsperf.com/quaternion-slerp-implementations - - var ax = a[0 + oa], ay = a[1 + oa], az = a[2 + oa], aw = a[3 + oa], - bx = b[0 + ob], by = b[1 + ob], bz = b[2 + ob], bw = b[3 + ob]; - - var omega, cosom, sinom, scale0, scale1; - - // calc cosine - cosom = ax * bx + ay * by + az * bz + aw * bw; - // adjust signs (if necessary) - if (cosom < 0.0) { - cosom = -cosom; - bx = - bx; - by = - by; - bz = - bz; - bw = - bw; - } - // calculate coefficients - if ((1.0 - cosom) > 0.000001) { - // standard case (slerp) - omega = Math.acos(cosom); - sinom = Math.sin(omega); - scale0 = Math.sin((1.0 - t) * omega) / sinom; - scale1 = Math.sin(t * omega) / sinom; - } else { - // 'from' and 'to' quaternions are very close - // ... so we can do a linear interpolation - scale0 = 1.0 - t; - scale1 = t; - } - // calculate final values - out[0] = scale0 * ax + scale1 * bx; - out[1] = scale0 * ay + scale1 * by; - out[2] = scale0 * az + scale1 * bz; - out[3] = scale0 * aw + scale1 * bw; - - return out; - } - - /** - * @constructor - * @alias qtek.animation.SamplerClip - * @extends qtek.animation.Clip - * - * @param {Object} [opts] - * @param {string} [opts.name] - * @param {Object} [opts.target] - * @param {number} [opts.life] - * @param {number} [opts.delay] - * @param {number} [opts.gap] - * @param {number} [opts.playbackRatio] - * @param {boolean|number} [opts.loop] If loop is a number, it indicate the loop count of animation - * @param {string|Function} [opts.easing] - * @param {Function} [opts.onframe] - * @param {Function} [opts.ondestroy] - * @param {Function} [opts.onrestart] - */ - var SamplerClip = function(opts) { - - opts = opts || {}; - - Clip.call(this, opts); - - /** - * @type {Float32Array} - */ - this.position = vec3.create(); - /** - * Rotation is represented by a quaternion - * @type {Float32Array} - */ - this.rotation = quat.create(); - /** - * @type {Float32Array} - */ - this.scale = vec3.fromValues(1, 1, 1); - - this.channels = { - time: null, - position: null, - rotation: null, - scale: null - }; - - this._cacheKey = 0; - this._cacheTime = 0; - }; - - SamplerClip.prototype = Object.create(Clip.prototype); - - SamplerClip.prototype.constructor = SamplerClip; - - SamplerClip.prototype.step = function(time) { - - var ret = Clip.prototype.step.call(this, time); - - if (ret !== 'destroy') { - this.setTime(this._elapsedTime); - } - - return ret; - }; - - SamplerClip.prototype.setTime = function(time) { - if (!this.channels.time) { - return; - } - var channels = this.channels; - var len = channels.time.length; - var key = -1; - if (time < this._cacheTime) { - var s = Math.min(len - 2, this._cacheKey); - for (var i = s; i >= 0; i--) { - if (channels.time[i - 1] <= time && channels.time[i] > time) { - key = i - 1; - break; - } - } - } else { - for (var i = this._cacheKey; i < len-1; i++) { - if (channels.time[i] <= time && channels.time[i + 1] > time) { - key = i; - break; - } - } - } - - if (key > -1) { - this._cacheKey = key; - this._cacheTime = time; - var start = key; - var end = key + 1; - var startTime = channels.time[start]; - var endTime = channels.time[end]; - var percent = (time - startTime) / (endTime - startTime); - - if (channels.rotation) { - quatSlerp(this.rotation, channels.rotation, channels.rotation, percent, start * 4, end * 4); - } - if (channels.position) { - vec3lerp(this.position, channels.position, channels.position, percent, start * 3, end * 3); - } - if (channels.scale) { - vec3lerp(this.scale, channels.scale, channels.scale, percent, start * 3, end * 3); - } - } - // Loop handling - if (key == len - 2) { - this._cacheKey = 0; - this._cacheTime = 0; - } - }; - - /** - * @param {number} startTime - * @param {number} endTime - * @return {qtek.animation.SamplerClip} - */ - SamplerClip.prototype.getSubClip = function(startTime, endTime) { - - var subClip = new SamplerClip({ - name: this.name - }); - var minTime = this.channels.time[0]; - startTime = Math.min(Math.max(startTime, minTime), this.life); - endTime = Math.min(Math.max(endTime, minTime), this.life); - - var rangeStart = this._findRange(startTime); - var rangeEnd = this._findRange(endTime); - - var count = rangeEnd[0] - rangeStart[0] + 1; - if (rangeStart[1] === 0 && rangeEnd[1] === 0) { - count -= 1; - } - if (this.channels.rotation) { - subClip.channels.rotation = new Float32Array(count * 4); - } - if (this.channels.position) { - subClip.channels.position = new Float32Array(count * 3); - } - if (this.channels.scale) { - subClip.channels.scale = new Float32Array(count * 3); - } - if (this.channels.time) { - subClip.channels.time = new Float32Array(count); - } - // Clip at the start - this.setTime(startTime); - for (var i = 0; i < 3; i++) { - subClip.channels.rotation[i] = this.rotation[i]; - subClip.channels.position[i] = this.position[i]; - subClip.channels.scale[i] = this.scale[i]; - } - subClip.channels.time[0] = 0; - subClip.channels.rotation[3] = this.rotation[3]; - - for (var i = 1; i < count-1; i++) { - var i2; - for (var j = 0; j < 3; j++) { - i2 = rangeStart[0] + i; - subClip.channels.rotation[i * 4 + j] = this.channels.rotation[i2 * 4 + j]; - subClip.channels.position[i * 3 + j] = this.channels.position[i2 * 3 + j]; - subClip.channels.scale[i * 3 + j] = this.channels.scale[i2 * 3 + j]; - } - subClip.channels.time[i] = this.channels.time[i2] - startTime; - subClip.channels.rotation[i * 4 + 3] = this.channels.rotation[i2 * 4 + 3]; - } - // Clip at the end - this.setTime(endTime); - for (var i = 0; i < 3; i++) { - subClip.channels.rotation[(count - 1) * 4 + i] = this.rotation[i]; - subClip.channels.position[(count - 1) * 3 + i] = this.position[i]; - subClip.channels.scale[(count - 1) * 3 + i] = this.scale[i]; - } - subClip.channels.time[(count - 1)] = endTime - startTime; - subClip.channels.rotation[(count - 1) * 4 + 3] = this.rotation[3]; - - // TODO set back ? - subClip.life = endTime - startTime; - return subClip; - }; - - SamplerClip.prototype._findRange = function(time) { - var channels = this.channels; - var len = channels.time.length; - var start = -1; - for (var i = 0; i < len - 1; i++) { - if (channels.time[i] <= time && channels.time[i+1] > time) { - start = i; - } - } - var percent = 0; - if (start >= 0) { - var startTime = channels.time[start]; - var endTime = channels.time[start+1]; - var percent = (time-startTime) / (endTime-startTime); - } - // Percent [0, 1) - return [start, percent]; - }; - - /** - * 1D blending between two clips - * @method - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 - * @param {number} w - */ - SamplerClip.prototype.blend1D = TransformClip.prototype.blend1D; - /** - * 2D blending between three clips - * @method - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c3 - * @param {number} f - * @param {number} g - */ - SamplerClip.prototype.blend2D = TransformClip.prototype.blend2D; - /** - * Additive blending between two clips - * @method - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 - */ - SamplerClip.prototype.additiveBlend = TransformClip.prototype.additiveBlend; - /** - * Subtractive blending between two clips - * @method - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 - */ - SamplerClip.prototype.subtractiveBlend = TransformClip.prototype.subtractiveBlend; - - /** - * Clone a new SamplerClip - * @return {qtek.animation.SamplerClip} - */ - SamplerClip.prototype.clone = function () { - var clip = Clip.prototype.clone.call(this); - clip.channels = { - time: this.channels.time || null, - position: this.channels.position || null, - rotation: this.channels.rotation || null, - scale: this.channels.scale || null - }; - vec3.copy(clip.position, this.position); - quat.copy(clip.rotation, this.rotation); - vec3.copy(clip.scale, this.scale); - - return clip; - - }; - - return SamplerClip; -}); -define('qtek/animation/SkinningClip',['require','./Clip','./TransformClip','../dep/glmatrix'],function(require) { - - - - var Clip = require('./Clip'); - - var TransformClip = require('./TransformClip'); - - var glMatrix = require('../dep/glmatrix'); - var quat = glMatrix.quat; - var vec3 = glMatrix.vec3; - - /** - * @constructor - * @alias qtek.animation.SkinningClip - * - * @extends qtek.animation.Clip - * @param {Object} [opts] - * @param {string} [opts.name] - * @param {Object} [opts.target] - * @param {number} [opts.life] - * @param {number} [opts.delay] - * @param {number} [opts.gap] - * @param {number} [opts.playbackRatio] - * @param {boolean|number} [opts.loop] If loop is a number, it indicate the loop count of animation - * @param {string|function} [opts.easing] - * @param {function} [opts.onframe] - * @param {function} [opts.ondestroy] - * @param {function} [opts.onrestart] - */ - var SkinningClip = function(opts) { - - opts = opts || {}; - - Clip.call(this, opts); - - this.jointClips = []; - - this.life = 0; - if (opts.jointClips && opts.jointClips.length > 0) { - for (var j = 0; j < opts.jointClips.length; j++) { - var jointPoseCfg = opts.jointClips[j]; - var jointClip = new TransformClip({ - keyFrames: jointPoseCfg.keyFrames - }); - jointClip.name = jointPoseCfg.name || ''; - this.jointClips[j] = jointClip; - - this.life = Math.max(jointClip.life, this.life); - } - } - }; - - SkinningClip.prototype = Object.create(Clip.prototype); - - SkinningClip.prototype.constructor = SkinningClip; - - SkinningClip.prototype.step = function(time) { - - var ret = Clip.prototype.step.call(this, time); - - if (ret !== 'destroy') { - this.setTime(this._elapsedTime); - } - - return ret; - }; - - SkinningClip.prototype.setTime = function(time) { - for (var i = 0; i < this.jointClips.length; i++) { - this.jointClips[i].setTime(time); - } - }; - - /** - * @param {qtek.animation.TransformClip|qtek.animation.SamplerClip} jointClip - */ - SkinningClip.prototype.addJointClip = function(jointClip) { - this.jointClips.push(jointClip); - this.life = Math.max(jointClip.life, this.life); - }; - - /** - * @param {qtek.animation.TransformClip|qtek.animation.SamplerClip} jointClip - */ - SkinningClip.prototype.removeJointClip = function(jointClip) { - this.jointClips.splice(this.jointClips.indexOf(jointClip), 1); - }; - - /** - * @param {number} startTime - * @param {number} endTime - * @param {boolean} isLoop - * @return {qtek.animation.SkinningClip} - */ - SkinningClip.prototype.getSubClip = function(startTime, endTime, isLoop) { - var subClip = new SkinningClip({ - name: this.name - }); - - for (var i = 0; i < this.jointClips.length; i++) { - var subJointClip = this.jointClips[i].getSubClip(startTime, endTime); - subClip.addJointClip(subJointClip); - } - - if (isLoop !== undefined) { - subClip.setLoop(isLoop); - } - - return subClip; - }; - - /** - * 1d blending between two skinning clips - * @param {qtek.animation.SkinningClip} clip1 - * @param {qtek.animation.SkinningClip} clip2 - * @param {number} w - */ - SkinningClip.prototype.blend1D = function(clip1, clip2, w) { - for (var i = 0; i < this.jointClips.length; i++) { - var c1 = clip1.jointClips[i]; - var c2 = clip2.jointClips[i]; - var tClip = this.jointClips[i]; - - tClip.blend1D(c1, c2, w); - } - }; - - /** - * Additive blending between two skinning clips - * @param {qtek.animation.SkinningClip} clip1 - * @param {qtek.animation.SkinningClip} clip2 - */ - SkinningClip.prototype.additiveBlend = function(clip1, clip2) { - for (var i = 0; i < this.jointClips.length; i++) { - var c1 = clip1.jointClips[i]; - var c2 = clip2.jointClips[i]; - var tClip = this.jointClips[i]; - - tClip.additiveBlend(c1, c2); - } - }; - - /** - * Subtractive blending between two skinning clips - * @param {qtek.animation.SkinningClip} clip1 - * @param {qtek.animation.SkinningClip} clip2 - */ - SkinningClip.prototype.subtractiveBlend = function(clip1, clip2) { - for (var i = 0; i < this.jointClips.length; i++) { - var c1 = clip1.jointClips[i]; - var c2 = clip2.jointClips[i]; - var tClip = this.jointClips[i]; - - tClip.subtractiveBlend(c1, c2); - } - }; - - /** - * 2D blending between three skinning clips - * @param {qtek.animation.SkinningClip} clip1 - * @param {qtek.animation.SkinningClip} clip2 - * @param {qtek.animation.SkinningClip} clip3 - * @param {number} f - * @param {number} g - */ - SkinningClip.prototype.blend2D = function(clip1, clip2, clip3, f, g) { - for (var i = 0; i < this.jointClips.length; i++) { - var c1 = clip1.jointClips[i]; - var c2 = clip2.jointClips[i]; - var c3 = clip3.jointClips[i]; - var tClip = this.jointClips[i]; - - tClip.blend2D(c1, c2, c3, f, g); - } - }; - - /** - * Copy SRT of all joints clips from another SkinningClip - * @param {qtek.animation.SkinningClip} clip - */ - SkinningClip.prototype.copy = function(clip) { - for (var i = 0; i < this.jointClips.length; i++) { - var sClip = clip.jointClips[i]; - var tClip = this.jointClips[i]; - - vec3.copy(tClip.position, sClip.position); - vec3.copy(tClip.scale, sClip.scale); - quat.copy(tClip.rotation, sClip.rotation); - } - }; - - SkinningClip.prototype.clone = function () { - var clip = Clip.prototype.clone.call(this); - for (var i = 0; i < this.jointClips.length; i++) { - clip.addJointClip(this.jointClips[i].clone()); - } - return clip; - }; - - return SkinningClip; -}); -define('qtek/core/request',['require'],function(require) { - - - - function get(options) { - - var xhr = new XMLHttpRequest(); - - xhr.open('get', options.url); - // With response type set browser can get and put binary data - // https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest/Sending_and_Receiving_Binary_Data - // Default is text, and it can be set - // arraybuffer, blob, document, json, text - xhr.responseType = options.responseType || 'text'; - - if (options.onprogress) { - //https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest/Using_XMLHttpRequest - xhr.onprogress = function(e) { - if (e.lengthComputable) { - var percent = e.loaded / e.total; - options.onprogress(percent, e.loaded, e.total); - } else { - options.onprogress(null); - } - }; - } - xhr.onload = function(e) { - options.onload && options.onload(xhr.response); - }; - if (options.onerror) { - xhr.onerror = options.onerror; - } - xhr.send(null); - } - - return { - get : get - }; -}); -define('qtek/async/Task',['require','../core/mixin/notifier','../core/request','../core/util'],function(require) { - - - - var notifier = require('../core/mixin/notifier'); - var request = require('../core/request'); - var util = require('../core/util'); - - /** - * @constructor - * @alias qtek.async.Task - * @mixes qtek.core.mixin.notifier - */ - var Task = function() { - this._fullfilled = false; - this._rejected = false; - }; - /** - * Task successed - * @param {} data - */ - Task.prototype.resolve = function(data) { - this._fullfilled = true; - this._rejected = false; - this.trigger('success', data); - }; - /** - * Task failed - * @param {} err - */ - Task.prototype.reject = function(err) { - this._rejected = true; - this._fullfilled = false; - this.trigger('error', err); - }; - /** - * If task successed - * @return {boolean} - */ - Task.prototype.isFullfilled = function() { - return this._fullfilled; - }; - /** - * If task failed - * @return {boolean} - */ - Task.prototype.isRejected = function() { - return this._rejected; - }; - /** - * If task finished, either successed or failed - * @return {boolean} - */ - Task.prototype.isSettled = function() { - return this._fullfilled || this._rejected; - }; - - util.extend(Task.prototype, notifier); - - function makeRequestTask(url, responseType) { - var task = new Task(); - request.get({ - url: url, - responseType: responseType, - onload: function(res) { - task.resolve(res); - }, - onerror: function(error) { - task.reject(error); - } - }); - return task; - } - /** - * Make a request task - * @param {string|object|object[]|string[]} url - * @param {string} [responseType] - * @example - * var task = Task.makeRequestTask('./a.json'); - * var task = Task.makeRequestTask({ - * url: 'b.bin', - * responseType: 'arraybuffer' - * }); - * var tasks = Task.makeRequestTask(['./a.json', './b.json']); - * var tasks = Task.makeRequestTask([ - * {url: 'a.json'}, - * {url: 'b.bin', responseType: 'arraybuffer'} - * ]); - * @return {qtek.async.Task|qtek.async.Task[]} - */ - Task.makeRequestTask = function(url, responseType) { - if (typeof url === 'string') { - return makeRequestTask(url, responseType); - } else if (url.url) { // Configure object - var obj = url; - return makeRequestTask(obj.url, obj.responseType); - } else if (url instanceof Array) { // Url list - var urlList = url; - var tasks = []; - urlList.forEach(function(obj) { - var url, responseType; - if (typeof obj === 'string') { - url = obj; - } else if (Object(obj) === obj) { - url = obj.url; - responseType = obj.responseType; - } - tasks.push(makeRequestTask(url, responseType)); - }); - return tasks; - } - }; - /** - * @return {qtek.async.Task} - */ - Task.makeTask = function() { - return new Task(); - }; - - util.extend(Task.prototype, notifier); - - return Task; -}); -define('qtek/async/TaskGroup',['require','../core/util','./Task'],function(require) { - - - - var util = require('../core/util'); - var Task = require('./Task'); - - /** - * @constructor - * @alias qtek.async.TaskGroup - * @extends qtek.async.Task - */ - var TaskGroup = function() { - - Task.apply(this, arguments); - - this._tasks = []; - - this._fulfilledNumber = 0; - - this._rejectedNumber = 0; - }; - - var Ctor = function(){}; - Ctor.prototype = Task.prototype; - TaskGroup.prototype = new Ctor(); - - TaskGroup.prototype.constructor = TaskGroup; - - /** - * Wait for all given tasks successed, task can also be any notifier object which will trigger success and error events. Like {@link qtek.Texture2D}, {@link qtek.TextureCube}, {@link qtek.loader.GLTF}. - * @param {Array.} tasks - * @chainable - * @example - * // Load texture list - * var list = ['a.jpg', 'b.jpg', 'c.jpg'] - * var textures = list.map(function(src) { - * var texture = new qtek.Texture2D(); - * texture.load(src); - * return texture; - * }); - * var taskGroup = new qtek.async.TaskGroup(); - * taskGroup.all(textures).success(function() { - * // Do some thing after all textures loaded - * }); - */ - TaskGroup.prototype.all = function(tasks) { - var count = 0; - var self = this; - var data = []; - this._tasks = tasks; - this._fulfilledNumber = 0; - this._rejectedNumber = 0; - - util.each(tasks, function(task, idx) { - if (!task || !task.once) { - return; - } - count++; - task.once('success', function(res) { - count--; - - self._fulfilledNumber++; - // TODO - // Some tasks like texture, loader are not inherited from task - // We need to set the states here - task._fulfilled = true; - task._rejected = false; - - data[idx] = res; - if (count === 0) { - self.resolve(data); - } - }); - task.once('error', function() { - - self._rejectedNumber ++; - - task._fulfilled = false; - task._rejected = true; - - self.reject(task); - }); - }); - if (count === 0) { - setTimeout(function() { - self.resolve(data); - }); - return this; - } - return this; - }; - /** - * Wait for all given tasks finished, either successed or failed - * @param {Array.} tasks - * @return {qtek.async.TaskGroup} - */ - TaskGroup.prototype.allSettled = function(tasks) { - var count = 0; - var self = this; - var data = []; - if (tasks.length === 0) { - setTimeout(function() { - self.trigger('success', data); - }); - return this; - } - this._tasks = tasks; - - util.each(tasks, function(task, idx) { - if (!task || !task.once) { - return; - } - count++; - task.once('success', function(res) { - count--; - - self._fulfilledNumber++; - - task._fulfilled = true; - task._rejected = false; - - data[idx] = res; - if (count === 0) { - self.resolve(data); - } - }); - task.once('error', function(err) { - count--; - - self._rejectedNumber++; - - task._fulfilled = false; - task._rejected = true; - - // TODO - data[idx] = null; - if (count === 0) { - self.resolve(data); - } - }); - }); - return this; - }; - /** - * Get successed sub tasks number, recursive can be true if sub task is also a TaskGroup. - * @param {boolean} [recursive] - * @return {number} - */ - TaskGroup.prototype.getFulfilledNumber = function(recursive) { - if (recursive) { - var nFulfilled = 0; - for (var i = 0; i < this._tasks.length; i++) { - var task = this._tasks[i]; - if (task instanceof TaskGroup) { - nFulfilled += task.getFulfilledNumber(recursive); - } else if(task._fulfilled) { - nFulfilled += 1; - } - } - return nFulfilled; - } else { - return this._fulfilledNumber; - } - }; - - /** - * Get failed sub tasks number, recursive can be true if sub task is also a TaskGroup. - * @param {boolean} [recursive] - * @return {number} - */ - TaskGroup.prototype.getRejectedNumber = function(recursive) { - if (recursive) { - var nRejected = 0; - for (var i = 0; i < this._tasks.length; i++) { - var task = this._tasks[i]; - if (task instanceof TaskGroup) { - nRejected += task.getRejectedNumber(recursive); - } else if(task._rejected) { - nRejected += 1; - } - } - return nRejected; - } else { - return this._rejectedNumber; - } - }; - - /** - * Get finished sub tasks number, recursive can be true if sub task is also a TaskGroup. - * @param {boolean} [recursive] - * @return {number} - */ - TaskGroup.prototype.getSettledNumber = function(recursive) { - - if (recursive) { - var nSettled = 0; - for (var i = 0; i < this._tasks.length; i++) { - var task = this._tasks[i]; - if (task instanceof TaskGroup) { - nSettled += task.getSettledNumber(recursive); - } else if(task._rejected || task._fulfilled) { - nSettled += 1; - } - } - return nSettled; - } else { - return this._fulfilledNumber + this._rejectedNumber; - } - }; - - /** - * Get all sub tasks number, recursive can be true if sub task is also a TaskGroup. - * @param {boolean} [recursive] - * @return {number} - */ - TaskGroup.prototype.getTaskNumber = function(recursive) { - if (recursive) { - var nTask = 0; - for (var i = 0; i < this._tasks.length; i++) { - var task = this._tasks[i]; - if (task instanceof TaskGroup) { - nTask += task.getTaskNumber(recursive); - } else { - nTask += 1; - } - } - return nTask; - } else { - return this._tasks.length; - } - }; - - return TaskGroup; -}); -define('qtek/camera/Orthographic',['require','../Camera'],function(require) { - - - - var Camera = require('../Camera'); - /** - * @constructor qtek.camera.Orthographic - * @extends qtek.Camera - */ - var Orthographic = Camera.derive( - /** @lends qtek.camera.Orthographic# */ - { - /** - * @type {number} - */ - left: -1, - /** - * @type {number} - */ - right: 1, - /** - * @type {number} - */ - near: -1, - /** - * @type {number} - */ - far: 1, - /** - * @type {number} - */ - top: 1, - /** - * @type {number} - */ - bottom: -1 - }, - /** @lends qtek.camera.Orthographic.prototype */ - { - - updateProjectionMatrix: function() { - this.projectionMatrix.ortho(this.left, this.right, this.bottom, this.top, this.near, this.far); - }, - /** - * @return {qtek.camera.Orthographic} - */ - clone: function() { - var camera = Camera.prototype.clone.call(this); - camera.left = this.left; - camera.right = this.right; - camera.near = this.near; - camera.far = this.far; - camera.top = this.top; - camera.bottom = this.bottom; - - return camera; - } - }); - - return Orthographic; -}); -define('qtek/camera/Perspective',['require','../Camera'],function(require) { - - - - var Camera = require('../Camera'); - - /** - * @constructor qtek.camera.Perspective - * @extends qtek.Camera - */ - var Perspective = Camera.derive( - /** @lends qtek.camera.Perspective# */ - { - /** - * @type {number} - */ - fov: 50, - /** - * @type {number} - */ - aspect: 1, - /** - * @type {number} - */ - near: 0.1, - /** - * @type {number} - */ - far: 2000 - }, - /** @lends qtek.camera.Perspective.prototype */ - { - - updateProjectionMatrix: function() { - var rad = this.fov / 180 * Math.PI; - this.projectionMatrix.perspective(rad, this.aspect, this.near, this.far); - }, - /** - * @return {qtek.camera.Perspective} - */ - clone: function() { - var camera = Camera.prototype.clone.call(this); - camera.fov = this.fov; - camera.aspect = this.aspect; - camera.near = this.near; - camera.far = this.far; - - return camera; - } - }); - - return Perspective; -}); -define('qtek/compositor/Graph',['require','../core/Base'],function(require) { - - - - var Base = require('../core/Base'); - - /** - * @constructor qtek.compositor.Graph - * @extends qtek.core.Base - */ - var Graph = Base.derive(function() { - return /** @lends qtek.compositor.Graph# */ { - /** - * @type {Array.} - */ - nodes: [] - }; - }, - /** @lends qtek.compositor.Graph.prototype */ - { - /** - * @param {qtek.compositor.Node} node - */ - addNode: function(node) { - - this.nodes.push(node); - - this._dirty = true; - }, - /** - * @param {qtek.compositor.Node} node - */ - removeNode: function(node) { - this.nodes.splice(this.nodes.indexOf(node), 1); - - this._dirty = true; - }, - /** - * @param {string} name - * @return {qtek.compositor.Node} - */ - findNode: function(name) { - for (var i = 0; i < this.nodes.length; i++) { - if (this.nodes[i].name === name) { - return this.nodes[i]; - } - } - }, - /** - * Update links of graph - */ - update: function() { - for (var i = 0; i < this.nodes.length; i++) { - this.nodes[i].clear(); - } - // Traverse all the nodes and build the graph - for (var i = 0; i < this.nodes.length; i++) { - var node = this.nodes[i]; - - if (!node.inputs) { - continue; - } - for (var inputName in node.inputs) { - var fromPinInfo = node.inputs[inputName]; - - var fromPin = this.findPin(fromPinInfo); - if (fromPin) { - node.link(inputName, fromPin.node, fromPin.pin); - }else{ - console.warn('Pin of ' + fromPinInfo.node + '.' + fromPinInfo.pin + ' not exist'); - } - } - } - }, - - findPin: function(input) { - var node; - if (typeof(input.node) === 'string') { - for (var i = 0; i < this.nodes.length; i++) { - var tmp = this.nodes[i]; - if (tmp.name === input.node) { - node = tmp; - } - } - } else { - node = input.node; - } - if (node) { - if (node.outputs[input.pin]) { - return { - node: node, - pin: input.pin - }; - } - } - } - }); - - return Graph; -}); -define('qtek/compositor/TexturePool',['require','../Texture2D','../core/glenum','../core/util'],function(require) { - - - - var Texture2D = require('../Texture2D'); - var glenum = require('../core/glenum'); - var util = require('../core/util'); - - var TexturePool = function () { - - this._pool = {}; - - this._allocatedTextures = []; - }; - - TexturePool.prototype = { - - constructor: TexturePool, - - get: function(parameters) { - var key = generateKey(parameters); - if (!this._pool.hasOwnProperty(key)) { - this._pool[key] = []; - } - var list = this._pool[key]; - if (!list.length) { - var texture = new Texture2D(parameters); - this._allocatedTextures.push(texture); - return texture; - } - return list.pop(); - }, - - put: function(texture) { - var key = generateKey(texture); - if (!this._pool.hasOwnProperty(key)) { - this._pool[key] = []; - } - var list = this._pool[key]; - list.push(texture); - }, - - clear: function(gl) { - for (var i = 0; i < this._allocatedTextures.length; i++) { - this._allocatedTextures[i].dispose(gl); - } - this._pool = {}; - this._allocatedTextures = []; - } - }; - - var defaultParams = { - width: 512, - height: 512, - type: glenum.UNSIGNED_BYTE, - format: glenum.RGBA, - wrapS: glenum.CLAMP_TO_EDGE, - wrapT: glenum.CLAMP_TO_EDGE, - minFilter: glenum.LINEAR_MIPMAP_LINEAR, - magFilter: glenum.LINEAR, - useMipmap: true, - anisotropic: 1, - flipY: true, - unpackAlignment: 4, - premultiplyAlpha: false - }; - - var defaultParamPropList = Object.keys(defaultParams); - - function generateKey(parameters) { - util.defaultsWithPropList(parameters, defaultParams, defaultParamPropList); - fallBack(parameters); - - var key = ''; - for (var i = 0; i < defaultParamPropList.length; i++) { - var name = defaultParamPropList[i]; - var chunk = parameters[name].toString(); - key += chunk; - } - return key; - } - - function fallBack(target) { - - var IPOT = isPowerOfTwo(target.width, target.height); - - if (target.format === glenum.DEPTH_COMPONENT) { - target.useMipmap = false; - } - - if (!IPOT || !target.useMipmap) { - if (target.minFilter == glenum.NEAREST_MIPMAP_NEAREST || - target.minFilter == glenum.NEAREST_MIPMAP_LINEAR) { - target.minFilter = glenum.NEAREST; - } else if ( - target.minFilter == glenum.LINEAR_MIPMAP_LINEAR || - target.minFilter == glenum.LINEAR_MIPMAP_NEAREST - ) { - target.minFilter = glenum.LINEAR; - } - - target.wrapS = glenum.CLAMP_TO_EDGE; - target.wrapT = glenum.CLAMP_TO_EDGE; - } - } - - function isPowerOfTwo(width, height) { - return (width & (width-1)) === 0 && - (height & (height-1)) === 0; - } - - return TexturePool; -}); -define('qtek/compositor/Compositor',['require','./Graph','./TexturePool'],function(require){ - - - - var Graph = require('./Graph'); - var TexturePool = require('./TexturePool'); - - /** - * Compositor provide graph based post processing - * - * @constructor qtek.compositor.Compositor - * @extends qtek.compositor.Graph - * - */ - var Compositor = Graph.derive(function() { - return { - // Output node - _outputs: [], - - _texturePool: new TexturePool() - }; - }, - /** @lends qtek.compositor.Compositor.prototype */ - { - addNode: function(node) { - Graph.prototype.addNode.call(this, node); - if (!node.outputs) { - this.addOutput(node); - } - node._compositor = this; - }, - /** - * @param {qtek.Renderer} renderer - */ - render: function(renderer, frameBuffer) { - if (this._dirty) { - this.update(); - this._dirty = false; - } - for (var i = 0; i < this.nodes.length; i++) { - // Update the reference number of each output texture - this.nodes[i].beforeFrame(); - } - - for (var i = 0; i < this._outputs.length; i++) { - this._outputs[i].updateReference(); - } - for (var i = 0; i < this._outputs.length; i++) { - this._outputs[i].render(renderer, frameBuffer); - } - - for (var i = 0; i < this.nodes.length; i++) { - // Clear up - this.nodes[i].afterFrame(); - } - }, - - addOutput: function(node) { - if (this._outputs.indexOf(node) < 0) { - this._outputs.push(node); - } - }, - - removeOutput: function(node) { - this._outputs.splice(this._outputs.indexOf(node), 1); - }, - - allocateTexture: function (parameters) { - return this._texturePool.get(parameters); - }, - - releaseTexture: function (parameters) { - this._texturePool.put(parameters); - }, - - dispose: function (renderer) { - this._texturePool.clear(renderer.gl); - } - }); - - return Compositor; -}); -define('qtek/geometry/Plane',['require','../DynamicGeometry','../math/BoundingBox'],function(require) { - - - - var DynamicGeometry = require('../DynamicGeometry'); - var BoundingBox = require('../math/BoundingBox'); - - /** - * @constructor qtek.geometry.Plane - * @extends qtek.DynamicGeometry - * @param {Object} [opt] - * @param {number} [opt.widthSegments] - * @param {number} [opt.heightSegments] - */ - var Plane = DynamicGeometry.derive( - /** @lends qtek.geometry.Plane# */ - { - /** - * @type {number} - */ - widthSegments: 1, - /** - * @type {number} - */ - heightSegments: 1 - }, function() { - this.build(); - }, - /** @lends qtek.geometry.Plane.prototype */ - { - /** - * Build plane geometry - */ - build: function() { - var heightSegments = this.heightSegments; - var widthSegments = this.widthSegments; - var positions = this.attributes.position.value; - var texcoords = this.attributes.texcoord0.value; - var normals = this.attributes.normal.value; - var faces = this.faces; - - positions.length = 0; - texcoords.length = 0; - normals.length = 0; - faces.length = 0; - - for (var y = 0; y <= heightSegments; y++) { - var t = y / heightSegments; - for (var x = 0; x <= widthSegments; x++) { - var s = x / widthSegments; - - positions.push([2 * s - 1, 2 * t - 1, 0]); - if (texcoords) { - texcoords.push([s, t]); - } - if (normals) { - normals.push([0, 0, 1]); - } - if (x < widthSegments && y < heightSegments) { - var i = x + y * (widthSegments + 1); - faces.push([i, i + 1, i + widthSegments + 1]); - faces.push([i + widthSegments + 1, i + 1, i + widthSegments + 2]); - } - } - } - - this.boundingBox = new BoundingBox(); - this.boundingBox.min.set(-1, -1, 0); - this.boundingBox.max.set(1, 1, 0); - } - }); - - return Plane; -}); -define('qtek/shader/source/compositor/vertex.essl',[],function () { return '\n@export buildin.compositor.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n\nvarying vec2 v_Texcoord;\n\nvoid main()\n{\n v_Texcoord = texcoord;\n gl_Position = worldViewProjection * vec4(position, 1.0);\n}\n\n@end';}); - -define('qtek/compositor/Pass',['require','../core/Base','../camera/Orthographic','../geometry/Plane','../Shader','../Material','../Mesh','../core/glinfo','../core/glenum','../shader/source/compositor/vertex.essl'],function(require) { - - - - var Base = require('../core/Base'); - var OrthoCamera = require('../camera/Orthographic'); - var Plane = require('../geometry/Plane'); - var Shader = require('../Shader'); - var Material = require('../Material'); - var Mesh = require('../Mesh'); - var glinfo = require('../core/glinfo'); - var glenum = require('../core/glenum'); - - Shader['import'](require('../shader/source/compositor/vertex.essl')); - - var planeGeo = new Plane(); - var mesh = new Mesh({ - geometry : planeGeo - }); - var camera = new OrthoCamera(); - - /** - * @constructor qtek.compositor.Pass - * @extends qtek.core.Base - */ - var Pass = Base.derive(function() { - return /** @lends qtek.compositor.Pass# */ { - /** - * Fragment shader string - * @type {string} - */ - fragment : '', - - /** - * @type {Object} - */ - outputs : null, - - /** - * @type {qtek.Material} - */ - material : null - }; - }, function() { - - var shader = new Shader({ - vertex : Shader.source('buildin.compositor.vertex'), - fragment : this.fragment - }); - var material = new Material({ - shader : shader - }); - shader.enableTexturesAll(); - - this.material = material; - - }, - /** @lends qtek.compositor.Pass.prototype */ - { - /** - * @param {string} name - * @param {} value - */ - setUniform : function(name, value) { - var uniform = this.material.uniforms[name]; - if (uniform) { - uniform.value = value; - } - }, - /** - * @param {string} name - * @return {} - */ - getUniform : function(name) { - var uniform = this.material.uniforms[name]; - if (uniform) { - return uniform.value; - } - }, - /** - * @param {qtek.Texture} texture - * @param {number} attachment - */ - attachOutput : function(texture, attachment) { - if (!this.outputs) { - this.outputs = {}; - } - attachment = attachment || glenum.COLOR_ATTACHMENT0; - this.outputs[attachment] = texture; - }, - /** - * @param {qtek.Texture} texture - */ - detachOutput : function(texture) { - for (var attachment in this.outputs) { - if (this.outputs[attachment] === texture) { - this.outputs[attachment] = null; - } - } - }, - - bind : function(renderer, frameBuffer) { - - if (this.outputs) { - for (var attachment in this.outputs) { - var texture = this.outputs[attachment]; - if (texture) { - frameBuffer.attach(renderer.gl, texture, attachment); - } - } - } - - if (frameBuffer) { - frameBuffer.bind(renderer); - } - }, - - unbind : function(renderer, frameBuffer) { - frameBuffer.unbind(renderer); - }, - /** - * @param {qtek.Renderer} renderer - * @param {qtek.FrameBuffer} [frameBuffer] - */ - render : function(renderer, frameBuffer) { - - var _gl = renderer.gl; - - if (frameBuffer) { - this.bind(renderer, frameBuffer); - // MRT Support in chrome - // https://www.khronos.org/registry/webgl/sdk/tests/conformance/extensions/ext-draw-buffers.html - var ext = glinfo.getExtension(_gl, 'EXT_draw_buffers'); - if (ext && this.outputs) { - var bufs = []; - for (var attachment in this.outputs) { - attachment = +attachment; - if (attachment >= _gl.COLOR_ATTACHMENT0 && attachment <= _gl.COLOR_ATTACHMENT0 + 8) { - bufs.push(attachment); - } - } - ext.drawBuffersEXT(bufs); - } - } - - this.trigger('beforerender', this, renderer); - // Don't clear in each pass, let the color overwrite the buffer - // PENDING Transparent ? - _gl.disable(_gl.BLEND); - _gl.clear(_gl.DEPTH_BUFFER_BIT); - this.renderQuad(renderer); - this.trigger('afterrender', this, renderer); - - if (frameBuffer) { - this.unbind(renderer, frameBuffer); - } - }, - - /** - * Simply do quad rendering - */ - renderQuad: function (renderer) { - mesh.material = this.material; - renderer.renderQueue([mesh], camera); - } - }); - - return Pass; -}); -define('qtek/compositor/Node',['require','../core/Base','./Pass','../FrameBuffer'],function(require) { - - - - var Base = require('../core/Base'); - var Pass = require('./Pass'); - var FrameBuffer = require('../FrameBuffer'); - - /** - * Node of graph based post processing. - * - * @constructor qtek.compositor.Node - * @extends qtek.core.Base - * - * @example - var node = new qtek.compositor.Node({ - name: 'fxaa', - shader: qtek.Shader.source('buildin.compositor.fxaa'), - inputs: { - texture: { - node: 'scene', - pin: 'color' - } - }, - // Multiple outputs is preserved for MRT support in WebGL2.0 - outputs: { - color: { - attachment: qtek.FrameBuffer.COLOR_ATTACHMENT0 - parameters: { - format: qtek.Texture.RGBA, - width: 512, - height: 512 - }, - // Node will keep the RTT rendered in last frame - keepLastFrame: true, - // Force the node output the RTT rendered in last frame - outputLastFrame: true - } - } - }); - * - */ - var Node = Base.derive(function() { - return /** @lends qtek.compositor.Node# */ { - /** - * @type {string} - */ - name: '', - - /** - * @type {Object} - */ - inputs: {}, - - /** - * @type {Object} - */ - outputs: null, - - /** - * @type {string} - */ - shader: '', - - /** - * Input links, will be updated by the graph - * @example: - * inputName: { - * node: someNode, - * pin: 'xxxx' - * } - * @type {Object} - */ - inputLinks: {}, - - /** - * Output links, will be updated by the graph - * @example: - * outputName: { - * node: someNode, - * pin: 'xxxx' - * } - * @type {Object} - */ - outputLinks: {}, - - /** - * @type {qtek.compositor.Pass} - */ - pass: null, - - // Save the output texture of previous frame - // Will be used when there exist a circular reference - _prevOutputTextures: {}, - _outputTextures: {}, - - // Example: { name: 2 } - _outputReferences: {}, - - _rendering: false, - // If rendered in this frame - _rendered: false, - - _compositor: null - }; - }, function() { - - var pass = new Pass({ - fragment: this.shader - }); - this.pass = pass; - - if (this.outputs) { - this.frameBuffer = new FrameBuffer({ - depthBuffer: false - }); - } - }, - /** @lends qtek.compositor.Node.prototype */ - { - /** - * @param {qtek.Renderer} renderer - */ - render: function(renderer, frameBuffer) { - this.trigger('beforerender', renderer); - - this._rendering = true; - - var _gl = renderer.gl; - - for (var inputName in this.inputLinks) { - var link = this.inputLinks[inputName]; - var inputTexture = link.node.getOutput(renderer, link.pin); - this.pass.setUniform(inputName, inputTexture); - } - // Output - if (! this.outputs) { - this.pass.outputs = null; - this.pass.render(renderer, frameBuffer); - } else { - this.pass.outputs = {}; - - for (var name in this.outputs) { - var parameters = this.updateParameter(name, renderer); - var outputInfo = this.outputs[name]; - var texture = this._compositor.allocateTexture(parameters); - this._outputTextures[name] = texture; - var attachment = outputInfo.attachment || _gl.COLOR_ATTACHMENT0; - if (typeof(attachment) == 'string') { - attachment = _gl[attachment]; - } - this.pass.outputs[attachment] = texture; - } - - this.pass.render(renderer, this.frameBuffer); - } - - for (var inputName in this.inputLinks) { - var link = this.inputLinks[inputName]; - link.node.removeReference(link.pin); - } - - this._rendering = false; - this._rendered = true; - - this.trigger('afterrender', renderer); - }, - - // TODO Remove parameter function callback - updateParameter: function(outputName, renderer) { - var outputInfo = this.outputs[outputName]; - var parameters = outputInfo.parameters; - var parametersCopy = outputInfo._parametersCopy; - if (!parametersCopy) { - parametersCopy = outputInfo._parametersCopy = {}; - } - if (parameters) { - for (var key in parameters) { - if (key !== 'width' && key !== 'height') { - parametersCopy[key] = parameters[key]; - } - } - } - var width, height; - if (parameters.width instanceof Function) { - width = parameters.width(renderer); - } else { - width = parameters.width; - } - if (parameters.height instanceof Function) { - height = parameters.height(renderer); - } else { - height = parameters.height; - } - if ( - parametersCopy.width !== width - || parametersCopy.height !== height - ) { - if (this._outputTextures[outputName]) { - this._outputTextures[outputName].dispose(renderer.gl); - } - } - parametersCopy.width = width; - parametersCopy.height = height; - - return parametersCopy; - }, - - /** - * Set parameter - * @param {string} name - * @param {} value - */ - setParameter: function(name, value) { - this.pass.setUniform(name, value); - }, - /** - * Get parameter value - * @param {string} name - * @return {} - */ - getParameter: function(name) { - return this.pass.getUniform(name); - }, - /** - * Set parameters - * @param {Object} obj - */ - setParameters: function(obj) { - for (var name in obj) { - this.setParameter(name, obj[name]); - } - }, - /** - * Set shader code - * @param {string} shaderStr - */ - setShader: function(shaderStr) { - var material = this.pass.material; - material.shader.setFragment(shaderStr); - material.attachShader(material.shader, true); - }, - - getOutput: function(renderer /*optional*/, name) { - if (name === undefined) { - // Return the output texture without rendering - name = renderer; - return this._outputTextures[name]; - } - var outputInfo = this.outputs[name]; - if (! outputInfo) { - return ; - } - - // Already been rendered in this frame - if (this._rendered) { - // Force return texture in last frame - if (outputInfo.outputLastFrame) { - return this._prevOutputTextures[name]; - } else { - return this._outputTextures[name]; - } - } else if ( - // TODO - this._rendering // Solve Circular Reference - ) { - if (!this._prevOutputTextures[name]) { - // Create a blank texture at first pass - this._prevOutputTextures[name] = this._compositor.allocateTexture(outputInfo.parameters || {}); - } - return this._prevOutputTextures[name]; - } - - this.render(renderer); - - return this._outputTextures[name]; - }, - - removeReference: function(outputName) { - this._outputReferences[outputName]--; - if (this._outputReferences[outputName] === 0) { - var outputInfo = this.outputs[outputName]; - if (outputInfo.keepLastFrame) { - if (this._prevOutputTextures[outputName]) { - this._compositor.releaseTexture(this._prevOutputTextures[outputName]); - } - this._prevOutputTextures[outputName] = this._outputTextures[outputName]; - } else { - // Output of this node have alreay been used by all other nodes - // Put the texture back to the pool. - this._compositor.releaseTexture(this._outputTextures[outputName]); - } - } - }, - - link: function(inputPinName, fromNode, fromPinName) { - - // The relationship from output pin to input pin is one-on-multiple - this.inputLinks[inputPinName] = { - node: fromNode, - pin: fromPinName - }; - if (! fromNode.outputLinks[fromPinName]) { - fromNode.outputLinks[fromPinName] = []; - } - fromNode.outputLinks[ fromPinName ].push({ - node: this, - pin: inputPinName - }); - // Enabled the pin texture in shader - var shader = this.pass.material.shader; - shader.enableTexture(inputPinName); - }, - - clear: function() { - this.inputLinks = {}; - this.outputLinks = {}; - - var shader = this.pass.material.shader; - shader.disableTexturesAll(); - }, - - updateReference: function(outputName) { - if (!this._rendering) { - this._rendering = true; - for (var inputName in this.inputLinks) { - var link = this.inputLinks[inputName]; - link.node.updateReference(link.pin); - } - this._rendering = false; - } - if (outputName) { - this._outputReferences[outputName] ++; - } - }, - - beforeFrame: function() { - this._rendered = false; - - for (var name in this.outputLinks) { - this._outputReferences[name] = 0; - } - }, - - afterFrame: function() { - // Put back all the textures to pool - for (var name in this.outputLinks) { - if (this._outputReferences[name] > 0) { - var outputInfo = this.outputs[name]; - if (outputInfo.keepLastFrame) { - if (this._prevOutputTextures[name]) { - this._compositor.releaseTexture(this._prevOutputTextures[name]); - } - this._prevOutputTextures[name] = this._outputTextures[name]; - } else { - this._compositor.releaseTexture(this._outputTextures[name]); - } - } - } - } - }); - - return Node; -}); -define('qtek/compositor/SceneNode',['require','./Node','../core/glinfo'],function(require) { - - - - var Node = require('./Node'); - var glinfo = require('../core/glinfo'); - - /** - * @constructor qtek.compositor.SceneNode - * @extends qtek.compositor.Node - */ - var SceneNode = Node.derive( - /** @lends qtek.compositor.SceneNode# */ - { - name: 'scene', - /** - * @type {qtek.Scene} - */ - scene: null, - /** - * @type {qtek.Camera} - */ - camera: null, - /** - * @type {boolean} - */ - autoUpdateScene: true, - /** - * @type {boolean} - */ - preZ: false - - }, function() { - if (this.frameBuffer) { - this.frameBuffer.depthBuffer = true; - } - }, { - render: function(renderer) { - - this._rendering = true; - var _gl = renderer.gl; - - this.trigger('beforerender'); - - var renderInfo; - - if (!this.outputs) { - - renderInfo = renderer.render(this.scene, this.camera, !this.autoUpdateScene, this.preZ); - - } else { - - var frameBuffer = this.frameBuffer; - for (var name in this.outputs) { - var parameters = this.updateParameter(name, renderer); - var outputInfo = this.outputs[name]; - var texture = this._compositor.allocateTexture(parameters); - this._outputTextures[name] = texture; - - var attachment = outputInfo.attachment || _gl.COLOR_ATTACHMENT0; - if (typeof(attachment) == 'string') { - attachment = _gl[attachment]; - } - frameBuffer.attach(renderer.gl, texture, attachment); - } - frameBuffer.bind(renderer); - - // MRT Support in chrome - // https://www.khronos.org/registry/webgl/sdk/tests/conformance/extensions/ext-draw-buffers.html - var ext = glinfo.getExtension(_gl, 'EXT_draw_buffers'); - if (ext) { - var bufs = []; - for (var attachment in this.outputs) { - attachment = parseInt(attachment); - if (attachment >= _gl.COLOR_ATTACHMENT0 && attachment <= _gl.COLOR_ATTACHMENT0 + 8) { - bufs.push(attachment); - } - } - ext.drawBuffersEXT(bufs); - } - - renderInfo = renderer.render(this.scene, this.camera, !this.autoUpdateScene, this.preZ); - - frameBuffer.unbind(renderer); - } - - this.trigger('afterrender', renderInfo); - - this._rendering = false; - this._rendered = true; - } - }); - - return SceneNode; -}); -define('qtek/compositor/TextureNode',['require','./Node','../Shader'],function(require) { - - - - var Node = require('./Node'); - var Shader = require('../Shader'); - - /** - * @constructor qtek.compositor.TextureNode - * @extends qtek.compositor.Node - */ - var TextureNode = Node.derive(function() { - return /** @lends qtek.compositor.TextureNode# */ { - shader: Shader.source('buildin.compositor.output'), - /** - * @type {qtek.Texture2D} - */ - texture: null - }; - }, { - render: function(renderer, frameBuffer) { - - this._rendering = true; - - var _gl = renderer.gl; - this.pass.setUniform('texture', this.texture); - - if (! this.outputs) { - this.pass.outputs = null; - this.pass.render(renderer, frameBuffer); - } else { - - this.pass.outputs = {}; - - for (var name in this.outputs) { - var parameters = this.updateParameter(name, renderer); - var outputInfo = this.outputs[name]; - var texture = this._compositor.allocateTexture(parameters); - this._outputTextures[name] = texture; - - var attachment = outputInfo.attachment || _gl.COLOR_ATTACHMENT0; - if (typeof(attachment) == 'string') { - attachment = _gl[attachment]; - } - this.pass.outputs[attachment] = texture; - - } - - this.pass.render(renderer, this.frameBuffer); - } - - this._rendering = false; - this._rendered = true; - } - }); - - return TextureNode; -}); -define('qtek/core/Event',['require','./Base'], function(require) { - - - - var Base = require('./Base'); - - var QEvent = Base.derive({ - cancelBubble : false - }, { - stopPropagation : function() { - this.cancelBubble = true; - } - }); - - QEvent['throw'] = function(eventType, target, props) { - - var e = new QEvent(props); - - e.type = eventType; - e.target = target; - - // enable bubbling - while (target && !e.cancelBubble) { - e.currentTarget = target; - target.trigger(eventType, e); - - target = target.getParent(); - } - }; - - return QEvent; -}); -define('qtek/core/LinkedList',['require'],function(require) { - - - - /** - * Simple double linked list. Compared with array, it has O(1) remove operation. - * @constructor - * @alias qtek.core.LinkedList - */ - var LinkedList = function() { - - /** - * @type {qtek.core.LinkedList.Entry} - */ - this.head = null; - - /** - * @type {qtek.core.LinkedList.Entry} - */ - this.tail = null; - - this._length = 0; - }; - - /** - * Insert a new value at the tail - * @param {} val - * @return {qtek.core.LinkedList.Entry} - */ - LinkedList.prototype.insert = function(val) { - var entry = new LinkedList.Entry(val); - this.insertEntry(entry); - return entry; - }; - - /** - * Insert a new value at idx - * @param {number} idx - * @param {} val - * @return {qtek.core.LinkedList.Entry} - */ - LinkedList.prototype.insertAt = function(idx, val) { - if (idx < 0) { - return; - } - var next = this.head; - var cursor = 0; - while (next && cursor != idx) { - next = next.next; - cursor++; - } - if (next) { - var entry = new LinkedList.Entry(val); - var prev = next.prev; - prev.next = entry; - entry.prev = prev; - entry.next = next; - next.prev = entry; - } else { - this.insert(val); - } - }; - - /** - * Insert an entry at the tail - * @param {qtek.core.LinkedList.Entry} entry - */ - LinkedList.prototype.insertEntry = function(entry) { - if (!this.head) { - this.head = this.tail = entry; - } else { - this.tail.next = entry; - entry.prev = this.tail; - this.tail = entry; - } - this._length++; - }; - - /** - * Remove entry. - * @param {qtek.core.LinkedList.Entry} entry - */ - LinkedList.prototype.remove = function(entry) { - var prev = entry.prev; - var next = entry.next; - if (prev) { - prev.next = next; - } else { - // Is head - this.head = next; - } - if (next) { - next.prev = prev; - } else { - // Is tail - this.tail = prev; - } - entry.next = entry.prev = null; - this._length--; - }; - - /** - * Remove entry at index. - * @param {number} idx - * @return {} - */ - LinkedList.prototype.removeAt = function(idx) { - if (idx < 0) { - return; - } - var curr = this.head; - var cursor = 0; - while (curr && cursor != idx) { - curr = curr.next; - cursor++; - } - if (curr) { - this.remove(curr); - return curr.value; - } - }; - /** - * Get head value - * @return {} - */ - LinkedList.prototype.getHead = function() { - if (this.head) { - return this.head.value; - } - }; - /** - * Get tail value - * @return {} - */ - LinkedList.prototype.getTail = function() { - if (this.tail) { - return this.tail.value; - } - }; - /** - * Get value at idx - * @param {number} idx - * @return {} - */ - LinkedList.prototype.getAt = function(idx) { - if (idx < 0) { - return; - } - var curr = this.head; - var cursor = 0; - while (curr && cursor != idx) { - curr = curr.next; - cursor++; - } - return curr.value; - }; - - /** - * @param {} value - * @return {number} - */ - LinkedList.prototype.indexOf = function(value) { - var curr = this.head; - var cursor = 0; - while (curr) { - if (curr.value === value) { - return cursor; - } - curr = curr.next; - cursor++; - } - }; - - /** - * @return {number} - */ - LinkedList.prototype.length = function() { - return this._length; - }; - - /** - * If list is empty - */ - LinkedList.prototype.isEmpty = function() { - return this._length === 0; - }; - - /** - * @param {Function} cb - * @param {} context - */ - LinkedList.prototype.forEach = function(cb, context) { - var curr = this.head; - var idx = 0; - var haveContext = typeof(context) != 'undefined'; - while (curr) { - if (haveContext) { - cb.call(context, curr.value, idx); - } else { - cb(curr.value, idx); - } - curr = curr.next; - idx++; - } - }; - - /** - * Clear the list - */ - LinkedList.prototype.clear = function() { - this.tail = this.head = null; - this._length = 0; - }; - - /** - * @constructor - * @param {} val - */ - LinkedList.Entry = function(val) { - /** - * @type {} - */ - this.value = val; - - /** - * @type {qtek.core.LinkedList.Entry} - */ - this.next = null; - - /** - * @type {qtek.core.LinkedList.Entry} - */ - this.prev = null; - }; - - return LinkedList; -}); -define('qtek/core/LRU',['require','./LinkedList'],function(require) { - - - - var LinkedList = require('./LinkedList'); - - /** - * LRU Cache - * @constructor - * @alias qtek.core.LRU - */ - var LRU = function(maxSize) { - - this._list = new LinkedList(); - - this._map = {}; - - this._maxSize = maxSize || 10; - }; - - /** - * Set cache max size - * @param {number} size - */ - LRU.prototype.setMaxSize = function(size) { - this._maxSize = size; - }; - - /** - * @param {string} key - * @param {} value - */ - LRU.prototype.put = function(key, value) { - if (typeof(this._map[key]) == 'undefined') { - var len = this._list.length(); - if (len >= this._maxSize && len > 0) { - // Remove the least recently used - var leastUsedEntry = this._list.head; - this._list.remove(leastUsedEntry); - delete this._map[leastUsedEntry.key]; - } - - var entry = this._list.insert(value); - entry.key = key; - this._map[key] = entry; - } - }; - - /** - * @param {string} key - * @return {} - */ - LRU.prototype.get = function(key) { - var entry = this._map[key]; - if (typeof(entry) != 'undefined') { - // Put the latest used entry in the tail - if (entry !== this._list.tail) { - this._list.remove(entry); - this._list.insertEntry(entry); - } - - return entry.value; - } - }; - - /** - * @param {string} key - */ - LRU.prototype.remove = function(key) { - var entry = this._map[key]; - if (typeof(entry) != 'undefined') { - delete this._map[key]; - this._list.remove(entry); - } - }; - - /** - * Clear the cache - */ - LRU.prototype.clear = function() { - this._list.clear(); - this._map = {}; - }; - - return LRU; -}); -define('qtek/deferred/StandardMaterial',['require','../core/Base'],function (require) { - - - - var Base = require('../core/Base'); - - var DeferredDummyShader = function () {}; - - var StandardMaterial = Base.derive({ - - /** - * @type {Array.} - * @default [1, 1, 1] - */ - color: null, - - /** - * @type {Array.} - * @default [0, 0, 0] - */ - emission: null, - - /** - * @type {Array.} - * @default [0.5, 0.5, 0.5] - */ - specularColor: null, - - /** - * @type {number} - * @default 0.5 - */ - glossiness: 0.5, - - /** - * @type {qtek.Texture2D} - */ - diffuseMap: null, - - /** - * @type {qtek.Texture2D} - */ - normalMap: null, - - /** - * @type {qtek.TextureCube} - */ - environmentMap: null, - - /** - * Diffuse alpha channel usage. - * 'none', 'alpha', 'glossiness' - * @type {string} - * @default 'none' - */ - diffuseAlphaUsage: 'none', - - /** - * @type {Array.} - */ - uvRepeat: null, - - /** - * @type {Array.} - */ - uvOffset: null - }, function () { - - this.color = this.color || [1, 1, 1]; - - this.emission = this.emission || [0, 0, 0]; - - this.specularColor = this.specularColor || [0.5, 0.5, 0.5]; - - this.uvRepeat = this.uvRepeat || [1, 1]; - this.uvOffset = this.uvOffset || [0, 0]; - - // Rendererable must need a material with shader - this.shader = new DeferredDummyShader(); - }, {}); - - return StandardMaterial; -}); -define('qtek/geometry/Sphere',['require','../DynamicGeometry','../dep/glmatrix','../math/BoundingBox'],function(require) { - - - - var DynamicGeometry = require('../DynamicGeometry'); - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - var vec2 = glMatrix.vec2; - var BoundingBox = require('../math/BoundingBox'); - - /** - * @constructor qtek.geometry.Sphere - * @extends qtek.DynamicGeometry - * @param {Object} [opt] - * @param {number} [widthSegments] - * @param {number} [heightSegments] - * @param {number} [phiStart] - * @param {number} [phiLength] - * @param {number} [thetaStart] - * @param {number} [thetaLength] - * @param {number} [radius] - */ - var Sphere = DynamicGeometry.derive( - /** @lends qtek.geometry.Sphere# */ - { - /** - * @type {number} - */ - widthSegments: 20, - /** - * @type {number} - */ - heightSegments: 20, - - /** - * @type {number} - */ - phiStart: 0, - /** - * @type {number} - */ - phiLength: Math.PI * 2, - - /** - * @type {number} - */ - thetaStart: 0, - /** - * @type {number} - */ - thetaLength: Math.PI, - - /** - * @type {number} - */ - radius: 1 - - }, function() { - this.build(); - }, - /** @lends qtek.geometry.Sphere.prototype */ - { - /** - * Build sphere geometry - */ - build: function() { - var positions = this.attributes.position.value; - var texcoords = this.attributes.texcoord0.value; - var normals = this.attributes.normal.value; - - positions.length = 0; - texcoords.length = 0; - normals.length = 0; - this.faces.length = 0; - - var x, y, z, - u, v, - i, j; - var normal; - - var heightSegments = this.heightSegments; - var widthSegments = this.widthSegments; - var radius = this.radius; - var phiStart = this.phiStart; - var phiLength = this.phiLength; - var thetaStart = this.thetaStart; - var thetaLength = this.thetaLength; - var radius = this.radius; - - for (j = 0; j <= heightSegments; j ++) { - for (i = 0; i <= widthSegments; i ++) { - u = i / widthSegments; - v = j / heightSegments; - - // X axis is inverted so texture can be mapped from left to right - x = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); - y = radius * Math.cos(thetaStart + v * thetaLength); - z = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); - - positions.push(vec3.fromValues(x, y, z)); - texcoords.push(vec2.fromValues(u, v)); - - normal = vec3.fromValues(x, y, z); - vec3.normalize(normal, normal); - normals.push(normal); - } - } - - var i1, i2, i3, i4; - var faces = this.faces; - - var len = widthSegments + 1; - - for (j = 0; j < heightSegments; j ++) { - for (i = 0; i < widthSegments; i ++) { - i2 = j * len + i; - i1 = (j * len + i + 1); - i4 = (j + 1) * len + i + 1; - i3 = (j + 1) * len + i; - - faces.push(vec3.fromValues(i1, i2, i4)); - faces.push(vec3.fromValues(i2, i3, i4)); - } - } - - this.boundingBox = new BoundingBox(); - this.boundingBox.max.set(radius, radius, radius); - this.boundingBox.min.set(-radius, -radius, -radius); - } - }); - - return Sphere; -}); -define('qtek/geometry/Cone',['require','../DynamicGeometry','../math/BoundingBox','../dep/glmatrix'],function(require) { - - - - var DynamicGeometry = require('../DynamicGeometry'); - var BoundingBox = require('../math/BoundingBox'); - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - var vec2 = glMatrix.vec2; - - /** - * @constructor qtek.geometry.Cone - * @extends qtek.DynamicGeometry - * @param {Object} [opt] - * @param {number} [opt.topRadius] - * @param {number} [opt.bottomRadius] - * @param {number} [opt.height] - * @param {number} [opt.capSegments] - * @param {number} [opt.heightSegments] - */ - var Cone = DynamicGeometry.derive( - /** @lends qtek.geometry.Cone# */ - { - /** - * @type {number} - */ - topRadius: 0, - - /** - * @type {number} - */ - bottomRadius: 1, - - /** - * @type {number} - */ - height: 2, - - /** - * @type {number} - */ - capSegments: 20, - - /** - * @type {number} - */ - heightSegments: 1 - }, function() { - this.build(); - }, - /** @lends qtek.geometry.Cone.prototype */ - { - /** - * Build cone geometry - */ - build: function() { - var positions = this.attributes.position.value; - var texcoords = this.attributes.texcoord0.value; - var faces = this.faces; - positions.length = 0; - texcoords.length = 0; - faces.length = 0; - // Top cap - var capSegRadial = Math.PI * 2 / this.capSegments; - - var topCap = []; - var bottomCap = []; - - var r1 = this.topRadius; - var r2 = this.bottomRadius; - var y = this.height / 2; - - var c1 = vec3.fromValues(0, y, 0); - var c2 = vec3.fromValues(0, -y, 0); - for (var i = 0; i < this.capSegments; i++) { - var theta = i * capSegRadial; - var x = r1 * Math.sin(theta); - var z = r1 * Math.cos(theta); - topCap.push(vec3.fromValues(x, y, z)); - - x = r2 * Math.sin(theta); - z = r2 * Math.cos(theta); - bottomCap.push(vec3.fromValues(x, -y, z)); - } - - // Build top cap - positions.push(c1); - // FIXME - texcoords.push(vec2.fromValues(0, 1)); - var n = this.capSegments; - for (var i = 0; i < n; i++) { - positions.push(topCap[i]); - // FIXME - texcoords.push(vec2.fromValues(i / n, 0)); - faces.push([0, i+1, (i+1) % n + 1]); - } - - // Build bottom cap - var offset = positions.length; - positions.push(c2); - texcoords.push(vec2.fromValues(0, 1)); - for (var i = 0; i < n; i++) { - positions.push(bottomCap[i]); - // FIXME - texcoords.push(vec2.fromValues(i / n, 0)); - faces.push([offset, offset+((i+1) % n + 1), offset+i+1]); - } - - // Build side - offset = positions.length; - var n2 = this.heightSegments; - for (var i = 0; i < n; i++) { - for (var j = 0; j < n2+1; j++) { - var v = j / n2; - positions.push(vec3.lerp(vec3.create(), topCap[i], bottomCap[i], v)); - texcoords.push(vec2.fromValues(i / n, v)); - } - } - for (var i = 0; i < n; i++) { - for (var j = 0; j < n2; j++) { - var i1 = i * (n2 + 1) + j; - var i2 = ((i + 1) % n) * (n2 + 1) + j; - var i3 = ((i + 1) % n) * (n2 + 1) + j + 1; - var i4 = i * (n2 + 1) + j + 1; - faces.push([offset+i2, offset+i1, offset+i4]); - faces.push([offset+i4, offset+i3, offset+i2]); - } - } - - this.generateVertexNormals(); - - this.boundingBox = new BoundingBox(); - var r = Math.max(this.topRadius, this.bottomRadius); - this.boundingBox.min.set(-r, -this.height/2, -r); - this.boundingBox.max.set(r, this.height/2, r); - } - }); - - return Cone; -}); -define('qtek/shader/source/util.essl',[],function () { return '// Use light attenuation formula in\n// http://blog.slindev.com/2011/01/10/natural-light-attenuation/\n@export buildin.util.calculate_attenuation\n\nuniform float attenuationFactor : 5.0;\n\nfloat lightAttenuation(float dist, float range)\n{\n float attenuation = 1.0;\n if( range > 0.0)\n {\n attenuation = dist*dist/(range*range);\n float att_s = attenuationFactor;\n attenuation = 1.0/(attenuation*att_s+1.0);\n att_s = 1.0/(att_s+1.0);\n attenuation = attenuation - att_s;\n attenuation /= 1.0 - att_s;\n }\n return clamp(attenuation, 0.0, 1.0);\n}\n\n@end\n\n//http://codeflow.org/entries/2012/aug/02/easy-wireframe-display-with-barycentric-coordinates/\n@export buildin.util.edge_factor\n\nfloat edgeFactor(float width)\n{\n vec3 d = fwidth(v_Barycentric);\n vec3 a3 = smoothstep(vec3(0.0), d * width, v_Barycentric);\n return min(min(a3.x, a3.y), a3.z);\n}\n\n@end\n\n// Pack depth\n// Float value can only be [0.0 - 1.0) ?\n@export buildin.util.encode_float\nvec4 encodeFloat( const in float depth )\n{\n\n const vec4 bitShifts = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );\n\n const vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );\n vec4 res = fract( depth * bitShifts );\n res -= res.xxyz * bit_mask;\n\n return res;\n}\n@end\n\n@export buildin.util.decode_float\nfloat decodeFloat(const in vec4 colour)\n{\n const vec4 bitShifts = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n return dot(colour, bitShifts);\n}\n@end\n\n// http://graphicrants.blogspot.com/2009/04/rgbm-color-encoding.html\n@export buildin.util.rgbm_decode\nvec3 RGBMDecode(vec4 rgbm, float range) {\n return range * rgbm.rgb * rgbm.a;\n}\n@end\n\n@export buildin.util.rgbm_encode\nvec4 RGBMEncode(vec3 color, float range) {\n vec4 rgbm;\n color *= 1.0 / range;\n rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 1e-6 ) ), 0.0, 1.0);\n rgbm.a = ceil(rgbm.a * 255.0) / 255.0;\n rgbm.rgb = color / rgbm.a;\n return rgbm;\n}\n@end\n\n\n@export buildin.chunk.skin_matrix\n\n// Weighted Sum Skinning Matrix\nmat4 skinMatrixWS;\nif (joint.x >= 0.0)\n{\n skinMatrixWS = skinMatrix[int(joint.x)] * weight.x;\n}\nif (joint.y >= 0.0)\n{\n skinMatrixWS += skinMatrix[int(joint.y)] * weight.y;\n}\nif (joint.z >= 0.0)\n{\n skinMatrixWS += skinMatrix[int(joint.z)] * weight.z;\n}\nif (joint.w >= 0.0)\n{\n skinMatrixWS += skinMatrix[int(joint.w)] * (1.0-weight.x-weight.y-weight.z);\n}\n@end\n';}); - -define('qtek/shader/source/deferred/gbuffer.essl',[],function () { return '@export buildin.deferred.gbuffer.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\n\nuniform vec2 uvRepeat;\nuniform vec2 uvOffset;\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 normal : NORMAL;\nattribute vec4 tangent : TANGENT;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\n\n#ifdef NORMALMAP_ENABLED\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\nvarying vec4 v_ProjPos;\n\nvoid main()\n{\n \n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n // Upper skinMatrix \n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n skinnedTangent = (skinMatrixWS * vec4(tangent.xyz, 0.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\n \n #ifdef NORMALMAP_ENABLED\n v_Tangent = normalize((worldInverseTranspose * vec4(skinnedTangent, 0.0)).xyz);\n v_Bitangent = normalize(cross(v_Normal, v_Tangent) * tangent.w);\n #endif\n\n v_ProjPos = gl_Position;\n}\n\n\n@end\n\n\n@export buildin.deferred.gbuffer.fragment\n\nuniform sampler2D diffuseMap;\nuniform float glossiness;\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\n\n#ifdef NORMALMAP_ENABLED\nuniform sampler2D normalMap;\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\nvarying vec4 v_ProjPos;\n\nvoid main()\n{\n vec3 N = v_Normal;\n #ifdef NORMALMAP_ENABLED\n N = texture2D(normalMap, v_Texcoord).xyz * 2.0 - 1.0;\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\n N = tbn * N;\n #endif\n\n // N.z can be recovered from sqrt(1 - dot(N.xy, N.xy));\n gl_FragColor.rg = (N.xy + 1.0) * 0.5;\n\n // Depth\n gl_FragColor.b = v_ProjPos.z / v_ProjPos.w;\n\n gl_FragColor.a = glossiness;\n #ifdef DIFFUSEMAP_ENABLED\n // Ouptut glossiness to alpha channel\n gl_FragColor.a *= texture2D(diffuseMap, v_Texcoord).a;\n #endif\n\n}\n@end';}); - -define('qtek/shader/source/deferred/chunk.essl',[],function () { return '@export buildin.deferred.chunk.light_head\nuniform sampler2D normalTex;\nuniform vec2 viewportSize;\n\nuniform mat4 viewProjectionInv;\n\nconst vec3 LUM = vec3(0.2125, 0.7154, 0.0721);\n@end\n\n@export buildin.deferred.chunk.gbuffer_read\n vec2 uv = gl_FragCoord.xy / viewportSize;\n\n vec4 tex = texture2D(normalTex, uv);\n // Is empty\n if (dot(tex.rgb, vec3(1.0)) == 0.0) {\n discard;\n }\n\n vec3 N;\n N.xy = tex.rg * 2.0 - 1.0;\n N.z = sqrt(1.0 - dot(N.xy, N.xy));\n\n // Depth value in depth texture is 0 - 1\n // float z = texture2D(depthTex, uv).r * 2.0 - 1.0;\n float z = tex.b;\n\n float glossiness = tex.a;\n\n vec2 xy = uv * 2.0 - 1.0;\n\n vec4 projectedPos = vec4(xy, z, 1.0);\n vec4 p4 = viewProjectionInv * projectedPos;\n\n vec3 position = p4.xyz / p4.w;\n@end\n\n@export buildin.deferred.chunk.light_equation\n\nfloat D_Phong(float g, float ndh) {\n // from black ops 2\n float a = pow(8192.0, g);\n return (a + 2.0) / 8.0 * pow(ndh, a);\n}\n\n@end';}); - -define('qtek/shader/source/deferred/lightvolume.essl',[],function () { return '@export buildin.deferred.light_volume.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\nvarying vec3 v_Position;\n\nvoid main()\n{\n gl_Position = worldViewProjection * vec4(position, 1.0);\n\n v_Position = position;\n}\n\n@end';}); - -define('qtek/shader/source/deferred/spot.essl',[],function () { return '@export buildin.deferred.spot_light\n\n@import buildin.deferred.chunk.light_head\n\n@import buildin.deferred.chunk.light_equation\n\n@import buildin.util.calculate_attenuation\n\nuniform vec3 lightPosition;\nuniform vec3 lightDirection;\nuniform vec3 lightColor;\nuniform float umbraAngleCosine;\nuniform float penumbraAngleCosine;\nuniform float lightRange;\nuniform float falloffFactor;\n\nuniform vec3 eyePosition;\n\nvoid main()\n{\n @import buildin.deferred.chunk.gbuffer_read\n\n vec3 L = lightPosition - position;\n vec3 V = normalize(eyePosition - position);\n\n float dist = length(L);\n L /= dist;\n\n float attenuation = lightAttenuation(dist, lightRange);\n float c = dot(-lightDirection, L);\n\n float falloff = clamp((c - umbraAngleCosine) / (penumbraAngleCosine - umbraAngleCosine), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n // Diffuse term\n gl_FragColor.rgb = lightColor * ndl * (1.0 - falloff) * attenuation;\n if (dot(gl_FragColor.rgb, vec3(1.0)) == 0.0) // Reduce blending\n {\n discard;\n }\n gl_FragColor.a = dot(LUM, gl_FragColor.rgb * D_Phong(glossiness, ndh));\n}\n@end\n';}); - -define('qtek/shader/source/deferred/directional.essl',[],function () { return '@export buildin.deferred.directional_light\n\n@import buildin.deferred.chunk.light_head\n\n@import buildin.deferred.chunk.light_equation\n\nuniform vec3 lightDirection;\nuniform vec3 lightColor;\n\nuniform vec3 eyePosition;\n\nvoid main()\n{\n @import buildin.deferred.chunk.gbuffer_read\n\n vec3 L = -normalize(lightDirection);\n vec3 V = normalize(eyePosition - position);\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n gl_FragColor.rgb = ndl * lightColor;\n gl_FragColor.a = dot(LUM, gl_FragColor.rgb * D_Phong(glossiness, ndh));\n}\n@end\n';}); - -define('qtek/shader/source/deferred/ambient.essl',[],function () { return '@export buildin.deferred.ambient_light\nuniform sampler2D normalTexture;\nuniform vec3 lightColor;\n\nvarying vec2 v_Texcoord;\n\nvoid main()\n{\n vec4 tex = texture2D(normalTexture, v_Texcoord);\n vec3 normal = tex.rgb * 2.0 - 1.0;\n\n gl_FragColor.rgb = lightColor * (clamp(normal.y * 0.7, 0.0, 1.0) + 0.3);\n gl_FragColor.a = 0.0;\n}\n@end';}); - -define('qtek/shader/source/deferred/point.essl',[],function () { return '@export buildin.deferred.point_light\n\n@import buildin.deferred.chunk.light_head\n\n@import buildin.util.calculate_attenuation\n\n@import buildin.deferred.chunk.light_equation\n\nuniform vec3 lightPosition;\nuniform vec3 lightColor;\nuniform float lightRange;\n\nuniform vec3 eyePosition;\n\nvarying vec3 v_Position;\n\nvoid main()\n{\n @import buildin.deferred.chunk.gbuffer_read\n\n vec3 L = lightPosition - position;\n vec3 V = normalize(eyePosition - position);\n\n float dist = length(L);\n L /= dist;\n\n vec3 H = normalize(L + V);\n\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n float attenuation = lightAttenuation(dist, lightRange);\n // Diffuse term\n gl_FragColor.rgb = lightColor * ndl * attenuation;\n if (dot(gl_FragColor.rgb, vec3(1.0)) == 0.0) // Reduce blending\n {\n discard;\n }\n // // Specular luminance\n gl_FragColor.a = dot(LUM, gl_FragColor.rgb * D_Phong(glossiness, ndh));\n}\n@end';}); - -define('qtek/shader/source/deferred/output.essl',[],function () { return '@export buildin.deferred.output.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world: WORLD;\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n\nuniform vec2 uvRepeat;\nuniform vec2 uvOffset;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\n\nvarying vec4 v_ProjPos;\n\nvarying vec3 v_WorldPos;\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n\n v_WorldPos = (world * vec4(skinnedPosition, 1.0)).xyz;\n\n v_ProjPos = gl_Position;\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n}\n@end\n\n@export buildin.deferred.output.fragment\n\nuniform sampler2D diffuseMap;\n\nuniform sampler2D lightAccumTex;\nuniform sampler2D normalTex;\n\nuniform vec3 color;\nuniform vec3 specularColor;\nuniform vec3 emission;\n\nuniform vec3 eyePosition;\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_WorldPos;\nvarying vec4 v_ProjPos;\n\nconst vec3 LUM = vec3(0.2125, 0.7154, 0.0721);\n\n// Fresnel\nvec3 F_Schlick(float ndv, vec3 spec) {\n return spec + (1.0 - spec) * pow(1.0 - ndv, 5.0);\n}\n\nvoid main()\n{\n vec2 uv = (v_ProjPos.xy / v_ProjPos.w + 1.0) * 0.5;\n\n vec3 V = normalize(eyePosition - v_WorldPos);\n\n vec3 albedo = color;\n #ifdef diffuseMap\n albedo *= texture2D(diffuseMap, v_Texcoord);\n #endif\n\n vec4 diffSpec = texture2D(lightAccumTex, uv);\n vec3 N;\n vec2 tex = texture2D(normalTex, uv).rg;\n N.xy = tex * 2.0 - 1.0;\n N.z = sqrt(1.0 - dot(N.xy, N.xy));\n\n vec3 diffTerm = diffSpec.rgb;\n // PENDING\n vec3 specTerm = diffTerm * diffSpec.a / (dot(LUM, diffTerm) + 0.1);\n vec3 fresnelTerm = F_Schlick(clamp(dot(N, V), 0.0, 1.0), specularColor);\n\n gl_FragColor.rgb = albedo * diffTerm + fresnelTerm * specTerm + emission;\n gl_FragColor.a = 1.0;\n}\n@end\n\n';}); - -define('qtek/shader/source/prez.essl',[],function () { return '// Shader for prez pass\n@export buildin.prez.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n \n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n \n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n}\n\n@end\n\n\n@export buildin.prez.fragment\n\nvoid main()\n{\n gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\n}\n\n@end';}); - -// Light-pre pass deferred rendering -// http://www.realtimerendering.com/blog/deferred-lighting-approaches/ -define('qtek/deferred/Renderer',['require','../core/Base','../Shader','./StandardMaterial','../Material','../FrameBuffer','../compositor/Pass','../Texture2D','../Texture','../math/BoundingBox','../Mesh','../geometry/Sphere','../geometry/Cone','../math/Matrix4','../math/Vector3','../Renderer','../shader/source/util.essl','../shader/source/deferred/gbuffer.essl','../shader/source/deferred/chunk.essl','../shader/source/deferred/lightvolume.essl','../shader/source/deferred/spot.essl','../shader/source/deferred/directional.essl','../shader/source/deferred/ambient.essl','../shader/source/deferred/point.essl','../shader/source/deferred/output.essl','../shader/source/prez.essl'],function (require) { - - - - var Base = require('../core/Base'); - var Shader = require('../Shader'); - var StandardMaterial = require('./StandardMaterial'); - var Material = require('../Material'); - var FrameBuffer = require('../FrameBuffer'); - var FullQuadPass = require('../compositor/Pass'); - var Texture2D = require('../Texture2D'); - var Texture = require('../Texture'); - var boundingBox = require('../math/BoundingBox'); - var Mesh = require('../Mesh'); - var SphereGeo = require('../geometry/Sphere'); - var ConeGeo = require('../geometry/Cone'); - var Matrix4 = require('../math/Matrix4'); - var Vector3 = require('../math/Vector3'); - var ForwardRenderer = require('../Renderer'); - - Shader.import(require('../shader/source/util.essl')); - Shader.import(require('../shader/source/deferred/gbuffer.essl')); - Shader.import(require('../shader/source/deferred/chunk.essl')); - - Shader.import(require('../shader/source/deferred/lightvolume.essl')); - // Light shaders - Shader.import(require('../shader/source/deferred/spot.essl')); - Shader.import(require('../shader/source/deferred/directional.essl')); - Shader.import(require('../shader/source/deferred/ambient.essl')); - Shader.import(require('../shader/source/deferred/point.essl')); - - Shader.import(require('../shader/source/deferred/output.essl')); - - Shader.import(require('../shader/source/prez.essl')); - - var errorShader = {}; - - var DeferredRenderer = Base.derive(function () { - - var gBufferShader = new Shader({ - vertex: Shader.source('buildin.deferred.gbuffer.vertex'), - fragment: Shader.source('buildin.deferred.gbuffer.fragment') - }); - - var gBufferDiffShader = gBufferShader.clone(); - gBufferDiffShader.enableTexture('diffuseMap'); - var gBufferNormShader = gBufferShader.clone(); - gBufferNormShader.enableTexture('normalMap'); - var gBufferDiffNormShader = gBufferDiffShader.clone(); - gBufferDiffNormShader.enableTexture('normalMap'); - - var outputShader = new Shader({ - vertex: Shader.source('buildin.deferred.output.vertex'), - fragment: Shader.source('buildin.deferred.output.fragment') - }); - var outputDiffShader = outputShader.clone(); - outputDiffShader.enableTexture('diffuseMap'); - - var fullQuadVertex = Shader.source('buildin.compositor.vertex'); - var lightVolumeVertex = Shader.source('buildin.deferred.light_volume.vertex'); - - var lightAccumulateBlendFunc = function (gl) { - gl.blendEquation(gl.FUNC_ADD); - gl.blendFunc(gl.ONE, gl.ONE); - } - - var createLightPassMat = function (shader) { - return new Material({ - shader: shader, - blend: lightAccumulateBlendFunc, - transparent: true, - depthMask: false - }) - } - - // Rotate and positioning to fit the spot light - // Which the cusp of cone pointing to the negative z - // and positioned on the origin - var coneGeo = new ConeGeo({ - bottomRadius: 1, - height: 2, - capSegments: 10 - }); - var mat = new Matrix4(); - mat.rotateX(Math.PI / 2); - mat.translate(new Vector3(0, 0, -1)); - - coneGeo.applyTransform(mat); - - return { - // GBuffer shaders - _gBufferShader: gBufferShader, - - _gBufferDiffShader: gBufferDiffShader, - - _gBufferDiffNormShader: gBufferDiffNormShader, - - _gBufferNormShader: gBufferNormShader, - - _outputShader: outputShader, - - _outputDiffShader: outputDiffShader, - - _gBufferFrameBuffer: new FrameBuffer(), - - _gBufferTex: new Texture2D({ - width: 0, - height: 0, - // FIXME Device not support float texture - // FIXME Half float seems has problem - type: Texture.FLOAT, - minFilter: Texture.NEAREST, - magFilter: Texture.NEAREST - }), - - // _depthTex: new Texture2D({ - // type: Texture.UNSIGNED_SHORT, - // format: Texture.DEPTH_COMPONENT - // }), - - _lightAccumFrameBuffer: new FrameBuffer(), - - _lightAccumTex: new Texture2D({ - // FIXME Device not support float texture - type: Texture.FLOAT, - minFilter: Texture.NEAREST, - magFilter: Texture.NEAREST - }), - - _fullQuadPass: new FullQuadPass(), - - _directionalLightMat: createLightPassMat(new Shader({ - vertex: fullQuadVertex, - fragment: Shader.source('buildin.deferred.directional_light') - })), - _ambientMat: createLightPassMat(new Shader({ - vertex: fullQuadVertex, - fragment: Shader.source('buildin.deferred.ambient_light') - })), - - _spotLightShader: new Shader({ - vertex: lightVolumeVertex, - fragment: Shader.source('buildin.deferred.spot_light') - }), - _pointLightShader: new Shader({ - vertex: lightVolumeVertex, - fragment: Shader.source('buildin.deferred.point_light') - }), - - _createLightPassMat: createLightPassMat, - - _lightSphereGeo: new SphereGeo({ - widthSegments: 10, - heightSegements: 10 - }), - - _lightConeGeo: coneGeo - } - }, { - render: function (renderer, scene, camera) { - - var gl = renderer.gl; - - scene.update(false, true); - - camera.update(true); - - var opaqueQueue = scene.opaqueQueue; - opaqueQueue.sort(ForwardRenderer.opaqueSortFunc); - - // Replace material - for (var i = 0; i < opaqueQueue.length; i++) { - this._replaceGBufferMaterial(opaqueQueue[i]); - } - - // Resize GBuffer - var width = renderer.getWidth(); - var height = renderer.getHeight(); - var gBufferFrameBuffer = this._gBufferFrameBuffer; - var gBufferTex = this._gBufferTex; - // var depthTex = this._depthTex; - var lightAccumTex = this._lightAccumTex; - if (width !== gBufferTex.width || height !== gBufferTex.height) { - gBufferTex.width = width; - gBufferTex.height = height; - // depthTex.width = width; - // depthTex.height = height; - lightAccumTex.width = width; - lightAccumTex.height = height; - gBufferTex.dirty(); - // depthTex.dirty(); - lightAccumTex.dirty(); - } - // Render normal and glossiness to GBuffer - gBufferFrameBuffer.attach(gl, gBufferTex); - // FIXME Device that not support depth texture - // gBufferFrameBuffer.attach(gl, depthTex, gl.DEPTH_ATTACHMENT); - gBufferFrameBuffer.bind(renderer); - gl.clearColor(0, 0, 0, 0); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - gl.disable(gl.BLEND); - renderer.renderQueue(opaqueQueue, camera); - gBufferFrameBuffer.unbind(renderer); - - // Accumulate light buffer - this._accumulateLightBuffer(renderer, scene, camera); - - // Final output rendering - var eyePosition = camera.getWorldPosition()._array; - for (var i = 0; i < opaqueQueue.length; i++) { - this._swapOutputMaterial(opaqueQueue[i], eyePosition); - } - - // Clear - if (renderer.clear) { - var cc = renderer.color; - gl.clearColor(cc[0], cc[1], cc[2], cc[3]); - gl.clear(renderer.clear); - } - - gl.disable(gl.BLEND); - renderer.renderQueue(opaqueQueue, camera); - - for (var i = 0; i < opaqueQueue.length; i++) { - // Swap material back - opaqueQueue[i].material = opaqueQueue[i].__standardMat; - } - }, - - _accumulateLightBuffer: function (renderer, scene, camera) { - var gl = renderer.gl; - var lightAccumTex = this._lightAccumTex; - var lightAccumFrameBuffer = this._lightAccumFrameBuffer; - var lightVolumeMeshList = this._lightVolumeMeshList; - var listLen = 0; - var eyePosition = camera.getWorldPosition()._array; - - lightAccumFrameBuffer.attach(gl, lightAccumTex); - lightAccumFrameBuffer.bind(renderer); - gl.clearColor(0, 0, 0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - gl.enable(gl.BLEND); - - var viewProjectionInv = new Matrix4(); - Matrix4.multiply(viewProjectionInv, camera.worldTransform, camera.invProjectionMatrix); - - var viewportSize = [lightAccumTex.width, lightAccumTex.height]; - var volumeMeshList = []; - - for (var i = 0; i < scene.lights.length; i++) { - var light = scene.lights[i]; - var uTpl = light.uniformTemplates; - - this._updateLightProxy(light); - - var volumeMesh = light.volumeMesh || light.__volumeMesh; - - if (volumeMesh) { - var material = volumeMesh.material; - // Volume mesh will affect the scene bounding box when rendering - // if castShadow is true - volumeMesh.castShadow = false; - - material.setUniform('eyePosition', eyePosition); - material.setUniform('viewportSize', viewportSize); - material.setUniform('viewProjectionInv', viewProjectionInv._array); - material.setUniform('normalTex', this._gBufferTex); - - switch (light.type) { - case 'POINT_LIGHT': - material.setUniform('lightColor', uTpl.pointLightColor.value(light)); - material.setUniform('lightRange', uTpl.pointLightRange.value(light)); - material.setUniform('lightPosition', uTpl.pointLightPosition.value(light)); - break; - case 'SPOT_LIGHT': - material.setUniform('lightPosition', uTpl.spotLightPosition.value(light)); - material.setUniform('lightColor', uTpl.spotLightColor.value(light)); - material.setUniform('lightRange', uTpl.spotLightRange.value(light)); - material.setUniform('lightDirection', uTpl.spotLightDirection.value(light)); - material.setUniform('umbraAngleCosine', uTpl.spotLightUmbraAngleCosine.value(light)); - material.setUniform('penumbraAngleCosine', uTpl.spotLightPenumbraAngleCosine.value(light)); - material.setUniform('falloffFactor', uTpl.spotLightFalloffFactor.value(light)); - break; - } - - volumeMeshList.push(volumeMesh); - } - else { - var pass = this._fullQuadPass; - // Full quad light - switch (light.type) { - case 'AMBIENT_LIGHT': - pass.material = this._ambientMat; - pass.material.set('lightColor', uTpl.ambientLightColor.value(light)); - break; - case 'DIRECTIONAL_LIGHT': - pass.material = this._directionalLightMat; - pass.material.set('lightColor', uTpl.directionalLightColor.value(light)); - pass.material.set('lightDirection', uTpl.directionalLightDirection.value(light)); - break; - } - pass.material.set('eyePosition', eyePosition); - pass.material.set('viewportSize', viewportSize); - pass.material.set('viewProjectionInv', viewProjectionInv._array); - pass.material.set('normalTex', this._gBufferTex); - - pass.renderQuad(renderer); - } - } - - this._renderVolumeMeshList(renderer, camera, volumeMeshList); - - lightAccumFrameBuffer.unbind(renderer); - }, - - // Update light volume mesh - // Light volume mesh is rendered in light accumulate pass instead of full quad. - // It will reduce pixels significantly when local light is relatively small. - // And we can use custom volume mesh to shape the light. - // - // See "Deferred Shading Optimizations" in GDC2011 - _updateLightProxy: function (light) { - var volumeMesh; - if (light.volumeMesh) { - volumeMesh = light.volumeMesh; - } - else { - switch (light.type) { - // Only local light (point and spot) needs volume mesh. - // Directional and ambient light renders in full quad - case 'POINT_LIGHT': - // Volume mesh created automatically - light.__volumeMesh = light.__volumeMesh || new Mesh({ - material: this._createLightPassMat(this._pointLightShader), - geometry: this._lightSphereGeo, - // Disable culling - // if light volume mesh intersect camera near plane - // We need mesh inside can still be rendered - culling: false - }); - volumeMesh = light.__volumeMesh; - var r = light.range; - volumeMesh.scale.set(r, r, r); - break; - case 'SPOT_LIGHT': - light.__volumeMesh = light.__volumeMesh || new Mesh({ - material: this._createLightPassMat(this._spotLightShader), - geometry: this._lightConeGeo, - culling: false - }); - volumeMesh = light.__volumeMesh; - var aspect = Math.tan(light.penumbraAngle * Math.PI / 180); - volumeMesh.scale.set( - aspect * light.range, aspect * light.range, light.range / 2 - ); - break; - } - } - if (volumeMesh) { - volumeMesh.update(); - // Apply light transform - Matrix4.multiply(volumeMesh.worldTransform, light.worldTransform, volumeMesh.worldTransform); - } - }, - - _renderVolumeMeshList: (function () { - var worldViewProjection = new Matrix4(); - var worldView = new Matrix4(); - var preZMaterial = new Material({ - shader: new Shader({ - vertex: Shader.source('buildin.prez.vertex'), - fragment: Shader.source('buildin.prez.fragment') - }) - }); - return function (renderer, camera, volumeMeshList) { - var gl = renderer.gl; - - gl.clear(gl.DEPTH_BUFFER_BIT); - - gl.enable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - gl.blendEquation(gl.FUNC_ADD); - gl.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.ONE); - gl.depthFunc(gl.LEQUAL); - for (var i = 0; i < volumeMeshList.length; i++) { - var volumeMesh = volumeMeshList[i]; - - // Frustum culling - Matrix4.multiply(worldView, camera.viewMatrix, volumeMesh.worldTransform); - if (renderer.isFrustumCulled( - volumeMesh, camera, worldView._array, camera.projectionMatrix._array - )) { - continue; - } - - // Use prez to avoid one pixel rendered twice - gl.colorMask(false, false, false, false); - gl.depthMask(true); - - gl.clear(gl.DEPTH_BUFFER_BIT); - - Matrix4.multiply(worldViewProjection, camera.projectionMatrix, worldView); - - var prezShader = preZMaterial.shader; - this._bindShader(renderer, prezShader); - - var semanticInfo = prezShader.matrixSemantics.WORLDVIEWPROJECTION; - prezShader.setUniform(gl, semanticInfo.type, semanticInfo.symbol, worldViewProjection._array); - volumeMesh.render(gl, preZMaterial); - - // Render light - gl.colorMask(true, true, true, true); - gl.depthMask(false); - var shader = volumeMesh.material.shader; - this._bindShader(renderer, shader); - - var semanticInfo = shader.matrixSemantics.WORLDVIEWPROJECTION; - shader.setUniform(gl, semanticInfo.type, semanticInfo.symbol, worldViewProjection._array); - volumeMesh.material.bind(gl); - volumeMesh.render(gl); - } - - gl.depthFunc(gl.LESS); - } - })(), - - _bindShader: function (renderer, shader) { - var errMsg = shader.bind(renderer.gl); - if (errMsg) { - - if (errorShader[shader.__GUID__]) { - return; - } - errorShader[shader.__GUID__] = true; - - if (renderer.throwError) { - throw new Error(errMsg); - } else { - renderer.trigger('error', errMsg); - } - } - }, - - _replaceGBufferMaterial: function (renderable) { - if (renderable.material instanceof StandardMaterial) { - var standardMat = renderable.material; - renderable.__standardMat = standardMat; - var isDiffuseAlphaGlossiness = standardMat.diffuseMap - && standardMat.diffuseAlphaUsage === 'glossiness'; - renderable.__gBufferMat = renderable.__gBufferMat || new Material(); - var gBufferMat = renderable.__gBufferMat; - - var shader; - if (standardMat.normalMap) { - if (isDiffuseAlphaGlossiness) { - // FIXME Toggle shader may be too frequently - if (gBufferMat.shader !== this._gBufferDiffNormShader) { - gBufferMat.attachShader(this._gBufferDiffNormShader, true); - } - gBufferMat.setUniform('diffuseMap', standardMat.diffuseMap); - } - else { - if (gBufferMat.shader !== this._gBufferNormShader) { - gBufferMat.attachShader(this._gBufferNormShader, true); - } - } - gBufferMat.setUniform('normalMap', standardMat.normalMap); - } - else { - if (isDiffuseAlphaGlossiness) { - if (gBufferMat.shader !== this._gBufferDiffShader) { - gBufferMat.attachShader(this._gBufferDiffShader, true); - } - gBufferMat.setUniform('diffuseMap', standardMat.diffuseMap); - } - else { - if (gBufferMat.shader !== this._gBufferShader) { - gBufferMat.attachShader(this._gBufferShader, true); - } - } - } - - gBufferMat.set('uvOffset', standardMat.uvOffset); - gBufferMat.setUniform('glossiness', standardMat.glossiness); - - renderable.material = gBufferMat; - } - else { - console.warn('Deferred renderer only support StandardMaterial'); - } - }, - - _swapOutputMaterial: function (renderable, eyePosition) { - if (renderable.__standardMat) { - var standardMat = renderable.__standardMat; - - renderable.__deferredOutputMat = renderable.__deferredOutputMat || new Material(); - var outputMat = renderable.__deferredOutputMat; - - if (standardMat.diffuseMap) { - if (outputMat.shader !== this._outputDiffShader) { - outputMat.attachShader(this._outputDiffShader, true); - } - - outputMat.setUniform('diffuseMap', standardMat.diffuseMap); - } - else { - if (outputMat.shader !== this._outputShader) { - outputMat.attachShader(this._outputShader, true); - } - } - - outputMat.set('eyePosition', eyePosition); - - outputMat.set('color', standardMat.color); - outputMat.set('emission', standardMat.emission); - outputMat.set('specularColor', standardMat.specularColor); - - outputMat.set('uvRepeat', standardMat.uvRepeat); - outputMat.set('uvOffset', standardMat.uvOffset); - outputMat.set('specularColor', standardMat.specularColor); - - outputMat.set('lightAccumTex', this._lightAccumTex); - outputMat.set('normalTex', this._gBufferTex); - - renderable.material = outputMat; - } - } - }); - - return DeferredRenderer; -}); -define('qtek/geometry/Cube',['require','../DynamicGeometry','./Plane','../math/Matrix4','../math/Vector3','../math/BoundingBox'],function(require) { - - - - var DynamicGeometry = require('../DynamicGeometry'); - var Plane = require('./Plane'); - var Matrix4 = require('../math/Matrix4'); - var Vector3 = require('../math/Vector3'); - var BoundingBox = require('../math/BoundingBox'); - - var planeMatrix = new Matrix4(); - - /** - * @constructor qtek.geometry.Cube - * @extends qtek.DynamicGeometry - * @param {Object} [opt] - * @param {number} [opt.widthSegments] - * @param {number} [opt.heightSegments] - * @param {number} [opt.depthSegments] - * @param {boolean} [opt.inside] - */ - var Cube = DynamicGeometry.derive( - /**@lends qtek.geometry.Cube# */ - { - /** - * @type {number} - */ - widthSegments: 1, - /** - * @type {number} - */ - heightSegments: 1, - /** - * @type {number} - */ - depthSegments: 1, - /** - * @type {boolean} - */ - inside: false - }, function() { - this.build(); - }, - /** @lends qtek.geometry.Cube.prototype */ - { - /** - * Build cube geometry - */ - build: function() { - - this.faces.length = 0; - this.attributes.position.value.length = 0; - this.attributes.texcoord0.value.length = 0; - this.attributes.normal.value.length = 0; - - var planes = { - 'px': createPlane('px', this.depthSegments, this.heightSegments), - 'nx': createPlane('nx', this.depthSegments, this.heightSegments), - 'py': createPlane('py', this.widthSegments, this.depthSegments), - 'ny': createPlane('ny', this.widthSegments, this.depthSegments), - 'pz': createPlane('pz', this.widthSegments, this.heightSegments), - 'nz': createPlane('nz', this.widthSegments, this.heightSegments), - }; - var cursor = 0; - var attrList = ['position', 'texcoord0', 'normal']; - for (var pos in planes) { - for (var k = 0; k < attrList.length; k++) { - var attrName = attrList[k]; - var attrArray = planes[pos].attributes[attrName].value; - for (var i = 0; i < attrArray.length; i++) { - var value = attrArray[i]; - if (this.inside && attrName === 'normal') { - value[0] = -value[0]; - value[1] = -value[1]; - value[2] = -value[2]; - } - this.attributes[attrName].value.push(value); - } - } - var plane = planes[pos]; - for (var i = 0; i < plane.faces.length; i++) { - var face = plane.faces[i]; - this.faces.push([face[0]+cursor, face[1]+cursor, face[2]+cursor]); - } - - cursor += planes[pos].getVertexNumber(); - } - - this.boundingBox = new BoundingBox(); - this.boundingBox.max.set(1, 1, 1); - this.boundingBox.min.set(-1, -1, -1); - } - }); - - function createPlane(pos, widthSegments, heightSegments) { - - planeMatrix.identity(); - - var plane = new Plane({ - widthSegments: widthSegments, - heightSegments: heightSegments - }); - - switch(pos) { - case 'px': - Matrix4.translate(planeMatrix, planeMatrix, Vector3.POSITIVE_X); - Matrix4.rotateY(planeMatrix, planeMatrix, Math.PI / 2); - break; - case 'nx': - Matrix4.translate(planeMatrix, planeMatrix, Vector3.NEGATIVE_X); - Matrix4.rotateY(planeMatrix, planeMatrix, -Math.PI / 2); - break; - case 'py': - Matrix4.translate(planeMatrix, planeMatrix, Vector3.POSITIVE_Y); - Matrix4.rotateX(planeMatrix, planeMatrix, -Math.PI / 2); - break; - case 'ny': - Matrix4.translate(planeMatrix, planeMatrix, Vector3.NEGATIVE_Y); - Matrix4.rotateX(planeMatrix, planeMatrix, Math.PI / 2); - break; - case 'pz': - Matrix4.translate(planeMatrix, planeMatrix, Vector3.POSITIVE_Z); - break; - case 'nz': - Matrix4.translate(planeMatrix, planeMatrix, Vector3.NEGATIVE_Z); - Matrix4.rotateY(planeMatrix, planeMatrix, Math.PI); - break; - } - plane.applyTransform(planeMatrix); - return plane; - } - - return Cube; -}); -define('qtek/geometry/Cylinder',['require','../DynamicGeometry','./Cone'],function(require) { - - - - var DynamicGeometry = require('../DynamicGeometry'); - var ConeGeometry = require('./Cone'); - - /** - * @constructor qtek.geometry.Cylinder - * @extends qtek.DynamicGeometry - * @param {Object} [opt] - * @param {number} [opt.radius] - * @param {number} [opt.height] - * @param {number} [opt.capSegments] - * @param {number} [opt.heightSegments] - */ - var Cylinder = DynamicGeometry.derive( - /** @lends qtek.geometry.Cylinder# */ - { - /** - * @type {number} - */ - radius: 1, - - /** - * @type {number} - */ - height: 2, - - /** - * @type {number} - */ - capSegments: 50, - - /** - * @type {number} - */ - heightSegments: 1 - }, function() { - this.build(); - }, - /** @lends qtek.geometry.Cylinder.prototype */ - { - /** - * Build cylinder geometry - */ - build: function() { - var cone = new ConeGeometry({ - topRadius: this.radius, - bottomRadius: this.radius, - capSegments: this.capSegments, - heightSegments: this.heightSegments, - height: this.height - }); - - this.attributes.position.value = cone.attributes.position.value; - this.attributes.normal.value = cone.attributes.normal.value; - this.attributes.texcoord0.value = cone.attributes.texcoord0.value; - this.faces = cone.faces; - - this.boundingBox = cone.boundingBox; - } - }); - - return Cylinder; -}); -define('qtek/light/Ambient',['require','../Light'],function(require) { - - - - var Light = require('../Light'); - - /** - * @constructor qtek.light.Ambient - * @extends qtek.Light - */ - var AmbientLight = Light.derive({ - castShadow: false - }, { - - type: 'AMBIENT_LIGHT', - - uniformTemplates: { - 'ambientLightColor': { - type: '3f', - value: function(instance) { - var color = instance.color, - intensity = instance.intensity; - return [color[0]*intensity, color[1]*intensity, color[2]*intensity]; - } - } - } - /** - * @method - * @name clone - * @return {qtek.light.Ambient} - * @memberOf qtek.light.Ambient.prototype - */ - }); - - return AmbientLight; -}); -define('qtek/light/Directional',['require','../Light','../math/Vector3'],function(require) { - - - - var Light = require('../Light'); - var Vector3 = require('../math/Vector3'); - - /** - * @constructor qtek.light.Directional - * @extends qtek.Light - * - * @example - * var light = new qtek.light.Directional({ - * intensity: 0.5, - * color: [1.0, 0.0, 0.0] - * }); - * light.position.set(10, 10, 10); - * light.lookAt(qtek.math.Vector3.ZERO); - * scene.add(light); - */ - var DirectionalLight = Light.derive( - /** @lends qtek.light.Directional# */ - { - /** - * @type {number} - */ - shadowBias: 0.0002, - /** - * @type {number} - */ - shadowSlopeScale: 2.0 - }, { - - type: 'DIRECTIONAL_LIGHT', - - uniformTemplates: { - 'directionalLightDirection': { - type: '3f', - value: (function() { - var z = new Vector3(); - return function(instance) { - return z.copy(instance.worldTransform.z).negate()._array; - }; - })() - }, - 'directionalLightColor': { - type: '3f', - value: function(instance) { - var color = instance.color; - var intensity = instance.intensity; - return [color[0] * intensity, color[1] * intensity, color[2] * intensity]; - } - } - }, - /** - * @return {qtek.light.Directional} - * @memberOf qtek.light.Directional.prototype - */ - clone: function() { - var light = Light.prototype.clone.call(this); - light.shadowBias = this.shadowBias; - light.shadowSlopeScale = this.shadowSlopeScale; - return light; - } - }); - - return DirectionalLight; -}); -define('qtek/light/Point',['require','../Light'],function(require) { - - - - var Light = require('../Light'); - - /** - * @constructor qtek.light.Point - * @extends qtek.Light - */ - var PointLight = Light.derive( - /** @lends qtek.light.Point# */ - { - /** - * @type {number} - */ - range: 100, - - /** - * @type {number} - */ - castShadow: false - }, { - - type: 'POINT_LIGHT', - - uniformTemplates: { - 'pointLightPosition': { - type: '3f', - value: function(instance) { - return instance.getWorldPosition()._array; - } - }, - 'pointLightRange': { - type: '1f', - value: function(instance) { - return instance.range; - } - }, - 'pointLightColor': { - type: '3f', - value: function(instance) { - var color = instance.color, - intensity = instance.intensity; - return [ color[0]*intensity, color[1]*intensity, color[2]*intensity ]; - } - } - }, - /** - * @return {qtek.light.Point} - * @memberOf qtek.light.Point.prototype - */ - clone: function() { - var light = Light.prototype.clone.call(this); - light.range = this.range; - return light; - } - }); - - return PointLight; -}); -define('qtek/light/Spot',['require','../Light','../math/Vector3'],function(require) { - - - - var Light = require('../Light'); - var Vector3 = require('../math/Vector3'); - - /** - * @constructor qtek.light.Spot - * @extends qtek.Light - */ - var SpotLight = Light.derive( - /**@lends qtek.light.Spot */ - { - /** - * @type {number} - */ - range: 20, - /** - * @type {number} - */ - umbraAngle: 30, - /** - * @type {number} - */ - penumbraAngle: 45, - /** - * @type {number} - */ - falloffFactor: 2.0, - /** - * @type {number} - */ - shadowBias: 0.0002, - /** - * @type {number} - */ - shadowSlopeScale: 2.0 - },{ - - type: 'SPOT_LIGHT', - - uniformTemplates: { - 'spotLightPosition': { - type: '3f', - value: function(instance) { - return instance.getWorldPosition()._array; - } - }, - 'spotLightRange': { - type: '1f', - value: function(instance) { - return instance.range; - } - }, - 'spotLightUmbraAngleCosine': { - type: '1f', - value: function(instance) { - return Math.cos(instance.umbraAngle * Math.PI / 180); - } - }, - 'spotLightPenumbraAngleCosine': { - type: '1f', - value: function(instance) { - return Math.cos(instance.penumbraAngle * Math.PI / 180); - } - }, - 'spotLightFalloffFactor': { - type: '1f', - value: function(instance) { - return instance.falloffFactor; - } - }, - 'spotLightDirection': { - type: '3f', - value: (function() { - var z = new Vector3(); - return function(instance) { - // Direction is target to eye - return z.copy(instance.worldTransform.z).negate()._array; - }; - })() - }, - 'spotLightColor': { - type: '3f', - value: function(instance) { - var color = instance.color; - var intensity = instance.intensity; - return [color[0] * intensity, color[1] * intensity, color[2] * intensity]; - } - } - }, - /** - * @return {qtek.light.Spot} - * @memberOf qtek.light.Spot.prototype - */ - clone: function() { - var light = Light.prototype.clone.call(this); - light.range = this.range; - light.umbraAngle = this.umbraAngle; - light.penumbraAngle = this.penumbraAngle; - light.falloffFactor = this.falloffFactor; - light.shadowBias = this.shadowBias; - light.shadowSlopeScale = this.shadowSlopeScale; - return light; - } - }); - - return SpotLight; -}); -define('qtek/loader/FX',['require','../core/Base','../core/request','../core/util','../compositor/Compositor','../compositor/Node','../Shader','../Texture','../Texture2D','../TextureCube'],function(require) { - - - - var Base = require('../core/Base'); - var request = require('../core/request'); - var util = require('../core/util'); - var Compositor = require('../compositor/Compositor'); - var CompoNode = require('../compositor/Node'); - var Shader = require('../Shader'); - var Texture = require('../Texture'); - var Texture2D = require('../Texture2D'); - var TextureCube = require('../TextureCube'); - - var shaderSourceReg = /#source\((.*?)\)/; - var urlReg = /#url\((.*?)\)/; - - /** - * @constructor qtek.loader.FX - * @extends qtek.core.Base - */ - var FXLoader = Base.derive( - /** @lends qtek.loader.FX# */ - { - /** - * @type {string} - */ - rootPath: '', - /** - * @type {string} - */ - textureRootPath: '', - /** - * @type {string} - */ - shaderRootPath: '' - }, - /** @lends qtek.loader.FX.prototype */ - { - /** - * @param {string} url - */ - load: function(url) { - var self = this; - - if (!this.rootPath) { - this.rootPath = url.slice(0, url.lastIndexOf('/')); - } - - request.get({ - url: url, - onprogress: function(percent, loaded, total) { - self.trigger('progress', percent, loaded, total); - }, - onerror: function(e) { - self.trigger('error', e); - }, - responseType: 'text', - onload: function(data) { - self.parse(JSON.parse(data)); - } - }); - }, - - /** - * @param {Object} json - * @return {qtek.compositor.Compositor} - */ - parse: function(json) { - var self = this; - var compositor = new Compositor(); - - var lib = { - textures: {}, - shaders: {}, - parameters: {} - }; - var afterLoad = function(shaderLib, textureLib) { - for (var i = 0; i < json.nodes.length; i++) { - var nodeInfo = json.nodes[i]; - var node = self._createNode(nodeInfo, lib); - if (node) { - compositor.addNode(node); - } - if (nodeInfo.output) { - compositor.addOutput(node); - } - } - - self.trigger('success', compositor); - }; - - for (var name in json.parameters) { - var paramInfo = json.parameters[name]; - lib.parameters[name] = this._convertParameter(paramInfo); - } - this._loadShaders(json, function(shaderLib) { - self._loadTextures(json, lib, function(textureLib) { - lib.textures = textureLib; - lib.shaders = shaderLib; - afterLoad(); - }); - }); - - return compositor; - }, - - _createNode: function(nodeInfo, lib) { - if (!nodeInfo.shader) { - return; - } - var type = nodeInfo.type || 'filter'; - var shaderSource; - var inputs; - var outputs; - - if (type === 'filter') { - var shaderExp = nodeInfo.shader.trim(); - var res = shaderSourceReg.exec(shaderExp); - if (res) { - shaderSource = Shader.source(res[1].trim()); - } else if (shaderExp.charAt(0) === '#') { - shaderSource = lib.shaders[shaderExp.substr(1)]; - } - if (!shaderSource) { - shaderSource = shaderExp; - } - if (!shaderSource) { - return; - } - } - - if (nodeInfo.inputs) { - inputs = {}; - for (var name in nodeInfo.inputs) { - inputs[name] = { - node: nodeInfo.inputs[name].node, - pin: nodeInfo.inputs[name].pin - }; - } - } - if (nodeInfo.outputs) { - outputs = {}; - for (var name in nodeInfo.outputs) { - var outputInfo = nodeInfo.outputs[name]; - outputs[name] = {}; - if (outputInfo.attachment !== undefined) { - outputs[name].attachment = outputInfo.attachment; - } - if (outputInfo.keepLastFrame !== undefined) { - outputs[name].keepLastFrame = outputInfo.keepLastFrame; - } - if (outputInfo.outputLastFrame !== undefined) { - outputs[name].outputLastFrame = outputInfo.outputLastFrame; - } - if (typeof(outputInfo.parameters) === 'string') { - var paramExp = outputInfo.parameters; - if (paramExp.charAt(0) === '#') { - outputs[name].parameters = lib.parameters[paramExp.substr(1)]; - } - } else if (outputInfo.parameters) { - outputs[name].parameters = this._convertParameter(outputInfo.parameters); - } - } - } - var node; - if (type === 'filter') { - node = new CompoNode({ - name: nodeInfo.name, - shader: shaderSource, - inputs: inputs, - outputs: outputs - }); - } - if (node) { - if (nodeInfo.parameters) { - for (var name in nodeInfo.parameters) { - var val = nodeInfo.parameters[name]; - if (typeof(val) === 'string') { - val = val.trim(); - if (val.charAt(0) === '#'){ - val = lib.textures[val.substr(1)]; - } else if (val.match(/%width$/)) { - node.on('beforerender', createWidthSetHandler(name, val)); - continue; - } else if (val.match(/%height$/)) { - node.on('beforerender', createHeightSetHandler(name, val)); - continue; - } - } else if (val instanceof Array) { - if ( - typeof(val[0]) === 'string' && typeof(val[1]) === 'string' - && (val[0].match(/%width$/)) - && (val[1].match(/%height$/)) - ) { - node.on('beforerender', createSizeSetHandler(name, val)); - continue; - } - } - node.setParameter(name, val); - } - } - if (nodeInfo.defines) { - for (var name in nodeInfo.defines) { - var val = nodeInfo.defines[name]; - node.pass.material.shader.define('fragment', name, val); - } - } - } - return node; - }, - - _convertParameter: function(paramInfo) { - var param = {}; - if (!paramInfo) { - return param; - } - ['type', 'minFilter', 'magFilter', 'wrapS', 'wrapT'] - .forEach(function(name) { - var val = paramInfo[name]; - if (val !== undefined) { - // Convert string to enum - if (typeof(val) === 'string') { - val = Texture[val]; - } - param[name] = val; - } - }); - ['width', 'height'] - .forEach(function(name) { - if (paramInfo[name] !== undefined) { - var val = paramInfo[name]; - if (typeof val === 'string') { - val = val.trim(); - if (val.match(/%width$/)) { - param[name] = percentToWidth.bind(null, parseFloat(val)); - } - else if (val.match(/%height$/)) { - param[name] = percentToHeight.bind(null, parseFloat(val)); - } - } else { - param[name] = val; - } - } - }); - if (paramInfo.useMipmap !== undefined) { - param.useMipmap = paramInfo.useMipmap; - } - return param; - }, - - _loadShaders: function(json, callback) { - if (!json.shaders) { - callback({}); - return; - } - var shaders = {}; - var loading = 0; - var cbd = false; - var shaderRootPath = this.shaderRootPath || this.rootPath; - util.each(json.shaders, function(shaderExp, name) { - var res = urlReg.exec(shaderExp); - if (res) { - var path = res[1]; - path = util.relative2absolute(path, shaderRootPath); - loading++; - request.get({ - url: path, - onload: function(shaderSource) { - shaders[name] = shaderSource; - Shader['import'](shaderSource); - loading--; - if (loading === 0) { - callback(shaders); - cbd = true; - } - } - }); - } else { - shaders[name] = shaderExp; - // Try import shader - Shader['import'](shaderExp); - } - }, this); - if (loading === 0 && !cbd) { - callback(shaders); - } - }, - - _loadTextures: function(json, lib, callback) { - if (!json.textures) { - callback({}); - return; - } - var textures = {}; - var loading = 0; - - var cbd = false; - var textureRootPath = this.textureRootPath || this.rootPath; - util.each(json.textures, function(textureInfo, name) { - var texture; - var path = textureInfo.path; - var parameters = this._convertParameter(textureInfo.parameters); - if (path instanceof Array && path.length === 6) { - path = path.map(function(item) { - return util.relative2absolute(item, textureRootPath); - }); - texture = new TextureCube(parameters); - } else if(typeof(path) === 'string') { - path = util.relative2absolute(path, textureRootPath); - texture = new Texture2D(parameters); - } else { - return; - } - - texture.load(path); - loading++; - texture.once('success', function() { - textures[name] = texture; - loading--; - if (loading === 0) { - callback(textures); - cbd = true; - } - }); - }, this); - - if (loading === 0 && !cbd) { - callback(textures); - } - } - }); - - function createWidthSetHandler(name, val) { - val = parseFloat(val); - return function (renderer) { - this.setParameter(name, percentToWidth(val, renderer)); - }; - } - function createHeightSetHandler(name, val) { - val = parseFloat(val); - return function (renderer) { - this.setParameter(name, percentToWidth(val, renderer)); - }; - } - - function createSizeSetHandler(name, val) { - val[0] = parseFloat(val[0]); - val[1] = parseFloat(val[1]); - return function (renderer) { - this.setParameter(name, [percentToWidth(val[0], renderer), percentToHeight(val[0], renderer)]); - }; - } - - function percentToWidth(percent, renderer) { - return percent / 100 * renderer.width; - } - - function percentToHeight(percent, renderer) { - return percent / 100 * renderer.height; - } - - return FXLoader; -}); -/** - * glTF Loader - * Specification https://github.com/KhronosGroup/glTF/blob/master/specification/README.md - * - * TODO https://github.com/KhronosGroup/glTF/issues/298 - */ -define('qtek/loader/GLTF',['require','../core/Base','../core/request','../core/util','../Scene','../Shader','../Material','../Mesh','../Node','../Texture','../Texture2D','../TextureCube','../shader/library','../Skeleton','../Joint','../camera/Perspective','../camera/Orthographic','../light/Point','../light/Spot','../light/Directional','../core/glenum','../math/Vector3','../math/Quaternion','../math/BoundingBox','../animation/SamplerClip','../animation/SkinningClip','../StaticGeometry','../dep/glmatrix'],function(require) { - - - - var Base = require('../core/Base'); - var request = require('../core/request'); - var util = require('../core/util'); - - var Scene = require('../Scene'); - var Shader = require('../Shader'); - var Material = require('../Material'); - var Mesh = require('../Mesh'); - var Node = require('../Node'); - var Texture = require('../Texture'); - var Texture2D = require('../Texture2D'); - var TextureCube = require('../TextureCube'); - var shaderLibrary = require('../shader/library'); - var Skeleton = require('../Skeleton'); - var Joint = require('../Joint'); - var PerspectiveCamera = require('../camera/Perspective'); - var OrthographicCamera = require('../camera/Orthographic'); - var PointLight = require('../light/Point'); - var SpotLight = require('../light/Spot'); - var DirectionalLight = require('../light/Directional'); - var glenum = require('../core/glenum'); - - var Vector3 = require('../math/Vector3'); - var Quaternion = require('../math/Quaternion'); - var BoundingBox = require('../math/BoundingBox'); - - var SamplerClip = require('../animation/SamplerClip'); - var SkinningClip = require('../animation/SkinningClip'); - - var StaticGeometry = require('../StaticGeometry'); - - var glMatrix = require('../dep/glmatrix'); - var quat = glMatrix.quat; - - var semanticAttributeMap = { - 'NORMAL': 'normal', - 'POSITION': 'position', - 'TEXCOORD_0': 'texcoord0', - 'WEIGHT': 'weight', - 'JOINT': 'joint', - 'COLOR': 'color' - }; - - /** - * @typedef {Object} qtek.loader.GLTF.IResult - * @property {qtek.Scene} scene - * @property {qtek.Node} rootNode - * @property {Object.} cameras - * @property {Object.} textures - * @property {Object.} materials - * @property {Object.} skeletons - * @property {Object.} meshes - * @property {qtek.animation.SkinningClip} clip - */ - - /** - * @constructor qtek.loader.GLTF - * @extends qtek.core.Base - */ - var GLTFLoader = Base.derive( - /** @lends qtek.loader.GLTF# */ - { - /** - * @type {qtek.Node} - */ - rootNode: null, - /** - * @type {string} - */ - rootPath: '', - - /** - * @type {string} - */ - textureRootPath: '', - - /** - * @type {string} - */ - bufferRootPath: '', - - /** - * @type {string} - */ - shaderName: 'buildin.standard', - - /** - * @type {boolean} - */ - includeCamera: true, - - /** - * @type {boolean} - */ - includeLight: true, - - /** - * @type {boolean} - */ - includeAnimation: true, - /** - * @type {boolean} - */ - includeMesh: true - }, - - /** @lends qtek.loader.GLTF.prototype */ - { - /** - * @param {string} url - */ - load: function(url) { - var self = this; - - if (!this.rootPath) { - this.rootPath = url.slice(0, url.lastIndexOf('/')); - } - - request.get({ - url: url, - onprogress: function(percent, loaded, total) { - self.trigger('progress', percent, loaded, total); - }, - onerror: function(e) { - self.trigger('error', e); - }, - responseType: 'text', - onload: function(data) { - self.parse(JSON.parse(data)); - } - }); - }, - - /** - * @param {Object} json - * @return {qtek.loader.GLTF.IResult} - */ - parse: function(json) { - var self = this; - var loading = 0; - - var lib = { - buffers: {}, - materials: {}, - textures: {}, - meshes: {}, - joints: {}, - skeletons: {}, - cameras: {}, - nodes: {} - }; - // Mount on the root node if given - var rootNode = this.rootNode || new Scene(); - // Load buffers - util.each(json.buffers, function(bufferInfo, name) { - loading++; - self._loadBuffer(bufferInfo.path, function(buffer) { - lib.buffers[name] = buffer; - loading--; - if (loading === 0) { - afterLoadBuffer(); - } - }, function() { - loading--; - if (loading === 0) { - afterLoadBuffer(); - } - }); - }); - - function afterLoadBuffer() { - if (self.includeMesh) { - self._parseTextures(json, lib); - self._parseMaterials(json, lib); - self._parseMeshes(json, lib); - } - self._parseNodes(json, lib); - - var sceneInfo = json.scenes[json.scene]; - for (var i = 0; i < sceneInfo.nodes.length; i++) { - var node = lib.nodes[sceneInfo.nodes[i]]; - node.update(); - rootNode.add(node); - } - - if (self.includeMesh) { - self._parseSkins(json, lib); - } - - if (self.includeAnimation) { - var clip = self._parseAnimations(json, lib); - if (clip) { - for (var name in lib.skeletons) { - lib.skeletons[name].addClip(clip); - } - } - } - - self.trigger('success', { - scene: self.rootNode ? null : rootNode, - rootNode: self.rootNode ? rootNode : null, - cameras: lib.cameras, - textures: lib.textures, - materials: lib.materials, - skeletons: lib.skeletons, - meshes: lib.meshes, - clip: clip - }); - } - - return { - scene: self.rootNode ? null : rootNode, - rootNode: self.rootNode ? rootNode : null, - cameras: lib.cameras, - textures: lib.textures, - materials: lib.materials, - skeletons: lib.skeletons, - meshes: lib.meshes, - clip: null - }; - }, - - _loadBuffer: function(path, onsuccess, onerror) { - var root = this.bufferRootPath || this.rootPath; - if (root) { - path = root + '/' + path; - } - request.get({ - url: path, - responseType: 'arraybuffer', - onload: function(buffer) { - onsuccess && onsuccess(buffer); - }, - onerror: function(buffer) { - onerror && onerror(buffer); - } - }); - }, - - // https://github.com/KhronosGroup/glTF/issues/100 - // https://github.com/KhronosGroup/glTF/issues/193 - _parseSkins: function(json, lib) { - - // Create skeletons and joints - var haveInvBindMatrices = false; - for (var name in json.skins) { - var skinInfo = json.skins[name]; - var skeleton = new Skeleton({ - name: name - }); - for (var i = 0; i < skinInfo.joints.length; i++) { - var jointId = skinInfo.joints[i]; - var joint = new Joint({ - name: jointId, - index: skeleton.joints.length - }); - skeleton.joints.push(joint); - } - if (skinInfo.inverseBindMatrices) { - haveInvBindMatrices = true; - var IBMInfo = skinInfo.inverseBindMatrices; - var bufferViewName = IBMInfo.bufferView; - var bufferViewInfo = json.bufferViews[bufferViewName]; - var buffer = lib.buffers[bufferViewInfo.buffer]; - - var offset = IBMInfo.byteOffset + bufferViewInfo.byteOffset; - var size = IBMInfo.count * 16; - - var array = new Float32Array(buffer, offset, size); - - skeleton._invBindPoseMatricesArray = array; - skeleton._skinMatricesArray = new Float32Array(array.length); - } - lib.skeletons[name] = skeleton; - } - - var bindNodeToJoint = function(jointsMap, nodeName, parentIndex, rootNode) { - var node = lib.nodes[nodeName]; - var nodeInfo = json.nodes[nodeName]; - var joint = jointsMap[nodeInfo.jointId]; - if (joint) { - // throw new Error('Joint bind to ' + nodeInfo.name + ' doesn\'t exist in skin'); - joint.node = node; - joint.parentIndex = parentIndex; - joint.rootNode = rootNode; - parentIndex = joint.index; - } - else { - // Some root node may be a simple transform joint, without deformation data. - // Which is, no vertex is attached to the joint - // PENDING - joint = new Joint({ - node: node, - rootNode: rootNode, - parentIndex: parentIndex - }); - } - - for (var i = 0; i < nodeInfo.children.length; i++) { - bindNodeToJoint(jointsMap, nodeInfo.children[i], parentIndex, rootNode); - } - - return joint; - }; - - var getJointIndex = function(joint) { - return joint.index; - }; - - var instanceSkins = {}; - - for (var name in json.nodes) { - - var nodeInfo = json.nodes[name]; - - if (nodeInfo.instanceSkin) { - var skinName = nodeInfo.instanceSkin.skin; - var skeleton = lib.skeletons[skinName]; - instanceSkins[skinName] = skeleton; - - var node = lib.nodes[name]; - var jointIndices = skeleton.joints.map(getJointIndex); - if (node instanceof Mesh) { - node.skeleton = skeleton; - node.joints = jointIndices; - var material = node.material; - material.shader = material.shader.clone(); - material.shader.define('vertex', 'SKINNING'); - material.shader.define('vertex', 'JOINT_NUMBER', jointIndices.length); - } else { - // Mesh have multiple primitives - for (var i = 0; i < node._children.length; i++) { - var child = node._children[i]; - if (child.skeleton) { - child.skeleton = skeleton; - child.joints = jointIndices; - var material = child.material; - material.shader = material.shader.clone(); - material.shader.define('vertex', 'SKINNING'); - material.shader.define('vertex', 'JOINT_NUMBER', jointIndices.length); - } - } - } - - var jointsMap = {}; - for (var i = 0; i < skeleton.joints.length; i++) { - var joint = skeleton.joints[i]; - jointsMap[joint.name] = joint; - } - // Build up hierarchy from root nodes - var rootNodes = nodeInfo.instanceSkin.skeletons; - for (i = 0; i < rootNodes.length; i++) { - var rootNode = lib.nodes[rootNodes[i]]; - var rootJoint = bindNodeToJoint(jointsMap, rootNodes[i], -1, rootNode); - // Root joint may not in the skeleton - if (rootJoint) { - skeleton.roots.push(rootJoint); - } - } - } - } - - for (var name in instanceSkins) { - var skeleton = instanceSkins[name]; - if (haveInvBindMatrices) { - skeleton.updateMatricesSubArrays(); - } else { - skeleton.updateJointMatrices(); - } - skeleton.update(); - } - }, - - _parseTextures: function(json, lib) { - var root = this.textureRootPath || this.rootPath; - util.each(json.textures, function(textureInfo, name){ - var samplerInfo = json.samplers[textureInfo.sampler]; - var parameters = {}; - ['wrapS', 'wrapT', 'magFilter', 'minFilter'] - .forEach(function(name) { - var value = samplerInfo[name]; - if (value !== undefined) { - if (typeof(value) === 'string') { - // DEPRECATED, sampler parameter now use gl enum instead of string - value = glenum[value]; - } - parameters[name] = value; - } - }); - - var target = textureInfo.target; - var format = textureInfo.format; - if (typeof(target) === 'string') { - // DEPRECATED - target = glenum[target]; - format = glenum[format]; - } - parameters.format = format; - - if (target === glenum.TEXTURE_2D) { - var texture = new Texture2D(parameters); - var imageInfo = json.images[textureInfo.source]; - texture.load(util.relative2absolute(imageInfo.path, root)); - lib.textures[name] = texture; - } else if(target === glenum.TEXTURE_CUBE_MAP) { - // TODO - } - }, this); - }, - - // Only phong material is support yet - // TODO support custom material - _parseMaterials: function(json, lib) { - var techniques = {}; - // Parse techniques - for (var name in json.techniques) { - var techniqueInfo = json.techniques[name]; - // Default phong shader - // var shader = new Shader({ - // vertex: Shader.source('buildin.phong.vertex'), - // fragment: Shader.source('buildin.phong.fragment') - // }); - techniques[name] = { - // shader: shader, - pass: techniqueInfo.passes[techniqueInfo.pass] - }; - } - for (var name in json.materials) { - var materialInfo = json.materials[name]; - - var instanceTechniqueInfo = materialInfo.instanceTechnique; - var technique = techniques[instanceTechniqueInfo.technique]; - var pass = technique.pass; - var uniforms = {}; - - uniforms = instanceTechniqueInfo.values; - for (var symbol in uniforms) { - var value = uniforms[symbol]; - // TODO: texture judgement should be more robust - if (typeof(value) === 'string' && lib.textures[value]) { - uniforms[symbol] = lib.textures[value]; - } - } - var enabledTextures = []; - if (uniforms['diffuse'] instanceof Texture2D) { - enabledTextures.push('diffuseMap'); - } - if (uniforms['normalMap'] instanceof Texture2D) { - enabledTextures.push('normalMap'); - } - var material = new Material({ - name: materialInfo.name, - shader: shaderLibrary.get(this.shaderName, enabledTextures) - }); - if (pass.states.depthMask !== undefined) { - material.depthMask = pass.states.depthMask; - } - if (pass.states.depthTestEnable !== undefined) { - material.depthTest = pass.states.depthTestEnable; - } - material.cullFace = pass.states.cullFaceEnable || false; - if (pass.states.blendEnable) { - material.transparent = true; - // TODO blend Func and blend Equation - } - - if (uniforms['diffuse']) { - // Color - if (uniforms['diffuse'] instanceof Array) { - material.set('color', uniforms['diffuse'].slice(0, 3)); - } else { // Texture - material.set('diffuseMap', uniforms['diffuse']); - } - } - if (uniforms['normalMap'] !== undefined) { - material.set('normalMap', uniforms['normalMap']); - } - if (uniforms['emission'] !== undefined) { - material.set('emission', uniforms['emission'].slice(0, 3)); - } - if (uniforms['shininess'] !== undefined) { - material.set('glossiness', Math.log(uniforms['shininess']) / Math.log(8192)); - material.set('shininess', uniforms['shininess']); - } else { - material.set('glossiness', 0.5); - material.set('shininess', 0.5); - } - if (uniforms['specular'] !== undefined) { - material.set('specularColor', uniforms['specular'].slice(0, 3)); - } - if (uniforms['transparency'] !== undefined) { - material.set('alpha', uniforms['transparency']); - } - - lib.materials[name] = material; - } - }, - - _parseMeshes: function(json, lib) { - var self = this; - - var meshKeys = Object.keys(json.meshes); - for (var nn = 0; nn < meshKeys.length; nn++) { - var name = meshKeys[nn]; - var meshInfo = json.meshes[name]; - - lib.meshes[name] = []; - // Geometry - for (var pp = 0; pp < meshInfo.primitives.length; pp++) { - var primitiveInfo = meshInfo.primitives[pp]; - var geometry = new StaticGeometry({ - boundingBox: new BoundingBox() - }); - // Parse attributes - var semantics = Object.keys(primitiveInfo.attributes); - for (var ss = 0; ss < semantics.length; ss++) { - var semantic = semantics[ss]; - var accessorName = primitiveInfo.attributes[semantic]; - var attributeInfo = json.accessors[accessorName]; - var attributeName = semanticAttributeMap[semantic]; - if (!attributeName) { - continue; - } - var attributeType = attributeInfo.type; - var bufferViewInfo = json.bufferViews[attributeInfo.bufferView]; - var buffer = lib.buffers[bufferViewInfo.buffer]; - var byteOffset = bufferViewInfo.byteOffset + attributeInfo.byteOffset; - - var size; - var ArrayCtor; - var type; - switch(attributeType) { - case 0x8B50: // FLOAT_VEC2 - size = 2; - type = 'float'; - ArrayCtor = Float32Array; - break; - case 0x8B51: // FLOAT_VEC3 - size = 3; - type = 'float'; - ArrayCtor = Float32Array; - break; - case 0x8B52: // FLOAT_VEC4 - size = 4; - type = 'float'; - ArrayCtor = Float32Array; - break; - case 0x1406: // FLOAT - size = 1; - type = 'float'; - ArrayCtor = Float32Array; - break; - default: - console.warn('Attribute type '+attributeInfo.type+' not support yet'); - break; - } - var attributeArray = new ArrayCtor(buffer, byteOffset, attributeInfo.count * size); - if (semantic === 'WEIGHT' && size === 4) { - // Weight data in QTEK has only 3 component, the last component can be evaluated since it is normalized - var weightArray = new ArrayCtor(attributeInfo.count * 3); - for (var i = 0; i < attributeInfo.count; i++) { - weightArray[i * 3] = attributeArray[i * 4]; - weightArray[i * 3 + 1] = attributeArray[i * 4 + 1]; - weightArray[i * 3 + 2] = attributeArray[i * 4 + 2]; - } - geometry.attributes[attributeName].value = weightArray; - } else { - geometry.attributes[attributeName].value = attributeArray; - } - if (semantic === 'POSITION') { - // Bounding Box - var min = attributeInfo.min; - var max = attributeInfo.max; - if (min) { - geometry.boundingBox.min.set(min[0], min[1], min[2]); - } - if (max) { - geometry.boundingBox.max.set(max[0], max[1], max[2]); - } - } - } - - // Parse indices - var indicesInfo = json.accessors[primitiveInfo.indices]; - - var bufferViewInfo = json.bufferViews[indicesInfo.bufferView]; - var buffer = lib.buffers[bufferViewInfo.buffer]; - var byteOffset = bufferViewInfo.byteOffset + indicesInfo.byteOffset; - - // index uint - if (indicesInfo.type === 0x1405) { // UNSIGNED_INT - geometry.faces = new Uint32Array(buffer, byteOffset, indicesInfo.count); - } - else { // UNSIGNED_SHORT, 0x1403 - geometry.faces = new Uint16Array(buffer, byteOffset, indicesInfo.count); - } - - var material = lib.materials[primitiveInfo.material]; - //Collada export from blender may not have default material - if (!material) { - material = new Material({ - shader: shaderLibrary.get(self.shaderName) - }); - } - var mesh = new Mesh({ - geometry: geometry, - material: material - }); - if (material.shader.isTextureEnabled('normalMap')) { - if (!mesh.geometry.attributes.tangent.value) { - mesh.geometry.generateTangents(); - } - } - - if (meshInfo.name) { - if (meshInfo.primitives.length > 1) { - mesh.name = [meshInfo.name, pp].join('-'); - } - else { - // PENDING name or meshInfo.name ? - mesh.name = meshInfo.name; - } - } - - lib.meshes[name].push(mesh); - } - } - }, - - _parseNodes: function(json, lib) { - - for (var name in json.nodes) { - var nodeInfo = json.nodes[name]; - var node; - if (nodeInfo.camera && this.includeCamera) { - var cameraInfo = json.cameras[nodeInfo.camera]; - - if (cameraInfo.projection === 'perspective') { - node = new PerspectiveCamera({ - name: nodeInfo.name, - aspect: cameraInfo.aspect_ratio, - fov: cameraInfo.xfov, - far: cameraInfo.zfar, - near: cameraInfo.znear - }); - } else { - // TODO - node = new OrthographicCamera(); - console.warn('TODO:Orthographic camera'); - } - node.setName(nodeInfo.name); - lib.cameras[nodeInfo.name] = node; - } - else if (nodeInfo.lights && this.includeLight) { - var lights = []; - for (var i = 0; i < nodeInfo.lights.length; i++) { - var lightInfo = json.lights[nodeInfo.lights[i]]; - var light = this._parseLight(lightInfo); - if (light) { - lights.push(light); - } - } - if (lights.length == 1) { - // Replace the node with light - node = lights[0]; - node.setName(nodeInfo.name); - } else { - node = new Node(); - node.setName(nodeInfo.name); - for (var i = 0; i < lights.length; i++) { - node.add(lights[i]); - } - } - } - else if ((nodeInfo.meshes || nodeInfo.instanceSkin) && this.includeMesh) { - // TODO one node have multiple meshes ? - var meshKey; - if (nodeInfo.meshes) { - meshKey = nodeInfo.meshes[0]; - } else { - meshKey = nodeInfo.instanceSkin.sources[0]; - } - if (meshKey) { - var primitives = lib.meshes[meshKey]; - if (primitives) { - if (primitives.length === 1) { - // Replace the node with mesh directly - node = primitives[0]; - node.setName(nodeInfo.name); - } else { - node = new Node(); - node.setName(nodeInfo.name); - for (var j = 0; j < primitives.length; j++) { - if (nodeInfo.instanceSkin) { - primitives[j].skeleton = nodeInfo.instanceSkin.skin; - } - node.add(primitives[j]); - } - } - } - } - } else { - node = new Node(); - node.setName(nodeInfo.name); - } - if (nodeInfo.matrix) { - for (var i = 0; i < 16; i++) { - node.localTransform._array[i] = nodeInfo.matrix[i]; - } - node.decomposeLocalTransform(); - } else { - if (nodeInfo.translation) { - node.position.setArray(nodeInfo.translation); - } - if (nodeInfo.rotation) { - // glTF use axis angle in rotation - // https://github.com/KhronosGroup/glTF/issues/144 - quat.setAxisAngle(node.rotation._array, nodeInfo.rotation.slice(0, 3), nodeInfo.rotation[3]); - node.rotation._dirty = true; - } - if (nodeInfo.scale) { - node.scale.setArray(nodeInfo.scale); - } - } - - lib.nodes[name] = node; - } - - // Build hierarchy - for (var name in json.nodes) { - var nodeInfo = json.nodes[name]; - var node = lib.nodes[name]; - if (nodeInfo.children) { - for (var i = 0; i < nodeInfo.children.length; i++) { - var childName = nodeInfo.children[i]; - var child = lib.nodes[childName]; - node.add(child); - } - } - } - }, - - _parseLight: function(lightInfo) { - // TODO Light parameters - switch(lightInfo.type) { - case 'point': - var light = new PointLight({ - name: lightInfo.id, - color: lightInfo.point.color, - }); - break; - case 'spot': - var light = new SpotLight({ - name: lightInfo.id, - color: lightInfo.spot.color - }); - break; - case 'directional': - var light = new DirectionalLight({ - name: lightInfo.id, - color: lightInfo.directional.color - }); - break; - default: - console.warn('Light ' + lightInfo.type + ' not support yet'); - } - - return light; - }, - - _parseAnimations: function(json, lib) { - // TODO Only support nodes animation now - var clip = new SkinningClip(); - // Default loop the skinning animation - clip.setLoop(true); - var haveAnimation = false; - - var jointClips = {}; - - var quatTmp = quat.create(); - - for (var animName in json.animations) { - haveAnimation = true; - var animationInfo = json.animations[animName]; - var parameters = {}; - - for (var paramName in animationInfo.parameters) { - var accessorName = animationInfo.parameters[paramName]; - var accessorInfo = json.accessors[accessorName]; - - var bufferViewInfo = json.bufferViews[accessorInfo.bufferView]; - var buffer = lib.buffers[bufferViewInfo.buffer]; - var byteOffset = bufferViewInfo.byteOffset + accessorInfo.byteOffset; - switch(accessorInfo.type) { - case 0x8B50: // FLOAT_VEC2 - var size = 2; - break; - case 0x8B51: // FLOAT_VEC3 - var size = 3; - break; - case 0x8B52: // FLOAT_VEC4 - var size = 4; - break; - case 0x1406: // FLOAT - var size = 1; - break; - } - parameters[paramName] = new Float32Array(buffer, byteOffset, size * accessorInfo.count); - } - - if (!parameters.TIME || !animationInfo.channels.length) { - continue; - } - - // Use the first channels target - var targetId = animationInfo.channels[0].target.id; - var targetNode = lib.nodes[targetId]; - - // glTF use axis angle in rotation, convert to quaternion - // https://github.com/KhronosGroup/glTF/issues/144 - var rotationArr = parameters.rotation; - if (rotationArr) { - for (var i = 0; i < parameters.TIME.length; i++) { - parameters.TIME[i] *= 1000; - var offset = i * 4; - if (rotationArr) { - quatTmp[0] = rotationArr[offset]; - quatTmp[1] = rotationArr[offset + 1]; - quatTmp[2] = rotationArr[offset + 2]; - quat.setAxisAngle(quatTmp, quatTmp, rotationArr[offset + 3]); - parameters.rotation[offset] = quatTmp[0]; - parameters.rotation[offset + 1] = quatTmp[1]; - parameters.rotation[offset + 2] = quatTmp[2]; - parameters.rotation[offset + 3] = quatTmp[3]; - } - } - } - - // TODO - // if (jointClips[targetId]) { - // continue; - // } - jointClips[targetId] = new SamplerClip({ - name: targetNode.name - }); - var jointClip = jointClips[targetId]; - jointClip.channels.time = parameters.TIME; - jointClip.channels.rotation = parameters.rotation || null; - jointClip.channels.position = parameters.translation || null; - jointClip.channels.scale = parameters.scale || null; - jointClip.life = parameters.TIME[parameters.TIME.length - 1]; - } - - for (var targetId in jointClips) { - clip.addJointClip(jointClips[targetId]); - } - - if (haveAnimation) { - return clip; - } else { - return null; - } - } - }); - - return GLTFLoader; -}); -/** - * Load three.js JSON Format model - * - * Format specification https://github.com/mrdoob/three.js/wiki/JSON-Model-format-3.1 - */ -define('qtek/loader/ThreeModel',['require','../core/Base','../core/request','../core/util','../Shader','../Material','../DynamicGeometry','../Mesh','../Node','../Texture2D','../TextureCube','../shader/library','../Skeleton','../Joint','../math/Vector3','../math/Quaternion','../core/glenum','../animation/SkinningClip','../dep/glmatrix'],function(require) { - - var Base = require('../core/Base'); - - var request = require('../core/request'); - var util = require('../core/util'); - var Shader = require('../Shader'); - var Material = require('../Material'); - var DynamicGeometry = require('../DynamicGeometry'); - var Mesh = require('../Mesh'); - var Node = require('../Node'); - var Texture2D = require('../Texture2D'); - var TextureCube = require('../TextureCube'); - var shaderLibrary = require('../shader/library'); - var Skeleton = require('../Skeleton'); - var Joint = require('../Joint'); - var Vector3 = require('../math/Vector3'); - var Quaternion = require('../math/Quaternion'); - var glenum = require('../core/glenum'); - var SkinningClip = require('../animation/SkinningClip'); - - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - var quat = glMatrix.quat; - - /** - * @constructor qtek.loader.ThreeModel - * @extends qtek.core.Base - */ - var Loader = Base.derive( - /** @lends qtek.loader.ThreeModel# */ - { - /** - * @type {string} - */ - rootPath: '', - /** - * @type {string} - */ - textureRootPath: '' - }, { - /** - * @param {string} url - */ - load: function(url) { - var self = this; - - if (!this.rootPath) { - this.rootPath = url.slice(0, url.lastIndexOf('/')); - } - - request.get({ - url: url, - onprogress: function(percent, loaded, total) { - self.trigger('progress', percent, loaded, total); - }, - onerror: function(e) { - self.trigger('error', e); - }, - responseType: 'text', - onload: function(data) { - self.parse(JSON.parse(data)); - } - }); - }, - - /** - * @param {Object} json - * @return {Array.} - */ - parse: function(data) { - - var geometryList = this._parseGeometry(data); - - var dSkinIndices = data.skinIndices; - var dSkinWeights = data.skinWeights; - var skinned = dSkinIndices && dSkinIndices.length - && dSkinWeights && dSkinWeights.length; - - var jointNumber; - var skeleton; - if (skinned) { - skeleton = this._parseSkeleton(data); - jointNumber = skeleton.joints.length; - } else { - jointNumber = 0; - } - - var meshList = []; - for (var i = 0; i < data.materials.length; i++) { - var geometry = geometryList[i]; - if ( - geometry && geometry.faces.length && geometry.attributes.position.value.length - ) { - geometry.updateBoundingBox(); - var material = this._parseMaterial(data.materials[i], jointNumber); - var mesh = new Mesh({ - geometry: geometryList[i], - material: material - }) ; - if (skinned) { - mesh.skeleton = skeleton; - for (var i = 0; i < skeleton.joints.length; i++) { - // Use all the joints of skeleton - mesh.joints[i] = i; - } - } - meshList.push(mesh); - } - } - - this.trigger('success', meshList); - - return meshList; - }, - - _parseGeometry: function(data) { - - var geometryList = []; - var cursorList = []; - - for (var i = 0; i < data.materials.length; i++) { - geometryList[i] = null; - cursorList[i] = 0; - } - geometryList[0] = new DynamicGeometry(); - - var dFaces = data.faces; - var dVertices = data.vertices; - var dNormals = data.normals; - var dColors = data.colors; - var dSkinIndices = data.skinIndices; - var dSkinWeights = data.skinWeights; - var dUvs = data.uvs; - - var skinned = dSkinIndices && dSkinIndices.length - && dSkinWeights && dSkinWeights.length; - - var geometry = geometryList[0]; - var attributes = geometry.attributes; - var positions = attributes.position.value; - var normals = attributes.normal.value; - var texcoords = [ - attributes.texcoord0.value, - attributes.texcoord1.value - ]; - var colors = attributes.color.value; - var jointIndices = attributes.joint.value; - var jointWeights = attributes.weight.value; - var faces = geometry.faces; - - var nUvLayers = 0; - if (dUvs[0] && dUvs[0].length) { - nUvLayers++; - } - if (dUvs[1] && dUvs[1].length) { - nUvLayers++; - } - - var offset = 0; - var len = dFaces.length; - - // Cache the reorganized index - var newIndexMap = []; - var geoIndexMap = []; - for (var i = 0; i < dVertices.length; i++) { - newIndexMap[i] = -1; - geoIndexMap[i] = -1; - } - - var currentGeometryIndex = 0; - var isNew = []; - function getNewIndex(oi, faceIndex) { - if ( newIndexMap[oi] >= 0) { - // Switch to the geometry of existed index - currentGeometryIndex = geoIndexMap[oi]; - geometry = geometryList[currentGeometryIndex]; - attributes = geometry.attributes; - positions = attributes.position.value; - normals = attributes.normal.value; - texcoords = [attributes.texcoord0.value, - attributes.texcoord1.value]; - colors = attributes.color.value; - jointWeights = attributes.weight.value; - jointIndices = attributes.joint.value; - - isNew[faceIndex] = false; - return newIndexMap[oi]; - }else{ - - positions.push([dVertices[oi * 3], dVertices[oi * 3 + 1], dVertices[oi * 3 + 2]]); - //Skin data - if (skinned) { - jointWeights.push([dSkinWeights[oi * 2], dSkinWeights[oi * 2 + 1], 0]); - jointIndices.push([dSkinIndices[oi * 2], dSkinIndices[oi * 2 + 1], -1, -1]); - } - - newIndexMap[oi] = cursorList[materialIndex]; - geoIndexMap[oi] = materialIndex; - - isNew[faceIndex] = true; - return cursorList[materialIndex]++; - } - } - // Put the vertex data of one face here - // Incase the program create amount of tmp arrays and cause - // GC bottleneck - var faceUvs = []; - var faceNormals = []; - var faceColors = []; - for (var i =0; i < 4; i++) { - faceUvs[i] = [0, 0]; - faceNormals[i] = [0, 0, 0]; - faceColors[i] = [0, 0, 0]; - } - var materialIndex = 0; - - while (offset < len) { - var type = dFaces[offset++]; - var isQuad = isBitSet(type, 0); - var hasMaterial = isBitSet(type, 1); - var hasFaceUv = isBitSet(type, 2); - var hasFaceVertexUv = isBitSet(type, 3); - var hasFaceNormal = isBitSet(type, 4); - var hasFaceVertexNormal = isBitSet(type, 5); - var hasFaceColor = isBitSet(type, 6); - var hasFaceVertexColor = isBitSet(type, 7); - - var nVertices = isQuad ? 4 : 3; - - if (hasMaterial) { - materialIndex = dFaces[offset+ (isQuad ? 4 : 3)]; - if (!geometryList[materialIndex]) { - geometryList[materialIndex] = new DynamicGeometry(); - } - geometry = geometryList[materialIndex]; - attributes = geometry.attributes; - positions = attributes.position.value; - normals = attributes.normal.value; - texcoords = [ - attributes.texcoord0.value, - attributes.texcoord1.value - ]; - colors = attributes.color.value; - jointWeights = attributes.weight.value; - jointIndices = attributes.joint.value; - faces = geometry.faces; - } - var i1o, i2o, i3o, i4o; - var i1, i2, i3, i4, i5, i6; - if (isQuad) { - // Split into two triangle faces, 1-2-4 and 2-3-4 - i1o = dFaces[offset++]; - i2o = dFaces[offset++]; - i3o = dFaces[offset++]; - i4o = dFaces[offset++]; - // Face1 - i1 = getNewIndex(i1o, 0); - i2 = getNewIndex(i2o, 1); - i3 = getNewIndex(i4o, 2); - // Face2 - i4 = getNewIndex(i2o, 3); - i5 = getNewIndex(i3o, 4); - i6 = getNewIndex(i4o, 5); - faces.push([i1, i2, i3], [i4, i5, i6]); - } else { - i1 = dFaces[offset++]; - i2 = dFaces[offset++]; - i3 = dFaces[offset++]; - i1 = getNewIndex(i1, 0); - i2 = getNewIndex(i2, 1); - i3 = getNewIndex(i3, 2); - faces.push([i1, i2, i3]); - } - if (hasMaterial) { - offset++; - } - if (hasFaceUv) { - for (var i = 0; i < nUvLayers; i++) { - var uvLayer = dUvs[i]; - var uvIndex = faces[offset++]; - var u = uvLayer[uvIndex*2]; - var v = uvLayer[uvIndex*2+1]; - if (isQuad) { - // Random write of array seems not slow - // http://jsperf.com/random-vs-sequence-array-set - isNew[0] && (texcoords[i][i1] = [u, v]); - isNew[1] && (texcoords[i][i2] = [u, v]); - isNew[2] && (texcoords[i][i3] = [u, v]); - isNew[3] && (texcoords[i][i4] = [u, v]); - isNew[4] && (texcoords[i][i5] = [u, v]); - isNew[5] && (texcoords[i][i6] = [u, v]); - } else { - isNew[0] && (texcoords[i][i1] = [u, v]); - isNew[1] && (texcoords[i][i2] = [u, v]); - isNew[2] && (texcoords[i][i3] = [u, v]); - } - } - } - if (hasFaceVertexUv) { - for (var i = 0; i < nUvLayers; i++) { - var uvLayer = dUvs[i]; - for (var j = 0; j < nVertices; j++) { - var uvIndex = dFaces[offset++]; - faceUvs[j][0] = uvLayer[uvIndex*2]; - faceUvs[j][1] = uvLayer[uvIndex*2+1]; - } - if (isQuad) { - // Use array slice to clone array is incredibly faster than - // Construct from Float32Array - // http://jsperf.com/typedarray-v-s-array-clone/2 - isNew[0] && (texcoords[i][i1] = faceUvs[0].slice()); - isNew[1] && (texcoords[i][i2] = faceUvs[1].slice()); - isNew[2] && (texcoords[i][i3] = faceUvs[3].slice()); - isNew[3] && (texcoords[i][i4] = faceUvs[1].slice()); - isNew[4] && (texcoords[i][i5] = faceUvs[2].slice()); - isNew[5] && (texcoords[i][i6] = faceUvs[3].slice()); - } else { - isNew[0] && (texcoords[i][i1] = faceUvs[0].slice()); - isNew[1] && (texcoords[i][i2] = faceUvs[1].slice()); - isNew[2] && (texcoords[i][i3] = faceUvs[2].slice()); - } - } - } - if (hasFaceNormal) { - var normalIndex = dFaces[offset++]*3; - var x = dNormals[normalIndex++]; - var y = dNormals[normalIndex++]; - var z = dNormals[normalIndex]; - if (isQuad) { - isNew[0] && (normals[i1] = [x, y, z]); - isNew[1] && (normals[i2] = [x, y, z]); - isNew[2] && (normals[i3] = [x, y, z]); - isNew[3] && (normals[i4] = [x, y, z]); - isNew[4] && (normals[i5] = [x, y, z]); - isNew[5] && (normals[i6] = [x, y, z]); - }else{ - isNew[0] && (normals[i1] = [x, y, z]); - isNew[1] && (normals[i2] = [x, y, z]); - isNew[2] && (normals[i3] = [x, y, z]); - } - } - if (hasFaceVertexNormal) { - for (var i = 0; i < nVertices; i++) { - var normalIndex = dFaces[offset++]*3; - faceNormals[i][0] = dNormals[normalIndex++]; - faceNormals[i][1] = dNormals[normalIndex++]; - faceNormals[i][2] = dNormals[normalIndex]; - } - if (isQuad) { - isNew[0] && (normals[i1] = faceNormals[0].slice()); - isNew[1] && (normals[i2] = faceNormals[1].slice()); - isNew[2] && (normals[i3] = faceNormals[3].slice()); - isNew[3] && (normals[i4] = faceNormals[1].slice()); - isNew[4] && (normals[i5] = faceNormals[2].slice()); - isNew[5] && (normals[i6] = faceNormals[3].slice()); - } else { - isNew[0] && (normals[i1] = faceNormals[0].slice()); - isNew[1] && (normals[i2] = faceNormals[1].slice()); - isNew[2] && (normals[i3] = faceNormals[2].slice()); - } - } - if (hasFaceColor) { - var colorIndex = dFaces[offset++]; - var color = hex2rgb(dColors[colorIndex]); - if (isQuad) { - // Does't clone the color here - isNew[0] && (colors[i1] = color); - isNew[1] && (colors[i2] = color); - isNew[2] && (colors[i3] = color); - isNew[3] && (colors[i4] = color); - isNew[4] && (colors[i5] = color); - isNew[5] && (colors[i6] = color); - } else { - isNew[0] && (colors[i1] = color); - isNew[1] && (colors[i2] = color); - isNew[2] && (colors[i3] = color); - } - } - if (hasFaceVertexColor) { - for (var i = 0; i < nVertices; i++) { - var colorIndex = dFaces[offset++]; - faceColors[i] = hex2rgb(dColors[colorIndex]); - } - if (isQuad) { - isNew[0] && (colors[i1] = faceColors[0].slice()); - isNew[1] && (colors[i2] = faceColors[1].slice()); - isNew[2] && (colors[i3] = faceColors[3].slice()); - isNew[3] && (colors[i4] = faceColors[1].slice()); - isNew[4] && (colors[i5] = faceColors[2].slice()); - isNew[5] && (colors[i6] = faceColors[3].slice()); - } else { - isNew[0] && (colors[i1] = faceColors[0].slice()); - isNew[1] && (colors[i2] = faceColors[1].slice()); - isNew[2] && (colors[i3] = faceColors[2].slice()); - } - } - } - - return geometryList; - }, - - _parseSkeleton: function(data) { - var joints = []; - var dBones = data.bones; - for ( var i = 0; i < dBones.length; i++) { - var dBone = dBones[i]; - var joint = new Joint({ - index: i, - parentIndex: dBone.parent, - name: dBone.name - }); - joint.node = new Node({ - name: dBone.name, - position: new Vector3(dBone.pos[0], dBone.pos[1], dBone.pos[2]), - rotation: new Quaternion(dBone.rotq[0], dBone.rotq[1], dBone.rotq[2], dBone.rotq[3]), - scale: new Vector3(dBone.scl[0], dBone.scl[1], dBone.scl[2]) - }); - joints.push(joint); - } - - var skeleton = new Skeleton({ - joints: joints - }); - skeleton.updateHierarchy(); - skeleton.updateJointMatrices(); - skeleton.update(); - - if (data.animation) { - var dFrames = data.animation.hierarchy; - - var jointClips = []; - // Parse Animations - for (var i = 0; i < dFrames.length; i++) { - var channel = dFrames[i]; - var jointPose = jointClips[i] = { - keyFrames: [] - }; - jointPose.name = joints[i].name; - for (var j = 0; j < channel.keys.length; j++) { - var key = channel.keys[j]; - jointPose.keyFrames[j] = {}; - var kf = jointPose.keyFrames[j]; - kf.time = parseFloat(key.time) * 1000; - if (key.pos) { - kf.position = vec3.fromValues(key.pos[0], key.pos[1], key.pos[2]); - } - if (key.rot) { - kf.rotation = quat.fromValues(key.rot[0], key.rot[1], key.rot[2], key.rot[3]); - } - if (key.scl) { - kf.scale = vec3.fromValues(key.scl[0], key.scl[1], key.scl[2]); - } - } - } - - var skinningClip = new SkinningClip({ - jointClips: jointClips - }); - - skeleton.addClip(skinningClip); - } - - return skeleton; - }, - - _parseMaterial: function(mConfig, jointNumber) { - var shaderName = 'buildin.lambert'; - var shading = mConfig.shading && mConfig.shading.toLowerCase(); - if (shading === 'phong' || shading === 'lambert') { - shaderName = 'buildin.' + shading; - } - var enabledTextures = []; - if (mConfig.mapDiffuse) { - enabledTextures.push('diffuseMap'); - } - if (mConfig.mapNormal || mConfig.mapBump) { - enabledTextures.push('normalMap'); - } - var shader; - if (jointNumber === 0) { - shader = shaderLibrary.get(shaderName, enabledTextures); - } else { - // Shader for skinned mesh - shader = new Shader({ - vertex: Shader.source(shaderName+'.vertex'), - fragment: Shader.source(shaderName+'.fragment') - }); - for (var i = 0; i < enabledTextures; i++) { - shader.enableTexture(enabledTextures[i]); - } - shader.define('vertex', 'SKINNING'); - shader.define('vertex', 'JOINT_NUMBER', jointNumber); - } - - var material = new Material({ - shader: shader - }); - if (mConfig.colorDiffuse) { - material.set('color', mConfig.colorDiffuse ); - } else if (mConfig.DbgColor) { - material.set('color', hex2rgb(mConfig.DbgColor)); - } - if (mConfig.colorSpecular) { - material.set('specular', mConfig.colorSpecular ); - } - if (mConfig.transparent !== undefined && mConfig.transparent) { - material.transparent = true; - } - if (mConfig.depthTest !== undefined) { - material.depthTest = mConfig.depthTest; - } - if (mConfig.depthWrite !== undefined) { - material.depthMask = mConfig.depthWrite; - } - - if (mConfig.transparency && mConfig.transparency < 1) { - material.set('opacity', mConfig.transparency); - } - if (mConfig.specularCoef) { - material.set('shininess', mConfig.specularCoef); - } - - // Textures - if (mConfig.mapDiffuse) { - material.set('diffuseMap', this._loadTexture(mConfig.mapDiffuse, mConfig.mapDiffuseWrap) ); - } - if (mConfig.mapBump) { - material.set('normalMap', this._loadTexture(mConfig.mapBump, mConfig.mapBumpWrap) ); - } - if (mConfig.mapNormal) { - material.set('normalMap', this._loadTexture(mConfig.mapNormal, mConfig.mapBumpWrap) ); - } - - return material; - }, - - _loadTexture: function(path, wrap) { - var img = new Image(); - var texture = new Texture2D(); - texture.image = img; - - if (wrap && wrap.length) { - texture.wrapS = glenum[wrap[0].toUpperCase()]; - texture.wrapT = glenum[wrap[1].toUpperCase()]; - } - img.onload = function() { - texture.dirty(); - }; - var root = this.textureRootPath || this.rootPath; - img.src = util.relative2absolute(path, root); - - return texture; - } - }); - - - function isBitSet(value, position) { - return value & ( 1 << position ); - } - - - function hex2rgb(hex) { - var r = (hex >> 16) & 0xff; - var g = (hex >> 8) & 0xff; - var b = hex & 0xff; - return [r / 255, g / 255, b / 255]; - } - - return Loader; -}); -define('qtek/math/Matrix2',['require','../dep/glmatrix'],function(require) { - - - - var glMatrix = require('../dep/glmatrix'); - var mat2 = glMatrix.mat2; - - function makeProperty(n) { - return { - configurable: false, - set: function(value) { - this._array[n] = value; - this._dirty = true; - }, - get: function() { - return this._array[n]; - } - }; - } - - /** - * @constructor - * @alias qtek.math.Matrix2 - */ - var Matrix2 = function() { - - /** - * Storage of Matrix2 - * @type {Float32Array} - */ - this._array = mat2.create(); - - /** - * @type {boolean} - */ - this._dirty = true; - }; - - Matrix2.prototype = { - - constructor: Matrix2, - - /** - * Clone a new Matrix2 - * @return {qtek.math.Matrix2} - */ - clone: function() { - return (new Matrix2()).copy(this); - }, - - /** - * Copy from b - * @param {qtek.math.Matrix2} b - * @return {qtek.math.Matrix2} - */ - copy: function(b) { - mat2.copy(this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Calculate the adjugate of self, in-place - * @return {qtek.math.Matrix2} - */ - adjoint: function() { - mat2.adjoint(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Calculate matrix determinant - * @return {number} - */ - determinant: function() { - return mat2.determinant(this._array); - }, - - /** - * Set to a identity matrix - * @return {qtek.math.Matrix2} - */ - identity: function() { - mat2.identity(this._array); - this._dirty = true; - return this; - }, - - /** - * Invert self - * @return {qtek.math.Matrix2} - */ - invert: function() { - mat2.invert(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Alias for mutiply - * @param {qtek.math.Matrix2} b - * @return {qtek.math.Matrix2} - */ - mul: function(b) { - mat2.mul(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Alias for multiplyLeft - * @param {qtek.math.Matrix2} a - * @return {qtek.math.Matrix2} - */ - mulLeft: function(a) { - mat2.mul(this._array, a._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Multiply self and b - * @param {qtek.math.Matrix2} b - * @return {qtek.math.Matrix2} - */ - multiply: function(b) { - mat2.multiply(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Multiply a and self, a is on the left - * @param {qtek.math.Matrix2} a - * @return {qtek.math.Matrix2} - */ - multiplyLeft: function(a) { - mat2.multiply(this._array, a._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Rotate self by a given radian - * @param {number} rad - * @return {qtek.math.Matrix2} - */ - rotate: function(rad) { - mat2.rotate(this._array, this._array, rad); - this._dirty = true; - return this; - }, - - /** - * Scale self by s - * @param {qtek.math.Vector2} s - * @return {qtek.math.Matrix2} - */ - scale: function(v) { - mat2.scale(this._array, this._array, v._array); - this._dirty = true; - return this; - }, - /** - * Transpose self, in-place. - * @return {qtek.math.Matrix2} - */ - transpose: function() { - mat2.transpose(this._array, this._array); - this._dirty = true; - return this; - }, - toString: function() { - return '[' + Array.prototype.join.call(this._array, ',') + ']'; - } - }; - - /** - * @param {Matrix2} out - * @param {Matrix2} a - * @return {Matrix2} - */ - Matrix2.adjoint = function(out, a) { - mat2.adjoint(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix2} out - * @param {qtek.math.Matrix2} a - * @return {qtek.math.Matrix2} - */ - Matrix2.copy = function(out, a) { - mat2.copy(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix2} a - * @return {number} - */ - Matrix2.determinant = function(a) { - return mat2.determinant(a._array); - }; - - /** - * @param {qtek.math.Matrix2} out - * @return {qtek.math.Matrix2} - */ - Matrix2.identity = function(out) { - mat2.identity(out._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix2} out - * @param {qtek.math.Matrix2} a - * @return {qtek.math.Matrix2} - */ - Matrix2.invert = function(out, a) { - mat2.invert(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix2} out - * @param {qtek.math.Matrix2} a - * @param {qtek.math.Matrix2} b - * @return {qtek.math.Matrix2} - */ - Matrix2.mul = function(out, a, b) { - mat2.mul(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @method - * @param {qtek.math.Matrix2} out - * @param {qtek.math.Matrix2} a - * @param {qtek.math.Matrix2} b - * @return {qtek.math.Matrix2} - */ - Matrix2.multiply = Matrix2.mul; - - /** - * @param {qtek.math.Matrix2} out - * @param {qtek.math.Matrix2} a - * @param {number} rad - * @return {qtek.math.Matrix2} - */ - Matrix2.rotate = function(out, a, rad) { - mat2.rotate(out._array, a._array, rad); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix2} out - * @param {qtek.math.Matrix2} a - * @param {qtek.math.Vector2} v - * @return {qtek.math.Matrix2} - */ - Matrix2.scale = function(out, a, v) { - mat2.scale(out._array, a._array, v._array); - out._dirty = true; - return out; - }; - /** - * @param {Matrix2} out - * @param {Matrix2} a - * @return {Matrix2} - */ - Matrix2.transpose = function(out, a) { - mat2.transpose(out._array, a._array); - out._dirty = true; - return out; - }; - - return Matrix2; -}); -define('qtek/math/Matrix2d',['require','../dep/glmatrix'],function(require) { - - - - var glMatrix = require('../dep/glmatrix'); - var mat2d = glMatrix.mat2d; - - function makeProperty(n) { - return { - configurable: false, - set: function(value) { - this._array[n] = value; - this._dirty = true; - }, - get: function() { - return this._array[n]; - } - }; - } - - /** - * @constructor - * @alias qtek.math.Matrix2d - */ - var Matrix2d = function() { - /** - * Storage of Matrix2d - * @type {Float32Array} - */ - this._array = mat2d.create(); - - /** - * @type {boolean} - */ - this._dirty = true; - }; - - Matrix2d.prototype = { - - constructor: Matrix2d, - - /** - * Clone a new Matrix2d - * @return {qtek.math.Matrix2d} - */ - clone: function() { - return (new Matrix2d()).copy(this); - }, - - /** - * Copy from b - * @param {qtek.math.Matrix2d} b - * @return {qtek.math.Matrix2d} - */ - copy: function(b) { - mat2d.copy(this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Calculate matrix determinant - * @return {number} - */ - determinant: function() { - return mat2d.determinant(this._array); - }, - - /** - * Set to a identity matrix - * @return {qtek.math.Matrix2d} - */ - identity: function() { - mat2d.identity(this._array); - this._dirty = true; - return this; - }, - - /** - * Invert self - * @return {qtek.math.Matrix2d} - */ - invert: function() { - mat2d.invert(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Alias for mutiply - * @param {qtek.math.Matrix2d} b - * @return {qtek.math.Matrix2d} - */ - mul: function(b) { - mat2d.mul(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Alias for multiplyLeft - * @param {qtek.math.Matrix2d} a - * @return {qtek.math.Matrix2d} - */ - mulLeft: function(b) { - mat2d.mul(this._array, b._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Multiply self and b - * @param {qtek.math.Matrix2d} b - * @return {qtek.math.Matrix2d} - */ - multiply: function(b) { - mat2d.multiply(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Multiply a and self, a is on the left - * @param {qtek.math.Matrix2d} a - * @return {qtek.math.Matrix2d} - */ - multiplyLeft: function(b) { - mat2d.multiply(this._array, b._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Rotate self by a given radian - * @param {number} rad - * @return {qtek.math.Matrix2d} - */ - rotate: function(rad) { - mat2d.rotate(this._array, this._array, rad); - this._dirty = true; - return this; - }, - - /** - * Scale self by s - * @param {qtek.math.Vector2} s - * @return {qtek.math.Matrix2d} - */ - scale: function(s) { - mat2d.scale(this._array, this._array, s._array); - this._dirty = true; - return this; - }, - - /** - * Translate self by v - * @param {qtek.math.Vector2} v - * @return {qtek.math.Matrix2d} - */ - translate: function(v) { - mat2d.translate(this._array, this._array, v._array); - this._dirty = true; - return this; - }, - toString: function() { - return '[' + Array.prototype.join.call(this._array, ',') + ']'; - } - }; - - /** - * @param {qtek.math.Matrix2d} out - * @param {qtek.math.Matrix2d} a - * @return {qtek.math.Matrix2d} - */ - Matrix2d.copy = function(out, a) { - mat2d.copy(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix2d} a - * @return {number} - */ - Matrix2d.determinant = function(a) { - return mat2d.determinant(a._array); - }; - - /** - * @param {qtek.math.Matrix2d} out - * @return {qtek.math.Matrix2d} - */ - Matrix2d.identity = function(out) { - mat2d.identity(out._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix2d} out - * @param {qtek.math.Matrix2d} a - * @return {qtek.math.Matrix2d} - */ - Matrix2d.invert = function(out, a) { - mat2d.invert(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix2d} out - * @param {qtek.math.Matrix2d} a - * @param {qtek.math.Matrix2d} b - * @return {qtek.math.Matrix2d} - */ - Matrix2d.mul = function(out, a, b) { - mat2d.mul(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @method - * @param {qtek.math.Matrix2d} out - * @param {qtek.math.Matrix2d} a - * @param {qtek.math.Matrix2d} b - * @return {qtek.math.Matrix2d} - */ - Matrix2d.multiply = Matrix2d.mul; - - /** - * @param {qtek.math.Matrix2d} out - * @param {qtek.math.Matrix2d} a - * @param {number} rad - * @return {qtek.math.Matrix2d} - */ - Matrix2d.rotate = function(out, a, rad) { - mat2d.rotate(out._array, a._array, rad); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix2d} out - * @param {qtek.math.Matrix2d} a - * @param {qtek.math.Vector2} v - * @return {qtek.math.Matrix2d} - */ - Matrix2d.scale = function(out, a, v) { - mat2d.scale(out._array, a._array, v._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix2d} out - * @param {qtek.math.Matrix2d} a - * @param {qtek.math.Vector2} v - * @return {qtek.math.Matrix2d} - */ - Matrix2d.translate = function(out, a, v) { - mat2d.translate(out._array, a._array, v._array); - out._dirty = true; - return out; - }; - - return Matrix2d; -}); -define('qtek/math/Matrix3',['require','../dep/glmatrix'],function(require) { - - - - var glMatrix = require('../dep/glmatrix'); - var mat3 = glMatrix.mat3; - - function makeProperty(n) { - return { - configurable: false, - set: function(value) { - this._array[n] = value; - this._dirty = true; - }, - get: function() { - return this._array[n]; - } - }; - } - - /** - * @constructor - * @alias qtek.math.Matrix3 - */ - var Matrix3 = function() { - - /** - * Storage of Matrix3 - * @type {Float32Array} - */ - this._array = mat3.create(); - - /** - * @type {boolean} - */ - this._dirty = true; - }; - - Matrix3.prototype = { - - constructor: Matrix3, - - /** - * Calculate the adjugate of self, in-place - * @return {qtek.math.Matrix3} - */ - adjoint: function() { - mat3.adjoint(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Clone a new Matrix3 - * @return {qtek.math.Matrix3} - */ - clone: function() { - return (new Matrix3()).copy(this); - }, - - /** - * Copy from b - * @param {qtek.math.Matrix3} b - * @return {qtek.math.Matrix3} - */ - copy: function(b) { - mat3.copy(this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Calculate matrix determinant - * @return {number} - */ - determinant: function() { - return mat3.determinant(this._array); - }, - - /** - * Copy the values from Matrix2d a - * @param {qtek.math.Matrix2d} a - * @return {qtek.math.Matrix3} - */ - fromMat2d: function(a) { - mat3.fromMat2d(this._array, a._array); - this._dirty = true; - return this; - }, - - /** - * Copies the upper-left 3x3 values of Matrix4 - * @param {qtek.math.Matrix4} a - * @return {qtek.math.Matrix3} - */ - fromMat4: function(a) { - mat3.fromMat4(this._array, a._array); - this._dirty = true; - return this; - }, - - /** - * Calculates a rotation matrix from the given quaternion - * @param {qtek.math.Quaternion} q - * @return {qtek.math.Matrix3} - */ - fromQuat: function(q) { - mat3.fromQuat(this._array, q._array); - this._dirty = true; - return this; - }, - - /** - * Set to a identity matrix - * @return {qtek.math.Matrix3} - */ - identity: function() { - mat3.identity(this._array); - this._dirty = true; - return this; - }, - - /** - * Invert self - * @return {qtek.math.Matrix3} - */ - invert: function() { - mat3.invert(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Alias for mutiply - * @param {qtek.math.Matrix3} b - * @return {qtek.math.Matrix3} - */ - mul: function(b) { - mat3.mul(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Alias for multiplyLeft - * @param {qtek.math.Matrix3} a - * @return {qtek.math.Matrix3} - */ - mulLeft: function(a) { - mat3.mul(this._array, a._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Multiply self and b - * @param {qtek.math.Matrix3} b - * @return {qtek.math.Matrix3} - */ - multiply: function(b) { - mat3.multiply(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Multiply a and self, a is on the left - * @param {qtek.math.Matrix3} a - * @return {qtek.math.Matrix3} - */ - multiplyLeft: function(a) { - mat3.multiply(this._array, a._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Rotate self by a given radian - * @param {number} rad - * @return {qtek.math.Matrix3} - */ - rotate: function(rad) { - mat3.rotate(this._array, this._array, rad); - this._dirty = true; - return this; - }, - - /** - * Scale self by s - * @param {qtek.math.Vector2} s - * @return {qtek.math.Matrix3} - */ - scale: function(v) { - mat3.scale(this._array, this._array, v._array); - this._dirty = true; - return this; - }, - - /** - * Translate self by v - * @param {qtek.math.Vector2} v - * @return {qtek.math.Matrix3} - */ - translate: function(v) { - mat3.translate(this._array, this._array, v._array); - this._dirty = true; - return this; - }, - /** - * Calculates a 3x3 normal matrix (transpose inverse) from the 4x4 matrix - * @param {qtek.math.Matrix4} a - */ - normalFromMat4: function(a) { - mat3.normalFromMat4(this._array, a._array); - this._dirty = true; - return this; - }, - - /** - * Transpose self, in-place. - * @return {qtek.math.Matrix2} - */ - transpose: function() { - mat3.transpose(this._array, this._array); - this._dirty = true; - return this; - }, - toString: function() { - return '[' + Array.prototype.join.call(this._array, ',') + ']'; - } - }; - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix3} a - * @return {qtek.math.Matrix3} - */ - Matrix3.adjoint = function(out, a) { - mat3.adjoint(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix3} a - * @return {qtek.math.Matrix3} - */ - Matrix3.copy = function(out, a) { - mat3.copy(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix3} a - * @return {number} - */ - Matrix3.determinant = function(a) { - return mat3.determinant(a._array); - }; - - /** - * @param {qtek.math.Matrix3} out - * @return {qtek.math.Matrix3} - */ - Matrix3.identity = function(out) { - mat3.identity(out._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix3} a - * @return {qtek.math.Matrix3} - */ - Matrix3.invert = function(out, a) { - mat3.invert(out._array, a._array); - return out; - }; - - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix3} a - * @param {qtek.math.Matrix3} b - * @return {qtek.math.Matrix3} - */ - Matrix3.mul = function(out, a, b) { - mat3.mul(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @method - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix3} a - * @param {qtek.math.Matrix3} b - * @return {qtek.math.Matrix3} - */ - Matrix3.multiply = Matrix3.mul; - - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix2d} a - * @return {qtek.math.Matrix3} - */ - Matrix3.fromMat2d = function(out, a) { - mat3.fromMat2d(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix4} a - * @return {qtek.math.Matrix3} - */ - Matrix3.fromMat4 = function(out, a) { - mat3.fromMat4(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Quaternion} a - * @return {qtek.math.Matrix3} - */ - Matrix3.fromQuat = function(out, q) { - mat3.fromQuat(out._array, q._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix4} a - * @return {qtek.math.Matrix3} - */ - Matrix3.normalFromMat4 = function(out, a) { - mat3.normalFromMat4(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix3} a - * @param {number} rad - * @return {qtek.math.Matrix3} - */ - Matrix3.rotate = function(out, a, rad) { - mat3.rotate(out._array, a._array, rad); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix3} a - * @param {qtek.math.Vector2} v - * @return {qtek.math.Matrix3} - */ - Matrix3.scale = function(out, a, v) { - mat3.scale(out._array, a._array, v._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix3} a - * @return {qtek.math.Matrix3} - */ - Matrix3.transpose = function(out, a) { - mat3.transpose(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix3} a - * @param {qtek.math.Vector2} v - * @return {qtek.math.Matrix3} - */ - Matrix3.translate = function(out, a, v) { - mat3.translate(out._array, a._array, v._array); - out._dirty = true; - return out; - }; - - return Matrix3; -}); -define('qtek/math/Value',['require','./Vector3','./Vector2'],function(require) { - - - - var Vector3 = require('./Vector3'); - var Vector2 = require('./Vector2'); - - /** - * Random or constant 1d, 2d, 3d vector generator - * @constructor - * @alias qtek.math.Value - */ - var Value = function() {}; - - /** - * @method - * @param {number|qtek.math.Vector2|qtek.math.Vector3} [out] - * @return {number|qtek.math.Vector2|qtek.math.Vector3} - */ - Value.prototype.get = function(out) {}; - - // Constant - var ConstantValue = function(val) { - this.get = function() { - return val; - }; - }; - ConstantValue.prototype = new Value(); - ConstantValue.prototype.constructor = ConstantValue; - - // Vector - var VectorValue = function(val) { - var Constructor = val.constructor; - this.get = function(out) { - if (!out) { - out = new Constructor(); - } - out.copy(val); - return out; - }; - }; - VectorValue.prototype = new Value(); - VectorValue.prototype.constructor = VectorValue; - //Random 1D - var Random1D = function(min, max) { - var range = max - min; - this.get = function() { - return Math.random() * range + min; - }; - }; - Random1D.prototype = new Value(); - Random1D.prototype.constructor = Random1D; - - // Random2D - var Random2D = function(min, max) { - var rangeX = max.x - min.x; - var rangeY = max.y - min.y; - - this.get = function(out) { - if (!out) { - out = new Vector2(); - } - Vector2.set( - out, - rangeX * Math.random() + min._array[0], - rangeY * Math.random() + min._array[1] - ); - - return out; - }; - }; - Random2D.prototype = new Value(); - Random2D.prototype.constructor = Random2D; - - var Random3D = function(min, max) { - var rangeX = max.x - min.x; - var rangeY = max.y - min.y; - var rangeZ = max.z - min.z; - - this.get = function(out) { - if (!out) { - out = new Vector3(); - } - Vector3.set( - out, - rangeX * Math.random() + min._array[0], - rangeY * Math.random() + min._array[1], - rangeZ * Math.random() + min._array[2] - ); - - return out; - }; - }; - Random3D.prototype = new Value(); - Random3D.prototype.constructor = Random3D; - - // Factory methods - - /** - * Create a constant 1d value generator - * @param {number} constant - * @return {qtek.math.Value} - */ - Value.constant = function(constant) { - return new ConstantValue(constant); - }; - - /** - * Create a constant vector value(2d or 3d) generator - * @param {qtek.math.Vector2|qtek.math.Vector3} vector - * @return {qtek.math.Value} - */ - Value.vector = function(vector) { - return new VectorValue(vector); - }; - - /** - * Create a random 1d value generator - * @param {number} min - * @param {number} max - * @return {qtek.math.Value} - */ - Value.random1D = function(min, max) { - return new Random1D(min, max); - }; - - /** - * Create a random 2d value generator - * @param {qtek.math.Vector2} min - * @param {qtek.math.Vector2} max - * @return {qtek.math.Value} - */ - Value.random2D = function(min, max) { - return new Random2D(min, max); - }; - - /** - * Create a random 3d value generator - * @param {qtek.math.Vector3} min - * @param {qtek.math.Vector3} max - * @return {qtek.math.Value} - */ - Value.random3D = function(min, max) { - return new Random3D(min, max); - }; - - return Value; -}); -define('qtek/math/Vector4',['require','../dep/glmatrix'], function(require) { - - - - var glMatrix = require('../dep/glmatrix'); - var vec4 = glMatrix.vec4; - - /** - * @constructor - * @alias qtek.math.Vector4 - * @param {number} x - * @param {number} y - * @param {number} z - * @param {number} w - */ - var Vector4 = function(x, y, z, w) { - - x = x || 0; - y = y || 0; - z = z || 0; - w = w || 0; - - /** - * Storage of Vector4, read and write of x, y, z, w will change the values in _array - * All methods also operate on the _array instead of x, y, z, w components - * @type {Float32Array} - */ - this._array = vec4.fromValues(x, y, z, w); - - /** - * Dirty flag is used by the Node to determine - * if the matrix is updated to latest - * @type {boolean} - */ - this._dirty = true; - }; - - Vector4.prototype = { - - constructor: Vector4, - - /** - * Add b to self - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - add: function(b) { - vec4.add( this._array, this._array, b._array ); - this._dirty = true; - return this; - }, - - /** - * Set x, y and z components - * @param {number} x - * @param {number} y - * @param {number} z - * @param {number} w - * @return {qtek.math.Vector4} - */ - set: function(x, y, z, w) { - this._array[0] = x; - this._array[1] = y; - this._array[2] = z; - this._array[3] = w; - this._dirty = true; - return this; - }, - - /** - * Set x, y, z and w components from array - * @param {Float32Array|number[]} arr - * @return {qtek.math.Vector4} - */ - setArray: function(arr) { - this._array[0] = arr[0]; - this._array[1] = arr[1]; - this._array[2] = arr[2]; - this._array[3] = arr[3]; - - this._dirty = true; - return this; - }, - - /** - * Clone a new Vector4 - * @return {qtek.math.Vector4} - */ - clone: function() { - return new Vector4( this.x, this.y, this.z, this.w); - }, - - /** - * Copy from b - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - copy: function(b) { - vec4.copy( this._array, b._array ); - this._dirty = true; - return this; - }, - - /** - * Alias for distance - * @param {qtek.math.Vector4} b - * @return {number} - */ - dist: function(b) { - return vec4.dist(this._array, b._array); - }, - - /** - * Distance between self and b - * @param {qtek.math.Vector4} b - * @return {number} - */ - distance: function(b) { - return vec4.distance(this._array, b._array); - }, - - /** - * Alias for divide - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - div: function(b) { - vec4.div(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Divide self by b - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - divide: function(b) { - vec4.divide(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Dot product of self and b - * @param {qtek.math.Vector4} b - * @return {number} - */ - dot: function(b) { - return vec4.dot(this._array, b._array); - }, - - /** - * Alias of length - * @return {number} - */ - len: function() { - return vec4.len(this._array); - }, - - /** - * Calculate the length - * @return {number} - */ - length: function() { - return vec4.length(this._array); - }, - /** - * Linear interpolation between a and b - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @param {number} t - * @return {qtek.math.Vector4} - */ - lerp: function(a, b, t) { - vec4.lerp(this._array, a._array, b._array, t); - this._dirty = true; - return this; - }, - - /** - * Minimum of self and b - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - min: function(b) { - vec4.min(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Maximum of self and b - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - max: function(b) { - vec4.max(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Alias for multiply - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - mul: function(b) { - vec4.mul(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Mutiply self and b - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - multiply: function(b) { - vec4.multiply(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Negate self - * @return {qtek.math.Vector4} - */ - negate: function() { - vec4.negate(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Normalize self - * @return {qtek.math.Vector4} - */ - normalize: function() { - vec4.normalize(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Generate random x, y, z, w components with a given scale - * @param {number} scale - * @return {qtek.math.Vector4} - */ - random: function(scale) { - vec4.random(this._array, scale); - this._dirty = true; - return this; - }, - - /** - * Scale self - * @param {number} scale - * @return {qtek.math.Vector4} - */ - scale: function(s) { - vec4.scale(this._array, this._array, s); - this._dirty = true; - return this; - }, - /** - * Scale b and add to self - * @param {qtek.math.Vector4} b - * @param {number} scale - * @return {qtek.math.Vector4} - */ - scaleAndAdd: function(b, s) { - vec4.scaleAndAdd(this._array, this._array, b._array, s); - this._dirty = true; - return this; - }, - - /** - * Alias for squaredDistance - * @param {qtek.math.Vector4} b - * @return {number} - */ - sqrDist: function(b) { - return vec4.sqrDist(this._array, b._array); - }, - - /** - * Squared distance between self and b - * @param {qtek.math.Vector4} b - * @return {number} - */ - squaredDistance: function(b) { - return vec4.squaredDistance(this._array, b._array); - }, - - /** - * Alias for squaredLength - * @return {number} - */ - sqrLen: function() { - return vec4.sqrLen(this._array); - }, - - /** - * Squared length of self - * @return {number} - */ - squaredLength: function() { - return vec4.squaredLength(this._array); - }, - - /** - * Alias for subtract - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - sub: function(b) { - vec4.sub(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Subtract b from self - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - subtract: function(b) { - vec4.subtract(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Transform self with a Matrix4 m - * @param {qtek.math.Matrix4} m - * @return {qtek.math.Vector4} - */ - transformMat4: function(m) { - vec4.transformMat4(this._array, this._array, m._array); - this._dirty = true; - return this; - }, - - /** - * Transform self with a Quaternion q - * @param {qtek.math.Quaternion} q - * @return {qtek.math.Vector4} - */ - transformQuat: function(q) { - vec4.transformQuat(this._array, this._array, q._array); - this._dirty = true; - return this; - }, - - toString: function() { - return '[' + Array.prototype.join.call(this._array, ',') + ']'; - } - }; - - // Getter and Setter - if (Object.defineProperty) { - - var proto = Vector4.prototype; - /** - * @name x - * @type {number} - * @memberOf qtek.math.Vector4 - * @instance - */ - Object.defineProperty(proto, 'x', { - get: function () { - return this._array[0]; - }, - set: function (value) { - this._array[0] = value; - this._dirty = true; - } - }); - - /** - * @name y - * @type {number} - * @memberOf qtek.math.Vector4 - * @instance - */ - Object.defineProperty(proto, 'y', { - get: function () { - return this._array[1]; - }, - set: function (value) { - this._array[1] = value; - this._dirty = true; - } - }); - - /** - * @name z - * @type {number} - * @memberOf qtek.math.Vector4 - * @instance - */ - Object.defineProperty(proto, 'z', { - get: function () { - return this._array[2]; - }, - set: function (value) { - this._array[2] = value; - this._dirty = true; - } - }); - - /** - * @name w - * @type {number} - * @memberOf qtek.math.Vector4 - * @instance - */ - Object.defineProperty(proto, 'w', { - get: function () { - return this._array[3]; - }, - set: function (value) { - this._array[3] = value; - this._dirty = true; - } - }); - } - - // Supply methods that are not in place - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - Vector4.add = function(out, a, b) { - vec4.add(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector4} out - * @param {number} x - * @param {number} y - * @param {number} z - * @return {qtek.math.Vector4} - */ - Vector4.set = function(out, x, y, z, w) { - vec4.set(out._array, x, y, z, w); - out._dirty = true; - }; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - Vector4.copy = function(out, b) { - vec4.copy(out._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {number} - */ - Vector4.dist = function(a, b) { - return vec4.distance(a._array, b._array); - }; - - /** - * @method - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {number} - */ - Vector4.distance = Vector4.dist; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - Vector4.div = function(out, a, b) { - vec4.divide(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @method - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - Vector4.divide = Vector4.div; - - /** - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {number} - */ - Vector4.dot = function(a, b) { - return vec4.dot(a._array, b._array); - }; - - /** - * @param {qtek.math.Vector4} a - * @return {number} - */ - Vector4.len = function(b) { - return vec4.length(b._array); - }; - - // Vector4.length = Vector4.len; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @param {number} t - * @return {qtek.math.Vector4} - */ - Vector4.lerp = function(out, a, b, t) { - vec4.lerp(out._array, a._array, b._array, t); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - Vector4.min = function(out, a, b) { - vec4.min(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - Vector4.max = function(out, a, b) { - vec4.max(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - Vector4.mul = function(out, a, b) { - vec4.multiply(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @method - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - Vector4.multiply = Vector4.mul; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @return {qtek.math.Vector4} - */ - Vector4.negate = function(out, a) { - vec4.negate(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @return {qtek.math.Vector4} - */ - Vector4.normalize = function(out, a) { - vec4.normalize(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector4} out - * @param {number} scale - * @return {qtek.math.Vector4} - */ - Vector4.random = function(out, scale) { - vec4.random(out._array, scale); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {number} scale - * @return {qtek.math.Vector4} - */ - Vector4.scale = function(out, a, scale) { - vec4.scale(out._array, a._array, scale); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @param {number} scale - * @return {qtek.math.Vector4} - */ - Vector4.scaleAndAdd = function(out, a, b, scale) { - vec4.scaleAndAdd(out._array, a._array, b._array, scale); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {number} - */ - Vector4.sqrDist = function(a, b) { - return vec4.sqrDist(a._array, b._array); - }; - - /** - * @method - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {number} - */ - Vector4.squaredDistance = Vector4.sqrDist; - - /** - * @param {qtek.math.Vector4} a - * @return {number} - */ - Vector4.sqrLen = function(a) { - return vec4.sqrLen(a._array); - }; - /** - * @method - * @param {qtek.math.Vector4} a - * @return {number} - */ - Vector4.squaredLength = Vector4.sqrLen; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - Vector4.sub = function(out, a, b) { - vec4.subtract(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - /** - * @method - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - Vector4.subtract = Vector4.sub; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Matrix4} m - * @return {qtek.math.Vector4} - */ - Vector4.transformMat4 = function(out, a, m) { - vec4.transformMat4(out._array, a._array, m._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Quaternion} q - * @return {qtek.math.Vector4} - */ - Vector4.transformQuat = function(out, a, q) { - vec4.transformQuat(out._array, a._array, q._array); - out._dirty = true; - return out; - }; - - return Vector4; -}); -define('qtek/particleSystem/Particle',['require','../math/Vector3','../dep/glmatrix'],function(require) { - - var Vector3 = require('../math/Vector3'); - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - - /** - * @constructor - * @alias qtek.particleSystem.Particle - */ - var Particle = function() { - /** - * @type {qtek.math.Vector3} - */ - this.position = new Vector3(); - - /** - * Use euler angle to represent particle rotation - * @type {qtek.math.Vector3} - */ - this.rotation = new Vector3(); - - /** - * @type {?qtek.math.Vector3} - */ - this.velocity = null; - - /** - * @type {?qtek.math.Vector3} - */ - this.angularVelocity = null; - - /** - * @type {number} - */ - this.life = 1; - - /** - * @type {number} - */ - this.age = 0; - - /** - * @type {number} - */ - this.spriteSize = 1; - - /** - * @type {number} - */ - this.weight = 1; - - /** - * @type {qtek.particleSystem.Emitter} - */ - this.emitter = null; - }; - - /** - * Update particle position - * @param {number} deltaTime - */ - Particle.prototype.update = function(deltaTime) { - if (this.velocity) { - vec3.scaleAndAdd(this.position._array, this.position._array, this.velocity._array, deltaTime); - } - if (this.angularVelocity) { - vec3.scaleAndAdd(this.rotation._array, this.rotation._array, this.angularVelocity._array, deltaTime); - } - }; - - return Particle; -}); -define('qtek/particleSystem/Emitter',['require','../core/Base','../math/Vector3','./Particle','../math/Value','../dep/glmatrix'],function(require) { - - var Base = require('../core/Base'); - var Vector3 = require('../math/Vector3'); - var Particle = require('./Particle'); - var Value = require('../math/Value'); - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - - /** - * @constructor qtek.particleSystem.Emitter - * @extends qtek.core.Base - */ - var Emitter = Base.derive( - /** @lends qtek.particleSystem.Emitter# */ - { - /** - * Maximum number of particles created by this emitter - * @type {number} - */ - max: 1000, - /** - * Number of particles created by this emitter each shot - * @type {number} - */ - amount: 20, - - // Init status for each particle - /** - * Particle life generator - * @type {?qtek.math.Value.} - */ - life: null, - /** - * Particle position generator - * @type {?qtek.math.Value.} - */ - position: null, - /** - * Particle rotation generator - * @type {?qtek.math.Value.} - */ - rotation: null, - /** - * Particle velocity generator - * @type {?qtek.math.Value.} - */ - velocity: null, - /** - * Particle angular velocity generator - * @type {?qtek.math.Value.} - */ - angularVelocity: null, - /** - * Particle sprite size generator - * @type {?qtek.math.Value.} - */ - spriteSize: null, - /** - * Particle weight generator - * @type {?qtek.math.Value.} - */ - weight: null, - - _particlePool: null - - }, function() { - - this._particlePool = []; - - // TODO Reduce heap memory - for (var i = 0; i < this.max; i++) { - var particle = new Particle(); - particle.emitter = this; - this._particlePool.push(particle); - - if (this.velocity) { - particle.velocity = new Vector3(); - } - if (this.angularVelocity) { - particle.angularVelocity = new Vector3(); - } - } - - }, - /** @lends qtek.particleSystem.Emitter.prototype */ - { - /** - * Emitter number of particles and push them to a given particle list. Emmit number is defined by amount property - * @param {Array.} out - */ - emit: function(out) { - var amount = Math.min(this._particlePool.length, this.amount); - - var particle; - for (var i = 0; i < amount; i++) { - particle = this._particlePool.pop(); - // Initialize particle status - if (this.position) { - this.position.get(particle.position); - } - if (this.rotation) { - this.rotation.get(particle.rotation); - } - if (this.velocity) { - this.velocity.get(particle.velocity); - } - if (this.angularVelocity) { - this.angularVelocity.get(particle.angularVelocity); - } - if (this.life) { - particle.life = this.life.get(); - } - if (this.spriteSize) { - particle.spriteSize = this.spriteSize.get(); - } - if (this.weight) { - particle.weight = this.weight.get(); - } - particle.age = 0; - - out.push(particle); - } - }, - /** - * Kill a dead particle and put it back in the pool - * @param {qtek.particleSystem.Particle} particle - */ - kill: function(particle) { - this._particlePool.push(particle); - } - }); - - /** - * Create a constant 1d value generator. Alias for {@link qtek.math.Value.constant} - * @method qtek.particleSystem.Emitter.constant - */ - Emitter.constant = Value.constant; - - /** - * Create a constant vector value(2d or 3d) generator. Alias for {@link qtek.math.Value.vector} - * @method qtek.particleSystem.Emitter.vector - */ - Emitter.vector = Value.vector; - - /** - * Create a random 1d value generator. Alias for {@link qtek.math.Value.random1D} - * @method qtek.particleSystem.Emitter.random1D - */ - Emitter.random1D = Value.random1D; - - /** - * Create a random 2d value generator. Alias for {@link qtek.math.Value.random2D} - * @method qtek.particleSystem.Emitter.random2D - */ - Emitter.random2D = Value.random2D; - - /** - * Create a random 3d value generator. Alias for {@link qtek.math.Value.random3D} - * @method qtek.particleSystem.Emitter.random3D - */ - Emitter.random3D = Value.random3D; - - return Emitter; -}); -define('qtek/particleSystem/Field',['require','../core/Base'],function(require) { - - var Base = require('../core/Base'); - /** - * @constructor qtek.particleSystem.Field - * @extends qtek.core.Base - */ - var Field = Base.derive({}, { - /** - * Apply a field to the particle and update the particle velocity - * @param {qtek.math.Vector3} velocity - * @param {qtek.math.Vector3} position - * @param {number} weight - * @param {number} deltaTime - * @memberOf qtek.particleSystem.Field.prototype - */ - applyTo: function(velocity, position, weight, deltaTime) {} - }); - - return Field; -}); -define('qtek/particleSystem/ForceField',['require','./Field','../math/Vector3','../dep/glmatrix'],function(require) { - - var Field = require('./Field'); - var Vector3 = require('../math/Vector3'); - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - - /** - * @constructor qtek.particleSystem.ForceField - * @extends qtek.particleSystem.Field - */ - var ForceField = Field.derive(function() { - return { - force: new Vector3() - }; - }, { - applyTo: function(velocity, position, weight, deltaTime) { - if (weight > 0) { - vec3.scaleAndAdd(velocity._array, velocity._array, this.force._array, deltaTime / weight); - } - } - }); - - return ForceField; -}); -define('qtek/particleSystem/particle.essl',[],function () { return '@export buildin.particle.vertex\n\nuniform mat4 worldView : WORLDVIEW;\nuniform mat4 projection : PROJECTION;\n\nattribute vec3 position : POSITION;\nattribute vec3 normal : NORMAL;\n\n#ifdef UV_ANIMATION\nattribute vec2 texcoord0 : TEXCOORD_0;\nattribute vec2 texcoord1 : TEXCOORD_1;\n\nvarying vec2 v_Uv0;\nvarying vec2 v_Uv1;\n#endif\n\nvarying float v_Age;\n\nvoid main() {\n v_Age = normal.x;\n float rotation = normal.y;\n\n vec4 worldViewPosition = worldView * vec4(position, 1.0);\n gl_Position = projection * worldViewPosition;\n float w = gl_Position.w;\n // TODO\n gl_PointSize = normal.z * projection[0].x / w;\n\n #ifdef UV_ANIMATION\n v_Uv0 = texcoord0;\n v_Uv1 = texcoord1;\n #endif\n}\n\n@end\n\n@export buildin.particle.fragment\n\nuniform sampler2D sprite;\nuniform sampler2D gradient;\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\n\nvarying float v_Age;\n\n#ifdef UV_ANIMATION\nvarying vec2 v_Uv0;\nvarying vec2 v_Uv1;\n#endif\n\nvoid main() {\n vec4 color = vec4(color, alpha);\n #ifdef SPRITE_ENABLED\n #ifdef UV_ANIMATION\n color *= texture2D(sprite, mix(v_Uv0, v_Uv1, gl_PointCoord));\n #else\n color *= texture2D(sprite, gl_PointCoord);\n #endif\n #endif\n #ifdef GRADIENT_ENABLED\n color *= texture2D(gradient, vec2(v_Age, 0.5));\n #endif\n gl_FragColor = color;\n}\n\n@end';}); - -define('qtek/particleSystem/ParticleRenderable',['require','../Renderable','../math/Vector3','../core/glenum','../StaticGeometry','../Material','../Shader','../dep/glmatrix','./particle.essl'],function(require) { - - - - var Renderable = require('../Renderable'); - var Vector3 = require('../math/Vector3'); - var glenum = require('../core/glenum'); - - var StaticGeometry = require('../StaticGeometry'); - var Material = require('../Material'); - var Shader = require('../Shader'); - - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - - Shader['import'](require('./particle.essl')); - - var particleShader = new Shader({ - vertex: Shader.source('buildin.particle.vertex'), - fragment: Shader.source('buildin.particle.fragment') - }); - particleShader.enableTexture('sprite'); - - /** - * @constructor qtek.particleSystem.ParticleRenderable - * @extends qtek.Renderable - * - * @example - * var particleRenderable = new qtek.particleSystem.ParticleRenderable({ - * spriteAnimationTileX: 4, - * spriteAnimationTileY: 4, - * spriteAnimationRepeat: 1 - * }); - * scene.add(particleRenderable); - * // Enable uv animation in the shader - * particleRenderable.material.shader.define('both', 'UV_ANIMATION'); - * var Emitter = qtek.particleSystem.Emitter; - * var Vector3 = qtek.math.Vector3; - * var emitter = new Emitter({ - * max: 2000, - * amount: 100, - * life: Emitter.random1D(10, 20), - * position: Emitter.vector(new Vector3()), - * velocity: Emitter.random3D(new Vector3(-10, 0, -10), new Vector3(10, 0, 10)); - * }); - * particleRenderable.addEmitter(emitter); - * var gravityField = new qtek.particleSystem.ForceField(); - * gravityField.force.y = -10; - * particleRenderable.addField(gravityField); - * ... - * animation.on('frame', function(frameTime) { - * particleRenderable.updateParticles(frameTime); - * renderer.render(scene, camera); - * }); - */ - var ParticleRenderable = Renderable.derive( - /** @lends qtek.particleSystem.ParticleRenderable# */ - { - /** - * @type {boolean} - */ - loop: true, - /** - * @type {boolean} - */ - oneshot: false, - /** - * Duration of particle system in milliseconds - * @type {number} - */ - duration: 1, - - // UV Animation - /** - * @type {number} - */ - spriteAnimationTileX: 1, - /** - * @type {number} - */ - spriteAnimationTileY: 1, - /** - * @type {number} - */ - spriteAnimationRepeat: 0, - - mode: Renderable.POINTS, - - _elapsedTime: 0, - - _emitting: true - - }, function(){ - - this.geometry = new StaticGeometry({ - dynamic: true - }); - - if (!this.material) { - this.material = new Material({ - shader: particleShader, - transparent: true, - depthMask: false - }); - } - - this._particles = []; - this._fields = []; - this._emitters = []; - }, - /** @lends qtek.particleSystem.ParticleRenderable.prototype */ - { - - culling: false, - - frustumCulling: false, - - castShadow: false, - receiveShadow: false, - - /** - * Add emitter - * @param {qtek.particleSystem.Emitter} emitter - */ - addEmitter: function(emitter) { - this._emitters.push(emitter); - }, - - /** - * Remove emitter - * @param {qtek.particleSystem.Emitter} emitter - */ - removeEmitter: function(emitter) { - this._emitters.splice(this._emitters.indexOf(emitter), 1); - }, - - /** - * Add field - * @param {qtek.particleSystem.Field} field - */ - addField: function(field) { - this._fields.push(field); - }, - - /** - * Remove field - * @param {qtek.particleSystem.Field} field - */ - removeField: function(field) { - this._fields.splice(this._fields.indexOf(field), 1); - }, - - /** - * Reset the particle system. - */ - reset: function() { - // Put all the particles back - for (var i = 0; i < this._particles.length; i++) { - var p = this._particles[i]; - p.emitter.kill(p); - } - this._particles.length = 0; - this._elapsedTime = 0; - this._emitting = true; - }, - - /** - * @param {number} deltaTime - */ - updateParticles: function(deltaTime) { - - // MS => Seconds - deltaTime /= 1000; - this._elapsedTime += deltaTime; - - var particles = this._particles; - - if (this._emitting) { - for (var i = 0; i < this._emitters.length; i++) { - this._emitters[i].emit(particles); - } - if (this.oneshot) { - this._emitting = false; - } - } - - // Aging - var len = particles.length; - for (var i = 0; i < len;) { - var p = particles[i]; - p.age += deltaTime; - if (p.age >= p.life) { - p.emitter.kill(p); - particles[i] = particles[len-1]; - particles.pop(); - len--; - } else { - i++; - } - } - - for (var i = 0; i < len; i++) { - // Update - var p = particles[i]; - if (this._fields.length > 0) { - for (var j = 0; j < this._fields.length; j++) { - this._fields[j].applyTo(p.velocity, p.position, p.weight, deltaTime); - } - } - p.update(deltaTime); - } - }, - - _updateVertices: function() { - var geometry = this.geometry; - // If has uv animation - var animTileX = this.spriteAnimationTileX; - var animTileY = this.spriteAnimationTileY; - var animRepeat = this.spriteAnimationRepeat; - var nUvAnimFrame = animTileY * animTileX * animRepeat; - var hasUvAnimation = nUvAnimFrame > 1; - var positions = geometry.attributes.position.value; - // Put particle status in normal - var normals = geometry.attributes.normal.value; - var uvs = geometry.attributes.texcoord0.value; - var uvs2 = geometry.attributes.texcoord1.value; - - var len = this._particles.length; - if (!positions || positions.length !== len * 3) { - // TODO Optimize - positions = geometry.attributes.position.value = new Float32Array(len * 3); - normals = geometry.attributes.normal.value = new Float32Array(len * 3); - if (hasUvAnimation) { - uvs = geometry.attributes.texcoord0.value = new Float32Array(len * 2); - uvs2 = geometry.attributes.texcoord1.value = new Float32Array(len * 2); - } - } - - var invAnimTileX = 1 / animTileX; - for (var i = 0; i < len; i++) { - var particle = this._particles[i]; - var offset = i * 3; - for (var j = 0; j < 3; j++) { - positions[offset + j] = particle.position._array[j]; - normals[offset] = particle.age / particle.life; - // normals[offset + 1] = particle.rotation; - normals[offset + 1] = 0; - normals[offset + 2] = particle.spriteSize; - } - var offset2 = i * 2; - if (hasUvAnimation) { - // TODO - var p = particle.age / particle.life; - var stage = Math.round(p * (nUvAnimFrame - 1)) * animRepeat; - var v = Math.floor(stage * invAnimTileX); - var u = stage - v * animTileX; - uvs[offset2] = u / animTileX; - uvs[offset2 + 1] = 1 - v / animTileY; - uvs2[offset2] = (u + 1) / animTileX; - uvs2[offset2 + 1] = 1 - (v + 1) / animTileY; - } - } - - geometry.dirty(); - }, - - render: function(_gl) { - this._updateVertices(); - return Renderable.prototype.render.call(this, _gl); - }, - - /** - * @return {boolean} - */ - isFinished: function() { - return this._elapsedTime > this.duration && !this.loop; - }, - - /** - * @param {WebGLRenderingContext} _gl - */ - dispose: function(_gl) { - // Put all the particles back - for (var i = 0; i < this._particles.length; i++) { - var p = this._particles[i]; - p.emitter.kill(p); - } - this.geometry.dispose(_gl); - // TODO Dispose texture, shader ? - }, - - /** - * @return {qtek.particleSystem.ParticleRenderable} - */ - clone: function() { - var particleSystem = new ParticleRenderable({ - material: this.material - }); - particleSystem.loop = this.loop; - particleSystem.duration = this.duration; - particleSystem.oneshot = this.oneshot; - particleSystem.spriteAnimationRepeat = this.spriteAnimationRepeat; - particleSystem.spriteAnimationTileY = this.spriteAnimationTileY; - particleSystem.spriteAnimationTileX = this.spriteAnimationTileX; - - particleSystem.position.copy(this.position); - particleSystem.rotation.copy(this.rotation); - particleSystem.scale.copy(this.scale); - - for (var i = 0; i < this._children.length; i++) { - particleSystem.add(this._children[i].clone()); - } - return particleSystem; - } - }); - - return ParticleRenderable; -}); -define('qtek/picking/color.essl',[],function () { return '@export buildin.picking.color.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvoid main(){\n\n vec3 skinnedPosition = position;\n\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n}\n\n@end\n\n@end\n@export buildin.picking.color.fragment\n\nuniform vec4 color : [1.0, 1.0, 1.0, 1.0];\n\nvoid main(){\n gl_FragColor = color;\n}\n\n@end';}); - -define('qtek/picking/PixelPicking',['require','../core/Base','../FrameBuffer','../Texture2D','../Shader','../Material','./color.essl'],function (require) { - - var Base = require('../core/Base'); - var FrameBuffer = require('../FrameBuffer'); - var Texture2D = require('../Texture2D'); - var Shader = require('../Shader'); - var Material = require('../Material'); - - Shader.import(require('./color.essl')); - - /** - * Pixel picking is gpu based picking, which is fast and accurate. - * But not like ray picking, it can't get the intersection point and triangle. - * @constructor qtek.picking.PixelPicking - * @extends qtek.core.Base - */ - var PixelPicking = Base.derive(function() { - return /** @lends qtek.picking.PixelPicking# */ { - /** - * Target renderer - * @type {qtek.Renderer} - */ - renderer: null, - /** - * Downsample ratio of hidden frame buffer - * @type {number} - */ - downSampleRatio: 1, - - width: 100, - height: 100, - - lookupOffset: 1, - - _frameBuffer: null, - _texture: null, - _shader: null, - - _idMaterials: [], - _lookupTable: [], - - _meshMaterials: [], - - _idOffset: 0 - }; - }, function() { - if (this.renderer) { - this.width = this.renderer.width; - this.height = this.renderer.height; - } - this._init(); - }, /** @lends qtek.picking.PixelPicking.prototype */ { - _init: function() { - this._texture = new Texture2D({ - width: this.width * this.downSampleRatio, - height: this.height * this.downSampleRatio - }); - this._frameBuffer = new FrameBuffer(); - - this._shader = new Shader({ - vertex: Shader.source('buildin.picking.color.vertex'), - fragment: Shader.source('buildin.picking.color.fragment') - }); - }, - /** - * Set picking presision - * @param {number} ratio - */ - setPrecision: function(ratio) { - this._texture.width = this.width * ratio; - this._texture.height = this.height * ratio; - this.downSampleRatio = ratio; - }, - resize: function(width, height) { - this._texture.width = width * this.downSampleRatio; - this._texture.height = height * this.downSampleRatio; - this.width = width; - this.height = height; - this._texture.dirty(); - }, - /** - * Update the picking framebuffer - * @param {number} ratio - */ - update: function(scene, camera) { - var renderer = this.renderer; - if (renderer.width !== this.width || renderer.height !== this.height) { - this.resize(renderer.width, renderer.height); - } - - this._frameBuffer.attach(renderer.gl, this._texture); - this._frameBuffer.bind(renderer); - this._idOffset = this.lookupOffset; - this._setMaterial(scene); - renderer.render(scene, camera); - this._restoreMaterial(); - this._frameBuffer.unbind(renderer); - }, - - _setMaterial: function(root) { - for (var i =0; i < root._children.length; i++) { - var child = root._children[i]; - if (child.geometry && child.material && child.material.shader) { - var id = this._idOffset++; - var idx = id - this.lookupOffset; - var material = this._idMaterials[idx]; - if (!material) { - material = new Material({ - shader: this._shader - }); - var color = packID(id); - color[0] /= 255; - color[1] /= 255; - color[2] /= 255; - color[3] = 1.0; - material.set('color', color); - this._idMaterials[idx] = material; - } - this._meshMaterials[idx] = child.material; - this._lookupTable[idx] = child; - child.material = material; - } - if (child._children.length) { - this._setMaterial(child); - } - } - }, - - /** - * Pick the object - * @param {number} x Mouse position x - * @param {number} y Mouse position y - * @return {qtek.Node} - */ - pick: function(x, y) { - var renderer = this.renderer; - - var ratio = this.downSampleRatio; - x = Math.ceil(ratio * x); - y = Math.ceil(ratio * (this.height - y)); - - this._frameBuffer.bind(renderer); - var pixel = new Uint8Array(4); - var _gl = renderer.gl; - // TODO out of bounds ? - // preserveDrawingBuffer ? - _gl.readPixels(x, y, 1, 1, _gl.RGBA, _gl.UNSIGNED_BYTE, pixel); - this._frameBuffer.unbind(renderer); - // Skip interpolated pixel because of anti alias - if (pixel[3] === 255) { - var id = unpackID(pixel[0], pixel[1], pixel[2]); - if (id) { - var el = this._lookupTable[id - this.lookupOffset]; - return el; - } - } - }, - - _restoreMaterial: function() { - for (var i = 0; i < this._lookupTable.length; i++) { - this._lookupTable[i].material = this._meshMaterials[i]; - } - }, - - dispose: function(_gl) { - this._frameBuffer.dispose(_gl); - this._shader.dispose(_gl); - } - }); - - function packID(id){ - var r = id >> 16; - var g = (id - (r << 8)) >> 8; - var b = id - (r << 16) - (g<<8); - return [r, g, b]; - } - - function unpackID(r, g, b){ - return (r << 16) + (g<<8) + b; - } - - return PixelPicking; -}); -define('qtek/picking/RayPicking',['require','../core/Base','../math/Ray','../math/Vector2','../math/Vector3','../math/Matrix4','../Renderable','../StaticGeometry','../core/glenum'],function(require) { - - var Base = require('../core/Base'); - var Ray = require('../math/Ray'); - var Vector2 = require('../math/Vector2'); - var Vector3 = require('../math/Vector3'); - var Matrix4 = require('../math/Matrix4'); - var Renderable = require('../Renderable'); - var StaticGeometry = require('../StaticGeometry'); - var glenum = require('../core/glenum'); - - /** - * @constructor qtek.picking.RayPicking - * @extends qtek.core.Base - */ - var RayPicking = Base.derive( - /** @lends qtek.picking.RayPicking# */ - { - /** - * Target scene - * @type {qtek.Scene} - */ - scene: null, - /** - * Target camera - * @type {qtek.Camera} - */ - camera: null, - /** - * Target renderer - * @type {qtek.Renderer} - */ - renderer: null - }, function() { - this._ray = new Ray(); - this._ndc = new Vector2(); - }, - /** @lends qtek.picking.RayPicking.prototype */ - { - - /** - * Pick the nearest intersection object in the scene - * @param {number} x Mouse position x - * @param {number} y Mouse position y - * @return {qtek.picking.RayPicking~Intersection} - */ - pick: function(x, y) { - var out = this.pickAll(x, y); - return out[0] || null; - }, - - /** - * Pick all intersection objects, wich will be sorted from near to far - * @param {number} x Mouse position x - * @param {number} y Mouse position y - * @return {Array.} - */ - pickAll: function(x, y) { - this.renderer.screenToNdc(x, y, this._ndc); - this.camera.castRay(this._ndc, this._ray); - - var output = []; - - this._intersectNode(this.scene, output); - - output.sort(this._intersectionCompareFunc); - - return output; - }, - - _intersectNode: function(node, out) { - if ((node instanceof Renderable) && node.isRenderable()) { - if (!node.ignorePicking && node.geometry.isUseFace()) { - this._intersectRenderable(node, out); - } - } - for (var i = 0; i < node._children.length; i++) { - this._intersectNode(node._children[i], out); - } - }, - - _intersectRenderable: (function() { - - var v1 = new Vector3(); - var v2 = new Vector3(); - var v3 = new Vector3(); - var ray = new Ray(); - var worldInverse = new Matrix4(); - - return function(renderable, out) { - - ray.copy(this._ray); - Matrix4.invert(worldInverse, renderable.worldTransform); - - ray.applyTransform(worldInverse); - - var geometry = renderable.geometry; - if (geometry.boundingBox) { - if (!ray.intersectBoundingBox(geometry.boundingBox)) { - return; - } - } - // Use user defined ray picking algorithm - if (geometry.pickByRay) { - var intersection = geometry.pickByRay(ray); - if (intersection) { - out.push(intersection); - } - return; - } - - var isStatic = geometry instanceof StaticGeometry; - var cullBack = (renderable.cullFace === glenum.BACK && renderable.frontFace === glenum.CCW) - || (renderable.cullFace === glenum.FRONT && renderable.frontFace === glenum.CW); - - var point; - if (isStatic) { - var faces = geometry.faces; - var positions = geometry.attributes.position.value; - for (var i = 0; i < faces.length;) { - var i1 = faces[i++] * 3; - var i2 = faces[i++] * 3; - var i3 = faces[i++] * 3; - - v1._array[0] = positions[i1]; - v1._array[1] = positions[i1 + 1]; - v1._array[2] = positions[i1 + 2]; - - v2._array[0] = positions[i2]; - v2._array[1] = positions[i2 + 1]; - v2._array[2] = positions[i2 + 2]; - - v3._array[0] = positions[i3]; - v3._array[1] = positions[i3 + 1]; - v3._array[2] = positions[i3 + 2]; - - if (cullBack) { - point = ray.intersectTriangle(v1, v2, v3, renderable.culling); - } else { - point = ray.intersectTriangle(v1, v3, v2, renderable.culling); - } - if (point) { - var pointW = new Vector3(); - Vector3.transformMat4(pointW, point, renderable.worldTransform); - out.push(new RayPicking.Intersection( - point, pointW, renderable, [i1, i2, i3], - Vector3.dist(pointW, this._ray.origin) - )); - } - } - } else { - var faces = geometry.faces; - var positions = geometry.attributes.position.value; - for (var i = 0; i < faces.length; i++) { - var face = faces[i]; - var i1 = face[0]; - var i2 = face[1]; - var i3 = face[2]; - - v1.setArray(positions[i1]); - v2.setArray(positions[i2]); - v3.setArray(positions[i3]); - - if (cullBack) { - point = ray.intersectTriangle(v1, v2, v3, renderable.culling); - } else { - point = ray.intersectTriangle(v1, v3, v2, renderable.culling); - } - if (point) { - var pointW = new Vector3(); - Vector3.transformMat4(pointW, point, renderable.worldTransform); - out.push(new RayPicking.Intersection( - point, pointW, renderable, [i1, i2, i3], - Vector3.dist(pointW, this._ray.origin) - )); - } - } - } - }; - })(), - - _intersectionCompareFunc: function(a, b) { - return a.distance - b.distance; - } - }); - - /** - * @constructor qtek.picking.RayPicking~Intersection - * @param {qtek.math.Vector3} point - * @param {qtek.math.Vector3} pointWorld - * @param {qtek.Node} target - * @param {Array.} face - * @param {number} distance - */ - RayPicking.Intersection = function(point, pointWorld, target, face, distance) { - /** - * Intersection point in local transform coordinates - * @type {qtek.math.Vector3} - */ - this.point = point; - /** - * Intersection point in world transform coordinates - * @type {qtek.math.Vector3} - */ - this.pointWorld = pointWorld; - /** - * Intersection scene node - * @type {qtek.Node} - */ - this.target = target; - /** - * Intersection triangle, which is an array of vertex index - * @type {Array.} - */ - this.face = face; - /** - * Distance from intersection point to ray origin - * @type {number} - */ - this.distance = distance; - }; - - return RayPicking; -}); -define('qtek/plugin/FirstPersonControl',['require','../core/Base','../math/Vector3','../math/Matrix4','../math/Quaternion'],function(require) { - - var Base = require('../core/Base'); - var Vector3 = require('../math/Vector3'); - var Matrix4 = require('../math/Matrix4'); - var Quaternion = require('../math/Quaternion'); - - /** - * @constructor qtek.plugin.FirstPersonControl - * @example - * var control = new qtek.plugin.FirstPersonControl({ - * target: camera, - * domElement: renderer.canvas - * }); - * ... - * animation.on('frame', function(frameTime) { - * control.update(frameTime); - * renderer.render(scene, camera); - * }); - */ - var FirstPersonControl = Base.derive(function() { - return /** @lends qtek.plugin.FirstPersonControl# */ { - /** - * Scene node to control, mostly it is a camera - * @type {qtek.Node} - */ - target: null, - - /** - * Target dom to bind with mouse events - * @type {HTMLElement} - */ - domElement: null, - - /** - * Mouse move sensitivity - * @type {number} - */ - sensitivity: 1, - - /** - * Target move speed - * @type {number} - */ - speed: 0.4, - - /** - * Up axis - * @type {qtek.math.Vector3} - */ - up: new Vector3(0, 1, 0), - - /** - * If lock vertical movement - * @type {boolean} - */ - verticalMoveLock: false, - - _moveForward: false, - _moveBackward: false, - _moveLeft: false, - _moveRight: false, - - _offsetPitch: 0, - _offsetRoll: 0 - }; - }, function() { - this._lockChange = this._lockChange.bind(this); - this._keyDown = this._keyDown.bind(this); - this._keyUp = this._keyUp.bind(this); - this._mouseMove = this._mouseMove.bind(this); - - if (this.domElement) { - this.enable(); - } - }, - /** @lends qtek.plugin.FirstPersonControl.prototype */ - { - /** - * Enable control - */ - enable: function() { - // Use pointer lock - // http://www.html5rocks.com/en/tutorials/pointerlock/intro/ - var el = this.domElement; - - //Must request pointer lock after click event, can't not do it directly - //Why ? ? - el.addEventListener('click', this._requestPointerLock); - - document.addEventListener('pointerlockchange', this._lockChange); - document.addEventListener('mozpointerlockchange', this._lockChange); - document.addEventListener('webkitpointerlockchange', this._lockChange); - - document.addEventListener('keydown', this._keyDown); - document.addEventListener('keyup', this._keyUp); - }, - - /** - * Disable control - */ - disable: function() { - - this.target.off('beforeupdate', this._beforeUpdateCamera); - - var el = this.domElement; - - el.exitPointerLock = el.exitPointerLock - || el.mozExitPointerLock - || el.webkitExitPointerLock; - - if (el.exitPointerLock) { - el.exitPointerLock(); - } - - this.domElement.removeEventListener('click', this._requestPointerLock); - - document.removeEventListener('pointerlockchange', this._lockChange); - document.removeEventListener('mozpointerlockchange', this._lockChange); - document.removeEventListener('webkitpointerlockchange', this._lockChange); - - document.removeEventListener('keydown', this._keyDown); - document.removeEventListener('keyup', this._keyUp); - }, - - _requestPointerLock: function() { - var el = this; - el.requestPointerLock = el.requestPointerLock - || el.mozRequestPointerLock - || el.webkitRequestPointerLock; - - el.requestPointerLock(); - }, - - /** - * Control update. Should be invoked every frame - * @param {number} frameTime Frame time - */ - update: function(frameTime) { - var target = this.target; - - var position = this.target.position; - var xAxis = target.localTransform.x.normalize(); - var zAxis = target.localTransform.z.normalize(); - - if (this.verticalMoveLock) { - zAxis.y = 0; - zAxis.normalize(); - } - - var speed = this.speed * frameTime / 20; - - if (this._moveForward) { - // Opposite direction of z - position.scaleAndAdd(zAxis, -speed); - } - if (this._moveBackward) { - position.scaleAndAdd(zAxis, speed); - } - if (this._moveLeft) { - position.scaleAndAdd(xAxis, -speed / 2); - } - if (this._moveRight) { - position.scaleAndAdd(xAxis, speed / 2); - } - - target.rotateAround(target.position, this.up, -this._offsetPitch * frameTime * Math.PI / 360); - var xAxis = target.localTransform.right; - target.rotateAround(target.position, xAxis, -this._offsetRoll * frameTime * Math.PI / 360); - - this._offsetRoll = this._offsetPitch = 0; - }, - - _lockChange: function() { - if ( - document.pointerLockElement === this.domElement - || document.mozPointerLockElement === this.domElement - || document.webkitPointerLockElement === this.domElement - ) { - document.addEventListener('mousemove', this._mouseMove, false); - } else { - document.removeEventListener('mousemove', this._mouseMove); - } - }, - - _mouseMove: function(e) { - var dx = e.movementX || e.mozMovementX || e.webkitMovementX || 0; - var dy = e.movementY || e.mozMovementY || e.webkitMovementY || 0; - - this._offsetPitch += dx * this.sensitivity / 200; - this._offsetRoll += dy * this.sensitivity / 200; - }, - - _keyDown: function(e) { - switch(e.keyCode) { - case 87: //w - case 37: //up arrow - this._moveForward = true; - break; - case 83: //s - case 40: //down arrow - this._moveBackward = true; - break; - case 65: //a - case 37: //left arrow - this._moveLeft = true; - break; - case 68: //d - case 39: //right arrow - this._moveRight = true; - break; - } - }, - - _keyUp: function(e) { - this._moveForward = false; - this._moveBackward = false; - this._moveLeft = false; - this._moveRight = false; - } - }); - - return FirstPersonControl; -}); -define('qtek/plugin/InfinitePlane',['require','../Mesh','../DynamicGeometry','../math/Plane','../math/Vector3','../math/Matrix4','../math/Ray','../camera/Perspective','../dep/glmatrix'],function(require) { - - var Mesh = require('../Mesh'); - var DynamicGeometry = require('../DynamicGeometry'); - var Plane = require('../math/Plane'); - var Vector3 = require('../math/Vector3'); - var Matrix4 = require('../math/Matrix4'); - var Ray = require('../math/Ray'); - - var PerspectiveCamera = require('../camera/Perspective'); - - var glMatrix = require('../dep/glmatrix'); - var mat4 = glMatrix.mat4; - var vec3 = glMatrix.vec3; - var vec4 = glMatrix.vec4; - - var uvs = [[0, 0], [0, 1], [1, 1], [1, 0]]; - var tris = [0, 1, 2, 2, 3, 0]; - - var InfinitePlane = Mesh.derive({ - - camera: null, - - plane: null, - - gridSize: 1, - - maxGrid: 0, - - // TODO - frustumCulling: false - - }, function() { - if (!this.geometry) { - this.geometry = new DynamicGeometry(); - } - if (!this.plane) { - this.plane = new Plane(); - } - }, { - - updateGeometry: function() { - - var coords = this._unProjectGrid(); - if (!coords) { - return; - } - var positions = this.geometry.attributes.position.value; - var normals = this.geometry.attributes.normal.value; - var texcoords = this.geometry.attributes.texcoord0.value; - var faces = this.geometry.faces; - var nVertices = 0; - var normal = vec3.clone(this.plane.normal._array); - - // if (this.gridSize > 0) { - // TODO - - // } else { - for (var i = 0; i < 6; i++) { - var idx = tris[i]; - positions[nVertices] = coords[idx]._array; - normals[nVertices] = normal; - texcoords[nVertices] = uvs[idx]; - nVertices++; - } - faces[0] = [0, 1, 2]; - faces[1] = [3, 4, 5]; - this.geometry.dirty(); - // } - }, - - // http://fileadmin.cs.lth.se/graphics/theses/projects/projgrid/ - _unProjectGrid: (function() { - - var planeViewSpace = new Plane(); - var lines = [ - 0, 1, 0, 2, 1, 3, 2, 3, - 4, 5, 4, 6, 5, 7, 6, 7, - 0, 4, 1, 5, 2, 6, 3, 7 - ]; - - var start = new Vector3(); - var end = new Vector3(); - - var points = []; - - // 1----2 - // | | - // 0----3 - var coords = []; - for (var i = 0; i < 4; i++) { - coords[i] = new Vector3(0, 0); - } - - var ray = new Ray(); - - return function() { - planeViewSpace.copy(this.plane); - planeViewSpace.applyTransform(this.camera.viewMatrix); - - var frustumVertices = this.camera.frustum.vertices; - - var nPoints = 0; - // Intersect with lines of frustum - for (var i = 0; i < 12; i++) { - start._array = frustumVertices[lines[i * 2]]; - end._array = frustumVertices[lines[i * 2 + 1]]; - - var point = planeViewSpace.intersectLine(start, end, points[nPoints]); - if (point) { - if (!points[nPoints]) { - points[nPoints] = point; - } - nPoints++; - } - } - if (nPoints === 0) { - return; - } - for (var i = 0; i < nPoints; i++) { - points[i].applyProjection(this.camera.projectionMatrix); - } - var minX = points[0]._array[0]; - var minY = points[0]._array[1]; - var maxX = points[0]._array[0]; - var maxY = points[0]._array[1]; - for (var i = 1; i < nPoints; i++) { - maxX = Math.max(maxX, points[i]._array[0]); - maxY = Math.max(maxY, points[i]._array[1]); - minX = Math.min(minX, points[i]._array[0]); - minY = Math.min(minY, points[i]._array[1]); - } - if (minX == maxX || minY == maxY) { - return; - } - coords[0]._array[0] = minX; - coords[0]._array[1] = minY; - coords[1]._array[0] = minX; - coords[1]._array[1] = maxY; - coords[2]._array[0] = maxX; - coords[2]._array[1] = maxY; - coords[3]._array[0] = maxX; - coords[3]._array[1] = minY; - - for (var i = 0; i < 4; i++) { - this.camera.castRay(coords[i], ray); - ray.intersectPlane(this.plane, coords[i]); - } - - return coords; - }; - })() - }); - - return InfinitePlane; -}); -define('qtek/plugin/OrbitControl',['require','../core/Base','../math/Vector3','../math/Matrix4'],function(require) { - - var Base = require('../core/Base'); - var Vector3 = require('../math/Vector3'); - var Matrix4 = require('../math/Matrix4'); - - /** - * @constructor qtek.plugin.OrbitControl - * - * @example - * - * var control = new qtek.plugin.OrbitControl({ - * target: camera, - * domElement: renderer.canvas - * }); - * // Rotate around car - * control.origin.copy(car.position); - * ... - * animation.on('frame', function(frameTime) { - * control.update(frameTime); - * renderer.render(scene, camera); - * }); - */ - var OrbitControl = Base.derive(function() { - return /** @lends qtek.plugin.OrbitControl# */ { - /** - * Scene node to control, mostly it is a camera - * @type {qtek.Node} - */ - target: null, - - /** - * Target dom to bind with mouse events - * @type {HTMLElement} - */ - domElement: null, - - /** - * Mouse move sensitivity - * @type {number} - */ - sensitivity: 1, - - /** - * Origin to rotate around - * @type {qtek.math.Vector3} - */ - origin: new Vector3(), - - /** - * Up axis - * @type {qtek.math.Vector3} - */ - up: new Vector3(0, 1, 0), - - /** - * Minimum distance from origin to target when zooming in - * @type {number} - */ - minDistance: 0, - /** - * Maximum distance from origin to target when zooming out - * @type {number} - */ - maxDistance: Infinity, - - /** - * Minimum polar angle when rotate up, it is 0 when the direction origin point to target is same with up axis - * @type {number} - */ - minPolarAngle: 0, // [0, Math.PI/2] - - /** - * Maximum polar angle when rotate down. It is PI when the direction origin point to target is opposite to up axis - * @type {number} - */ - maxPolarAngle: Math.PI, // [Math.PI/2, Math.PI] - - // Rotate around origin - _offsetPitch: 0, - _offsetRoll: 0, - - // Pan the origin - _panX: 0, - _panY: 0, - - // Offset of mouse move - _offsetX: 0, - _offsetY: 0, - - // Zoom with mouse wheel - _forward: 0, - - _op: -1 //0: ROTATE, 1: PAN - }; - }, function() { - this._mouseDown = this._mouseDown.bind(this); - this._mouseUp = this._mouseUp.bind(this); - this._mouseMove = this._mouseMove.bind(this); - this._mouseOut = this._mouseOut.bind(this); - this._mouseWheel = this._mouseWheel.bind(this); - - if (this.domElement) { - this.enable(); - } - }, - /** @lends qtek.plugin.OrbitControl.prototype */ - { - /** - * Enable control - */ - enable: function() { - var domElement = this.domElement; - domElement.addEventListener('mousedown', this._mouseDown); - domElement.addEventListener('mousewheel', this._mouseWheel); - domElement.addEventListener('DOMMouseScroll', this._mouseWheel); - - domElement.addEventListener('touchstart', this._mouseDown); - - }, - - /** - * Disable control - */ - disable: function() { - this.domElement.removeEventListener('mousedown', this._mouseDown); - this.domElement.removeEventListener('mousewheel', this._mouseWheel); - this.domElement.removeEventListener('DOMMouseScroll', this._mouseWheel); - - this.domElement.removeEventListener('touchstart', this._mouseDown); - - this._mouseUp(); - }, - - _mouseWheel: function(e) { - e.preventDefault(); - var delta = e.wheelDelta // Webkit - || -e.detail; // Firefox - - this._forward += delta * this.sensitivity; - }, - - _mouseDown: function(e) { - document.addEventListener('mousemove', this._mouseMove); - document.addEventListener('mouseup', this._mouseUp); - document.addEventListener('mouseout', this._mouseOut); - - document.addEventListener('touchend', this._mouseUp); - document.addEventListener('touchmove', this._mouseMove); - - this._offsetX = e.pageX; - this._offsetY = e.pageY; - - // Rotate - if (e.button === 0) { - this._op = 0; - } else if (e.button === 1) { - this._op = 1; - } - }, - - _mouseMove: function(e) { - var dx = e.pageX - this._offsetX; - var dy = e.pageY - this._offsetY; - - if (this._op === 0) { - this._offsetPitch += dx * this.sensitivity / 100; - this._offsetRoll += dy * this.sensitivity / 100; - } else if (this._op === 1) { - var len = this.origin.distance(this.target.position); - var divider; - if (this.target.fov) { - divider = Math.sin(this.target.fov * Math.PI / 360) / 200; - } else { - divider = 1 / 200; - } - this._panX += dx * this.sensitivity * len * divider; - this._panY += dy * this.sensitivity * len * divider; - } - - this._offsetX = e.pageX; - this._offsetY = e.pageY; - }, - - _mouseUp: function() { - - document.removeEventListener('mousemove', this._mouseMove); - document.removeEventListener('mouseup', this._mouseUp); - document.removeEventListener('mouseout', this._mouseOut); - - document.removeEventListener('touchend', this._mouseUp); - document.removeEventListener('touchmove', this._mouseMove); - - this._op = -1; - }, - - _mouseOut: function() { - this._mouseUp(); - }, - - /** - * Control update. Should be invoked every frame - * @param {number} frameTime Frame time - */ - update: function(frameTime) { - var target = this.target; - var zAxis = target.localTransform.z.normalize(); - var yAxis = target.localTransform.y.normalize(); - if (this._op === 0 && this._offsetPitch !== 0 && this._offsetRoll !== 0) { - // Rotate - target.rotateAround(this.origin, this.up, -this._offsetPitch); - var xAxis = target.localTransform.x; - target.rotateAround(this.origin, xAxis, -this._offsetRoll); - - var zAxis = target.worldTransform.z.normalize(); - var phi = Math.acos(this.up.dot(zAxis)); - // Rotate back a bit - if (this._offsetRoll > 0 && phi <= this.minPolarAngle) { - target.rotateAround(this.origin, xAxis, -phi + this.minPolarAngle); - } - else if (this._offsetRoll < 0 && phi >= this.maxPolarAngle) { - target.rotateAround(this.origin, xAxis, -phi + this.maxPolarAngle); - } - this._offsetRoll = this._offsetPitch = 0; - } else if (this._op === 1) { - // Pan - var xAxis = target.localTransform.x.normalize().scale(-this._panX); - var yAxis = target.localTransform.y.normalize().scale(this._panY); - target.position.add(xAxis).add(yAxis); - this.origin.add(xAxis).add(yAxis); - this._panX = this._panY = 0; - } - if (this._forward !== 0) { - // Zoom - var distance = target.position.distance(this.origin); - var nextDistance = distance + this._forward * distance / 5000; - if (nextDistance < this.maxDistance && nextDistance > this.minDistance) { - target.position.scaleAndAdd(zAxis, this._forward * distance / 5000); - } - this._forward = 0; - } - - } - }); - - return OrbitControl; -}); -define('qtek/plugin/Skybox',['require','../Mesh','../geometry/Cube','../Shader','../Material'],function(require) { - - var Mesh = require('../Mesh'); - var CubeGeometry = require('../geometry/Cube'); - var Shader = require('../Shader'); - var Material = require('../Material'); - - var skyboxShader; - - /** - * @constructor qtek.plugin.Skybox - * - * @example - * var skyTex = new qtek.TextureCube(); - * skyTex.load({ - * 'px': 'assets/textures/sky/px.jpg', - * 'nx': 'assets/textures/sky/nx.jpg' - * 'py': 'assets/textures/sky/py.jpg' - * 'ny': 'assets/textures/sky/ny.jpg' - * 'pz': 'assets/textures/sky/pz.jpg' - * 'nz': 'assets/textures/sky/nz.jpg' - * }); - * var skybox = new qtek.plugin.Skybox({ - * scene: scene - * }); - * skybox.material.set('environmentMap', skyTex); - */ - var Skybox = Mesh.derive(function() { - - if (!skyboxShader) { - skyboxShader = new Shader({ - vertex: Shader.source('buildin.skybox.vertex'), - fragment: Shader.source('buildin.skybox.fragment') - }); - } - var material = new Material({ - shader: skyboxShader, - depthMask: false - }); - - return { - /** - * @type {qtek.Scene} - * @memberOf qtek.plugin.Skybox.prototype - */ - scene: null, - - geometry: new CubeGeometry(), - material: material, - culling: false - }; - }, function() { - var scene = this.scene; - if (scene) { - this.attachScene(scene); - } - }, { - /** - * Attach the skybox to the scene - * @param {qtek.Scene} scene - * @memberOf qtek.plugin.Skybox.prototype - */ - attachScene: function(scene) { - if (this.scene) { - this.detachScene(); - } - this.scene = scene; - scene.on('beforerender', this._beforeRenderScene, this); - }, - /** - * Detach from scene - * @memberOf qtek.plugin.Skybox.prototype - */ - detachScene: function() { - if (this.scene) { - this.scene.off('beforerender', this._beforeRenderScene, this); - } - this.scene = null; - }, - - dispose: function() { - this.detachScene(); - }, - - _beforeRenderScene: function(renderer, scene, camera) { - this.position.copy(camera.getWorldPosition()); - this.update(); - renderer.renderQueue([this], camera); - } - }); - - return Skybox; -}); -define('qtek/plugin/Skydome',['require','../Mesh','../geometry/Sphere','../Shader','../Material','../shader/library'],function(require) { - - var Mesh = require('../Mesh'); - var SphereGeometry = require('../geometry/Sphere'); - var Shader = require('../Shader'); - var Material = require('../Material'); - var shaderLibrary = require('../shader/library'); - - var skydomeShader; - - /** - * @constructor qtek.plugin.Skydome - * - * @example - * var skyTex = new qtek.Texture2D(); - * skyTex.load('assets/textures/sky.jpg'); - * var skydome = new qtek.plugin.Skydome({ - * scene: scene - * }); - * skydome.material.set('diffuseMap', skyTex); - */ - var Skydome = Mesh.derive(function() { - - if (!skydomeShader) { - skydomeShader = new Shader({ - vertex: Shader.source('buildin.basic.vertex'), - fragment: Shader.source('buildin.basic.fragment') - }); - skydomeShader.enableTexture('diffuseMap'); - } - - var material = new Material({ - shader: skydomeShader, - depthMask: false - }); - - return { - /** - * @type {qtek.Scene} - * @memberOf qtek.plugin.Skydome# - */ - scene: null, - - geometry: new SphereGeometry({ - widthSegments: 30, - heightSegments: 30, - // thetaLength: Math.PI / 2 - }), - material: material, - culling: false - }; - }, function() { - var scene = this.scene; - if (scene) { - this.attachScene(scene); - } - }, { - /** - * Attach the skybox to the scene - * @param {qtek.Scene} scene - * @memberOf qtek.plugin.Skydome.prototype - */ - attachScene: function(scene) { - if (this.scene) { - this.detachScene(); - } - this.scene = scene; - scene.on('beforerender', this._beforeRenderScene, this); - }, - /** - * Detach from scene - * @memberOf qtek.plugin.Skydome.prototype - */ - detachScene: function() { - if (this.scene) { - this.scene.off('beforerender', this._beforeRenderScene, this); - } - this.scene = null; - }, - - _beforeRenderScene: function(renderer, scene, camera) { - this.position.copy(camera.getWorldPosition()); - this.update(); - renderer.renderQueue([this], camera); - }, - - dispose: function() { - this.detachScene(); - } - }); - - return Skydome; -}); -define('qtek/plugin/_Sprite',['require','../Mesh','../geometry/Plane','../Material','../Shader','../math/Matrix4'],function (require) { - - var Mesh = require('../Mesh'); - var PlaneGeometry = require('../geometry/Plane'); - var Material = require('../Material'); - var Shader = require('../Shader'); - var Matrix4 = require('../math/Matrix4'); - - var spriteShader; - - /** - * @constructor qtek.plugin.Sprite - */ - var Sprite = Mesh.derive(function () { - if (!spriteShader) { - spriteShader = new Shader({ - vertex: Shader.source('buildin.basic.vertex'), - fragment: Shader.source('buildin.basic.fragment') - }); - spriteShader.enableTexture('diffuseMap'); - } - var material = new Material({ - shader: spriteShader - }); - - return { - /** - * @type {qtek.Camera} - * @memberOf qtek.plugin.Skybox.prototype - */ - camera: null, - - geometry: new PlaneGeometry() - } - }, { - update: function (forceUpdateWorld) { - this.worldTransform.z = this.camera.worldTransform.z; - this.worldTransform.y = this.camera.worldTransform.y; - this.worldTransform.x = this.camera.worldTransform.x; - - this.decomposeWorldTransform(); - - Mesh.prototype.update.call(this, forceUpdateWorld); - } - }); -}); -define('qtek/prePass/EnvironmentMap',['require','../core/Base','../math/Vector3','../camera/Perspective','../core/glenum','../FrameBuffer','../TextureCube'],function (require) { - - var Base = require('../core/Base'); - var Vector3 = require('../math/Vector3'); - var PerspectiveCamera = require('../camera/Perspective'); - var glenum = require('../core/glenum'); - var FrameBuffer = require('../FrameBuffer'); - var TextureCube = require('../TextureCube'); - - var targets = ['px', 'nx', 'py', 'ny', 'pz', 'nz']; - - /** - * Pass rendering scene to a environment cube map - * - * @constructor qtek.prePass.EnvironmentMap - * @extends qtek.core.Base - * @example - * // Example of car reflection - * var envMap = new qtek.TextureCube({ - * width: 256, - * height: 256 - * }); - * var envPass = new qtek.prePass.EnvironmentMap({ - * position: car.position, - * texture: envMap - * }); - * var carBody = car.getChildByName('body'); - * carBody.material.shader.enableTexture('environmentMap'); - * carBody.material.set('environmentMap', envMap); - * ... - * animation.on('frame', function(frameTime) { - * envPass.render(renderer, scene); - * renderer.render(scene, camera); - * }); - */ - var EnvironmentMapPass = Base.derive(function() { - var ret = { - /** - * Camera position - * @type {qtek.math.Vector3} - * @memberOf qtek.prePass.EnvironmentMap# - */ - position: new Vector3(), - /** - * Camera far plane - * @type {number} - * @memberOf qtek.prePass.EnvironmentMap# - */ - far: 1000, - /** - * Camera near plane - * @type {number} - * @memberOf qtek.prePass.EnvironmentMap# - */ - near: 0.1, - /** - * Environment cube map - * @type {qtek.TextureCube} - * @memberOf qtek.prePass.EnvironmentMap# - */ - texture: null - - // frameBuffer: new FrameBuffer() - }; - ret._cameras = { - px: new PerspectiveCamera({fov: 90}), - nx: new PerspectiveCamera({fov: 90}), - py: new PerspectiveCamera({fov: 90}), - ny: new PerspectiveCamera({fov: 90}), - pz: new PerspectiveCamera({fov: 90}), - nz: new PerspectiveCamera({fov: 90}) - }; - ret._cameras.px.lookAt(Vector3.POSITIVE_X, Vector3.NEGATIVE_Y); - ret._cameras.nx.lookAt(Vector3.NEGATIVE_X, Vector3.NEGATIVE_Y); - ret._cameras.py.lookAt(Vector3.POSITIVE_Y, Vector3.POSITIVE_Z); - ret._cameras.ny.lookAt(Vector3.NEGATIVE_Y, Vector3.NEGATIVE_Z); - ret._cameras.pz.lookAt(Vector3.POSITIVE_Z, Vector3.NEGATIVE_Y); - ret._cameras.nz.lookAt(Vector3.NEGATIVE_Z, Vector3.NEGATIVE_Y); - - // FIXME In windows, use one framebuffer only renders one side of cubemap - ret._frameBuffers = { - px: new FrameBuffer(), - nx: new FrameBuffer(), - py: new FrameBuffer(), - ny: new FrameBuffer(), - pz: new FrameBuffer(), - nz: new FrameBuffer() - }; - - return ret; - }, { - /** - * @param {qtek.Renderer} renderer - * @param {qtek.Scene} scene - * @param {boolean} [notUpdateScene=false] - */ - render: function(renderer, scene, notUpdateScene) { - var _gl = renderer.gl; - if (!notUpdateScene) { - scene.update(true); - } - // Tweak fov - // http://the-witness.net/news/2012/02/seamless-cube-map-filtering/ - var n = this.texture.width; - var fov = 2 * Math.atan(n / (n - 0.5)) / Math.PI * 180; - for (var i = 0; i < 6; i++) { - var target = targets[i]; - var camera = this._cameras[target]; - Vector3.copy(camera.position, this.position); - camera.far = this.far; - camera.near = this.near; - camera.fov = fov; - - this._frameBuffers[target].attach( - _gl, this.texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i - ); - this._frameBuffers[target].bind(renderer); - renderer.render(scene, camera, true); - this._frameBuffers[target].unbind(renderer); - } - }, - /** - * @param {qtek.Renderer} renderer - */ - dispose: function(renderer) { - // this.frameBuffer.dispose(renderer.gl); - for (var i = 0; i < 6; i++) { - var target = targets[i]; - this._frameBuffers[target].dispose(renderer.gl); - } - } - }); - - return EnvironmentMapPass; -}); -define('qtek/prePass/Reflection',['require','../core/Base','../math/Vector4'],function(require) { - - var Base = require('../core/Base'); - var Vector4 = require('../math/Vector4'); - - var ReflectionPass = Base.derive(function() { - console.warn('TODO'); - }, { - render : function(renderer, scene, camera) { - - } - }); - - return ReflectionPass; -}); -define('qtek/prePass/ShadowMap',['require','../core/Base','../core/glenum','../math/Vector3','../math/BoundingBox','../math/Frustum','../math/Matrix4','../Renderer','../Shader','../Light','../Mesh','../light/Spot','../light/Directional','../light/Point','../shader/library','../Material','../FrameBuffer','../Texture2D','../TextureCube','../camera/Perspective','../camera/Orthographic','../compositor/Pass','../compositor/TexturePool','../dep/glmatrix'],function(require) { - - var Base = require('../core/Base'); - var glenum = require('../core/glenum'); - var Vector3 = require('../math/Vector3'); - var BoundingBox = require('../math/BoundingBox'); - var Frustum = require('../math/Frustum'); - var Matrix4 = require('../math/Matrix4'); - var Renderer = require('../Renderer'); - var Shader = require('../Shader'); - var Light = require('../Light'); - var Mesh = require('../Mesh'); - var SpotLight = require('../light/Spot'); - var DirectionalLight = require('../light/Directional'); - var PointLight = require('../light/Point'); - var shaderLibrary = require('../shader/library'); - var Material = require('../Material'); - var FrameBuffer = require('../FrameBuffer'); - var Texture2D = require('../Texture2D'); - var TextureCube = require('../TextureCube'); - var PerspectiveCamera = require('../camera/Perspective'); - var OrthoCamera = require('../camera/Orthographic'); - - var Pass = require('../compositor/Pass'); - var TexturePool = require('../compositor/TexturePool'); - - var glMatrix = require('../dep/glmatrix'); - var mat4 = glMatrix.mat4; - var vec3 = glMatrix.vec3; - - var targets = ['px', 'nx', 'py', 'ny', 'pz', 'nz']; - - /** - * Pass rendering shadow map. - * - * @constructor qtek.prePass.ShadowMap - * @extends qtek.core.Base - * @example - * var shadowMapPass = new qtek.prePass.ShadowMap({ - * softShadow: qtek.prePass.ShadowMap.VSM - * }); - * ... - * animation.on('frame', function(frameTime) { - * shadowMapPass.render(renderer, scene, camera); - * renderer.render(scene, camera); - * }); - */ - var ShadowMapPass = Base.derive(function() { - return /** @lends qtek.prePass.ShadowMap# */ { - /** - * Soft shadow technique. - * Can be {@link qtek.prePass.ShadowMap.PCF} or {@link qtek.prePass.ShadowMap.VSM} - * @type {number} - */ - softShadow: ShadowMapPass.PCF, - - /** - * Soft shadow blur size - * @type {number} - */ - shadowBlur: 1.0, - - /** - * Shadow cascade. - * Use PSSM technique when it is larger than 1 and have a unique directional light in scene. - * @type {number} - */ - shadowCascade: 1, - - /** - * Available when shadowCascade is larger than 1 and have a unique directional light in scene. - * @type {number} - */ - cascadeSplitLogFactor: 0.2, - - lightFrustumBias: 10, - - _frameBuffer: new FrameBuffer(), - - _textures: {}, - _shadowMapNumber: { - 'POINT_LIGHT': 0, - 'DIRECTIONAL_LIGHT': 0, - 'SPOT_LIGHT': 0 - }, - - _meshMaterials: {}, - _depthMaterials: {}, - _depthShaders: {}, - _distanceMaterials: {}, - - _opaqueCasters: [], - _receivers: [], - _lightsCastShadow: [], - - _lightCameras: {}, - - _texturePool: new TexturePool() - }; - }, function() { - // Gaussian filter pass for VSM - this._gaussianPassH = new Pass({ - fragment: Shader.source('buildin.compositor.gaussian_blur_h') - }); - this._gaussianPassV = new Pass({ - fragment: Shader.source('buildin.compositor.gaussian_blur_v') - }); - this._gaussianPassH.setUniform('blurSize', this.shadowBlur); - this._gaussianPassV.setUniform('blurSize', this.shadowBlur); - - this._outputDepthPass = new Pass({ - fragment: Shader.source('buildin.sm.debug_depth') - }); - }, { - /** - * Render scene to shadow textures - * @param {qtek.Renderer} renderer - * @param {qtek.Scene} scene - * @param {qtek.Camera} sceneCamera - * @memberOf qtek.prePass.ShadowMap.prototype - */ - render: function(renderer, scene, sceneCamera) { - this.trigger('beforerender', this, renderer, scene, sceneCamera); - this._renderShadowPass(renderer, scene, sceneCamera); - this.trigger('afterrender', this, renderer, scene, sceneCamera); - }, - - /** - * Debug rendering of shadow textures - * @param {qtek.Renderer} renderer - * @param {number} size - * @memberOf qtek.prePass.ShadowMap.prototype - */ - renderDebug: function(renderer, size) { - var prevClear = renderer.clear; - renderer.clear = glenum.DEPTH_BUFFER_BIT; - var viewport = renderer.viewport; - var x = 0, y = 0; - var width = size || viewport.width / 4; - var height = width; - if (this.softShadow === ShadowMapPass.VSM) { - this._outputDepthPass.material.shader.define('fragment', 'USE_VSM'); - } else { - this._outputDepthPass.material.shader.unDefine('fragment', 'USE_VSM'); - } - for (var name in this._textures) { - renderer.setViewport(x, y, width, height); - this._outputDepthPass.setUniform('depthMap', this._textures[name]); - this._outputDepthPass.render(renderer); - x += width; - } - renderer.setViewport(viewport); - renderer.clear = prevClear; - }, - - _bindDepthMaterial: function(casters, bias, slopeScale) { - for (var i = 0; i < casters.length; i++) { - var mesh = casters[i]; - var isShadowTransparent = mesh.material.shadowTransparentMap instanceof Texture2D; - var transparentMap = mesh.material.shadowTransparentMap; - var nJoints = mesh.joints && mesh.joints.length; - var matHashKey; - var shaderHashKey; - if (isShadowTransparent) { - matHashKey = nJoints + '-' + transparentMap.__GUID__; - shaderHashKey = nJoints + 's'; - } else { - matHashKey = nJoints; - shaderHashKey = nJoints; - } - var depthMaterial = this._depthMaterials[matHashKey]; - var depthShader = this._depthShaders[shaderHashKey]; - - if (mesh.material !== depthMaterial) { // Not binded yet - if (!depthShader) { - depthShader = new Shader({ - vertex: Shader.source('buildin.sm.depth.vertex'), - fragment: Shader.source('buildin.sm.depth.fragment') - }); - if (nJoints > 0) { - depthShader.define('vertex', 'SKINNING'); - depthShader.define('vertex', 'JOINT_NUMBER', nJoints); - } - if (isShadowTransparent) { - depthShader.define('both', 'SHADOW_TRANSPARENT'); - } - this._depthShaders[shaderHashKey] = depthShader; - } - if (!depthMaterial) { - // Skinned mesh - depthMaterial = new Material({ - shader: depthShader - }); - this._depthMaterials[matHashKey] = depthMaterial; - } - - this._meshMaterials[mesh.__GUID__] = mesh.material; - mesh.material = depthMaterial; - - if (this.softShadow === ShadowMapPass.VSM) { - depthShader.define('fragment', 'USE_VSM'); - } else { - depthShader.unDefine('fragment', 'USE_VSM'); - } - - depthMaterial.setUniform('bias', bias); - depthMaterial.setUniform('slopeScale', slopeScale); - if (isShadowTransparent) { - depthMaterial.set('shadowTransparentMap', transparentMap); - } - } - } - }, - - _bindDistanceMaterial: function(casters, light) { - for (var i = 0; i < casters.length; i++) { - var mesh = casters[i]; - var nJoints = mesh.joints && mesh.joints.length; - var distanceMaterial = this._distanceMaterials[nJoints]; - if (mesh.material !== distanceMaterial) { - if (!distanceMaterial) { - // Skinned mesh - distanceMaterial = new Material({ - shader: new Shader({ - vertex: Shader.source('buildin.sm.distance.vertex'), - fragment: Shader.source('buildin.sm.distance.fragment') - }) - }); - if (nJoints > 0) { - distanceMaterial.shader.define('vertex', 'SKINNING'); - distanceMaterial.shader.define('vertex', 'JOINT_NUMBER', nJoints); - } - this._distanceMaterials[nJoints] = distanceMaterial; - } - - this._meshMaterials[mesh.__GUID__] = mesh.material; - mesh.material = distanceMaterial; - - if (this.softShadow === ShadowMapPass.VSM) { - distanceMaterial.shader.define('fragment', 'USE_VSM'); - } else { - distanceMaterial.shader.unDefine('fragment', 'USE_VSM'); - } - distanceMaterial.set('lightPosition', light.position._array); - distanceMaterial.set('range', light.range * 5); - } - } - }, - - _restoreMaterial: function(casters) { - for (var i = 0; i < casters.length; i++) { - var mesh = casters[i]; - mesh.material = this._meshMaterials[mesh.__GUID__]; - } - }, - - _updateCaster: function(mesh) { - if (mesh.castShadow) { - this._opaqueCasters.push(mesh); - } - if (mesh.receiveShadow) { - this._receivers.push(mesh); - mesh.material.__shadowUniformUpdated = false; - mesh.material.shader.__shadowDefineUpdated = false; - mesh.material.set('shadowEnabled', 1); - } else { - mesh.material.set('shadowEnabled', 0); - } - if (this.softShadow === ShadowMapPass.VSM) { - mesh.material.shader.define('fragment', 'USE_VSM'); - } else { - mesh.material.shader.unDefine('fragment', 'USE_VSM'); - } - }, - - _update: function(scene) { - for (var i = 0; i < scene.opaqueQueue.length; i++) { - this._updateCaster(scene.opaqueQueue[i]); - } - for (var i = 0; i < scene.transparentQueue.length; i++) { - // TODO Transparent object receive shadow will be very slow - // in stealth demo, still not find the reason - this._updateCaster(scene.transparentQueue[i]); - } - for (var i = 0; i < scene.lights.length; i++) { - var light = scene.lights[i]; - if (light.castShadow) { - this._lightsCastShadow.push(light); - } - } - }, - - _renderShadowPass: function(renderer, scene, sceneCamera) { - // reset - for (var name in this._shadowMapNumber) { - this._shadowMapNumber[name] = 0; - } - this._lightsCastShadow.length = 0; - this._opaqueCasters.length = 0; - this._receivers.length = 0; - - var _gl = renderer.gl; - - scene.update(); - - this._update(scene); - - if (!this._lightsCastShadow.length) { - return; - } - - _gl.enable(_gl.DEPTH_TEST); - _gl.depthMask(true); - _gl.disable(_gl.BLEND); - - // Clear with high-z, so the part not rendered will not been shadowed - // TODO - _gl.clearColor(1.0, 1.0, 1.0, 1.0); - - // Shadow uniforms - var spotLightShadowMaps = []; - var spotLightMatrices = []; - var directionalLightShadowMaps = []; - var directionalLightMatrices = []; - var shadowCascadeClips = []; - var pointLightShadowMaps = []; - var pointLightRanges = []; - - // Create textures for shadow map - for (var i = 0; i < this._lightsCastShadow.length; i++) { - var light = this._lightsCastShadow[i]; - if (light instanceof DirectionalLight) { - this._renderDirectionalLightShadow( - renderer, - light, - scene, - sceneCamera, - this._opaqueCasters, - shadowCascadeClips, - directionalLightMatrices, - directionalLightShadowMaps - ); - } else if (light instanceof SpotLight) { - this._renderSpotLightShadow( - renderer, - light, - this._opaqueCasters, - spotLightMatrices, - spotLightShadowMaps - ); - } else if (light instanceof PointLight) { - this._renderPointLightShadow( - renderer, - light, - this._opaqueCasters, - pointLightRanges, - pointLightShadowMaps - ); - } - - this._shadowMapNumber[light.type]++; - } - this._restoreMaterial(this._opaqueCasters); - - if (this.shadowCascade > 1 && this._shadowMapNumber.DIRECTIONAL_LIGHT > 1) { - console.warn('There is only one directional light can cast shadow when using cascaded shadow map'); - } - - var shadowCascadeClipsNear = shadowCascadeClips.slice(); - var shadowCascadeClipsFar = shadowCascadeClips.slice(); - shadowCascadeClipsNear.pop(); - shadowCascadeClipsFar.shift(); - - // Iterate from far to near - shadowCascadeClipsNear.reverse(); - shadowCascadeClipsFar.reverse(); - directionalLightShadowMaps.reverse(); - directionalLightMatrices.reverse(); - - for (var i = 0; i < this._receivers.length; i++) { - var mesh = this._receivers[i]; - var material = mesh.material; - if (material.__shadowUniformUpdated) { - continue; - } - var shader = material.shader; - - if (!shader.__shadowDefineUpdated) { - var shaderNeedsUpdate = false; - for (var lightType in this._shadowMapNumber) { - var number = this._shadowMapNumber[lightType]; - var key = lightType + '_SHADOWMAP_NUMBER'; - - if (shader.fragmentDefines[key] !== number && number > 0) { - shader.fragmentDefines[key] = number; - shaderNeedsUpdate = true; - } - } - if (shaderNeedsUpdate) { - shader.dirty(); - } - if (this.shadowCascade > 1) { - shader.define('fragment', 'SHADOW_CASCADE', this.shadowCascade); - } else { - shader.unDefine('fragment', 'SHADOW_CASCADE'); - } - shader.__shadowDefineUpdated = true; - } - - if (spotLightShadowMaps.length > 0) { - material.setUniform('spotLightShadowMaps', spotLightShadowMaps); - material.setUniform('spotLightMatrices', spotLightMatrices); - } - if (directionalLightShadowMaps.length > 0) { - material.setUniform('directionalLightShadowMaps', directionalLightShadowMaps); - if (this.shadowCascade > 1) { - material.setUniform('shadowCascadeClipsNear', shadowCascadeClipsNear); - material.setUniform('shadowCascadeClipsFar', shadowCascadeClipsFar); - } - material.setUniform('directionalLightMatrices', directionalLightMatrices); - } - if (pointLightShadowMaps.length > 0) { - material.setUniform('pointLightShadowMaps', pointLightShadowMaps); - material.setUniform('pointLightRanges', pointLightRanges); - } - material.__shadowUniformUpdated = true; - } - }, - - _renderDirectionalLightShadow: (function() { - - var splitFrustum = new Frustum(); - var splitProjMatrix = new Matrix4(); - var cropBBox = new BoundingBox(); - var cropMatrix = new Matrix4(); - var lightViewProjMatrix = new Matrix4(); - var lightProjMatrix = new Matrix4(); - - var prevDepth = 0; - var deltaDepth = 0; - return function(renderer, light, scene, sceneCamera, casters, shadowCascadeClips, directionalLightMatrices, directionalLightShadowMaps) { - - var shadowBias = light.shadowBias; - this._bindDepthMaterial(casters, shadowBias, light.shadowSlopeScale); - - casters.sort(Renderer.opaqueSortFunc); - - // Adjust scene camera - var originalFar = sceneCamera.far; - - // Considering moving speed since the bounding box is from last frame - // verlet integration ? - var depth = -sceneCamera.sceneBoundingBoxLastFrame.min.z; - deltaDepth = Math.max(depth - prevDepth, 0); - prevDepth = depth; - depth += deltaDepth; - // TODO: add a bias - if (depth > sceneCamera.near) { - sceneCamera.far = Math.min(sceneCamera.far, depth); - } - sceneCamera.updateProjectionMatrix(); - sceneCamera.frustum.setFromProjection(sceneCamera.projectionMatrix); - var lightCamera = this._getDirectionalLightCamera(light, scene, sceneCamera); - - var lvpMat4Arr = lightViewProjMatrix._array; - mat4.copy(lvpMat4Arr, lightCamera.worldTransform._array); - mat4.invert(lvpMat4Arr, lvpMat4Arr); - mat4.multiply(lvpMat4Arr, lightCamera.projectionMatrix._array, lvpMat4Arr); - mat4.multiply(lvpMat4Arr, lvpMat4Arr, sceneCamera.worldTransform._array); - - lightProjMatrix.copy(lightCamera.projectionMatrix); - - var clipPlanes = []; - var near = sceneCamera.near; - var far = sceneCamera.far; - var rad = sceneCamera.fov / 180 * Math.PI; - var aspect = sceneCamera.aspect; - - var scaleZ = (near + originalFar) / (near - originalFar); - var offsetZ = 2 * near * originalFar / (near - originalFar); - for (var i = 0; i <= this.shadowCascade; i++) { - var clog = near * Math.pow(far / near, i / this.shadowCascade); - var cuni = near + (far - near) * i / this.shadowCascade; - var c = clog * this.cascadeSplitLogFactor + cuni * (1 - this.cascadeSplitLogFactor); - clipPlanes.push(c); - shadowCascadeClips.push(-(-c * scaleZ + offsetZ) / -c); - } - for (var i = 0; i < this.shadowCascade; i++) { - var texture = this._getTexture(light.__GUID__ + '_' + i, light); - - // Get the splitted frustum - var nearPlane = clipPlanes[i]; - var farPlane = clipPlanes[i+1]; - mat4.perspective(splitProjMatrix._array, rad, aspect, nearPlane, farPlane); - splitFrustum.setFromProjection(splitProjMatrix); - splitFrustum.getTransformedBoundingBox(cropBBox, lightViewProjMatrix); - var _min = cropBBox.min._array; - var _max = cropBBox.max._array; - cropMatrix.ortho(_min[0], _max[0], _min[1], _max[1], 1, -1); - lightCamera.projectionMatrix.multiplyLeft(cropMatrix); - - var _gl = renderer.gl; - - this._frameBuffer.attach(_gl, texture); - this._frameBuffer.bind(renderer); - - _gl.clear(_gl.COLOR_BUFFER_BIT | _gl.DEPTH_BUFFER_BIT); - - // Set bias seperately for each cascade - // TODO Simply divide 1.5 ? - for (var key in this._depthMaterials) { - this._depthMaterials[key].set('shadowBias', shadowBias); - } - - renderer.renderQueue(casters, lightCamera); - - this._frameBuffer.unbind(renderer); - - // Filter for VSM - if (this.softShadow === ShadowMapPass.VSM) { - this._gaussianFilter(renderer, texture, texture.width); - } - - var matrix = new Matrix4(); - matrix.copy(lightCamera.worldTransform) - .invert() - .multiplyLeft(lightCamera.projectionMatrix); - - directionalLightShadowMaps.push(texture); - directionalLightMatrices.push(matrix._array); - - lightCamera.projectionMatrix.copy(lightProjMatrix); - } - - // set back - sceneCamera.far = originalFar; - }; - })(), - - _renderSpotLightShadow: function(renderer, light, casters, spotLightMatrices, spotLightShadowMaps) { - - this._bindDepthMaterial(casters, light.shadowBias, light.shadowSlopeScale); - casters.sort(Renderer.opaqueSortFunc); - - var texture = this._getTexture(light.__GUID__, light); - var camera = this._getSpotLightCamera(light); - var _gl = renderer.gl; - - this._frameBuffer.attach(_gl, texture); - this._frameBuffer.bind(renderer); - - _gl.clear(_gl.COLOR_BUFFER_BIT | _gl.DEPTH_BUFFER_BIT); - - renderer.renderQueue(casters, camera); - - this._frameBuffer.unbind(renderer); - - // Filter for VSM - if (this.softShadow === ShadowMapPass.VSM) { - this._gaussianFilter(renderer, texture, texture.width); - } - - var matrix = new Matrix4(); - matrix.copy(camera.worldTransform) - .invert() - .multiplyLeft(camera.projectionMatrix); - - spotLightShadowMaps.push(texture); - spotLightMatrices.push(matrix._array); - }, - - _renderPointLightShadow: function(renderer, light, casters, pointLightRanges, pointLightShadowMaps) { - var texture = this._getTexture(light.__GUID__, light); - var _gl = renderer.gl; - pointLightShadowMaps.push(texture); - pointLightRanges.push(light.range * 5); - - this._bindDistanceMaterial(casters, light); - for (var i = 0; i < 6; i++) { - var target = targets[i]; - var camera = this._getPointLightCamera(light, target); - - this._frameBuffer.attach(renderer.gl, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i); - this._frameBuffer.bind(renderer); - - _gl.clear(_gl.COLOR_BUFFER_BIT | _gl.DEPTH_BUFFER_BIT); - - renderer.renderQueue(casters, camera); - - this._frameBuffer.unbind(renderer); - } - }, - - _gaussianFilter: function(renderer, texture, size) { - var parameter = { - width: size, - height: size, - type: glenum.FLOAT - }; - var _gl = renderer.gl; - var tmpTexture = this._texturePool.get(parameter); - - this._frameBuffer.attach(_gl, tmpTexture); - this._frameBuffer.bind(renderer); - this._gaussianPassH.setUniform('texture', texture); - this._gaussianPassH.setUniform('textureWidth', size); - this._gaussianPassH.render(renderer); - this._frameBuffer.unbind(renderer); - - this._frameBuffer.attach(_gl, texture); - this._frameBuffer.bind(renderer); - this._gaussianPassV.setUniform('texture', tmpTexture); - this._gaussianPassV.setUniform('textureHeight', size); - this._gaussianPassV.render(renderer); - this._frameBuffer.unbind(renderer); - - this._texturePool.put(tmpTexture); - }, - - _getTexture: function(key, light) { - var texture = this._textures[key]; - var resolution = light.shadowResolution || 512; - if (!texture) { - if (light instanceof PointLight) { - texture = new TextureCube(); - } else { - texture = new Texture2D(); - } - texture.width = resolution; - texture.height = resolution; - if (this.softShadow === ShadowMapPass.VSM) { - texture.type = glenum.FLOAT; - texture.anisotropic = 4; - } else { - texture.minFilter = glenum.LINEAR; - texture.magFilter = glenum.LINEAR; - texture.useMipmap = false; - } - this._textures[key] = texture; - } - - return texture; - }, - - _getPointLightCamera: function(light, target) { - if (!this._lightCameras.point) { - this._lightCameras.point = { - px: new PerspectiveCamera(), - nx: new PerspectiveCamera(), - py: new PerspectiveCamera(), - ny: new PerspectiveCamera(), - pz: new PerspectiveCamera(), - nz: new PerspectiveCamera() - }; - } - var camera = this._lightCameras.point[target]; - - camera.far = light.range; - camera.fov = 90; - camera.position.set(0, 0, 0); - switch (target) { - case 'px': - camera.lookAt(Vector3.POSITIVE_X, Vector3.NEGATIVE_Y); - break; - case 'nx': - camera.lookAt(Vector3.NEGATIVE_X, Vector3.NEGATIVE_Y); - break; - case 'py': - camera.lookAt(Vector3.POSITIVE_Y, Vector3.POSITIVE_Z); - break; - case 'ny': - camera.lookAt(Vector3.NEGATIVE_Y, Vector3.NEGATIVE_Z); - break; - case 'pz': - camera.lookAt(Vector3.POSITIVE_Z, Vector3.NEGATIVE_Y); - break; - case 'nz': - camera.lookAt(Vector3.NEGATIVE_Z, Vector3.NEGATIVE_Y); - break; - } - camera.position.copy(light.position); - camera.update(); - - return camera; - }, - - _getDirectionalLightCamera: (function() { - var lightViewMatrix = new Matrix4(); - var lightViewBBox = new BoundingBox(); - // Camera of directional light will be adjusted - // to contain the view frustum and scene bounding box as tightly as possible - return function(light, scene, sceneCamera) { - if (!this._lightCameras.directional) { - this._lightCameras.directional = new OrthoCamera(); - } - var camera = this._lightCameras.directional; - - // Move to the center of frustum(in world space) - camera.position - .copy(sceneCamera.frustum.boundingBox.min) - .add(sceneCamera.frustum.boundingBox.max) - .scale(0.5) - .transformMat4(sceneCamera.worldTransform); - camera.rotation.copy(light.rotation); - camera.scale.copy(light.scale); - camera.updateLocalTransform(); - camera.updateWorldTransform(); - - // Transform to light view space - lightViewMatrix - .copy(camera.worldTransform) - .invert() - .multiply(sceneCamera.worldTransform); - - sceneCamera.frustum.getTransformedBoundingBox(lightViewBBox, lightViewMatrix); - var min = lightViewBBox.min._array; - var max = lightViewBBox.max._array; - - // Move camera to adjust the near to 0 - // TODO: some scene object cast shadow in view will also be culled - // add a bias? - camera.position.scaleAndAdd(camera.worldTransform.z, max[2] + this.lightFrustumBias); - camera.near = 0; - camera.far = -min[2] + max[2] + this.lightFrustumBias; - camera.left = min[0] - this.lightFrustumBias; - camera.right = max[0] + this.lightFrustumBias; - camera.top = max[1] + this.lightFrustumBias; - camera.bottom = min[1] - this.lightFrustumBias; - camera.update(true); - - return camera; - }; - })(), - - _getSpotLightCamera: function(light) { - if (!this._lightCameras.spot) { - this._lightCameras.spot = new PerspectiveCamera(); - } - var camera = this._lightCameras.spot; - // Update properties - camera.fov = light.penumbraAngle * 2; - camera.far = light.range; - camera.worldTransform.copy(light.worldTransform); - camera.updateProjectionMatrix(); - mat4.invert(camera.viewMatrix._array, camera.worldTransform._array); - - return camera; - }, - - /** - * @param {qtek.Renderer} renderer - * @memberOf qtek.prePass.ShadowMap.prototype - */ - dispose: function(renderer) { - var _gl = renderer.gl; - for (var guid in this._depthMaterials) { - var mat = this._depthMaterials[guid]; - mat.dispose(_gl); - } - for (var guid in this._distanceMaterials) { - var mat = this._distanceMaterials[guid]; - mat.dispose(_gl); - } - - if (this._frameBuffer) { - this._frameBuffer.dispose(_gl); - } - - for (var name in this._textures) { - this._textures[name].dispose(_gl); - } - - this._texturePool.clear(renderer.gl); - - this._depthMaterials = {}; - this._distanceMaterials = {}; - this._textures = {}; - this._lightCameras = {}; - this._shadowMapNumber = { - 'POINT_LIGHT': 0, - 'DIRECTIONAL_LIGHT': 0, - 'SPOT_LIGHT': 0 - }; - this._meshMaterials = {}; - - for (var i = 0; i < this._receivers.length; i++) { - var mesh = this._receivers[i]; - // Mesh may be disposed - if (mesh.material && mesh.material.shader) { - var material = mesh.material; - var shader = material.shader; - shader.unDefine('fragment', 'POINT_LIGHT_SHADOW_NUMBER'); - shader.unDefine('fragment', 'DIRECTIONAL_LIGHT_SHADOW_NUMBER'); - shader.unDefine('fragment', 'AMBIENT_LIGHT_SHADOW_NUMBER'); - material.set('shadowEnabled', 0); - } - } - - this._opaqueCasters = []; - this._receivers = []; - this._lightsCastShadow = []; - } - }); - - /** - * @name qtek.prePass.ShadowMap.VSM - * @type {number} - */ - ShadowMapPass.VSM = 1; - - /** - * @name qtek.prePass.ShadowMap.PCF - * @type {number} - */ - ShadowMapPass.PCF = 2; - - return ShadowMapPass; -}); -define('qtek/shader/source/basic.essl',[],function () { return '@export buildin.basic.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\n\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 position : POSITION;\n\nattribute vec3 barycentric;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Barycentric;\n\nvoid main()\n{\n vec3 skinnedPosition = position;\n\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n \n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_Barycentric = barycentric;\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n}\n\n@end\n\n\n\n\n@export buildin.basic.fragment\n\nvarying vec2 v_Texcoord;\nuniform sampler2D diffuseMap;\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform vec3 emission : [0.0, 0.0, 0.0];\nuniform float alpha : 1.0;\n\n// Uniforms for wireframe\nuniform float lineWidth : 0.0;\nuniform vec3 lineColor : [0.0, 0.0, 0.0];\nvarying vec3 v_Barycentric;\n\n#extension GL_OES_standard_derivatives : enable\n@import buildin.util.edge_factor\n\nvoid main()\n{\n\n #ifdef RENDER_TEXCOORD\n gl_FragColor = vec4(v_Texcoord, 1.0, 1.0);\n return;\n #endif\n\n gl_FragColor = vec4(color, alpha);\n \n #ifdef DIFFUSEMAP_ENABLED\n vec4 tex = texture2D( diffuseMap, v_Texcoord );\n\n #ifdef SRGB_DECODE\n tex.rgb = pow(tex.rgb, vec3(2.2));\n #endif\n \n #if defined(DIFFUSEMAP_ALPHA_ALPHA)\n gl_FragColor.a = tex.a;\n #endif\n\n gl_FragColor.rgb *= tex.rgb;\n #endif\n\n gl_FragColor.rgb += emission;\n if( lineWidth > 0.01)\n {\n gl_FragColor.rgb = gl_FragColor.rgb * mix(lineColor, vec3(1.0), edgeFactor(lineWidth));\n }\n}\n\n@end';}); - -define('qtek/shader/source/lambert.essl',[],function () { return '/**\n * http://en.wikipedia.org/wiki/Lambertian_reflectance\n */\n\n@export buildin.lambert.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\n\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 normal : NORMAL;\n\nattribute vec3 barycentric;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n // Upper 3x3 of skinMatrix is orthogonal\n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4( skinnedPosition, 1.0 );\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_Normal = normalize( ( worldInverseTranspose * vec4(skinnedNormal, 0.0) ).xyz );\n v_WorldPosition = ( world * vec4( skinnedPosition, 1.0) ).xyz;\n\n v_Barycentric = barycentric;\n}\n\n@end\n\n\n@export buildin.lambert.fragment\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\n\nuniform sampler2D diffuseMap;\nuniform sampler2D alphaMap;\n\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform vec3 emission : [0.0, 0.0, 0.0];\nuniform float alpha : 1.0;\n\n// Uniforms for wireframe\nuniform float lineWidth : 0.0;\nuniform vec3 lineColor : [0.0, 0.0, 0.0];\nvarying vec3 v_Barycentric;\n\n#ifdef AMBIENT_LIGHT_NUMBER\n@import buildin.header.ambient_light\n#endif\n#ifdef POINT_LIGHT_NUMBER\n@import buildin.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_NUMBER\n@import buildin.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_NUMBER\n@import buildin.header.spot_light\n#endif\n\n#extension GL_OES_standard_derivatives : enable\n// Import util functions and uniforms needed\n@import buildin.util.calculate_attenuation\n\n@import buildin.util.edge_factor\n\n@import buildin.plugin.compute_shadow_map\n\nvoid main()\n{\n #ifdef RENDER_NORMAL\n gl_FragColor = vec4(v_Normal, 1.0);\n return;\n #endif\n #ifdef RENDER_TEXCOORD\n gl_FragColor = vec4(v_Texcoord, 1.0, 1.0);\n return;\n #endif\n\n gl_FragColor = vec4(color, alpha);\n\n #ifdef DIFFUSEMAP_ENABLED\n vec4 tex = texture2D( diffuseMap, v_Texcoord );\n #ifdef SRGB_DECODE\n tex.rgb = pow(tex.rgb, vec3(2.2));\n #endif\n gl_FragColor.rgb *= tex.rgb;\n #ifdef DIFFUSEMAP_ALPHA_ALPHA\n gl_FragColor.a *= tex.a;\n #endif\n #endif\n\n vec3 diffuseColor = vec3(0.0, 0.0, 0.0);\n \n #ifdef AMBIENT_LIGHT_NUMBER\n for(int i = 0; i < AMBIENT_LIGHT_NUMBER; i++)\n {\n diffuseColor += ambientLightColor[i];\n }\n #endif\n // Compute point light color\n #ifdef POINT_LIGHT_NUMBER\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[POINT_LIGHT_NUMBER];\n if( shadowEnabled )\n {\n computeShadowOfPointLights( v_WorldPosition, shadowContribs );\n }\n #endif\n for(int i = 0; i < POINT_LIGHT_NUMBER; i++)\n {\n\n vec3 lightPosition = pointLightPosition[i];\n vec3 lightColor = pointLightColor[i];\n float range = pointLightRange[i];\n\n vec3 lightDirection = lightPosition - v_WorldPosition;\n\n // Calculate point light attenuation\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range);\n\n // Normalize vectors\n lightDirection /= dist;\n\n float ndl = dot( v_Normal, lightDirection );\n\n float shadowContrib = 1.0;\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n diffuseColor += lightColor * clamp(ndl, 0.0, 1.0) * attenuation * shadowContrib;\n }\n #endif\n #ifdef DIRECTIONAL_LIGHT_NUMBER\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[DIRECTIONAL_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfDirectionalLights( v_WorldPosition, shadowContribs );\n }\n #endif\n for(int i = 0; i < DIRECTIONAL_LIGHT_NUMBER; i++)\n {\n vec3 lightDirection = -directionalLightDirection[i];\n vec3 lightColor = directionalLightColor[i];\n \n float ndl = dot( v_Normal, normalize( lightDirection ) );\n\n float shadowContrib = 1.0;\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n diffuseColor += lightColor * clamp(ndl, 0.0, 1.0) * shadowContrib;\n }\n #endif\n \n #ifdef SPOT_LIGHT_NUMBER\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[SPOT_LIGHT_NUMBER];\n if( shadowEnabled )\n {\n computeShadowOfSpotLights( v_WorldPosition, shadowContribs );\n }\n #endif\n for(int i = 0; i < SPOT_LIGHT_NUMBER; i++)\n {\n vec3 lightPosition = -spotLightPosition[i];\n vec3 spotLightDirection = -normalize( spotLightDirection[i] );\n vec3 lightColor = spotLightColor[i];\n float range = spotLightRange[i];\n float a = spotLightUmbraAngleCosine[i];\n float b = spotLightPenumbraAngleCosine[i];\n float falloffFactor = spotLightFalloffFactor[i];\n\n vec3 lightDirection = lightPosition - v_WorldPosition;\n // Calculate attenuation\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range); \n\n // Normalize light direction\n lightDirection /= dist;\n // Calculate spot light fall off\n float c = dot(spotLightDirection, lightDirection);\n\n float falloff;\n falloff = clamp((c - a) /( b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n\n float ndl = dot(v_Normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n diffuseColor += lightColor * ndl * attenuation * (1.0-falloff) * shadowContrib;\n\n }\n #endif\n\n gl_FragColor.rgb *= diffuseColor;\n gl_FragColor.rgb += emission;\n if(lineWidth > 0.01)\n {\n gl_FragColor.rgb = gl_FragColor.rgb * mix(lineColor, vec3(1.0), edgeFactor(lineWidth));\n }\n}\n\n@end';}); - -define('qtek/shader/source/phong.essl',[],function () { return '\n// http://en.wikipedia.org/wiki/Blinn%E2%80%93Phong_shading_model\n\n@export buildin.phong.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\n\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 normal : NORMAL;\nattribute vec4 tangent : TANGENT;\n\n#ifdef VERTEX_COLOR\nattribute vec4 color : COLOR;\n#endif\n\nattribute vec3 barycentric;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\n\n#ifdef NORMALMAP_ENABLED\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\n#ifdef VERTEX_COLOR\nvarying vec4 v_Color;\n#endif\n\nvoid main()\n{\n \n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n // Upper 3x3 of skinMatrix is orthogonal\n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n skinnedTangent = (skinMatrixWS * vec4(tangent.xyz, 0.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n v_Barycentric = barycentric;\n\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\n \n #ifdef NORMALMAP_ENABLED\n v_Tangent = normalize((worldInverseTranspose * vec4(skinnedTangent, 0.0)).xyz);\n v_Bitangent = normalize(cross(v_Normal, v_Tangent) * tangent.w);\n #endif\n\n #ifdef VERTEX_COLOR\n v_Color = color;\n #endif\n}\n\n@end\n\n\n@export buildin.phong.fragment\n\nuniform mat4 viewInverse : VIEWINVERSE;\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\n\n#ifdef NORMALMAP_ENABLED\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\nuniform sampler2D diffuseMap;\nuniform sampler2D normalMap;\nuniform sampler2D specularMap;\nuniform samplerCube environmentMap;\n\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\n\nuniform float shininess : 30;\n\nuniform vec3 specularColor : [1.0, 1.0, 1.0];\nuniform vec3 emission : [0.0, 0.0, 0.0];\n\nuniform float reflectivity : 0.5;\n\n// Uniforms for wireframe\nuniform float lineWidth : 0.0;\nuniform vec3 lineColor : [0.0, 0.0, 0.0];\nvarying vec3 v_Barycentric;\n\n#ifdef AMBIENT_LIGHT_NUMBER\n@import buildin.header.ambient_light\n#endif\n#ifdef POINT_LIGHT_NUMBER\n@import buildin.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_NUMBER\n@import buildin.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_NUMBER\n@import buildin.header.spot_light\n#endif\n\n#extension GL_OES_standard_derivatives : enable\n// Import util functions and uniforms needed\n@import buildin.util.calculate_attenuation\n\n@import buildin.util.edge_factor\n\n@import buildin.plugin.compute_shadow_map\n\nvoid main()\n{\n #ifdef RENDER_TEXCOORD\n gl_FragColor = vec4(v_Texcoord, 1.0, 1.0);\n return;\n #endif\n\n vec4 finalColor = vec4(color, alpha);\n\n vec3 eyePos = viewInverse[3].xyz;\n vec3 viewDirection = normalize(eyePos - v_WorldPosition);\n\n #ifdef DIFFUSEMAP_ENABLED\n vec4 tex = texture2D(diffuseMap, v_Texcoord);\n #ifdef SRGB_DECODE\n tex.rgb = pow(tex.rgb, vec3(2.2));\n #endif\n finalColor.rgb *= tex.rgb;\n #ifdef DIFFUSEMAP_ALPHA_ALPHA\n finalColor.a *= tex.a;\n #endif\n #endif\n\n vec3 spec = specularColor;\n #ifdef SPECULARMAP_ENABLED\n spec *= texture2D(specularMap, v_Texcoord).rgb;\n #endif\n\n vec3 normal = v_Normal;\n #ifdef NORMALMAP_ENABLED\n normal = texture2D(normalMap, v_Texcoord).xyz * 2.0 - 1.0;\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\n normal = normalize(tbn * normal);\n #endif\n\n #ifdef RENDER_NORMAL\n gl_FragColor = vec4(normal, 1.0);\n return;\n #endif\n\n // Diffuse part of all lights\n vec3 diffuseTerm = vec3(0.0, 0.0, 0.0);\n // Specular part of all lights\n vec3 specularTerm = vec3(0.0, 0.0, 0.0);\n \n #ifdef AMBIENT_LIGHT_NUMBER\n for(int i = 0; i < AMBIENT_LIGHT_NUMBER; i++)\n {\n diffuseTerm += ambientLightColor[i];\n }\n #endif\n #ifdef POINT_LIGHT_NUMBER\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[POINT_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfPointLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < POINT_LIGHT_NUMBER; i++)\n {\n vec3 lightPosition = pointLightPosition[i];\n vec3 lightColor = pointLightColor[i];\n float range = pointLightRange[i];\n\n vec3 lightDirection = lightPosition - v_WorldPosition;\n\n // Calculate point light attenuation\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range); \n\n // Normalize vectors\n lightDirection /= dist;\n vec3 halfVector = normalize(lightDirection + viewDirection);\n\n float ndh = dot(normal, halfVector);\n ndh = clamp(ndh, 0.0, 1.0);\n\n float ndl = dot(normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lightColor * ndl * attenuation * shadowContrib;\n\n diffuseTerm += li;\n if (shininess > 0.0)\n {\n specularTerm += li * pow(ndh, shininess);\n }\n\n }\n #endif\n\n #ifdef DIRECTIONAL_LIGHT_NUMBER\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[DIRECTIONAL_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfDirectionalLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < DIRECTIONAL_LIGHT_NUMBER; i++)\n {\n\n vec3 lightDirection = -normalize(directionalLightDirection[i]);\n vec3 lightColor = directionalLightColor[i];\n\n vec3 halfVector = normalize(lightDirection + viewDirection);\n\n float ndh = dot(normal, halfVector);\n ndh = clamp(ndh, 0.0, 1.0);\n\n float ndl = dot(normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lightColor * ndl * shadowContrib;\n\n diffuseTerm += li;\n if (shininess > 0.0)\n {\n specularTerm += li * pow(ndh, shininess);\n }\n }\n #endif\n\n #ifdef SPOT_LIGHT_NUMBER\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[SPOT_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfSpotLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < SPOT_LIGHT_NUMBER; i++)\n {\n vec3 lightPosition = spotLightPosition[i];\n vec3 spotLightDirection = -normalize(spotLightDirection[i]);\n vec3 lightColor = spotLightColor[i];\n float range = spotLightRange[i];\n float a = spotLightUmbraAngleCosine[i];\n float b = spotLightPenumbraAngleCosine[i];\n float falloffFactor = spotLightFalloffFactor[i];\n\n vec3 lightDirection = lightPosition - v_WorldPosition;\n // Calculate attenuation\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range); \n\n // Normalize light direction\n lightDirection /= dist;\n // Calculate spot light fall off\n float c = dot(spotLightDirection, lightDirection);\n\n float falloff;\n // Fomular from real-time-rendering\n falloff = clamp((c - a) /( b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n\n vec3 halfVector = normalize(lightDirection + viewDirection);\n\n float ndh = dot(normal, halfVector);\n ndh = clamp(ndh, 0.0, 1.0);\n\n float ndl = dot(normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n if (shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lightColor * ndl * attenuation * (1.0-falloff) * shadowContrib;\n\n diffuseTerm += li;\n if (shininess > 0.0)\n {\n specularTerm += li * pow(ndh, shininess);\n }\n }\n #endif\n\n finalColor.rgb *= diffuseTerm;\n finalColor.rgb += specularTerm * spec;\n finalColor.rgb += emission;\n\n #ifdef ENVIRONMENTMAP_ENABLED\n vec3 envTex = textureCube(environmentMap, reflect(-viewDirection, normal)).xyz;\n finalColor.rgb = finalColor.rgb + envTex * reflectivity;\n #endif\n\n if(lineWidth > 0.01)\n {\n finalColor.rgb = finalColor.rgb * mix(lineColor, vec3(1.0), edgeFactor(lineWidth));\n }\n\n #ifdef GAMMA_ENCODE\n finalColor.rgb = pow(finalColor.rgb, vec3(1 / 2.2));\n #endif\n\n gl_FragColor = finalColor;\n}\n\n@end';}); - -define('qtek/shader/source/standard.essl',[],function () { return '\n// http://blog.selfshadow.com/publications/s2013-shading-course/\n\n@export buildin.standard.vertex\n\n@import buildin.phong.vertex\n\n@end\n\n\n@export buildin.standard.fragment\n\n#define PI 3.14159265358979\n\nuniform mat4 viewInverse : VIEWINVERSE;\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\n\n#ifdef NORMALMAP_ENABLED\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\nuniform sampler2D diffuseMap;\nuniform sampler2D normalMap;\nuniform sampler2D specularMap;\nuniform samplerCube environmentMap;\n\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\n\nuniform float glossiness : 0.5;\n\nuniform vec3 specularColor : [0.1, 0.1, 0.1];\nuniform vec3 emission : [0.0, 0.0, 0.0];\n\n// Uniforms for wireframe\nuniform float lineWidth : 0.0;\nuniform vec3 lineColor : [0.0, 0.0, 0.0];\nvarying vec3 v_Barycentric;\n\n#ifdef AMBIENT_LIGHT_NUMBER\n@import buildin.header.ambient_light\n#endif\n#ifdef POINT_LIGHT_NUMBER\n@import buildin.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_NUMBER\n@import buildin.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_NUMBER\n@import buildin.header.spot_light\n#endif\n\n#extension GL_OES_standard_derivatives : enable\n\n// Import util functions and uniforms needed\n@import buildin.util.calculate_attenuation\n\n@import buildin.util.edge_factor\n\n@import buildin.plugin.compute_shadow_map\n\n\nfloat G_Smith(float glossiness, float ndv, float ndl)\n{\n // float k = (roughness+1.0) * (roughness+1.0) * 0.125;\n float roughness = 1.0 - glossiness;\n float k = roughness * roughness / 2.0;\n float G1V = ndv / (ndv * (1.0 - k) + k);\n float G1L = ndl / (ndl * (1.0 - k) + k);\n return G1L * G1V;\n}\n// Fresnel\nvec3 F_Schlick(float ndv, vec3 spec) {\n return spec + (1.0 - spec) * pow(1.0 - ndv, 5.0);\n}\n\nfloat D_Phong(float g, float ndh) {\n // from black ops 2\n float a = pow(8192.0, g);\n return (a + 2.0) / 8.0 * pow(ndh, a);\n}\n\nfloat D_GGX(float g, float ndh) {\n float r = 1.0 - g;\n float a = r * r;\n float tmp = ndh * ndh * (a - 1.0) + 1.0;\n return a / (PI * tmp * tmp);\n}\n\nvoid main()\n{\n #ifdef RENDER_TEXCOORD\n gl_FragColor = vec4(v_Texcoord, 1.0, 1.0);\n return;\n #endif\n\n vec4 finalColor = vec4(color, alpha);\n\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = normalize(eyePos - v_WorldPosition);\n float g = glossiness;\n\n #ifdef DIFFUSEMAP_ENABLED\n vec4 tex = texture2D(diffuseMap, v_Texcoord);\n #ifdef SRGB_DECODE\n tex.rgb = pow(tex.rgb, vec3(2.2));\n #endif\n finalColor.rgb *= tex.rgb;\n #ifdef DIFFUSEMAP_ALPHA_ALPHA\n finalColor.a *= tex.a;\n #endif\n #ifdef DIFFUSEMAP_ALPHA_GLOSS\n g *= tex.a;\n #endif\n #endif\n\n vec3 spec = specularColor;\n #ifdef SPECULARMAP_ENABLED\n spec *= texture2D(specularMap, v_Texcoord).rgb;\n #endif\n\n vec3 N = v_Normal;\n #ifdef NORMALMAP_ENABLED\n N = texture2D(normalMap, v_Texcoord).xyz * 2.0 - 1.0;\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\n N = normalize(tbn * N);\n #endif\n\n #ifdef RENDER_NORMAL\n gl_FragColor = vec4(N, 1.0);\n return;\n #endif\n\n #ifdef RENDER_GLOSSINESS\n gl_FragColor = vec4(vec3(g), 1.0);\n return;\n #endif\n\n // Diffuse part of all lights\n vec3 diffuseTerm = vec3(0.0, 0.0, 0.0);\n // Specular part of all lights\n vec3 specularTerm = vec3(0.0, 0.0, 0.0);\n \n vec3 fresnelTerm = F_Schlick(clamp(dot(N, V), 0.0, 1.0), spec);\n \n #ifdef AMBIENT_LIGHT_NUMBER\n for(int i = 0; i < AMBIENT_LIGHT_NUMBER; i++)\n {\n // Hemisphere ambient lighting from cryengine\n diffuseTerm += ambientLightColor[i] * (clamp(N.y * 0.7, 0.0, 1.0) + 0.3);\n // diffuseTerm += ambientLightColor[i];\n }\n #endif\n #ifdef POINT_LIGHT_NUMBER\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[POINT_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfPointLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < POINT_LIGHT_NUMBER; i++)\n {\n\n vec3 lightPosition = pointLightPosition[i];\n vec3 lc = pointLightColor[i];\n float range = pointLightRange[i];\n\n vec3 L = lightPosition - v_WorldPosition;\n\n // Calculate point light attenuation\n float dist = length(L);\n float attenuation = lightAttenuation(dist, range);\n L /= dist;\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lc * ndl * attenuation * shadowContrib;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }\n #endif\n\n #ifdef DIRECTIONAL_LIGHT_NUMBER\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[DIRECTIONAL_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfDirectionalLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < DIRECTIONAL_LIGHT_NUMBER; i++)\n {\n\n vec3 L = -normalize(directionalLightDirection[i]);\n vec3 lc = directionalLightColor[i];\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lc * ndl * shadowContrib;\n\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }\n #endif\n\n #ifdef SPOT_LIGHT_NUMBER\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[SPOT_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfSpotLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < SPOT_LIGHT_NUMBER; i++)\n {\n vec3 lightPosition = spotLightPosition[i];\n vec3 spotLightDirection = -normalize(spotLightDirection[i]);\n vec3 lc = spotLightColor[i];\n float range = spotLightRange[i];\n float a = spotLightUmbraAngleCosine[i];\n float b = spotLightPenumbraAngleCosine[i];\n float falloffFactor = spotLightFalloffFactor[i];\n\n vec3 L = lightPosition - v_WorldPosition;\n // Calculate attenuation\n float dist = length(L);\n float attenuation = lightAttenuation(dist, range); \n\n // Normalize light direction\n L /= dist;\n // Calculate spot light fall off\n float c = dot(spotLightDirection, L);\n\n float falloff;\n // Fomular from real-time-rendering\n falloff = clamp((c - a) /( b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n if (shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lc * attenuation * (1.0-falloff) * shadowContrib * ndl;\n\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }\n #endif\n\n finalColor.rgb *= diffuseTerm;\n finalColor.rgb += specularTerm;\n finalColor.rgb += emission;\n\n #ifdef ENVIRONMENTMAP_ENABLED\n vec3 envTex = textureCube(environmentMap, reflect(-V, N)).xyz;;\n finalColor.rgb = finalColor.rgb + envTex * g * fresnelTerm;\n #endif\n\n if(lineWidth > 0.)\n {\n finalColor.rgb = finalColor.rgb * mix(lineColor, vec3(1.0), edgeFactor(lineWidth));\n }\n\n #ifdef GAMMA_ENCODE\n finalColor.rgb = pow(finalColor.rgb, vec3(1 / 2.2));\n #endif\n gl_FragColor = finalColor;\n}\n\n@end\n\n\n@export buildin.physical.vertex\n\n@import buildin.standard.vertex\n\n@end\n\n@export buildin.physical.fragment\n\n@import buildin.standard.fragment\n\n@end';}); - -define('qtek/shader/source/wireframe.essl',[],function () { return '@export buildin.wireframe.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world : WORLD;\n\nattribute vec3 position : POSITION;\nattribute vec3 barycentric;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec3 v_Barycentric;\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n #ifdef SKINNING\n\n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0 );\n\n v_Barycentric = barycentric;\n}\n\n@end\n\n\n@export buildin.wireframe.fragment\n\nuniform vec3 color : [0.0, 0.0, 0.0];\n\nuniform float alpha : 1.0;\nuniform float lineWidth : 1.0;\n\nvarying vec3 v_Barycentric;\n\n#extension GL_OES_standard_derivatives : enable\n\n@import buildin.util.edge_factor\n\nvoid main()\n{\n\n gl_FragColor.rgb = color;\n gl_FragColor.a = ( 1.0-edgeFactor(lineWidth) ) * alpha;\n}\n\n@end';}); - -define('qtek/shader/source/skybox.essl',[],function () { return '@export buildin.skybox.vertex\n\nuniform mat4 world : WORLD;\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\nvarying vec3 v_WorldPosition;\n\nvoid main()\n{\n v_WorldPosition = (world * vec4(position, 1.0)).xyz;\n gl_Position = worldViewProjection * vec4(position, 1.0);\n}\n\n@end\n\n@export buildin.skybox.fragment\n\nuniform mat4 viewInverse : VIEWINVERSE;\nuniform samplerCube environmentMap;\n\nvarying vec3 v_WorldPosition;\n\nvoid main()\n{\n vec3 eyePos = viewInverse[3].xyz;\n vec3 viewDirection = normalize(v_WorldPosition - eyePos);\n\n vec3 tex = textureCube(environmentMap, viewDirection).xyz;\n\n #ifdef SRGB_DECODE\n tex.rgb = pow(tex.rgb, vec3(2.2));\n #endif\n \n gl_FragColor = vec4(tex, 1.0);\n}\n@end';}); - -define('qtek/shader/source/shadowmap.essl',[],function () { return '\n@export buildin.sm.depth.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\n#ifdef SHADOW_TRANSPARENT \nattribute vec2 texcoord : TEXCOORD_0;\n#endif\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec4 v_ViewPosition;\n\n#ifdef SHADOW_TRANSPARENT\nvarying vec2 v_Texcoord;\n#endif\n\nvoid main(){\n \n vec3 skinnedPosition = position;\n \n #ifdef SKINNING\n\n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n\n v_ViewPosition = worldViewProjection * vec4(skinnedPosition, 1.0);\n gl_Position = v_ViewPosition;\n\n #ifdef SHADOW_TRANSPARENT\n v_Texcoord = texcoord;\n #endif\n}\n@end\n\n@export buildin.sm.depth.fragment\n\nvarying vec4 v_ViewPosition;\n\n#ifdef SHADOW_TRANSPARENT\nvarying vec2 v_Texcoord;\n#endif\n\nuniform float bias : 0.001;\nuniform float slopeScale : 1.0;\n\n#ifdef SHADOW_TRANSPARENT\nuniform sampler2D transparentMap;\n#endif\n\n#extension GL_OES_standard_derivatives : enable\n\n@import buildin.util.encode_float\n\nvoid main(){\n // Whats the difference between gl_FragCoord.z and this v_ViewPosition\n // gl_FragCoord consider the polygon offset ?\n float depth = v_ViewPosition.z / v_ViewPosition.w;\n // float depth = gl_FragCoord.z / gl_FragCoord.w;\n\n #ifdef USE_VSM\n depth = depth * 0.5 + 0.5;\n float moment1 = depth;\n float moment2 = depth * depth;\n\n // Adjusting moments using partial derivative\n float dx = dFdx(depth);\n float dy = dFdy(depth);\n moment2 += 0.25*(dx*dx+dy*dy);\n\n gl_FragColor = vec4(moment1, moment2, 0.0, 1.0);\n #else\n // Add slope scaled bias using partial derivative\n float dx = dFdx(depth);\n float dy = dFdy(depth);\n depth += sqrt(dx*dx + dy*dy) * slopeScale + bias;\n\n #ifdef SHADOW_TRANSPARENT\n if (texture2D(transparentMap, v_Texcoord).a <= 0.1) {\n // Hi-Z\n gl_FragColor = encodeFloat(0.9999);\n return;\n }\n #endif\n\n gl_FragColor = encodeFloat(depth * 0.5 + 0.5);\n #endif\n}\n@end\n\n@export buildin.sm.debug_depth\n\nuniform sampler2D depthMap;\nvarying vec2 v_Texcoord;\n\n@import buildin.util.decode_float\n\nvoid main() {\n vec4 tex = texture2D(depthMap, v_Texcoord);\n #ifdef USE_VSM\n gl_FragColor = vec4(tex.rgb, 1.0);\n #else\n float depth = decodeFloat(tex);\n gl_FragColor = vec4(depth, depth, depth, 1.0);\n #endif\n}\n\n@end\n\n\n@export buildin.sm.distance.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world : WORLD;\n\nattribute vec3 position : POSITION;\n\n#ifdef SKINNING\nattribute vec3 boneWeight;\nattribute vec4 boneIndex;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec3 v_WorldPosition;\n\nvoid main (){\n\n vec3 skinnedPosition = position;\n #ifdef SKINNING\n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition , 1.0);\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n}\n\n@end\n\n@export buildin.sm.distance.fragment\n\nuniform vec3 lightPosition;\nuniform float range : 100;\n\nvarying vec3 v_WorldPosition;\n\n@import buildin.util.encode_float\n\nvoid main(){\n float dist = distance(lightPosition, v_WorldPosition);\n #ifdef USE_VSM\n gl_FragColor = vec4(dist, dist * dist, 0.0, 0.0);\n #else\n dist = dist / range;\n gl_FragColor = encodeFloat(dist);\n #endif\n}\n@end\n\n@export buildin.plugin.compute_shadow_map\n\n#if defined(SPOT_LIGHT_SHADOWMAP_NUMBER) || defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER) || defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n\n#ifdef SPOT_LIGHT_SHADOWMAP_NUMBER\nuniform sampler2D spotLightShadowMaps[SPOT_LIGHT_SHADOWMAP_NUMBER];\nuniform mat4 spotLightMatrices[SPOT_LIGHT_SHADOWMAP_NUMBER];\n#endif\n\n#ifdef DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER\n#if defined(SHADOW_CASCADE)\nuniform sampler2D directionalLightShadowMaps[SHADOW_CASCADE];\nuniform mat4 directionalLightMatrices[SHADOW_CASCADE];\nuniform float shadowCascadeClipsNear[SHADOW_CASCADE];\nuniform float shadowCascadeClipsFar[SHADOW_CASCADE];\n#else\nuniform sampler2D directionalLightShadowMaps[DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER];\nuniform mat4 directionalLightMatrices[DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER];\n#endif\n#endif\n\n#ifdef POINT_LIGHT_SHADOWMAP_NUMBER\nuniform samplerCube pointLightShadowMaps[POINT_LIGHT_SHADOWMAP_NUMBER];\nuniform float pointLightRanges[POINT_LIGHT_SHADOWMAP_NUMBER];\n#endif\n\nuniform bool shadowEnabled : true;\n\n@import buildin.util.decode_float\n\n#if defined(DIRECTIONAL_LIGHT_NUMBER) || defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n\nfloat tapShadowMap(sampler2D map, vec2 uv, float z){\n vec4 tex = texture2D(map, uv);\n return decodeFloat(tex) * 2.0 - 1.0 < z ? 0.0 : 1.0;\n}\n\nfloat pcf(sampler2D map, vec2 uv, float z){\n\n float shadowContrib = tapShadowMap(map, uv, z);\n float offset = 1.0 / 2048.0;\n shadowContrib += tapShadowMap(map, uv+vec2(offset, 0.0), z);\n shadowContrib += tapShadowMap(map, uv+vec2(offset, offset), z);\n shadowContrib += tapShadowMap(map, uv+vec2(-offset, offset), z);\n shadowContrib += tapShadowMap(map, uv+vec2(0.0, offset), z);\n shadowContrib += tapShadowMap(map, uv+vec2(-offset, 0.0), z);\n shadowContrib += tapShadowMap(map, uv+vec2(-offset, -offset), z);\n shadowContrib += tapShadowMap(map, uv+vec2(offset, -offset), z);\n shadowContrib += tapShadowMap(map, uv+vec2(0.0, -offset), z);\n\n return shadowContrib / 9.0;\n}\nfloat chebyshevUpperBound(vec2 moments, float z){\n float p = 0.0;\n z = z * 0.5 + 0.5;\n if (z <= moments.x) {\n p = 1.0;\n }\n float variance = moments.y - moments.x * moments.x;\n // http://fabiensanglard.net/shadowmappingVSM/\n variance = max(variance, 0.0000001);\n // Compute probabilistic upper bound. \n float mD = moments.x - z;\n float pMax = variance / (variance + mD * mD);\n // Now reduce light-bleeding by removing the [0, x] tail and linearly rescaling (x, 1]\n // TODO : bleedBias parameter ?\n pMax = clamp((pMax-0.4)/(1.0-0.4), 0.0, 1.0);\n return max(p, pMax);\n}\nfloat computeShadowContrib(sampler2D map, mat4 lightVPM, vec3 position){\n \n vec4 posInLightSpace = lightVPM * vec4(v_WorldPosition, 1.0);\n posInLightSpace.xyz /= posInLightSpace.w;\n float z = posInLightSpace.z;\n // In frustum\n if(all(greaterThan(posInLightSpace.xyz, vec3(-0.99, -0.99, -1.0))) &&\n all(lessThan(posInLightSpace.xyz, vec3(0.99, 0.99, 1.0)))){\n // To texture uv\n vec2 uv = (posInLightSpace.xy+1.0) / 2.0;\n\n #ifdef USE_VSM\n vec2 moments = texture2D(map, uv).xy;\n return chebyshevUpperBound(moments, z);\n #else\n return pcf(map, uv, z);\n #endif\n }\n return 1.0;\n}\n\n#endif\n\n#ifdef POINT_LIGHT_SHADOWMAP_NUMBER\n\nfloat computeShadowOfCube(samplerCube map, vec3 direction, float range){\n vec4 shadowTex = textureCube(map, direction);\n float dist = length(direction);\n\n #ifdef USE_VSM\n vec2 moments = shadowTex.xy;\n float variance = moments.y - moments.x * moments.x;\n float mD = moments.x - dist;\n float p = variance / (variance + mD * mD);\n if(moments.x + 0.001 < dist){\n return clamp(p, 0.0, 1.0);\n }else{\n return 1.0;\n }\n #else\n if((decodeFloat(shadowTex) + 0.0002) * range < dist){\n return 0.0;\n }else{\n return 1.0;\n }\n #endif\n}\n#endif\n\n#if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n\nvoid computeShadowOfSpotLights(vec3 position, inout float shadowContribs[SPOT_LIGHT_NUMBER] ){\n for(int i = 0; i < SPOT_LIGHT_SHADOWMAP_NUMBER; i++){\n float shadowContrib = computeShadowContrib(spotLightShadowMaps[i], spotLightMatrices[i], position);\n shadowContribs[i] = shadowContrib;\n }\n // set default fallof of rest lights\n for(int i = SPOT_LIGHT_SHADOWMAP_NUMBER; i < SPOT_LIGHT_NUMBER; i++){\n shadowContribs[i] = 1.0;\n }\n}\n\n#endif\n\n\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n\n#ifdef SHADOW_CASCADE\n\nvoid computeShadowOfDirectionalLights(vec3 position, inout float shadowContribs[DIRECTIONAL_LIGHT_NUMBER]){\n // http://www.opengl.org/wiki/Compute_eye_space_from_window_space\n float depth = (2.0 * gl_FragCoord.z - gl_DepthRange.near - gl_DepthRange.far)\n / (gl_DepthRange.far - gl_DepthRange.near);\n\n // Pixels not in light box are lighted\n // TODO\n shadowContribs[0] = 1.0;\n\n for (int i = 0; i < SHADOW_CASCADE; i++) {\n if (\n depth >= shadowCascadeClipsNear[i] &&\n depth <= shadowCascadeClipsFar[i]\n ) {\n float shadowContrib = computeShadowContrib(directionalLightShadowMaps[i], directionalLightMatrices[i], position);\n // TODO Will get a sampler needs to be be uniform error in native gl\n shadowContribs[0] = shadowContrib;\n }\n }\n // set default fallof of rest lights\n for(int i = DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER; i < DIRECTIONAL_LIGHT_NUMBER; i++){\n shadowContribs[i] = 1.0;\n }\n}\n\n#else\n\nvoid computeShadowOfDirectionalLights(vec3 position, inout float shadowContribs[DIRECTIONAL_LIGHT_NUMBER]){\n for(int i = 0; i < DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER; i++){\n float shadowContrib = computeShadowContrib(directionalLightShadowMaps[i], directionalLightMatrices[i], position);\n shadowContribs[i] = shadowContrib;\n }\n // set default fallof of rest lights\n for(int i = DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER; i < DIRECTIONAL_LIGHT_NUMBER; i++){\n shadowContribs[i] = 1.0;\n }\n}\n#endif\n\n#endif\n\n\n#if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n\nvoid computeShadowOfPointLights(vec3 position, inout float shadowContribs[POINT_LIGHT_NUMBER] ){\n for(int i = 0; i < POINT_LIGHT_SHADOWMAP_NUMBER; i++){\n vec3 lightPosition = pointLightPosition[i];\n vec3 direction = position - lightPosition;\n shadowContribs[i] = computeShadowOfCube(pointLightShadowMaps[i], direction, pointLightRanges[i]);\n }\n for(int i = POINT_LIGHT_SHADOWMAP_NUMBER; i < POINT_LIGHT_NUMBER; i++){\n shadowContribs[i] = 1.0;\n }\n}\n\n#endif\n\n#endif\n\n@end';}); - -define('qtek/shader/source/compositor/coloradjust.essl',[],function () { return '@export buildin.compositor.coloradjust\n\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float brightness : 0.0;\nuniform float contrast : 1.0;\nuniform float exposure : 0.0;\nuniform float gamma : 1.0;\nuniform float saturation : 1.0;\n\n// Values from "Graphics Shaders: Theory and Practice" by Bailey and Cunningham\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord);\n\n // brightness\n vec3 color = clamp(tex.rgb + vec3(brightness), 0.0, 1.0);\n // contrast\n color = clamp( (color-vec3(0.5))*contrast+vec3(0.5), 0.0, 1.0);\n // exposure\n color = clamp( color * pow(2.0, exposure), 0.0, 1.0);\n // gamma\n color = clamp( pow(color, vec3(gamma)), 0.0, 1.0);\n // saturation\n float luminance = dot( color, w );\n color = mix(vec3(luminance), color, saturation);\n \n gl_FragColor = vec4(color, tex.a);\n}\n\n@end\n\n// Seperate shader for float texture\n@export buildin.compositor.brightness\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float brightness : 0.0;\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord);\n vec3 color = tex.rgb + vec3(brightness);\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export buildin.compositor.contrast\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float contrast : 1.0;\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord);\n vec3 color = (tex.rgb-vec3(0.5))*contrast+vec3(0.5);\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export buildin.compositor.exposure\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float exposure : 0.0;\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n vec3 color = tex.rgb * pow(2.0, exposure);\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export buildin.compositor.gamma\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float gamma : 1.0;\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n vec3 color = pow(tex.rgb, vec3(gamma));\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export buildin.compositor.saturation\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float saturation : 1.0;\n\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n vec3 color = tex.rgb;\n float luminance = dot(color, w);\n color = mix(vec3(luminance), color, saturation);\n gl_FragColor = vec4(color, tex.a);\n}\n@end';}); - -define('qtek/shader/source/compositor/blur.essl',[],function () { return '@export buildin.compositor.gaussian_blur_h\n\nuniform sampler2D texture; // the texture with the scene you want to blur\nvarying vec2 v_Texcoord;\n \nuniform float blurSize : 2.0; \nuniform float textureWidth : 512.0;\n\nvoid main(void)\n{\n vec4 sum = vec4(0.0);\n float blurOffset = blurSize / textureWidth;\n // blur in y (vertical)\n // take nine samples, with the distance blurSize between them\n sum += texture2D(texture, vec2(max(v_Texcoord.x - 4.0*blurOffset, 0.0), v_Texcoord.y)) * 0.05;\n sum += texture2D(texture, vec2(max(v_Texcoord.x - 3.0*blurOffset, 0.0), v_Texcoord.y)) * 0.09;\n sum += texture2D(texture, vec2(max(v_Texcoord.x - 2.0*blurOffset, 0.0), v_Texcoord.y)) * 0.12;\n sum += texture2D(texture, vec2(max(v_Texcoord.x - blurOffset, 0.0), v_Texcoord.y)) * 0.15;\n sum += texture2D(texture, vec2(v_Texcoord.x, v_Texcoord.y)) * 0.18;\n sum += texture2D(texture, vec2(min(v_Texcoord.x + blurOffset, 1.0), v_Texcoord.y)) * 0.15;\n sum += texture2D(texture, vec2(min(v_Texcoord.x + 2.0*blurOffset, 1.0), v_Texcoord.y)) * 0.12;\n sum += texture2D(texture, vec2(min(v_Texcoord.x + 3.0*blurOffset, 1.0), v_Texcoord.y)) * 0.09;\n sum += texture2D(texture, vec2(min(v_Texcoord.x + 4.0*blurOffset, 1.0), v_Texcoord.y)) * 0.05;\n \n gl_FragColor = sum;\n}\n\n@end\n\n@export buildin.compositor.gaussian_blur_v\n\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\n \nuniform float blurSize : 2.0;\nuniform float textureHeight : 512.0;\n \nvoid main(void)\n{\n vec4 sum = vec4(0.0);\n float blurOffset = blurSize / textureHeight;\n // blur in y (vertical)\n // take nine samples, with the distance blurSize between them\n sum += texture2D(texture, vec2(v_Texcoord.x, max(v_Texcoord.y - 4.0*blurOffset, 0.0))) * 0.05;\n sum += texture2D(texture, vec2(v_Texcoord.x, max(v_Texcoord.y - 3.0*blurOffset, 0.0))) * 0.09;\n sum += texture2D(texture, vec2(v_Texcoord.x, max(v_Texcoord.y - 2.0*blurOffset, 0.0))) * 0.12;\n sum += texture2D(texture, vec2(v_Texcoord.x, max(v_Texcoord.y - blurOffset, 0.0))) * 0.15;\n sum += texture2D(texture, vec2(v_Texcoord.x, v_Texcoord.y)) * 0.18;\n sum += texture2D(texture, vec2(v_Texcoord.x, min(v_Texcoord.y + blurOffset, 1.0))) * 0.15;\n sum += texture2D(texture, vec2(v_Texcoord.x, min(v_Texcoord.y + 2.0*blurOffset, 1.0))) * 0.12;\n sum += texture2D(texture, vec2(v_Texcoord.x, min(v_Texcoord.y + 3.0*blurOffset, 1.0))) * 0.09;\n sum += texture2D(texture, vec2(v_Texcoord.x, min(v_Texcoord.y + 4.0*blurOffset, 1.0))) * 0.05;\n \n gl_FragColor = sum;\n}\n\n@end\n\n@export buildin.compositor.box_blur\n\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 3.0;\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n\n vec4 tex = texture2D(texture, v_Texcoord);\n vec2 offset = blurSize / textureSize;\n\n tex += texture2D(texture, v_Texcoord + vec2(offset.x, 0.0) );\n tex += texture2D(texture, v_Texcoord + vec2(offset.x, offset.y) );\n tex += texture2D(texture, v_Texcoord + vec2(-offset.x, offset.y) );\n tex += texture2D(texture, v_Texcoord + vec2(0.0, offset.y) );\n tex += texture2D(texture, v_Texcoord + vec2(-offset.x, 0.0) );\n tex += texture2D(texture, v_Texcoord + vec2(-offset.x, -offset.y) );\n tex += texture2D(texture, v_Texcoord + vec2(offset.x, -offset.y) );\n tex += texture2D(texture, v_Texcoord + vec2(0.0, -offset.y) );\n\n tex /= 9.0;\n\n gl_FragColor = tex;\n}\n\n@end\n\n// http://www.slideshare.net/DICEStudio/five-rendering-ideas-from-battlefield-3-need-for-speed-the-run\n@export buildin.compositor.hexagonal_blur_mrt_1\n\n// MRT in chrome\n// https://www.khronos.org/registry/webgl/sdk/tests/conformance/extensions/webgl-draw-buffers.html\n#extension GL_EXT_draw_buffers : require\n\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 2.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n vec2 offset = blurSize / textureSize;\n\n vec4 color = vec4(0.0);\n // Top\n for(int i = 0; i < 10; i++){\n color += 1.0/10.0 * texture2D(texture, v_Texcoord + vec2(0.0, offset.y * float(i)) );\n }\n gl_FragData[0] = color;\n vec4 color2 = vec4(0.0);\n // Down left\n for(int i = 0; i < 10; i++){\n color2 += 1.0/10.0 * texture2D(texture, v_Texcoord - vec2(offset.x * float(i), offset.y * float(i)) );\n }\n gl_FragData[1] = (color + color2) / 2.0;\n}\n\n@end\n\n@export buildin.compositor.hexagonal_blur_mrt_2\n\nuniform sampler2D texture0;\nuniform sampler2D texture1;\n\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 2.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n vec2 offset = blurSize / textureSize;\n\n vec4 color1 = vec4(0.0);\n // Down left\n for(int i = 0; i < 10; i++){\n color1 += 1.0/10.0 * texture2D(texture0, v_Texcoord - vec2(offset.x * float(i), offset.y * float(i)) );\n }\n vec4 color2 = vec4(0.0);\n // Down right\n for(int i = 0; i < 10; i++){\n color2 += 1.0/10.0 * texture2D(texture1, v_Texcoord + vec2(offset.x * float(i), -offset.y * float(i)) );\n }\n\n gl_FragColor = (color1 + color2) / 2.0;\n}\n\n@end\n\n\n@export buildin.compositor.hexagonal_blur_1\n\n#define KERNEL_SIZE 10\n\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 1.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n vec2 offset = blurSize / textureSize;\n\n vec4 color = vec4(0.0);\n float fKernelSize = float(KERNEL_SIZE);\n // Top\n for(int i = 0; i < KERNEL_SIZE; i++){\n color += 1.0 / fKernelSize * texture2D(texture, v_Texcoord + vec2(0.0, offset.y * float(i)) );\n }\n gl_FragColor = color;\n}\n\n@end\n\n@export buildin.compositor.hexagonal_blur_2\n\n#define KERNEL_SIZE 10\n\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 1.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n vec2 offset = blurSize / textureSize;\n offset.y /= 2.0;\n\n vec4 color = vec4(0.0);\n float fKernelSize = float(KERNEL_SIZE);\n // Down left\n for(int i = 0; i < KERNEL_SIZE; i++){\n color += 1.0/fKernelSize * texture2D(texture, v_Texcoord - vec2(offset.x * float(i), offset.y * float(i)) );\n }\n gl_FragColor = color;\n}\n@end\n\n@export buildin.compositor.hexagonal_blur_3\n\n#define KERNEL_SIZE 10\n\nuniform sampler2D texture1;\nuniform sampler2D texture2;\n\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 1.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n vec2 offset = blurSize / textureSize;\n offset.y /= 2.0;\n\n vec4 color1 = vec4(0.0);\n float fKernelSize = float(KERNEL_SIZE);\n // Down left\n for(int i = 0; i < KERNEL_SIZE; i++){\n color1 += 1.0/fKernelSize * texture2D(texture1, v_Texcoord - vec2(offset.x * float(i), offset.y * float(i)) );\n }\n vec4 color2 = vec4(0.0);\n // Down right\n for(int i = 0; i < KERNEL_SIZE; i++){\n color2 += 1.0/fKernelSize * texture2D(texture1, v_Texcoord + vec2(offset.x * float(i), -offset.y * float(i)) );\n }\n\n vec4 color3 = vec4(0.0);\n // Down right\n for(int i = 0; i < KERNEL_SIZE; i++){\n color3 += 1.0/fKernelSize * texture2D(texture2, v_Texcoord + vec2(offset.x * float(i), -offset.y * float(i)) );\n }\n\n gl_FragColor = (color1 + color2 + color3) / 3.0;\n}\n\n@end';}); - -define('qtek/shader/source/compositor/lum.essl',[],function () { return '\n@export buildin.compositor.lum\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\n\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord );\n float luminance = dot(tex.rgb, w);\n\n gl_FragColor = vec4(vec3(luminance), 1.0);\n}\n\n@end';}); - -define('qtek/shader/source/compositor/lut.essl',[],function () { return '\n// https://github.com/BradLarson/GPUImage?source=c\n@export buildin.compositor.lut\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\nuniform sampler2D lookup;\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n\n float blueColor = tex.b * 63.0;\n \n vec2 quad1;\n quad1.y = floor(floor(blueColor) / 8.0);\n quad1.x = floor(blueColor) - (quad1.y * 8.0);\n \n vec2 quad2;\n quad2.y = floor(ceil(blueColor) / 8.0);\n quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n \n vec2 texPos1;\n texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.r);\n texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.g);\n \n vec2 texPos2;\n texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.r);\n texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.g);\n \n vec4 newColor1 = texture2D(lookup, texPos1);\n vec4 newColor2 = texture2D(lookup, texPos2);\n \n vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n gl_FragColor = vec4(newColor.rgb, tex.w);\n}\n\n@end';}); - -define('qtek/shader/source/compositor/output.essl',[],function () { return '@export buildin.compositor.output\n\n#define OUTPUT_ALPHA;\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n\n gl_FragColor.rgb = tex.rgb;\n\n #ifdef OUTPUT_ALPHA\n gl_FragColor.a = tex.a;\n #else\n gl_FragColor.a = 1.0;\n #endif\n\n}\n\n@end';}); - -define('qtek/shader/source/compositor/hdr.essl',[],function () { return '// HDR Pipeline\n@export buildin.compositor.hdr.bright\n\nuniform sampler2D texture;\nuniform float threshold : 1;\nuniform float scale : 1.0;\n\nvarying vec2 v_Texcoord;\n\nconst vec3 lumWeight = vec3(0.2125, 0.7154, 0.0721);\n\n@import buildin.util.rgbm_decode\n@import buildin.util.rgbm_encode\n\nvoid main()\n{\n #ifdef TEXTURE_ENABLED\n #ifdef RGBM_DECODE\n vec3 tex = RGBMDecode(texture2D(texture, v_Texcoord));\n #else\n vec3 tex = texture2D(texture, v_Texcoord).rgb;\n #endif\n #else\n vec3 tex = vec3(0.0);\n #endif\n\n float lum = dot(tex, lumWeight);\n if (lum > threshold)\n {\n gl_FragColor.rgb = tex * scale;\n }\n else\n {\n gl_FragColor.rgb = vec3(0.0);\n }\n gl_FragColor.a = 1.0;\n\n #ifdef RGBM_ENCODE\n gl_FragColor.rgba = RGBMEncode(gl_FragColor.rgb);\n #endif\n}\n@end\n\n@export buildin.compositor.hdr.log_lum\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\n\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n float luminance = dot(tex.rgb, w);\n luminance = log(luminance + 0.001);\n\n gl_FragColor = vec4(vec3(luminance), 1.0);\n}\n\n@end\n\n@export buildin.compositor.hdr.lum_adaption\nvarying vec2 v_Texcoord;\n\nuniform sampler2D adaptedLum;\nuniform sampler2D currentLum;\n\nuniform float frameTime : 0.02;\n\nvoid main()\n{\n float fAdaptedLum = texture2D(adaptedLum, vec2(0.5, 0.5)).r;\n float fCurrentLum = exp(texture2D(currentLum, vec2(0.5, 0.5)).r);\n\n fAdaptedLum += (fCurrentLum - fAdaptedLum) * (1.0 - pow(0.98, 30.0 * frameTime));\n gl_FragColor.rgb = vec3(fAdaptedLum);\n gl_FragColor.a = 1.0;\n}\n@end\n\n// Tone mapping with gamma correction\n// http://filmicgames.com/archives/75\n@export buildin.compositor.hdr.tonemapping\n\nuniform sampler2D texture;\nuniform sampler2D bloom;\nuniform sampler2D lensflare;\nuniform sampler2D lum;\n\nuniform float exposure : 1.0;\n\nvarying vec2 v_Texcoord;\n\nconst float A = 0.22; // Shoulder Strength\nconst float B = 0.30; // Linear Strength\nconst float C = 0.10; // Linear Angle\nconst float D = 0.20; // Toe Strength\nconst float E = 0.01; // Toe Numerator\nconst float F = 0.30; // Toe Denominator\nconst vec3 whiteScale = vec3(11.2);\n\nvec3 uncharted2ToneMap(vec3 x)\n{\n return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;\n}\n\nvec3 filmicToneMap(vec3 color)\n{\n vec3 x = max(vec3(0.0), color - 0.004);\n return (x*(6.2*x+0.5))/(x*(6.2*x+1.7)+0.06);\n}\n\nfloat eyeAdaption(float fLum)\n{\n return mix(0.2, fLum, 0.5);\n}\n\nvoid main()\n{\n vec3 tex = vec3(0.0);\n float a = 1.0;\n #ifdef TEXTURE_ENABLED\n vec4 res = texture2D(texture, v_Texcoord);\n a = res.a;\n tex = res.rgb;\n #endif\n\n #ifdef BLOOM_ENABLED\n tex += texture2D(bloom, v_Texcoord).rgb * 0.25;\n #endif\n\n #ifdef LENSFLARE_ENABLED\n tex += texture2D(lensflare, v_Texcoord).rgb;\n #endif\n\n // Adjust exposure\n // From KlayGE\n #ifdef LUM_ENABLED\n float fLum = texture2D(lum, vec2(0.5, 0.5)).r;\n float adaptedLumDest = 3.0 / (max(0.1, 1.0 + 10.0*eyeAdaption(fLum)));\n float exposureBias = adaptedLumDest * exposure;\n #else\n float exposureBias = exposure;\n #endif\n tex *= exposureBias;\n\n // Do tone mapping\n vec3 color = uncharted2ToneMap(tex) / uncharted2ToneMap(whiteScale);\n color = pow(color, vec3(1.0/2.2));\n // vec3 color = filmicToneMap(tex);\n\n #ifdef RGBM_ENCODE\n gl_FragColor.rgba = RGBMEncode(color);\n #else\n gl_FragColor = vec4(color, a);\n #endif\n}\n\n@end';}); - -define('qtek/shader/source/compositor/lensflare.essl',[],function () { return '// john-chapman-graphics.blogspot.co.uk/2013/02/pseudo-lens-flare.html\n@export buildin.compositor.lensflare\n\n#define SAMPLE_NUMBER 8\n\nuniform sampler2D texture;\nuniform sampler2D lensColor;\n\nuniform vec2 textureSize : [512, 512];\n\nuniform float dispersal : 0.3;\nuniform float haloWidth : 0.4;\nuniform float distortion : 1.0;\n\nvarying vec2 v_Texcoord;\n\nvec4 textureDistorted(\n in vec2 texcoord,\n in vec2 direction,\n in vec3 distortion\n) {\n return vec4(\n texture2D(texture, texcoord + direction * distortion.r).r,\n texture2D(texture, texcoord + direction * distortion.g).g,\n texture2D(texture, texcoord + direction * distortion.b).b,\n 1.0\n );\n}\n\nvoid main()\n{\n vec2 texcoord = -v_Texcoord + vec2(1.0); // Flip texcoords\n vec2 textureOffset = 1.0 / textureSize;\n\n vec2 ghostVec = (vec2(0.5) - texcoord) * dispersal;\n vec2 haloVec = normalize(ghostVec) * haloWidth;\n\n vec3 distortion = vec3(-textureOffset.x * distortion, 0.0, textureOffset.x * distortion);\n //Sample ghost\n vec4 result = vec4(0.0);\n for (int i = 0; i < SAMPLE_NUMBER; i++)\n {\n vec2 offset = fract(texcoord + ghostVec * float(i));\n\n float weight = length(vec2(0.5) - offset) / length(vec2(0.5));\n weight = pow(1.0 - weight, 10.0);\n\n result += textureDistorted(offset, normalize(ghostVec), distortion) * weight;\n }\n\n result *= texture2D(lensColor, vec2(length(vec2(0.5) - texcoord)) / length(vec2(0.5)));\n //Sample halo\n float weight = length(vec2(0.5) - fract(texcoord + haloVec)) / length(vec2(0.5));\n weight = pow(1.0 - weight, 10.0);\n vec2 offset = fract(texcoord + haloVec);\n result += textureDistorted(offset, normalize(ghostVec), distortion) * weight;\n\n gl_FragColor = result;\n}\n@end';}); - -define('qtek/shader/source/compositor/blend.essl',[],function () { return '@export buildin.compositor.blend\n// Blend at most 4 textures\n#ifdef TEXTURE1_ENABLED\nuniform sampler2D texture1;\nuniform float weight1 : 1.0;\n#endif\n#ifdef TEXTURE2_ENABLED\nuniform sampler2D texture2;\nuniform float weight2 : 1.0;\n#endif\n#ifdef TEXTURE3_ENABLED\nuniform sampler2D texture3;\nuniform float weight3 : 1.0;\n#endif\n#ifdef TEXTURE4_ENABLED\nuniform sampler2D texture4;\nuniform float weight4 : 1.0;\n#endif\n\nvarying vec2 v_Texcoord;\n\nvoid main()\n{\n vec3 tex = vec3(0.0);\n #ifdef TEXTURE1_ENABLED\n tex += texture2D(texture1, v_Texcoord).rgb * weight1;\n #endif\n #ifdef TEXTURE2_ENABLED\n tex += texture2D(texture2, v_Texcoord).rgb * weight2;\n #endif\n #ifdef TEXTURE3_ENABLED\n tex += texture2D(texture3, v_Texcoord).rgb * weight3;\n #endif\n #ifdef TEXTURE4_ENABLED\n tex += texture2D(texture4, v_Texcoord).rgb * weight4;\n #endif\n\n gl_FragColor = vec4(tex, 1.0);\n}\n@end';}); - -define('qtek/shader/source/compositor/fxaa.essl',[],function () { return '// https://github.com/mitsuhiko/webgl-meincraft/blob/master/assets/shaders/fxaa.glsl\n@export buildin.compositor.fxaa\n\nuniform sampler2D texture;\nuniform vec2 viewportSize : [512, 512];\n\nvarying vec2 v_Texcoord;\n\n#define FXAA_REDUCE_MIN (1.0/128.0)\n#define FXAA_REDUCE_MUL (1.0/8.0)\n#define FXAA_SPAN_MAX 8.0\n\nvoid main()\n{\n vec2 resolution = 1.0 / viewportSize;\n vec3 rgbNW = texture2D( texture, ( gl_FragCoord.xy + vec2( -1.0, -1.0 ) ) * resolution ).xyz;\n vec3 rgbNE = texture2D( texture, ( gl_FragCoord.xy + vec2( 1.0, -1.0 ) ) * resolution ).xyz;\n vec3 rgbSW = texture2D( texture, ( gl_FragCoord.xy + vec2( -1.0, 1.0 ) ) * resolution ).xyz;\n vec3 rgbSE = texture2D( texture, ( gl_FragCoord.xy + vec2( 1.0, 1.0 ) ) * resolution ).xyz;\n vec4 rgbaM = texture2D( texture, gl_FragCoord.xy * resolution );\n vec3 rgbM = rgbaM.xyz;\n float opacity = rgbaM.w;\n\n vec3 luma = vec3( 0.299, 0.587, 0.114 );\n\n float lumaNW = dot( rgbNW, luma );\n float lumaNE = dot( rgbNE, luma );\n float lumaSW = dot( rgbSW, luma );\n float lumaSE = dot( rgbSE, luma );\n float lumaM = dot( rgbM, luma );\n float lumaMin = min( lumaM, min( min( lumaNW, lumaNE ), min( lumaSW, lumaSE ) ) );\n float lumaMax = max( lumaM, max( max( lumaNW, lumaNE) , max( lumaSW, lumaSE ) ) );\n\n vec2 dir;\n dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\n float dirReduce = max( ( lumaNW + lumaNE + lumaSW + lumaSE ) * ( 0.25 * FXAA_REDUCE_MUL ), FXAA_REDUCE_MIN );\n\n float rcpDirMin = 1.0 / ( min( abs( dir.x ), abs( dir.y ) ) + dirReduce );\n dir = min( vec2( FXAA_SPAN_MAX, FXAA_SPAN_MAX),\n max( vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),\n dir * rcpDirMin)) * resolution;\n\n vec3 rgbA = texture2D( texture, gl_FragCoord.xy * resolution + dir * ( 1.0 / 3.0 - 0.5 ) ).xyz;\n rgbA += texture2D( texture, gl_FragCoord.xy * resolution + dir * ( 2.0 / 3.0 - 0.5 ) ).xyz;\n rgbA *= 0.5;\n\n vec3 rgbB = texture2D( texture, gl_FragCoord.xy * resolution + dir * -0.5 ).xyz;\n rgbB += texture2D( texture, gl_FragCoord.xy * resolution + dir * 0.5 ).xyz;\n rgbB *= 0.25;\n rgbB += rgbA * 0.5;\n\n float lumaB = dot( rgbB, luma );\n\n if ( ( lumaB < lumaMin ) || ( lumaB > lumaMax ) )\n {\n\n gl_FragColor = vec4( rgbA, opacity );\n\n } else {\n\n gl_FragColor = vec4( rgbB, opacity );\n\n }\n}\n\n@end';}); - -define('qtek/shader/buildin',['require','./library','../Shader','./source/basic.essl','./source/lambert.essl','./source/phong.essl','./source/standard.essl','./source/wireframe.essl','./source/skybox.essl','./source/util.essl','./source/prez.essl','./source/shadowmap.essl','./source/compositor/coloradjust.essl','./source/compositor/blur.essl','./source/compositor/lum.essl','./source/compositor/lut.essl','./source/compositor/output.essl','./source/compositor/hdr.essl','./source/compositor/lensflare.essl','./source/compositor/blend.essl','./source/compositor/fxaa.essl'],function (require) { - - var library = require('./library'); - var Shader = require('../Shader'); - - // Some build in shaders - Shader['import'](require('./source/basic.essl')); - Shader['import'](require('./source/lambert.essl')); - Shader['import'](require('./source/phong.essl')); - Shader['import'](require('./source/standard.essl')); - Shader['import'](require('./source/wireframe.essl')); - Shader['import'](require('./source/skybox.essl')); - Shader['import'](require('./source/util.essl')); - Shader['import'](require('./source/prez.essl')); - - Shader['import'](require('./source/shadowmap.essl')); - - library.template('buildin.basic', Shader.source('buildin.basic.vertex'), Shader.source('buildin.basic.fragment')); - library.template('buildin.lambert', Shader.source('buildin.lambert.vertex'), Shader.source('buildin.lambert.fragment')); - library.template('buildin.phong', Shader.source('buildin.phong.vertex'), Shader.source('buildin.phong.fragment')); - library.template('buildin.wireframe', Shader.source('buildin.wireframe.vertex'), Shader.source('buildin.wireframe.fragment')); - library.template('buildin.skybox', Shader.source('buildin.skybox.vertex'), Shader.source('buildin.skybox.fragment')); - library.template('buildin.prez', Shader.source('buildin.prez.vertex'), Shader.source('buildin.prez.fragment')); - library.template('buildin.standard', Shader.source('buildin.standard.vertex'), Shader.source('buildin.standard.fragment')); - // Compatible with previous - library.template('buildin.physical', Shader.source('buildin.physical.vertex'), Shader.source('buildin.physical.fragment')); - - // Some build in shaders - Shader['import'](require('./source/compositor/coloradjust.essl')); - Shader['import'](require('./source/compositor/blur.essl')); - Shader['import'](require('./source/compositor/lum.essl')); - Shader['import'](require('./source/compositor/lut.essl')); - Shader['import'](require('./source/compositor/output.essl')); - Shader['import'](require('./source/compositor/hdr.essl')); - Shader['import'](require('./source/compositor/lensflare.essl')); - Shader['import'](require('./source/compositor/blend.essl')); - Shader['import'](require('./source/compositor/fxaa.essl')); - -}); -define('qtek/util/dds',['require','../Texture','../Texture2D','../TextureCube'],function(require) { - - - - var Texture = require('../Texture'); - var Texture2D = require('../Texture2D'); - var TextureCube = require('../TextureCube'); - - // http://msdn.microsoft.com/en-us/library/windows/desktop/bb943991(v=vs.85).aspx - // https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js - var DDS_MAGIC = 0x20534444; - - var DDSD_CAPS = 0x1; - var DDSD_HEIGHT = 0x2; - var DDSD_WIDTH = 0x4; - var DDSD_PITCH = 0x8; - var DDSD_PIXELFORMAT = 0x1000; - var DDSD_MIPMAPCOUNT = 0x20000; - var DDSD_LINEARSIZE = 0x80000; - var DDSD_DEPTH = 0x800000; - - var DDSCAPS_COMPLEX = 0x8; - var DDSCAPS_MIPMAP = 0x400000; - var DDSCAPS_TEXTURE = 0x1000; - - var DDSCAPS2_CUBEMAP = 0x200; - var DDSCAPS2_CUBEMAP_POSITIVEX = 0x400; - var DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800; - var DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000; - var DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000; - var DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000; - var DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000; - var DDSCAPS2_VOLUME = 0x200000; - - var DDPF_ALPHAPIXELS = 0x1; - var DDPF_ALPHA = 0x2; - var DDPF_FOURCC = 0x4; - var DDPF_RGB = 0x40; - var DDPF_YUV = 0x200; - var DDPF_LUMINANCE = 0x20000; - - function fourCCToInt32(value) { - return value.charCodeAt(0) + - (value.charCodeAt(1) << 8) + - (value.charCodeAt(2) << 16) + - (value.charCodeAt(3) << 24); - } - - function int32ToFourCC(value) { - return String.fromCharCode( - value & 0xff, - (value >> 8) & 0xff, - (value >> 16) & 0xff, - (value >> 24) & 0xff - ); - } - - var headerLengthInt = 31; // The header length in 32 bit ints - - var FOURCC_DXT1 = fourCCToInt32('DXT1'); - var FOURCC_DXT3 = fourCCToInt32('DXT3'); - var FOURCC_DXT5 = fourCCToInt32('DXT5'); - // Offsets into the header array - var off_magic = 0; - - var off_size = 1; - var off_flags = 2; - var off_height = 3; - var off_width = 4; - - var off_mipmapCount = 7; - - var off_pfFlags = 20; - var off_pfFourCC = 21; - - var off_caps = 27; - var off_caps2 = 28; - var off_caps3 = 29; - var off_caps4 = 30; - - var ret = { - parse: function(arrayBuffer, out) { - var header = new Int32Array(arrayBuffer, 0, headerLengthInt); - if (header[off_magic] !== DDS_MAGIC) { - return null; - } - if (!header(off_pfFlags) & DDPF_FOURCC) { - return null; - } - - var fourCC = header(off_pfFourCC); - var width = header[off_width]; - var height = header[off_height]; - var isCubeMap = header[off_caps2] & DDSCAPS2_CUBEMAP; - var hasMipmap = header[off_flags] & DDSD_MIPMAPCOUNT; - var blockBytes, internalFormat; - switch(fourCC) { - case FOURCC_DXT1: - blockBytes = 8; - internalFormat = Texture.COMPRESSED_RGB_S3TC_DXT1_EXT; - break; - case FOURCC_DXT3: - blockBytes = 16; - internalFormat = Texture.COMPRESSED_RGBA_S3TC_DXT3_EXT; - break; - case FOURCC_DXT5: - blockBytes = 16; - internalFormat = Texture.COMPRESSED_RGBA_S3TC_DXT5_EXT; - break; - default: - return null; - } - var dataOffset = header[off_size] + 4; - // TODO: Suppose all face are existed - var faceNumber = isCubeMap ? 6 : 1; - var mipmapCount = 1; - if (hasMipmap) { - mipmapCount = Math.max(1, header[off_mipmapCount]); - } - - var textures = []; - for (var f = 0; f < faceNumber; f++) { - var _width = width; - var _height = height; - textures[f] = new Texture2D({ - width : _width, - height : _height, - format : internalFormat - }); - var mipmaps = []; - for (var i = 0; i < mipmapCount; i++) { - var dataLength = Math.max(4, _width) / 4 * Math.max(4, _height) / 4 * blockBytes; - var byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength); - - dataOffset += dataLength; - _width *= 0.5; - _height *= 0.5; - mipmaps[i] = byteArray; - } - textures[f].pixels = mipmaps[0]; - if (hasMipmap) { - textures[f].mipmaps = mipmaps; - } - } - // TODO - // return isCubeMap ? textures : textures[0]; - if (out) { - out.width = textures[0].width; - out.height = textures[0].height; - out.format = textures[0].format; - out.pixels = textures[0].pixels; - out.mipmaps = textures[0].mipmaps; - } else { - return textures[0]; - } - } - }; - - return ret; -}); -define('qtek/util/hdr',['require','../Texture','../Texture2D'],function(require) { - - - - var Texture = require('../Texture'); - var Texture2D = require('../Texture2D'); - var toChar = String.fromCharCode; - - var MINELEN = 8; - var MAXELEN = 0x7fff; - function rgbe2float(rgbe, buffer, offset, exposure) { - if (rgbe[3] > 0) { - var f = Math.pow(2.0, rgbe[3] - 128 - 8 + exposure); - buffer[offset + 0] = rgbe[0] * f; - buffer[offset + 1] = rgbe[1] * f; - buffer[offset + 2] = rgbe[2] * f; - } else { - buffer[offset + 0] = 0; - buffer[offset + 1] = 0; - buffer[offset + 2] = 0; - } - buffer[offset + 3] = 1.0; - return buffer; - } - - function uint82string(array, offset, size) { - var str = ''; - for (var i = offset; i < size; i++) { - str += toChar(array[i]); - } - return str; - } - - function copyrgbe(s, t) { - t[0] = s[0]; - t[1] = s[1]; - t[2] = s[2]; - t[3] = s[3]; - } - - // TODO : check - function oldReadColors(scan, buffer, offset, xmax) { - var rshift = 0, x = 0, len = xmax; - while (len > 0) { - scan[x][0] = buffer[offset++]; - scan[x][1] = buffer[offset++]; - scan[x][2] = buffer[offset++]; - scan[x][3] = buffer[offset++]; - if (scan[x][0] === 1 && scan[x][1] === 1 && scan[x][2] === 1) { - // exp is count of repeated pixels - for (var i = (scan[x][3] << rshift) >>> 0; i > 0; i--) { - copyrgbe(scan[x-1], scan[x]); - x++; - len--; - } - rshift += 8; - } else { - x++; - len--; - rshift = 0; - } - } - return offset; - } - - function readColors(scan, buffer, offset, xmax) { - if ((xmax < MINELEN) | (xmax > MAXELEN)) { - return oldReadColors(scan, buffer, offset, xmax); - } - var i = buffer[offset++]; - if (i != 2) { - return oldReadColors(scan, buffer, offset - 1, xmax); - } - scan[0][1] = buffer[offset++]; - scan[0][2] = buffer[offset++]; - - i = buffer[offset++]; - if ((((scan[0][2] << 8) >>> 0) | i) >>> 0 !== xmax) { - return null; - } - for (var i = 0; i < 4; i++) { - for (var x = 0; x < xmax;) { - var code = buffer[offset++]; - if (code > 128) { - code = (code & 127) >>> 0; - var val = buffer[offset++]; - while (code--) { - scan[x++][i] = val; - } - } else { - while (code--) { - scan[x++][i] = buffer[offset++]; - } - } - } - } - return offset; - } - - - var ret = { - // http://www.graphics.cornell.edu/~bjw/rgbe.html - // Blender source - // http://radsite.lbl.gov/radiance/refer/Notes/picture_format.html - parseRGBE: function(arrayBuffer, texture, exposure) { - if (exposure === undefined) { - exposure = 0; - } - var data = new Uint8Array(arrayBuffer); - var size = data.length; - if (uint82string(data, 0, 2) !== '#?') { - return; - } - // find empty line, next line is resolution info - for (var i = 2; i < size; i++) { - if (toChar(data[i]) === '\n' && toChar(data[i+1]) === '\n') { - break; - } - } - if (i >= size) { // not found - return; - } - // find resolution info line - i += 2; - var str = ''; - for (; i < size; i++) { - var _char = toChar(data[i]); - if (_char === '\n') { - break; - } - str += _char; - } - // -Y M +X N - var tmp = str.split(' '); - var height = parseInt(tmp[1]); - var width = parseInt(tmp[3]); - if (!width || !height) { - return; - } - - // read and decode actual data - var offset = i+1; - var scanline = []; - // memzero - for (var x = 0; x < width; x++) { - scanline[x] = []; - for (var j = 0; j < 4; j++) { - scanline[x][j] = 0; - } - } - var pixels = new Float32Array(width * height * 4); - var offset2 = 0; - for (var y = 0; y < height; y++) { - var offset = readColors(scanline, data, offset, width); - if (!offset) { - return null; - } - for (var x = 0; x < width; x++) { - rgbe2float(scanline[x], pixels, offset2, exposure); - offset2 += 4; - } - } - - if (!texture) { - texture = new Texture2D(); - } - texture.width = width; - texture.height = height; - texture.pixels = pixels; - texture.type = Texture.FLOAT; - return texture; - }, - - parseRGBEFromPNG: function(png) { - - } - }; - - return ret; -}); -define('qtek/util/mesh',['require','../Geometry','../DynamicGeometry','../StaticGeometry','../Mesh','../Node','../Material','../Shader','../math/BoundingBox','../dep/glmatrix'],function(require) { - - - - var Geometry = require('../Geometry'); - var DynamicGeometry = require('../DynamicGeometry'); - var StaticGeometry = require('../StaticGeometry'); - var Mesh = require('../Mesh'); - var Node = require('../Node'); - var Material = require('../Material'); - var Shader = require('../Shader'); - var BoundingBox = require('../math/BoundingBox'); - var glMatrix = require('../dep/glmatrix'); - var mat4 = glMatrix.mat4; - var vec3 = glMatrix.vec3; - - var arraySlice = Array.prototype.slice; - - /** - * @namespace qtek.util.mesh - */ - var meshUtil = { - /** - * Merge multiple meshes to one. - * Note that these meshes must have the same material - * - * @param {Array.} meshes - * @param {boolean} applyWorldTransform - * @return qtek.Mesh - * @memberOf qtek.util.mesh - */ - merge: function(meshes, applyWorldTransform) { - - if (! meshes.length) { - return; - } - - var templateMesh = meshes[0]; - var templateGeo = templateMesh.geometry; - var material = templateMesh.material; - var isStatic = templateGeo instanceof StaticGeometry; - - var geometry = isStatic ? new StaticGeometry() : new DynamicGeometry(); - geometry.boundingBox = new BoundingBox(); - var faces = geometry.faces; - - var attributeNames = templateGeo.getEnabledAttributes(); - // TODO - if (!isStatic) { - attributeNames = Object.keys(attributeNames); - } - - for (var i = 0; i < attributeNames.length; i++) { - var name = attributeNames[i]; - var attr = templateGeo.attributes[name]; - // Extend custom attributes - if (! geometry.attributes[name]) { - geometry.attributes[name] = attr.clone(false); - } - } - - var inverseTransposeMatrix = mat4.create(); - // Initialize the array data and merge bounding box - if (isStatic) { - var nVertex = 0; - var nFace = 0; - for (var k = 0; k < meshes.length; k++) { - var currentGeo = meshes[k].geometry; - if (currentGeo.boundingBox) { - currentGeo.boundingBox.applyTransform(applyWorldTransform ? meshes[k].worldTransform : meshes[k].localTransform); - geometry.boundingBox.union(currentGeo.boundingBox); - } - nVertex += currentGeo.getVertexNumber(); - nFace += currentGeo.getFaceNumber(); - } - for (var n = 0; n < attributeNames.length; n++) { - var name = attributeNames[n]; - var attrib = geometry.attributes[name]; - attrib.init(nVertex); - } - if (nVertex >= 0xffff) { - geometry.faces = new Uint32Array(nFace * 3); - } else { - geometry.faces = new Uint16Array(nFace * 3); - } - } - - var vertexOffset = 0; - var faceOffset = 0; - var useFaces = templateGeo.isUseFace(); - - for (var mm = 0; mm < meshes.length; mm++) { - var mesh = meshes[mm]; - var currentGeo = mesh.geometry; - - var nVertex = currentGeo.getVertexNumber(); - - var matrix = applyWorldTransform ? mesh.worldTransform._array : mesh.localTransform._array; - mat4.invert(inverseTransposeMatrix, matrix); - mat4.transpose(inverseTransposeMatrix, inverseTransposeMatrix); - - for (var nn = 0; nn < attributeNames.length; nn++) { - var name = attributeNames[nn]; - var currentAttr = currentGeo.attributes[name]; - var targetAttr = geometry.attributes[name]; - // Skip the unused attributes; - if (!currentAttr.value.length) { - continue; - } - if (isStatic) { - var len = currentAttr.value.length; - var size = currentAttr.size; - var offset = vertexOffset * size; - var count = len / size; - for (var i = 0; i < len; i++) { - targetAttr.value[offset + i] = currentAttr.value[i]; - } - // Transform position, normal and tangent - if (name === 'position') { - vec3.forEach(targetAttr.value, size, offset, count, vec3.transformMat4, matrix); - } else if (name === 'normal' || name === 'tangent') { - vec3.forEach(targetAttr.value, size, offset, count, vec3.transformMat4, inverseTransposeMatrix); - } - } else { - for (var i = 0; i < nVertex; i++) { - // Transform position, normal and tangent - if (name === 'position') { - var newValue = vec3.create(); - vec3.transformMat4(newValue, currentAttr.value[i], matrix); - targetAttr.value.push(newValue); - } - else if (name === 'normal' || name === 'tangent') { - var newValue = vec3.create(); - vec3.transformMat4(newValue, currentAttr.value[i], inverseTransposeMatrix); - targetAttr.value.push(newValue); - } else { - targetAttr.value.push(currentAttr.value[i]); - } - } - } - } - - if (useFaces) { - var len = currentGeo.faces.length; - if (isStatic) { - for (var i = 0; i < len; i++) { - geometry.faces[i + faceOffset] = currentGeo.faces[i] + vertexOffset; - } - faceOffset += len; - } else { - for (var i = 0; i < len; i++) { - var newFace = []; - var face = currentGeo.faces[i]; - newFace[0] = face[0] + vertexOffset; - newFace[1] = face[1] + vertexOffset; - newFace[2] = face[2] + vertexOffset; - - faces.push(newFace); - } - } - } - - vertexOffset += nVertex; - } - - return new Mesh({ - material: material, - geometry: geometry - }); - }, - - /** - * Split mesh into sub meshes, each mesh will have maxJointNumber joints. - * @param {qtek.Mesh} mesh - * @param {number} maxJointNumber - * @param {boolean} inPlace - * @return {qtek.Node} - * - * @memberOf qtek.util.mesh - */ - splitByJoints: function(mesh, maxJointNumber, inPlace) { - var geometry = mesh.geometry; - var skeleton = mesh.skeleton; - var material = mesh.material; - var shader = material.shader; - var joints = mesh.joints; - if (!geometry || !skeleton || !joints.length) { - return; - } - if (joints.length < maxJointNumber) { - return mesh; - } - var isStatic = geometry instanceof StaticGeometry; - - var shaders = {}; - - var faces = geometry.faces; - - var faceLen = geometry.getFaceNumber(); - var rest = faceLen; - var isFaceAdded = []; - var jointValues = geometry.attributes.joint.value; - for (var i = 0; i < faceLen; i++) { - isFaceAdded[i] = false; - } - var addedJointIdxPerFace = []; - - var buckets = []; - - var getJointByIndex = function(idx) { - return joints[idx]; - }; - while(rest > 0) { - var bucketFaces = []; - var bucketJointReverseMap = []; - var bucketJoints = []; - var subJointNumber = 0; - for (var i = 0; i < joints.length; i++) { - bucketJointReverseMap[i] = -1; - } - for (var f = 0; f < faceLen; f++) { - if (isFaceAdded[f]) { - continue; - } - var canAddToBucket = true; - var addedNumber = 0; - for (var i = 0; i < 3; i++) { - - var idx = isStatic ? faces[f * 3 + i] : faces[f][i]; - - for (var j = 0; j < 4; j++) { - var jointIdx; - if (isStatic) { - jointIdx = jointValues[idx * 4 + j]; - } else { - jointIdx = jointValues[idx][j]; - } - if (jointIdx >= 0) { - if (bucketJointReverseMap[jointIdx] === -1) { - if (subJointNumber < maxJointNumber) { - bucketJointReverseMap[jointIdx] = subJointNumber; - bucketJoints[subJointNumber++] = jointIdx; - addedJointIdxPerFace[addedNumber++] = jointIdx; - } else { - canAddToBucket = false; - } - } - } - } - } - if (!canAddToBucket) { - // Reverse operation - for (var i = 0; i < addedNumber; i++) { - bucketJointReverseMap[addedJointIdxPerFace[i]] = -1; - bucketJoints.pop(); - subJointNumber--; - } - } else { - if (isStatic) { - bucketFaces.push(faces.subarray(f * 3, (f + 1) * 3)); - } else { - bucketFaces.push(faces[f]); - } - isFaceAdded[f] = true; - rest--; - } - } - buckets.push({ - faces : bucketFaces, - joints : bucketJoints.map(getJointByIndex), - jointReverseMap : bucketJointReverseMap - }); - } - - var root = new Node({ - name : mesh.name - }); - var attribNames = geometry.getEnabledAttributes(); - // TODO - if (!isStatic) { - attribNames = Object.keys(attribNames); - } - - attribNames.splice(attribNames.indexOf('joint'), 1); - // Map from old vertex index to new vertex index - var newIndices = []; - for (var b = 0; b < buckets.length; b++) { - var bucket = buckets[b]; - var jointReverseMap = bucket.jointReverseMap; - var subJointNumber = bucket.joints.length; - var subShader = shaders[subJointNumber]; - if (!subShader) { - subShader = shader.clone(); - subShader.define('vertex', 'JOINT_NUMBER', subJointNumber); - shaders[subJointNumber] = subShader; - } - var subMat = new Material({ - name : [material.name, b].join('-'), - shader : subShader, - transparent : material.transparent, - depthTest : material.depthTest, - depthMask : material.depthMask, - blend : material.blend - }); - for (var name in material.uniforms) { - var uniform = material.uniforms[name]; - subMat.set(name, uniform.value); - } - var subGeo = isStatic ? new StaticGeometry() : new DynamicGeometry(); - - var subMesh = new Mesh({ - name : [mesh.name, i].join('-'), - material : subMat, - geometry : subGeo, - skeleton : skeleton, - joints : bucket.joints.slice() - }); - var nVertex = 0; - var nVertex2 = geometry.getVertexNumber(); - for (var i = 0; i < nVertex2; i++) { - newIndices[i] = -1; - } - // Count sub geo number - for (var f = 0; f < bucket.faces.length; f++) { - var face = bucket.faces[f]; - for (var i = 0; i < 3; i++) { - var idx = face[i]; - if (newIndices[idx] === -1) { - newIndices[idx] = nVertex; - nVertex++; - } - } - } - if (isStatic) { - for (var a = 0; a < attribNames.length; a++) { - var attribName = attribNames[a]; - var subAttrib = subGeo.attributes[attribName]; - subAttrib.init(nVertex); - } - subGeo.attributes.joint.value = new Float32Array(nVertex * 4); - - if (nVertex > 0xffff) { - subGeo.faces = new Uint32Array(bucket.faces.length * 3); - } else { - subGeo.faces = new Uint16Array(bucket.faces.length * 3); - } - } - - var faceOffset = 0; - nVertex = 0; - for (var i = 0; i < nVertex2; i++) { - newIndices[i] = -1; - } - - for (var f = 0; f < bucket.faces.length; f++) { - var newFace; - if (!isStatic) { - newFace = []; - } - var face = bucket.faces[f]; - for (var i = 0; i < 3; i++) { - - var idx = face[i]; - - if (newIndices[idx] === -1) { - newIndices[idx] = nVertex; - for (var a = 0; a < attribNames.length; a++) { - var attribName = attribNames[a]; - var attrib = geometry.attributes[attribName]; - var subAttrib = subGeo.attributes[attribName]; - var size = attrib.size; - - if (isStatic) { - for (var j = 0; j < size; j++) { - subAttrib.value[nVertex * size + j] = attrib.value[idx * size + j]; - } - } else { - if (attrib.size === 1) { - subAttrib.value[nVertex] = attrib.value[idx]; - } else { - subAttrib.value[nVertex] = arraySlice.call(attrib.value[idx]); - } - } - } - if (isStatic) { - for (var j = 0; j < 4; j++) { - var jointIdx = geometry.attributes.joint.value[idx * 4 + j]; - var offset = nVertex * 4 + j; - if (jointIdx >= 0) { - subGeo.attributes.joint.value[offset] = jointReverseMap[jointIdx]; - } else { - subGeo.attributes.joint.value[offset] = -1; - } - } - } else { - var newJoints = subGeo.attributes.joint.value[nVertex] = [-1, -1, -1, -1]; - // joints - for (var j = 0; j < 4; j++) { - var jointIdx = geometry.attributes.joint.value[idx][j]; - if (jointIdx >= 0) { - newJoints[j] = jointReverseMap[jointIdx]; - } - } - } - nVertex++; - } - if (isStatic) { - subGeo.faces[faceOffset++] = newIndices[idx]; - } else { - newFace.push(newIndices[idx]); - } - } - if (!isStatic) { - subGeo.faces.push(newFace); - } - } - - root.add(subMesh); - } - var children = mesh.children(); - for (var i = 0; i < children.length; i++) { - root.add(children[i]); - } - root.position.copy(mesh.position); - root.rotation.copy(mesh.rotation); - root.scale.copy(mesh.scale); - - if (inPlace) { - if (mesh.getParent()) { - var parent = mesh.getParent(); - parent.remove(mesh); - parent.add(root); - } - } - return root; - } - }; - - return meshUtil; -}); -define('qtek/util/texture',['require','../Texture','../Texture2D','../TextureCube','../core/request','../prePass/EnvironmentMap','../plugin/Skydome','../Scene','./dds','./hdr'],function(require) { - - - - var Texture = require('../Texture'); - var Texture2D = require('../Texture2D'); - var TextureCube = require('../TextureCube'); - var request = require('../core/request'); - var EnvironmentMapPass = require('../prePass/EnvironmentMap'); - var Skydome = require('../plugin/Skydome'); - var Scene = require('../Scene'); - - var dds = require('./dds'); - var hdr = require('./hdr'); - - /** - * @namespace qtek.util.texture - */ - var textureUtil = { - /** - * @param {string|object} path - * @param {object} [option] - * @param {Function} [onsuccess] - * @param {Function} [onerror] - * @return {qtek.Texture} - * - * @memberOf qtek.util.texture - */ - loadTexture: function(path, option, onsuccess, onerror) { - var texture; - if (typeof(option) === 'function') { - onsuccess = option; - onerror = onsuccess; - option = {}; - } else { - option = option || {}; - } - if (typeof(path) === 'string') { - if (path.match(/.hdr$/)) { - texture = new Texture2D({ - width: 0, - height: 0 - }); - textureUtil._fetchTexture( - path, - function (data) { - hdr.parseRGBE(data, texture, option.exposure); - texture.dirty(); - onsuccess && onsuccess(texture); - }, - onerror - ); - return texture; - } else if (path.match(/.dds$/)) { - texture = new Texture2D({ - width: 0, - height: 0 - }); - textureUtil._fetchTexture( - path, - function (data) { - dds.parse(data, texture); - texture.dirty(); - onsuccess && onsuccess(texture); - }, - onerror - ); - } else { - texture = new Texture2D(); - texture.load(path); - texture.success(onsuccess); - texture.error(onerror); - } - } else if (typeof(path) == 'object' && typeof(path.px) !== 'undefined') { - var texture = new TextureCube(); - texture.load(path); - texture.success(onsuccess); - texture.error(onerror); - } - return texture; - }, - - /** - * Load a panorama texture and render it to a cube map - * @param {string} path - * @param {qtek.TextureCube} cubeMap - * @param {qtek.Renderer} renderer - * @param {object} [option] - * @param {Function} [onsuccess] - * @param {Function} [onerror] - * - * @memberOf qtek.util.texture - */ - loadPanorama: function(path, cubeMap, renderer, option, onsuccess, onerror) { - var self = this; - - if (typeof(option) === 'function') { - onsuccess = option; - onerror = onsuccess; - option = {}; - } else { - option = option || {}; - } - - textureUtil.loadTexture(path, option, function(texture) { - // PENDING - texture.flipY = false; - self.panoramaToCubeMap(texture, cubeMap, renderer); - texture.dispose(renderer.gl); - onsuccess && onsuccess(cubeMap); - }, onerror); - }, - - /** - * Render a panorama texture to a cube map - * @param {qtek.Texture2D} panoramaMap - * @param {qtek.TextureCube} cubeMap - * @param {qtek.Renderer} renderer - * - * @memberOf qtek.util.texture - */ - panoramaToCubeMap: function(panoramaMap, cubeMap, renderer) { - var environmentMapPass = new EnvironmentMapPass(); - var skydome = new Skydome({ - scene: new Scene() - }); - skydome.material.set('diffuseMap', panoramaMap); - environmentMapPass.texture = cubeMap; - environmentMapPass.render(renderer, skydome.scene); - environmentMapPass.texture = null; - environmentMapPass.dispose(renderer); - return cubeMap; - }, - - _fetchTexture: function(path, onsuccess, onerror) { - request.get({ - url: path, - responseType: 'arraybuffer', - onload: onsuccess, - onerror: onerror - }); - }, - - /** - * Create a chessboard texture - * @param {number} [size] - * @param {number} [unitSize] - * @param {string} [color1] - * @param {string} [color2] - * @return {qtek.Texture2D} - * - * @memberOf qtek.util.texture - */ - createChessboard: function(size, unitSize, color1, color2) { - size = size || 512; - unitSize = unitSize || 64; - color1 = color1 || 'black'; - color2 = color2 || 'white'; - - var repeat = Math.ceil(size / unitSize); - - var canvas = document.createElement('canvas'); - canvas.width = size; - canvas.height = size; - var ctx = canvas.getContext('2d'); - ctx.fillStyle = color2; - ctx.fillRect(0, 0, size, size); - - ctx.fillStyle = color1; - for (var i = 0; i < repeat; i++) { - for (var j = 0; j < repeat; j++) { - var isFill = j % 2 ? (i % 2) : (i % 2 - 1); - if (isFill) { - ctx.fillRect(i * unitSize, j * unitSize, unitSize, unitSize); - } - } - } - - var texture = new Texture2D({ - image: canvas, - anisotropic: 8 - }); - - return texture; - }, - - /** - * Create a blank pure color 1x1 texture - * @param {string} color - * @return {qtek.Texture2D} - * - * @memberOf qtek.util.texture - */ - createBlank: function(color) { - var canvas = document.createElement('canvas'); - canvas.width = 1; - canvas.height = 1; - var ctx = canvas.getContext('2d'); - ctx.fillStyle = color; - ctx.fillRect(0, 0, 1, 1); - - var texture = new Texture2D({ - image: canvas - }); - - return texture; - } - }; - - return textureUtil; -}); \ No newline at end of file diff --git a/dist/qtek.amd.min.js b/dist/qtek.amd.min.js deleted file mode 100644 index 15d4455cb..000000000 --- a/dist/qtek.amd.min.js +++ /dev/null @@ -1,12 +0,0 @@ -define("qtek/qtek.amd",[],function(){return{version:"0.2.1"}}),define("qtek",["qtek/qtek.amd"],function(a){return a}),define("qtek/core/mixin/derive",["require"],function(){function a(a,d,e){"object"==typeof d&&(e=d,d=null);var f,g=this;if(!(a instanceof Function)){f=[];for(var h in a)a.hasOwnProperty(h)&&f.push(h)}var i=function(){if(g.apply(this,arguments),a instanceof Function?b(this,a.call(this)):c(this,a,f),this.constructor===i)for(var d=i.__initializers__,e=0;ed;d++)b.call(c,a[d],d,a);else for(var f in a)a.hasOwnProperty(f)&&b.call(c,a[f],f,a)},isObject:function(a){return a===Object(a)},isArray:function(a){return a instanceof Array},isArrayLike:function(a){return a?a.length===+a.length:!1},clone:function(a){if(b.isObject(a)){if(b.isArray(a))return a.slice();if(b.isArrayLike(a)){for(var c=new a.constructor(a.length),d=0;d0&&(e=1/Math.sqrt(e),a[0]=b[0]*e,a[1]=b[1]*e),a},g.dot=function(a,b){return a[0]*b[0]+a[1]*b[1]},g.cross=function(a,b,c){var d=b[0]*c[1]-b[1]*c[0];return a[0]=a[1]=0,a[2]=d,a},g.lerp=function(a,b,c,d){var e=b[0],f=b[1];return a[0]=e+d*(c[0]-e),a[1]=f+d*(c[1]-f),a},g.random=function(a,b){b=b||1;var c=2*d()*Math.PI;return a[0]=Math.cos(c)*b,a[1]=Math.sin(c)*b,a},g.transformMat2=function(a,b,c){var d=b[0],e=b[1];return a[0]=c[0]*d+c[2]*e,a[1]=c[1]*d+c[3]*e,a},g.transformMat2d=function(a,b,c){var d=b[0],e=b[1];return a[0]=c[0]*d+c[2]*e+c[4],a[1]=c[1]*d+c[3]*e+c[5],a},g.transformMat3=function(a,b,c){var d=b[0],e=b[1];return a[0]=c[0]*d+c[3]*e+c[6],a[1]=c[1]*d+c[4]*e+c[7],a},g.transformMat4=function(a,b,c){var d=b[0],e=b[1];return a[0]=c[0]*d+c[4]*e+c[12],a[1]=c[1]*d+c[5]*e+c[13],a},g.forEach=function(){var a=g.create();return function(b,c,d,e,f,g){var h,i;for(c||(c=2),d||(d=0),i=e?Math.min(e*c+d,b.length):b.length,h=d;i>h;h+=c)a[0]=b[h],a[1]=b[h+1],f(a,a,g),b[h]=a[0],b[h+1]=a[1];return b}}(),g.str=function(a){return"vec2("+a[0]+", "+a[1]+")"},"undefined"!=typeof a&&(a.vec2=g);var h={};h.create=function(){var a=new c(3);return a[0]=0,a[1]=0,a[2]=0,a},h.clone=function(a){var b=new c(3);return b[0]=a[0],b[1]=a[1],b[2]=a[2],b},h.fromValues=function(a,b,d){var e=new c(3);return e[0]=a,e[1]=b,e[2]=d,e},h.copy=function(a,b){return a[0]=b[0],a[1]=b[1],a[2]=b[2],a},h.set=function(a,b,c,d){return a[0]=b,a[1]=c,a[2]=d,a},h.add=function(a,b,c){return a[0]=b[0]+c[0],a[1]=b[1]+c[1],a[2]=b[2]+c[2],a},h.subtract=function(a,b,c){return a[0]=b[0]-c[0],a[1]=b[1]-c[1],a[2]=b[2]-c[2],a},h.sub=h.subtract,h.multiply=function(a,b,c){return a[0]=b[0]*c[0],a[1]=b[1]*c[1],a[2]=b[2]*c[2],a},h.mul=h.multiply,h.divide=function(a,b,c){return a[0]=b[0]/c[0],a[1]=b[1]/c[1],a[2]=b[2]/c[2],a},h.div=h.divide,h.min=function(a,b,c){return a[0]=Math.min(b[0],c[0]),a[1]=Math.min(b[1],c[1]),a[2]=Math.min(b[2],c[2]),a},h.max=function(a,b,c){return a[0]=Math.max(b[0],c[0]),a[1]=Math.max(b[1],c[1]),a[2]=Math.max(b[2],c[2]),a},h.scale=function(a,b,c){return a[0]=b[0]*c,a[1]=b[1]*c,a[2]=b[2]*c,a},h.scaleAndAdd=function(a,b,c,d){return a[0]=b[0]+c[0]*d,a[1]=b[1]+c[1]*d,a[2]=b[2]+c[2]*d,a},h.distance=function(a,b){var c=b[0]-a[0],d=b[1]-a[1],e=b[2]-a[2];return Math.sqrt(c*c+d*d+e*e)},h.dist=h.distance,h.squaredDistance=function(a,b){var c=b[0]-a[0],d=b[1]-a[1],e=b[2]-a[2];return c*c+d*d+e*e},h.sqrDist=h.squaredDistance,h.length=function(a){var b=a[0],c=a[1],d=a[2];return Math.sqrt(b*b+c*c+d*d)},h.len=h.length,h.squaredLength=function(a){var b=a[0],c=a[1],d=a[2];return b*b+c*c+d*d},h.sqrLen=h.squaredLength,h.negate=function(a,b){return a[0]=-b[0],a[1]=-b[1],a[2]=-b[2],a},h.normalize=function(a,b){var c=b[0],d=b[1],e=b[2],f=c*c+d*d+e*e;return f>0&&(f=1/Math.sqrt(f),a[0]=b[0]*f,a[1]=b[1]*f,a[2]=b[2]*f),a},h.dot=function(a,b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]},h.cross=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=c[0],h=c[1],i=c[2];return a[0]=e*i-f*h,a[1]=f*g-d*i,a[2]=d*h-e*g,a},h.lerp=function(a,b,c,d){var e=b[0],f=b[1],g=b[2];return a[0]=e+d*(c[0]-e),a[1]=f+d*(c[1]-f),a[2]=g+d*(c[2]-g),a},h.random=function(a,b){b=b||1;var c=2*d()*Math.PI,e=2*d()-1,f=Math.sqrt(1-e*e)*b;return a[0]=Math.cos(c)*f,a[1]=Math.sin(c)*f,a[2]=e*b,a},h.transformMat4=function(a,b,c){var d=b[0],e=b[1],f=b[2];return a[0]=c[0]*d+c[4]*e+c[8]*f+c[12],a[1]=c[1]*d+c[5]*e+c[9]*f+c[13],a[2]=c[2]*d+c[6]*e+c[10]*f+c[14],a},h.transformMat3=function(a,b,c){var d=b[0],e=b[1],f=b[2];return a[0]=d*c[0]+e*c[3]+f*c[6],a[1]=d*c[1]+e*c[4]+f*c[7],a[2]=d*c[2]+e*c[5]+f*c[8],a},h.transformQuat=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=c[0],h=c[1],i=c[2],j=c[3],k=j*d+h*f-i*e,l=j*e+i*d-g*f,m=j*f+g*e-h*d,n=-g*d-h*e-i*f;return a[0]=k*j+n*-g+l*-i-m*-h,a[1]=l*j+n*-h+m*-g-k*-i,a[2]=m*j+n*-i+k*-h-l*-g,a},h.forEach=function(){var a=h.create();return function(b,c,d,e,f,g){var h,i;for(c||(c=3),d||(d=0),i=e?Math.min(e*c+d,b.length):b.length,h=d;i>h;h+=c)a[0]=b[h],a[1]=b[h+1],a[2]=b[h+2],f(a,a,g),b[h]=a[0],b[h+1]=a[1],b[h+2]=a[2];return b}}(),h.str=function(a){return"vec3("+a[0]+", "+a[1]+", "+a[2]+")"},"undefined"!=typeof a&&(a.vec3=h);var i={};i.create=function(){var a=new c(4);return a[0]=0,a[1]=0,a[2]=0,a[3]=0,a},i.clone=function(a){var b=new c(4);return b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b},i.fromValues=function(a,b,d,e){var f=new c(4);return f[0]=a,f[1]=b,f[2]=d,f[3]=e,f},i.copy=function(a,b){return a[0]=b[0],a[1]=b[1],a[2]=b[2],a[3]=b[3],a},i.set=function(a,b,c,d,e){return a[0]=b,a[1]=c,a[2]=d,a[3]=e,a},i.add=function(a,b,c){return a[0]=b[0]+c[0],a[1]=b[1]+c[1],a[2]=b[2]+c[2],a[3]=b[3]+c[3],a},i.subtract=function(a,b,c){return a[0]=b[0]-c[0],a[1]=b[1]-c[1],a[2]=b[2]-c[2],a[3]=b[3]-c[3],a},i.sub=i.subtract,i.multiply=function(a,b,c){return a[0]=b[0]*c[0],a[1]=b[1]*c[1],a[2]=b[2]*c[2],a[3]=b[3]*c[3],a},i.mul=i.multiply,i.divide=function(a,b,c){return a[0]=b[0]/c[0],a[1]=b[1]/c[1],a[2]=b[2]/c[2],a[3]=b[3]/c[3],a},i.div=i.divide,i.min=function(a,b,c){return a[0]=Math.min(b[0],c[0]),a[1]=Math.min(b[1],c[1]),a[2]=Math.min(b[2],c[2]),a[3]=Math.min(b[3],c[3]),a},i.max=function(a,b,c){return a[0]=Math.max(b[0],c[0]),a[1]=Math.max(b[1],c[1]),a[2]=Math.max(b[2],c[2]),a[3]=Math.max(b[3],c[3]),a},i.scale=function(a,b,c){return a[0]=b[0]*c,a[1]=b[1]*c,a[2]=b[2]*c,a[3]=b[3]*c,a},i.scaleAndAdd=function(a,b,c,d){return a[0]=b[0]+c[0]*d,a[1]=b[1]+c[1]*d,a[2]=b[2]+c[2]*d,a[3]=b[3]+c[3]*d,a},i.distance=function(a,b){var c=b[0]-a[0],d=b[1]-a[1],e=b[2]-a[2],f=b[3]-a[3];return Math.sqrt(c*c+d*d+e*e+f*f)},i.dist=i.distance,i.squaredDistance=function(a,b){var c=b[0]-a[0],d=b[1]-a[1],e=b[2]-a[2],f=b[3]-a[3];return c*c+d*d+e*e+f*f},i.sqrDist=i.squaredDistance,i.length=function(a){var b=a[0],c=a[1],d=a[2],e=a[3];return Math.sqrt(b*b+c*c+d*d+e*e)},i.len=i.length,i.squaredLength=function(a){var b=a[0],c=a[1],d=a[2],e=a[3];return b*b+c*c+d*d+e*e},i.sqrLen=i.squaredLength,i.negate=function(a,b){return a[0]=-b[0],a[1]=-b[1],a[2]=-b[2],a[3]=-b[3],a},i.normalize=function(a,b){var c=b[0],d=b[1],e=b[2],f=b[3],g=c*c+d*d+e*e+f*f;return g>0&&(g=1/Math.sqrt(g),a[0]=b[0]*g,a[1]=b[1]*g,a[2]=b[2]*g,a[3]=b[3]*g),a},i.dot=function(a,b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3]},i.lerp=function(a,b,c,d){var e=b[0],f=b[1],g=b[2],h=b[3];return a[0]=e+d*(c[0]-e),a[1]=f+d*(c[1]-f),a[2]=g+d*(c[2]-g),a[3]=h+d*(c[3]-h),a},i.random=function(a,b){return b=b||1,a[0]=d(),a[1]=d(),a[2]=d(),a[3]=d(),i.normalize(a,a),i.scale(a,a,b),a},i.transformMat4=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3];return a[0]=c[0]*d+c[4]*e+c[8]*f+c[12]*g,a[1]=c[1]*d+c[5]*e+c[9]*f+c[13]*g,a[2]=c[2]*d+c[6]*e+c[10]*f+c[14]*g,a[3]=c[3]*d+c[7]*e+c[11]*f+c[15]*g,a},i.transformQuat=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=c[0],h=c[1],i=c[2],j=c[3],k=j*d+h*f-i*e,l=j*e+i*d-g*f,m=j*f+g*e-h*d,n=-g*d-h*e-i*f;return a[0]=k*j+n*-g+l*-i-m*-h,a[1]=l*j+n*-h+m*-g-k*-i,a[2]=m*j+n*-i+k*-h-l*-g,a},i.forEach=function(){var a=i.create();return function(b,c,d,e,f,g){var h,i;for(c||(c=4),d||(d=0),i=e?Math.min(e*c+d,b.length):b.length,h=d;i>h;h+=c)a[0]=b[h],a[1]=b[h+1],a[2]=b[h+2],a[3]=b[h+3],f(a,a,g),b[h]=a[0],b[h+1]=a[1],b[h+2]=a[2],b[h+3]=a[3];return b}}(),i.str=function(a){return"vec4("+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+")"},"undefined"!=typeof a&&(a.vec4=i);var j={};j.create=function(){var a=new c(4);return a[0]=1,a[1]=0,a[2]=0,a[3]=1,a},j.clone=function(a){var b=new c(4);return b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b},j.copy=function(a,b){return a[0]=b[0],a[1]=b[1],a[2]=b[2],a[3]=b[3],a},j.identity=function(a){return a[0]=1,a[1]=0,a[2]=0,a[3]=1,a},j.transpose=function(a,b){if(a===b){var c=b[1];a[1]=b[2],a[2]=c}else a[0]=b[0],a[1]=b[2],a[2]=b[1],a[3]=b[3];return a},j.invert=function(a,b){var c=b[0],d=b[1],e=b[2],f=b[3],g=c*f-e*d;return g?(g=1/g,a[0]=f*g,a[1]=-d*g,a[2]=-e*g,a[3]=c*g,a):null},j.adjoint=function(a,b){var c=b[0];return a[0]=b[3],a[1]=-b[1],a[2]=-b[2],a[3]=c,a},j.determinant=function(a){return a[0]*a[3]-a[2]*a[1]},j.multiply=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3],h=c[0],i=c[1],j=c[2],k=c[3];return a[0]=d*h+e*j,a[1]=d*i+e*k,a[2]=f*h+g*j,a[3]=f*i+g*k,a},j.mul=j.multiply,j.rotate=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3],h=Math.sin(c),i=Math.cos(c);return a[0]=d*i+e*h,a[1]=d*-h+e*i,a[2]=f*i+g*h,a[3]=f*-h+g*i,a},j.scale=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3],h=c[0],i=c[1];return a[0]=d*h,a[1]=e*i,a[2]=f*h,a[3]=g*i,a},j.str=function(a){return"mat2("+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+")"},"undefined"!=typeof a&&(a.mat2=j);var k={};k.create=function(){var a=new c(6);return a[0]=1,a[1]=0,a[2]=0,a[3]=1,a[4]=0,a[5]=0,a},k.clone=function(a){var b=new c(6);return b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b[4]=a[4],b[5]=a[5],b},k.copy=function(a,b){return a[0]=b[0],a[1]=b[1],a[2]=b[2],a[3]=b[3],a[4]=b[4],a[5]=b[5],a},k.identity=function(a){return a[0]=1,a[1]=0,a[2]=0,a[3]=1,a[4]=0,a[5]=0,a},k.invert=function(a,b){var c=b[0],d=b[1],e=b[2],f=b[3],g=b[4],h=b[5],i=c*f-d*e;return i?(i=1/i,a[0]=f*i,a[1]=-d*i,a[2]=-e*i,a[3]=c*i,a[4]=(e*h-f*g)*i,a[5]=(d*g-c*h)*i,a):null},k.determinant=function(a){return a[0]*a[3]-a[1]*a[2]},k.multiply=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3],h=b[4],i=b[5],j=c[0],k=c[1],l=c[2],m=c[3],n=c[4],o=c[5];return a[0]=d*j+e*l,a[1]=d*k+e*m,a[2]=f*j+g*l,a[3]=f*k+g*m,a[4]=j*h+l*i+n,a[5]=k*h+m*i+o,a},k.mul=k.multiply,k.rotate=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3],h=b[4],i=b[5],j=Math.sin(c),k=Math.cos(c);return a[0]=d*k+e*j,a[1]=-d*j+e*k,a[2]=f*k+g*j,a[3]=-f*j+k*g,a[4]=k*h+j*i,a[5]=k*i-j*h,a},k.scale=function(a,b,c){var d=c[0],e=c[1];return a[0]=b[0]*d,a[1]=b[1]*e,a[2]=b[2]*d,a[3]=b[3]*e,a[4]=b[4]*d,a[5]=b[5]*e,a},k.translate=function(a,b,c){return a[0]=b[0],a[1]=b[1],a[2]=b[2],a[3]=b[3],a[4]=b[4]+c[0],a[5]=b[5]+c[1],a},k.str=function(a){return"mat2d("+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+", "+a[4]+", "+a[5]+")"},"undefined"!=typeof a&&(a.mat2d=k);var l={};l.create=function(){var a=new c(9);return a[0]=1,a[1]=0,a[2]=0,a[3]=0,a[4]=1,a[5]=0,a[6]=0,a[7]=0,a[8]=1,a},l.fromMat4=function(a,b){return a[0]=b[0],a[1]=b[1],a[2]=b[2],a[3]=b[4],a[4]=b[5],a[5]=b[6],a[6]=b[8],a[7]=b[9],a[8]=b[10],a},l.clone=function(a){var b=new c(9);return b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b[4]=a[4],b[5]=a[5],b[6]=a[6],b[7]=a[7],b[8]=a[8],b},l.copy=function(a,b){return a[0]=b[0],a[1]=b[1],a[2]=b[2],a[3]=b[3],a[4]=b[4],a[5]=b[5],a[6]=b[6],a[7]=b[7],a[8]=b[8],a},l.identity=function(a){return a[0]=1,a[1]=0,a[2]=0,a[3]=0,a[4]=1,a[5]=0,a[6]=0,a[7]=0,a[8]=1,a},l.transpose=function(a,b){if(a===b){var c=b[1],d=b[2],e=b[5];a[1]=b[3],a[2]=b[6],a[3]=c,a[5]=b[7],a[6]=d,a[7]=e}else a[0]=b[0],a[1]=b[3],a[2]=b[6],a[3]=b[1],a[4]=b[4],a[5]=b[7],a[6]=b[2],a[7]=b[5],a[8]=b[8];return a},l.invert=function(a,b){var c=b[0],d=b[1],e=b[2],f=b[3],g=b[4],h=b[5],i=b[6],j=b[7],k=b[8],l=k*g-h*j,m=-k*f+h*i,n=j*f-g*i,o=c*l+d*m+e*n;return o?(o=1/o,a[0]=l*o,a[1]=(-k*d+e*j)*o,a[2]=(h*d-e*g)*o,a[3]=m*o,a[4]=(k*c-e*i)*o,a[5]=(-h*c+e*f)*o,a[6]=n*o,a[7]=(-j*c+d*i)*o,a[8]=(g*c-d*f)*o,a):null},l.adjoint=function(a,b){var c=b[0],d=b[1],e=b[2],f=b[3],g=b[4],h=b[5],i=b[6],j=b[7],k=b[8];return a[0]=g*k-h*j,a[1]=e*j-d*k,a[2]=d*h-e*g,a[3]=h*i-f*k,a[4]=c*k-e*i,a[5]=e*f-c*h,a[6]=f*j-g*i,a[7]=d*i-c*j,a[8]=c*g-d*f,a},l.determinant=function(a){var b=a[0],c=a[1],d=a[2],e=a[3],f=a[4],g=a[5],h=a[6],i=a[7],j=a[8];return b*(j*f-g*i)+c*(-j*e+g*h)+d*(i*e-f*h)},l.multiply=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3],h=b[4],i=b[5],j=b[6],k=b[7],l=b[8],m=c[0],n=c[1],o=c[2],p=c[3],q=c[4],r=c[5],s=c[6],t=c[7],u=c[8];return a[0]=m*d+n*g+o*j,a[1]=m*e+n*h+o*k,a[2]=m*f+n*i+o*l,a[3]=p*d+q*g+r*j,a[4]=p*e+q*h+r*k,a[5]=p*f+q*i+r*l,a[6]=s*d+t*g+u*j,a[7]=s*e+t*h+u*k,a[8]=s*f+t*i+u*l,a},l.mul=l.multiply,l.translate=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3],h=b[4],i=b[5],j=b[6],k=b[7],l=b[8],m=c[0],n=c[1];return a[0]=d,a[1]=e,a[2]=f,a[3]=g,a[4]=h,a[5]=i,a[6]=m*d+n*g+j,a[7]=m*e+n*h+k,a[8]=m*f+n*i+l,a},l.rotate=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3],h=b[4],i=b[5],j=b[6],k=b[7],l=b[8],m=Math.sin(c),n=Math.cos(c);return a[0]=n*d+m*g,a[1]=n*e+m*h,a[2]=n*f+m*i,a[3]=n*g-m*d,a[4]=n*h-m*e,a[5]=n*i-m*f,a[6]=j,a[7]=k,a[8]=l,a},l.scale=function(a,b,c){var d=c[0],e=c[1];return a[0]=d*b[0],a[1]=d*b[1],a[2]=d*b[2],a[3]=e*b[3],a[4]=e*b[4],a[5]=e*b[5],a[6]=b[6],a[7]=b[7],a[8]=b[8],a},l.fromMat2d=function(a,b){return a[0]=b[0],a[1]=b[1],a[2]=0,a[3]=b[2],a[4]=b[3],a[5]=0,a[6]=b[4],a[7]=b[5],a[8]=1,a},l.fromQuat=function(a,b){var c=b[0],d=b[1],e=b[2],f=b[3],g=c+c,h=d+d,i=e+e,j=c*g,k=d*g,l=d*h,m=e*g,n=e*h,o=e*i,p=f*g,q=f*h,r=f*i;return a[0]=1-l-o,a[3]=k-r,a[6]=m+q,a[1]=k+r,a[4]=1-j-o,a[7]=n-p,a[2]=m-q,a[5]=n+p,a[8]=1-j-l,a},l.normalFromMat4=function(a,b){var c=b[0],d=b[1],e=b[2],f=b[3],g=b[4],h=b[5],i=b[6],j=b[7],k=b[8],l=b[9],m=b[10],n=b[11],o=b[12],p=b[13],q=b[14],r=b[15],s=c*h-d*g,t=c*i-e*g,u=c*j-f*g,v=d*i-e*h,w=d*j-f*h,x=e*j-f*i,y=k*p-l*o,z=k*q-m*o,A=k*r-n*o,B=l*q-m*p,C=l*r-n*p,D=m*r-n*q,E=s*D-t*C+u*B+v*A-w*z+x*y;return E?(E=1/E,a[0]=(h*D-i*C+j*B)*E,a[1]=(i*A-g*D-j*z)*E,a[2]=(g*C-h*A+j*y)*E,a[3]=(e*C-d*D-f*B)*E,a[4]=(c*D-e*A+f*z)*E,a[5]=(d*A-c*C-f*y)*E,a[6]=(p*x-q*w+r*v)*E,a[7]=(q*u-o*x-r*t)*E,a[8]=(o*w-p*u+r*s)*E,a):null},l.str=function(a){return"mat3("+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+", "+a[4]+", "+a[5]+", "+a[6]+", "+a[7]+", "+a[8]+")"},"undefined"!=typeof a&&(a.mat3=l);var m={};m.create=function(){var a=new c(16);return a[0]=1,a[1]=0,a[2]=0,a[3]=0,a[4]=0,a[5]=1,a[6]=0,a[7]=0,a[8]=0,a[9]=0,a[10]=1,a[11]=0,a[12]=0,a[13]=0,a[14]=0,a[15]=1,a},m.clone=function(a){var b=new c(16);return b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b[4]=a[4],b[5]=a[5],b[6]=a[6],b[7]=a[7],b[8]=a[8],b[9]=a[9],b[10]=a[10],b[11]=a[11],b[12]=a[12],b[13]=a[13],b[14]=a[14],b[15]=a[15],b},m.copy=function(a,b){return a[0]=b[0],a[1]=b[1],a[2]=b[2],a[3]=b[3],a[4]=b[4],a[5]=b[5],a[6]=b[6],a[7]=b[7],a[8]=b[8],a[9]=b[9],a[10]=b[10],a[11]=b[11],a[12]=b[12],a[13]=b[13],a[14]=b[14],a[15]=b[15],a},m.identity=function(a){return a[0]=1,a[1]=0,a[2]=0,a[3]=0,a[4]=0,a[5]=1,a[6]=0,a[7]=0,a[8]=0,a[9]=0,a[10]=1,a[11]=0,a[12]=0,a[13]=0,a[14]=0,a[15]=1,a},m.transpose=function(a,b){if(a===b){var c=b[1],d=b[2],e=b[3],f=b[6],g=b[7],h=b[11];a[1]=b[4],a[2]=b[8],a[3]=b[12],a[4]=c,a[6]=b[9],a[7]=b[13],a[8]=d,a[9]=f,a[11]=b[14],a[12]=e,a[13]=g,a[14]=h}else a[0]=b[0],a[1]=b[4],a[2]=b[8],a[3]=b[12],a[4]=b[1],a[5]=b[5],a[6]=b[9],a[7]=b[13],a[8]=b[2],a[9]=b[6],a[10]=b[10],a[11]=b[14],a[12]=b[3],a[13]=b[7],a[14]=b[11],a[15]=b[15];return a},m.invert=function(a,b){var c=b[0],d=b[1],e=b[2],f=b[3],g=b[4],h=b[5],i=b[6],j=b[7],k=b[8],l=b[9],m=b[10],n=b[11],o=b[12],p=b[13],q=b[14],r=b[15],s=c*h-d*g,t=c*i-e*g,u=c*j-f*g,v=d*i-e*h,w=d*j-f*h,x=e*j-f*i,y=k*p-l*o,z=k*q-m*o,A=k*r-n*o,B=l*q-m*p,C=l*r-n*p,D=m*r-n*q,E=s*D-t*C+u*B+v*A-w*z+x*y;return E?(E=1/E,a[0]=(h*D-i*C+j*B)*E,a[1]=(e*C-d*D-f*B)*E,a[2]=(p*x-q*w+r*v)*E,a[3]=(m*w-l*x-n*v)*E,a[4]=(i*A-g*D-j*z)*E,a[5]=(c*D-e*A+f*z)*E,a[6]=(q*u-o*x-r*t)*E,a[7]=(k*x-m*u+n*t)*E,a[8]=(g*C-h*A+j*y)*E,a[9]=(d*A-c*C-f*y)*E,a[10]=(o*w-p*u+r*s)*E,a[11]=(l*u-k*w-n*s)*E,a[12]=(h*z-g*B-i*y)*E,a[13]=(c*B-d*z+e*y)*E,a[14]=(p*t-o*v-q*s)*E,a[15]=(k*v-l*t+m*s)*E,a):null},m.adjoint=function(a,b){var c=b[0],d=b[1],e=b[2],f=b[3],g=b[4],h=b[5],i=b[6],j=b[7],k=b[8],l=b[9],m=b[10],n=b[11],o=b[12],p=b[13],q=b[14],r=b[15];return a[0]=h*(m*r-n*q)-l*(i*r-j*q)+p*(i*n-j*m),a[1]=-(d*(m*r-n*q)-l*(e*r-f*q)+p*(e*n-f*m)),a[2]=d*(i*r-j*q)-h*(e*r-f*q)+p*(e*j-f*i),a[3]=-(d*(i*n-j*m)-h*(e*n-f*m)+l*(e*j-f*i)),a[4]=-(g*(m*r-n*q)-k*(i*r-j*q)+o*(i*n-j*m)),a[5]=c*(m*r-n*q)-k*(e*r-f*q)+o*(e*n-f*m),a[6]=-(c*(i*r-j*q)-g*(e*r-f*q)+o*(e*j-f*i)),a[7]=c*(i*n-j*m)-g*(e*n-f*m)+k*(e*j-f*i),a[8]=g*(l*r-n*p)-k*(h*r-j*p)+o*(h*n-j*l),a[9]=-(c*(l*r-n*p)-k*(d*r-f*p)+o*(d*n-f*l)),a[10]=c*(h*r-j*p)-g*(d*r-f*p)+o*(d*j-f*h),a[11]=-(c*(h*n-j*l)-g*(d*n-f*l)+k*(d*j-f*h)),a[12]=-(g*(l*q-m*p)-k*(h*q-i*p)+o*(h*m-i*l)),a[13]=c*(l*q-m*p)-k*(d*q-e*p)+o*(d*m-e*l),a[14]=-(c*(h*q-i*p)-g*(d*q-e*p)+o*(d*i-e*h)),a[15]=c*(h*m-i*l)-g*(d*m-e*l)+k*(d*i-e*h),a},m.determinant=function(a){var b=a[0],c=a[1],d=a[2],e=a[3],f=a[4],g=a[5],h=a[6],i=a[7],j=a[8],k=a[9],l=a[10],m=a[11],n=a[12],o=a[13],p=a[14],q=a[15],r=b*g-c*f,s=b*h-d*f,t=b*i-e*f,u=c*h-d*g,v=c*i-e*g,w=d*i-e*h,x=j*o-k*n,y=j*p-l*n,z=j*q-m*n,A=k*p-l*o,B=k*q-m*o,C=l*q-m*p;return r*C-s*B+t*A+u*z-v*y+w*x},m.multiply=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3],h=b[4],i=b[5],j=b[6],k=b[7],l=b[8],m=b[9],n=b[10],o=b[11],p=b[12],q=b[13],r=b[14],s=b[15],t=c[0],u=c[1],v=c[2],w=c[3];return a[0]=t*d+u*h+v*l+w*p,a[1]=t*e+u*i+v*m+w*q,a[2]=t*f+u*j+v*n+w*r,a[3]=t*g+u*k+v*o+w*s,t=c[4],u=c[5],v=c[6],w=c[7],a[4]=t*d+u*h+v*l+w*p,a[5]=t*e+u*i+v*m+w*q,a[6]=t*f+u*j+v*n+w*r,a[7]=t*g+u*k+v*o+w*s,t=c[8],u=c[9],v=c[10],w=c[11],a[8]=t*d+u*h+v*l+w*p,a[9]=t*e+u*i+v*m+w*q,a[10]=t*f+u*j+v*n+w*r,a[11]=t*g+u*k+v*o+w*s,t=c[12],u=c[13],v=c[14],w=c[15],a[12]=t*d+u*h+v*l+w*p,a[13]=t*e+u*i+v*m+w*q,a[14]=t*f+u*j+v*n+w*r,a[15]=t*g+u*k+v*o+w*s,a},m.mul=m.multiply,m.translate=function(a,b,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t=c[0],u=c[1],v=c[2];return d=b[0],e=b[1],f=b[2],g=b[3],h=b[4],i=b[5],j=b[6],k=b[7],l=b[8],m=b[9],n=b[10],o=b[11],p=b[12],q=b[13],r=b[14],s=b[15],a[0]=d+g*t,a[1]=e+g*u,a[2]=f+g*v,a[3]=g,a[4]=h+k*t,a[5]=i+k*u,a[6]=j+k*v,a[7]=k,a[8]=l+o*t,a[9]=m+o*u,a[10]=n+o*v,a[11]=o,a[12]=p+s*t,a[13]=q+s*u,a[14]=r+s*v,a[15]=s,a},m.scale=function(a,b,c){var d=c[0],e=c[1],f=c[2];return a[0]=b[0]*d,a[1]=b[1]*d,a[2]=b[2]*d,a[3]=b[3]*d,a[4]=b[4]*e,a[5]=b[5]*e,a[6]=b[6]*e,a[7]=b[7]*e,a[8]=b[8]*f,a[9]=b[9]*f,a[10]=b[10]*f,a[11]=b[11]*f,a[12]=b[12],a[13]=b[13],a[14]=b[14],a[15]=b[15],a},m.rotate=function(a,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D=e[0],E=e[1],F=e[2],G=Math.sqrt(D*D+E*E+F*F);return Math.abs(G)g?(h.cross(a,b,e),h.length(a)<1e-6&&h.cross(a,c,e),h.normalize(a,a),n.setAxisAngle(d,a,Math.PI),d):g>.999999?(d[0]=0,d[1]=0,d[2]=0,d[3]=1,d):(h.cross(a,e,f),d[0]=a[0],d[1]=a[1],d[2]=a[2],d[3]=1+g,n.normalize(d,d))}}(),n.setAxes=function(){var a=l.create();return function(b,c,d,e){return a[0]=d[0],a[3]=d[1],a[6]=d[2],a[1]=e[0],a[4]=e[1],a[7]=e[2],a[2]=-c[0],a[5]=-c[1],a[8]=-c[2],n.normalize(b,n.fromMat3(b,a))}}(),n.clone=i.clone,n.fromValues=i.fromValues,n.copy=i.copy,n.set=i.set,n.identity=function(a){return a[0]=0,a[1]=0,a[2]=0,a[3]=1,a},n.setAxisAngle=function(a,b,c){c=.5*c;var d=Math.sin(c);return a[0]=d*b[0],a[1]=d*b[1],a[2]=d*b[2],a[3]=Math.cos(c),a},n.add=i.add,n.multiply=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3],h=c[0],i=c[1],j=c[2],k=c[3];return a[0]=d*k+g*h+e*j-f*i,a[1]=e*k+g*i+f*h-d*j,a[2]=f*k+g*j+d*i-e*h,a[3]=g*k-d*h-e*i-f*j,a},n.mul=n.multiply,n.scale=i.scale,n.rotateX=function(a,b,c){c*=.5;var d=b[0],e=b[1],f=b[2],g=b[3],h=Math.sin(c),i=Math.cos(c);return a[0]=d*i+g*h,a[1]=e*i+f*h,a[2]=f*i-e*h,a[3]=g*i-d*h,a},n.rotateY=function(a,b,c){c*=.5;var d=b[0],e=b[1],f=b[2],g=b[3],h=Math.sin(c),i=Math.cos(c);return a[0]=d*i-f*h,a[1]=e*i+g*h,a[2]=f*i+d*h,a[3]=g*i-e*h,a},n.rotateZ=function(a,b,c){c*=.5;var d=b[0],e=b[1],f=b[2],g=b[3],h=Math.sin(c),i=Math.cos(c);return a[0]=d*i+e*h,a[1]=e*i-d*h,a[2]=f*i+g*h,a[3]=g*i-f*h,a},n.calculateW=function(a,b){var c=b[0],d=b[1],e=b[2];return a[0]=c,a[1]=d,a[2]=e,a[3]=-Math.sqrt(Math.abs(1-c*c-d*d-e*e)),a},n.dot=i.dot,n.lerp=i.lerp,n.slerp=function(a,b,c,d){var e,f,g,h,i,j=b[0],k=b[1],l=b[2],m=b[3],n=c[0],o=c[1],p=c[2],q=c[3];return f=j*n+k*o+l*p+m*q,0>f&&(f=-f,n=-n,o=-o,p=-p,q=-q),1-f>1e-6?(e=Math.acos(f),g=Math.sin(e),h=Math.sin((1-d)*e)/g,i=Math.sin(d*e)/g):(h=1-d,i=d),a[0]=h*j+i*n,a[1]=h*k+i*o,a[2]=h*l+i*p,a[3]=h*m+i*q,a},n.invert=function(a,b){var c=b[0],d=b[1],e=b[2],f=b[3],g=c*c+d*d+e*e+f*f,h=g?1/g:0;return a[0]=-c*h,a[1]=-d*h,a[2]=-e*h,a[3]=f*h,a},n.conjugate=function(a,b){return a[0]=-b[0],a[1]=-b[1],a[2]=-b[2],a[3]=b[3],a},n.length=i.length,n.len=n.length,n.squaredLength=i.squaredLength,n.sqrLen=n.squaredLength,n.normalize=i.normalize,n.fromMat3=function(a,b){var c,d=b[0]+b[4]+b[8];if(d>0)c=Math.sqrt(d+1),a[3]=.5*c,c=.5/c,a[0]=(b[7]-b[5])*c,a[1]=(b[2]-b[6])*c,a[2]=(b[3]-b[1])*c;else{var e=0;b[4]>b[0]&&(e=1),b[8]>b[3*e+e]&&(e=2);var f=(e+1)%3,g=(e+2)%3;c=Math.sqrt(b[3*e+e]-b[3*f+f]-b[3*g+g]+1),a[e]=.5*c,c=.5/c,a[3]=(b[3*g+f]-b[3*f+g])*c,a[f]=(b[3*f+e]+b[3*e+f])*c,a[g]=(b[3*g+e]+b[3*e+g])*c}return a},n.str=function(a){return"quat("+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+")"},"undefined"!=typeof a&&(a.quat=n)}(b.exports)}(this),define("qtek/math/Vector3",["require","../dep/glmatrix"],function(a){function b(a,b,c){return b>a?b:a>c?c:a}var c=a("../dep/glmatrix"),d=c.vec3,e=function(a,b,c){a=a||0,b=b||0,c=c||0,this._array=d.fromValues(a,b,c),this._dirty=!0};if(e.prototype={constructor:e,add:function(a){return d.add(this._array,this._array,a._array),this._dirty=!0,this},set:function(a,b,c){return this._array[0]=a,this._array[1]=b,this._array[2]=c,this._dirty=!0,this},setArray:function(a){return this._array[0]=a[0],this._array[1]=a[1],this._array[2]=a[2],this._dirty=!0,this},clone:function(){return new e(this.x,this.y,this.z)},copy:function(a){return d.copy(this._array,a._array),this._dirty=!0,this},cross:function(a,b){return d.cross(this._array,a._array,b._array),this._dirty=!0,this},dist:function(a){return d.dist(this._array,a._array)},distance:function(a){return d.distance(this._array,a._array)},div:function(a){return d.div(this._array,this._array,a._array),this._dirty=!0,this},divide:function(a){return d.divide(this._array,this._array,a._array),this._dirty=!0,this},dot:function(a){return d.dot(this._array,a._array)},len:function(){return d.len(this._array)},length:function(){return d.length(this._array)},lerp:function(a,b,c){return d.lerp(this._array,a._array,b._array,c),this._dirty=!0,this},min:function(a){return d.min(this._array,this._array,a._array),this._dirty=!0,this},max:function(a){return d.max(this._array,this._array,a._array),this._dirty=!0,this},mul:function(a){return d.mul(this._array,this._array,a._array),this._dirty=!0,this},multiply:function(a){return d.multiply(this._array,this._array,a._array),this._dirty=!0,this},negate:function(){return d.negate(this._array,this._array),this._dirty=!0,this},normalize:function(){return d.normalize(this._array,this._array),this._dirty=!0,this},random:function(a){return d.random(this._array,a),this._dirty=!0,this},scale:function(a){return d.scale(this._array,this._array,a),this._dirty=!0,this},scaleAndAdd:function(a,b){return d.scaleAndAdd(this._array,this._array,a._array,b),this._dirty=!0,this},sqrDist:function(a){return d.sqrDist(this._array,a._array)},squaredDistance:function(a){return d.squaredDistance(this._array,a._array) -},sqrLen:function(){return d.sqrLen(this._array)},squaredLength:function(){return d.squaredLength(this._array)},sub:function(a){return d.sub(this._array,this._array,a._array),this._dirty=!0,this},subtract:function(a){return d.subtract(this._array,this._array,a._array),this._dirty=!0,this},transformMat3:function(a){return d.transformMat3(this._array,this._array,a._array),this._dirty=!0,this},transformMat4:function(a){return d.transformMat4(this._array,this._array,a._array),this._dirty=!0,this},transformQuat:function(a){return d.transformQuat(this._array,this._array,a._array),this._dirty=!0,this},applyProjection:function(a){var b=this._array;if(a=a._array,0===a[15]){var c=-1/b[2];b[0]=a[0]*b[0]*c,b[1]=a[5]*b[1]*c,b[2]=(a[10]*b[2]+a[14])*c}else b[0]=a[0]*b[0]+a[12],b[1]=a[5]*b[1]+a[13],b[2]=a[10]*b[2]+a[14];return this._dirty=!0,this},eulerFromQuaternion:function(a,b){e.eulerFromQuaternion(this,a,b)},toString:function(){return"["+Array.prototype.join.call(this._array,",")+"]"}},Object.defineProperty){var f=e.prototype;Object.defineProperty(f,"x",{get:function(){return this._array[0]},set:function(a){this._array[0]=a,this._dirty=!0}}),Object.defineProperty(f,"y",{get:function(){return this._array[1]},set:function(a){this._array[1]=a,this._dirty=!0}}),Object.defineProperty(f,"z",{get:function(){return this._array[2]},set:function(a){this._array[2]=a,this._dirty=!0}})}return e.add=function(a,b,c){return d.add(a._array,b._array,c._array),a._dirty=!0,a},e.set=function(a,b,c,e){d.set(a._array,b,c,e),a._dirty=!0},e.copy=function(a,b){return d.copy(a._array,b._array),a._dirty=!0,a},e.cross=function(a,b,c){return d.cross(a._array,b._array,c._array),a._dirty=!0,a},e.dist=function(a,b){return d.distance(a._array,b._array)},e.distance=e.dist,e.div=function(a,b,c){return d.divide(a._array,b._array,c._array),a._dirty=!0,a},e.divide=e.div,e.dot=function(a,b){return d.dot(a._array,b._array)},e.len=function(a){return d.length(a._array)},e.lerp=function(a,b,c,e){return d.lerp(a._array,b._array,c._array,e),a._dirty=!0,a},e.min=function(a,b,c){return d.min(a._array,b._array,c._array),a._dirty=!0,a},e.max=function(a,b,c){return d.max(a._array,b._array,c._array),a._dirty=!0,a},e.mul=function(a,b,c){return d.multiply(a._array,b._array,c._array),a._dirty=!0,a},e.multiply=e.mul,e.negate=function(a,b){return d.negate(a._array,b._array),a._dirty=!0,a},e.normalize=function(a,b){return d.normalize(a._array,b._array),a._dirty=!0,a},e.random=function(a,b){return d.random(a._array,b),a._dirty=!0,a},e.scale=function(a,b,c){return d.scale(a._array,b._array,c),a._dirty=!0,a},e.scaleAndAdd=function(a,b,c,e){return d.scaleAndAdd(a._array,b._array,c._array,e),a._dirty=!0,a},e.sqrDist=function(a,b){return d.sqrDist(a._array,b._array)},e.squaredDistance=e.sqrDist,e.sqrLen=function(a){return d.sqrLen(a._array)},e.squaredLength=e.sqrLen,e.sub=function(a,b,c){return d.subtract(a._array,b._array,c._array),a._dirty=!0,a},e.subtract=e.sub,e.transformMat3=function(a,b,c){return d.transformMat3(a._array,b._array,c._array),a._dirty=!0,a},e.transformMat4=function(a,b,c){return d.transformMat4(a._array,b._array,c._array),a._dirty=!0,a},e.transformQuat=function(a,b,c){return d.transformQuat(a._array,b._array,c._array),a._dirty=!0,a},e.eulerFromQuaternion=function(a,c,d){a=a._array,c=c._array;var e=c[0],f=c[1],g=c[2],h=c[3],i=e*e,j=f*f,k=g*g,l=h*h,m=Math.atan2,n=Math.asin;switch(d&&d.toUpperCase()){case"YXZ":a[0]=n(b(2*(e*h-f*g),-1,1)),a[1]=m(2*(e*g+f*h),l-i-j+k),a[2]=m(2*(e*f+g*h),l-i+j-k);break;case"ZXY":a[0]=n(b(2*(e*h+f*g),-1,1)),a[1]=m(2*(f*h-g*e),l-i-j+k),a[2]=m(2*(g*h-e*f),l-i+j-k);break;case"ZYX":a[0]=m(2*(e*h+g*f),l-i-j+k),a[1]=n(b(2*(f*h-e*g),-1,1)),a[2]=m(2*(e*f+g*h),l+i-j-k);break;case"YZX":a[0]=m(2*(e*h-g*f),l-i+j-k),a[1]=m(2*(f*h-e*g),l+i-j-k),a[2]=n(b(2*(e*f+g*h),-1,1));break;case"XZY":a[0]=m(2*(e*h+f*g),l-i+j-k),a[1]=m(2*(e*g+f*h),l+i-j-k),a[2]=n(b(2*(g*h-e*f),-1,1));break;case"XYZ":default:a[0]=m(2*(e*h-f*g),l-i-j+k),a[1]=n(b(2*(e*g+f*h),-1,1)),a[2]=m(2*(g*h-e*f),l+i-j-k)}return a._dirty=!0,a},e.POSITIVE_X=new e(1,0,0),e.NEGATIVE_X=new e(-1,0,0),e.POSITIVE_Y=new e(0,1,0),e.NEGATIVE_Y=new e(0,-1,0),e.POSITIVE_Z=new e(0,0,1),e.NEGATIVE_Z=new e(0,0,-1),e.UP=new e(0,1,0),e.ZERO=new e(0,0,0),e}),define("qtek/math/Quaternion",["require","../dep/glmatrix"],function(a){var b=a("../dep/glmatrix"),c=b.quat,d=function(a,b,d,e){a=a||0,b=b||0,d=d||0,e=void 0===e?1:e,this._array=c.fromValues(a,b,d,e),this._dirty=!0};if(d.prototype={constructor:d,add:function(a){return c.add(this._array,this._array,a._array),this._dirty=!0,this},calculateW:function(){return c.calculateW(this._array,this._array),this._dirty=!0,this},set:function(a,b,c,d){return this._array[0]=a,this._array[1]=b,this._array[2]=c,this._array[3]=d,this._dirty=!0,this},setArray:function(a){return this._array[0]=a[0],this._array[1]=a[1],this._array[2]=a[2],this._array[3]=a[3],this._dirty=!0,this},clone:function(){return new d(this.x,this.y,this.z,this.w)},conjugate:function(){return c.conjugate(this._array,this._array),this._dirty=!0,this},copy:function(a){return c.copy(this._array,a._array),this._dirty=!0,this},dot:function(a){return c.dot(this._array,a._array)},fromMat3:function(a){return c.fromMat3(this._array,a._array),this._dirty=!0,this},fromMat4:function(){var a=b.mat3,d=a.create();return function(b){return a.fromMat4(d,b._array),a.transpose(d,d),c.fromMat3(this._array,d),this._dirty=!0,this}}(),identity:function(){return c.identity(this._array),this._dirty=!0,this},invert:function(){return c.invert(this._array,this._array),this._dirty=!0,this},len:function(){return c.len(this._array)},length:function(){return c.length(this._array)},lerp:function(a,b,d){return c.lerp(this._array,a._array,b._array,d),this._dirty=!0,this},mul:function(a){return c.mul(this._array,this._array,a._array),this._dirty=!0,this},mulLeft:function(a){return c.multiply(this._array,a._array,this._array),this._dirty=!0,this},multiply:function(a){return c.multiply(this._array,this._array,a._array),this._dirty=!0,this},multiplyLeft:function(a){return c.multiply(this._array,a._array,this._array),this._dirty=!0,this},normalize:function(){return c.normalize(this._array,this._array),this._dirty=!0,this},rotateX:function(a){return c.rotateX(this._array,this._array,a),this._dirty=!0,this},rotateY:function(a){return c.rotateY(this._array,this._array,a),this._dirty=!0,this},rotateZ:function(a){return c.rotateZ(this._array,this._array,a),this._dirty=!0,this},rotationTo:function(a,b){return c.rotationTo(this._array,a._array,b._array),this._dirty=!0,this},setAxes:function(a,b,d){return c.setAxes(this._array,a._array,b._array,d._array),this._dirty=!0,this},setAxisAngle:function(a,b){return c.setAxisAngle(this._array,a._array,b),this._dirty=!0,this},slerp:function(a,b,d){return c.slerp(this._array,a._array,b._array,d),this._dirty=!0,this},sqrLen:function(){return c.sqrLen(this._array)},squaredLength:function(){return c.squaredLength(this._array)},setFromEuler:function(){},toString:function(){return"["+Array.prototype.join.call(this._array,",")+"]"}},Object.defineProperty){var e=d.prototype;Object.defineProperty(e,"x",{get:function(){return this._array[0]},set:function(a){this._array[0]=a,this._dirty=!0}}),Object.defineProperty(e,"y",{get:function(){return this._array[1]},set:function(a){this._array[1]=a,this._dirty=!0}}),Object.defineProperty(e,"z",{get:function(){return this._array[2]},set:function(a){this._array[2]=a,this._dirty=!0}}),Object.defineProperty(e,"w",{get:function(){return this._array[3]},set:function(a){this._array[3]=a,this._dirty=!0}})}return d.add=function(a,b,d){return c.add(a._array,b._array,d._array),a._dirty=!0,a},d.set=function(a,b,d,e,f){c.set(a._array,b,d,e,f),a._dirty=!0},d.copy=function(a,b){return c.copy(a._array,b._array),a._dirty=!0,a},d.calculateW=function(a,b){return c.calculateW(a._array,b._array),a._dirty=!0,a},d.conjugate=function(a,b){return c.conjugate(a._array,b._array),a._dirty=!0,a},d.identity=function(a){return c.identity(a._array),a._dirty=!0,a},d.invert=function(a,b){return c.invert(a._array,b._array),a._dirty=!0,a},d.dot=function(a,b){return c.dot(a._array,b._array)},d.len=function(a){return c.length(a._array)},d.lerp=function(a,b,d,e){return c.lerp(a._array,b._array,d._array,e),a._dirty=!0,a},d.slerp=function(a,b,d,e){return c.slerp(a._array,b._array,d._array,e),a._dirty=!0,a},d.mul=function(a,b,d){return c.multiply(a._array,b._array,d._array),a._dirty=!0,a},d.multiply=d.mul,d.rotateX=function(a,b,d){return c.rotateX(a._array,b._array,d),a._dirty=!0,a},d.rotateY=function(a,b,d){return c.rotateY(a._array,b._array,d),a._dirty=!0,a},d.rotateZ=function(a,b,d){return c.rotateZ(a._array,b._array,d),a._dirty=!0,a},d.setAxisAngle=function(a,b,d){return c.setAxisAngle(a._array,b._array,d),a._dirty=!0,a},d.normalize=function(a,b){return c.normalize(a._array,b._array),a._dirty=!0,a},d.sqrLen=function(a){return c.sqrLen(a._array)},d.squaredLength=d.sqrLen,d.fromMat3=function(a,b){return c.fromMat3(a._array,b._array),a._dirty=!0,a},d.setAxes=function(a,b,d,e){return c.setAxes(a._array,b._array,d._array,e._array),a._dirty=!0,a},d.rotationTo=function(a,b,d){return c.rotationTo(a._array,b._array,d._array),a._dirty=!0,a},d}),define("qtek/math/Matrix4",["require","../dep/glmatrix","./Vector3"],function(a){var b=a("../dep/glmatrix"),c=a("./Vector3"),d=b.mat4,e=b.vec3,f=b.mat3,g=b.quat,h=function(){this._axisX=new c,this._axisY=new c,this._axisZ=new c,this._array=d.create(),this._dirty=!0};if(h.prototype={constructor:h,adjoint:function(){return d.adjoint(this._array,this._array),this._dirty=!0,this},clone:function(){return(new h).copy(this)},copy:function(a){return d.copy(this._array,a._array),this._dirty=!0,this},determinant:function(){return d.determinant(this._array)},fromQuat:function(a){return d.fromQuat(this._array,a._array),this._dirty=!0,this},fromRotationTranslation:function(a,b){return d.fromRotationTranslation(this._array,a._array,b._array),this._dirty=!0,this},fromMat2d:function(a){return h.fromMat2d(this,a),this},frustum:function(a,b,c,e,f,g){return d.frustum(this._array,a,b,c,e,f,g),this._dirty=!0,this},identity:function(){return d.identity(this._array),this._dirty=!0,this},invert:function(){return d.invert(this._array,this._array),this._dirty=!0,this},lookAt:function(a,b,c){return d.lookAt(this._array,a._array,b._array,c._array),this._dirty=!0,this},mul:function(a){return d.mul(this._array,this._array,a._array),this._dirty=!0,this},mulLeft:function(a){return d.mul(this._array,a._array,this._array),this._dirty=!0,this},multiply:function(a){return d.multiply(this._array,this._array,a._array),this._dirty=!0,this},multiplyLeft:function(a){return d.multiply(this._array,a._array,this._array),this._dirty=!0,this},ortho:function(a,b,c,e,f,g){return d.ortho(this._array,a,b,c,e,f,g),this._dirty=!0,this},perspective:function(a,b,c,e){return d.perspective(this._array,a,b,c,e),this._dirty=!0,this},rotate:function(a,b){return d.rotate(this._array,this._array,a,b._array),this._dirty=!0,this},rotateX:function(a){return d.rotateX(this._array,this._array,a),this._dirty=!0,this},rotateY:function(a){return d.rotateY(this._array,this._array,a),this._dirty=!0,this},rotateZ:function(a){return d.rotateZ(this._array,this._array,a),this._dirty=!0,this},scale:function(a){return d.scale(this._array,this._array,a._array),this._dirty=!0,this},translate:function(a){return d.translate(this._array,this._array,a._array),this._dirty=!0,this},transpose:function(){return d.transpose(this._array,this._array),this._dirty=!0,this},decomposeMatrix:function(){var a=e.create(),b=e.create(),c=e.create(),d=f.create();return function(h,i,j){var k=this._array;e.set(a,k[0],k[1],k[2]),e.set(b,k[4],k[5],k[6]),e.set(c,k[8],k[9],k[10]);var l=e.length(a),m=e.length(b),n=e.length(c);h&&(h.x=l,h.y=m,h.z=n,h._dirty=!0),j.set(k[12],k[13],k[14]),f.fromMat4(d,k),f.transpose(d,d),d[0]/=l,d[1]/=l,d[2]/=l,d[3]/=m,d[4]/=m,d[5]/=m,d[6]/=n,d[7]/=n,d[8]/=n,g.fromMat3(i._array,d),g.normalize(i._array,i._array),i._dirty=!0,j._dirty=!0}}(),toString:function(){return"["+Array.prototype.join.call(this._array,",")+"]"}},Object.defineProperty){var i=h.prototype;Object.defineProperty(i,"z",{get:function(){var a=this._array;return this._axisZ.set(a[8],a[9],a[10]),this._axisZ},set:function(a){var b=this._array;a=a._array,b[8]=a[0],b[9]=a[1],b[10]=a[2],this._dirty=!0}}),Object.defineProperty(i,"y",{get:function(){var a=this._array;return this._axisY.set(a[4],a[5],a[6]),this._axisY},set:function(a){var b=this._array;a=a._array,b[4]=a[0],b[5]=a[1],b[6]=a[2],this._dirty=!0}}),Object.defineProperty(i,"x",{get:function(){var a=this._array;return this._axisX.set(a[0],a[1],a[2]),this._axisX},set:function(a){var b=this._array;a=a._array,b[0]=a[0],b[1]=a[1],b[2]=a[2],this._dirty=!0}})}return h.adjoint=function(a,b){return d.adjoint(a._array,b._array),a._dirty=!0,a},h.copy=function(a,b){return d.copy(a._array,b._array),a._dirty=!0,a},h.determinant=function(a){return d.determinant(a._array)},h.identity=function(a){return d.identity(a._array),a._dirty=!0,a},h.ortho=function(a,b,c,e,f,g,h){return d.ortho(a._array,b,c,e,f,g,h),a._dirty=!0,a},h.perspective=function(a,b,c,e,f){return d.perspective(a._array,b,c,e,f),a._dirty=!0,a},h.lookAt=function(a,b,c,e){return d.lookAt(a._array,b._array,c._array,e._array),a._dirty=!0,a},h.invert=function(a,b){return d.invert(a._array,b._array),a._dirty=!0,a},h.mul=function(a,b,c){return d.mul(a._array,b._array,c._array),a._dirty=!0,a},h.multiply=h.mul,h.fromQuat=function(a,b){return d.fromQuat(a._array,b._array),a._dirty=!0,a},h.fromRotationTranslation=function(a,b,c){return d.fromRotationTranslation(a._array,b._array,c._array),a._dirty=!0,a},h.fromMat2d=function(a,b){a._dirty=!0;var b=b._array,a=a._array;return a[0]=b[0],a[4]=b[2],a[12]=b[4],a[1]=b[1],a[5]=b[3],a[13]=b[5],a},h.rotate=function(a,b,c,e){return d.rotate(a._array,b._array,c,e._array),a._dirty=!0,a},h.rotateX=function(a,b,c){return d.rotateX(a._array,b._array,c),a._dirty=!0,a},h.rotateY=function(a,b,c){return d.rotateY(a._array,b._array,c),a._dirty=!0,a},h.rotateZ=function(a,b,c){return d.rotateZ(a._array,b._array,c),a._dirty=!0,a},h.scale=function(a,b,c){return d.scale(a._array,b._array,c._array),a._dirty=!0,a},h.transpose=function(a,b){return d.transpose(a._array,b._array),a._dirty=!0,a},h.translate=function(a,b,c){return d.translate(a._array,b._array,c._array),a._dirty=!0,a},h}),define("qtek/Node",["require","./core/Base","./math/Vector3","./math/Quaternion","./math/Matrix4","./dep/glmatrix"],function(a){var b=a("./core/Base"),c=a("./math/Vector3"),d=a("./math/Quaternion"),e=a("./math/Matrix4"),f=a("./dep/glmatrix"),g=f.mat4,h=0,i=b.derive({name:"",position:null,rotation:null,scale:null,worldTransform:null,localTransform:null,autoUpdateLocalTransform:!0,_parent:null,_scene:null,_needsUpdateWorldTransform:!0,_inIterating:!1,__depth:0},function(){this.name||(this.name="NODE_"+h++),this.position||(this.position=new c),this.rotation||(this.rotation=new d),this.scale||(this.scale=new c(1,1,1)),this.worldTransform=new e,this.localTransform=new e,this._children=[]},{visible:!0,isRenderable:function(){return!1},setName:function(a){this._scene&&(delete this._scene._nodeRepository[this.name],this._scene._nodeRepository[a]=this),this.name=a},add:function(a){this._inIterating&&console.warn("Add operation can cause unpredictable error when in iterating"),a._parent!==this&&(a._parent&&a._parent.remove(a),a._parent=this,this._children.push(a),this._scene&&this._scene!==a.scene&&a.traverse(this._addSelfToScene,this))},remove:function(a){this._inIterating&&console.warn("Remove operation can cause unpredictable error when in iterating");var b=this._children.indexOf(a);0>b||(this._children.splice(b,1),a._parent=null,this._scene&&a.traverse(this._removeSelfFromScene,this))},getScene:function(){return this._scene},getParent:function(){return this._parent},_removeSelfFromScene:function(a){a._scene.removeFromScene(a),a._scene=null},_addSelfToScene:function(a){this._scene.addToScene(a),a._scene=this._scene},isAncestor:function(a){for(var b=a._parent;b;){if(b===this)return!0;b=b._parent}return!1},children:function(){return this._children.slice()},childAt:function(a){return this._children[a]},getChildByName:function(a){for(var b=0;be;e++)d[e].traverse(a,b,c);this._inIterating=!1},setLocalTransform:function(a){g.copy(this.localTransform._array,a._array),this.decomposeLocalTransform()},decomposeLocalTransform:function(a){var b=a?null:this.scale;this.localTransform.decomposeMatrix(b,this.rotation,this.position)},setWorldTransform:function(a){g.copy(this.worldTransform._array,a._array),this.decomposeWorldTransform()},decomposeWorldTransform:function(){var a=g.create();return function(b){this._parent?(g.invert(a,this._parent.worldTransform._array),g.multiply(this.localTransform._array,a,this.worldTransform._array)):g.copy(this.localTransform._array,this.worldTransform._array);var c=b?null:this.scale;this.localTransform.decomposeMatrix(c,this.rotation,this.position)}}(),updateLocalTransform:function(){var a=this.position,b=this.rotation,c=this.scale;if(a._dirty||c._dirty||b._dirty){var d=this.localTransform._array;g.fromRotationTranslation(d,b._array,a._array),g.scale(d,d,c._array),b._dirty=!1,c._dirty=!1,a._dirty=!1,this._needsUpdateWorldTransform=!0}},updateWorldTransform:function(){this._parent?g.multiply(this.worldTransform._array,this._parent.worldTransform._array,this.localTransform._array):g.copy(this.worldTransform._array,this.localTransform._array)},update:function(a){this.autoUpdateLocalTransform?this.updateLocalTransform():a=!0,(a||this._needsUpdateWorldTransform)&&(this.updateWorldTransform(),a=!0,this._needsUpdateWorldTransform=!1);for(var b=0,c=this._children.length;c>b;b++)this._children[b].update(a)},getWorldPosition:function(a){var b=this.worldTransform._array;return a?(a._array[0]=b[12],a._array[1]=b[13],a._array[2]=b[14],a):new c(b[12],b[13],b[14])},clone:function(){var a=new this.constructor;a.setName(this.name),a.position.copy(this.position),a.rotation.copy(this.rotation),a.scale.copy(this.scale);for(var b=0;bf;f++)e[f]=d.fromValues(0,0,0);this.vertices=e};return h.prototype={constructor:h,updateFromVertices:function(a){if(a.length>0){var b=this.min._array,c=this.max._array;f(b,a[0]),f(c,a[0]);for(var d=1;dc[0]&&(c[0]=e[0]),e[1]>c[1]&&(c[1]=e[1]),e[2]>c[2]&&(c[2]=e[2])}this.min._dirty=!0,this.max._dirty=!0}},union:function(a){d.min(this.min._array,this.min._array,a.min._array),d.max(this.max._array,this.max._array,a.max._array),this.min._dirty=!0,this.max._dirty=!0},intersectBoundingBox:function(a){var b=this.min._array,c=this.max._array,d=a.min._array,e=a.max._array;return!(b[0]>e[0]||b[1]>e[1]||b[2]>e[2]||c[0]i;i++)h=g[i],e(h,h,b),h[0]d[0]&&(d[0]=h[0]),h[1]>d[1]&&(d[1]=h[1]),h[2]>d[2]&&(d[2]=h[2]);this.min._dirty=!0,this.max._dirty=!0},applyProjection:function(a){(this.min._dirty||this.max._dirty)&&(this.updateVertices(),this.min._dirty=!1,this.max._dirty=!1);var b=a._array,c=this.vertices[0],d=this.vertices[3],e=this.vertices[7],f=this.min._array,g=this.max._array;if(1===b[15])f[0]=b[0]*c[0]+b[12],f[1]=b[5]*c[1]+b[13],g[2]=b[10]*c[2]+b[14],g[0]=b[0]*e[0]+b[12],g[1]=b[5]*e[1]+b[13],f[2]=b[10]*e[2]+b[14];else{var h=-1/c[2];f[0]=b[0]*c[0]*h,f[1]=b[5]*c[1]*h,g[2]=(b[10]*c[2]+b[14])*h,h=-1/d[2],g[0]=b[0]*d[0]*h,g[1]=b[5]*d[1]*h,h=-1/e[2],f[2]=(b[10]*e[2]+b[14])*h}this.min._dirty=!0,this.max._dirty=!0},updateVertices:function(){var a=this.min._array,b=this.max._array,c=this.vertices;g(c[0],a[0],a[1],a[2]),g(c[1],a[0],b[1],a[2]),g(c[2],b[0],a[1],a[2]),g(c[3],b[0],b[1],a[2]),g(c[4],a[0],a[1],b[2]),g(c[5],a[0],b[1],b[2]),g(c[6],b[0],a[1],b[2]),g(c[7],b[0],b[1],b[2])},copy:function(a){f(this.min._array,a.min._array),f(this.max._array,a.max._array),this.min._dirty=!0,this.max._dirty=!0},clone:function(){var a=new h;return a.copy(this),a}},h}),define("qtek/math/Plane",["require","./Vector3","../dep/glmatrix"],function(a){var b=a("./Vector3"),c=a("../dep/glmatrix"),d=c.vec3,e=c.mat4,f=c.vec4,g=function(a,c){this.normal=a||new b(0,1,0),this.distance=c||0};return g.prototype={constructor:g,distanceToPoint:function(a){return d.dot(a._array,this.normal._array)-this.distance},projectPoint:function(a,c){c||(c=new b);var e=this.distanceToPoint(a);return d.scaleAndAdd(c._array,a._array,this.normal._array,-e),c._dirty=!0,c},normalize:function(){var a=1/d.len(this.normal._array);d.scale(this.normal._array,a),this.distance*=a},intersectFrustum:function(a){for(var b=a.vertices,c=this.normal._array,e=d.dot(b[0]._array,c)>this.distance,f=1;8>f;f++)if(d.dot(b[f]._array,c)>this.distance!=e)return!0},intersectLine:function(){var a=d.create();return function(c,e,f){var g=this.distanceToPoint(c),h=this.distanceToPoint(e);if(g>0&&h>0||0>g&&0>h)return null;var i=this.normal._array,j=this.distance,k=c._array;d.sub(a,e._array,c._array),d.normalize(a,a);var l=d.dot(i,a);if(0===l)return null;f||(f=new b);var m=(d.dot(i,k)-j)/l;return d.scaleAndAdd(f._array,k,a,-m),f._dirty=!0,f}}(),applyTransform:function(){var a=e.create(),b=f.create(),c=f.create();return c[3]=1,function(g){g=g._array,d.scale(c,this.normal._array,this.distance),f.transformMat4(c,c,g),this.distance=d.dot(c,this.normal._array),e.invert(a,g),e.transpose(a,a),b[3]=0,d.copy(b,this.normal._array),f.transformMat4(b,b,a),d.copy(this.normal._array,b)}}(),copy:function(a){d.copy(this.normal._array,a.normal._array),this.normal._dirty=!0,this.distance=a.distance},clone:function(){var a=new g;return a.copy(this),a}},g}),define("qtek/math/Frustum",["require","./Vector3","./BoundingBox","./Plane","../dep/glmatrix"],function(a){a("./Vector3");var b=a("./BoundingBox"),c=a("./Plane"),d=a("../dep/glmatrix"),e=d.vec3,f=function(){this.planes=[];for(var a=0;6>a;a++)this.planes.push(new c);this.boundingBox=new b,this.vertices=[];for(var a=0;8>a;a++)this.vertices[a]=e.fromValues(0,0,0)};return f.prototype={setFromProjection:function(a){var b=this.planes,c=a._array,d=c[0],f=c[1],g=c[2],h=c[3],i=c[4],j=c[5],k=c[6],l=c[7],m=c[8],n=c[9],o=c[10],p=c[11],q=c[12],r=c[13],s=c[14],t=c[15];if(e.set(b[0].normal._array,h-d,l-i,p-m),b[0].distance=-(t-q),b[0].normalize(),e.set(b[1].normal._array,h+d,l+i,p+m),b[1].distance=-(t+q),b[1].normalize(),e.set(b[2].normal._array,h+f,l+j,p+n),b[2].distance=-(t+r),b[2].normalize(),e.set(b[3].normal._array,h-f,l-j,p-n),b[3].distance=-(t-r),b[3].normalize(),e.set(b[4].normal._array,h-g,l-k,p-o),b[4].distance=-(t-s),b[4].normalize(),e.set(b[5].normal._array,h+g,l+k,p+o),b[5].distance=-(t+s),b[5].normalize(),0===t){var u=j/d,v=-s/(o-1),w=-s/(o+1),x=-w/j,y=-v/j;this.boundingBox.min.set(-x*u,-x,w),this.boundingBox.max.set(x*u,x,v);var z=this.vertices;e.set(z[0],-x*u,-x,w),e.set(z[1],-x*u,x,w),e.set(z[2],x*u,-x,w),e.set(z[3],x*u,x,w),e.set(z[4],-y*u,-y,v),e.set(z[5],-y*u,y,v),e.set(z[6],y*u,-y,v),e.set(z[7],y*u,y,v)}else{var A=(-1-q)/d,B=(1-q)/d,C=(1-r)/j,D=(-1-r)/j,E=(-1-s)/o,F=(1-s)/o;this.boundingBox.min.set(A,D,F),this.boundingBox.max.set(B,C,E);for(var G=0;8>G;G++)e.copy(this.vertices[G],this.boundingBox.vertices[G])}},getTransformedBoundingBox:function(){var a=e.create();return function(b,c){var d=this.vertices,f=c._array,g=b.min._array,h=b.max._array,i=d[0];e.transformMat4(a,i,f),e.copy(g,a),e.copy(h,a);for(var j=1;8>j;j++)i=d[j],e.transformMat4(a,i,f),g[0]=Math.min(a[0],g[0]),g[1]=Math.min(a[1],g[1]),g[2]=Math.min(a[2],g[2]),h[0]=Math.max(a[0],h[0]),h[1]=Math.max(a[1],h[1]),h[2]=Math.max(a[2],h[2]);return b.min._dirty=!0,b.max._dirty=!0,b}}()},f}),define("qtek/math/Ray",["require","./Vector3","../dep/glmatrix"],function(a){var b=a("./Vector3"),c=a("../dep/glmatrix"),d=c.vec3,e=1e-5,f=function(a,c){this.origin=a||new b,this.direction=c||new b};return f.prototype={constructor:f,intersectPlane:function(a,c){var e=a.normal._array,f=a.distance,g=this.origin._array,h=this.direction._array,i=d.dot(e,h);if(0===i)return null;c||(c=new b);var j=(d.dot(e,g)-f)/i;return d.scaleAndAdd(c._array,g,h,-j),c._dirty=!0,c},mirrorAgainstPlane:function(a){var b=d.dot(a.normal._array,this.direction._array);d.scaleAndAdd(this.direction._array,this.direction._array,a.normal._array,2*-b),this.direction._dirty=!0},distanceToPoint:function(){var a=d.create();return function(b){d.sub(a,b,this.origin._array);var c=d.dot(a,this.direction._array);if(0>c)return d.distance(this.origin._array,b);var e=d.lenSquared(a);return Math.sqrt(e-c*c)}}(),intersectSphere:function(){var a=d.create();return function(c,e,f){var g=this.origin._array,h=this.direction._array;c=c._array,d.sub(a,c,g);var i=d.dot(a,h),j=d.squaredLength(a),k=j-i*i,l=e*e;if(!(k>l)){var m=Math.sqrt(l-k),n=i-m,o=i+m;return f||(f=new b),0>n?0>o?null:(d.scaleAndAdd(f._array,g,h,o),f):(d.scaleAndAdd(f._array,g,h,n),f)}}}(),intersectBoundingBox:function(a,c){var e,f,g,h,i,j,k=this.direction._array,l=this.origin._array,m=a.min._array,n=a.max._array,o=1/k[0],p=1/k[1],q=1/k[2];if(o>=0?(e=(m[0]-l[0])*o,f=(n[0]-l[0])*o):(f=(m[0]-l[0])*o,e=(n[0]-l[0])*o),p>=0?(g=(m[1]-l[1])*p,h=(n[1]-l[1])*p):(h=(m[1]-l[1])*p,g=(n[1]-l[1])*p),e>h||g>f)return null;if((g>e||e!==e)&&(e=g),(f>h||f!==f)&&(f=h),q>=0?(i=(m[2]-l[2])*q,j=(n[2]-l[2])*q):(j=(m[2]-l[2])*q,i=(n[2]-l[2])*q),e>j||i>f)return null;if((i>e||e!==e)&&(e=i),(f>j||f!==f)&&(f=j),0>f)return null;var r=e>=0?e:f;return c||(c=new b),d.scaleAndAdd(c._array,l,k,r),c},intersectTriangle:function(){var a=d.create(),c=d.create(),f=d.create(),g=d.create();return function(h,i,j,k,l,m){var n=this.direction._array,o=this.origin._array;h=h._array,i=i._array,j=j._array,d.sub(a,i,h),d.sub(c,j,h),d.cross(g,c,n);var p=d.dot(a,g);if(k){if(p>-e)return null}else if(p>-e&&e>p)return null;d.sub(f,o,h);var q=d.dot(g,f)/p;if(0>q||q>1)return null;d.cross(g,a,f);var r=d.dot(n,g)/p;if(0>r||r>1||q+r>1)return null;d.cross(g,a,c);var s=-d.dot(f,g)/p;return 0>s?null:(l||(l=new b),m&&b.set(m,1-q-r,q,r),d.scaleAndAdd(l._array,o,n,s),l)}}(),applyTransform:function(a){b.add(this.direction,this.direction,this.origin),b.transformMat4(this.origin,this.origin,a),b.transformMat4(this.direction,this.direction,a),b.sub(this.direction,this.direction,this.origin),b.normalize(this.direction,this.direction)},copy:function(a){b.copy(this.origin,a.origin),b.copy(this.direction,a.direction)},clone:function(){var a=new f;return a.copy(this),a}},f}),define("qtek/Camera",["require","./Node","./math/Matrix4","./math/Frustum","./math/BoundingBox","./math/Ray","./dep/glmatrix"],function(a){var b=a("./Node"),c=a("./math/Matrix4"),d=a("./math/Frustum"),e=a("./math/BoundingBox"),f=a("./math/Ray"),g=a("./dep/glmatrix"),h=g.mat4,i=g.vec3,j=g.vec4,k=b.derive(function(){return{projectionMatrix:new c,invProjectionMatrix:new c,viewMatrix:new c,frustum:new d,sceneBoundingBoxLastFrame:new e}},function(){this.update(!0)},{update:function(a){b.prototype.update.call(this,a),h.invert(this.viewMatrix._array,this.worldTransform._array),this.updateProjectionMatrix(),h.invert(this.invProjectionMatrix._array,this.projectionMatrix._array),this.frustum.setFromProjection(this.projectionMatrix)},updateProjectionMatrix:function(){},castRay:function(){var a=j.create();return function(b,c){var d=void 0!==c?c:new f,e=b._array[0],g=b._array[1];return j.set(a,e,g,-1,1),j.transformMat4(a,a,this.invProjectionMatrix._array),j.transformMat4(a,a,this.worldTransform._array),i.scale(d.origin._array,a,1/a[3]),j.set(a,e,g,1,1),j.transformMat4(a,a,this.invProjectionMatrix._array),j.transformMat4(a,a,this.worldTransform._array),i.scale(a,a,1/a[3]),i.sub(d.direction._array,a,d.origin._array),i.normalize(d.direction._array,d.direction._array),d.direction._dirty=!0,d.origin._dirty=!0,d}}()});return k}),define("qtek/core/glenum",[],function(){return{DEPTH_BUFFER_BIT:256,STENCIL_BUFFER_BIT:1024,COLOR_BUFFER_BIT:16384,POINTS:0,LINES:1,LINE_LOOP:2,LINE_STRIP:3,TRIANGLES:4,TRIANGLE_STRIP:5,TRIANGLE_FAN:6,ZERO:0,ONE:1,SRC_COLOR:768,ONE_MINUS_SRC_COLOR:769,SRC_ALPHA:770,ONE_MINUS_SRC_ALPHA:771,DST_ALPHA:772,ONE_MINUS_DST_ALPHA:773,DST_COLOR:774,ONE_MINUS_DST_COLOR:775,SRC_ALPHA_SATURATE:776,FUNC_ADD:32774,BLEND_EQUATION:32777,BLEND_EQUATION_RGB:32777,BLEND_EQUATION_ALPHA:34877,FUNC_SUBTRACT:32778,FUNC_REVERSE_SUBTRACT:32779,BLEND_DST_RGB:32968,BLEND_SRC_RGB:32969,BLEND_DST_ALPHA:32970,BLEND_SRC_ALPHA:32971,CONSTANT_COLOR:32769,ONE_MINUS_CONSTANT_COLOR:32770,CONSTANT_ALPHA:32771,ONE_MINUS_CONSTANT_ALPHA:32772,BLEND_COLOR:32773,ARRAY_BUFFER:34962,ELEMENT_ARRAY_BUFFER:34963,ARRAY_BUFFER_BINDING:34964,ELEMENT_ARRAY_BUFFER_BINDING:34965,STREAM_DRAW:35040,STATIC_DRAW:35044,DYNAMIC_DRAW:35048,BUFFER_SIZE:34660,BUFFER_USAGE:34661,CURRENT_VERTEX_ATTRIB:34342,FRONT:1028,BACK:1029,FRONT_AND_BACK:1032,CULL_FACE:2884,BLEND:3042,DITHER:3024,STENCIL_TEST:2960,DEPTH_TEST:2929,SCISSOR_TEST:3089,POLYGON_OFFSET_FILL:32823,SAMPLE_ALPHA_TO_COVERAGE:32926,SAMPLE_COVERAGE:32928,NO_ERROR:0,INVALID_ENUM:1280,INVALID_VALUE:1281,INVALID_OPERATION:1282,OUT_OF_MEMORY:1285,CW:2304,CCW:2305,LINE_WIDTH:2849,ALIASED_POINT_SIZE_RANGE:33901,ALIASED_LINE_WIDTH_RANGE:33902,CULL_FACE_MODE:2885,FRONT_FACE:2886,DEPTH_RANGE:2928,DEPTH_WRITEMASK:2930,DEPTH_CLEAR_VALUE:2931,DEPTH_FUNC:2932,STENCIL_CLEAR_VALUE:2961,STENCIL_FUNC:2962,STENCIL_FAIL:2964,STENCIL_PASS_DEPTH_FAIL:2965,STENCIL_PASS_DEPTH_PASS:2966,STENCIL_REF:2967,STENCIL_VALUE_MASK:2963,STENCIL_WRITEMASK:2968,STENCIL_BACK_FUNC:34816,STENCIL_BACK_FAIL:34817,STENCIL_BACK_PASS_DEPTH_FAIL:34818,STENCIL_BACK_PASS_DEPTH_PASS:34819,STENCIL_BACK_REF:36003,STENCIL_BACK_VALUE_MASK:36004,STENCIL_BACK_WRITEMASK:36005,VIEWPORT:2978,SCISSOR_BOX:3088,COLOR_CLEAR_VALUE:3106,COLOR_WRITEMASK:3107,UNPACK_ALIGNMENT:3317,PACK_ALIGNMENT:3333,MAX_TEXTURE_SIZE:3379,MAX_VIEWPORT_DIMS:3386,SUBPIXEL_BITS:3408,RED_BITS:3410,GREEN_BITS:3411,BLUE_BITS:3412,ALPHA_BITS:3413,DEPTH_BITS:3414,STENCIL_BITS:3415,POLYGON_OFFSET_UNITS:10752,POLYGON_OFFSET_FACTOR:32824,TEXTURE_BINDING_2D:32873,SAMPLE_BUFFERS:32936,SAMPLES:32937,SAMPLE_COVERAGE_VALUE:32938,SAMPLE_COVERAGE_INVERT:32939,COMPRESSED_TEXTURE_FORMATS:34467,DONT_CARE:4352,FASTEST:4353,NICEST:4354,GENERATE_MIPMAP_HINT:33170,BYTE:5120,UNSIGNED_BYTE:5121,SHORT:5122,UNSIGNED_SHORT:5123,INT:5124,UNSIGNED_INT:5125,FLOAT:5126,DEPTH_COMPONENT:6402,ALPHA:6406,RGB:6407,RGBA:6408,LUMINANCE:6409,LUMINANCE_ALPHA:6410,UNSIGNED_SHORT_4_4_4_4:32819,UNSIGNED_SHORT_5_5_5_1:32820,UNSIGNED_SHORT_5_6_5:33635,FRAGMENT_SHADER:35632,VERTEX_SHADER:35633,MAX_VERTEX_ATTRIBS:34921,MAX_VERTEX_UNIFORM_VECTORS:36347,MAX_VARYING_VECTORS:36348,MAX_COMBINED_TEXTURE_IMAGE_UNITS:35661,MAX_VERTEX_TEXTURE_IMAGE_UNITS:35660,MAX_TEXTURE_IMAGE_UNITS:34930,MAX_FRAGMENT_UNIFORM_VECTORS:36349,SHADER_TYPE:35663,DELETE_STATUS:35712,LINK_STATUS:35714,VALIDATE_STATUS:35715,ATTACHED_SHADERS:35717,ACTIVE_UNIFORMS:35718,ACTIVE_ATTRIBUTES:35721,SHADING_LANGUAGE_VERSION:35724,CURRENT_PROGRAM:35725,NEVER:512,LESS:513,EQUAL:514,LEQUAL:515,GREATER:516,NOTEQUAL:517,GEQUAL:518,ALWAYS:519,KEEP:7680,REPLACE:7681,INCR:7682,DECR:7683,INVERT:5386,INCR_WRAP:34055,DECR_WRAP:34056,VENDOR:7936,RENDERER:7937,VERSION:7938,NEAREST:9728,LINEAR:9729,NEAREST_MIPMAP_NEAREST:9984,LINEAR_MIPMAP_NEAREST:9985,NEAREST_MIPMAP_LINEAR:9986,LINEAR_MIPMAP_LINEAR:9987,TEXTURE_MAG_FILTER:10240,TEXTURE_MIN_FILTER:10241,TEXTURE_WRAP_S:10242,TEXTURE_WRAP_T:10243,TEXTURE_2D:3553,TEXTURE:5890,TEXTURE_CUBE_MAP:34067,TEXTURE_BINDING_CUBE_MAP:34068,TEXTURE_CUBE_MAP_POSITIVE_X:34069,TEXTURE_CUBE_MAP_NEGATIVE_X:34070,TEXTURE_CUBE_MAP_POSITIVE_Y:34071,TEXTURE_CUBE_MAP_NEGATIVE_Y:34072,TEXTURE_CUBE_MAP_POSITIVE_Z:34073,TEXTURE_CUBE_MAP_NEGATIVE_Z:34074,MAX_CUBE_MAP_TEXTURE_SIZE:34076,TEXTURE0:33984,TEXTURE1:33985,TEXTURE2:33986,TEXTURE3:33987,TEXTURE4:33988,TEXTURE5:33989,TEXTURE6:33990,TEXTURE7:33991,TEXTURE8:33992,TEXTURE9:33993,TEXTURE10:33994,TEXTURE11:33995,TEXTURE12:33996,TEXTURE13:33997,TEXTURE14:33998,TEXTURE15:33999,TEXTURE16:34e3,TEXTURE17:34001,TEXTURE18:34002,TEXTURE19:34003,TEXTURE20:34004,TEXTURE21:34005,TEXTURE22:34006,TEXTURE23:34007,TEXTURE24:34008,TEXTURE25:34009,TEXTURE26:34010,TEXTURE27:34011,TEXTURE28:34012,TEXTURE29:34013,TEXTURE30:34014,TEXTURE31:34015,ACTIVE_TEXTURE:34016,REPEAT:10497,CLAMP_TO_EDGE:33071,MIRRORED_REPEAT:33648,FLOAT_VEC2:35664,FLOAT_VEC3:35665,FLOAT_VEC4:35666,INT_VEC2:35667,INT_VEC3:35668,INT_VEC4:35669,BOOL:35670,BOOL_VEC2:35671,BOOL_VEC3:35672,BOOL_VEC4:35673,FLOAT_MAT2:35674,FLOAT_MAT3:35675,FLOAT_MAT4:35676,SAMPLER_2D:35678,SAMPLER_CUBE:35680,VERTEX_ATTRIB_ARRAY_ENABLED:34338,VERTEX_ATTRIB_ARRAY_SIZE:34339,VERTEX_ATTRIB_ARRAY_STRIDE:34340,VERTEX_ATTRIB_ARRAY_TYPE:34341,VERTEX_ATTRIB_ARRAY_NORMALIZED:34922,VERTEX_ATTRIB_ARRAY_POINTER:34373,VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:34975,COMPILE_STATUS:35713,LOW_FLOAT:36336,MEDIUM_FLOAT:36337,HIGH_FLOAT:36338,LOW_INT:36339,MEDIUM_INT:36340,HIGH_INT:36341,FRAMEBUFFER:36160,RENDERBUFFER:36161,RGBA4:32854,RGB5_A1:32855,RGB565:36194,DEPTH_COMPONENT16:33189,STENCIL_INDEX:6401,STENCIL_INDEX8:36168,DEPTH_STENCIL:34041,RENDERBUFFER_WIDTH:36162,RENDERBUFFER_HEIGHT:36163,RENDERBUFFER_INTERNAL_FORMAT:36164,RENDERBUFFER_RED_SIZE:36176,RENDERBUFFER_GREEN_SIZE:36177,RENDERBUFFER_BLUE_SIZE:36178,RENDERBUFFER_ALPHA_SIZE:36179,RENDERBUFFER_DEPTH_SIZE:36180,RENDERBUFFER_STENCIL_SIZE:36181,FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:36048,FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:36049,FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL:36050,FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE:36051,COLOR_ATTACHMENT0:36064,DEPTH_ATTACHMENT:36096,STENCIL_ATTACHMENT:36128,DEPTH_STENCIL_ATTACHMENT:33306,NONE:0,FRAMEBUFFER_COMPLETE:36053,FRAMEBUFFER_INCOMPLETE_ATTACHMENT:36054,FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:36055,FRAMEBUFFER_INCOMPLETE_DIMENSIONS:36057,FRAMEBUFFER_UNSUPPORTED:36061,FRAMEBUFFER_BINDING:36006,RENDERBUFFER_BINDING:36007,MAX_RENDERBUFFER_SIZE:34024,INVALID_FRAMEBUFFER_OPERATION:1286,UNPACK_FLIP_Y_WEBGL:37440,UNPACK_PREMULTIPLY_ALPHA_WEBGL:37441,CONTEXT_LOST_WEBGL:37442,UNPACK_COLORSPACE_CONVERSION_WEBGL:37443,BROWSER_DEFAULT_WEBGL:37444} -}),define("qtek/core/Cache",[],function(){var a=function(){this._contextId=0,this._caches=[],this._context={}};return a.prototype={use:function(a,b){this._caches[a]||(this._caches[a]={},b&&(this._caches[a]=b())),this._contextId=a,this._context=this._caches[a]},put:function(a,b){this._context[a]=b},get:function(a){return this._context[a]},dirty:function(a){a=a||"";var b="__dirty__"+a;this.put(b,!0)},dirtyAll:function(a){a=a||"";for(var b="__dirty__"+a,c=0;c=0?(b||(b=g.create()),g.copy(b,this.faces[a]),b):void 0},isUseFace:function(){return this.useFace&&this.faces.length>0},isSplitted:function(){return this.getVertexNumber()>65535},createAttribute:function(a,c,d,e){var f=new b.Attribute(a,c,d,e,!0);return this.attributes[a]=f,this._attributeList.push(a),f},removeAttribute:function(a){var b=this._attributeList.indexOf(a);return b>=0?(this._attributeList.splice(b,1),delete this.attributes[a],!0):!1},getEnabledAttributes:function(){if(this._enabledAttributes)return this._enabledAttributes;for(var a={},b=this.getVertexNumber(),c=0;ch;h++)f[h]=-1;return d._arrayChunks.push(c),c},k=Object.keys(a);if(e>65535&&this.isUseFace()&&!c){var l,m=0,n=[0],o=[];for(q=0;e>q;q++)o[q]=-1,f[q]=-1;if(b)for(q=0;q65535&&(m++,n[m]=q,p=0,l=j(m));for(var t=0;3>t;t++){for(var u=r[t],v=-1===f[u],w=0;wA;A++)x[p*z+A]=y[u][A]}}v?(f[u]=p,s[t]=p,p++):s[t]=f[u]}}for(var B=0;Bq;q++)H[G++]=g[q][0],H[G++]=g[q][1],H[G++]=g[q][2]}}else{var C=j(0);if(b){var H=C.indicesArray,I=this.faces.length;if(!H||3*I!==H.length){var J=e>65535?Uint32Array:Uint16Array;H=C.indicesArray=new J(3*this.faces.length)}for(var G=0,q=0;I>q;q++)H[G++]=this.faces[q][0],H[G++]=this.faces[q][1],H[G++]=this.faces[q][2]}for(var i in a){var y=a[i].value,K=a[i].type,z=a[i].size,x=C.attributeArrays[i],L=e*z;if(x&&x.length===L||(x=new h[i](L),C.attributeArrays[i]=x),1===z)for(var q=0;qA;A++)x[G++]=y[q][A]}}},_updateBuffer:function(a,c,d){var e=this._cache.get("chunks"),f=!1;if(!e){e=[];for(var g=0;g=0;g--)if(j[g].name===q){r=j[g],p=g;break}}var w;w=r?r.buffer:a.createBuffer(),a.bindBuffer(a.ARRAY_BUFFER,w),a.bufferData(a.ARRAY_BUFFER,m[q],this.hint),j[o++]=new b.AttributeBuffer(q,t,w,v,u)}j.length=o,d&&(k||(k=new b.IndicesBuffer(a.createBuffer()),i.indicesBuffer=k),k.count=n.length,a.bindBuffer(a.ELEMENT_ARRAY_BUFFER,k.buffer),a.bufferData(a.ELEMENT_ARRAY_BUFFER,n,this.hint))}},generateVertexNormals:function(){for(var a=this.faces,b=a.length,c=this.attributes.position.value,d=this.attributes.normal.value,e=g.create(),f=g.create(),h=g.create(),i=0;ij;j++){var k=a[j],l=k[0],m=k[1],n=k[2],o=c[l],p=c[m],q=c[n];g.sub(f,o,p),g.sub(h,p,q),g.cross(e,f,h),g.add(d[l],d[l],e),g.add(d[m],d[m],e),g.add(d[n],d[n],e)}for(var i=0;ik;k++){var l=a[k],m=l[0],n=l[1],o=l[2],p=c[m],q=c[n],r=c[o];g.sub(f,p,q),g.sub(h,q,r),g.cross(e,f,h),j?(g.copy(d[m],e),g.copy(d[n],e),g.copy(d[o],e)):d[m]=d[n]=d[o]=i.call(e)}},generateTangents:function(){for(var a=this.attributes.texcoord0.value,b=this.attributes.position.value,c=this.attributes.tangent.value,d=this.attributes.normal.value,e=[],f=[],h=this.getVertexNumber(),i=0;h>i;i++)e[i]=[0,0,0],f[i]=[0,0,0];for(var j=[0,0,0],k=[0,0,0],i=0;ii;i++){var I=d[i],J=e[i];g.scale(G,I,g.dot(I,J)),g.sub(G,J,G),g.normalize(G,G),g.cross(H,I,J),G[3]=g.dot(H,f[i])<0?-1:1,c[i]=G.slice()}},isUniqueVertex:function(){return this.isUseFace()?this.getVertexNumber()===3*this.faces.length:!0},generateUniqueVertex:function(){for(var a=[],b=0;bh;h++){var j=g[h];if(a[j]>0){for(var k=0;k1)console.warn("Large geometry will discard chunks when convert to StaticGeometry");else if(0===this._arrayChunks.length)return a;var d=this._arrayChunks[0],e=this.getEnabledAttributes();for(var f in e){var g=e[f],h=a.attributes[f];h||(h=a.attributes[f]={type:g.type,size:g.size,value:null},g.semantic&&(h.semantic=g.semantic)),h.value=d.attributeArrays[f]}return a.faces=d.indicesArray,this.boundingBox&&(a.boundingBox=new c,a.boundingBox.min.copy(this.boundingBox.min),a.boundingBox.max.copy(this.boundingBox.max)),a},applyTransform:function(a){var b=this.attributes.position.value,c=this.attributes.normal.value,d=this.attributes.tangent.value;a=a._array;for(var e=0;eb;b<<=1)a|=a>>b;return a+1},dispose:function(a){var b=this._cache;b.use(a.__GLID__);var c=b.get("webgl_texture");c&&a.deleteTexture(c),b.deleteContext(a.__GLID__)},isRenderable:function(){},isPowerOfTwo:function(){}});return e.BYTE=c.BYTE,e.UNSIGNED_BYTE=c.UNSIGNED_BYTE,e.SHORT=c.SHORT,e.UNSIGNED_SHORT=c.UNSIGNED_SHORT,e.INT=c.INT,e.UNSIGNED_INT=c.UNSIGNED_INT,e.FLOAT=c.FLOAT,e.HALF_FLOAT=36193,e.DEPTH_COMPONENT=c.DEPTH_COMPONENT,e.ALPHA=c.ALPHA,e.RGB=c.RGB,e.RGBA=c.RGBA,e.LUMINANCE=c.LUMINANCE,e.LUMINANCE_ALPHA=c.LUMINANCE_ALPHA,e.COMPRESSED_RGB_S3TC_DXT1_EXT=33776,e.COMPRESSED_RGBA_S3TC_DXT1_EXT=33777,e.COMPRESSED_RGBA_S3TC_DXT3_EXT=33778,e.COMPRESSED_RGBA_S3TC_DXT5_EXT=33779,e.NEAREST=c.NEAREST,e.LINEAR=c.LINEAR,e.NEAREST_MIPMAP_NEAREST=c.NEAREST_MIPMAP_NEAREST,e.LINEAR_MIPMAP_NEAREST=c.LINEAR_MIPMAP_NEAREST,e.NEAREST_MIPMAP_LINEAR=c.NEAREST_MIPMAP_LINEAR,e.LINEAR_MIPMAP_LINEAR=c.LINEAR_MIPMAP_LINEAR,e.TEXTURE_MAG_FILTER=c.TEXTURE_MAG_FILTER,e.TEXTURE_MIN_FILTER=c.TEXTURE_MIN_FILTER,e.REPEAT=c.REPEAT,e.CLAMP_TO_EDGE=c.CLAMP_TO_EDGE,e.MIRRORED_REPEAT=c.MIRRORED_REPEAT,e}),define("qtek/TextureCube",["require","./Texture","./core/glinfo","./core/glenum","./core/util"],function(a){function b(a){return"CANVAS"===a.nodeName||a.complete}var c=a("./Texture"),d=a("./core/glinfo"),e=a("./core/glenum"),f=a("./core/util"),g=["px","nx","py","ny","pz","nz"],h=c.derive(function(){return{image:{px:null,nx:null,py:null,ny:null,pz:null,nz:null},pixels:{px:null,nx:null,py:null,ny:null,pz:null,nz:null},mipmaps:[]}},{update:function(a){a.bindTexture(a.TEXTURE_CUBE_MAP,this._cache.get("webgl_texture")),this.beforeUpdate(a);var b=this.format,c=this.type;a.texParameteri(a.TEXTURE_CUBE_MAP,a.TEXTURE_WRAP_S,this.wrapS),a.texParameteri(a.TEXTURE_CUBE_MAP,a.TEXTURE_WRAP_T,this.wrapT),a.texParameteri(a.TEXTURE_CUBE_MAP,a.TEXTURE_MAG_FILTER,this.magFilter),a.texParameteri(a.TEXTURE_CUBE_MAP,a.TEXTURE_MIN_FILTER,this.minFilter);var f=d.getExtension(a,"EXT_texture_filter_anisotropic");if(f&&this.anisotropic>1&&a.texParameterf(a.TEXTURE_CUBE_MAP,f.TEXTURE_MAX_ANISOTROPY_EXT,this.anisotropic),36193===c){var g=d.getExtension(a,"OES_texture_half_float");g||(c=e.FLOAT)}if(this.mipmaps.length)for(var h=this.width,i=this.height,j=0;ji;i++){var j=g[i],k=b.image&&b.image[j];k?a.texImage2D(a.TEXTURE_CUBE_MAP_POSITIVE_X+i,c,f,f,h,k):a.texImage2D(a.TEXTURE_CUBE_MAP_POSITIVE_X+i,c,f,d,e,0,f,h,b.pixels&&b.pixels[j])}},generateMipmap:function(a){this.useMipmap&&!this.NPOT&&(a.bindTexture(a.TEXTURE_CUBE_MAP,this._cache.get("webgl_texture")),a.generateMipmap(a.TEXTURE_CUBE_MAP))},bind:function(a){a.bindTexture(a.TEXTURE_CUBE_MAP,this.getWebGLTexture(a))},unbind:function(a){a.bindTexture(a.TEXTURE_CUBE_MAP,null)},isPowerOfTwo:function(){function a(a){return 0===(a&a-1)}return this.image.px?a(this.image.px.width)&&a(this.image.px.height):a(this.width)&&a(this.height)},isRenderable:function(){return this.image.px?b(this.image.px)&&b(this.image.nx)&&b(this.image.py)&&b(this.image.ny)&&b(this.image.pz)&&b(this.image.nz):this.width&&this.height},load:function(a){var b=0,c=this;return f.each(a,function(a,d){var e=new Image;e.onload=function(){b--,0===b&&(c.dirty(),c.trigger("success",c)),e.onload=null},e.onerror=function(){b--,e.onerror=null},b++,e.src=a,c.image[d]=e}),this}});return h}),define("qtek/FrameBuffer",["require","./core/Base","./TextureCube","./core/glinfo","./core/glenum","./core/Cache"],function(a){var b=a("./core/Base"),c=a("./TextureCube"),d=a("./core/glinfo"),e=a("./core/glenum"),f=a("./core/Cache"),g=b.derive({depthBuffer:!0,_attachedTextures:null,_width:0,_height:0,_binded:!1},function(){this._cache=new f,this._attachedTextures={}},{resize:function(a,b){this._width=a,this._height=b},bind:function(a){var b=a.gl;this._binded||(b.bindFramebuffer(b.FRAMEBUFFER,this.getFrameBuffer(b)),this._binded=!0);var c=this._cache;if(c.put("viewport",a.viewport),a.setViewport(0,0,this._width,this._height,1),!c.get("depthtexture_attached")&&this.depthBuffer){c.miss("renderbuffer")&&c.put("renderbuffer",b.createRenderbuffer());var d=this._width,e=this._height,f=c.get("renderbuffer");(d!==c.get("renderbuffer_width")||e!==c.get("renderbuffer_height"))&&(b.bindRenderbuffer(b.RENDERBUFFER,f),b.renderbufferStorage(b.RENDERBUFFER,b.DEPTH_COMPONENT16,d,e),c.put("renderbuffer_width",d),c.put("renderbuffer_height",e),b.bindRenderbuffer(b.RENDERBUFFER,null)),c.get("renderbuffer_attached")||(b.framebufferRenderbuffer(b.FRAMEBUFFER,b.DEPTH_ATTACHMENT,b.RENDERBUFFER,f),c.put("renderbuffer_attached",!0))}},unbind:function(a){var b=a.gl;b.bindFramebuffer(b.FRAMEBUFFER,null),this._binded=!1,this._cache.use(b.__GLID__);var d=this._cache.get("viewport");d&&a.setViewport(d.x,d.y,d.width,d.height);for(var e in this._attachedTextures){var f=this._attachedTextures[e];if(!f.NPOT&&f.useMipmap){var g=f instanceof c?b.TEXTURE_CUBE_MAP:b.TEXTURE_2D;b.bindTexture(g,f.getWebGLTexture(b)),b.generateMipmap(g),b.bindTexture(g,null)}}},getFrameBuffer:function(a){return this._cache.use(a.__GLID__),this._cache.miss("framebuffer")&&this._cache.put("framebuffer",a.createFramebuffer()),this._cache.get("framebuffer")},attach:function(a,b,c,f,g){if(!b.width)throw new Error("The texture attached to color buffer is not a valid.");if(this._binded||(a.bindFramebuffer(a.FRAMEBUFFER,this.getFrameBuffer(a)),this._binded=!0),this._width=b.width,this._height=b.height,c=c||a.COLOR_ATTACHMENT0,f=f||a.TEXTURE_2D,g=g||0,c===a.DEPTH_ATTACHMENT){var h=d.getExtension(a,"WEBGL_depth_texture");if(!h)return console.error(" Depth texture is not supported by the browser"),void 0;if(b.format!==e.DEPTH_COMPONENT)return console.error("The texture attached to depth buffer is not a valid."),void 0;this._cache.put("renderbuffer_attached",!1),this._cache.put("depthtexture_attached",!0)}this._attachedTextures[c]=b,a.framebufferTexture2D(a.FRAMEBUFFER,c,f,b.getWebGLTexture(a),g)},detach:function(){},dispose:function(a){this._cache.use(a.__GLID__);var b=this._cache.get("renderbuffer");b&&a.deleteRenderbuffer(b);var c=this._cache.get("framebuffer");c&&a.deleteFramebuffer(c),this._attachedTextures={},this._width=this._height=0,this._cache.deleteContext(a.__GLID__)}});return g.COLOR_ATTACHMENT0=e.COLOR_ATTACHMENT0,g.DEPTH_ATTACHMENT=e.DEPTH_ATTACHMENT,g.STENCIL_ATTACHMENT=e.STENCIL_ATTACHMENT,g.DEPTH_STENCIL_ATTACHMENT=e.DEPTH_STENCIL_ATTACHMENT,g}),define("qtek/Joint",["require","./core/Base"],function(a){var b=a("./core/Base"),c=b.derive({name:"",index:-1,parentIndex:-1,node:null,rootNode:null});return c}),define("qtek/Shader",["require","./core/Base","./core/util","./core/Cache","./dep/glmatrix"],function(a){function b(){return{locations:{},attriblocations:{}}}function c(a){for(var b=a.split("\n"),c=0,d=b.length;d>c;c++)b[c]=c+1+": "+b[c];return b.join("\n")}var d=a("./core/Base"),e=a("./core/util"),f=a("./core/Cache"),g=a("./dep/glmatrix"),h=g.mat2,i=g.mat3,j=g.mat4,k=/uniform\s+(bool|float|int|vec2|vec3|vec4|ivec2|ivec3|ivec4|mat2|mat3|mat4|sampler2D|samplerCube)\s+([\w\,]+)?(\[.*?\])?\s*(:\s*([\S\s]+?))?;/g,l=/attribute\s+(float|int|vec2|vec3|vec4)\s+(\w*)\s*(:\s*(\w+))?;/g,m=/#define\s+(\w+)?(\s+[\w-.]+)?\s*\n/g,n={bool:"1i","int":"1i",sampler2D:"t",samplerCube:"t","float":"1f",vec2:"2f",vec3:"3f",vec4:"4f",ivec2:"2i",ivec3:"3i",ivec4:"4i",mat2:"m2",mat3:"m3",mat4:"m4"},o={bool:function(){return!0},"int":function(){return 0},"float":function(){return 0},sampler2D:function(){return null},samplerCube:function(){return null},vec2:function(){return[0,0]},vec3:function(){return[0,0,0]},vec4:function(){return[0,0,0,0]},ivec2:function(){return[0,0]},ivec3:function(){return[0,0,0]},ivec4:function(){return[0,0,0,0]},mat2:function(){return h.create()},mat3:function(){return i.create()},mat4:function(){return j.create()},array:function(){return[]}},p=["POSITION","NORMAL","BINORMAL","TANGENT","TEXCOORD","TEXCOORD_0","TEXCOORD_1","COLOR","JOINT","WEIGHT","SKIN_MATRIX"],q=["WORLD","VIEW","PROJECTION","WORLDVIEW","VIEWPROJECTION","WORLDVIEWPROJECTION","WORLDINVERSE","VIEWINVERSE","PROJECTIONINVERSE","WORLDVIEWINVERSE","VIEWPROJECTIONINVERSE","WORLDVIEWPROJECTIONINVERSE","WORLDTRANSPOSE","VIEWTRANSPOSE","PROJECTIONTRANSPOSE","WORLDVIEWTRANSPOSE","VIEWPROJECTIONTRANSPOSE","WORLDVIEWPROJECTIONTRANSPOSE","WORLDINVERSETRANSPOSE","VIEWINVERSETRANSPOSE","PROJECTIONINVERSETRANSPOSE","WORLDVIEWINVERSETRANSPOSE","VIEWPROJECTIONINVERSETRANSPOSE","WORLDVIEWPROJECTIONINVERSETRANSPOSE"],r={},s=1,t=2,u=3,v=d.derive(function(){return{vertex:"",fragment:"",precision:"mediump",attribSemantics:{},matrixSemantics:{},matrixSemanticKeys:[],uniformTemplates:{},attributeTemplates:{},vertexDefines:{},fragmentDefines:{},lightNumber:{},_attacheMaterialNumber:0,_uniformList:[],_textureStatus:{},_vertexProcessed:"",_fragmentProcessed:"",_currentLocationsMap:{}}},function(){this._cache=new f,this._updateShaderString()},{setVertex:function(a){this.vertex=a,this._updateShaderString(),this.dirty()},setFragment:function(a){this.fragment=a,this._updateShaderString(),this.dirty()},bind:function(a){if(this._cache.use(a.__GLID__,b),this._currentLocationsMap=this._cache.get("locations"),this._cache.isDirty()){this._updateShaderString();var c=this._buildProgram(a,this._vertexProcessed,this._fragmentProcessed);if(this._cache.fresh(),c)return c}a.useProgram(this._cache.get("program"))},dirty:function(){this._cache.dirtyAll();for(var a=0;ak;k++)g[h++]=j[k];a.uniformMatrix4fv(f,!1,g)}else d instanceof Float32Array&&a.uniformMatrix4fv(f,!1,d)}return!0},setUniformBySemantic:function(a,b,c){var d=this.attribSemantics[b];return d?this.setUniform(a,d.type,d.symbol,c):!1},enableAttributes:function(a,b,c){var d,e=this._cache.get("program"),f=this._cache.get("attriblocations");d=c?c.__enabledAttributeList:r[a.__GLID__],d||(d=c?c.__enabledAttributeList=[]:r[a.__GLID__]=[]);for(var g=[],h=0;h0&&a.push("#define "+b.toUpperCase()+"_NUMBER "+c)}for(var d in this._textureStatus){var e=this._textureStatus[d];e.enabled&&a.push("#define "+d.toUpperCase()+"_ENABLED")}for(var d in this.vertexDefines){var f=this.vertexDefines[d];null===f?a.push("#define "+d):a.push("#define "+d+" "+f.toString())}this._vertexProcessed=a.join("\n")+"\n"+this._vertexProcessedNoDefine,a=[];for(var b in this.lightNumber){var c=this.lightNumber[b];c>0&&a.push("#define "+b.toUpperCase()+"_NUMBER "+c)}for(var d in this._textureStatus){var e=this._textureStatus[d];e.enabled&&a.push("#define "+d.toUpperCase()+"_ENABLED")}for(var d in this.fragmentDefines){var f=this.fragmentDefines[d];null===f?a.push("#define "+d):a.push("#define "+d+" "+f.toString())}var g=a.join("\n")+"\n"+this._fragmentProcessedNoDefine;this._fragmentProcessed=["precision",this.precision,"float"].join(" ")+";\n"+g},_parseUniforms:function(){function a(a,e,f,g,h,i){if(e&&f){var j,k=n[e],l=!0;if(k){if(c._uniformList.push(f),("sampler2D"===e||"samplerCube"===e)&&(c._textureStatus[f]={enabled:!1,shaderType:d}),g&&(k+="v"),i)if(p.indexOf(i)>=0)c.attribSemantics[i]={symbol:f,type:k},l=!1;else if(q.indexOf(i)>=0){var m=!1,r=i;i.match(/TRANSPOSE$/)&&(m=!0,r=i.slice(0,-9)),c.matrixSemantics[i]={symbol:f,type:k,isTranspose:m,semanticNoTranspose:r},l=!1}else if("unconfigurable"===i)l=!1;else{if(j=c._parseDefaultValue(e,i),!j)throw new Error('Unkown semantic "'+i+'"');i=""}l&&(b[f]={type:k,value:g?o.array:j||o[e],semantic:i||null})}return["uniform",e,f,g].join(" ")+";\n" -}}var b={},c=this,d="vertex";this._uniformList=[],this._vertexProcessedNoDefine=this._vertexProcessedNoDefine.replace(k,a),d="fragment",this._fragmentProcessedNoDefine=this._fragmentProcessedNoDefine.replace(k,a),c.matrixSemanticKeys=Object.keys(this.matrixSemantics),this.uniformTemplates=b},_parseDefaultValue:function(a,b){var c=/\[\s*(.*)\s*\]/;{if("vec2"!==a&&"vec3"!==a&&"vec4"!==a)return"bool"===a?function(){return"true"===b.toLowerCase()?!0:!1}:"float"===a?function(){return parseFloat(b)}:void 0;var d=c.exec(b)[1];if(d){var e=d.split(/\s*,\s*/);return function(){return new Float32Array(e)}}}},createUniforms:function(){var a={};for(var b in this.uniformTemplates){var c=this.uniformTemplates[b];a[b]={type:c.type,value:c.value()}}return a},attached:function(){this._attacheMaterialNumber++},detached:function(){this._attacheMaterialNumber--},isAttachedToAny:function(){return 0!==this._attacheMaterialNumber},_parseAttributes:function(){function a(a,d,e,f,g){if(d&&e){var h=1;switch(d){case"vec4":h=4;break;case"vec3":h=3;break;case"vec2":h=2;break;case"float":h=1}if(b[e]={type:"float",size:h,semantic:g||null},g){if(p.indexOf(g)<0)throw new Error('Unkown semantic "'+g+'"');c.attribSemantics[g]={symbol:e,type:d}}}return["attribute",d,e].join(" ")+";\n"}var b={},c=this;this._vertexProcessedNoDefine=this._vertexProcessedNoDefine.replace(l,a),this.attributeTemplates=b},_parseDefines:function(){function a(a,d,e){var f="vertex"===c?b.vertexDefines:b.fragmentDefines;return f[d]||(f[d]="false"==e?!1:"true"==e?!0:e?parseFloat(e):null),""}var b=this,c="vertex";this._vertexProcessedNoDefine=this._vertexProcessedNoDefine.replace(m,a),c="fragment",this._fragmentProcessedNoDefine=this._fragmentProcessedNoDefine.replace(m,a)},_buildProgram:function(a,b,c){this._cache.get("program")&&a.deleteProgram(this._cache.get("program"));var d=a.createProgram(),e=a.createShader(a.VERTEX_SHADER);a.shaderSource(e,b),a.compileShader(e);var f=a.createShader(a.FRAGMENT_SHADER);a.shaderSource(f,c),a.compileShader(f);var g=this._checkShaderErrorMsg(a,e,b);if(g)return g;if(g=this._checkShaderErrorMsg(a,f,c))return g;if(a.attachShader(d,e),a.attachShader(d,f),this.attribSemantics.POSITION)a.bindAttribLocation(d,0,this.attribSemantics.POSITION.symbol);else{var h=Object.keys(this.attributeTemplates);a.bindAttribLocation(d,0,h[0])}if(a.linkProgram(d),!a.getProgramParameter(d,a.LINK_STATUS))return"Could not link program\nVALIDATE_STATUS: "+a.getProgramParameter(d,a.VALIDATE_STATUS)+", gl error ["+a.getError()+"]";for(var i=0;i=0&&this._enabledUniforms.splice(b,1)},isUniformEnabled:function(a){return this._enabledUniforms.indexOf(a)>=0},set:function(a,b){if("object"==typeof a)for(var c in a){var d=a[c];this.set(c,d)}else{var e=this.uniforms[a];e&&(e.value=b)}},get:function(a){var b=this.uniforms[a];return b?b.value:void 0},attachShader:function(a,b){this.shader&&this.shader.detached();var c=this.uniforms;if(this.uniforms=a.createUniforms(),this.shader=a,this._enabledUniforms=Object.keys(this.uniforms),b)for(var d in c)this.uniforms[d]&&(this.uniforms[d].value=c[d].value);a.attached()},detachShader:function(){this.shader.detached(),this.shader=null,this.uniforms={}},clone:function(){var a=new d({name:this.name,shader:this.shader});for(var b in this.uniforms)a.uniforms[b].value=this.uniforms[b].value;return a.depthTest=this.depthTest,a.depthMask=this.depthMask,a.transparent=this.transparent,a.blend=this.blend,a},dispose:function(a,b){if(b)for(var d in this.uniforms){var e=this.uniforms[d].value;if(e)if(e instanceof c)e.dispose(a);else if(e instanceof Array)for(var f=0;f65535,s=r?a.UNSIGNED_INT:a.UNSIGNED_SHORT,t=f.getExtension(a,"OES_vertex_array_object"),u=!m.dynamic,v=this._renderInfo;v.vertexNumber=o,v.faceNumber=0,v.drawCallNumber=0;var w=!1;if(c=a.__GLID__+"-"+m.__GUID__+"-"+l.__GUID__,c!==h?w=!0:(m instanceof g&&o>65535&&!q&&p||t&&u||m._cache.isDirty())&&(w=!0),h=c,w){var x=this._drawCache[c];if(!x){var y=m.getBufferChunks(a);if(!y)return;x=[];for(var z=0;z0&&this.setViewport(this._viewportSettings.pop())},saveClear:function(){this._clearSettings.push(this.clear)},restoreClear:function(){this._clearSettings.length>0&&(this.clear=this._clearSettings.pop())},render:function(a,b,c,d){var e=this.gl;this._sceneRendering=a;var f=this.color;this.clear&&(e.colorMask(!0,!0,!0,!0),e.depthMask(!0),e.clearColor(f[0],f[1],f[2],f[3]),e.clear(this.clear)),c||a.update(!1),b.getScene()||b.update(!0);var g=a.opaqueQueue,h=a.transparentQueue,i=a.material;if(a.trigger("beforerender",this,a,b),h.length>0)for(var j=k.create(),m=l.create(),n=0;n0&&a.min._array[2]<0&&(a.max._array[2]=-1e-20),a.applyProjection(b);var h=a.min._array,i=a.max._array;if(i[0]<-1||h[0]>1||i[1]<-1||h[1]>1||i[2]<-1||h[2]>1)return!0}return!1}}(),disposeScene:function(a){this.disposeNode(a,!0,!0),a.dispose()},disposeNode:function(a,b,c){var d={},e=this.gl;a.getParent()&&a.getParent().remove(a),a.traverse(function(a){a.geometry&&b&&a.geometry.dispose(e),a.material&&(d[a.material.__GUID__]=a.material),a.dispose&&a.dispose(e)});for(var f in d){var g=d[f];g.dispose(e,c)}},disposeShader:function(a){a.dispose(this.gl)},disposeGeometry:function(a){a.dispose(this.gl)},disposeTexture:function(a){a.dispose(this.gl)},disposeFrameBuffer:function(a){a.dispose(this.gl)},dispose:function(){c.dispose(this.gl)},screenToNdc:function(a,b,c){c||(c=new i),b=this.height-b;var d=this.viewport,e=c._array;return e[0]=(a-d.x)/d.width,e[0]=2*e[0]-1,e[1]=(b-d.y)/d.height,e[1]=2*e[1]-1,c}});o.opaqueSortFunc=function(a,b){return a.material.shader===b.material.shader?a.material===b.material?a.geometry.__GUID__-b.geometry.__GUID__:a.material.__GUID__-b.material.__GUID__:a.material.shader.__GUID__-b.material.shader.__GUID__},o.transparentSortFunc=function(a,b){return a.__depth===b.__depth?a.material.shader===b.material.shader?a.material===b.material?a.geometry.__GUID__-b.geometry.__GUID__:a.material.__GUID__-b.material.__GUID__:a.material.shader.__GUID__-b.material.shader.__GUID__:a.__depth-b.__depth};var p={WORLD:k.create(),VIEW:k.create(),PROJECTION:k.create(),WORLDVIEW:k.create(),VIEWPROJECTION:k.create(),WORLDVIEWPROJECTION:k.create(),WORLDINVERSE:k.create(),VIEWINVERSE:k.create(),PROJECTIONINVERSE:k.create(),WORLDVIEWINVERSE:k.create(),VIEWPROJECTIONINVERSE:k.create(),WORLDVIEWPROJECTIONINVERSE:k.create(),WORLDTRANSPOSE:k.create(),VIEWTRANSPOSE:k.create(),PROJECTIONTRANSPOSE:k.create(),WORLDVIEWTRANSPOSE:k.create(),VIEWPROJECTIONTRANSPOSE:k.create(),WORLDVIEWPROJECTIONTRANSPOSE:k.create(),WORLDINVERSETRANSPOSE:k.create(),VIEWINVERSETRANSPOSE:k.create(),PROJECTIONINVERSETRANSPOSE:k.create(),WORLDVIEWINVERSETRANSPOSE:k.create(),VIEWPROJECTIONINVERSETRANSPOSE:k.create(),WORLDVIEWPROJECTIONINVERSETRANSPOSE:k.create()};return o.COLOR_BUFFER_BIT=d.COLOR_BUFFER_BIT,o.DEPTH_BUFFER_BIT=d.DEPTH_BUFFER_BIT,o.STENCIL_BUFFER_BIT=d.STENCIL_BUFFER_BIT,o}),define("qtek/Scene",["require","./Node","./Light"],function(a){function b(a,b){return b.castShadow&&!a.castShadow?!0:void 0}var c=a("./Node"),d=a("./Light"),e=c.derive(function(){return{material:null,autoUpdate:!0,opaqueQueue:[],transparentQueue:[],lights:[],_lightUniforms:{},_lightNumber:{POINT_LIGHT:0,DIRECTIONAL_LIGHT:0,SPOT_LIGHT:0,AMBIENT_LIGHT:0},_opaqueObjectCount:0,_transparentObjectCount:0,_nodeRepository:{}}},function(){this._scene=this},{addToScene:function(a){a.name&&(this._nodeRepository[a.name]=a)},removeFromScene:function(a){a.name&&delete this._nodeRepository[a.name]},getNode:function(a){return this._nodeRepository[a]},cloneNode:function(a){var b=a.clone(),c={},d=function(e,f){e.skeleton&&(f.skeleton=e.skeleton.clone(a,b),f.joints=e.joints.slice()),e.material&&(c[e.material.__GUID__]={oldMat:e.material});for(var g=0;g0&&this._updateRenderQueue(e)}},_updateLightUniforms:function(){var a=this.lights;a.sort(b);var c=this._lightUniforms;for(var d in c)c[d].value.length=0;for(var e=0;e=0){var d=a[c.parentIndex].node;d.add(c.node)}else this.roots.push(c)}},addClip:function(a,b){for(var c=0;c0&&this._clips.splice(b,1) -},removeClipsAll:function(){this._clips=[]},getClip:function(a){return this._clips[a]?this._clips[a].clip:void 0},getClipNumber:function(){return this._clips.length},updateJointMatrices:function(){var a=g.create();return function(){for(var b=0;be;e++)this._invBindPoseMatricesArray[d+e]=a[e]}this.updateMatricesSubArrays()}}(),updateMatricesSubArrays:function(){for(var a=0;ag;g++)c[d++]=this._skinMatricesArray[16*f+g];return c},setPose:function(a){if(this._clips[a]){for(var b=this._clips[a].clip,c=this._clips[a].maps,d=0;de;e++)d._invBindPoseMatricesArray[e]=this._invBindPoseMatricesArray[e];d._skinMatricesArray=new Float32Array(j),d.updateMatricesSubArrays()}return d.update(),d}});return h}),define("qtek/StaticGeometry",["require","./Geometry","./math/BoundingBox","./dep/glmatrix","./core/glenum"],function(a){var b=a("./Geometry"),c=a("./math/BoundingBox"),d=a("./dep/glmatrix"),e=a("./core/glenum"),f=d.mat4,g=d.vec3,h=b.derive(function(){return{attributes:{position:new b.Attribute("position","float",3,"POSITION",!1),texcoord0:new b.Attribute("texcoord0","float",2,"TEXCOORD_0",!1),texcoord1:new b.Attribute("texcoord1","float",2,"TEXCOORD_1",!1),normal:new b.Attribute("normal","float",3,"NORMAL",!1),tangent:new b.Attribute("tangent","float",4,"TANGENT",!1),color:new b.Attribute("color","float",4,"COLOR",!1),weight:new b.Attribute("weight","float",3,"WEIGHT",!1),joint:new b.Attribute("joint","float",4,"JOINT",!1),barycentric:new b.Attribute("barycentric","float",3,null,!1)},hint:e.STATIC_DRAW,faces:null,_normalType:"vertex",_enabledAttributes:null}},{dirty:function(){this._cache.dirtyAll(),this._enabledAttributes=null},getVertexNumber:function(){var a=this.attributes[this.mainAttribute];return a&&a.value?a.value.length/a.size:0},getFaceNumber:function(){return this.faces?this.faces.length/3:0},getFace:function(a,b){return a=0?(b||(b=g.create()),b[0]=this.faces[3*a],b[1]=this.faces[3*a+1],b[2]=this.faces[3*a+2],b):void 0},isUseFace:function(){return this.useFace&&null!=this.faces},createAttribute:function(a,c,d,e){var f=new b.Attribute(a,c,d,e,!1);return this.attributes[a]=f,this._attributeList.push(a),f},removeAttribute:function(a){var b=this._attributeList.indexOf(a);return b>=0?(this._attributeList.splice(b,1),delete this.attributes[a],!0):!1},getEnabledAttributes:function(){if(this._enabledAttributes)return this._enabledAttributes;for(var a=[],b=this.getVertexNumber(),c=0;c=0;o--)if(f[o].name===m){l=f[o],i=o;break}}var p;p=l?l.buffer:a.createBuffer(),a.bindBuffer(a.ARRAY_BUFFER,p),a.bufferData(a.ARRAY_BUFFER,n.value,this.hint),f[j++]=new b.AttributeBuffer(m,n.type,p,n.size,n.semantic)}f.length=j,this.isUseFace()&&(g||(g=new b.IndicesBuffer(a.createBuffer()),e.indicesBuffer=g),g.count=this.faces.length,a.bindBuffer(a.ELEMENT_ARRAY_BUFFER,g.buffer),a.bufferData(a.ELEMENT_ARRAY_BUFFER,this.faces,this.hint))},generateVertexNormals:function(){var a=this.faces,b=this.attributes.position.value,c=this.attributes.normal.value;if(c&&c.length===b.length)for(var d=0;dd;d++)c[3*m+d]=c[3*m+d]+k[d],c[3*n+d]=c[3*n+d]+k[d],c[3*o+d]=c[3*o+d]+k[d]}for(var d=0;do;o++)c[3*l+o]=j[o],c[3*m+o]=j[o],c[3*n+o]=j[o]}},generateTangents:function(){var a=this.getVertexNumber();this.attributes.tangent.value||(this.attributes.tangent.value=new Float32Array(4*a));for(var b=this.attributes.texcoord0.value,c=this.attributes.position.value,d=this.attributes.tangent.value,e=this.attributes.normal.value,f=[],h=[],i=0;a>i;i++)f[i]=[0,0,0],h[i]=[0,0,0];for(var j=[0,0,0],k=[0,0,0],i=0;ii;i++){Q[0]=e[3*i],Q[1]=e[3*i+1],Q[2]=e[3*i+2];var R=f[i];g.scale(O,Q,g.dot(Q,R)),g.sub(O,R,O),g.normalize(O,O),g.cross(P,Q,R),d[4*i]=O[0],d[4*i+1]=O[1],d[4*i+2]=O[2],d[4*i+3]=g.dot(P,h[i])<0?-1:1}},isUniqueVertex:function(){return this.isUseFace()?this.getVertexNumber()===this.faces.length:!0},generateUniqueVertex:function(){for(var a=[],b=0,c=this.getVertexNumber();c>b;b++)a[b]=0;for(var d=this.getVertexNumber(),e=this.attributes,f=this.faces,g=this.getEnabledAttributes(),h=0;hb;b++)j[b]=e[i].value[b];e[i].value=j}for(var b=0;b0){for(var h=0;hn;n++)l[d*m+n]=l[k*m+n];f[b]=d,d++}a[k]++}},generateBarycentric:function(){this.isUniqueVertex()||this.generateUniqueVertex();var a=this.attributes.barycentric.value;if(!a||a.length!==3*this.faces.length){a=this.attributes.barycentric.value=new Float32Array(3*this.faces.length);for(var b=0;bc;c++){var d=this.faces[b++];a[d+c]=1}}},convertToDynamic:function(a){for(var b=0;b1&&a.texParameterf(a.TEXTURE_2D,f.TEXTURE_MAX_ANISOTROPY_EXT,this.anisotropic),36193===e){var g=c.getExtension(a,"OES_texture_half_float");g||(e=d.FLOAT)}if(this.mipmaps.length)for(var h=this.width,i=this.height,j=0;j=b.COMPRESSED_RGB_S3TC_DXT1_EXT?a.compressedTexImage2D(a.TEXTURE_2D,d,g,e,f,0,c.pixels):a.texImage2D(a.TEXTURE_2D,d,g,e,f,0,g,h,c.pixels)},generateMipmap:function(a){this.useMipmap&&!this.NPOT&&(a.bindTexture(a.TEXTURE_2D,this._cache.get("webgl_texture")),a.generateMipmap(a.TEXTURE_2D))},isPowerOfTwo:function(){var a,b;return this.image?(a=this.image.width,b=this.image.height):(a=this.width,b=this.height),0===(a&a-1)&&0===(b&b-1)},isRenderable:function(){return this.image?"CANVAS"===this.image.nodeName||this.image.complete:this.width&&this.height},bind:function(a){a.bindTexture(a.TEXTURE_2D,this.getWebGLTexture(a))},unbind:function(a){a.bindTexture(a.TEXTURE_2D,null)},load:function(a){var b=new Image,c=this;return b.onload=function(){c.dirty(),c.trigger("success",c),b.onload=null},b.onerror=function(){c.trigger("error",c),b.onerror=null},b.src=a,this.image=b,this}});return e}),define("qtek/animation/easing",[],function(){var a={Linear:function(a){return a},QuadraticIn:function(a){return a*a},QuadraticOut:function(a){return a*(2-a)},QuadraticInOut:function(a){return(a*=2)<1?.5*a*a:-.5*(--a*(a-2)-1)},CubicIn:function(a){return a*a*a},CubicOut:function(a){return--a*a*a+1},CubicInOut:function(a){return(a*=2)<1?.5*a*a*a:.5*((a-=2)*a*a+2)},QuarticIn:function(a){return a*a*a*a},QuarticOut:function(a){return 1- --a*a*a*a},QuarticInOut:function(a){return(a*=2)<1?.5*a*a*a*a:-.5*((a-=2)*a*a*a-2)},QuinticIn:function(a){return a*a*a*a*a},QuinticOut:function(a){return--a*a*a*a*a+1},QuinticInOut:function(a){return(a*=2)<1?.5*a*a*a*a*a:.5*((a-=2)*a*a*a*a+2)},SinusoidalIn:function(a){return 1-Math.cos(a*Math.PI/2)},SinusoidalOut:function(a){return Math.sin(a*Math.PI/2)},SinusoidalInOut:function(a){return.5*(1-Math.cos(Math.PI*a))},ExponentialIn:function(a){return 0===a?0:Math.pow(1024,a-1)},ExponentialOut:function(a){return 1===a?1:1-Math.pow(2,-10*a)},ExponentialInOut:function(a){return 0===a?0:1===a?1:(a*=2)<1?.5*Math.pow(1024,a-1):.5*(-Math.pow(2,-10*(a-1))+2)},CircularIn:function(a){return 1-Math.sqrt(1-a*a)},CircularOut:function(a){return Math.sqrt(1- --a*a)},CircularInOut:function(a){return(a*=2)<1?-.5*(Math.sqrt(1-a*a)-1):.5*(Math.sqrt(1-(a-=2)*a)+1)},ElasticIn:function(a){var b,c=.1,d=.4;return 0===a?0:1===a?1:(!c||1>c?(c=1,b=d/4):b=d*Math.asin(1/c)/(2*Math.PI),-(c*Math.pow(2,10*(a-=1))*Math.sin((a-b)*2*Math.PI/d)))},ElasticOut:function(a){var b,c=.1,d=.4;return 0===a?0:1===a?1:(!c||1>c?(c=1,b=d/4):b=d*Math.asin(1/c)/(2*Math.PI),c*Math.pow(2,-10*a)*Math.sin((a-b)*2*Math.PI/d)+1)},ElasticInOut:function(a){var b,c=.1,d=.4;return 0===a?0:1===a?1:(!c||1>c?(c=1,b=d/4):b=d*Math.asin(1/c)/(2*Math.PI),(a*=2)<1?-.5*c*Math.pow(2,10*(a-=1))*Math.sin((a-b)*2*Math.PI/d):.5*c*Math.pow(2,-10*(a-=1))*Math.sin((a-b)*2*Math.PI/d)+1)},BackIn:function(a){var b=1.70158;return a*a*((b+1)*a-b)},BackOut:function(a){var b=1.70158;return--a*a*((b+1)*a+b)+1},BackInOut:function(a){var b=2.5949095;return(a*=2)<1?.5*a*a*((b+1)*a-b):.5*((a-=2)*a*((b+1)*a+b)+2)},BounceIn:function(b){return 1-a.BounceOut(1-b)},BounceOut:function(a){return 1/2.75>a?7.5625*a*a:2/2.75>a?7.5625*(a-=1.5/2.75)*a+.75:2.5/2.75>a?7.5625*(a-=2.25/2.75)*a+.9375:7.5625*(a-=2.625/2.75)*a+.984375},BounceInOut:function(b){return.5>b?.5*a.BounceIn(2*b):.5*a.BounceOut(2*b-1)+.5}};return a}),define("qtek/animation/Clip",["require","./easing"],function(a){var b=a("./easing"),c=function(a){a=a||{},this.name=a.name||"",this.target=a.target,"undefined"!=typeof a.life&&(this.life=a.life),"undefined"!=typeof a.delay&&(this.delay=a.delay),"undefined"!=typeof a.gap&&(this.gap=a.gap),this.playbackRatio="undefined"!=typeof a.playbackRatio?a.playbackRatio:1,this._currentTime=(new Date).getTime(),this._startTime=this._currentTime+this.delay,this._elapsedTime=0,this._loop=void 0===a.loop?!1:a.loop,this.setLoop(this._loop),"undefined"!=typeof a.easing&&this.setEasing(a.easing),"undefined"!=typeof a.onframe&&(this.onframe=a.onframe),"undefined"!=typeof a.ondestroy&&(this.ondestroy=a.ondestroy),"undefined"!=typeof a.onrestart&&(this.onrestart=a.onrestart)};return c.prototype={gap:0,life:0,delay:0,setLoop:function(a){this._loop=a,a&&(this._loopRemained="number"==typeof a?a:1e8)},setEasing:function(a){"string"==typeof a&&(a=b[a]),this.easing=a},step:function(a){if(ab)){b>1&&(b=1);var c;return c=this.easing?this.easing(b):b,this.fire("frame",c),1==b?this._loop&&this._loopRemained>0?(this._restartInLoop(),this._loopRemained--,"restart"):(this._needsRemove=!0,"destroy"):null}},setTime:function(a){return this.step(a+this._startTime)},restart:function(){var a=(new Date).getTime();this._elapse(a);var b=this._elapsedTime%this.life;this._startTime=a-b+this.delay,this._elapsedTime=0,this._currentTime=a-b,this._needsRemove=!1},_restartInLoop:function(){var a=(new Date).getTime();this._startTime=a+this.gap,this._currentTime=a,this._elapsedTime=0},_elapse:function(a){this._elapsedTime+=(a-this._currentTime)*this.playbackRatio,this._currentTime=a},fire:function(a,b){var c="on"+a;this[c]&&this[c](this.target,b)},clone:function(){var a=new this.constructor;return a.name=this.name,a._loop=this._loop,a._loopRemained=this._loopRemained,a.life=this.life,a.gap=this.gap,a.delay=this.delay,a}},c.prototype.constructor=c,c}),define("qtek/animation/Animation",["require","./Clip","../core/Base"],function(a){function b(a,b){return a[b]}function c(a,b,c){a[b]=c}function d(a,b,c){return(b-a)*c+a}function e(a,b,c,e,f){var g=a.length;if(1==f)for(var h=0;g>h;h++)e[h]=d(a[h],b[h],c);else for(var i=a[0].length,h=0;g>h;h++)for(var j=0;i>j;j++)e[h][j]=d(a[h][j],b[h][j],c)}function f(a){return"undefined"==typeof a?!1:"string"==typeof a?!1:"number"==typeof a.length}function g(a){if(f(a)){var b=a.length;if(f(a[0])){for(var c=[],d=0;b>d;d++)c.push(n.call(a[d]));return c}return n.call(a)}return a}function h(a,b,c,d,e,f,g,h,j){var k=a.length;if(1==j)for(var l=0;k>l;l++)h[l]=i(a[l],b[l],c[l],d[l],e,f,g);else for(var m=a[0].length,l=0;k>l;l++)for(var n=0;m>n;n++)h[l][n]=i(a[l][n],b[l][n],c[l][n],d[l][n],e,f,g)}function i(a,b,c,d,e,f,g){var h=.5*(c-a),i=.5*(d-b);return(2*(b-c)+h+i)*g+(-3*(b-c)-2*h-i)*f+h*e+b}function j(a,d,e,f,g){this._tracks={},this._target=a,this._loop=d||!1,this._getter=e||b,this._setter=f||c,this._interpolater=g||null,this._clipCount=0,this._delay=0,this._doneList=[],this._onframeList=[],this._clipList=[]}var k=a("./Clip"),l=a("../core/Base"),m=window.requestAnimationFrame||window.msRequestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||function(a){setTimeout(a,16)},n=Array.prototype.slice,o=l.derive(function(){return{stage:null,_clips:[],_running:!1,_time:0}},{addClip:function(a){this._clips.push(a)},removeClip:function(a){var b=this._clips.indexOf(a);this._clips.splice(b,1)},_update:function(){for(var a=(new Date).getTime(),b=a-this._time,c=this._clips,d=c.length,e=[],f=[],g=0;d>g;g++){var h=c[g],i=h.step(a);i&&(e.push(i),f.push(h))}for(var g=0;d>g;)c[g]._needsRemove?(c[g]=c[d-1],c.pop(),d--):g++;d=e.length;for(var g=0;d>g;g++)f[g].fire(e[g]);this._time=a,this.trigger("frame",b),this.stage&&this.stage.render&&this.stage.render()},start:function(){function a(){b._running&&(m(a),b._update())}var b=this;this._running=!0,this._time=(new Date).getTime(),m(a)},stop:function(){this._running=!1},removeClipsAll:function(){this._clips=[]},animate:function(a,b){b=b||{};var c=new j(a,b.loop,b.getter,b.setter,b.interpolater);return c.animation=this,c}});return j.prototype={constructor:j,when:function(a,b){for(var c in b)this._tracks[c]||(this._tracks[c]=[],0!==a&&this._tracks[c].push({time:0,value:g(this._getter(this._target,c))})),this._tracks[c].push({time:parseInt(a),value:b[c]});return this},during:function(a){return this._onframeList.push(a),this},start:function(a){var b=this,c=this._setter,g=this._getter,j=this._interpolater,l=b._onframeList.length,m="spline"===a,n=function(){if(b._clipCount--,0===b._clipCount){b._tracks={};for(var a=b._doneList.length,c=0;a>c;c++)b._doneList[c].call(b)}},o=function(o,p){var q=o.length;if(q){var r=o[0].value,s=f(r),t=s&&f(r[0])?2:1;o.sort(function(a,b){return a.time-b.time});for(var u=o[q-1].time,v=[],w=[],x=0;q>x;x++)v.push(o[x].time/u),w.push(o[x].value);var y,x,z,A,B,C,D,E=0,F=0,G=function(a,f){if(F>f){for(y=Math.min(E+1,q-1),x=y;x>=0&&!(v[x]<=f);x--);x=Math.min(x,q-2)}else{for(x=E;q>x&&!(v[x]>f);x++);x=Math.min(x-1,q-2)}E=x,F=f;var k=v[x+1]-v[x];if(0!==k)for(z=(f-v[x])/k,m?(B=w[x],A=w[0===x?x:x-1],C=w[x>q-2?q-1:x+1],D=w[x>q-3?q-1:x+2],j?c(a,p,j(g(a,p),A,B,C,D,z)):s?h(A,B,C,D,z,z*z,z*z*z,g(a,p),t):c(a,p,i(A,B,C,D,z,z*z,z*z*z))):j?c(a,p,j(g(a,p),w[x],w[x+1],z)):s?e(w[x],w[x+1],z,g(a,p),t):c(a,p,d(w[x],w[x+1],z)),x=0;l>x;x++)b._onframeList[x](a,f)},H=new k({target:b._target,life:u,loop:b._loop,delay:b._delay,onframe:G,ondestroy:n});a&&"spline"!==a&&H.setEasing(a),b._clipList.push(H),b._clipCount++,b.animation.addClip(H)}};for(var p in this._tracks)o(this._tracks[p],p);return this},stop:function(){for(var a=0;aa)this.inputs.unshift(d);else if(this.inputs[e-1].position<=a)this.inputs.push(d);else{var f=this._findKey(a);this.inputs.splice(f,d)}return d},d.prototype.step=function(a){var c=b.prototype.step.call(this,a);return"destroy"!==c&&this.setTime(this._elapsedTime),c},d.prototype.setTime=function(a){var c=this.position,d=this.inputs,e=d.length,f=d[0].position,g=d[e-1].position;if(f>=c||c>=g){var h=f>=c?d[0]:d[e-1],i=h.clip,j=h.offset;i.setTime((a+j)%i.life),i.output instanceof b?this.output.copy(i.output):this.output.copy(i)}else{var k=this._findKey(c),l=d[k],m=d[k+1],n=l.clip,o=m.clip;n.setTime((a+l.offset)%n.life),o.setTime((a+m.offset)%o.life);var p=(this.position-l.position)/(m.position-l.position),q=n.output instanceof b?n.output:n,r=o.output instanceof b?o.output:o;this.output.blend1D(q,r,p)}},d.prototype.clone=function(a){var c=b.prototype.clone.call(this);c.output=this.output.clone();for(var d=0;de;e++)a>=c[e].position&&a=0;e--)a>=c[e].position&&a=0&&(this._cacheKey=b,this._cachePosition=a),b},d}),define("qtek/util/delaunay",["require"],function(){function a(a){var b,c,d,e,f,g,h=Number.POSITIVE_INFINITY,i=Number.POSITIVE_INFINITY,j=Number.NEGATIVE_INFINITY,k=Number.NEGATIVE_INFINITY;for(b=a.length;b--;)a[b][0]j&&(j=a[b][0]),a[b][1]k&&(k=a[b][1]);c=j-h,d=k-i,e=Math.max(c,d),f=h+.5*c,g=i+.5*d,a.push([f-20*e,g-e],[f,g+20*e],[f+20*e,g-e])}function b(a,b,c,d){var e,f,g,h,i,j,k=a[b],l=a[c],m=a[d],n=l[0]-k[0],o=l[1]-k[1],p=m[0]-k[0],q=m[1]-k[1],r=n*(k[0]+l[0])+o*(k[1]+l[1]),s=p*(k[0]+m[0])+q*(k[1]+m[1]),t=2*(n*(m[1]-l[1])-o*(m[0]-l[0]));return Math.abs(t)<1e-6?(e=Math.min(k[0],l[0],m[0]),f=Math.min(k[1],l[1],m[1]),g=.5*(Math.max(k[0],l[0],m[0])-e),h=.5*(Math.max(k[1],l[1],m[1])-f),i=e+g,j=f+h):(i=(q*r-o*s)/t,j=(n*s-p*r)/t,g=i-k[0],h=j-k[1]),{i:b,j:c,k:d,x:i,y:j,r:g*g+h*h}}function c(a){var b,c,d,e,f,g=a.length;a:for(;g;)for(c=a[--g],b=a[--g],d=g;d;)if(f=a[--d],e=a[--d],b===e&&c===f||b===f&&c===e){a.splice(g,2),a.splice(d,2),g-=2;continue a}}var d={triangulate:function(d,e){var f,g,h,i,j,k,l,m,n,o,p,q=d.length;if(3>q)return[];if(d=d.slice(0),e)for(f=q;f--;)d[f]=d[f][e];for(h=new Array(q),f=q;f--;)h[f]=f;for(h.sort(function(a,b){return d[b][0]-d[a][0]}),a(d),i=[b(d,q+0,q+1,q+2)],j=[],k=[],f=h.length;f--;){for(p=h[f],k.length=0,g=i.length;g--;)l=d[p][0]-i[g].x,l>0&&l*l>i[g].r?(j.push(i[g]),i.splice(g,1)):(m=d[p][1]-i[g].y,l*l+m*m>i[g].r||(k.push(i[g].i,i[g].j,i[g].j,i[g].k,i[g].k,i[g].i),i.splice(g,1)));for(c(k),g=k.length;g;)o=k[--g],n=k[--g],i.push(b(d,n,o,p))}for(f=i.length;f--;)j.push(i[f]);for(i.length=0,f=j.length;f--;)if(j[f].ia[0][0]&&b[0]>a[1][0]&&b[0]>a[2][0]||b[1]a[0][1]&&b[1]>a[1][1]&&b[1]>a[2][1])return null;var c=a[1][0]-a[0][0],d=a[2][0]-a[0][0],e=a[1][1]-a[0][1],f=a[2][1]-a[0][1],g=c*f-d*e;if(0===g)return null;var h=(f*(b[0]-a[0][0])-d*(b[1]-a[0][1]))/g,i=(c*(b[1]-a[0][1])-e*(b[0]-a[0][0]))/g;return 0>h||0>i||h+i>1?null:[h,i]}};return d}),define("qtek/animation/Blend2DClip",["require","./Clip","../util/delaunay","../math/Vector2"],function(a){var b=a("./Clip"),c=a("../util/delaunay"),d=a("../math/Vector2"),e=function(a){a=a||{},b.call(this,a),this.output=a.output||null,this.inputs=a.inputs||[],this.position=new d,this._cacheTriangle=null,this._triangles=[],this._updateTriangles()};return e.prototype=new b,e.prototype.constructor=e,e.prototype.addInput=function(a,b,c){var d={position:a,clip:b,offset:c||0};return this.inputs.push(d),this.life=Math.max(b.life,this.life),this._updateTriangles(),d},e.prototype._updateTriangles=function(){var a=this.inputs.map(function(a){return a.position});this._triangles=c.triangulate(a,"_array")},e.prototype.step=function(a){var c=b.prototype.step.call(this,a);return"destroy"!==c&&this.setTime(this._elapsedTime),c},e.prototype.setTime=function(a){var c=this._findTriangle(this.position);if(c){var d=c[1],e=c[2],f=c[0],g=this.inputs[f.indices[0]],h=this.inputs[f.indices[1]],i=this.inputs[f.indices[2]],j=g.clip,k=h.clip,l=i.clip;j.setTime((a+g.offset)%j.life),k.setTime((a+h.offset)%k.life),l.setTime((a+i.offset)%l.life);var m=j.output instanceof b?j.output:j,n=k.output instanceof b?k.output:k,o=l.output instanceof b?l.output:l;this.output.blend2D(m,n,o,d,e)}},e.prototype.clone=function(a){var c=b.prototype.clone.call(this);c.output=this.output.clone();for(var d=0;d=a.time)return this.keyFrames.splice(b,0,a),b}this.life=a.time,this.keyFrames.push(a)},g.prototype.addKeyFrames=function(a){for(var c=0;cg[g.length-1].time)){if(a=h-1?h-1:this._cacheKey+1,j=i;j>=0;j--)if(g[j].time<=a&&g[j][b])c=g[j],this._cacheKey=j,this._cacheTime=a;else if(g[j][b]){d=g[j];break}}else for(var j=this._cacheKey;h>j;j++)if(g[j].time<=a&&g[j][b])c=g[j],this._cacheKey=j,this._cacheTime=a;else if(g[j][b]){d=g[j];break}if(c&&d){var k=(a-c.time)/(d.time-c.time);k=Math.max(Math.min(k,1),0),"rotation"===b?e.slerp(this[b],c[b],d[b],k):f.lerp(this[b],c[b],d[b],k)}else this._cacheKey=0,this._cacheTime=0}},g.prototype.blend1D=function(a,b,c){f.lerp(this.position,a.position,b.position,c),f.lerp(this.scale,a.scale,b.scale,c),e.slerp(this.rotation,a.rotation,b.rotation,c)},g.prototype.blend2D=function(){var a=e.create(),b=e.create();return function(c,d,f,g,h){var i=1-g-h;this.position[0]=c.position[0]*i+d.position[0]*g+f.position[0]*h,this.position[1]=c.position[1]*i+d.position[1]*g+f.position[1]*h,this.position[2]=c.position[2]*i+d.position[2]*g+f.position[2]*h,this.scale[0]=c.scale[0]*i+d.scale[0]*g+f.scale[0]*h,this.scale[1]=c.scale[1]*i+d.scale[1]*g+f.scale[1]*h,this.scale[2]=c.scale[2]*i+d.scale[2]*g+f.scale[2]*h;var j=g+h;0===j?e.copy(this.rotation,c.rotation):(e.slerp(a,c.rotation,d.rotation,j),e.slerp(b,c.rotation,f.rotation,j),e.slerp(this.rotation,a,b,h/j))}}(),g.prototype.additiveBlend=function(a,b){f.add(this.position,a.position,b.position),f.add(this.scale,a.scale,b.scale),e.multiply(this.rotation,b.rotation,a.rotation)},g.prototype.subtractiveBlend=function(a,b){f.sub(this.position,a.position,b.position),f.sub(this.scale,a.scale,b.scale),e.invert(this.rotation,b.rotation),e.multiply(this.rotation,this.rotation,a.rotation)},g.prototype.getSubClip=function(){console.warn("TODO")},g.prototype.clone=function(){var a=c.prototype.clone.call(this);return a.keyFrames=this.keyFrames,f.copy(a.position,this.position),e.copy(a.rotation,this.rotation),f.copy(a.scale,this.scale),a},g}),define("qtek/animation/SamplerClip",["require","./Clip","./TransformClip","../dep/glmatrix"],function(a){function b(a,b,c,d,e,f){var g=b[e],h=b[e+1],i=b[e+2];return a[0]=g+d*(c[f]-g),a[1]=h+d*(c[f+1]-h),a[2]=i+d*(c[f+2]-i),a}function c(a,b,c,d,e,f){var g,h,i,j,k,l=b[0+e],m=b[1+e],n=b[2+e],o=b[3+e],p=c[0+f],q=c[1+f],r=c[2+f],s=c[3+f];return h=l*p+m*q+n*r+o*s,0>h&&(h=-h,p=-p,q=-q,r=-r,s=-s),1-h>1e-6?(g=Math.acos(h),i=Math.sin(g),j=Math.sin((1-d)*g)/i,k=Math.sin(d*g)/i):(j=1-d,k=d),a[0]=j*l+k*p,a[1]=j*m+k*q,a[2]=j*n+k*r,a[3]=j*o+k*s,a}var d=a("./Clip"),e=a("./TransformClip"),f=a("../dep/glmatrix"),g=f.quat,h=f.vec3,i=function(a){a=a||{},d.call(this,a),this.position=h.create(),this.rotation=g.create(),this.scale=h.fromValues(1,1,1),this.channels={time:null,position:null,rotation:null,scale:null},this._cacheKey=0,this._cacheTime=0};return i.prototype=Object.create(d.prototype),i.prototype.constructor=i,i.prototype.step=function(a){var b=d.prototype.step.call(this,a);return"destroy"!==b&&this.setTime(this._elapsedTime),b},i.prototype.setTime=function(a){if(this.channels.time){var d=this.channels,e=d.time.length,f=-1;if(a=0;h--)if(d.time[h-1]<=a&&d.time[h]>a){f=h-1;break}}else for(var h=this._cacheKey;e-1>h;h++)if(d.time[h]<=a&&d.time[h+1]>a){f=h;break}if(f>-1){this._cacheKey=f,this._cacheTime=a;var i=f,j=f+1,k=d.time[i],l=d.time[j],m=(a-k)/(l-k);d.rotation&&c(this.rotation,d.rotation,d.rotation,m,4*i,4*j),d.position&&b(this.position,d.position,d.position,m,3*i,3*j),d.scale&&b(this.scale,d.scale,d.scale,m,3*i,3*j)}f==e-2&&(this._cacheKey=0,this._cacheTime=0)}},i.prototype.getSubClip=function(a,b){var c=new i({name:this.name}),d=this.channels.time[0];a=Math.min(Math.max(a,d),this.life),b=Math.min(Math.max(b,d),this.life);var e=this._findRange(a),f=this._findRange(b),g=f[0]-e[0]+1; -0===e[1]&&0===f[1]&&(g-=1),this.channels.rotation&&(c.channels.rotation=new Float32Array(4*g)),this.channels.position&&(c.channels.position=new Float32Array(3*g)),this.channels.scale&&(c.channels.scale=new Float32Array(3*g)),this.channels.time&&(c.channels.time=new Float32Array(g)),this.setTime(a);for(var h=0;3>h;h++)c.channels.rotation[h]=this.rotation[h],c.channels.position[h]=this.position[h],c.channels.scale[h]=this.scale[h];c.channels.time[0]=0,c.channels.rotation[3]=this.rotation[3];for(var h=1;g-1>h;h++){for(var j,k=0;3>k;k++)j=e[0]+h,c.channels.rotation[4*h+k]=this.channels.rotation[4*j+k],c.channels.position[3*h+k]=this.channels.position[3*j+k],c.channels.scale[3*h+k]=this.channels.scale[3*j+k];c.channels.time[h]=this.channels.time[j]-a,c.channels.rotation[4*h+3]=this.channels.rotation[4*j+3]}this.setTime(b);for(var h=0;3>h;h++)c.channels.rotation[4*(g-1)+h]=this.rotation[h],c.channels.position[3*(g-1)+h]=this.position[h],c.channels.scale[3*(g-1)+h]=this.scale[h];return c.channels.time[g-1]=b-a,c.channels.rotation[4*(g-1)+3]=this.rotation[3],c.life=b-a,c},i.prototype._findRange=function(a){for(var b=this.channels,c=b.time.length,d=-1,e=0;c-1>e;e++)b.time[e]<=a&&b.time[e+1]>a&&(d=e);var f=0;if(d>=0)var g=b.time[d],h=b.time[d+1],f=(a-g)/(h-g);return[d,f]},i.prototype.blend1D=e.prototype.blend1D,i.prototype.blend2D=e.prototype.blend2D,i.prototype.additiveBlend=e.prototype.additiveBlend,i.prototype.subtractiveBlend=e.prototype.subtractiveBlend,i.prototype.clone=function(){var a=d.prototype.clone.call(this);return a.channels={time:this.channels.time||null,position:this.channels.position||null,rotation:this.channels.rotation||null,scale:this.channels.scale||null},h.copy(a.position,this.position),g.copy(a.rotation,this.rotation),h.copy(a.scale,this.scale),a},i}),define("qtek/animation/SkinningClip",["require","./Clip","./TransformClip","../dep/glmatrix"],function(a){var b=a("./Clip"),c=a("./TransformClip"),d=a("../dep/glmatrix"),e=d.quat,f=d.vec3,g=function(a){if(a=a||{},b.call(this,a),this.jointClips=[],this.life=0,a.jointClips&&a.jointClips.length>0)for(var d=0;d=h;h++)for(var i=h/a,j=0;b>=j;j++){var k=j/b;if(d.push([2*k-1,2*i-1,0]),e&&e.push([k,i]),f&&f.push([0,0,1]),b>j&&a>h){var l=j+h*(b+1);g.push([l,l+1,l+b+1]),g.push([l+b+1,l+1,l+b+2])}}this.boundingBox=new c,this.boundingBox.min.set(-1,-1,0),this.boundingBox.max.set(1,1,0)}});return d}),define("qtek/shader/source/compositor/vertex.essl",[],function(){return"\n@export buildin.compositor.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n\nvarying vec2 v_Texcoord;\n\nvoid main()\n{\n v_Texcoord = texcoord;\n gl_Position = worldViewProjection * vec4(position, 1.0);\n}\n\n@end"}),define("qtek/compositor/Pass",["require","../core/Base","../camera/Orthographic","../geometry/Plane","../Shader","../Material","../Mesh","../core/glinfo","../core/glenum","../shader/source/compositor/vertex.essl"],function(a){var b=a("../core/Base"),c=a("../camera/Orthographic"),d=a("../geometry/Plane"),e=a("../Shader"),f=a("../Material"),g=a("../Mesh"),h=a("../core/glinfo"),i=a("../core/glenum");e["import"](a("../shader/source/compositor/vertex.essl"));var j=new d,k=new g({geometry:j}),l=new c,m=b.derive(function(){return{fragment:"",outputs:null,material:null}},function(){var a=new e({vertex:e.source("buildin.compositor.vertex"),fragment:this.fragment}),b=new f({shader:a});a.enableTexturesAll(),this.material=b},{setUniform:function(a,b){var c=this.material.uniforms[a];c&&(c.value=b)},getUniform:function(a){var b=this.material.uniforms[a];return b?b.value:void 0},attachOutput:function(a,b){this.outputs||(this.outputs={}),b=b||i.COLOR_ATTACHMENT0,this.outputs[b]=a},detachOutput:function(a){for(var b in this.outputs)this.outputs[b]===a&&(this.outputs[b]=null)},bind:function(a,b){if(this.outputs)for(var c in this.outputs){var d=this.outputs[c];d&&b.attach(a.gl,d,c)}b&&b.bind(a)},unbind:function(a,b){b.unbind(a)},render:function(a,b){var c=a.gl;if(b){this.bind(a,b);var d=h.getExtension(c,"EXT_draw_buffers");if(d&&this.outputs){var e=[];for(var f in this.outputs)f=+f,f>=c.COLOR_ATTACHMENT0&&f<=c.COLOR_ATTACHMENT0+8&&e.push(f);d.drawBuffersEXT(e)}}this.trigger("beforerender",this,a),c.disable(c.BLEND),c.clear(c.DEPTH_BUFFER_BIT),this.renderQuad(a),this.trigger("afterrender",this,a),b&&this.unbind(a,b)},renderQuad:function(a){k.material=this.material,a.renderQueue([k],l)}});return m}),define("qtek/compositor/Node",["require","../core/Base","./Pass","../FrameBuffer"],function(a){var b=a("../core/Base"),c=a("./Pass"),d=a("../FrameBuffer"),e=b.derive(function(){return{name:"",inputs:{},outputs:null,shader:"",inputLinks:{},outputLinks:{},pass:null,_prevOutputTextures:{},_outputTextures:{},_outputReferences:{},_rendering:!1,_rendered:!1,_compositor:null}},function(){var a=new c({fragment:this.shader});this.pass=a,this.outputs&&(this.frameBuffer=new d({depthBuffer:!1}))},{render:function(a,b){this.trigger("beforerender",a),this._rendering=!0;var c=a.gl;for(var d in this.inputLinks){var e=this.inputLinks[d],f=e.node.getOutput(a,e.pin);this.pass.setUniform(d,f)}if(this.outputs){this.pass.outputs={};for(var g in this.outputs){var h=this.updateParameter(g,a),i=this.outputs[g],j=this._compositor.allocateTexture(h);this._outputTextures[g]=j;var k=i.attachment||c.COLOR_ATTACHMENT0;"string"==typeof k&&(k=c[k]),this.pass.outputs[k]=j}this.pass.render(a,this.frameBuffer)}else this.pass.outputs=null,this.pass.render(a,b);for(var d in this.inputLinks){var e=this.inputLinks[d];e.node.removeReference(e.pin)}this._rendering=!1,this._rendered=!0,this.trigger("afterrender",a)},updateParameter:function(a,b){var c=this.outputs[a],d=c.parameters,e=c._parametersCopy;if(e||(e=c._parametersCopy={}),d)for(var f in d)"width"!==f&&"height"!==f&&(e[f]=d[f]);var g,h;return g=d.width instanceof Function?d.width(b):d.width,h=d.height instanceof Function?d.height(b):d.height,(e.width!==g||e.height!==h)&&this._outputTextures[a]&&this._outputTextures[a].dispose(b.gl),e.width=g,e.height=h,e},setParameter:function(a,b){this.pass.setUniform(a,b)},getParameter:function(a){return this.pass.getUniform(a)},setParameters:function(a){for(var b in a)this.setParameter(b,a[b])},setShader:function(a){var b=this.pass.material;b.shader.setFragment(a),b.attachShader(b.shader,!0)},getOutput:function(a,b){if(void 0===b)return b=a,this._outputTextures[b];var c=this.outputs[b];if(c)return this._rendered?c.outputLastFrame?this._prevOutputTextures[b]:this._outputTextures[b]:this._rendering?(this._prevOutputTextures[b]||(this._prevOutputTextures[b]=this._compositor.allocateTexture(c.parameters||{})),this._prevOutputTextures[b]):(this.render(a),this._outputTextures[b])},removeReference:function(a){if(this._outputReferences[a]--,0===this._outputReferences[a]){var b=this.outputs[a];b.keepLastFrame?(this._prevOutputTextures[a]&&this._compositor.releaseTexture(this._prevOutputTextures[a]),this._prevOutputTextures[a]=this._outputTextures[a]):this._compositor.releaseTexture(this._outputTextures[a])}},link:function(a,b,c){this.inputLinks[a]={node:b,pin:c},b.outputLinks[c]||(b.outputLinks[c]=[]),b.outputLinks[c].push({node:this,pin:a});var d=this.pass.material.shader;d.enableTexture(a)},clear:function(){this.inputLinks={},this.outputLinks={};var a=this.pass.material.shader;a.disableTexturesAll()},updateReference:function(a){if(!this._rendering){this._rendering=!0;for(var b in this.inputLinks){var c=this.inputLinks[b];c.node.updateReference(c.pin)}this._rendering=!1}a&&this._outputReferences[a]++},beforeFrame:function(){this._rendered=!1;for(var a in this.outputLinks)this._outputReferences[a]=0},afterFrame:function(){for(var a in this.outputLinks)if(this._outputReferences[a]>0){var b=this.outputs[a];b.keepLastFrame?(this._prevOutputTextures[a]&&this._compositor.releaseTexture(this._prevOutputTextures[a]),this._prevOutputTextures[a]=this._outputTextures[a]):this._compositor.releaseTexture(this._outputTextures[a])}}});return e}),define("qtek/compositor/SceneNode",["require","./Node","../core/glinfo"],function(a){var b=a("./Node"),c=a("../core/glinfo"),d=b.derive({name:"scene",scene:null,camera:null,autoUpdateScene:!0,preZ:!1},function(){this.frameBuffer&&(this.frameBuffer.depthBuffer=!0)},{render:function(a){this._rendering=!0;var b=a.gl;this.trigger("beforerender");var d;if(this.outputs){var e=this.frameBuffer;for(var f in this.outputs){var g=this.updateParameter(f,a),h=this.outputs[f],i=this._compositor.allocateTexture(g);this._outputTextures[f]=i;var j=h.attachment||b.COLOR_ATTACHMENT0;"string"==typeof j&&(j=b[j]),e.attach(a.gl,i,j)}e.bind(a);var k=c.getExtension(b,"EXT_draw_buffers");if(k){var l=[];for(var j in this.outputs)j=parseInt(j),j>=b.COLOR_ATTACHMENT0&&j<=b.COLOR_ATTACHMENT0+8&&l.push(j);k.drawBuffersEXT(l)}d=a.render(this.scene,this.camera,!this.autoUpdateScene,this.preZ),e.unbind(a)}else d=a.render(this.scene,this.camera,!this.autoUpdateScene,this.preZ);this.trigger("afterrender",d),this._rendering=!1,this._rendered=!0}});return d}),define("qtek/compositor/TextureNode",["require","./Node","../Shader"],function(a){var b=a("./Node"),c=a("../Shader"),d=b.derive(function(){return{shader:c.source("buildin.compositor.output"),texture:null}},{render:function(a,b){this._rendering=!0;var c=a.gl;if(this.pass.setUniform("texture",this.texture),this.outputs){this.pass.outputs={};for(var d in this.outputs){var e=this.updateParameter(d,a),f=this.outputs[d],g=this._compositor.allocateTexture(e);this._outputTextures[d]=g;var h=f.attachment||c.COLOR_ATTACHMENT0;"string"==typeof h&&(h=c[h]),this.pass.outputs[h]=g}this.pass.render(a,this.frameBuffer)}else this.pass.outputs=null,this.pass.render(a,b);this._rendering=!1,this._rendered=!0}});return d}),define("qtek/core/Event",["require","./Base"],function(a){var b=a("./Base"),c=b.derive({cancelBubble:!1},{stopPropagation:function(){this.cancelBubble=!0}});return c["throw"]=function(a,b,d){var e=new c(d);for(e.type=a,e.target=b;b&&!e.cancelBubble;)e.currentTarget=b,b.trigger(a,e),b=b.getParent()},c}),define("qtek/core/LinkedList",["require"],function(){var a=function(){this.head=null,this.tail=null,this._length=0};return a.prototype.insert=function(b){var c=new a.Entry(b);return this.insertEntry(c),c},a.prototype.insertAt=function(b,c){if(!(0>b)){for(var d=this.head,e=0;d&&e!=b;)d=d.next,e++;if(d){var f=new a.Entry(c),g=d.prev;g.next=f,f.prev=g,f.next=d,d.prev=f}else this.insert(c)}},a.prototype.insertEntry=function(a){this.head?(this.tail.next=a,a.prev=this.tail,this.tail=a):this.head=this.tail=a,this._length++},a.prototype.remove=function(a){var b=a.prev,c=a.next;b?b.next=c:this.head=c,c?c.prev=b:this.tail=b,a.next=a.prev=null,this._length--},a.prototype.removeAt=function(a){if(!(0>a)){for(var b=this.head,c=0;b&&c!=a;)b=b.next,c++;return b?(this.remove(b),b.value):void 0}},a.prototype.getHead=function(){return this.head?this.head.value:void 0},a.prototype.getTail=function(){return this.tail?this.tail.value:void 0},a.prototype.getAt=function(a){if(!(0>a)){for(var b=this.head,c=0;b&&c!=a;)b=b.next,c++;return b.value}},a.prototype.indexOf=function(a){for(var b=this.head,c=0;b;){if(b.value===a)return c;b=b.next,c++}},a.prototype.length=function(){return this._length},a.prototype.isEmpty=function(){return 0===this._length},a.prototype.forEach=function(a,b){for(var c=this.head,d=0,e="undefined"!=typeof b;c;)e?a.call(b,c.value,d):a(c.value,d),c=c.next,d++},a.prototype.clear=function(){this.tail=this.head=null,this._length=0},a.Entry=function(a){this.value=a,this.next=null,this.prev=null},a}),define("qtek/core/LRU",["require","./LinkedList"],function(a){var b=a("./LinkedList"),c=function(a){this._list=new b,this._map={},this._maxSize=a||10};return c.prototype.setMaxSize=function(a){this._maxSize=a},c.prototype.put=function(a,b){if("undefined"==typeof this._map[a]){var c=this._list.length();if(c>=this._maxSize&&c>0){var d=this._list.head;this._list.remove(d),delete this._map[d.key]}var e=this._list.insert(b);e.key=a,this._map[a]=e}},c.prototype.get=function(a){var b=this._map[a];return"undefined"!=typeof b?(b!==this._list.tail&&(this._list.remove(b),this._list.insertEntry(b)),b.value):void 0},c.prototype.remove=function(a){var b=this._map[a];"undefined"!=typeof b&&(delete this._map[a],this._list.remove(b))},c.prototype.clear=function(){this._list.clear(),this._map={}},c}),define("qtek/deferred/StandardMaterial",["require","../core/Base"],function(a){var b=a("../core/Base"),c=function(){},d=b.derive({color:null,emission:null,specularColor:null,glossiness:.5,diffuseMap:null,normalMap:null,environmentMap:null,diffuseAlphaUsage:"none",uvRepeat:null,uvOffset:null},function(){this.color=this.color||[1,1,1],this.emission=this.emission||[0,0,0],this.specularColor=this.specularColor||[.5,.5,.5],this.uvRepeat=this.uvRepeat||[1,1],this.uvOffset=this.uvOffset||[0,0],this.shader=new c},{});return d}),define("qtek/geometry/Sphere",["require","../DynamicGeometry","../dep/glmatrix","../math/BoundingBox"],function(a){var b=a("../DynamicGeometry"),c=a("../dep/glmatrix"),d=c.vec3,e=c.vec2,f=a("../math/BoundingBox"),g=b.derive({widthSegments:20,heightSegments:20,phiStart:0,phiLength:2*Math.PI,thetaStart:0,thetaLength:Math.PI,radius:1},function(){this.build()},{build:function(){var a=this.attributes.position.value,b=this.attributes.texcoord0.value,c=this.attributes.normal.value;a.length=0,b.length=0,c.length=0,this.faces.length=0;var g,h,i,j,k,l,m,n,o=this.heightSegments,p=this.widthSegments,q=this.radius,r=this.phiStart,s=this.phiLength,t=this.thetaStart,u=this.thetaLength,q=this.radius;for(m=0;o>=m;m++)for(l=0;p>=l;l++)j=l/p,k=m/o,g=-q*Math.cos(r+j*s)*Math.sin(t+k*u),h=q*Math.cos(t+k*u),i=q*Math.sin(r+j*s)*Math.sin(t+k*u),a.push(d.fromValues(g,h,i)),b.push(e.fromValues(j,k)),n=d.fromValues(g,h,i),d.normalize(n,n),c.push(n);var v,w,x,y,z=this.faces,A=p+1;for(m=0;o>m;m++)for(l=0;p>l;l++)w=m*A+l,v=m*A+l+1,y=(m+1)*A+l+1,x=(m+1)*A+l,z.push(d.fromValues(v,w,y)),z.push(d.fromValues(w,x,y));this.boundingBox=new f,this.boundingBox.max.set(q,q,q),this.boundingBox.min.set(-q,-q,-q)}});return g}),define("qtek/geometry/Cone",["require","../DynamicGeometry","../math/BoundingBox","../dep/glmatrix"],function(a){var b=a("../DynamicGeometry"),c=a("../math/BoundingBox"),d=a("../dep/glmatrix"),e=d.vec3,f=d.vec2,g=b.derive({topRadius:0,bottomRadius:1,height:2,capSegments:20,heightSegments:1},function(){this.build()},{build:function(){var a=this.attributes.position.value,b=this.attributes.texcoord0.value,d=this.faces;a.length=0,b.length=0,d.length=0;for(var g=2*Math.PI/this.capSegments,h=[],i=[],j=this.topRadius,k=this.bottomRadius,l=this.height/2,m=e.fromValues(0,l,0),n=e.fromValues(0,-l,0),o=0;oo;o++)a.push(h[o]),b.push(f.fromValues(o/s,0)),d.push([0,o+1,(o+1)%s+1]);var t=a.length;a.push(n),b.push(f.fromValues(0,1));for(var o=0;s>o;o++)a.push(i[o]),b.push(f.fromValues(o/s,0)),d.push([t,t+((o+1)%s+1),t+o+1]);t=a.length;for(var u=this.heightSegments,o=0;s>o;o++)for(var v=0;u+1>v;v++){var w=v/u;a.push(e.lerp(e.create(),h[o],i[o],w)),b.push(f.fromValues(o/s,w))}for(var o=0;s>o;o++)for(var v=0;u>v;v++){var x=o*(u+1)+v,y=(o+1)%s*(u+1)+v,z=(o+1)%s*(u+1)+v+1,A=o*(u+1)+v+1;d.push([t+y,t+x,t+A]),d.push([t+A,t+z,t+y])}this.generateVertexNormals(),this.boundingBox=new c;var B=Math.max(this.topRadius,this.bottomRadius);this.boundingBox.min.set(-B,-this.height/2,-B),this.boundingBox.max.set(B,this.height/2,B)}});return g}),define("qtek/shader/source/util.essl",[],function(){return"// Use light attenuation formula in\n// http://blog.slindev.com/2011/01/10/natural-light-attenuation/\n@export buildin.util.calculate_attenuation\n\nuniform float attenuationFactor : 5.0;\n\nfloat lightAttenuation(float dist, float range)\n{\n float attenuation = 1.0;\n if( range > 0.0)\n {\n attenuation = dist*dist/(range*range);\n float att_s = attenuationFactor;\n attenuation = 1.0/(attenuation*att_s+1.0);\n att_s = 1.0/(att_s+1.0);\n attenuation = attenuation - att_s;\n attenuation /= 1.0 - att_s;\n }\n return clamp(attenuation, 0.0, 1.0);\n}\n\n@end\n\n//http://codeflow.org/entries/2012/aug/02/easy-wireframe-display-with-barycentric-coordinates/\n@export buildin.util.edge_factor\n\nfloat edgeFactor(float width)\n{\n vec3 d = fwidth(v_Barycentric);\n vec3 a3 = smoothstep(vec3(0.0), d * width, v_Barycentric);\n return min(min(a3.x, a3.y), a3.z);\n}\n\n@end\n\n// Pack depth\n// Float value can only be [0.0 - 1.0) ?\n@export buildin.util.encode_float\nvec4 encodeFloat( const in float depth )\n{\n\n const vec4 bitShifts = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );\n\n const vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );\n vec4 res = fract( depth * bitShifts );\n res -= res.xxyz * bit_mask;\n\n return res;\n}\n@end\n\n@export buildin.util.decode_float\nfloat decodeFloat(const in vec4 colour)\n{\n const vec4 bitShifts = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n return dot(colour, bitShifts);\n}\n@end\n\n// http://graphicrants.blogspot.com/2009/04/rgbm-color-encoding.html\n@export buildin.util.rgbm_decode\nvec3 RGBMDecode(vec4 rgbm, float range) {\n return range * rgbm.rgb * rgbm.a;\n}\n@end\n\n@export buildin.util.rgbm_encode\nvec4 RGBMEncode(vec3 color, float range) {\n vec4 rgbm;\n color *= 1.0 / range;\n rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 1e-6 ) ), 0.0, 1.0);\n rgbm.a = ceil(rgbm.a * 255.0) / 255.0;\n rgbm.rgb = color / rgbm.a;\n return rgbm;\n}\n@end\n\n\n@export buildin.chunk.skin_matrix\n\n// Weighted Sum Skinning Matrix\nmat4 skinMatrixWS;\nif (joint.x >= 0.0)\n{\n skinMatrixWS = skinMatrix[int(joint.x)] * weight.x;\n}\nif (joint.y >= 0.0)\n{\n skinMatrixWS += skinMatrix[int(joint.y)] * weight.y;\n}\nif (joint.z >= 0.0)\n{\n skinMatrixWS += skinMatrix[int(joint.z)] * weight.z;\n}\nif (joint.w >= 0.0)\n{\n skinMatrixWS += skinMatrix[int(joint.w)] * (1.0-weight.x-weight.y-weight.z);\n}\n@end\n"}),define("qtek/shader/source/deferred/gbuffer.essl",[],function(){return"@export buildin.deferred.gbuffer.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\n\nuniform vec2 uvRepeat;\nuniform vec2 uvOffset;\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 normal : NORMAL;\nattribute vec4 tangent : TANGENT;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\n\n#ifdef NORMALMAP_ENABLED\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\nvarying vec4 v_ProjPos;\n\nvoid main()\n{\n \n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n // Upper skinMatrix \n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n skinnedTangent = (skinMatrixWS * vec4(tangent.xyz, 0.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\n \n #ifdef NORMALMAP_ENABLED\n v_Tangent = normalize((worldInverseTranspose * vec4(skinnedTangent, 0.0)).xyz);\n v_Bitangent = normalize(cross(v_Normal, v_Tangent) * tangent.w);\n #endif\n\n v_ProjPos = gl_Position;\n}\n\n\n@end\n\n\n@export buildin.deferred.gbuffer.fragment\n\nuniform sampler2D diffuseMap;\nuniform float glossiness;\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\n\n#ifdef NORMALMAP_ENABLED\nuniform sampler2D normalMap;\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\nvarying vec4 v_ProjPos;\n\nvoid main()\n{\n vec3 N = v_Normal;\n #ifdef NORMALMAP_ENABLED\n N = texture2D(normalMap, v_Texcoord).xyz * 2.0 - 1.0;\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\n N = tbn * N;\n #endif\n\n // N.z can be recovered from sqrt(1 - dot(N.xy, N.xy));\n gl_FragColor.rg = (N.xy + 1.0) * 0.5;\n\n // Depth\n gl_FragColor.b = v_ProjPos.z / v_ProjPos.w;\n\n gl_FragColor.a = glossiness;\n #ifdef DIFFUSEMAP_ENABLED\n // Ouptut glossiness to alpha channel\n gl_FragColor.a *= texture2D(diffuseMap, v_Texcoord).a;\n #endif\n\n}\n@end"}),define("qtek/shader/source/deferred/chunk.essl",[],function(){return"@export buildin.deferred.chunk.light_head\nuniform sampler2D normalTex;\nuniform vec2 viewportSize;\n\nuniform mat4 viewProjectionInv;\n\nconst vec3 LUM = vec3(0.2125, 0.7154, 0.0721);\n@end\n\n@export buildin.deferred.chunk.gbuffer_read\n vec2 uv = gl_FragCoord.xy / viewportSize;\n\n vec4 tex = texture2D(normalTex, uv);\n // Is empty\n if (dot(tex.rgb, vec3(1.0)) == 0.0) {\n discard;\n }\n\n vec3 N;\n N.xy = tex.rg * 2.0 - 1.0;\n N.z = sqrt(1.0 - dot(N.xy, N.xy));\n\n // Depth value in depth texture is 0 - 1\n // float z = texture2D(depthTex, uv).r * 2.0 - 1.0;\n float z = tex.b;\n\n float glossiness = tex.a;\n\n vec2 xy = uv * 2.0 - 1.0;\n\n vec4 projectedPos = vec4(xy, z, 1.0);\n vec4 p4 = viewProjectionInv * projectedPos;\n\n vec3 position = p4.xyz / p4.w;\n@end\n\n@export buildin.deferred.chunk.light_equation\n\nfloat D_Phong(float g, float ndh) {\n // from black ops 2\n float a = pow(8192.0, g);\n return (a + 2.0) / 8.0 * pow(ndh, a);\n}\n\n@end" -}),define("qtek/shader/source/deferred/lightvolume.essl",[],function(){return"@export buildin.deferred.light_volume.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\nvarying vec3 v_Position;\n\nvoid main()\n{\n gl_Position = worldViewProjection * vec4(position, 1.0);\n\n v_Position = position;\n}\n\n@end"}),define("qtek/shader/source/deferred/spot.essl",[],function(){return"@export buildin.deferred.spot_light\n\n@import buildin.deferred.chunk.light_head\n\n@import buildin.deferred.chunk.light_equation\n\n@import buildin.util.calculate_attenuation\n\nuniform vec3 lightPosition;\nuniform vec3 lightDirection;\nuniform vec3 lightColor;\nuniform float umbraAngleCosine;\nuniform float penumbraAngleCosine;\nuniform float lightRange;\nuniform float falloffFactor;\n\nuniform vec3 eyePosition;\n\nvoid main()\n{\n @import buildin.deferred.chunk.gbuffer_read\n\n vec3 L = lightPosition - position;\n vec3 V = normalize(eyePosition - position);\n\n float dist = length(L);\n L /= dist;\n\n float attenuation = lightAttenuation(dist, lightRange);\n float c = dot(-lightDirection, L);\n\n float falloff = clamp((c - umbraAngleCosine) / (penumbraAngleCosine - umbraAngleCosine), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n // Diffuse term\n gl_FragColor.rgb = lightColor * ndl * (1.0 - falloff) * attenuation;\n if (dot(gl_FragColor.rgb, vec3(1.0)) == 0.0) // Reduce blending\n {\n discard;\n }\n gl_FragColor.a = dot(LUM, gl_FragColor.rgb * D_Phong(glossiness, ndh));\n}\n@end\n"}),define("qtek/shader/source/deferred/directional.essl",[],function(){return"@export buildin.deferred.directional_light\n\n@import buildin.deferred.chunk.light_head\n\n@import buildin.deferred.chunk.light_equation\n\nuniform vec3 lightDirection;\nuniform vec3 lightColor;\n\nuniform vec3 eyePosition;\n\nvoid main()\n{\n @import buildin.deferred.chunk.gbuffer_read\n\n vec3 L = -normalize(lightDirection);\n vec3 V = normalize(eyePosition - position);\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n gl_FragColor.rgb = ndl * lightColor;\n gl_FragColor.a = dot(LUM, gl_FragColor.rgb * D_Phong(glossiness, ndh));\n}\n@end\n"}),define("qtek/shader/source/deferred/ambient.essl",[],function(){return"@export buildin.deferred.ambient_light\nuniform sampler2D normalTexture;\nuniform vec3 lightColor;\n\nvarying vec2 v_Texcoord;\n\nvoid main()\n{\n vec4 tex = texture2D(normalTexture, v_Texcoord);\n vec3 normal = tex.rgb * 2.0 - 1.0;\n\n gl_FragColor.rgb = lightColor * (clamp(normal.y * 0.7, 0.0, 1.0) + 0.3);\n gl_FragColor.a = 0.0;\n}\n@end"}),define("qtek/shader/source/deferred/point.essl",[],function(){return"@export buildin.deferred.point_light\n\n@import buildin.deferred.chunk.light_head\n\n@import buildin.util.calculate_attenuation\n\n@import buildin.deferred.chunk.light_equation\n\nuniform vec3 lightPosition;\nuniform vec3 lightColor;\nuniform float lightRange;\n\nuniform vec3 eyePosition;\n\nvarying vec3 v_Position;\n\nvoid main()\n{\n @import buildin.deferred.chunk.gbuffer_read\n\n vec3 L = lightPosition - position;\n vec3 V = normalize(eyePosition - position);\n\n float dist = length(L);\n L /= dist;\n\n vec3 H = normalize(L + V);\n\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n float attenuation = lightAttenuation(dist, lightRange);\n // Diffuse term\n gl_FragColor.rgb = lightColor * ndl * attenuation;\n if (dot(gl_FragColor.rgb, vec3(1.0)) == 0.0) // Reduce blending\n {\n discard;\n }\n // // Specular luminance\n gl_FragColor.a = dot(LUM, gl_FragColor.rgb * D_Phong(glossiness, ndh));\n}\n@end"}),define("qtek/shader/source/deferred/output.essl",[],function(){return"@export buildin.deferred.output.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world: WORLD;\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n\nuniform vec2 uvRepeat;\nuniform vec2 uvOffset;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\n\nvarying vec4 v_ProjPos;\n\nvarying vec3 v_WorldPos;\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n\n v_WorldPos = (world * vec4(skinnedPosition, 1.0)).xyz;\n\n v_ProjPos = gl_Position;\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n}\n@end\n\n@export buildin.deferred.output.fragment\n\nuniform sampler2D diffuseMap;\n\nuniform sampler2D lightAccumTex;\nuniform sampler2D normalTex;\n\nuniform vec3 color;\nuniform vec3 specularColor;\nuniform vec3 emission;\n\nuniform vec3 eyePosition;\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_WorldPos;\nvarying vec4 v_ProjPos;\n\nconst vec3 LUM = vec3(0.2125, 0.7154, 0.0721);\n\n// Fresnel\nvec3 F_Schlick(float ndv, vec3 spec) {\n return spec + (1.0 - spec) * pow(1.0 - ndv, 5.0);\n}\n\nvoid main()\n{\n vec2 uv = (v_ProjPos.xy / v_ProjPos.w + 1.0) * 0.5;\n\n vec3 V = normalize(eyePosition - v_WorldPos);\n\n vec3 albedo = color;\n #ifdef diffuseMap\n albedo *= texture2D(diffuseMap, v_Texcoord);\n #endif\n\n vec4 diffSpec = texture2D(lightAccumTex, uv);\n vec3 N;\n vec2 tex = texture2D(normalTex, uv).rg;\n N.xy = tex * 2.0 - 1.0;\n N.z = sqrt(1.0 - dot(N.xy, N.xy));\n\n vec3 diffTerm = diffSpec.rgb;\n // PENDING\n vec3 specTerm = diffTerm * diffSpec.a / (dot(LUM, diffTerm) + 0.1);\n vec3 fresnelTerm = F_Schlick(clamp(dot(N, V), 0.0, 1.0), specularColor);\n\n gl_FragColor.rgb = albedo * diffTerm + fresnelTerm * specTerm + emission;\n gl_FragColor.a = 1.0;\n}\n@end\n\n"}),define("qtek/shader/source/prez.essl",[],function(){return"// Shader for prez pass\n@export buildin.prez.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n \n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n \n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n}\n\n@end\n\n\n@export buildin.prez.fragment\n\nvoid main()\n{\n gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\n}\n\n@end"}),define("qtek/deferred/Renderer",["require","../core/Base","../Shader","./StandardMaterial","../Material","../FrameBuffer","../compositor/Pass","../Texture2D","../Texture","../math/BoundingBox","../Mesh","../geometry/Sphere","../geometry/Cone","../math/Matrix4","../math/Vector3","../Renderer","../shader/source/util.essl","../shader/source/deferred/gbuffer.essl","../shader/source/deferred/chunk.essl","../shader/source/deferred/lightvolume.essl","../shader/source/deferred/spot.essl","../shader/source/deferred/directional.essl","../shader/source/deferred/ambient.essl","../shader/source/deferred/point.essl","../shader/source/deferred/output.essl","../shader/source/prez.essl"],function(a){var b=a("../core/Base"),c=a("../Shader"),d=a("./StandardMaterial"),e=a("../Material"),f=a("../FrameBuffer"),g=a("../compositor/Pass"),h=a("../Texture2D"),i=a("../Texture");a("../math/BoundingBox");var j=a("../Mesh"),k=a("../geometry/Sphere"),l=a("../geometry/Cone"),m=a("../math/Matrix4"),n=a("../math/Vector3"),o=a("../Renderer");c.import(a("../shader/source/util.essl")),c.import(a("../shader/source/deferred/gbuffer.essl")),c.import(a("../shader/source/deferred/chunk.essl")),c.import(a("../shader/source/deferred/lightvolume.essl")),c.import(a("../shader/source/deferred/spot.essl")),c.import(a("../shader/source/deferred/directional.essl")),c.import(a("../shader/source/deferred/ambient.essl")),c.import(a("../shader/source/deferred/point.essl")),c.import(a("../shader/source/deferred/output.essl")),c.import(a("../shader/source/prez.essl"));var p={},q=b.derive(function(){var a=new c({vertex:c.source("buildin.deferred.gbuffer.vertex"),fragment:c.source("buildin.deferred.gbuffer.fragment")}),b=a.clone();b.enableTexture("diffuseMap");var d=a.clone();d.enableTexture("normalMap");var j=b.clone();j.enableTexture("normalMap");var o=new c({vertex:c.source("buildin.deferred.output.vertex"),fragment:c.source("buildin.deferred.output.fragment")}),p=o.clone();p.enableTexture("diffuseMap");var q=c.source("buildin.compositor.vertex"),r=c.source("buildin.deferred.light_volume.vertex"),s=function(a){a.blendEquation(a.FUNC_ADD),a.blendFunc(a.ONE,a.ONE)},t=function(a){return new e({shader:a,blend:s,transparent:!0,depthMask:!1})},u=new l({bottomRadius:1,height:2,capSegments:10}),v=new m;return v.rotateX(Math.PI/2),v.translate(new n(0,0,-1)),u.applyTransform(v),{_gBufferShader:a,_gBufferDiffShader:b,_gBufferDiffNormShader:j,_gBufferNormShader:d,_outputShader:o,_outputDiffShader:p,_gBufferFrameBuffer:new f,_gBufferTex:new h({width:0,height:0,type:i.FLOAT,minFilter:i.NEAREST,magFilter:i.NEAREST}),_lightAccumFrameBuffer:new f,_lightAccumTex:new h({type:i.FLOAT,minFilter:i.NEAREST,magFilter:i.NEAREST}),_fullQuadPass:new g,_directionalLightMat:t(new c({vertex:q,fragment:c.source("buildin.deferred.directional_light")})),_ambientMat:t(new c({vertex:q,fragment:c.source("buildin.deferred.ambient_light")})),_spotLightShader:new c({vertex:r,fragment:c.source("buildin.deferred.spot_light")}),_pointLightShader:new c({vertex:r,fragment:c.source("buildin.deferred.point_light")}),_createLightPassMat:t,_lightSphereGeo:new k({widthSegments:10,heightSegements:10}),_lightConeGeo:u}},{render:function(a,b,c){var d=a.gl;b.update(!1,!0),c.update(!0);var e=b.opaqueQueue;e.sort(o.opaqueSortFunc);for(var f=0;f1?[i.name,k].join("-"):i.name),b.meshes[h].push(K)}}},_parseNodes:function(a,b){for(var c in a.nodes){var d,e=a.nodes[c];if(e.camera&&this.includeCamera){var f=a.cameras[e.camera];"perspective"===f.projection?d=new m({name:e.name,aspect:f.aspect_ratio,fov:f.xfov,far:f.zfar,near:f.znear}):(d=new n,console.warn("TODO:Orthographic camera")),d.setName(e.name),b.cameras[e.name]=d}else if(e.lights&&this.includeLight){for(var g=[],i=0;ii;i++)d.localTransform._array[i]=e.matrix[i];d.decomposeLocalTransform()}else e.translation&&d.position.setArray(e.translation),e.rotation&&(x.setAxisAngle(d.rotation._array,e.rotation.slice(0,3),e.rotation[3]),d.rotation._dirty=!0),e.scale&&d.scale.setArray(e.scale);b.nodes[c]=d}for(var c in a.nodes){var e=a.nodes[c],d=b.nodes[c];if(e.children)for(var i=0;i>16,c=255&a>>8,d=255&a;return[b/255,c/255,d/255]}var d=a("../core/Base"),e=a("../core/request"),f=a("../core/util"),g=a("../Shader"),h=a("../Material"),i=a("../DynamicGeometry"),j=a("../Mesh"),k=a("../Node"),l=a("../Texture2D");a("../TextureCube");var m=a("../shader/library"),n=a("../Skeleton"),o=a("../Joint"),p=a("../math/Vector3"),q=a("../math/Quaternion"),r=a("../core/glenum"),s=a("../animation/SkinningClip"),t=a("../dep/glmatrix"),u=t.vec3,v=t.quat,w=d.derive({rootPath:"",textureRootPath:""},{load:function(a){var b=this;this.rootPath||(this.rootPath=a.slice(0,a.lastIndexOf("/"))),e.get({url:a,onprogress:function(a,c,d){b.trigger("progress",a,c,d)},onerror:function(a){b.trigger("error",a)},responseType:"text",onload:function(a){b.parse(JSON.parse(a))}})},parse:function(a){var b,c,d=this._parseGeometry(a),e=a.skinIndices,f=a.skinWeights,g=e&&e.length&&f&&f.length;g?(c=this._parseSkeleton(a),b=c.joints.length):b=0;for(var h=[],i=0;i=0?(E=D[a],q=e[E],r=q.attributes,s=r.position.value,t=r.normal.value,u=[r.texcoord0.value,r.texcoord1.value],v=r.color.value,x=r.weight.value,w=r.joint.value,F[b]=!1,C[a]):(s.push([j[3*a],j[3*a+1],j[3*a+2]]),p&&(x.push([n[2*a],n[2*a+1],0]),w.push([m[2*a],m[2*a+1],-1,-1])),C[a]=f[J],D[a]=J,F[b]=!0,f[J]++)}for(var e=[],f=[],g=0;gg;g++)G[g]=[0,0],H[g]=[0,0,0],I[g]=[0,0,0];for(var J=0;B>A;){var K=h[A++],L=b(K,0),M=b(K,1),N=b(K,2),O=b(K,3),P=b(K,4),Q=b(K,5),R=b(K,6),S=b(K,7),T=L?4:3;M&&(J=h[A+(L?4:3)],e[J]||(e[J]=new i),q=e[J],r=q.attributes,s=r.position.value,t=r.normal.value,u=[r.texcoord0.value,r.texcoord1.value],v=r.color.value,x=r.weight.value,w=r.joint.value,y=q.faces);var U,V,W,X,Y,Z,$,_,ab,bb;if(L?(U=h[A++],V=h[A++],W=h[A++],X=h[A++],Y=d(U,0),Z=d(V,1),$=d(X,2),_=d(V,3),ab=d(W,4),bb=d(X,5),y.push([Y,Z,$],[_,ab,bb])):(Y=h[A++],Z=h[A++],$=h[A++],Y=d(Y,0),Z=d(Z,1),$=d($,2),y.push([Y,Z,$])),M&&A++,N)for(var g=0;z>g;g++){var cb=o[g],db=y[A++],eb=cb[2*db],fb=cb[2*db+1];L?(F[0]&&(u[g][Y]=[eb,fb]),F[1]&&(u[g][Z]=[eb,fb]),F[2]&&(u[g][$]=[eb,fb]),F[3]&&(u[g][_]=[eb,fb]),F[4]&&(u[g][ab]=[eb,fb]),F[5]&&(u[g][bb]=[eb,fb])):(F[0]&&(u[g][Y]=[eb,fb]),F[1]&&(u[g][Z]=[eb,fb]),F[2]&&(u[g][$]=[eb,fb]))}if(O)for(var g=0;z>g;g++){for(var cb=o[g],gb=0;T>gb;gb++){var db=h[A++];G[gb][0]=cb[2*db],G[gb][1]=cb[2*db+1]}L?(F[0]&&(u[g][Y]=G[0].slice()),F[1]&&(u[g][Z]=G[1].slice()),F[2]&&(u[g][$]=G[3].slice()),F[3]&&(u[g][_]=G[1].slice()),F[4]&&(u[g][ab]=G[2].slice()),F[5]&&(u[g][bb]=G[3].slice())):(F[0]&&(u[g][Y]=G[0].slice()),F[1]&&(u[g][Z]=G[1].slice()),F[2]&&(u[g][$]=G[2].slice()))}if(P){var hb=3*h[A++],ib=k[hb++],jb=k[hb++],kb=k[hb];L?(F[0]&&(t[Y]=[ib,jb,kb]),F[1]&&(t[Z]=[ib,jb,kb]),F[2]&&(t[$]=[ib,jb,kb]),F[3]&&(t[_]=[ib,jb,kb]),F[4]&&(t[ab]=[ib,jb,kb]),F[5]&&(t[bb]=[ib,jb,kb])):(F[0]&&(t[Y]=[ib,jb,kb]),F[1]&&(t[Z]=[ib,jb,kb]),F[2]&&(t[$]=[ib,jb,kb]))}if(Q){for(var g=0;T>g;g++){var hb=3*h[A++];H[g][0]=k[hb++],H[g][1]=k[hb++],H[g][2]=k[hb]}L?(F[0]&&(t[Y]=H[0].slice()),F[1]&&(t[Z]=H[1].slice()),F[2]&&(t[$]=H[3].slice()),F[3]&&(t[_]=H[1].slice()),F[4]&&(t[ab]=H[2].slice()),F[5]&&(t[bb]=H[3].slice())):(F[0]&&(t[Y]=H[0].slice()),F[1]&&(t[Z]=H[1].slice()),F[2]&&(t[$]=H[2].slice()))}if(R){var lb=h[A++],mb=c(l[lb]);L?(F[0]&&(v[Y]=mb),F[1]&&(v[Z]=mb),F[2]&&(v[$]=mb),F[3]&&(v[_]=mb),F[4]&&(v[ab]=mb),F[5]&&(v[bb]=mb)):(F[0]&&(v[Y]=mb),F[1]&&(v[Z]=mb),F[2]&&(v[$]=mb))}if(S){for(var g=0;T>g;g++){var lb=h[A++];I[g]=c(l[lb])}L?(F[0]&&(v[Y]=I[0].slice()),F[1]&&(v[Z]=I[1].slice()),F[2]&&(v[$]=I[3].slice()),F[3]&&(v[_]=I[1].slice()),F[4]&&(v[ab]=I[2].slice()),F[5]&&(v[bb]=I[3].slice())):(F[0]&&(v[Y]=I[0].slice()),F[1]&&(v[Z]=I[1].slice()),F[2]&&(v[$]=I[2].slice()))}}return e},_parseSkeleton:function(a){for(var b=[],c=a.bones,d=0;dj;j++)i.enableTexture(f[j]);i.define("vertex","SKINNING"),i.define("vertex","JOINT_NUMBER",b)}var k=new h({shader:i});return a.colorDiffuse?k.set("color",a.colorDiffuse):a.DbgColor&&k.set("color",c(a.DbgColor)),a.colorSpecular&&k.set("specular",a.colorSpecular),void 0!==a.transparent&&a.transparent&&(k.transparent=!0),void 0!==a.depthTest&&(k.depthTest=a.depthTest),void 0!==a.depthWrite&&(k.depthMask=a.depthWrite),a.transparency&&a.transparency<1&&k.set("opacity",a.transparency),a.specularCoef&&k.set("shininess",a.specularCoef),a.mapDiffuse&&k.set("diffuseMap",this._loadTexture(a.mapDiffuse,a.mapDiffuseWrap)),a.mapBump&&k.set("normalMap",this._loadTexture(a.mapBump,a.mapBumpWrap)),a.mapNormal&&k.set("normalMap",this._loadTexture(a.mapNormal,a.mapBumpWrap)),k},_loadTexture:function(a,b){var c=new Image,d=new l;d.image=c,b&&b.length&&(d.wrapS=r[b[0].toUpperCase()],d.wrapT=r[b[1].toUpperCase()]),c.onload=function(){d.dirty()};var e=this.textureRootPath||this.rootPath;return c.src=f.relative2absolute(a,e),d}});return w}),define("qtek/math/Matrix2",["require","../dep/glmatrix"],function(a){var b=a("../dep/glmatrix"),c=b.mat2,d=function(){this._array=c.create(),this._dirty=!0};return d.prototype={constructor:d,clone:function(){return(new d).copy(this)},copy:function(a){return c.copy(this._array,a._array),this._dirty=!0,this},adjoint:function(){return c.adjoint(this._array,this._array),this._dirty=!0,this},determinant:function(){return c.determinant(this._array)},identity:function(){return c.identity(this._array),this._dirty=!0,this},invert:function(){return c.invert(this._array,this._array),this._dirty=!0,this},mul:function(a){return c.mul(this._array,this._array,a._array),this._dirty=!0,this},mulLeft:function(a){return c.mul(this._array,a._array,this._array),this._dirty=!0,this},multiply:function(a){return c.multiply(this._array,this._array,a._array),this._dirty=!0,this},multiplyLeft:function(a){return c.multiply(this._array,a._array,this._array),this._dirty=!0,this},rotate:function(a){return c.rotate(this._array,this._array,a),this._dirty=!0,this},scale:function(a){return c.scale(this._array,this._array,a._array),this._dirty=!0,this},transpose:function(){return c.transpose(this._array,this._array),this._dirty=!0,this},toString:function(){return"["+Array.prototype.join.call(this._array,",")+"]"}},d.adjoint=function(a,b){return c.adjoint(a._array,b._array),a._dirty=!0,a},d.copy=function(a,b){return c.copy(a._array,b._array),a._dirty=!0,a},d.determinant=function(a){return c.determinant(a._array)},d.identity=function(a){return c.identity(a._array),a._dirty=!0,a},d.invert=function(a,b){return c.invert(a._array,b._array),a._dirty=!0,a},d.mul=function(a,b,d){return c.mul(a._array,b._array,d._array),a._dirty=!0,a},d.multiply=d.mul,d.rotate=function(a,b,d){return c.rotate(a._array,b._array,d),a._dirty=!0,a},d.scale=function(a,b,d){return c.scale(a._array,b._array,d._array),a._dirty=!0,a},d.transpose=function(a,b){return c.transpose(a._array,b._array),a._dirty=!0,a},d}),define("qtek/math/Matrix2d",["require","../dep/glmatrix"],function(a){var b=a("../dep/glmatrix"),c=b.mat2d,d=function(){this._array=c.create(),this._dirty=!0};return d.prototype={constructor:d,clone:function(){return(new d).copy(this)},copy:function(a){return c.copy(this._array,a._array),this._dirty=!0,this},determinant:function(){return c.determinant(this._array)},identity:function(){return c.identity(this._array),this._dirty=!0,this},invert:function(){return c.invert(this._array,this._array),this._dirty=!0,this},mul:function(a){return c.mul(this._array,this._array,a._array),this._dirty=!0,this},mulLeft:function(a){return c.mul(this._array,a._array,this._array),this._dirty=!0,this},multiply:function(a){return c.multiply(this._array,this._array,a._array),this._dirty=!0,this},multiplyLeft:function(a){return c.multiply(this._array,a._array,this._array),this._dirty=!0,this},rotate:function(a){return c.rotate(this._array,this._array,a),this._dirty=!0,this},scale:function(a){return c.scale(this._array,this._array,a._array),this._dirty=!0,this},translate:function(a){return c.translate(this._array,this._array,a._array),this._dirty=!0,this},toString:function(){return"["+Array.prototype.join.call(this._array,",")+"]"}},d.copy=function(a,b){return c.copy(a._array,b._array),a._dirty=!0,a},d.determinant=function(a){return c.determinant(a._array)},d.identity=function(a){return c.identity(a._array),a._dirty=!0,a},d.invert=function(a,b){return c.invert(a._array,b._array),a._dirty=!0,a},d.mul=function(a,b,d){return c.mul(a._array,b._array,d._array),a._dirty=!0,a},d.multiply=d.mul,d.rotate=function(a,b,d){return c.rotate(a._array,b._array,d),a._dirty=!0,a},d.scale=function(a,b,d){return c.scale(a._array,b._array,d._array),a._dirty=!0,a},d.translate=function(a,b,d){return c.translate(a._array,b._array,d._array),a._dirty=!0,a},d}),define("qtek/math/Matrix3",["require","../dep/glmatrix"],function(a){var b=a("../dep/glmatrix"),c=b.mat3,d=function(){this._array=c.create(),this._dirty=!0};return d.prototype={constructor:d,adjoint:function(){return c.adjoint(this._array,this._array),this._dirty=!0,this},clone:function(){return(new d).copy(this)},copy:function(a){return c.copy(this._array,a._array),this._dirty=!0,this},determinant:function(){return c.determinant(this._array)},fromMat2d:function(a){return c.fromMat2d(this._array,a._array),this._dirty=!0,this},fromMat4:function(a){return c.fromMat4(this._array,a._array),this._dirty=!0,this},fromQuat:function(a){return c.fromQuat(this._array,a._array),this._dirty=!0,this},identity:function(){return c.identity(this._array),this._dirty=!0,this},invert:function(){return c.invert(this._array,this._array),this._dirty=!0,this},mul:function(a){return c.mul(this._array,this._array,a._array),this._dirty=!0,this},mulLeft:function(a){return c.mul(this._array,a._array,this._array),this._dirty=!0,this},multiply:function(a){return c.multiply(this._array,this._array,a._array),this._dirty=!0,this},multiplyLeft:function(a){return c.multiply(this._array,a._array,this._array),this._dirty=!0,this},rotate:function(a){return c.rotate(this._array,this._array,a),this._dirty=!0,this},scale:function(a){return c.scale(this._array,this._array,a._array),this._dirty=!0,this},translate:function(a){return c.translate(this._array,this._array,a._array),this._dirty=!0,this},normalFromMat4:function(a){return c.normalFromMat4(this._array,a._array),this._dirty=!0,this},transpose:function(){return c.transpose(this._array,this._array),this._dirty=!0,this},toString:function(){return"["+Array.prototype.join.call(this._array,",")+"]"}},d.adjoint=function(a,b){return c.adjoint(a._array,b._array),a._dirty=!0,a},d.copy=function(a,b){return c.copy(a._array,b._array),a._dirty=!0,a},d.determinant=function(a){return c.determinant(a._array)},d.identity=function(a){return c.identity(a._array),a._dirty=!0,a},d.invert=function(a,b){return c.invert(a._array,b._array),a},d.mul=function(a,b,d){return c.mul(a._array,b._array,d._array),a._dirty=!0,a},d.multiply=d.mul,d.fromMat2d=function(a,b){return c.fromMat2d(a._array,b._array),a._dirty=!0,a},d.fromMat4=function(a,b){return c.fromMat4(a._array,b._array),a._dirty=!0,a},d.fromQuat=function(a,b){return c.fromQuat(a._array,b._array),a._dirty=!0,a},d.normalFromMat4=function(a,b){return c.normalFromMat4(a._array,b._array),a._dirty=!0,a},d.rotate=function(a,b,d){return c.rotate(a._array,b._array,d),a._dirty=!0,a},d.scale=function(a,b,d){return c.scale(a._array,b._array,d._array),a._dirty=!0,a},d.transpose=function(a,b){return c.transpose(a._array,b._array),a._dirty=!0,a},d.translate=function(a,b,d){return c.translate(a._array,b._array,d._array),a._dirty=!0,a},d}),define("qtek/math/Value",["require","./Vector3","./Vector2"],function(a){var b=a("./Vector3"),c=a("./Vector2"),d=function(){};d.prototype.get=function(){};var e=function(a){this.get=function(){return a}};e.prototype=new d,e.prototype.constructor=e;var f=function(a){var b=a.constructor;this.get=function(c){return c||(c=new b),c.copy(a),c}};f.prototype=new d,f.prototype.constructor=f;var g=function(a,b){var c=b-a;this.get=function(){return Math.random()*c+a}};g.prototype=new d,g.prototype.constructor=g;var h=function(a,b){var d=b.x-a.x,e=b.y-a.y;this.get=function(b){return b||(b=new c),c.set(b,d*Math.random()+a._array[0],e*Math.random()+a._array[1]),b}};h.prototype=new d,h.prototype.constructor=h;var i=function(a,c){var d=c.x-a.x,e=c.y-a.y,f=c.z-a.z;this.get=function(c){return c||(c=new b),b.set(c,d*Math.random()+a._array[0],e*Math.random()+a._array[1],f*Math.random()+a._array[2]),c}};return i.prototype=new d,i.prototype.constructor=i,d.constant=function(a){return new e(a)},d.vector=function(a){return new f(a)},d.random1D=function(a,b){return new g(a,b)},d.random2D=function(a,b){return new h(a,b)},d.random3D=function(a,b){return new i(a,b)},d}),define("qtek/math/Vector4",["require","../dep/glmatrix"],function(a){var b=a("../dep/glmatrix"),c=b.vec4,d=function(a,b,d,e){a=a||0,b=b||0,d=d||0,e=e||0,this._array=c.fromValues(a,b,d,e),this._dirty=!0};if(d.prototype={constructor:d,add:function(a){return c.add(this._array,this._array,a._array),this._dirty=!0,this},set:function(a,b,c,d){return this._array[0]=a,this._array[1]=b,this._array[2]=c,this._array[3]=d,this._dirty=!0,this},setArray:function(a){return this._array[0]=a[0],this._array[1]=a[1],this._array[2]=a[2],this._array[3]=a[3],this._dirty=!0,this},clone:function(){return new d(this.x,this.y,this.z,this.w)},copy:function(a){return c.copy(this._array,a._array),this._dirty=!0,this},dist:function(a){return c.dist(this._array,a._array)},distance:function(a){return c.distance(this._array,a._array)},div:function(a){return c.div(this._array,this._array,a._array),this._dirty=!0,this},divide:function(a){return c.divide(this._array,this._array,a._array),this._dirty=!0,this},dot:function(a){return c.dot(this._array,a._array)},len:function(){return c.len(this._array)},length:function(){return c.length(this._array)},lerp:function(a,b,d){return c.lerp(this._array,a._array,b._array,d),this._dirty=!0,this},min:function(a){return c.min(this._array,this._array,a._array),this._dirty=!0,this},max:function(a){return c.max(this._array,this._array,a._array),this._dirty=!0,this},mul:function(a){return c.mul(this._array,this._array,a._array),this._dirty=!0,this},multiply:function(a){return c.multiply(this._array,this._array,a._array),this._dirty=!0,this},negate:function(){return c.negate(this._array,this._array),this._dirty=!0,this},normalize:function(){return c.normalize(this._array,this._array),this._dirty=!0,this},random:function(a){return c.random(this._array,a),this._dirty=!0,this},scale:function(a){return c.scale(this._array,this._array,a),this._dirty=!0,this},scaleAndAdd:function(a,b){return c.scaleAndAdd(this._array,this._array,a._array,b),this._dirty=!0,this},sqrDist:function(a){return c.sqrDist(this._array,a._array)},squaredDistance:function(a){return c.squaredDistance(this._array,a._array)},sqrLen:function(){return c.sqrLen(this._array)},squaredLength:function(){return c.squaredLength(this._array)},sub:function(a){return c.sub(this._array,this._array,a._array),this._dirty=!0,this},subtract:function(a){return c.subtract(this._array,this._array,a._array),this._dirty=!0,this},transformMat4:function(a){return c.transformMat4(this._array,this._array,a._array),this._dirty=!0,this},transformQuat:function(a){return c.transformQuat(this._array,this._array,a._array),this._dirty=!0,this},toString:function(){return"["+Array.prototype.join.call(this._array,",")+"]"}},Object.defineProperty){var e=d.prototype;Object.defineProperty(e,"x",{get:function(){return this._array[0]},set:function(a){this._array[0]=a,this._dirty=!0}}),Object.defineProperty(e,"y",{get:function(){return this._array[1]},set:function(a){this._array[1]=a,this._dirty=!0}}),Object.defineProperty(e,"z",{get:function(){return this._array[2]},set:function(a){this._array[2]=a,this._dirty=!0}}),Object.defineProperty(e,"w",{get:function(){return this._array[3]},set:function(a){this._array[3]=a,this._dirty=!0}})}return d.add=function(a,b,d){return c.add(a._array,b._array,d._array),a._dirty=!0,a},d.set=function(a,b,d,e,f){c.set(a._array,b,d,e,f),a._dirty=!0},d.copy=function(a,b){return c.copy(a._array,b._array),a._dirty=!0,a},d.dist=function(a,b){return c.distance(a._array,b._array)},d.distance=d.dist,d.div=function(a,b,d){return c.divide(a._array,b._array,d._array),a._dirty=!0,a},d.divide=d.div,d.dot=function(a,b){return c.dot(a._array,b._array)},d.len=function(a){return c.length(a._array)},d.lerp=function(a,b,d,e){return c.lerp(a._array,b._array,d._array,e),a._dirty=!0,a},d.min=function(a,b,d){return c.min(a._array,b._array,d._array),a._dirty=!0,a},d.max=function(a,b,d){return c.max(a._array,b._array,d._array),a._dirty=!0,a},d.mul=function(a,b,d){return c.multiply(a._array,b._array,d._array),a._dirty=!0,a},d.multiply=d.mul,d.negate=function(a,b){return c.negate(a._array,b._array),a._dirty=!0,a},d.normalize=function(a,b){return c.normalize(a._array,b._array),a._dirty=!0,a},d.random=function(a,b){return c.random(a._array,b),a._dirty=!0,a},d.scale=function(a,b,d){return c.scale(a._array,b._array,d),a._dirty=!0,a},d.scaleAndAdd=function(a,b,d,e){return c.scaleAndAdd(a._array,b._array,d._array,e),a._dirty=!0,a},d.sqrDist=function(a,b){return c.sqrDist(a._array,b._array)},d.squaredDistance=d.sqrDist,d.sqrLen=function(a){return c.sqrLen(a._array)},d.squaredLength=d.sqrLen,d.sub=function(a,b,d){return c.subtract(a._array,b._array,d._array),a._dirty=!0,a},d.subtract=d.sub,d.transformMat4=function(a,b,d){return c.transformMat4(a._array,b._array,d._array),a._dirty=!0,a},d.transformQuat=function(a,b,d){return c.transformQuat(a._array,b._array,d._array),a._dirty=!0,a},d}),define("qtek/particleSystem/Particle",["require","../math/Vector3","../dep/glmatrix"],function(a){var b=a("../math/Vector3"),c=a("../dep/glmatrix"),d=c.vec3,e=function(){this.position=new b,this.rotation=new b,this.velocity=null,this.angularVelocity=null,this.life=1,this.age=0,this.spriteSize=1,this.weight=1,this.emitter=null};return e.prototype.update=function(a){this.velocity&&d.scaleAndAdd(this.position._array,this.position._array,this.velocity._array,a),this.angularVelocity&&d.scaleAndAdd(this.rotation._array,this.rotation._array,this.angularVelocity._array,a)},e}),define("qtek/particleSystem/Emitter",["require","../core/Base","../math/Vector3","./Particle","../math/Value","../dep/glmatrix"],function(a){var b=a("../core/Base"),c=a("../math/Vector3"),d=a("./Particle"),e=a("../math/Value"),f=a("../dep/glmatrix");f.vec3;var g=b.derive({max:1e3,amount:20,life:null,position:null,rotation:null,velocity:null,angularVelocity:null,spriteSize:null,weight:null,_particlePool:null},function(){this._particlePool=[];for(var a=0;ad;d++)b=this._particlePool.pop(),this.position&&this.position.get(b.position),this.rotation&&this.rotation.get(b.rotation),this.velocity&&this.velocity.get(b.velocity),this.angularVelocity&&this.angularVelocity.get(b.angularVelocity),this.life&&(b.life=this.life.get()),this.spriteSize&&(b.spriteSize=this.spriteSize.get()),this.weight&&(b.weight=this.weight.get()),b.age=0,a.push(b)},kill:function(a){this._particlePool.push(a)}});return g.constant=e.constant,g.vector=e.vector,g.random1D=e.random1D,g.random2D=e.random2D,g.random3D=e.random3D,g}),define("qtek/particleSystem/Field",["require","../core/Base"],function(a){var b=a("../core/Base"),c=b.derive({},{applyTo:function(){}});return c}),define("qtek/particleSystem/ForceField",["require","./Field","../math/Vector3","../dep/glmatrix"],function(a){var b=a("./Field"),c=a("../math/Vector3"),d=a("../dep/glmatrix"),e=d.vec3,f=b.derive(function(){return{force:new c}},{applyTo:function(a,b,c,d){c>0&&e.scaleAndAdd(a._array,a._array,this.force._array,d/c)}});return f}),define("qtek/particleSystem/particle.essl",[],function(){return"@export buildin.particle.vertex\n\nuniform mat4 worldView : WORLDVIEW;\nuniform mat4 projection : PROJECTION;\n\nattribute vec3 position : POSITION;\nattribute vec3 normal : NORMAL;\n\n#ifdef UV_ANIMATION\nattribute vec2 texcoord0 : TEXCOORD_0;\nattribute vec2 texcoord1 : TEXCOORD_1;\n\nvarying vec2 v_Uv0;\nvarying vec2 v_Uv1;\n#endif\n\nvarying float v_Age;\n\nvoid main() {\n v_Age = normal.x;\n float rotation = normal.y;\n\n vec4 worldViewPosition = worldView * vec4(position, 1.0);\n gl_Position = projection * worldViewPosition;\n float w = gl_Position.w;\n // TODO\n gl_PointSize = normal.z * projection[0].x / w;\n\n #ifdef UV_ANIMATION\n v_Uv0 = texcoord0;\n v_Uv1 = texcoord1;\n #endif\n}\n\n@end\n\n@export buildin.particle.fragment\n\nuniform sampler2D sprite;\nuniform sampler2D gradient;\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\n\nvarying float v_Age;\n\n#ifdef UV_ANIMATION\nvarying vec2 v_Uv0;\nvarying vec2 v_Uv1;\n#endif\n\nvoid main() {\n vec4 color = vec4(color, alpha);\n #ifdef SPRITE_ENABLED\n #ifdef UV_ANIMATION\n color *= texture2D(sprite, mix(v_Uv0, v_Uv1, gl_PointCoord));\n #else\n color *= texture2D(sprite, gl_PointCoord);\n #endif\n #endif\n #ifdef GRADIENT_ENABLED\n color *= texture2D(gradient, vec2(v_Age, 0.5));\n #endif\n gl_FragColor = color;\n}\n\n@end"}),define("qtek/particleSystem/ParticleRenderable",["require","../Renderable","../math/Vector3","../core/glenum","../StaticGeometry","../Material","../Shader","../dep/glmatrix","./particle.essl"],function(a){var b=a("../Renderable");a("../math/Vector3"),a("../core/glenum");var c=a("../StaticGeometry"),d=a("../Material"),e=a("../Shader"),f=a("../dep/glmatrix");f.vec3,e["import"](a("./particle.essl"));var g=new e({vertex:e.source("buildin.particle.vertex"),fragment:e.source("buildin.particle.fragment")});g.enableTexture("sprite");var h=b.derive({loop:!0,oneshot:!1,duration:1,spriteAnimationTileX:1,spriteAnimationTileY:1,spriteAnimationRepeat:0,mode:b.POINTS,_elapsedTime:0,_emitting:!0},function(){this.geometry=new c({dynamic:!0}),this.material||(this.material=new d({shader:g,transparent:!0,depthMask:!1})),this._particles=[],this._fields=[],this._emitters=[]},{culling:!1,frustumCulling:!1,castShadow:!1,receiveShadow:!1,addEmitter:function(a){this._emitters.push(a)},removeEmitter:function(a){this._emitters.splice(this._emitters.indexOf(a),1)},addField:function(a){this._fields.push(a)},removeField:function(a){this._fields.splice(this._fields.indexOf(a),1)},reset:function(){for(var a=0;ac;){var e=b[c];e.age+=a,e.age>=e.life?(e.emitter.kill(e),b[c]=b[d-1],b.pop(),d--):c++}for(var c=0;d>c;c++){var e=b[c];if(this._fields.length>0)for(var f=0;f1,g=a.attributes.position.value,h=a.attributes.normal.value,i=a.attributes.texcoord0.value,j=a.attributes.texcoord1.value,k=this._particles.length;g&&g.length===3*k||(g=a.attributes.position.value=new Float32Array(3*k),h=a.attributes.normal.value=new Float32Array(3*k),f&&(i=a.attributes.texcoord0.value=new Float32Array(2*k),j=a.attributes.texcoord1.value=new Float32Array(2*k)));for(var l=1/b,m=0;k>m;m++){for(var n=this._particles[m],o=3*m,p=0;3>p;p++)g[o+p]=n.position._array[p],h[o]=n.age/n.life,h[o+1]=0,h[o+2]=n.spriteSize;var q=2*m;if(f){var r=n.age/n.life,s=Math.round(r*(e-1))*d,t=Math.floor(s*l),u=s-t*b;i[q]=u/b,i[q+1]=1-t/c,j[q]=(u+1)/b,j[q+1]=1-(t+1)/c}}a.dirty()},render:function(a){return this._updateVertices(),b.prototype.render.call(this,a)},isFinished:function(){return this._elapsedTime>this.duration&&!this.loop},dispose:function(a){for(var b=0;b>16,c=a-(b<<8)>>8,d=a-(b<<16)-(c<<8);return[b,c,d]}function c(a,b,c){return(a<<16)+(b<<8)+c}var d=a("../core/Base"),e=a("../FrameBuffer"),f=a("../Texture2D"),g=a("../Shader"),h=a("../Material");g.import(a("./color.essl"));var i=d.derive(function(){return{renderer:null,downSampleRatio:1,width:100,height:100,lookupOffset:1,_frameBuffer:null,_texture:null,_shader:null,_idMaterials:[],_lookupTable:[],_meshMaterials:[],_idOffset:0} -},function(){this.renderer&&(this.width=this.renderer.width,this.height=this.renderer.height),this._init()},{_init:function(){this._texture=new f({width:this.width*this.downSampleRatio,height:this.height*this.downSampleRatio}),this._frameBuffer=new e,this._shader=new g({vertex:g.source("buildin.picking.color.vertex"),fragment:g.source("buildin.picking.color.fragment")})},setPrecision:function(a){this._texture.width=this.width*a,this._texture.height=this.height*a,this.downSampleRatio=a},resize:function(a,b){this._texture.width=a*this.downSampleRatio,this._texture.height=b*this.downSampleRatio,this.width=a,this.height=b,this._texture.dirty()},update:function(a,b){var c=this.renderer;(c.width!==this.width||c.height!==this.height)&&this.resize(c.width,c.height),this._frameBuffer.attach(c.gl,this._texture),this._frameBuffer.bind(c),this._idOffset=this.lookupOffset,this._setMaterial(a),c.render(a,b),this._restoreMaterial(),this._frameBuffer.unbind(c)},_setMaterial:function(a){for(var c=0;ck;k++){var l=j[k];b[f]=a[l]._array,c[f]=g,d[f]=i[l],f++}e[0]=[0,1,2],e[1]=[3,4,5],this.geometry.dirty()}},_unProjectGrid:function(){for(var a=new d,b=[0,1,0,2,1,3,2,3,4,5,4,6,5,7,6,7,0,4,1,5,2,6,3,7],c=new e,g=new e,h=[],i=[],j=0;4>j;j++)i[j]=new e(0,0);var k=new f;return function(){a.copy(this.plane),a.applyTransform(this.camera.viewMatrix);for(var d=this.camera.frustum.vertices,e=0,f=0;12>f;f++){c._array=d[b[2*f]],g._array=d[b[2*f+1]];var j=a.intersectLine(c,g,h[e]);j&&(h[e]||(h[e]=j),e++)}if(0!==e){for(var f=0;e>f;f++)h[f].applyProjection(this.camera.projectionMatrix);for(var l=h[0]._array[0],m=h[0]._array[1],n=h[0]._array[0],o=h[0]._array[1],f=1;e>f;f++)n=Math.max(n,h[f]._array[0]),o=Math.max(o,h[f]._array[1]),l=Math.min(l,h[f]._array[0]),m=Math.min(m,h[f]._array[1]);if(l!=n&&m!=o){i[0]._array[0]=l,i[0]._array[1]=m,i[1]._array[0]=l,i[1]._array[1]=o,i[2]._array[0]=n,i[2]._array[1]=o,i[3]._array[0]=n,i[3]._array[1]=m;for(var f=0;4>f;f++)this.camera.castRay(i[f],k),k.intersectPlane(this.plane,i[f]);return i}}}}()});return k}),define("qtek/plugin/OrbitControl",["require","../core/Base","../math/Vector3","../math/Matrix4"],function(a){var b=a("../core/Base"),c=a("../math/Vector3");a("../math/Matrix4");var d=b.derive(function(){return{target:null,domElement:null,sensitivity:1,origin:new c,up:new c(0,1,0),minDistance:0,maxDistance:1/0,minPolarAngle:0,maxPolarAngle:Math.PI,_offsetPitch:0,_offsetRoll:0,_panX:0,_panY:0,_offsetX:0,_offsetY:0,_forward:0,_op:-1}},function(){this._mouseDown=this._mouseDown.bind(this),this._mouseUp=this._mouseUp.bind(this),this._mouseMove=this._mouseMove.bind(this),this._mouseOut=this._mouseOut.bind(this),this._mouseWheel=this._mouseWheel.bind(this),this.domElement&&this.enable()},{enable:function(){var a=this.domElement;a.addEventListener("mousedown",this._mouseDown),a.addEventListener("mousewheel",this._mouseWheel),a.addEventListener("DOMMouseScroll",this._mouseWheel),a.addEventListener("touchstart",this._mouseDown)},disable:function(){this.domElement.removeEventListener("mousedown",this._mouseDown),this.domElement.removeEventListener("mousewheel",this._mouseWheel),this.domElement.removeEventListener("DOMMouseScroll",this._mouseWheel),this.domElement.removeEventListener("touchstart",this._mouseDown),this._mouseUp()},_mouseWheel:function(a){a.preventDefault();var b=a.wheelDelta||-a.detail;this._forward+=b*this.sensitivity},_mouseDown:function(a){document.addEventListener("mousemove",this._mouseMove),document.addEventListener("mouseup",this._mouseUp),document.addEventListener("mouseout",this._mouseOut),document.addEventListener("touchend",this._mouseUp),document.addEventListener("touchmove",this._mouseMove),this._offsetX=a.pageX,this._offsetY=a.pageY,0===a.button?this._op=0:1===a.button&&(this._op=1)},_mouseMove:function(a){var b=a.pageX-this._offsetX,c=a.pageY-this._offsetY;if(0===this._op)this._offsetPitch+=b*this.sensitivity/100,this._offsetRoll+=c*this.sensitivity/100;else if(1===this._op){var d,e=this.origin.distance(this.target.position);d=this.target.fov?Math.sin(this.target.fov*Math.PI/360)/200:.005,this._panX+=b*this.sensitivity*e*d,this._panY+=c*this.sensitivity*e*d}this._offsetX=a.pageX,this._offsetY=a.pageY},_mouseUp:function(){document.removeEventListener("mousemove",this._mouseMove),document.removeEventListener("mouseup",this._mouseUp),document.removeEventListener("mouseout",this._mouseOut),document.removeEventListener("touchend",this._mouseUp),document.removeEventListener("touchmove",this._mouseMove),this._op=-1},_mouseOut:function(){this._mouseUp()},update:function(){var a=this.target,b=a.localTransform.z.normalize(),c=a.localTransform.y.normalize();if(0===this._op&&0!==this._offsetPitch&&0!==this._offsetRoll){a.rotateAround(this.origin,this.up,-this._offsetPitch);var d=a.localTransform.x;a.rotateAround(this.origin,d,-this._offsetRoll);var b=a.worldTransform.z.normalize(),e=Math.acos(this.up.dot(b));this._offsetRoll>0&&e<=this.minPolarAngle?a.rotateAround(this.origin,d,-e+this.minPolarAngle):this._offsetRoll<0&&e>=this.maxPolarAngle&&a.rotateAround(this.origin,d,-e+this.maxPolarAngle),this._offsetRoll=this._offsetPitch=0}else if(1===this._op){var d=a.localTransform.x.normalize().scale(-this._panX),c=a.localTransform.y.normalize().scale(this._panY);a.position.add(d).add(c),this.origin.add(d).add(c),this._panX=this._panY=0}if(0!==this._forward){var f=a.position.distance(this.origin),g=f+this._forward*f/5e3;gthis.minDistance&&a.position.scaleAndAdd(b,this._forward*f/5e3),this._forward=0}}});return d}),define("qtek/plugin/Skybox",["require","../Mesh","../geometry/Cube","../Shader","../Material"],function(a){var b,c=a("../Mesh"),d=a("../geometry/Cube"),e=a("../Shader"),f=a("../Material"),g=c.derive(function(){b||(b=new e({vertex:e.source("buildin.skybox.vertex"),fragment:e.source("buildin.skybox.fragment")}));var a=new f({shader:b,depthMask:!1});return{scene:null,geometry:new d,material:a,culling:!1}},function(){var a=this.scene;a&&this.attachScene(a)},{attachScene:function(a){this.scene&&this.detachScene(),this.scene=a,a.on("beforerender",this._beforeRenderScene,this)},detachScene:function(){this.scene&&this.scene.off("beforerender",this._beforeRenderScene,this),this.scene=null},dispose:function(){this.detachScene()},_beforeRenderScene:function(a,b,c){this.position.copy(c.getWorldPosition()),this.update(),a.renderQueue([this],c)}});return g}),define("qtek/plugin/Skydome",["require","../Mesh","../geometry/Sphere","../Shader","../Material","../shader/library"],function(a){var b=a("../Mesh"),c=a("../geometry/Sphere"),d=a("../Shader"),e=a("../Material");a("../shader/library");var f,g=b.derive(function(){f||(f=new d({vertex:d.source("buildin.basic.vertex"),fragment:d.source("buildin.basic.fragment")}),f.enableTexture("diffuseMap"));var a=new e({shader:f,depthMask:!1});return{scene:null,geometry:new c({widthSegments:30,heightSegments:30}),material:a,culling:!1}},function(){var a=this.scene;a&&this.attachScene(a)},{attachScene:function(a){this.scene&&this.detachScene(),this.scene=a,a.on("beforerender",this._beforeRenderScene,this)},detachScene:function(){this.scene&&this.scene.off("beforerender",this._beforeRenderScene,this),this.scene=null},_beforeRenderScene:function(a,b,c){this.position.copy(c.getWorldPosition()),this.update(),a.renderQueue([this],c)},dispose:function(){this.detachScene()}});return g}),define("qtek/plugin/_Sprite",["require","../Mesh","../geometry/Plane","../Material","../Shader","../math/Matrix4"],function(a){var b=a("../Mesh"),c=a("../geometry/Plane"),d=a("../Material"),e=a("../Shader");a("../math/Matrix4");var f;b.derive(function(){return f||(f=new e({vertex:e.source("buildin.basic.vertex"),fragment:e.source("buildin.basic.fragment")}),f.enableTexture("diffuseMap")),new d({shader:f}),{camera:null,geometry:new c}},{update:function(a){this.worldTransform.z=this.camera.worldTransform.z,this.worldTransform.y=this.camera.worldTransform.y,this.worldTransform.x=this.camera.worldTransform.x,this.decomposeWorldTransform(),b.prototype.update.call(this,a)}})}),define("qtek/prePass/EnvironmentMap",["require","../core/Base","../math/Vector3","../camera/Perspective","../core/glenum","../FrameBuffer","../TextureCube"],function(a){var b=a("../core/Base"),c=a("../math/Vector3"),d=a("../camera/Perspective");a("../core/glenum");var e=a("../FrameBuffer");a("../TextureCube");var f=["px","nx","py","ny","pz","nz"],g=b.derive(function(){var a={position:new c,far:1e3,near:.1,texture:null};return a._cameras={px:new d({fov:90}),nx:new d({fov:90}),py:new d({fov:90}),ny:new d({fov:90}),pz:new d({fov:90}),nz:new d({fov:90})},a._cameras.px.lookAt(c.POSITIVE_X,c.NEGATIVE_Y),a._cameras.nx.lookAt(c.NEGATIVE_X,c.NEGATIVE_Y),a._cameras.py.lookAt(c.POSITIVE_Y,c.POSITIVE_Z),a._cameras.ny.lookAt(c.NEGATIVE_Y,c.NEGATIVE_Z),a._cameras.pz.lookAt(c.POSITIVE_Z,c.NEGATIVE_Y),a._cameras.nz.lookAt(c.NEGATIVE_Z,c.NEGATIVE_Y),a._frameBuffers={px:new e,nx:new e,py:new e,ny:new e,pz:new e,nz:new e},a},{render:function(a,b,d){var e=a.gl;d||b.update(!0);for(var g=this.texture.width,h=180*(2*Math.atan(g/(g-.5))/Math.PI),i=0;6>i;i++){var j=f[i],k=this._cameras[j];c.copy(k.position,this.position),k.far=this.far,k.near=this.near,k.fov=h,this._frameBuffers[j].attach(e,this.texture,e.COLOR_ATTACHMENT0,e.TEXTURE_CUBE_MAP_POSITIVE_X+i),this._frameBuffers[j].bind(a),a.render(b,k,!0),this._frameBuffers[j].unbind(a)}},dispose:function(a){for(var b=0;6>b;b++){var c=f[b];this._frameBuffers[c].dispose(a.gl)}}});return g}),define("qtek/prePass/Reflection",["require","../core/Base","../math/Vector4"],function(a){var b=a("../core/Base");a("../math/Vector4");var c=b.derive(function(){console.warn("TODO")},{render:function(){}});return c}),define("qtek/prePass/ShadowMap",["require","../core/Base","../core/glenum","../math/Vector3","../math/BoundingBox","../math/Frustum","../math/Matrix4","../Renderer","../Shader","../Light","../Mesh","../light/Spot","../light/Directional","../light/Point","../shader/library","../Material","../FrameBuffer","../Texture2D","../TextureCube","../camera/Perspective","../camera/Orthographic","../compositor/Pass","../compositor/TexturePool","../dep/glmatrix"],function(a){var b=a("../core/Base"),c=a("../core/glenum"),d=a("../math/Vector3"),e=a("../math/BoundingBox"),f=a("../math/Frustum"),g=a("../math/Matrix4"),h=a("../Renderer"),i=a("../Shader");a("../Light"),a("../Mesh");var j=a("../light/Spot"),k=a("../light/Directional"),l=a("../light/Point");a("../shader/library");var m=a("../Material"),n=a("../FrameBuffer"),o=a("../Texture2D"),p=a("../TextureCube"),q=a("../camera/Perspective"),r=a("../camera/Orthographic"),s=a("../compositor/Pass"),t=a("../compositor/TexturePool"),u=a("../dep/glmatrix"),v=u.mat4;u.vec3;var w=["px","nx","py","ny","pz","nz"],x=b.derive(function(){return{softShadow:x.PCF,shadowBlur:1,shadowCascade:1,cascadeSplitLogFactor:.2,lightFrustumBias:10,_frameBuffer:new n,_textures:{},_shadowMapNumber:{POINT_LIGHT:0,DIRECTIONAL_LIGHT:0,SPOT_LIGHT:0},_meshMaterials:{},_depthMaterials:{},_depthShaders:{},_distanceMaterials:{},_opaqueCasters:[],_receivers:[],_lightsCastShadow:[],_lightCameras:{},_texturePool:new t}},function(){this._gaussianPassH=new s({fragment:i.source("buildin.compositor.gaussian_blur_h")}),this._gaussianPassV=new s({fragment:i.source("buildin.compositor.gaussian_blur_v")}),this._gaussianPassH.setUniform("blurSize",this.shadowBlur),this._gaussianPassV.setUniform("blurSize",this.shadowBlur),this._outputDepthPass=new s({fragment:i.source("buildin.sm.debug_depth")})},{render:function(a,b,c){this.trigger("beforerender",this,a,b,c),this._renderShadowPass(a,b,c),this.trigger("afterrender",this,a,b,c)},renderDebug:function(a,b){var d=a.clear;a.clear=c.DEPTH_BUFFER_BIT;var e=a.viewport,f=0,g=0,h=b||e.width/4,i=h;this.softShadow===x.VSM?this._outputDepthPass.material.shader.define("fragment","USE_VSM"):this._outputDepthPass.material.shader.unDefine("fragment","USE_VSM");for(var j in this._textures)a.setViewport(f,g,h,i),this._outputDepthPass.setUniform("depthMap",this._textures[j]),this._outputDepthPass.render(a),f+=h;a.setViewport(e),a.clear=d},_bindDepthMaterial:function(a,b,c){for(var d=0;d0&&(n.define("vertex","SKINNING"),n.define("vertex","JOINT_NUMBER",k)),h&&n.define("both","SHADOW_TRANSPARENT"),this._depthShaders[f]=n),l||(l=new m({shader:n}),this._depthMaterials[e]=l),this._meshMaterials[g.__GUID__]=g.material,g.material=l,this.softShadow===x.VSM?n.define("fragment","USE_VSM"):n.unDefine("fragment","USE_VSM"),l.setUniform("bias",b),l.setUniform("slopeScale",c),h&&l.set("shadowTransparentMap",j))}},_bindDistanceMaterial:function(a,b){for(var c=0;c0&&(f.shader.define("vertex","SKINNING"),f.shader.define("vertex","JOINT_NUMBER",e)),this._distanceMaterials[e]=f),this._meshMaterials[d.__GUID__]=d.material,d.material=f,this.softShadow===x.VSM?f.shader.define("fragment","USE_VSM"):f.shader.unDefine("fragment","USE_VSM"),f.set("lightPosition",b.position._array),f.set("range",5*b.range))}},_restoreMaterial:function(a){for(var b=0;b1&&this._shadowMapNumber.DIRECTIONAL_LIGHT>1&&console.warn("There is only one directional light can cast shadow when using cascaded shadow map");var r=m.slice(),s=m.slice();r.pop(),s.shift(),r.reverse(),s.reverse(),h.reverse(),i.reverse();for(var p=0;p0&&(v.fragmentDefines[z]=y,w=!0)}w&&v.dirty(),this.shadowCascade>1?v.define("fragment","SHADOW_CASCADE",this.shadowCascade):v.unDefine("fragment","SHADOW_CASCADE"),v.__shadowDefineUpdated=!0}f.length>0&&(u.setUniform("spotLightShadowMaps",f),u.setUniform("spotLightMatrices",g)),h.length>0&&(u.setUniform("directionalLightShadowMaps",h),this.shadowCascade>1&&(u.setUniform("shadowCascadeClipsNear",r),u.setUniform("shadowCascadeClipsFar",s)),u.setUniform("directionalLightMatrices",i)),n.length>0&&(u.setUniform("pointLightShadowMaps",n),u.setUniform("pointLightRanges",o)),u.__shadowUniformUpdated=!0}}}},_renderDirectionalLightShadow:function(){var a=new f,b=new g,c=new e,d=new g,i=new g,j=new g,k=0,l=0;return function(e,f,m,n,o,p,q,r){var s=f.shadowBias;this._bindDepthMaterial(o,s,f.shadowSlopeScale),o.sort(h.opaqueSortFunc);var t=n.far,u=-n.sceneBoundingBoxLastFrame.min.z;l=Math.max(u-k,0),k=u,u+=l,u>n.near&&(n.far=Math.min(n.far,u)),n.updateProjectionMatrix(),n.frustum.setFromProjection(n.projectionMatrix);var w=this._getDirectionalLightCamera(f,m,n),y=i._array;v.copy(y,w.worldTransform._array),v.invert(y,y),v.multiply(y,w.projectionMatrix._array,y),v.multiply(y,y,n.worldTransform._array),j.copy(w.projectionMatrix);for(var z=[],A=n.near,B=n.far,C=n.fov/180*Math.PI,D=n.aspect,E=(A+t)/(A-t),F=2*A*t/(A-t),G=0;G<=this.shadowCascade;G++){var H=A*Math.pow(B/A,G/this.shadowCascade),I=A+(B-A)*G/this.shadowCascade,J=H*this.cascadeSplitLogFactor+I*(1-this.cascadeSplitLogFactor);z.push(J),p.push(-(-J*E+F)/-J)}for(var G=0;Gh;h++){var i=w[h],j=this._getPointLightCamera(b,i);this._frameBuffer.attach(a.gl,f,g.COLOR_ATTACHMENT0,g.TEXTURE_CUBE_MAP_POSITIVE_X+h),this._frameBuffer.bind(a),g.clear(g.COLOR_BUFFER_BIT|g.DEPTH_BUFFER_BIT),a.renderQueue(c,j),this._frameBuffer.unbind(a)}},_gaussianFilter:function(a,b,d){var e={width:d,height:d,type:c.FLOAT},f=a.gl,g=this._texturePool.get(e);this._frameBuffer.attach(f,g),this._frameBuffer.bind(a),this._gaussianPassH.setUniform("texture",b),this._gaussianPassH.setUniform("textureWidth",d),this._gaussianPassH.render(a),this._frameBuffer.unbind(a),this._frameBuffer.attach(f,b),this._frameBuffer.bind(a),this._gaussianPassV.setUniform("texture",g),this._gaussianPassV.setUniform("textureHeight",d),this._gaussianPassV.render(a),this._frameBuffer.unbind(a),this._texturePool.put(g)},_getTexture:function(a,b){var d=this._textures[a],e=b.shadowResolution||512;return d||(d=b instanceof l?new p:new o,d.width=e,d.height=e,this.softShadow===x.VSM?(d.type=c.FLOAT,d.anisotropic=4):(d.minFilter=c.LINEAR,d.magFilter=c.LINEAR,d.useMipmap=!1),this._textures[a]=d),d},_getPointLightCamera:function(a,b){this._lightCameras.point||(this._lightCameras.point={px:new q,nx:new q,py:new q,ny:new q,pz:new q,nz:new q});var c=this._lightCameras.point[b];switch(c.far=a.range,c.fov=90,c.position.set(0,0,0),b){case"px":c.lookAt(d.POSITIVE_X,d.NEGATIVE_Y);break;case"nx":c.lookAt(d.NEGATIVE_X,d.NEGATIVE_Y);break;case"py":c.lookAt(d.POSITIVE_Y,d.POSITIVE_Z);break;case"ny":c.lookAt(d.NEGATIVE_Y,d.NEGATIVE_Z);break;case"pz":c.lookAt(d.POSITIVE_Z,d.NEGATIVE_Y);break;case"nz":c.lookAt(d.NEGATIVE_Z,d.NEGATIVE_Y)}return c.position.copy(a.position),c.update(),c},_getDirectionalLightCamera:function(){var a=new g,b=new e;return function(c,d,e){this._lightCameras.directional||(this._lightCameras.directional=new r);var f=this._lightCameras.directional;f.position.copy(e.frustum.boundingBox.min).add(e.frustum.boundingBox.max).scale(.5).transformMat4(e.worldTransform),f.rotation.copy(c.rotation),f.scale.copy(c.scale),f.updateLocalTransform(),f.updateWorldTransform(),a.copy(f.worldTransform).invert().multiply(e.worldTransform),e.frustum.getTransformedBoundingBox(b,a);var g=b.min._array,h=b.max._array;return f.position.scaleAndAdd(f.worldTransform.z,h[2]+this.lightFrustumBias),f.near=0,f.far=-g[2]+h[2]+this.lightFrustumBias,f.left=g[0]-this.lightFrustumBias,f.right=h[0]+this.lightFrustumBias,f.top=h[1]+this.lightFrustumBias,f.bottom=g[1]-this.lightFrustumBias,f.update(!0),f}}(),_getSpotLightCamera:function(a){this._lightCameras.spot||(this._lightCameras.spot=new q);var b=this._lightCameras.spot;return b.fov=2*a.penumbraAngle,b.far=a.range,b.worldTransform.copy(a.worldTransform),b.updateProjectionMatrix(),v.invert(b.viewMatrix._array,b.worldTransform._array),b},dispose:function(a){var b=a.gl;for(var c in this._depthMaterials){var d=this._depthMaterials[c];d.dispose(b)}for(var c in this._distanceMaterials){var d=this._distanceMaterials[c];d.dispose(b)}this._frameBuffer&&this._frameBuffer.dispose(b);for(var e in this._textures)this._textures[e].dispose(b);this._texturePool.clear(a.gl),this._depthMaterials={},this._distanceMaterials={},this._textures={},this._lightCameras={},this._shadowMapNumber={POINT_LIGHT:0,DIRECTIONAL_LIGHT:0,SPOT_LIGHT:0},this._meshMaterials={};for(var f=0;f 0.01)\n {\n gl_FragColor.rgb = gl_FragColor.rgb * mix(lineColor, vec3(1.0), edgeFactor(lineWidth));\n }\n}\n\n@end"}),define("qtek/shader/source/lambert.essl",[],function(){return"/**\n * http://en.wikipedia.org/wiki/Lambertian_reflectance\n */\n\n@export buildin.lambert.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\n\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 normal : NORMAL;\n\nattribute vec3 barycentric;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n // Upper 3x3 of skinMatrix is orthogonal\n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4( skinnedPosition, 1.0 );\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_Normal = normalize( ( worldInverseTranspose * vec4(skinnedNormal, 0.0) ).xyz );\n v_WorldPosition = ( world * vec4( skinnedPosition, 1.0) ).xyz;\n\n v_Barycentric = barycentric;\n}\n\n@end\n\n\n@export buildin.lambert.fragment\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\n\nuniform sampler2D diffuseMap;\nuniform sampler2D alphaMap;\n\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform vec3 emission : [0.0, 0.0, 0.0];\nuniform float alpha : 1.0;\n\n// Uniforms for wireframe\nuniform float lineWidth : 0.0;\nuniform vec3 lineColor : [0.0, 0.0, 0.0];\nvarying vec3 v_Barycentric;\n\n#ifdef AMBIENT_LIGHT_NUMBER\n@import buildin.header.ambient_light\n#endif\n#ifdef POINT_LIGHT_NUMBER\n@import buildin.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_NUMBER\n@import buildin.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_NUMBER\n@import buildin.header.spot_light\n#endif\n\n#extension GL_OES_standard_derivatives : enable\n// Import util functions and uniforms needed\n@import buildin.util.calculate_attenuation\n\n@import buildin.util.edge_factor\n\n@import buildin.plugin.compute_shadow_map\n\nvoid main()\n{\n #ifdef RENDER_NORMAL\n gl_FragColor = vec4(v_Normal, 1.0);\n return;\n #endif\n #ifdef RENDER_TEXCOORD\n gl_FragColor = vec4(v_Texcoord, 1.0, 1.0);\n return;\n #endif\n\n gl_FragColor = vec4(color, alpha);\n\n #ifdef DIFFUSEMAP_ENABLED\n vec4 tex = texture2D( diffuseMap, v_Texcoord );\n #ifdef SRGB_DECODE\n tex.rgb = pow(tex.rgb, vec3(2.2));\n #endif\n gl_FragColor.rgb *= tex.rgb;\n #ifdef DIFFUSEMAP_ALPHA_ALPHA\n gl_FragColor.a *= tex.a;\n #endif\n #endif\n\n vec3 diffuseColor = vec3(0.0, 0.0, 0.0);\n \n #ifdef AMBIENT_LIGHT_NUMBER\n for(int i = 0; i < AMBIENT_LIGHT_NUMBER; i++)\n {\n diffuseColor += ambientLightColor[i];\n }\n #endif\n // Compute point light color\n #ifdef POINT_LIGHT_NUMBER\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[POINT_LIGHT_NUMBER];\n if( shadowEnabled )\n {\n computeShadowOfPointLights( v_WorldPosition, shadowContribs );\n }\n #endif\n for(int i = 0; i < POINT_LIGHT_NUMBER; i++)\n {\n\n vec3 lightPosition = pointLightPosition[i];\n vec3 lightColor = pointLightColor[i];\n float range = pointLightRange[i];\n\n vec3 lightDirection = lightPosition - v_WorldPosition;\n\n // Calculate point light attenuation\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range);\n\n // Normalize vectors\n lightDirection /= dist;\n\n float ndl = dot( v_Normal, lightDirection );\n\n float shadowContrib = 1.0;\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n diffuseColor += lightColor * clamp(ndl, 0.0, 1.0) * attenuation * shadowContrib;\n }\n #endif\n #ifdef DIRECTIONAL_LIGHT_NUMBER\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[DIRECTIONAL_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfDirectionalLights( v_WorldPosition, shadowContribs );\n }\n #endif\n for(int i = 0; i < DIRECTIONAL_LIGHT_NUMBER; i++)\n {\n vec3 lightDirection = -directionalLightDirection[i];\n vec3 lightColor = directionalLightColor[i];\n \n float ndl = dot( v_Normal, normalize( lightDirection ) );\n\n float shadowContrib = 1.0;\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n diffuseColor += lightColor * clamp(ndl, 0.0, 1.0) * shadowContrib;\n }\n #endif\n \n #ifdef SPOT_LIGHT_NUMBER\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[SPOT_LIGHT_NUMBER];\n if( shadowEnabled )\n {\n computeShadowOfSpotLights( v_WorldPosition, shadowContribs );\n }\n #endif\n for(int i = 0; i < SPOT_LIGHT_NUMBER; i++)\n {\n vec3 lightPosition = -spotLightPosition[i];\n vec3 spotLightDirection = -normalize( spotLightDirection[i] );\n vec3 lightColor = spotLightColor[i];\n float range = spotLightRange[i];\n float a = spotLightUmbraAngleCosine[i];\n float b = spotLightPenumbraAngleCosine[i];\n float falloffFactor = spotLightFalloffFactor[i];\n\n vec3 lightDirection = lightPosition - v_WorldPosition;\n // Calculate attenuation\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range); \n\n // Normalize light direction\n lightDirection /= dist;\n // Calculate spot light fall off\n float c = dot(spotLightDirection, lightDirection);\n\n float falloff;\n falloff = clamp((c - a) /( b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n\n float ndl = dot(v_Normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n diffuseColor += lightColor * ndl * attenuation * (1.0-falloff) * shadowContrib;\n\n }\n #endif\n\n gl_FragColor.rgb *= diffuseColor;\n gl_FragColor.rgb += emission;\n if(lineWidth > 0.01)\n {\n gl_FragColor.rgb = gl_FragColor.rgb * mix(lineColor, vec3(1.0), edgeFactor(lineWidth));\n }\n}\n\n@end" -}),define("qtek/shader/source/phong.essl",[],function(){return"\n// http://en.wikipedia.org/wiki/Blinn%E2%80%93Phong_shading_model\n\n@export buildin.phong.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\n\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 normal : NORMAL;\nattribute vec4 tangent : TANGENT;\n\n#ifdef VERTEX_COLOR\nattribute vec4 color : COLOR;\n#endif\n\nattribute vec3 barycentric;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\n\n#ifdef NORMALMAP_ENABLED\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\n#ifdef VERTEX_COLOR\nvarying vec4 v_Color;\n#endif\n\nvoid main()\n{\n \n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n // Upper 3x3 of skinMatrix is orthogonal\n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n skinnedTangent = (skinMatrixWS * vec4(tangent.xyz, 0.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n v_Barycentric = barycentric;\n\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\n \n #ifdef NORMALMAP_ENABLED\n v_Tangent = normalize((worldInverseTranspose * vec4(skinnedTangent, 0.0)).xyz);\n v_Bitangent = normalize(cross(v_Normal, v_Tangent) * tangent.w);\n #endif\n\n #ifdef VERTEX_COLOR\n v_Color = color;\n #endif\n}\n\n@end\n\n\n@export buildin.phong.fragment\n\nuniform mat4 viewInverse : VIEWINVERSE;\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\n\n#ifdef NORMALMAP_ENABLED\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\nuniform sampler2D diffuseMap;\nuniform sampler2D normalMap;\nuniform sampler2D specularMap;\nuniform samplerCube environmentMap;\n\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\n\nuniform float shininess : 30;\n\nuniform vec3 specularColor : [1.0, 1.0, 1.0];\nuniform vec3 emission : [0.0, 0.0, 0.0];\n\nuniform float reflectivity : 0.5;\n\n// Uniforms for wireframe\nuniform float lineWidth : 0.0;\nuniform vec3 lineColor : [0.0, 0.0, 0.0];\nvarying vec3 v_Barycentric;\n\n#ifdef AMBIENT_LIGHT_NUMBER\n@import buildin.header.ambient_light\n#endif\n#ifdef POINT_LIGHT_NUMBER\n@import buildin.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_NUMBER\n@import buildin.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_NUMBER\n@import buildin.header.spot_light\n#endif\n\n#extension GL_OES_standard_derivatives : enable\n// Import util functions and uniforms needed\n@import buildin.util.calculate_attenuation\n\n@import buildin.util.edge_factor\n\n@import buildin.plugin.compute_shadow_map\n\nvoid main()\n{\n #ifdef RENDER_TEXCOORD\n gl_FragColor = vec4(v_Texcoord, 1.0, 1.0);\n return;\n #endif\n\n vec4 finalColor = vec4(color, alpha);\n\n vec3 eyePos = viewInverse[3].xyz;\n vec3 viewDirection = normalize(eyePos - v_WorldPosition);\n\n #ifdef DIFFUSEMAP_ENABLED\n vec4 tex = texture2D(diffuseMap, v_Texcoord);\n #ifdef SRGB_DECODE\n tex.rgb = pow(tex.rgb, vec3(2.2));\n #endif\n finalColor.rgb *= tex.rgb;\n #ifdef DIFFUSEMAP_ALPHA_ALPHA\n finalColor.a *= tex.a;\n #endif\n #endif\n\n vec3 spec = specularColor;\n #ifdef SPECULARMAP_ENABLED\n spec *= texture2D(specularMap, v_Texcoord).rgb;\n #endif\n\n vec3 normal = v_Normal;\n #ifdef NORMALMAP_ENABLED\n normal = texture2D(normalMap, v_Texcoord).xyz * 2.0 - 1.0;\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\n normal = normalize(tbn * normal);\n #endif\n\n #ifdef RENDER_NORMAL\n gl_FragColor = vec4(normal, 1.0);\n return;\n #endif\n\n // Diffuse part of all lights\n vec3 diffuseTerm = vec3(0.0, 0.0, 0.0);\n // Specular part of all lights\n vec3 specularTerm = vec3(0.0, 0.0, 0.0);\n \n #ifdef AMBIENT_LIGHT_NUMBER\n for(int i = 0; i < AMBIENT_LIGHT_NUMBER; i++)\n {\n diffuseTerm += ambientLightColor[i];\n }\n #endif\n #ifdef POINT_LIGHT_NUMBER\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[POINT_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfPointLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < POINT_LIGHT_NUMBER; i++)\n {\n vec3 lightPosition = pointLightPosition[i];\n vec3 lightColor = pointLightColor[i];\n float range = pointLightRange[i];\n\n vec3 lightDirection = lightPosition - v_WorldPosition;\n\n // Calculate point light attenuation\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range); \n\n // Normalize vectors\n lightDirection /= dist;\n vec3 halfVector = normalize(lightDirection + viewDirection);\n\n float ndh = dot(normal, halfVector);\n ndh = clamp(ndh, 0.0, 1.0);\n\n float ndl = dot(normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lightColor * ndl * attenuation * shadowContrib;\n\n diffuseTerm += li;\n if (shininess > 0.0)\n {\n specularTerm += li * pow(ndh, shininess);\n }\n\n }\n #endif\n\n #ifdef DIRECTIONAL_LIGHT_NUMBER\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[DIRECTIONAL_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfDirectionalLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < DIRECTIONAL_LIGHT_NUMBER; i++)\n {\n\n vec3 lightDirection = -normalize(directionalLightDirection[i]);\n vec3 lightColor = directionalLightColor[i];\n\n vec3 halfVector = normalize(lightDirection + viewDirection);\n\n float ndh = dot(normal, halfVector);\n ndh = clamp(ndh, 0.0, 1.0);\n\n float ndl = dot(normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lightColor * ndl * shadowContrib;\n\n diffuseTerm += li;\n if (shininess > 0.0)\n {\n specularTerm += li * pow(ndh, shininess);\n }\n }\n #endif\n\n #ifdef SPOT_LIGHT_NUMBER\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[SPOT_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfSpotLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < SPOT_LIGHT_NUMBER; i++)\n {\n vec3 lightPosition = spotLightPosition[i];\n vec3 spotLightDirection = -normalize(spotLightDirection[i]);\n vec3 lightColor = spotLightColor[i];\n float range = spotLightRange[i];\n float a = spotLightUmbraAngleCosine[i];\n float b = spotLightPenumbraAngleCosine[i];\n float falloffFactor = spotLightFalloffFactor[i];\n\n vec3 lightDirection = lightPosition - v_WorldPosition;\n // Calculate attenuation\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range); \n\n // Normalize light direction\n lightDirection /= dist;\n // Calculate spot light fall off\n float c = dot(spotLightDirection, lightDirection);\n\n float falloff;\n // Fomular from real-time-rendering\n falloff = clamp((c - a) /( b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n\n vec3 halfVector = normalize(lightDirection + viewDirection);\n\n float ndh = dot(normal, halfVector);\n ndh = clamp(ndh, 0.0, 1.0);\n\n float ndl = dot(normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n if (shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lightColor * ndl * attenuation * (1.0-falloff) * shadowContrib;\n\n diffuseTerm += li;\n if (shininess > 0.0)\n {\n specularTerm += li * pow(ndh, shininess);\n }\n }\n #endif\n\n finalColor.rgb *= diffuseTerm;\n finalColor.rgb += specularTerm * spec;\n finalColor.rgb += emission;\n\n #ifdef ENVIRONMENTMAP_ENABLED\n vec3 envTex = textureCube(environmentMap, reflect(-viewDirection, normal)).xyz;\n finalColor.rgb = finalColor.rgb + envTex * reflectivity;\n #endif\n\n if(lineWidth > 0.01)\n {\n finalColor.rgb = finalColor.rgb * mix(lineColor, vec3(1.0), edgeFactor(lineWidth));\n }\n\n #ifdef GAMMA_ENCODE\n finalColor.rgb = pow(finalColor.rgb, vec3(1 / 2.2));\n #endif\n\n gl_FragColor = finalColor;\n}\n\n@end"}),define("qtek/shader/source/standard.essl",[],function(){return"\n// http://blog.selfshadow.com/publications/s2013-shading-course/\n\n@export buildin.standard.vertex\n\n@import buildin.phong.vertex\n\n@end\n\n\n@export buildin.standard.fragment\n\n#define PI 3.14159265358979\n\nuniform mat4 viewInverse : VIEWINVERSE;\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\n\n#ifdef NORMALMAP_ENABLED\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\nuniform sampler2D diffuseMap;\nuniform sampler2D normalMap;\nuniform sampler2D specularMap;\nuniform samplerCube environmentMap;\n\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\n\nuniform float glossiness : 0.5;\n\nuniform vec3 specularColor : [0.1, 0.1, 0.1];\nuniform vec3 emission : [0.0, 0.0, 0.0];\n\n// Uniforms for wireframe\nuniform float lineWidth : 0.0;\nuniform vec3 lineColor : [0.0, 0.0, 0.0];\nvarying vec3 v_Barycentric;\n\n#ifdef AMBIENT_LIGHT_NUMBER\n@import buildin.header.ambient_light\n#endif\n#ifdef POINT_LIGHT_NUMBER\n@import buildin.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_NUMBER\n@import buildin.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_NUMBER\n@import buildin.header.spot_light\n#endif\n\n#extension GL_OES_standard_derivatives : enable\n\n// Import util functions and uniforms needed\n@import buildin.util.calculate_attenuation\n\n@import buildin.util.edge_factor\n\n@import buildin.plugin.compute_shadow_map\n\n\nfloat G_Smith(float glossiness, float ndv, float ndl)\n{\n // float k = (roughness+1.0) * (roughness+1.0) * 0.125;\n float roughness = 1.0 - glossiness;\n float k = roughness * roughness / 2.0;\n float G1V = ndv / (ndv * (1.0 - k) + k);\n float G1L = ndl / (ndl * (1.0 - k) + k);\n return G1L * G1V;\n}\n// Fresnel\nvec3 F_Schlick(float ndv, vec3 spec) {\n return spec + (1.0 - spec) * pow(1.0 - ndv, 5.0);\n}\n\nfloat D_Phong(float g, float ndh) {\n // from black ops 2\n float a = pow(8192.0, g);\n return (a + 2.0) / 8.0 * pow(ndh, a);\n}\n\nfloat D_GGX(float g, float ndh) {\n float r = 1.0 - g;\n float a = r * r;\n float tmp = ndh * ndh * (a - 1.0) + 1.0;\n return a / (PI * tmp * tmp);\n}\n\nvoid main()\n{\n #ifdef RENDER_TEXCOORD\n gl_FragColor = vec4(v_Texcoord, 1.0, 1.0);\n return;\n #endif\n\n vec4 finalColor = vec4(color, alpha);\n\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = normalize(eyePos - v_WorldPosition);\n float g = glossiness;\n\n #ifdef DIFFUSEMAP_ENABLED\n vec4 tex = texture2D(diffuseMap, v_Texcoord);\n #ifdef SRGB_DECODE\n tex.rgb = pow(tex.rgb, vec3(2.2));\n #endif\n finalColor.rgb *= tex.rgb;\n #ifdef DIFFUSEMAP_ALPHA_ALPHA\n finalColor.a *= tex.a;\n #endif\n #ifdef DIFFUSEMAP_ALPHA_GLOSS\n g *= tex.a;\n #endif\n #endif\n\n vec3 spec = specularColor;\n #ifdef SPECULARMAP_ENABLED\n spec *= texture2D(specularMap, v_Texcoord).rgb;\n #endif\n\n vec3 N = v_Normal;\n #ifdef NORMALMAP_ENABLED\n N = texture2D(normalMap, v_Texcoord).xyz * 2.0 - 1.0;\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\n N = normalize(tbn * N);\n #endif\n\n #ifdef RENDER_NORMAL\n gl_FragColor = vec4(N, 1.0);\n return;\n #endif\n\n #ifdef RENDER_GLOSSINESS\n gl_FragColor = vec4(vec3(g), 1.0);\n return;\n #endif\n\n // Diffuse part of all lights\n vec3 diffuseTerm = vec3(0.0, 0.0, 0.0);\n // Specular part of all lights\n vec3 specularTerm = vec3(0.0, 0.0, 0.0);\n \n vec3 fresnelTerm = F_Schlick(clamp(dot(N, V), 0.0, 1.0), spec);\n \n #ifdef AMBIENT_LIGHT_NUMBER\n for(int i = 0; i < AMBIENT_LIGHT_NUMBER; i++)\n {\n // Hemisphere ambient lighting from cryengine\n diffuseTerm += ambientLightColor[i] * (clamp(N.y * 0.7, 0.0, 1.0) + 0.3);\n // diffuseTerm += ambientLightColor[i];\n }\n #endif\n #ifdef POINT_LIGHT_NUMBER\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[POINT_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfPointLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < POINT_LIGHT_NUMBER; i++)\n {\n\n vec3 lightPosition = pointLightPosition[i];\n vec3 lc = pointLightColor[i];\n float range = pointLightRange[i];\n\n vec3 L = lightPosition - v_WorldPosition;\n\n // Calculate point light attenuation\n float dist = length(L);\n float attenuation = lightAttenuation(dist, range);\n L /= dist;\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lc * ndl * attenuation * shadowContrib;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }\n #endif\n\n #ifdef DIRECTIONAL_LIGHT_NUMBER\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[DIRECTIONAL_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfDirectionalLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < DIRECTIONAL_LIGHT_NUMBER; i++)\n {\n\n vec3 L = -normalize(directionalLightDirection[i]);\n vec3 lc = directionalLightColor[i];\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lc * ndl * shadowContrib;\n\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }\n #endif\n\n #ifdef SPOT_LIGHT_NUMBER\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[SPOT_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfSpotLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < SPOT_LIGHT_NUMBER; i++)\n {\n vec3 lightPosition = spotLightPosition[i];\n vec3 spotLightDirection = -normalize(spotLightDirection[i]);\n vec3 lc = spotLightColor[i];\n float range = spotLightRange[i];\n float a = spotLightUmbraAngleCosine[i];\n float b = spotLightPenumbraAngleCosine[i];\n float falloffFactor = spotLightFalloffFactor[i];\n\n vec3 L = lightPosition - v_WorldPosition;\n // Calculate attenuation\n float dist = length(L);\n float attenuation = lightAttenuation(dist, range); \n\n // Normalize light direction\n L /= dist;\n // Calculate spot light fall off\n float c = dot(spotLightDirection, L);\n\n float falloff;\n // Fomular from real-time-rendering\n falloff = clamp((c - a) /( b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n if (shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lc * attenuation * (1.0-falloff) * shadowContrib * ndl;\n\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }\n #endif\n\n finalColor.rgb *= diffuseTerm;\n finalColor.rgb += specularTerm;\n finalColor.rgb += emission;\n\n #ifdef ENVIRONMENTMAP_ENABLED\n vec3 envTex = textureCube(environmentMap, reflect(-V, N)).xyz;;\n finalColor.rgb = finalColor.rgb + envTex * g * fresnelTerm;\n #endif\n\n if(lineWidth > 0.)\n {\n finalColor.rgb = finalColor.rgb * mix(lineColor, vec3(1.0), edgeFactor(lineWidth));\n }\n\n #ifdef GAMMA_ENCODE\n finalColor.rgb = pow(finalColor.rgb, vec3(1 / 2.2));\n #endif\n gl_FragColor = finalColor;\n}\n\n@end\n\n\n@export buildin.physical.vertex\n\n@import buildin.standard.vertex\n\n@end\n\n@export buildin.physical.fragment\n\n@import buildin.standard.fragment\n\n@end"}),define("qtek/shader/source/wireframe.essl",[],function(){return"@export buildin.wireframe.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world : WORLD;\n\nattribute vec3 position : POSITION;\nattribute vec3 barycentric;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec3 v_Barycentric;\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n #ifdef SKINNING\n\n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0 );\n\n v_Barycentric = barycentric;\n}\n\n@end\n\n\n@export buildin.wireframe.fragment\n\nuniform vec3 color : [0.0, 0.0, 0.0];\n\nuniform float alpha : 1.0;\nuniform float lineWidth : 1.0;\n\nvarying vec3 v_Barycentric;\n\n#extension GL_OES_standard_derivatives : enable\n\n@import buildin.util.edge_factor\n\nvoid main()\n{\n\n gl_FragColor.rgb = color;\n gl_FragColor.a = ( 1.0-edgeFactor(lineWidth) ) * alpha;\n}\n\n@end"}),define("qtek/shader/source/skybox.essl",[],function(){return"@export buildin.skybox.vertex\n\nuniform mat4 world : WORLD;\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\nvarying vec3 v_WorldPosition;\n\nvoid main()\n{\n v_WorldPosition = (world * vec4(position, 1.0)).xyz;\n gl_Position = worldViewProjection * vec4(position, 1.0);\n}\n\n@end\n\n@export buildin.skybox.fragment\n\nuniform mat4 viewInverse : VIEWINVERSE;\nuniform samplerCube environmentMap;\n\nvarying vec3 v_WorldPosition;\n\nvoid main()\n{\n vec3 eyePos = viewInverse[3].xyz;\n vec3 viewDirection = normalize(v_WorldPosition - eyePos);\n\n vec3 tex = textureCube(environmentMap, viewDirection).xyz;\n\n #ifdef SRGB_DECODE\n tex.rgb = pow(tex.rgb, vec3(2.2));\n #endif\n \n gl_FragColor = vec4(tex, 1.0);\n}\n@end"}),define("qtek/shader/source/shadowmap.essl",[],function(){return"\n@export buildin.sm.depth.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\n#ifdef SHADOW_TRANSPARENT \nattribute vec2 texcoord : TEXCOORD_0;\n#endif\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec4 v_ViewPosition;\n\n#ifdef SHADOW_TRANSPARENT\nvarying vec2 v_Texcoord;\n#endif\n\nvoid main(){\n \n vec3 skinnedPosition = position;\n \n #ifdef SKINNING\n\n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n\n v_ViewPosition = worldViewProjection * vec4(skinnedPosition, 1.0);\n gl_Position = v_ViewPosition;\n\n #ifdef SHADOW_TRANSPARENT\n v_Texcoord = texcoord;\n #endif\n}\n@end\n\n@export buildin.sm.depth.fragment\n\nvarying vec4 v_ViewPosition;\n\n#ifdef SHADOW_TRANSPARENT\nvarying vec2 v_Texcoord;\n#endif\n\nuniform float bias : 0.001;\nuniform float slopeScale : 1.0;\n\n#ifdef SHADOW_TRANSPARENT\nuniform sampler2D transparentMap;\n#endif\n\n#extension GL_OES_standard_derivatives : enable\n\n@import buildin.util.encode_float\n\nvoid main(){\n // Whats the difference between gl_FragCoord.z and this v_ViewPosition\n // gl_FragCoord consider the polygon offset ?\n float depth = v_ViewPosition.z / v_ViewPosition.w;\n // float depth = gl_FragCoord.z / gl_FragCoord.w;\n\n #ifdef USE_VSM\n depth = depth * 0.5 + 0.5;\n float moment1 = depth;\n float moment2 = depth * depth;\n\n // Adjusting moments using partial derivative\n float dx = dFdx(depth);\n float dy = dFdy(depth);\n moment2 += 0.25*(dx*dx+dy*dy);\n\n gl_FragColor = vec4(moment1, moment2, 0.0, 1.0);\n #else\n // Add slope scaled bias using partial derivative\n float dx = dFdx(depth);\n float dy = dFdy(depth);\n depth += sqrt(dx*dx + dy*dy) * slopeScale + bias;\n\n #ifdef SHADOW_TRANSPARENT\n if (texture2D(transparentMap, v_Texcoord).a <= 0.1) {\n // Hi-Z\n gl_FragColor = encodeFloat(0.9999);\n return;\n }\n #endif\n\n gl_FragColor = encodeFloat(depth * 0.5 + 0.5);\n #endif\n}\n@end\n\n@export buildin.sm.debug_depth\n\nuniform sampler2D depthMap;\nvarying vec2 v_Texcoord;\n\n@import buildin.util.decode_float\n\nvoid main() {\n vec4 tex = texture2D(depthMap, v_Texcoord);\n #ifdef USE_VSM\n gl_FragColor = vec4(tex.rgb, 1.0);\n #else\n float depth = decodeFloat(tex);\n gl_FragColor = vec4(depth, depth, depth, 1.0);\n #endif\n}\n\n@end\n\n\n@export buildin.sm.distance.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world : WORLD;\n\nattribute vec3 position : POSITION;\n\n#ifdef SKINNING\nattribute vec3 boneWeight;\nattribute vec4 boneIndex;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec3 v_WorldPosition;\n\nvoid main (){\n\n vec3 skinnedPosition = position;\n #ifdef SKINNING\n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition , 1.0);\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n}\n\n@end\n\n@export buildin.sm.distance.fragment\n\nuniform vec3 lightPosition;\nuniform float range : 100;\n\nvarying vec3 v_WorldPosition;\n\n@import buildin.util.encode_float\n\nvoid main(){\n float dist = distance(lightPosition, v_WorldPosition);\n #ifdef USE_VSM\n gl_FragColor = vec4(dist, dist * dist, 0.0, 0.0);\n #else\n dist = dist / range;\n gl_FragColor = encodeFloat(dist);\n #endif\n}\n@end\n\n@export buildin.plugin.compute_shadow_map\n\n#if defined(SPOT_LIGHT_SHADOWMAP_NUMBER) || defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER) || defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n\n#ifdef SPOT_LIGHT_SHADOWMAP_NUMBER\nuniform sampler2D spotLightShadowMaps[SPOT_LIGHT_SHADOWMAP_NUMBER];\nuniform mat4 spotLightMatrices[SPOT_LIGHT_SHADOWMAP_NUMBER];\n#endif\n\n#ifdef DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER\n#if defined(SHADOW_CASCADE)\nuniform sampler2D directionalLightShadowMaps[SHADOW_CASCADE];\nuniform mat4 directionalLightMatrices[SHADOW_CASCADE];\nuniform float shadowCascadeClipsNear[SHADOW_CASCADE];\nuniform float shadowCascadeClipsFar[SHADOW_CASCADE];\n#else\nuniform sampler2D directionalLightShadowMaps[DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER];\nuniform mat4 directionalLightMatrices[DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER];\n#endif\n#endif\n\n#ifdef POINT_LIGHT_SHADOWMAP_NUMBER\nuniform samplerCube pointLightShadowMaps[POINT_LIGHT_SHADOWMAP_NUMBER];\nuniform float pointLightRanges[POINT_LIGHT_SHADOWMAP_NUMBER];\n#endif\n\nuniform bool shadowEnabled : true;\n\n@import buildin.util.decode_float\n\n#if defined(DIRECTIONAL_LIGHT_NUMBER) || defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n\nfloat tapShadowMap(sampler2D map, vec2 uv, float z){\n vec4 tex = texture2D(map, uv);\n return decodeFloat(tex) * 2.0 - 1.0 < z ? 0.0 : 1.0;\n}\n\nfloat pcf(sampler2D map, vec2 uv, float z){\n\n float shadowContrib = tapShadowMap(map, uv, z);\n float offset = 1.0 / 2048.0;\n shadowContrib += tapShadowMap(map, uv+vec2(offset, 0.0), z);\n shadowContrib += tapShadowMap(map, uv+vec2(offset, offset), z);\n shadowContrib += tapShadowMap(map, uv+vec2(-offset, offset), z);\n shadowContrib += tapShadowMap(map, uv+vec2(0.0, offset), z);\n shadowContrib += tapShadowMap(map, uv+vec2(-offset, 0.0), z);\n shadowContrib += tapShadowMap(map, uv+vec2(-offset, -offset), z);\n shadowContrib += tapShadowMap(map, uv+vec2(offset, -offset), z);\n shadowContrib += tapShadowMap(map, uv+vec2(0.0, -offset), z);\n\n return shadowContrib / 9.0;\n}\nfloat chebyshevUpperBound(vec2 moments, float z){\n float p = 0.0;\n z = z * 0.5 + 0.5;\n if (z <= moments.x) {\n p = 1.0;\n }\n float variance = moments.y - moments.x * moments.x;\n // http://fabiensanglard.net/shadowmappingVSM/\n variance = max(variance, 0.0000001);\n // Compute probabilistic upper bound. \n float mD = moments.x - z;\n float pMax = variance / (variance + mD * mD);\n // Now reduce light-bleeding by removing the [0, x] tail and linearly rescaling (x, 1]\n // TODO : bleedBias parameter ?\n pMax = clamp((pMax-0.4)/(1.0-0.4), 0.0, 1.0);\n return max(p, pMax);\n}\nfloat computeShadowContrib(sampler2D map, mat4 lightVPM, vec3 position){\n \n vec4 posInLightSpace = lightVPM * vec4(v_WorldPosition, 1.0);\n posInLightSpace.xyz /= posInLightSpace.w;\n float z = posInLightSpace.z;\n // In frustum\n if(all(greaterThan(posInLightSpace.xyz, vec3(-0.99, -0.99, -1.0))) &&\n all(lessThan(posInLightSpace.xyz, vec3(0.99, 0.99, 1.0)))){\n // To texture uv\n vec2 uv = (posInLightSpace.xy+1.0) / 2.0;\n\n #ifdef USE_VSM\n vec2 moments = texture2D(map, uv).xy;\n return chebyshevUpperBound(moments, z);\n #else\n return pcf(map, uv, z);\n #endif\n }\n return 1.0;\n}\n\n#endif\n\n#ifdef POINT_LIGHT_SHADOWMAP_NUMBER\n\nfloat computeShadowOfCube(samplerCube map, vec3 direction, float range){\n vec4 shadowTex = textureCube(map, direction);\n float dist = length(direction);\n\n #ifdef USE_VSM\n vec2 moments = shadowTex.xy;\n float variance = moments.y - moments.x * moments.x;\n float mD = moments.x - dist;\n float p = variance / (variance + mD * mD);\n if(moments.x + 0.001 < dist){\n return clamp(p, 0.0, 1.0);\n }else{\n return 1.0;\n }\n #else\n if((decodeFloat(shadowTex) + 0.0002) * range < dist){\n return 0.0;\n }else{\n return 1.0;\n }\n #endif\n}\n#endif\n\n#if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n\nvoid computeShadowOfSpotLights(vec3 position, inout float shadowContribs[SPOT_LIGHT_NUMBER] ){\n for(int i = 0; i < SPOT_LIGHT_SHADOWMAP_NUMBER; i++){\n float shadowContrib = computeShadowContrib(spotLightShadowMaps[i], spotLightMatrices[i], position);\n shadowContribs[i] = shadowContrib;\n }\n // set default fallof of rest lights\n for(int i = SPOT_LIGHT_SHADOWMAP_NUMBER; i < SPOT_LIGHT_NUMBER; i++){\n shadowContribs[i] = 1.0;\n }\n}\n\n#endif\n\n\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n\n#ifdef SHADOW_CASCADE\n\nvoid computeShadowOfDirectionalLights(vec3 position, inout float shadowContribs[DIRECTIONAL_LIGHT_NUMBER]){\n // http://www.opengl.org/wiki/Compute_eye_space_from_window_space\n float depth = (2.0 * gl_FragCoord.z - gl_DepthRange.near - gl_DepthRange.far)\n / (gl_DepthRange.far - gl_DepthRange.near);\n\n // Pixels not in light box are lighted\n // TODO\n shadowContribs[0] = 1.0;\n\n for (int i = 0; i < SHADOW_CASCADE; i++) {\n if (\n depth >= shadowCascadeClipsNear[i] &&\n depth <= shadowCascadeClipsFar[i]\n ) {\n float shadowContrib = computeShadowContrib(directionalLightShadowMaps[i], directionalLightMatrices[i], position);\n // TODO Will get a sampler needs to be be uniform error in native gl\n shadowContribs[0] = shadowContrib;\n }\n }\n // set default fallof of rest lights\n for(int i = DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER; i < DIRECTIONAL_LIGHT_NUMBER; i++){\n shadowContribs[i] = 1.0;\n }\n}\n\n#else\n\nvoid computeShadowOfDirectionalLights(vec3 position, inout float shadowContribs[DIRECTIONAL_LIGHT_NUMBER]){\n for(int i = 0; i < DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER; i++){\n float shadowContrib = computeShadowContrib(directionalLightShadowMaps[i], directionalLightMatrices[i], position);\n shadowContribs[i] = shadowContrib;\n }\n // set default fallof of rest lights\n for(int i = DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER; i < DIRECTIONAL_LIGHT_NUMBER; i++){\n shadowContribs[i] = 1.0;\n }\n}\n#endif\n\n#endif\n\n\n#if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n\nvoid computeShadowOfPointLights(vec3 position, inout float shadowContribs[POINT_LIGHT_NUMBER] ){\n for(int i = 0; i < POINT_LIGHT_SHADOWMAP_NUMBER; i++){\n vec3 lightPosition = pointLightPosition[i];\n vec3 direction = position - lightPosition;\n shadowContribs[i] = computeShadowOfCube(pointLightShadowMaps[i], direction, pointLightRanges[i]);\n }\n for(int i = POINT_LIGHT_SHADOWMAP_NUMBER; i < POINT_LIGHT_NUMBER; i++){\n shadowContribs[i] = 1.0;\n }\n}\n\n#endif\n\n#endif\n\n@end" -}),define("qtek/shader/source/compositor/coloradjust.essl",[],function(){return'@export buildin.compositor.coloradjust\n\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float brightness : 0.0;\nuniform float contrast : 1.0;\nuniform float exposure : 0.0;\nuniform float gamma : 1.0;\nuniform float saturation : 1.0;\n\n// Values from "Graphics Shaders: Theory and Practice" by Bailey and Cunningham\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord);\n\n // brightness\n vec3 color = clamp(tex.rgb + vec3(brightness), 0.0, 1.0);\n // contrast\n color = clamp( (color-vec3(0.5))*contrast+vec3(0.5), 0.0, 1.0);\n // exposure\n color = clamp( color * pow(2.0, exposure), 0.0, 1.0);\n // gamma\n color = clamp( pow(color, vec3(gamma)), 0.0, 1.0);\n // saturation\n float luminance = dot( color, w );\n color = mix(vec3(luminance), color, saturation);\n \n gl_FragColor = vec4(color, tex.a);\n}\n\n@end\n\n// Seperate shader for float texture\n@export buildin.compositor.brightness\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float brightness : 0.0;\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord);\n vec3 color = tex.rgb + vec3(brightness);\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export buildin.compositor.contrast\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float contrast : 1.0;\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord);\n vec3 color = (tex.rgb-vec3(0.5))*contrast+vec3(0.5);\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export buildin.compositor.exposure\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float exposure : 0.0;\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n vec3 color = tex.rgb * pow(2.0, exposure);\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export buildin.compositor.gamma\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float gamma : 1.0;\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n vec3 color = pow(tex.rgb, vec3(gamma));\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export buildin.compositor.saturation\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float saturation : 1.0;\n\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n vec3 color = tex.rgb;\n float luminance = dot(color, w);\n color = mix(vec3(luminance), color, saturation);\n gl_FragColor = vec4(color, tex.a);\n}\n@end'}),define("qtek/shader/source/compositor/blur.essl",[],function(){return"@export buildin.compositor.gaussian_blur_h\n\nuniform sampler2D texture; // the texture with the scene you want to blur\nvarying vec2 v_Texcoord;\n \nuniform float blurSize : 2.0; \nuniform float textureWidth : 512.0;\n\nvoid main(void)\n{\n vec4 sum = vec4(0.0);\n float blurOffset = blurSize / textureWidth;\n // blur in y (vertical)\n // take nine samples, with the distance blurSize between them\n sum += texture2D(texture, vec2(max(v_Texcoord.x - 4.0*blurOffset, 0.0), v_Texcoord.y)) * 0.05;\n sum += texture2D(texture, vec2(max(v_Texcoord.x - 3.0*blurOffset, 0.0), v_Texcoord.y)) * 0.09;\n sum += texture2D(texture, vec2(max(v_Texcoord.x - 2.0*blurOffset, 0.0), v_Texcoord.y)) * 0.12;\n sum += texture2D(texture, vec2(max(v_Texcoord.x - blurOffset, 0.0), v_Texcoord.y)) * 0.15;\n sum += texture2D(texture, vec2(v_Texcoord.x, v_Texcoord.y)) * 0.18;\n sum += texture2D(texture, vec2(min(v_Texcoord.x + blurOffset, 1.0), v_Texcoord.y)) * 0.15;\n sum += texture2D(texture, vec2(min(v_Texcoord.x + 2.0*blurOffset, 1.0), v_Texcoord.y)) * 0.12;\n sum += texture2D(texture, vec2(min(v_Texcoord.x + 3.0*blurOffset, 1.0), v_Texcoord.y)) * 0.09;\n sum += texture2D(texture, vec2(min(v_Texcoord.x + 4.0*blurOffset, 1.0), v_Texcoord.y)) * 0.05;\n \n gl_FragColor = sum;\n}\n\n@end\n\n@export buildin.compositor.gaussian_blur_v\n\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\n \nuniform float blurSize : 2.0;\nuniform float textureHeight : 512.0;\n \nvoid main(void)\n{\n vec4 sum = vec4(0.0);\n float blurOffset = blurSize / textureHeight;\n // blur in y (vertical)\n // take nine samples, with the distance blurSize between them\n sum += texture2D(texture, vec2(v_Texcoord.x, max(v_Texcoord.y - 4.0*blurOffset, 0.0))) * 0.05;\n sum += texture2D(texture, vec2(v_Texcoord.x, max(v_Texcoord.y - 3.0*blurOffset, 0.0))) * 0.09;\n sum += texture2D(texture, vec2(v_Texcoord.x, max(v_Texcoord.y - 2.0*blurOffset, 0.0))) * 0.12;\n sum += texture2D(texture, vec2(v_Texcoord.x, max(v_Texcoord.y - blurOffset, 0.0))) * 0.15;\n sum += texture2D(texture, vec2(v_Texcoord.x, v_Texcoord.y)) * 0.18;\n sum += texture2D(texture, vec2(v_Texcoord.x, min(v_Texcoord.y + blurOffset, 1.0))) * 0.15;\n sum += texture2D(texture, vec2(v_Texcoord.x, min(v_Texcoord.y + 2.0*blurOffset, 1.0))) * 0.12;\n sum += texture2D(texture, vec2(v_Texcoord.x, min(v_Texcoord.y + 3.0*blurOffset, 1.0))) * 0.09;\n sum += texture2D(texture, vec2(v_Texcoord.x, min(v_Texcoord.y + 4.0*blurOffset, 1.0))) * 0.05;\n \n gl_FragColor = sum;\n}\n\n@end\n\n@export buildin.compositor.box_blur\n\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 3.0;\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n\n vec4 tex = texture2D(texture, v_Texcoord);\n vec2 offset = blurSize / textureSize;\n\n tex += texture2D(texture, v_Texcoord + vec2(offset.x, 0.0) );\n tex += texture2D(texture, v_Texcoord + vec2(offset.x, offset.y) );\n tex += texture2D(texture, v_Texcoord + vec2(-offset.x, offset.y) );\n tex += texture2D(texture, v_Texcoord + vec2(0.0, offset.y) );\n tex += texture2D(texture, v_Texcoord + vec2(-offset.x, 0.0) );\n tex += texture2D(texture, v_Texcoord + vec2(-offset.x, -offset.y) );\n tex += texture2D(texture, v_Texcoord + vec2(offset.x, -offset.y) );\n tex += texture2D(texture, v_Texcoord + vec2(0.0, -offset.y) );\n\n tex /= 9.0;\n\n gl_FragColor = tex;\n}\n\n@end\n\n// http://www.slideshare.net/DICEStudio/five-rendering-ideas-from-battlefield-3-need-for-speed-the-run\n@export buildin.compositor.hexagonal_blur_mrt_1\n\n// MRT in chrome\n// https://www.khronos.org/registry/webgl/sdk/tests/conformance/extensions/webgl-draw-buffers.html\n#extension GL_EXT_draw_buffers : require\n\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 2.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n vec2 offset = blurSize / textureSize;\n\n vec4 color = vec4(0.0);\n // Top\n for(int i = 0; i < 10; i++){\n color += 1.0/10.0 * texture2D(texture, v_Texcoord + vec2(0.0, offset.y * float(i)) );\n }\n gl_FragData[0] = color;\n vec4 color2 = vec4(0.0);\n // Down left\n for(int i = 0; i < 10; i++){\n color2 += 1.0/10.0 * texture2D(texture, v_Texcoord - vec2(offset.x * float(i), offset.y * float(i)) );\n }\n gl_FragData[1] = (color + color2) / 2.0;\n}\n\n@end\n\n@export buildin.compositor.hexagonal_blur_mrt_2\n\nuniform sampler2D texture0;\nuniform sampler2D texture1;\n\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 2.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n vec2 offset = blurSize / textureSize;\n\n vec4 color1 = vec4(0.0);\n // Down left\n for(int i = 0; i < 10; i++){\n color1 += 1.0/10.0 * texture2D(texture0, v_Texcoord - vec2(offset.x * float(i), offset.y * float(i)) );\n }\n vec4 color2 = vec4(0.0);\n // Down right\n for(int i = 0; i < 10; i++){\n color2 += 1.0/10.0 * texture2D(texture1, v_Texcoord + vec2(offset.x * float(i), -offset.y * float(i)) );\n }\n\n gl_FragColor = (color1 + color2) / 2.0;\n}\n\n@end\n\n\n@export buildin.compositor.hexagonal_blur_1\n\n#define KERNEL_SIZE 10\n\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 1.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n vec2 offset = blurSize / textureSize;\n\n vec4 color = vec4(0.0);\n float fKernelSize = float(KERNEL_SIZE);\n // Top\n for(int i = 0; i < KERNEL_SIZE; i++){\n color += 1.0 / fKernelSize * texture2D(texture, v_Texcoord + vec2(0.0, offset.y * float(i)) );\n }\n gl_FragColor = color;\n}\n\n@end\n\n@export buildin.compositor.hexagonal_blur_2\n\n#define KERNEL_SIZE 10\n\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 1.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n vec2 offset = blurSize / textureSize;\n offset.y /= 2.0;\n\n vec4 color = vec4(0.0);\n float fKernelSize = float(KERNEL_SIZE);\n // Down left\n for(int i = 0; i < KERNEL_SIZE; i++){\n color += 1.0/fKernelSize * texture2D(texture, v_Texcoord - vec2(offset.x * float(i), offset.y * float(i)) );\n }\n gl_FragColor = color;\n}\n@end\n\n@export buildin.compositor.hexagonal_blur_3\n\n#define KERNEL_SIZE 10\n\nuniform sampler2D texture1;\nuniform sampler2D texture2;\n\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 1.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n vec2 offset = blurSize / textureSize;\n offset.y /= 2.0;\n\n vec4 color1 = vec4(0.0);\n float fKernelSize = float(KERNEL_SIZE);\n // Down left\n for(int i = 0; i < KERNEL_SIZE; i++){\n color1 += 1.0/fKernelSize * texture2D(texture1, v_Texcoord - vec2(offset.x * float(i), offset.y * float(i)) );\n }\n vec4 color2 = vec4(0.0);\n // Down right\n for(int i = 0; i < KERNEL_SIZE; i++){\n color2 += 1.0/fKernelSize * texture2D(texture1, v_Texcoord + vec2(offset.x * float(i), -offset.y * float(i)) );\n }\n\n vec4 color3 = vec4(0.0);\n // Down right\n for(int i = 0; i < KERNEL_SIZE; i++){\n color3 += 1.0/fKernelSize * texture2D(texture2, v_Texcoord + vec2(offset.x * float(i), -offset.y * float(i)) );\n }\n\n gl_FragColor = (color1 + color2 + color3) / 3.0;\n}\n\n@end"}),define("qtek/shader/source/compositor/lum.essl",[],function(){return"\n@export buildin.compositor.lum\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\n\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord );\n float luminance = dot(tex.rgb, w);\n\n gl_FragColor = vec4(vec3(luminance), 1.0);\n}\n\n@end"}),define("qtek/shader/source/compositor/lut.essl",[],function(){return"\n// https://github.com/BradLarson/GPUImage?source=c\n@export buildin.compositor.lut\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\nuniform sampler2D lookup;\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n\n float blueColor = tex.b * 63.0;\n \n vec2 quad1;\n quad1.y = floor(floor(blueColor) / 8.0);\n quad1.x = floor(blueColor) - (quad1.y * 8.0);\n \n vec2 quad2;\n quad2.y = floor(ceil(blueColor) / 8.0);\n quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n \n vec2 texPos1;\n texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.r);\n texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.g);\n \n vec2 texPos2;\n texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.r);\n texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.g);\n \n vec4 newColor1 = texture2D(lookup, texPos1);\n vec4 newColor2 = texture2D(lookup, texPos2);\n \n vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n gl_FragColor = vec4(newColor.rgb, tex.w);\n}\n\n@end"}),define("qtek/shader/source/compositor/output.essl",[],function(){return"@export buildin.compositor.output\n\n#define OUTPUT_ALPHA;\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n\n gl_FragColor.rgb = tex.rgb;\n\n #ifdef OUTPUT_ALPHA\n gl_FragColor.a = tex.a;\n #else\n gl_FragColor.a = 1.0;\n #endif\n\n}\n\n@end"}),define("qtek/shader/source/compositor/hdr.essl",[],function(){return"// HDR Pipeline\n@export buildin.compositor.hdr.bright\n\nuniform sampler2D texture;\nuniform float threshold : 1;\nuniform float scale : 1.0;\n\nvarying vec2 v_Texcoord;\n\nconst vec3 lumWeight = vec3(0.2125, 0.7154, 0.0721);\n\n@import buildin.util.rgbm_decode\n@import buildin.util.rgbm_encode\n\nvoid main()\n{\n #ifdef TEXTURE_ENABLED\n #ifdef RGBM_DECODE\n vec3 tex = RGBMDecode(texture2D(texture, v_Texcoord));\n #else\n vec3 tex = texture2D(texture, v_Texcoord).rgb;\n #endif\n #else\n vec3 tex = vec3(0.0);\n #endif\n\n float lum = dot(tex, lumWeight);\n if (lum > threshold)\n {\n gl_FragColor.rgb = tex * scale;\n }\n else\n {\n gl_FragColor.rgb = vec3(0.0);\n }\n gl_FragColor.a = 1.0;\n\n #ifdef RGBM_ENCODE\n gl_FragColor.rgba = RGBMEncode(gl_FragColor.rgb);\n #endif\n}\n@end\n\n@export buildin.compositor.hdr.log_lum\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\n\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n float luminance = dot(tex.rgb, w);\n luminance = log(luminance + 0.001);\n\n gl_FragColor = vec4(vec3(luminance), 1.0);\n}\n\n@end\n\n@export buildin.compositor.hdr.lum_adaption\nvarying vec2 v_Texcoord;\n\nuniform sampler2D adaptedLum;\nuniform sampler2D currentLum;\n\nuniform float frameTime : 0.02;\n\nvoid main()\n{\n float fAdaptedLum = texture2D(adaptedLum, vec2(0.5, 0.5)).r;\n float fCurrentLum = exp(texture2D(currentLum, vec2(0.5, 0.5)).r);\n\n fAdaptedLum += (fCurrentLum - fAdaptedLum) * (1.0 - pow(0.98, 30.0 * frameTime));\n gl_FragColor.rgb = vec3(fAdaptedLum);\n gl_FragColor.a = 1.0;\n}\n@end\n\n// Tone mapping with gamma correction\n// http://filmicgames.com/archives/75\n@export buildin.compositor.hdr.tonemapping\n\nuniform sampler2D texture;\nuniform sampler2D bloom;\nuniform sampler2D lensflare;\nuniform sampler2D lum;\n\nuniform float exposure : 1.0;\n\nvarying vec2 v_Texcoord;\n\nconst float A = 0.22; // Shoulder Strength\nconst float B = 0.30; // Linear Strength\nconst float C = 0.10; // Linear Angle\nconst float D = 0.20; // Toe Strength\nconst float E = 0.01; // Toe Numerator\nconst float F = 0.30; // Toe Denominator\nconst vec3 whiteScale = vec3(11.2);\n\nvec3 uncharted2ToneMap(vec3 x)\n{\n return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;\n}\n\nvec3 filmicToneMap(vec3 color)\n{\n vec3 x = max(vec3(0.0), color - 0.004);\n return (x*(6.2*x+0.5))/(x*(6.2*x+1.7)+0.06);\n}\n\nfloat eyeAdaption(float fLum)\n{\n return mix(0.2, fLum, 0.5);\n}\n\nvoid main()\n{\n vec3 tex = vec3(0.0);\n float a = 1.0;\n #ifdef TEXTURE_ENABLED\n vec4 res = texture2D(texture, v_Texcoord);\n a = res.a;\n tex = res.rgb;\n #endif\n\n #ifdef BLOOM_ENABLED\n tex += texture2D(bloom, v_Texcoord).rgb * 0.25;\n #endif\n\n #ifdef LENSFLARE_ENABLED\n tex += texture2D(lensflare, v_Texcoord).rgb;\n #endif\n\n // Adjust exposure\n // From KlayGE\n #ifdef LUM_ENABLED\n float fLum = texture2D(lum, vec2(0.5, 0.5)).r;\n float adaptedLumDest = 3.0 / (max(0.1, 1.0 + 10.0*eyeAdaption(fLum)));\n float exposureBias = adaptedLumDest * exposure;\n #else\n float exposureBias = exposure;\n #endif\n tex *= exposureBias;\n\n // Do tone mapping\n vec3 color = uncharted2ToneMap(tex) / uncharted2ToneMap(whiteScale);\n color = pow(color, vec3(1.0/2.2));\n // vec3 color = filmicToneMap(tex);\n\n #ifdef RGBM_ENCODE\n gl_FragColor.rgba = RGBMEncode(color);\n #else\n gl_FragColor = vec4(color, a);\n #endif\n}\n\n@end"}),define("qtek/shader/source/compositor/lensflare.essl",[],function(){return"// john-chapman-graphics.blogspot.co.uk/2013/02/pseudo-lens-flare.html\n@export buildin.compositor.lensflare\n\n#define SAMPLE_NUMBER 8\n\nuniform sampler2D texture;\nuniform sampler2D lensColor;\n\nuniform vec2 textureSize : [512, 512];\n\nuniform float dispersal : 0.3;\nuniform float haloWidth : 0.4;\nuniform float distortion : 1.0;\n\nvarying vec2 v_Texcoord;\n\nvec4 textureDistorted(\n in vec2 texcoord,\n in vec2 direction,\n in vec3 distortion\n) {\n return vec4(\n texture2D(texture, texcoord + direction * distortion.r).r,\n texture2D(texture, texcoord + direction * distortion.g).g,\n texture2D(texture, texcoord + direction * distortion.b).b,\n 1.0\n );\n}\n\nvoid main()\n{\n vec2 texcoord = -v_Texcoord + vec2(1.0); // Flip texcoords\n vec2 textureOffset = 1.0 / textureSize;\n\n vec2 ghostVec = (vec2(0.5) - texcoord) * dispersal;\n vec2 haloVec = normalize(ghostVec) * haloWidth;\n\n vec3 distortion = vec3(-textureOffset.x * distortion, 0.0, textureOffset.x * distortion);\n //Sample ghost\n vec4 result = vec4(0.0);\n for (int i = 0; i < SAMPLE_NUMBER; i++)\n {\n vec2 offset = fract(texcoord + ghostVec * float(i));\n\n float weight = length(vec2(0.5) - offset) / length(vec2(0.5));\n weight = pow(1.0 - weight, 10.0);\n\n result += textureDistorted(offset, normalize(ghostVec), distortion) * weight;\n }\n\n result *= texture2D(lensColor, vec2(length(vec2(0.5) - texcoord)) / length(vec2(0.5)));\n //Sample halo\n float weight = length(vec2(0.5) - fract(texcoord + haloVec)) / length(vec2(0.5));\n weight = pow(1.0 - weight, 10.0);\n vec2 offset = fract(texcoord + haloVec);\n result += textureDistorted(offset, normalize(ghostVec), distortion) * weight;\n\n gl_FragColor = result;\n}\n@end"}),define("qtek/shader/source/compositor/blend.essl",[],function(){return"@export buildin.compositor.blend\n// Blend at most 4 textures\n#ifdef TEXTURE1_ENABLED\nuniform sampler2D texture1;\nuniform float weight1 : 1.0;\n#endif\n#ifdef TEXTURE2_ENABLED\nuniform sampler2D texture2;\nuniform float weight2 : 1.0;\n#endif\n#ifdef TEXTURE3_ENABLED\nuniform sampler2D texture3;\nuniform float weight3 : 1.0;\n#endif\n#ifdef TEXTURE4_ENABLED\nuniform sampler2D texture4;\nuniform float weight4 : 1.0;\n#endif\n\nvarying vec2 v_Texcoord;\n\nvoid main()\n{\n vec3 tex = vec3(0.0);\n #ifdef TEXTURE1_ENABLED\n tex += texture2D(texture1, v_Texcoord).rgb * weight1;\n #endif\n #ifdef TEXTURE2_ENABLED\n tex += texture2D(texture2, v_Texcoord).rgb * weight2;\n #endif\n #ifdef TEXTURE3_ENABLED\n tex += texture2D(texture3, v_Texcoord).rgb * weight3;\n #endif\n #ifdef TEXTURE4_ENABLED\n tex += texture2D(texture4, v_Texcoord).rgb * weight4;\n #endif\n\n gl_FragColor = vec4(tex, 1.0);\n}\n@end"}),define("qtek/shader/source/compositor/fxaa.essl",[],function(){return"// https://github.com/mitsuhiko/webgl-meincraft/blob/master/assets/shaders/fxaa.glsl\n@export buildin.compositor.fxaa\n\nuniform sampler2D texture;\nuniform vec2 viewportSize : [512, 512];\n\nvarying vec2 v_Texcoord;\n\n#define FXAA_REDUCE_MIN (1.0/128.0)\n#define FXAA_REDUCE_MUL (1.0/8.0)\n#define FXAA_SPAN_MAX 8.0\n\nvoid main()\n{\n vec2 resolution = 1.0 / viewportSize;\n vec3 rgbNW = texture2D( texture, ( gl_FragCoord.xy + vec2( -1.0, -1.0 ) ) * resolution ).xyz;\n vec3 rgbNE = texture2D( texture, ( gl_FragCoord.xy + vec2( 1.0, -1.0 ) ) * resolution ).xyz;\n vec3 rgbSW = texture2D( texture, ( gl_FragCoord.xy + vec2( -1.0, 1.0 ) ) * resolution ).xyz;\n vec3 rgbSE = texture2D( texture, ( gl_FragCoord.xy + vec2( 1.0, 1.0 ) ) * resolution ).xyz;\n vec4 rgbaM = texture2D( texture, gl_FragCoord.xy * resolution );\n vec3 rgbM = rgbaM.xyz;\n float opacity = rgbaM.w;\n\n vec3 luma = vec3( 0.299, 0.587, 0.114 );\n\n float lumaNW = dot( rgbNW, luma );\n float lumaNE = dot( rgbNE, luma );\n float lumaSW = dot( rgbSW, luma );\n float lumaSE = dot( rgbSE, luma );\n float lumaM = dot( rgbM, luma );\n float lumaMin = min( lumaM, min( min( lumaNW, lumaNE ), min( lumaSW, lumaSE ) ) );\n float lumaMax = max( lumaM, max( max( lumaNW, lumaNE) , max( lumaSW, lumaSE ) ) );\n\n vec2 dir;\n dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\n float dirReduce = max( ( lumaNW + lumaNE + lumaSW + lumaSE ) * ( 0.25 * FXAA_REDUCE_MUL ), FXAA_REDUCE_MIN );\n\n float rcpDirMin = 1.0 / ( min( abs( dir.x ), abs( dir.y ) ) + dirReduce );\n dir = min( vec2( FXAA_SPAN_MAX, FXAA_SPAN_MAX),\n max( vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),\n dir * rcpDirMin)) * resolution;\n\n vec3 rgbA = texture2D( texture, gl_FragCoord.xy * resolution + dir * ( 1.0 / 3.0 - 0.5 ) ).xyz;\n rgbA += texture2D( texture, gl_FragCoord.xy * resolution + dir * ( 2.0 / 3.0 - 0.5 ) ).xyz;\n rgbA *= 0.5;\n\n vec3 rgbB = texture2D( texture, gl_FragCoord.xy * resolution + dir * -0.5 ).xyz;\n rgbB += texture2D( texture, gl_FragCoord.xy * resolution + dir * 0.5 ).xyz;\n rgbB *= 0.25;\n rgbB += rgbA * 0.5;\n\n float lumaB = dot( rgbB, luma );\n\n if ( ( lumaB < lumaMin ) || ( lumaB > lumaMax ) )\n {\n\n gl_FragColor = vec4( rgbA, opacity );\n\n } else {\n\n gl_FragColor = vec4( rgbB, opacity );\n\n }\n}\n\n@end"}),define("qtek/shader/buildin",["require","./library","../Shader","./source/basic.essl","./source/lambert.essl","./source/phong.essl","./source/standard.essl","./source/wireframe.essl","./source/skybox.essl","./source/util.essl","./source/prez.essl","./source/shadowmap.essl","./source/compositor/coloradjust.essl","./source/compositor/blur.essl","./source/compositor/lum.essl","./source/compositor/lut.essl","./source/compositor/output.essl","./source/compositor/hdr.essl","./source/compositor/lensflare.essl","./source/compositor/blend.essl","./source/compositor/fxaa.essl"],function(a){var b=a("./library"),c=a("../Shader");c["import"](a("./source/basic.essl")),c["import"](a("./source/lambert.essl")),c["import"](a("./source/phong.essl")),c["import"](a("./source/standard.essl")),c["import"](a("./source/wireframe.essl")),c["import"](a("./source/skybox.essl")),c["import"](a("./source/util.essl")),c["import"](a("./source/prez.essl")),c["import"](a("./source/shadowmap.essl")),b.template("buildin.basic",c.source("buildin.basic.vertex"),c.source("buildin.basic.fragment")),b.template("buildin.lambert",c.source("buildin.lambert.vertex"),c.source("buildin.lambert.fragment")),b.template("buildin.phong",c.source("buildin.phong.vertex"),c.source("buildin.phong.fragment")),b.template("buildin.wireframe",c.source("buildin.wireframe.vertex"),c.source("buildin.wireframe.fragment")),b.template("buildin.skybox",c.source("buildin.skybox.vertex"),c.source("buildin.skybox.fragment")),b.template("buildin.prez",c.source("buildin.prez.vertex"),c.source("buildin.prez.fragment")),b.template("buildin.standard",c.source("buildin.standard.vertex"),c.source("buildin.standard.fragment")),b.template("buildin.physical",c.source("buildin.physical.vertex"),c.source("buildin.physical.fragment")),c["import"](a("./source/compositor/coloradjust.essl")),c["import"](a("./source/compositor/blur.essl")),c["import"](a("./source/compositor/lum.essl")),c["import"](a("./source/compositor/lut.essl")),c["import"](a("./source/compositor/output.essl")),c["import"](a("./source/compositor/hdr.essl")),c["import"](a("./source/compositor/lensflare.essl")),c["import"](a("./source/compositor/blend.essl")),c["import"](a("./source/compositor/fxaa.essl"))}),define("qtek/util/dds",["require","../Texture","../Texture2D","../TextureCube"],function(a){function b(a){return a.charCodeAt(0)+(a.charCodeAt(1)<<8)+(a.charCodeAt(2)<<16)+(a.charCodeAt(3)<<24)}var c=a("../Texture"),d=a("../Texture2D");a("../TextureCube");var e=542327876,f=131072,g=512,h=4,i=31,j=b("DXT1"),k=b("DXT3"),l=b("DXT5"),m=0,n=1,o=2,p=3,q=4,r=7,s=20,t=21,u=28,v={parse:function(a,b){var v=new Int32Array(a,0,i);if(v[m]!==e)return null;if(!v(s)&h)return null;var w,x,y=v(t),z=v[q],A=v[p],B=v[u]&g,C=v[o]&f;switch(y){case j:w=8,x=c.COMPRESSED_RGB_S3TC_DXT1_EXT;break;case k:w=16,x=c.COMPRESSED_RGBA_S3TC_DXT3_EXT;break;case l:w=16,x=c.COMPRESSED_RGBA_S3TC_DXT5_EXT;break;default:return null}var D=v[n]+4,E=B?6:1,F=1;C&&(F=Math.max(1,v[r]));for(var G=[],H=0;E>H;H++){var I=z,J=A;G[H]=new d({width:I,height:J,format:x});for(var K=[],L=0;F>L;L++){var M=Math.max(4,I)/4*Math.max(4,J)/4*w,N=new Uint8Array(a,D,M);D+=M,I*=.5,J*=.5,K[L]=N}G[H].pixels=K[0],C&&(G[H].mipmaps=K)}return b?(b.width=G[0].width,b.height=G[0].height,b.format=G[0].format,b.pixels=G[0].pixels,b.mipmaps=G[0].mipmaps,void 0):G[0]}};return v}),define("qtek/util/hdr",["require","../Texture","../Texture2D"],function(a){function b(a,b,c,d){if(a[3]>0){var e=Math.pow(2,a[3]-128-8+d);b[c+0]=a[0]*e,b[c+1]=a[1]*e,b[c+2]=a[2]*e}else b[c+0]=0,b[c+1]=0,b[c+2]=0;return b[c+3]=1,b}function c(a,b,c){for(var d="",e=b;c>e;e++)d+=i(a[e]);return d}function d(a,b){b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3]}function e(a,b,c,e){for(var f=0,g=0,h=e;h>0;)if(a[g][0]=b[c++],a[g][1]=b[c++],a[g][2]=b[c++],a[g][3]=b[c++],1===a[g][0]&&1===a[g][1]&&1===a[g][2]){for(var i=a[g][3]<>>0;i>0;i--)d(a[g-1],a[g]),g++,h--;f+=8}else g++,h--,f=0;return c}function f(a,b,c,d){if(j>d|d>k)return e(a,b,c,d);var f=b[c++];if(2!=f)return e(a,b,c-1,d);if(a[0][1]=b[c++],a[0][2]=b[c++],f=b[c++],(a[0][2]<<8>>>0|f)>>>0!==d)return null;for(var f=0;4>f;f++)for(var g=0;d>g;){var h=b[c++];if(h>128){h=(127&h)>>>0;for(var i=b[c++];h--;)a[g++][f]=i}else for(;h--;)a[g++][f]=b[c++]}return c}var g=a("../Texture"),h=a("../Texture2D"),i=String.fromCharCode,j=8,k=32767,l={parseRGBE:function(a,d,e){void 0===e&&(e=0);var j=new Uint8Array(a),k=j.length;if("#?"===c(j,0,2)){for(var l=2;k>l&&("\n"!==i(j[l])||"\n"!==i(j[l+1]));l++);if(!(l>=k)){l+=2;for(var m="";k>l;l++){var n=i(j[l]);if("\n"===n)break;m+=n}var o=m.split(" "),p=parseInt(o[1]),q=parseInt(o[3]);if(q&&p){for(var r=l+1,s=[],t=0;q>t;t++){s[t]=[];for(var u=0;4>u;u++)s[t][u]=0}for(var v=new Float32Array(4*q*p),w=0,x=0;p>x;x++){var r=f(s,j,r,q);if(!r)return null;for(var t=0;q>t;t++)b(s[t],v,w,e),w+=4}return d||(d=new h),d.width=q,d.height=p,d.pixels=v,d.type=g.FLOAT,d}}}},parseRGBEFromPNG:function(){}};return l}),define("qtek/util/mesh",["require","../Geometry","../DynamicGeometry","../StaticGeometry","../Mesh","../Node","../Material","../Shader","../math/BoundingBox","../dep/glmatrix"],function(a){a("../Geometry");var b=a("../DynamicGeometry"),c=a("../StaticGeometry"),d=a("../Mesh"),e=a("../Node"),f=a("../Material");a("../Shader");var g=a("../math/BoundingBox"),h=a("../dep/glmatrix"),i=h.mat4,j=h.vec3,k=Array.prototype.slice,l={merge:function(a,e){if(a.length){var f=a[0],h=f.geometry,k=f.material,l=h instanceof c,m=l?new c:new b;m.boundingBox=new g;var n=m.faces,o=h.getEnabledAttributes();l||(o=Object.keys(o));for(var p=0;p=65535?new Uint32Array(3*u):new Uint16Array(3*u)}for(var z=0,A=0,B=h.isUseFace(),C=0;Cp;p++)H.value[K+p]=G.value[p];"position"===q?j.forEach(H.value,J,K,L,j.transformMat4,E):("normal"===q||"tangent"===q)&&j.forEach(H.value,J,K,L,j.transformMat4,s)}else for(var p=0;t>p;p++)if("position"===q){var M=j.create();j.transformMat4(M,G.value[p],E),H.value.push(M)}else if("normal"===q||"tangent"===q){var M=j.create();j.transformMat4(M,G.value[p],s),H.value.push(M)}else H.value.push(G.value[p])}if(B){var I=w.faces.length;if(l){for(var p=0;I>p;p++)m.faces[p+A]=w.faces[p]+z;A+=I}else for(var p=0;I>p;p++){var N=[],O=w.faces[p];N[0]=O[0]+z,N[1]=O[1]+z,N[2]=O[2]+z,n.push(N)}}z+=t}return new d({material:k,geometry:m})}},splitByJoints:function(a,g,h){var i=a.geometry,j=a.skeleton,l=a.material,m=l.shader,n=a.joints;if(i&&j&&n.length){if(n.lengthv;v++)t[v]=!1;for(var w=[],x=[],y=function(a){return n[a]};s>0;){for(var z=[],A=[],B=[],C=0,v=0;vD;D++)if(!t[D]){for(var E=!0,F=0,v=0;3>v;v++)for(var G=o?q[3*D+v]:q[D][v],H=0;4>H;H++){var I;I=o?u[4*G+H]:u[G][H],I>=0&&-1===A[I]&&(g>C?(A[I]=C,B[C++]=I,w[F++]=I):E=!1)}if(E)o?z.push(q.subarray(3*D,3*(D+1))):z.push(q[D]),t[D]=!0,s--;else for(var v=0;F>v;v++)A[w[v]]=-1,B.pop(),C--}x.push({faces:z,joints:B.map(y),jointReverseMap:A})}var J=new e({name:a.name}),K=i.getEnabledAttributes();o||(K=Object.keys(K)),K.splice(K.indexOf("joint"),1);for(var L=[],M=0;Mv;v++)L[v]=-1;for(var D=0;Dv;v++){var G=X[v];-1===L[G]&&(L[G]=V,V++)}if(o){for(var Y=0;Y65535?new Uint32Array(3*N.faces.length):new Uint16Array(3*N.faces.length)}var _=0;V=0;for(var v=0;W>v;v++)L[v]=-1;for(var D=0;Dv;v++){var G=X[v];if(-1===L[G]){L[G]=V;for(var Y=0;YH;H++)$.value[V*cb+H]=bb.value[G*cb+H];else $.value[V]=1===bb.size?bb.value[G]:k.call(bb.value[G])}if(o)for(var H=0;4>H;H++){var I=i.attributes.joint.value[4*G+H],db=4*V+H;T.attributes.joint.value[db]=I>=0?O[I]:-1}else for(var eb=T.attributes.joint.value[V]=[-1,-1,-1,-1],H=0;4>H;H++){var I=i.attributes.joint.value[G][H];I>=0&&(eb[H]=O[I])}V++}o?T.faces[_++]=L[G]:ab.push(L[G])}o||T.faces.push(ab)}J.add(U)}for(var fb=a.children(),v=0;vi;i++)for(var j=0;f>j;j++){var k=j%2?i%2:i%2-1;k&&h.fillRect(i*c,j*c,c,c)}var l=new b({image:g,anisotropic:8});return l},createBlank:function(a){var c=document.createElement("canvas");c.width=1,c.height=1;var d=c.getContext("2d");d.fillStyle=a,d.fillRect(0,0,1,1);var e=new b({image:c});return e}};return j}); \ No newline at end of file diff --git a/dist/qtek.js b/dist/qtek.js index 5fb95fc8a..9d33c76f5 100644 --- a/dist/qtek.js +++ b/dist/qtek.js @@ -1,29877 +1,34371 @@ - (function(factory){ - // AMD - if( typeof define !== "undefined" && define["amd"] ){ - define( ["exports"], factory.bind(window) ); - // No module loader - }else{ - factory( window["qtek"] = {} ); - } - -})(function(_exports){ - -/** - * almond 0.2.5 Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/almond for details - */ -//Going sloppy to avoid 'use strict' string cost, but strict practices should -//be followed. -/*jslint sloppy: true */ -/*global setTimeout: false */ - -var requirejs, require, define; -(function (undef) { - var main, req, makeMap, handlers, - defined = {}, - waiting = {}, - config = {}, - defining = {}, - hasOwn = Object.prototype.hasOwnProperty, - aps = [].slice; - - function hasProp(obj, prop) { - return hasOwn.call(obj, prop); - } - - /** - * Given a relative module name, like ./something, normalize it to - * a real name that can be mapped to a path. - * @param {String} name the relative name - * @param {String} baseName a real name that the name arg is relative - * to. - * @returns {String} normalized name - */ - function normalize(name, baseName) { - var nameParts, nameSegment, mapValue, foundMap, - foundI, foundStarMap, starI, i, j, part, - baseParts = baseName && baseName.split("/"), - map = config.map, - starMap = (map && map['*']) || {}; - - //Adjust any relative paths. - if (name && name.charAt(0) === ".") { - //If have a base name, try to normalize against it, - //otherwise, assume it is a top-level require that will - //be relative to baseUrl in the end. - if (baseName) { - //Convert baseName to array, and lop off the last part, - //so that . matches that "directory" and not name of the baseName's - //module. For instance, baseName of "one/two/three", maps to - //"one/two/three.js", but we want the directory, "one/two" for - //this normalization. - baseParts = baseParts.slice(0, baseParts.length - 1); - - name = baseParts.concat(name.split("/")); - - //start trimDots - for (i = 0; i < name.length; i += 1) { - part = name[i]; - if (part === ".") { - name.splice(i, 1); - i -= 1; - } else if (part === "..") { - if (i === 1 && (name[2] === '..' || name[0] === '..')) { - //End of the line. Keep at least one non-dot - //path segment at the front so it can be mapped - //correctly to disk. Otherwise, there is likely - //no path mapping for a path starting with '..'. - //This can still fail, but catches the most reasonable - //uses of .. - break; - } else if (i > 0) { - name.splice(i - 1, 2); - i -= 2; - } - } - } - //end trimDots - - name = name.join("/"); - } else if (name.indexOf('./') === 0) { - // No baseName, so this is ID is resolved relative - // to baseUrl, pull off the leading dot. - name = name.substring(2); - } - } - - //Apply map config if available. - if ((baseParts || starMap) && map) { - nameParts = name.split('/'); - - for (i = nameParts.length; i > 0; i -= 1) { - nameSegment = nameParts.slice(0, i).join("/"); - - if (baseParts) { - //Find the longest baseName segment match in the config. - //So, do joins on the biggest to smallest lengths of baseParts. - for (j = baseParts.length; j > 0; j -= 1) { - mapValue = map[baseParts.slice(0, j).join('/')]; - - //baseName segment has config, find if it has one for - //this name. - if (mapValue) { - mapValue = mapValue[nameSegment]; - if (mapValue) { - //Match, update name to the new value. - foundMap = mapValue; - foundI = i; - break; - } - } - } - } - - if (foundMap) { - break; - } - - //Check for a star map match, but just hold on to it, - //if there is a shorter segment match later in a matching - //config, then favor over this star map. - if (!foundStarMap && starMap && starMap[nameSegment]) { - foundStarMap = starMap[nameSegment]; - starI = i; - } - } - - if (!foundMap && foundStarMap) { - foundMap = foundStarMap; - foundI = starI; - } - - if (foundMap) { - nameParts.splice(0, foundI, foundMap); - name = nameParts.join('/'); - } - } - - return name; - } - - function makeRequire(relName, forceSync) { - return function () { - //A version of a require function that passes a moduleName - //value for items that may need to - //look up paths relative to the moduleName - return req.apply(undef, aps.call(arguments, 0).concat([relName, forceSync])); - }; - } - - function makeNormalize(relName) { - return function (name) { - return normalize(name, relName); - }; - } - - function makeLoad(depName) { - return function (value) { - defined[depName] = value; - }; - } - - function callDep(name) { - if (hasProp(waiting, name)) { - var args = waiting[name]; - delete waiting[name]; - defining[name] = true; - main.apply(undef, args); - } - - if (!hasProp(defined, name) && !hasProp(defining, name)) { - throw new Error('No ' + name); - } - return defined[name]; - } - - //Turns a plugin!resource to [plugin, resource] - //with the plugin being undefined if the name - //did not have a plugin prefix. - function splitPrefix(name) { - var prefix, - index = name ? name.indexOf('!') : -1; - if (index > -1) { - prefix = name.substring(0, index); - name = name.substring(index + 1, name.length); - } - return [prefix, name]; - } - - /** - * Makes a name map, normalizing the name, and using a plugin - * for normalization if necessary. Grabs a ref to plugin - * too, as an optimization. - */ - makeMap = function (name, relName) { - var plugin, - parts = splitPrefix(name), - prefix = parts[0]; - - name = parts[1]; - - if (prefix) { - prefix = normalize(prefix, relName); - plugin = callDep(prefix); - } - - //Normalize according - if (prefix) { - if (plugin && plugin.normalize) { - name = plugin.normalize(name, makeNormalize(relName)); - } else { - name = normalize(name, relName); - } - } else { - name = normalize(name, relName); - parts = splitPrefix(name); - prefix = parts[0]; - name = parts[1]; - if (prefix) { - plugin = callDep(prefix); - } - } - - //Using ridiculous property names for space reasons - return { - f: prefix ? prefix + '!' + name : name, //fullName - n: name, - pr: prefix, - p: plugin - }; - }; - - function makeConfig(name) { - return function () { - return (config && config.config && config.config[name]) || {}; - }; - } - - handlers = { - require: function (name) { - return makeRequire(name); - }, - exports: function (name) { - var e = defined[name]; - if (typeof e !== 'undefined') { - return e; - } else { - return (defined[name] = {}); - } - }, - module: function (name) { - return { - id: name, - uri: '', - exports: defined[name], - config: makeConfig(name) - }; - } - }; - - main = function (name, deps, callback, relName) { - var cjsModule, depName, ret, map, i, - args = [], - usingExports; - - //Use name if no relName - relName = relName || name; - - //Call the callback to define the module, if necessary. - if (typeof callback === 'function') { - - //Pull out the defined dependencies and pass the ordered - //values to the callback. - //Default to [require, exports, module] if no deps - deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps; - for (i = 0; i < deps.length; i += 1) { - map = makeMap(deps[i], relName); - depName = map.f; - - //Fast path CommonJS standard dependencies. - if (depName === "require") { - args[i] = handlers.require(name); - } else if (depName === "exports") { - //CommonJS module spec 1.1 - args[i] = handlers.exports(name); - usingExports = true; - } else if (depName === "module") { - //CommonJS module spec 1.1 - cjsModule = args[i] = handlers.module(name); - } else if (hasProp(defined, depName) || - hasProp(waiting, depName) || - hasProp(defining, depName)) { - args[i] = callDep(depName); - } else if (map.p) { - map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {}); - args[i] = defined[depName]; - } else { - throw new Error(name + ' missing ' + depName); - } - } - - ret = callback.apply(defined[name], args); - - if (name) { - //If setting exports via "module" is in play, - //favor that over return value and exports. After that, - //favor a non-undefined return value over exports use. - if (cjsModule && cjsModule.exports !== undef && - cjsModule.exports !== defined[name]) { - defined[name] = cjsModule.exports; - } else if (ret !== undef || !usingExports) { - //Use the return value from the function. - defined[name] = ret; - } - } - } else if (name) { - //May just be an object definition for the module. Only - //worry about defining if have a module name. - defined[name] = callback; - } - }; - - requirejs = require = req = function (deps, callback, relName, forceSync, alt) { - if (typeof deps === "string") { - if (handlers[deps]) { - //callback in this case is really relName - return handlers[deps](callback); - } - //Just return the module wanted. In this scenario, the - //deps arg is the module name, and second arg (if passed) - //is just the relName. - //Normalize module name, if it contains . or .. - return callDep(makeMap(deps, callback).f); - } else if (!deps.splice) { - //deps is a config object, not an array. - config = deps; - if (callback.splice) { - //callback is an array, which means it is a dependency list. - //Adjust args if there are dependencies - deps = callback; - callback = relName; - relName = null; - } else { - deps = undef; - } - } - - //Support require(['a']) - callback = callback || function () {}; - - //If relName is a function, it is an errback handler, - //so remove it. - if (typeof relName === 'function') { - relName = forceSync; - forceSync = alt; - } - - //Simulate async callback; - if (forceSync) { - main(undef, deps, callback, relName); - } else { - //Using a non-zero value because of concern for what old browsers - //do, and latest browsers "upgrade" to 4 if lower value is used: - //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout: - //If want a value immediately, use require('id') instead -- something - //that works in almond on the global level, but not guaranteed and - //unlikely to work in other AMD implementations. - setTimeout(function () { - main(undef, deps, callback, relName); - }, 4); - } - - return req; - }; - - /** - * Just drops the config on the floor, but returns req in case - * the config return value is used. - */ - req.config = function (cfg) { - config = cfg; - if (config.deps) { - req(config.deps, config.callback); - } - return req; - }; - - define = function (name, deps, callback) { - - //This module may not have dependencies - if (!deps.splice) { - //deps is not an array, so probably means - //an object literal or factory function for - //the value. Adjust args. - callback = deps; - deps = []; - } - - if (!hasProp(defined, name) && !hasProp(waiting, name)) { - waiting[name] = [name, deps, callback]; - } - }; - - define.amd = { - jQuery: true - }; -}()); - -define('qtek/core/mixin/derive',['require'],function(require) { - - - - /** - * Extend a sub class from base class - * @param {object|Function} makeDefaultOpt default option of this sub class, method of the sub can use this.xxx to access this option - * @param {Function} [initialize] Initialize after the sub class is instantiated - * @param {Object} [proto] Prototype methods/properties of the sub class - * @memberOf qtek.core.mixin.derive. - * @return {Function} - */ - function derive(makeDefaultOpt, initialize/*optional*/, proto/*optional*/) { - - if (typeof initialize == 'object') { - proto = initialize; - initialize = null; - } - - var _super = this; - - var propList; - if (!(makeDefaultOpt instanceof Function)) { - // Optimize the property iterate if it have been fixed - propList = []; - for (var propName in makeDefaultOpt) { - if (makeDefaultOpt.hasOwnProperty(propName)) { - propList.push(propName); - } - } - } - - var sub = function(options) { - - // call super constructor - _super.apply(this, arguments); - - if (makeDefaultOpt instanceof Function) { - // Invoke makeDefaultOpt each time if it is a function, So we can make sure each - // property in the object will not be shared by mutiple instances - extend(this, makeDefaultOpt.call(this)); - } else { - extendWithPropList(this, makeDefaultOpt, propList); - } - - if (this.constructor === sub) { - // Initialize function will be called in the order of inherit - var initializers = sub.__initializers__; - for (var i = 0; i < initializers.length; i++) { - initializers[i].apply(this, arguments); - } - } - }; - // save super constructor - sub.__super__ = _super; - // Initialize function will be called after all the super constructor is called - if (!_super.__initializers__) { - sub.__initializers__ = []; - } else { - sub.__initializers__ = _super.__initializers__.slice(); - } - if (initialize) { - sub.__initializers__.push(initialize); - } - - var Ctor = function() {}; - Ctor.prototype = _super.prototype; - sub.prototype = new Ctor(); - sub.prototype.constructor = sub; - extend(sub.prototype, proto); - - // extend the derive method as a static method; - sub.derive = _super.derive; - - return sub; - } - - function extend(target, source) { - if (!source) { - return; - } - for (var name in source) { - if (source.hasOwnProperty(name)) { - target[name] = source[name]; - } - } - } - - function extendWithPropList(target, source, propList) { - for (var i = 0; i < propList.length; i++) { - var propName = propList[i]; - target[propName] = source[propName]; - } - } - - /** - * @alias qtek.core.mixin.derive - * @mixin - */ - return { - derive : derive - }; -}); -define('qtek/core/mixin/notifier',[],function() { - - function Handler(action, context) { - this.action = action; - this.context = context; - } - /** - * @mixin - * @alias qtek.core.mixin.notifier - */ - var notifier = { - /** - * Trigger event - * @param {string} name - */ - trigger: function(name) { - if (!this.hasOwnProperty('__handlers__')) { - return; - } - if (!this.__handlers__.hasOwnProperty(name)) { - return; - } - - var hdls = this.__handlers__[name]; - var l = hdls.length, i = -1, args = arguments; - // Optimize advise from backbone - switch (args.length) { - case 1: - while (++i < l) { - hdls[i].action.call(hdls[i].context); - } - return; - case 2: - while (++i < l) { - hdls[i].action.call(hdls[i].context, args[1]); - } - return; - case 3: - while (++i < l) { - hdls[i].action.call(hdls[i].context, args[1], args[2]); - } - return; - case 4: - while (++i < l) { - hdls[i].action.call(hdls[i].context, args[1], args[2], args[3]); - } - return; - case 5: - while (++i < l) { - hdls[i].action.call(hdls[i].context, args[1], args[2], args[3], args[4]); - } - return; - default: - while (++i < l) { - hdls[i].action.apply(hdls[i].context, Array.prototype.slice.call(args, 1)); - } - return; - } - }, - /** - * Register event handler - * @param {string} name - * @param {Function} action - * @param {Object} [context] - * @chainable - */ - on: function(name, action, context) { - if (!name || !action) { - return; - } - var handlers = this.__handlers__ || (this.__handlers__={}); - if (! handlers[name]) { - handlers[name] = []; - } else { - if (this.has(name, action)) { - return; - } - } - var handler = new Handler(action, context || this); - handlers[name].push(handler); - - return this; - }, - - /** - * Register event, event will only be triggered once and then removed - * @param {string} name - * @param {Function} action - * @param {Object} [context] - * @chainable - */ - once: function(name, action, context) { - if (!name || !action) { - return; - } - var self = this; - function wrapper() { - self.off(name, wrapper); - action.apply(this, arguments); - } - return this.on(name, wrapper, context); - }, - - /** - * Alias of once('before' + name) - * @param {string} name - * @param {Function} action - * @param {Object} [context] - * @chainable - */ - before: function(name, action, context) { - if (!name || !action) { - return; - } - name = 'before' + name; - return this.on(name, action, context); - }, - - /** - * Alias of once('after' + name) - * @param {string} name - * @param {Function} action - * @param {Object} [context] - * @chainable - */ - after: function(name, action, context) { - if (!name || !action) { - return; - } - name = 'after' + name; - return this.on(name, action, context); - }, - - /** - * Alias of on('success') - * @param {Function} action - * @param {Object} [context] - * @chainable - */ - success: function(action, context) { - return this.once('success', action, context); - }, - - /** - * Alias of on('error') - * @param {Function} action - * @param {Object} [context] - * @chainable - */ - error: function(action, context) { - return this.once('error', action, context); - }, - - /** - * Alias of on('success') - * @param {Function} action - * @param {Object} [context] - * @chainable - */ - off: function(name, action) { - - var handlers = this.__handlers__ || (this.__handlers__={}); - - if (!action) { - handlers[name] = []; - return; - } - if (handlers[name]) { - var hdls = handlers[name]; - var retains = []; - for (var i = 0; i < hdls.length; i++) { - if (action && hdls[i].action !== action) { - retains.push(hdls[i]); - } - } - handlers[name] = retains; - } - - return this; - }, - - /** - * If registered the event handler - * @param {string} name - * @param {Function} action - * @return {boolean} - */ - has: function(name, action) { - var handlers = this.__handlers__; - - if (! handlers || - ! handlers[name]) { - return false; - } - var hdls = handlers[name]; - for (var i = 0; i < hdls.length; i++) { - if (hdls[i].action === action) { - return true; - } - } - } - }; - - return notifier; -}); -define('qtek/core/util',['require'],function(require){ - - - - var guid = 0; - - /** - * Util functions - * @namespace qtek.core.util - */ - var util = { - - /** - * Generate GUID - * @return {number} - * @memberOf qtek.core.util - */ - genGUID: function() { - return ++guid; +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["qtek"] = factory(); + else + root["qtek"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + module.exports = __webpack_require__(1); + +/***/ }, +/* 1 */ +/***/ function(module, exports, __webpack_require__) { + + /** @namespace qtek */ + /** @namespace qtek.math */ + /** @namespace qtek.animation */ + /** @namespace qtek.async */ + /** @namespace qtek.camera */ + /** @namespace qtek.compositor */ + /** @namespace qtek.core */ + /** @namespace qtek.geometry */ + /** @namespace qtek.helper */ + /** @namespace qtek.light */ + /** @namespace qtek.loader */ + /** @namespace qtek.particleSystem */ + /** @namespace qtek.plugin */ + /** @namespace qtek.prePass */ + /** @namespace qtek.shader */ + /** @namespace qtek.texture */ + /** @namespace qtek.util */ + + + var exportsObject = { + "animation": { + "Animation": __webpack_require__(2), + "Animator": __webpack_require__(7), + "Blend1DClip": __webpack_require__(10), + "Blend2DClip": __webpack_require__(11), + "Clip": __webpack_require__(8), + "easing": __webpack_require__(9), + "SamplerClip": __webpack_require__(15), + "SkinningClip": __webpack_require__(17), + "TransformClip": __webpack_require__(16) + }, + "async": { + "Task": __webpack_require__(18), + "TaskGroup": __webpack_require__(20) + }, + "Camera": __webpack_require__(21), + "camera": { + "Orthographic": __webpack_require__(30), + "Perspective": __webpack_require__(31) + }, + "canvas": { + "Material": __webpack_require__(32), + "Renderer": __webpack_require__(33) + }, + "compositor": { + "Compositor": __webpack_require__(35), + "FilterNode": __webpack_require__(46), + "Graph": __webpack_require__(36), + "Node": __webpack_require__(37), + "Pass": __webpack_require__(47), + "SceneNode": __webpack_require__(57), + "TextureNode": __webpack_require__(58), + "TexturePool": __webpack_require__(38) + }, + "core": { + "Base": __webpack_require__(3), + "Cache": __webpack_require__(41), + "Event": __webpack_require__(59), + "glenum": __webpack_require__(34), + "glinfo": __webpack_require__(42), + "LinkedList": __webpack_require__(60), + "LRU": __webpack_require__(61), + "mixin": { + "extend": __webpack_require__(4), + "notifier": __webpack_require__(5) + }, + "request": __webpack_require__(19), + "util": __webpack_require__(6), + "vendor": __webpack_require__(51) + }, + "deferred": { + "GBuffer": __webpack_require__(62), + "Renderer": __webpack_require__(69) + }, + "dep": { + "glmatrix": __webpack_require__(14) + }, + "FrameBuffer": __webpack_require__(44), + "Geometry": __webpack_require__(50), + "geometry": { + "Cone": __webpack_require__(71), + "Cube": __webpack_require__(84), + "Cylinder": __webpack_require__(72), + "Plane": __webpack_require__(48), + "Sphere": __webpack_require__(70) + }, + "Joint": __webpack_require__(85), + "Light": __webpack_require__(86), + "light": { + "Ambient": __webpack_require__(87), + "AmbientCubemap": __webpack_require__(88), + "AmbientSH": __webpack_require__(101), + "Directional": __webpack_require__(102), + "Point": __webpack_require__(103), + "Sphere": __webpack_require__(104), + "Spot": __webpack_require__(105), + "Tube": __webpack_require__(106) + }, + "loader": { + "FX": __webpack_require__(107), + "GLTF": __webpack_require__(108) + }, + "Material": __webpack_require__(53), + "math": { + "BoundingBox": __webpack_require__(26), + "Frustum": __webpack_require__(27), + "Matrix2": __webpack_require__(130), + "Matrix2d": __webpack_require__(131), + "Matrix3": __webpack_require__(132), + "Matrix4": __webpack_require__(25), + "Plane": __webpack_require__(28), + "Quaternion": __webpack_require__(24), + "Ray": __webpack_require__(29), + "util": __webpack_require__(43), + "Value": __webpack_require__(133), + "Vector2": __webpack_require__(13), + "Vector3": __webpack_require__(23), + "Vector4": __webpack_require__(134) + }, + "Mesh": __webpack_require__(54), + "Node": __webpack_require__(22), + "particle": { + "Emitter": __webpack_require__(135), + "Field": __webpack_require__(137), + "ForceField": __webpack_require__(138), + "Particle": __webpack_require__(136), + "ParticleRenderable": __webpack_require__(139) + }, + "picking": { + "PixelPicking": __webpack_require__(141), + "RayPicking": __webpack_require__(143) + }, + "plugin": { + "FirstPersonControl": __webpack_require__(144), + "InfinitePlane": __webpack_require__(145), + "OrbitControl": __webpack_require__(146), + "Skybox": __webpack_require__(90), + "Skydome": __webpack_require__(95) }, - /** - * Relative path to absolute path - * @param {string} path - * @param {string} basePath - * @return {string} - * @memberOf qtek.core.util - */ - relative2absolute: function(path, basePath) { - if (!basePath || path.match(/^\//)) { - return path; - } - var pathParts = path.split('/'); - var basePathParts = basePath.split('/'); - - var item = pathParts[0]; - while(item === '.' || item === '..') { - if (item === '..') { - basePathParts.pop(); - } - pathParts.shift(); - item = pathParts[0]; - } - return basePathParts.join('/') + '/' + pathParts.join('/'); - }, - - /** - * Extend target with source - * @param {Object} target - * @param {Object} source - * @return {Object} - * @memberOf qtek.core.util - */ - extend: function(target, source) { - if (source) { - for (var name in source) { - if (source.hasOwnProperty(name)) { - target[name] = source[name]; - } - } - } - return target; - }, - - /** - * Extend properties to target if not exist. - * @param {Object} target - * @param {Object} source - * @return {Object} - * @memberOf qtek.core.util - */ - defaults: function(target, source) { - if (source) { - for (var propName in source) { - if (target[propName] === undefined) { - target[propName] = source[propName]; - } - } - } - return target; - }, - /** - * Extend properties with a given property list to avoid for..in.. iteration. - * @param {Object} target - * @param {Object} source - * @param {Array.} propList - * @return {Object} - * @memberOf qtek.core.util - */ - extendWithPropList: function(target, source, propList) { - if (source) { - for (var i = 0; i < propList.length; i++) { - var propName = propList[i]; - target[propName] = source[propName]; - } - } - return target; - }, - /** - * Extend properties to target if not exist. With a given property list avoid for..in.. iteration. - * @param {Object} target - * @param {Object} source - * @param {Array.} propList - * @return {Object} - * @memberOf qtek.core.util - */ - defaultsWithPropList: function(target, source, propList) { - if (source) { - for (var i = 0; i < propList.length; i++) { - var propName = propList[i]; - if (target[propName] === undefined) { - target[propName] = source[propName]; - } - } - } - return target; - }, - /** - * @param {Object|Array} obj - * @param {Function} iterator - * @param {Object} [context] - * @memberOf qtek.core.util - */ - each: function(obj, iterator, context) { - if (!(obj && iterator)) { - return; - } - if (obj.forEach) { - obj.forEach(iterator, context); - } else if (obj.length === + obj.length) { - for (var i = 0, len = obj.length; i < len; i++) { - iterator.call(context, obj[i], i, obj); - } - } else { - for (var key in obj) { - if (obj.hasOwnProperty(key)) { - iterator.call(context, obj[key], key, obj); - } - } - } - }, - - /** - * Is object ? - * @param {} obj - * @return {boolean} - * @memberOf qtek.core.util - */ - isObject: function(obj) { - return obj === Object(obj); - }, - - /** - * Is array ? - * @param {} obj - * @return {boolean} - * @memberOf qtek.core.util - */ - isArray: function(obj) { - return obj instanceof Array; - }, - - /** - * Is array like, which have a length property - * @param {} obj - * @return {boolean} - * @memberOf qtek.core.util - */ - isArrayLike: function(obj) { - if (!obj) { - return false; - } else { - return obj.length === + obj.length; - } - }, - - /** - * @param {} obj - * @return {} - * @memberOf qtek.core.util - */ - clone: function(obj) { - if (!util.isObject(obj)) { - return obj; - } else if (util.isArray(obj)) { - return obj.slice(); - } else if (util.isArrayLike(obj)) { // is typed array - var ret = new obj.constructor(obj.length); - for (var i = 0; i < obj.length; i++) { - ret[i] = obj[i]; - } - return ret; - } else { - return util.extend({}, obj); - } - } + "prePass": { + "EnvironmentMap": __webpack_require__(93), + "Reflection": __webpack_require__(147), + "ShadowMap": __webpack_require__(148) + }, + "Renderable": __webpack_require__(55), + "Renderer": __webpack_require__(63), + "Scene": __webpack_require__(92), + "Shader": __webpack_require__(52), + "shader": { + "builtin": __webpack_require__(112), + "library": __webpack_require__(64), + "source": { + "header": { + "light": __webpack_require__(65) + } + } + }, + "Skeleton": __webpack_require__(111), + "StandardMaterial": __webpack_require__(109), + "StaticGeometry": __webpack_require__(49), + "Texture": __webpack_require__(40), + "Texture2D": __webpack_require__(39), + "TextureCube": __webpack_require__(45), + "util": { + "cubemap": __webpack_require__(89), + "dds": __webpack_require__(97), + "delaunay": __webpack_require__(12), + "earClipping": __webpack_require__(150), + "hdr": __webpack_require__(98), + "mesh": __webpack_require__(151), + "sh": __webpack_require__(152), + "texture": __webpack_require__(94) + }, + "version": __webpack_require__(154), + "vr": { + "CardboardDistorter": __webpack_require__(155), + "StereoCamera": __webpack_require__(157) + } }; - return util; -}); -define('qtek/core/Base',['require','./mixin/derive','./mixin/notifier','./util'],function(require){ - - - - var deriveMixin = require('./mixin/derive'); - var notifierMixin = require('./mixin/notifier'); - var util = require('./util'); - - /** - * Base class of all objects - * @constructor - * @alias qtek.core.Base - * @mixes qtek.core.mixin.notifier - */ - var Base = function() { - /** - * @type {number} - */ - this.__GUID__ = util.genGUID(); - }; - - Base.__initializers__ = [ - function(opts) { - util.extend(this, opts); - } - ]; - - util.extend(Base, deriveMixin); - util.extend(Base.prototype, notifierMixin); - - return Base; -}); -/** - * @fileoverview gl-matrix - High performance matrix and vector operations - * @author Brandon Jones - * @author Colin MacKenzie IV - * @version 2.2.0 - */ - -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - - -(function(_global) { - - - var shim = {}; - if (typeof(exports) === 'undefined') { - if(typeof define == 'function' && typeof define.amd == 'object' && define.amd) { - shim.exports = {}; - define('qtek/dep/glmatrix',[],function() { - return shim.exports; - }); - } else { - // gl-matrix lives in a browser, define its namespaces in global - shim.exports = typeof(window) !== 'undefined' ? window : _global; - } - } - else { - // gl-matrix lives in commonjs, define its namespaces in exports - shim.exports = exports; - } - - (function(exports) { - /* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - - -if(!GLMAT_EPSILON) { - var GLMAT_EPSILON = 0.000001; -} - -if(!GLMAT_ARRAY_TYPE) { - var GLMAT_ARRAY_TYPE = (typeof Float32Array !== 'undefined') ? Float32Array : Array; -} - -if(!GLMAT_RANDOM) { - var GLMAT_RANDOM = Math.random; -} - -/** - * @class Common utilities - * @name glMatrix - */ -var glMatrix = {}; - -/** - * Sets the type of array used when creating new vectors and matricies - * - * @param {Type} type Array type, such as Float32Array or Array - */ -glMatrix.setMatrixArrayType = function(type) { - GLMAT_ARRAY_TYPE = type; -} - -if(typeof(exports) !== 'undefined') { - exports.glMatrix = glMatrix; -} - -var degree = Math.PI / 180; - -/** -* Convert Degree To Radian -* -* @param {Number} Angle in Degrees -*/ -glMatrix.toRadian = function(a){ - return a * degree; -} -; -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -/** - * @class 2 Dimensional Vector - * @name vec2 - */ - -var vec2 = {}; - -/** - * Creates a new, empty vec2 - * - * @returns {vec2} a new 2D vector - */ -vec2.create = function() { - var out = new GLMAT_ARRAY_TYPE(2); - out[0] = 0; - out[1] = 0; - return out; -}; - -/** - * Creates a new vec2 initialized with values from an existing vector - * - * @param {vec2} a vector to clone - * @returns {vec2} a new 2D vector - */ -vec2.clone = function(a) { - var out = new GLMAT_ARRAY_TYPE(2); - out[0] = a[0]; - out[1] = a[1]; - return out; -}; - -/** - * Creates a new vec2 initialized with the given values - * - * @param {Number} x X component - * @param {Number} y Y component - * @returns {vec2} a new 2D vector - */ -vec2.fromValues = function(x, y) { - var out = new GLMAT_ARRAY_TYPE(2); - out[0] = x; - out[1] = y; - return out; -}; - -/** - * Copy the values from one vec2 to another - * - * @param {vec2} out the receiving vector - * @param {vec2} a the source vector - * @returns {vec2} out - */ -vec2.copy = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - return out; -}; - -/** - * Set the components of a vec2 to the given values - * - * @param {vec2} out the receiving vector - * @param {Number} x X component - * @param {Number} y Y component - * @returns {vec2} out - */ -vec2.set = function(out, x, y) { - out[0] = x; - out[1] = y; - return out; -}; - -/** - * Adds two vec2's - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {vec2} out - */ -vec2.add = function(out, a, b) { - out[0] = a[0] + b[0]; - out[1] = a[1] + b[1]; - return out; -}; - -/** - * Subtracts vector b from vector a - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {vec2} out - */ -vec2.subtract = function(out, a, b) { - out[0] = a[0] - b[0]; - out[1] = a[1] - b[1]; - return out; -}; - -/** - * Alias for {@link vec2.subtract} - * @function - */ -vec2.sub = vec2.subtract; - -/** - * Multiplies two vec2's - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {vec2} out - */ -vec2.multiply = function(out, a, b) { - out[0] = a[0] * b[0]; - out[1] = a[1] * b[1]; - return out; -}; - -/** - * Alias for {@link vec2.multiply} - * @function - */ -vec2.mul = vec2.multiply; - -/** - * Divides two vec2's - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {vec2} out - */ -vec2.divide = function(out, a, b) { - out[0] = a[0] / b[0]; - out[1] = a[1] / b[1]; - return out; -}; - -/** - * Alias for {@link vec2.divide} - * @function - */ -vec2.div = vec2.divide; - -/** - * Returns the minimum of two vec2's - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {vec2} out - */ -vec2.min = function(out, a, b) { - out[0] = Math.min(a[0], b[0]); - out[1] = Math.min(a[1], b[1]); - return out; -}; - -/** - * Returns the maximum of two vec2's - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {vec2} out - */ -vec2.max = function(out, a, b) { - out[0] = Math.max(a[0], b[0]); - out[1] = Math.max(a[1], b[1]); - return out; -}; - -/** - * Scales a vec2 by a scalar number - * - * @param {vec2} out the receiving vector - * @param {vec2} a the vector to scale - * @param {Number} b amount to scale the vector by - * @returns {vec2} out - */ -vec2.scale = function(out, a, b) { - out[0] = a[0] * b; - out[1] = a[1] * b; - return out; -}; - -/** - * Adds two vec2's after scaling the second operand by a scalar value - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @param {Number} scale the amount to scale b by before adding - * @returns {vec2} out - */ -vec2.scaleAndAdd = function(out, a, b, scale) { - out[0] = a[0] + (b[0] * scale); - out[1] = a[1] + (b[1] * scale); - return out; -}; - -/** - * Calculates the euclidian distance between two vec2's - * - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {Number} distance between a and b - */ -vec2.distance = function(a, b) { - var x = b[0] - a[0], - y = b[1] - a[1]; - return Math.sqrt(x*x + y*y); -}; - -/** - * Alias for {@link vec2.distance} - * @function - */ -vec2.dist = vec2.distance; - -/** - * Calculates the squared euclidian distance between two vec2's - * - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {Number} squared distance between a and b - */ -vec2.squaredDistance = function(a, b) { - var x = b[0] - a[0], - y = b[1] - a[1]; - return x*x + y*y; -}; - -/** - * Alias for {@link vec2.squaredDistance} - * @function - */ -vec2.sqrDist = vec2.squaredDistance; - -/** - * Calculates the length of a vec2 - * - * @param {vec2} a vector to calculate length of - * @returns {Number} length of a - */ -vec2.length = function (a) { - var x = a[0], - y = a[1]; - return Math.sqrt(x*x + y*y); -}; - -/** - * Alias for {@link vec2.length} - * @function - */ -vec2.len = vec2.length; - -/** - * Calculates the squared length of a vec2 - * - * @param {vec2} a vector to calculate squared length of - * @returns {Number} squared length of a - */ -vec2.squaredLength = function (a) { - var x = a[0], - y = a[1]; - return x*x + y*y; -}; - -/** - * Alias for {@link vec2.squaredLength} - * @function - */ -vec2.sqrLen = vec2.squaredLength; - -/** - * Negates the components of a vec2 - * - * @param {vec2} out the receiving vector - * @param {vec2} a vector to negate - * @returns {vec2} out - */ -vec2.negate = function(out, a) { - out[0] = -a[0]; - out[1] = -a[1]; - return out; -}; - -/** - * Normalize a vec2 - * - * @param {vec2} out the receiving vector - * @param {vec2} a vector to normalize - * @returns {vec2} out - */ -vec2.normalize = function(out, a) { - var x = a[0], - y = a[1]; - var len = x*x + y*y; - if (len > 0) { - //TODO: evaluate use of glm_invsqrt here? - len = 1 / Math.sqrt(len); - out[0] = a[0] * len; - out[1] = a[1] * len; - } - return out; -}; - -/** - * Calculates the dot product of two vec2's - * - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {Number} dot product of a and b - */ -vec2.dot = function (a, b) { - return a[0] * b[0] + a[1] * b[1]; -}; - -/** - * Computes the cross product of two vec2's - * Note that the cross product must by definition produce a 3D vector - * - * @param {vec3} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {vec3} out - */ -vec2.cross = function(out, a, b) { - var z = a[0] * b[1] - a[1] * b[0]; - out[0] = out[1] = 0; - out[2] = z; - return out; -}; - -/** - * Performs a linear interpolation between two vec2's - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @param {Number} t interpolation amount between the two inputs - * @returns {vec2} out - */ -vec2.lerp = function (out, a, b, t) { - var ax = a[0], - ay = a[1]; - out[0] = ax + t * (b[0] - ax); - out[1] = ay + t * (b[1] - ay); - return out; -}; - -/** - * Generates a random vector with the given scale - * - * @param {vec2} out the receiving vector - * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned - * @returns {vec2} out - */ -vec2.random = function (out, scale) { - scale = scale || 1.0; - var r = GLMAT_RANDOM() * 2.0 * Math.PI; - out[0] = Math.cos(r) * scale; - out[1] = Math.sin(r) * scale; - return out; -}; - -/** - * Transforms the vec2 with a mat2 - * - * @param {vec2} out the receiving vector - * @param {vec2} a the vector to transform - * @param {mat2} m matrix to transform with - * @returns {vec2} out - */ -vec2.transformMat2 = function(out, a, m) { - var x = a[0], - y = a[1]; - out[0] = m[0] * x + m[2] * y; - out[1] = m[1] * x + m[3] * y; - return out; -}; - -/** - * Transforms the vec2 with a mat2d - * - * @param {vec2} out the receiving vector - * @param {vec2} a the vector to transform - * @param {mat2d} m matrix to transform with - * @returns {vec2} out - */ -vec2.transformMat2d = function(out, a, m) { - var x = a[0], - y = a[1]; - out[0] = m[0] * x + m[2] * y + m[4]; - out[1] = m[1] * x + m[3] * y + m[5]; - return out; -}; - -/** - * Transforms the vec2 with a mat3 - * 3rd vector component is implicitly '1' - * - * @param {vec2} out the receiving vector - * @param {vec2} a the vector to transform - * @param {mat3} m matrix to transform with - * @returns {vec2} out - */ -vec2.transformMat3 = function(out, a, m) { - var x = a[0], - y = a[1]; - out[0] = m[0] * x + m[3] * y + m[6]; - out[1] = m[1] * x + m[4] * y + m[7]; - return out; -}; - -/** - * Transforms the vec2 with a mat4 - * 3rd vector component is implicitly '0' - * 4th vector component is implicitly '1' - * - * @param {vec2} out the receiving vector - * @param {vec2} a the vector to transform - * @param {mat4} m matrix to transform with - * @returns {vec2} out - */ -vec2.transformMat4 = function(out, a, m) { - var x = a[0], - y = a[1]; - out[0] = m[0] * x + m[4] * y + m[12]; - out[1] = m[1] * x + m[5] * y + m[13]; - return out; -}; - -/** - * Perform some operation over an array of vec2s. - * - * @param {Array} a the array of vectors to iterate over - * @param {Number} stride Number of elements between the start of each vec2. If 0 assumes tightly packed - * @param {Number} offset Number of elements to skip at the beginning of the array - * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array - * @param {Function} fn Function to call for each vector in the array - * @param {Object} [arg] additional argument to pass to fn - * @returns {Array} a - * @function - */ -vec2.forEach = (function() { - var vec = vec2.create(); - - return function(a, stride, offset, count, fn, arg) { - var i, l; - if(!stride) { - stride = 2; - } - - if(!offset) { - offset = 0; - } - - if(count) { - l = Math.min((count * stride) + offset, a.length); - } else { - l = a.length; - } - - for(i = offset; i < l; i += stride) { - vec[0] = a[i]; vec[1] = a[i+1]; - fn(vec, vec, arg); - a[i] = vec[0]; a[i+1] = vec[1]; - } - - return a; - }; -})(); - -/** - * Returns a string representation of a vector - * - * @param {vec2} vec vector to represent as a string - * @returns {String} string representation of the vector - */ -vec2.str = function (a) { - return 'vec2(' + a[0] + ', ' + a[1] + ')'; -}; - -if(typeof(exports) !== 'undefined') { - exports.vec2 = vec2; -} -; -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -/** - * @class 3 Dimensional Vector - * @name vec3 - */ - -var vec3 = {}; - -/** - * Creates a new, empty vec3 - * - * @returns {vec3} a new 3D vector - */ -vec3.create = function() { - var out = new GLMAT_ARRAY_TYPE(3); - out[0] = 0; - out[1] = 0; - out[2] = 0; - return out; -}; - -/** - * Creates a new vec3 initialized with values from an existing vector - * - * @param {vec3} a vector to clone - * @returns {vec3} a new 3D vector - */ -vec3.clone = function(a) { - var out = new GLMAT_ARRAY_TYPE(3); - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - return out; -}; - -/** - * Creates a new vec3 initialized with the given values - * - * @param {Number} x X component - * @param {Number} y Y component - * @param {Number} z Z component - * @returns {vec3} a new 3D vector - */ -vec3.fromValues = function(x, y, z) { - var out = new GLMAT_ARRAY_TYPE(3); - out[0] = x; - out[1] = y; - out[2] = z; - return out; -}; - -/** - * Copy the values from one vec3 to another - * - * @param {vec3} out the receiving vector - * @param {vec3} a the source vector - * @returns {vec3} out - */ -vec3.copy = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - return out; -}; - -/** - * Set the components of a vec3 to the given values - * - * @param {vec3} out the receiving vector - * @param {Number} x X component - * @param {Number} y Y component - * @param {Number} z Z component - * @returns {vec3} out - */ -vec3.set = function(out, x, y, z) { - out[0] = x; - out[1] = y; - out[2] = z; - return out; -}; - -/** - * Adds two vec3's - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {vec3} out - */ -vec3.add = function(out, a, b) { - out[0] = a[0] + b[0]; - out[1] = a[1] + b[1]; - out[2] = a[2] + b[2]; - return out; -}; - -/** - * Subtracts vector b from vector a - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {vec3} out - */ -vec3.subtract = function(out, a, b) { - out[0] = a[0] - b[0]; - out[1] = a[1] - b[1]; - out[2] = a[2] - b[2]; - return out; -}; - -/** - * Alias for {@link vec3.subtract} - * @function - */ -vec3.sub = vec3.subtract; - -/** - * Multiplies two vec3's - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {vec3} out - */ -vec3.multiply = function(out, a, b) { - out[0] = a[0] * b[0]; - out[1] = a[1] * b[1]; - out[2] = a[2] * b[2]; - return out; -}; - -/** - * Alias for {@link vec3.multiply} - * @function - */ -vec3.mul = vec3.multiply; - -/** - * Divides two vec3's - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {vec3} out - */ -vec3.divide = function(out, a, b) { - out[0] = a[0] / b[0]; - out[1] = a[1] / b[1]; - out[2] = a[2] / b[2]; - return out; -}; - -/** - * Alias for {@link vec3.divide} - * @function - */ -vec3.div = vec3.divide; - -/** - * Returns the minimum of two vec3's - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {vec3} out - */ -vec3.min = function(out, a, b) { - out[0] = Math.min(a[0], b[0]); - out[1] = Math.min(a[1], b[1]); - out[2] = Math.min(a[2], b[2]); - return out; -}; - -/** - * Returns the maximum of two vec3's - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {vec3} out - */ -vec3.max = function(out, a, b) { - out[0] = Math.max(a[0], b[0]); - out[1] = Math.max(a[1], b[1]); - out[2] = Math.max(a[2], b[2]); - return out; -}; - -/** - * Scales a vec3 by a scalar number - * - * @param {vec3} out the receiving vector - * @param {vec3} a the vector to scale - * @param {Number} b amount to scale the vector by - * @returns {vec3} out - */ -vec3.scale = function(out, a, b) { - out[0] = a[0] * b; - out[1] = a[1] * b; - out[2] = a[2] * b; - return out; -}; - -/** - * Adds two vec3's after scaling the second operand by a scalar value - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @param {Number} scale the amount to scale b by before adding - * @returns {vec3} out - */ -vec3.scaleAndAdd = function(out, a, b, scale) { - out[0] = a[0] + (b[0] * scale); - out[1] = a[1] + (b[1] * scale); - out[2] = a[2] + (b[2] * scale); - return out; -}; - -/** - * Calculates the euclidian distance between two vec3's - * - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {Number} distance between a and b - */ -vec3.distance = function(a, b) { - var x = b[0] - a[0], - y = b[1] - a[1], - z = b[2] - a[2]; - return Math.sqrt(x*x + y*y + z*z); -}; - -/** - * Alias for {@link vec3.distance} - * @function - */ -vec3.dist = vec3.distance; - -/** - * Calculates the squared euclidian distance between two vec3's - * - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {Number} squared distance between a and b - */ -vec3.squaredDistance = function(a, b) { - var x = b[0] - a[0], - y = b[1] - a[1], - z = b[2] - a[2]; - return x*x + y*y + z*z; -}; - -/** - * Alias for {@link vec3.squaredDistance} - * @function - */ -vec3.sqrDist = vec3.squaredDistance; - -/** - * Calculates the length of a vec3 - * - * @param {vec3} a vector to calculate length of - * @returns {Number} length of a - */ -vec3.length = function (a) { - var x = a[0], - y = a[1], - z = a[2]; - return Math.sqrt(x*x + y*y + z*z); -}; - -/** - * Alias for {@link vec3.length} - * @function - */ -vec3.len = vec3.length; - -/** - * Calculates the squared length of a vec3 - * - * @param {vec3} a vector to calculate squared length of - * @returns {Number} squared length of a - */ -vec3.squaredLength = function (a) { - var x = a[0], - y = a[1], - z = a[2]; - return x*x + y*y + z*z; -}; - -/** - * Alias for {@link vec3.squaredLength} - * @function - */ -vec3.sqrLen = vec3.squaredLength; - -/** - * Negates the components of a vec3 - * - * @param {vec3} out the receiving vector - * @param {vec3} a vector to negate - * @returns {vec3} out - */ -vec3.negate = function(out, a) { - out[0] = -a[0]; - out[1] = -a[1]; - out[2] = -a[2]; - return out; -}; - -/** - * Normalize a vec3 - * - * @param {vec3} out the receiving vector - * @param {vec3} a vector to normalize - * @returns {vec3} out - */ -vec3.normalize = function(out, a) { - var x = a[0], - y = a[1], - z = a[2]; - var len = x*x + y*y + z*z; - if (len > 0) { - //TODO: evaluate use of glm_invsqrt here? - len = 1 / Math.sqrt(len); - out[0] = a[0] * len; - out[1] = a[1] * len; - out[2] = a[2] * len; - } - return out; -}; - -/** - * Calculates the dot product of two vec3's - * - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {Number} dot product of a and b - */ -vec3.dot = function (a, b) { - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; -}; - -/** - * Computes the cross product of two vec3's - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @returns {vec3} out - */ -vec3.cross = function(out, a, b) { - var ax = a[0], ay = a[1], az = a[2], - bx = b[0], by = b[1], bz = b[2]; - - out[0] = ay * bz - az * by; - out[1] = az * bx - ax * bz; - out[2] = ax * by - ay * bx; - return out; -}; - -/** - * Performs a linear interpolation between two vec3's - * - * @param {vec3} out the receiving vector - * @param {vec3} a the first operand - * @param {vec3} b the second operand - * @param {Number} t interpolation amount between the two inputs - * @returns {vec3} out - */ -vec3.lerp = function (out, a, b, t) { - var ax = a[0], - ay = a[1], - az = a[2]; - out[0] = ax + t * (b[0] - ax); - out[1] = ay + t * (b[1] - ay); - out[2] = az + t * (b[2] - az); - return out; -}; - -/** - * Generates a random vector with the given scale - * - * @param {vec3} out the receiving vector - * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned - * @returns {vec3} out - */ -vec3.random = function (out, scale) { - scale = scale || 1.0; - - var r = GLMAT_RANDOM() * 2.0 * Math.PI; - var z = (GLMAT_RANDOM() * 2.0) - 1.0; - var zScale = Math.sqrt(1.0-z*z) * scale; - - out[0] = Math.cos(r) * zScale; - out[1] = Math.sin(r) * zScale; - out[2] = z * scale; - return out; -}; - -/** - * Transforms the vec3 with a mat4. - * 4th vector component is implicitly '1' - * - * @param {vec3} out the receiving vector - * @param {vec3} a the vector to transform - * @param {mat4} m matrix to transform with - * @returns {vec3} out - */ -vec3.transformMat4 = function(out, a, m) { - var x = a[0], y = a[1], z = a[2]; - out[0] = m[0] * x + m[4] * y + m[8] * z + m[12]; - out[1] = m[1] * x + m[5] * y + m[9] * z + m[13]; - out[2] = m[2] * x + m[6] * y + m[10] * z + m[14]; - return out; -}; - -/** - * Transforms the vec3 with a mat3. - * - * @param {vec3} out the receiving vector - * @param {vec3} a the vector to transform - * @param {mat4} m the 3x3 matrix to transform with - * @returns {vec3} out - */ -vec3.transformMat3 = function(out, a, m) { - var x = a[0], y = a[1], z = a[2]; - out[0] = x * m[0] + y * m[3] + z * m[6]; - out[1] = x * m[1] + y * m[4] + z * m[7]; - out[2] = x * m[2] + y * m[5] + z * m[8]; - return out; -}; - -/** - * Transforms the vec3 with a quat - * - * @param {vec3} out the receiving vector - * @param {vec3} a the vector to transform - * @param {quat} q quaternion to transform with - * @returns {vec3} out - */ -vec3.transformQuat = function(out, a, q) { - // benchmarks: http://jsperf.com/quaternion-transform-vec3-implementations - - var x = a[0], y = a[1], z = a[2], - qx = q[0], qy = q[1], qz = q[2], qw = q[3], - - // calculate quat * vec - ix = qw * x + qy * z - qz * y, - iy = qw * y + qz * x - qx * z, - iz = qw * z + qx * y - qy * x, - iw = -qx * x - qy * y - qz * z; - - // calculate result * inverse quat - out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; - out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; - out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; - return out; -}; - -/** - * Perform some operation over an array of vec3s. - * - * @param {Array} a the array of vectors to iterate over - * @param {Number} stride Number of elements between the start of each vec3. If 0 assumes tightly packed - * @param {Number} offset Number of elements to skip at the beginning of the array - * @param {Number} count Number of vec3s to iterate over. If 0 iterates over entire array - * @param {Function} fn Function to call for each vector in the array - * @param {Object} [arg] additional argument to pass to fn - * @returns {Array} a - * @function - */ -vec3.forEach = (function() { - var vec = vec3.create(); - - return function(a, stride, offset, count, fn, arg) { - var i, l; - if(!stride) { - stride = 3; - } - - if(!offset) { - offset = 0; - } - - if(count) { - l = Math.min((count * stride) + offset, a.length); - } else { - l = a.length; - } - - for(i = offset; i < l; i += stride) { - vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; - fn(vec, vec, arg); - a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; - } - - return a; - }; -})(); - -/** - * Returns a string representation of a vector - * - * @param {vec3} vec vector to represent as a string - * @returns {String} string representation of the vector - */ -vec3.str = function (a) { - return 'vec3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ')'; -}; - -if(typeof(exports) !== 'undefined') { - exports.vec3 = vec3; -} -; -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -/** - * @class 4 Dimensional Vector - * @name vec4 - */ - -var vec4 = {}; - -/** - * Creates a new, empty vec4 - * - * @returns {vec4} a new 4D vector - */ -vec4.create = function() { - var out = new GLMAT_ARRAY_TYPE(4); - out[0] = 0; - out[1] = 0; - out[2] = 0; - out[3] = 0; - return out; -}; - -/** - * Creates a new vec4 initialized with values from an existing vector - * - * @param {vec4} a vector to clone - * @returns {vec4} a new 4D vector - */ -vec4.clone = function(a) { - var out = new GLMAT_ARRAY_TYPE(4); - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - return out; -}; - -/** - * Creates a new vec4 initialized with the given values - * - * @param {Number} x X component - * @param {Number} y Y component - * @param {Number} z Z component - * @param {Number} w W component - * @returns {vec4} a new 4D vector - */ -vec4.fromValues = function(x, y, z, w) { - var out = new GLMAT_ARRAY_TYPE(4); - out[0] = x; - out[1] = y; - out[2] = z; - out[3] = w; - return out; -}; - -/** - * Copy the values from one vec4 to another - * - * @param {vec4} out the receiving vector - * @param {vec4} a the source vector - * @returns {vec4} out - */ -vec4.copy = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - return out; -}; - -/** - * Set the components of a vec4 to the given values - * - * @param {vec4} out the receiving vector - * @param {Number} x X component - * @param {Number} y Y component - * @param {Number} z Z component - * @param {Number} w W component - * @returns {vec4} out - */ -vec4.set = function(out, x, y, z, w) { - out[0] = x; - out[1] = y; - out[2] = z; - out[3] = w; - return out; -}; - -/** - * Adds two vec4's - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {vec4} out - */ -vec4.add = function(out, a, b) { - out[0] = a[0] + b[0]; - out[1] = a[1] + b[1]; - out[2] = a[2] + b[2]; - out[3] = a[3] + b[3]; - return out; -}; - -/** - * Subtracts vector b from vector a - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {vec4} out - */ -vec4.subtract = function(out, a, b) { - out[0] = a[0] - b[0]; - out[1] = a[1] - b[1]; - out[2] = a[2] - b[2]; - out[3] = a[3] - b[3]; - return out; -}; - -/** - * Alias for {@link vec4.subtract} - * @function - */ -vec4.sub = vec4.subtract; - -/** - * Multiplies two vec4's - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {vec4} out - */ -vec4.multiply = function(out, a, b) { - out[0] = a[0] * b[0]; - out[1] = a[1] * b[1]; - out[2] = a[2] * b[2]; - out[3] = a[3] * b[3]; - return out; -}; - -/** - * Alias for {@link vec4.multiply} - * @function - */ -vec4.mul = vec4.multiply; - -/** - * Divides two vec4's - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {vec4} out - */ -vec4.divide = function(out, a, b) { - out[0] = a[0] / b[0]; - out[1] = a[1] / b[1]; - out[2] = a[2] / b[2]; - out[3] = a[3] / b[3]; - return out; -}; - -/** - * Alias for {@link vec4.divide} - * @function - */ -vec4.div = vec4.divide; - -/** - * Returns the minimum of two vec4's - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {vec4} out - */ -vec4.min = function(out, a, b) { - out[0] = Math.min(a[0], b[0]); - out[1] = Math.min(a[1], b[1]); - out[2] = Math.min(a[2], b[2]); - out[3] = Math.min(a[3], b[3]); - return out; -}; - -/** - * Returns the maximum of two vec4's - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {vec4} out - */ -vec4.max = function(out, a, b) { - out[0] = Math.max(a[0], b[0]); - out[1] = Math.max(a[1], b[1]); - out[2] = Math.max(a[2], b[2]); - out[3] = Math.max(a[3], b[3]); - return out; -}; - -/** - * Scales a vec4 by a scalar number - * - * @param {vec4} out the receiving vector - * @param {vec4} a the vector to scale - * @param {Number} b amount to scale the vector by - * @returns {vec4} out - */ -vec4.scale = function(out, a, b) { - out[0] = a[0] * b; - out[1] = a[1] * b; - out[2] = a[2] * b; - out[3] = a[3] * b; - return out; -}; - -/** - * Adds two vec4's after scaling the second operand by a scalar value - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @param {Number} scale the amount to scale b by before adding - * @returns {vec4} out - */ -vec4.scaleAndAdd = function(out, a, b, scale) { - out[0] = a[0] + (b[0] * scale); - out[1] = a[1] + (b[1] * scale); - out[2] = a[2] + (b[2] * scale); - out[3] = a[3] + (b[3] * scale); - return out; -}; - -/** - * Calculates the euclidian distance between two vec4's - * - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {Number} distance between a and b - */ -vec4.distance = function(a, b) { - var x = b[0] - a[0], - y = b[1] - a[1], - z = b[2] - a[2], - w = b[3] - a[3]; - return Math.sqrt(x*x + y*y + z*z + w*w); -}; - -/** - * Alias for {@link vec4.distance} - * @function - */ -vec4.dist = vec4.distance; - -/** - * Calculates the squared euclidian distance between two vec4's - * - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {Number} squared distance between a and b - */ -vec4.squaredDistance = function(a, b) { - var x = b[0] - a[0], - y = b[1] - a[1], - z = b[2] - a[2], - w = b[3] - a[3]; - return x*x + y*y + z*z + w*w; -}; - -/** - * Alias for {@link vec4.squaredDistance} - * @function - */ -vec4.sqrDist = vec4.squaredDistance; - -/** - * Calculates the length of a vec4 - * - * @param {vec4} a vector to calculate length of - * @returns {Number} length of a - */ -vec4.length = function (a) { - var x = a[0], - y = a[1], - z = a[2], - w = a[3]; - return Math.sqrt(x*x + y*y + z*z + w*w); -}; - -/** - * Alias for {@link vec4.length} - * @function - */ -vec4.len = vec4.length; - -/** - * Calculates the squared length of a vec4 - * - * @param {vec4} a vector to calculate squared length of - * @returns {Number} squared length of a - */ -vec4.squaredLength = function (a) { - var x = a[0], - y = a[1], - z = a[2], - w = a[3]; - return x*x + y*y + z*z + w*w; -}; - -/** - * Alias for {@link vec4.squaredLength} - * @function - */ -vec4.sqrLen = vec4.squaredLength; - -/** - * Negates the components of a vec4 - * - * @param {vec4} out the receiving vector - * @param {vec4} a vector to negate - * @returns {vec4} out - */ -vec4.negate = function(out, a) { - out[0] = -a[0]; - out[1] = -a[1]; - out[2] = -a[2]; - out[3] = -a[3]; - return out; -}; - -/** - * Normalize a vec4 - * - * @param {vec4} out the receiving vector - * @param {vec4} a vector to normalize - * @returns {vec4} out - */ -vec4.normalize = function(out, a) { - var x = a[0], - y = a[1], - z = a[2], - w = a[3]; - var len = x*x + y*y + z*z + w*w; - if (len > 0) { - len = 1 / Math.sqrt(len); - out[0] = a[0] * len; - out[1] = a[1] * len; - out[2] = a[2] * len; - out[3] = a[3] * len; - } - return out; -}; - -/** - * Calculates the dot product of two vec4's - * - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @returns {Number} dot product of a and b - */ -vec4.dot = function (a, b) { - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; -}; - -/** - * Performs a linear interpolation between two vec4's - * - * @param {vec4} out the receiving vector - * @param {vec4} a the first operand - * @param {vec4} b the second operand - * @param {Number} t interpolation amount between the two inputs - * @returns {vec4} out - */ -vec4.lerp = function (out, a, b, t) { - var ax = a[0], - ay = a[1], - az = a[2], - aw = a[3]; - out[0] = ax + t * (b[0] - ax); - out[1] = ay + t * (b[1] - ay); - out[2] = az + t * (b[2] - az); - out[3] = aw + t * (b[3] - aw); - return out; -}; - -/** - * Generates a random vector with the given scale - * - * @param {vec4} out the receiving vector - * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned - * @returns {vec4} out - */ -vec4.random = function (out, scale) { - scale = scale || 1.0; - - //TODO: This is a pretty awful way of doing this. Find something better. - out[0] = GLMAT_RANDOM(); - out[1] = GLMAT_RANDOM(); - out[2] = GLMAT_RANDOM(); - out[3] = GLMAT_RANDOM(); - vec4.normalize(out, out); - vec4.scale(out, out, scale); - return out; -}; - -/** - * Transforms the vec4 with a mat4. - * - * @param {vec4} out the receiving vector - * @param {vec4} a the vector to transform - * @param {mat4} m matrix to transform with - * @returns {vec4} out - */ -vec4.transformMat4 = function(out, a, m) { - var x = a[0], y = a[1], z = a[2], w = a[3]; - out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w; - out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w; - out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w; - out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w; - return out; -}; - -/** - * Transforms the vec4 with a quat - * - * @param {vec4} out the receiving vector - * @param {vec4} a the vector to transform - * @param {quat} q quaternion to transform with - * @returns {vec4} out - */ -vec4.transformQuat = function(out, a, q) { - var x = a[0], y = a[1], z = a[2], - qx = q[0], qy = q[1], qz = q[2], qw = q[3], - - // calculate quat * vec - ix = qw * x + qy * z - qz * y, - iy = qw * y + qz * x - qx * z, - iz = qw * z + qx * y - qy * x, - iw = -qx * x - qy * y - qz * z; - - // calculate result * inverse quat - out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; - out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; - out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; - return out; -}; - -/** - * Perform some operation over an array of vec4s. - * - * @param {Array} a the array of vectors to iterate over - * @param {Number} stride Number of elements between the start of each vec4. If 0 assumes tightly packed - * @param {Number} offset Number of elements to skip at the beginning of the array - * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array - * @param {Function} fn Function to call for each vector in the array - * @param {Object} [arg] additional argument to pass to fn - * @returns {Array} a - * @function - */ -vec4.forEach = (function() { - var vec = vec4.create(); - - return function(a, stride, offset, count, fn, arg) { - var i, l; - if(!stride) { - stride = 4; - } - - if(!offset) { - offset = 0; - } - - if(count) { - l = Math.min((count * stride) + offset, a.length); - } else { - l = a.length; - } - - for(i = offset; i < l; i += stride) { - vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; vec[3] = a[i+3]; - fn(vec, vec, arg); - a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; a[i+3] = vec[3]; - } - - return a; - }; -})(); - -/** - * Returns a string representation of a vector - * - * @param {vec4} vec vector to represent as a string - * @returns {String} string representation of the vector - */ -vec4.str = function (a) { - return 'vec4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; -}; - -if(typeof(exports) !== 'undefined') { - exports.vec4 = vec4; -} -; -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -/** - * @class 2x2 Matrix - * @name mat2 - */ - -var mat2 = {}; - -/** - * Creates a new identity mat2 - * - * @returns {mat2} a new 2x2 matrix - */ -mat2.create = function() { - var out = new GLMAT_ARRAY_TYPE(4); - out[0] = 1; - out[1] = 0; - out[2] = 0; - out[3] = 1; - return out; -}; - -/** - * Creates a new mat2 initialized with values from an existing matrix - * - * @param {mat2} a matrix to clone - * @returns {mat2} a new 2x2 matrix - */ -mat2.clone = function(a) { - var out = new GLMAT_ARRAY_TYPE(4); - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - return out; -}; - -/** - * Copy the values from one mat2 to another - * - * @param {mat2} out the receiving matrix - * @param {mat2} a the source matrix - * @returns {mat2} out - */ -mat2.copy = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - return out; -}; - -/** - * Set a mat2 to the identity matrix - * - * @param {mat2} out the receiving matrix - * @returns {mat2} out - */ -mat2.identity = function(out) { - out[0] = 1; - out[1] = 0; - out[2] = 0; - out[3] = 1; - return out; -}; - -/** - * Transpose the values of a mat2 - * - * @param {mat2} out the receiving matrix - * @param {mat2} a the source matrix - * @returns {mat2} out - */ -mat2.transpose = function(out, a) { - // If we are transposing ourselves we can skip a few steps but have to cache some values - if (out === a) { - var a1 = a[1]; - out[1] = a[2]; - out[2] = a1; - } else { - out[0] = a[0]; - out[1] = a[2]; - out[2] = a[1]; - out[3] = a[3]; - } - - return out; -}; - -/** - * Inverts a mat2 - * - * @param {mat2} out the receiving matrix - * @param {mat2} a the source matrix - * @returns {mat2} out - */ -mat2.invert = function(out, a) { - var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], - - // Calculate the determinant - det = a0 * a3 - a2 * a1; - - if (!det) { - return null; - } - det = 1.0 / det; - - out[0] = a3 * det; - out[1] = -a1 * det; - out[2] = -a2 * det; - out[3] = a0 * det; - - return out; -}; - -/** - * Calculates the adjugate of a mat2 - * - * @param {mat2} out the receiving matrix - * @param {mat2} a the source matrix - * @returns {mat2} out - */ -mat2.adjoint = function(out, a) { - // Caching this value is nessecary if out == a - var a0 = a[0]; - out[0] = a[3]; - out[1] = -a[1]; - out[2] = -a[2]; - out[3] = a0; - - return out; -}; - -/** - * Calculates the determinant of a mat2 - * - * @param {mat2} a the source matrix - * @returns {Number} determinant of a - */ -mat2.determinant = function (a) { - return a[0] * a[3] - a[2] * a[1]; -}; - -/** - * Multiplies two mat2's - * - * @param {mat2} out the receiving matrix - * @param {mat2} a the first operand - * @param {mat2} b the second operand - * @returns {mat2} out - */ -mat2.multiply = function (out, a, b) { - var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3]; - var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; - out[0] = a0 * b0 + a1 * b2; - out[1] = a0 * b1 + a1 * b3; - out[2] = a2 * b0 + a3 * b2; - out[3] = a2 * b1 + a3 * b3; - return out; -}; - -/** - * Alias for {@link mat2.multiply} - * @function - */ -mat2.mul = mat2.multiply; - -/** - * Rotates a mat2 by the given angle - * - * @param {mat2} out the receiving matrix - * @param {mat2} a the matrix to rotate - * @param {Number} rad the angle to rotate the matrix by - * @returns {mat2} out - */ -mat2.rotate = function (out, a, rad) { - var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], - s = Math.sin(rad), - c = Math.cos(rad); - out[0] = a0 * c + a1 * s; - out[1] = a0 * -s + a1 * c; - out[2] = a2 * c + a3 * s; - out[3] = a2 * -s + a3 * c; - return out; -}; - -/** - * Scales the mat2 by the dimensions in the given vec2 - * - * @param {mat2} out the receiving matrix - * @param {mat2} a the matrix to rotate - * @param {vec2} v the vec2 to scale the matrix by - * @returns {mat2} out - **/ -mat2.scale = function(out, a, v) { - var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], - v0 = v[0], v1 = v[1]; - out[0] = a0 * v0; - out[1] = a1 * v1; - out[2] = a2 * v0; - out[3] = a3 * v1; - return out; -}; - -/** - * Returns a string representation of a mat2 - * - * @param {mat2} mat matrix to represent as a string - * @returns {String} string representation of the matrix - */ -mat2.str = function (a) { - return 'mat2(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; -}; - -if(typeof(exports) !== 'undefined') { - exports.mat2 = mat2; -} -; -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -/** - * @class 2x3 Matrix - * @name mat2d - * - * @description - * A mat2d contains six elements defined as: - *
- * [a, b,
- *  c, d,
- *  tx,ty]
- * 
- * This is a short form for the 3x3 matrix: - *
- * [a, b, 0
- *  c, d, 0
- *  tx,ty,1]
- * 
- * The last column is ignored so the array is shorter and operations are faster. - */ - -var mat2d = {}; - -/** - * Creates a new identity mat2d - * - * @returns {mat2d} a new 2x3 matrix - */ -mat2d.create = function() { - var out = new GLMAT_ARRAY_TYPE(6); - out[0] = 1; - out[1] = 0; - out[2] = 0; - out[3] = 1; - out[4] = 0; - out[5] = 0; - return out; -}; - -/** - * Creates a new mat2d initialized with values from an existing matrix - * - * @param {mat2d} a matrix to clone - * @returns {mat2d} a new 2x3 matrix - */ -mat2d.clone = function(a) { - var out = new GLMAT_ARRAY_TYPE(6); - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - out[4] = a[4]; - out[5] = a[5]; - return out; -}; - -/** - * Copy the values from one mat2d to another - * - * @param {mat2d} out the receiving matrix - * @param {mat2d} a the source matrix - * @returns {mat2d} out - */ -mat2d.copy = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - out[4] = a[4]; - out[5] = a[5]; - return out; -}; - -/** - * Set a mat2d to the identity matrix - * - * @param {mat2d} out the receiving matrix - * @returns {mat2d} out - */ -mat2d.identity = function(out) { - out[0] = 1; - out[1] = 0; - out[2] = 0; - out[3] = 1; - out[4] = 0; - out[5] = 0; - return out; -}; - -/** - * Inverts a mat2d - * - * @param {mat2d} out the receiving matrix - * @param {mat2d} a the source matrix - * @returns {mat2d} out - */ -mat2d.invert = function(out, a) { - var aa = a[0], ab = a[1], ac = a[2], ad = a[3], - atx = a[4], aty = a[5]; - - var det = aa * ad - ab * ac; - if(!det){ - return null; - } - det = 1.0 / det; - - out[0] = ad * det; - out[1] = -ab * det; - out[2] = -ac * det; - out[3] = aa * det; - out[4] = (ac * aty - ad * atx) * det; - out[5] = (ab * atx - aa * aty) * det; - return out; -}; - -/** - * Calculates the determinant of a mat2d - * - * @param {mat2d} a the source matrix - * @returns {Number} determinant of a - */ -mat2d.determinant = function (a) { - return a[0] * a[3] - a[1] * a[2]; -}; - -/** - * Multiplies two mat2d's - * - * @param {mat2d} out the receiving matrix - * @param {mat2d} a the first operand - * @param {mat2d} b the second operand - * @returns {mat2d} out - */ -mat2d.multiply = function (out, a, b) { - var aa = a[0], ab = a[1], ac = a[2], ad = a[3], - atx = a[4], aty = a[5], - ba = b[0], bb = b[1], bc = b[2], bd = b[3], - btx = b[4], bty = b[5]; - - out[0] = aa*ba + ab*bc; - out[1] = aa*bb + ab*bd; - out[2] = ac*ba + ad*bc; - out[3] = ac*bb + ad*bd; - out[4] = ba*atx + bc*aty + btx; - out[5] = bb*atx + bd*aty + bty; - return out; -}; - -/** - * Alias for {@link mat2d.multiply} - * @function - */ -mat2d.mul = mat2d.multiply; - - -/** - * Rotates a mat2d by the given angle - * - * @param {mat2d} out the receiving matrix - * @param {mat2d} a the matrix to rotate - * @param {Number} rad the angle to rotate the matrix by - * @returns {mat2d} out - */ -mat2d.rotate = function (out, a, rad) { - var aa = a[0], - ab = a[1], - ac = a[2], - ad = a[3], - atx = a[4], - aty = a[5], - st = Math.sin(rad), - ct = Math.cos(rad); - - out[0] = aa*ct + ab*st; - out[1] = -aa*st + ab*ct; - out[2] = ac*ct + ad*st; - out[3] = -ac*st + ct*ad; - out[4] = ct*atx + st*aty; - out[5] = ct*aty - st*atx; - return out; -}; - -/** - * Scales the mat2d by the dimensions in the given vec2 - * - * @param {mat2d} out the receiving matrix - * @param {mat2d} a the matrix to translate - * @param {vec2} v the vec2 to scale the matrix by - * @returns {mat2d} out - **/ -mat2d.scale = function(out, a, v) { - var vx = v[0], vy = v[1]; - out[0] = a[0] * vx; - out[1] = a[1] * vy; - out[2] = a[2] * vx; - out[3] = a[3] * vy; - out[4] = a[4] * vx; - out[5] = a[5] * vy; - return out; -}; - -/** - * Translates the mat2d by the dimensions in the given vec2 - * - * @param {mat2d} out the receiving matrix - * @param {mat2d} a the matrix to translate - * @param {vec2} v the vec2 to translate the matrix by - * @returns {mat2d} out - **/ -mat2d.translate = function(out, a, v) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - out[4] = a[4] + v[0]; - out[5] = a[5] + v[1]; - return out; -}; - -/** - * Returns a string representation of a mat2d - * - * @param {mat2d} a matrix to represent as a string - * @returns {String} string representation of the matrix - */ -mat2d.str = function (a) { - return 'mat2d(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + - a[3] + ', ' + a[4] + ', ' + a[5] + ')'; -}; - -if(typeof(exports) !== 'undefined') { - exports.mat2d = mat2d; -} -; -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -/** - * @class 3x3 Matrix - * @name mat3 - */ - -var mat3 = {}; - -/** - * Creates a new identity mat3 - * - * @returns {mat3} a new 3x3 matrix - */ -mat3.create = function() { - var out = new GLMAT_ARRAY_TYPE(9); - out[0] = 1; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 1; - out[5] = 0; - out[6] = 0; - out[7] = 0; - out[8] = 1; - return out; -}; - -/** - * Copies the upper-left 3x3 values into the given mat3. - * - * @param {mat3} out the receiving 3x3 matrix - * @param {mat4} a the source 4x4 matrix - * @returns {mat3} out - */ -mat3.fromMat4 = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[4]; - out[4] = a[5]; - out[5] = a[6]; - out[6] = a[8]; - out[7] = a[9]; - out[8] = a[10]; - return out; -}; - -/** - * Creates a new mat3 initialized with values from an existing matrix - * - * @param {mat3} a matrix to clone - * @returns {mat3} a new 3x3 matrix - */ -mat3.clone = function(a) { - var out = new GLMAT_ARRAY_TYPE(9); - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - out[4] = a[4]; - out[5] = a[5]; - out[6] = a[6]; - out[7] = a[7]; - out[8] = a[8]; - return out; -}; - -/** - * Copy the values from one mat3 to another - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the source matrix - * @returns {mat3} out - */ -mat3.copy = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - out[4] = a[4]; - out[5] = a[5]; - out[6] = a[6]; - out[7] = a[7]; - out[8] = a[8]; - return out; -}; - -/** - * Set a mat3 to the identity matrix - * - * @param {mat3} out the receiving matrix - * @returns {mat3} out - */ -mat3.identity = function(out) { - out[0] = 1; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 1; - out[5] = 0; - out[6] = 0; - out[7] = 0; - out[8] = 1; - return out; -}; - -/** - * Transpose the values of a mat3 - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the source matrix - * @returns {mat3} out - */ -mat3.transpose = function(out, a) { - // If we are transposing ourselves we can skip a few steps but have to cache some values - if (out === a) { - var a01 = a[1], a02 = a[2], a12 = a[5]; - out[1] = a[3]; - out[2] = a[6]; - out[3] = a01; - out[5] = a[7]; - out[6] = a02; - out[7] = a12; - } else { - out[0] = a[0]; - out[1] = a[3]; - out[2] = a[6]; - out[3] = a[1]; - out[4] = a[4]; - out[5] = a[7]; - out[6] = a[2]; - out[7] = a[5]; - out[8] = a[8]; - } - - return out; -}; - -/** - * Inverts a mat3 - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the source matrix - * @returns {mat3} out - */ -mat3.invert = function(out, a) { - var a00 = a[0], a01 = a[1], a02 = a[2], - a10 = a[3], a11 = a[4], a12 = a[5], - a20 = a[6], a21 = a[7], a22 = a[8], - - b01 = a22 * a11 - a12 * a21, - b11 = -a22 * a10 + a12 * a20, - b21 = a21 * a10 - a11 * a20, - - // Calculate the determinant - det = a00 * b01 + a01 * b11 + a02 * b21; - - if (!det) { - return null; - } - det = 1.0 / det; - - out[0] = b01 * det; - out[1] = (-a22 * a01 + a02 * a21) * det; - out[2] = (a12 * a01 - a02 * a11) * det; - out[3] = b11 * det; - out[4] = (a22 * a00 - a02 * a20) * det; - out[5] = (-a12 * a00 + a02 * a10) * det; - out[6] = b21 * det; - out[7] = (-a21 * a00 + a01 * a20) * det; - out[8] = (a11 * a00 - a01 * a10) * det; - return out; -}; - -/** - * Calculates the adjugate of a mat3 - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the source matrix - * @returns {mat3} out - */ -mat3.adjoint = function(out, a) { - var a00 = a[0], a01 = a[1], a02 = a[2], - a10 = a[3], a11 = a[4], a12 = a[5], - a20 = a[6], a21 = a[7], a22 = a[8]; - - out[0] = (a11 * a22 - a12 * a21); - out[1] = (a02 * a21 - a01 * a22); - out[2] = (a01 * a12 - a02 * a11); - out[3] = (a12 * a20 - a10 * a22); - out[4] = (a00 * a22 - a02 * a20); - out[5] = (a02 * a10 - a00 * a12); - out[6] = (a10 * a21 - a11 * a20); - out[7] = (a01 * a20 - a00 * a21); - out[8] = (a00 * a11 - a01 * a10); - return out; -}; - -/** - * Calculates the determinant of a mat3 - * - * @param {mat3} a the source matrix - * @returns {Number} determinant of a - */ -mat3.determinant = function (a) { - var a00 = a[0], a01 = a[1], a02 = a[2], - a10 = a[3], a11 = a[4], a12 = a[5], - a20 = a[6], a21 = a[7], a22 = a[8]; - - return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20); -}; - -/** - * Multiplies two mat3's - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the first operand - * @param {mat3} b the second operand - * @returns {mat3} out - */ -mat3.multiply = function (out, a, b) { - var a00 = a[0], a01 = a[1], a02 = a[2], - a10 = a[3], a11 = a[4], a12 = a[5], - a20 = a[6], a21 = a[7], a22 = a[8], - - b00 = b[0], b01 = b[1], b02 = b[2], - b10 = b[3], b11 = b[4], b12 = b[5], - b20 = b[6], b21 = b[7], b22 = b[8]; - - out[0] = b00 * a00 + b01 * a10 + b02 * a20; - out[1] = b00 * a01 + b01 * a11 + b02 * a21; - out[2] = b00 * a02 + b01 * a12 + b02 * a22; - - out[3] = b10 * a00 + b11 * a10 + b12 * a20; - out[4] = b10 * a01 + b11 * a11 + b12 * a21; - out[5] = b10 * a02 + b11 * a12 + b12 * a22; - - out[6] = b20 * a00 + b21 * a10 + b22 * a20; - out[7] = b20 * a01 + b21 * a11 + b22 * a21; - out[8] = b20 * a02 + b21 * a12 + b22 * a22; - return out; -}; - -/** - * Alias for {@link mat3.multiply} - * @function - */ -mat3.mul = mat3.multiply; - -/** - * Translate a mat3 by the given vector - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the matrix to translate - * @param {vec2} v vector to translate by - * @returns {mat3} out - */ -mat3.translate = function(out, a, v) { - var a00 = a[0], a01 = a[1], a02 = a[2], - a10 = a[3], a11 = a[4], a12 = a[5], - a20 = a[6], a21 = a[7], a22 = a[8], - x = v[0], y = v[1]; - - out[0] = a00; - out[1] = a01; - out[2] = a02; - - out[3] = a10; - out[4] = a11; - out[5] = a12; - - out[6] = x * a00 + y * a10 + a20; - out[7] = x * a01 + y * a11 + a21; - out[8] = x * a02 + y * a12 + a22; - return out; -}; - -/** - * Rotates a mat3 by the given angle - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the matrix to rotate - * @param {Number} rad the angle to rotate the matrix by - * @returns {mat3} out - */ -mat3.rotate = function (out, a, rad) { - var a00 = a[0], a01 = a[1], a02 = a[2], - a10 = a[3], a11 = a[4], a12 = a[5], - a20 = a[6], a21 = a[7], a22 = a[8], - - s = Math.sin(rad), - c = Math.cos(rad); - - out[0] = c * a00 + s * a10; - out[1] = c * a01 + s * a11; - out[2] = c * a02 + s * a12; - - out[3] = c * a10 - s * a00; - out[4] = c * a11 - s * a01; - out[5] = c * a12 - s * a02; - - out[6] = a20; - out[7] = a21; - out[8] = a22; - return out; -}; - -/** - * Scales the mat3 by the dimensions in the given vec2 - * - * @param {mat3} out the receiving matrix - * @param {mat3} a the matrix to rotate - * @param {vec2} v the vec2 to scale the matrix by - * @returns {mat3} out - **/ -mat3.scale = function(out, a, v) { - var x = v[0], y = v[1]; - - out[0] = x * a[0]; - out[1] = x * a[1]; - out[2] = x * a[2]; - - out[3] = y * a[3]; - out[4] = y * a[4]; - out[5] = y * a[5]; - - out[6] = a[6]; - out[7] = a[7]; - out[8] = a[8]; - return out; -}; - -/** - * Copies the values from a mat2d into a mat3 - * - * @param {mat3} out the receiving matrix - * @param {mat2d} a the matrix to copy - * @returns {mat3} out - **/ -mat3.fromMat2d = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = 0; - - out[3] = a[2]; - out[4] = a[3]; - out[5] = 0; - - out[6] = a[4]; - out[7] = a[5]; - out[8] = 1; - return out; -}; - -/** -* Calculates a 3x3 matrix from the given quaternion -* -* @param {mat3} out mat3 receiving operation result -* @param {quat} q Quaternion to create matrix from -* -* @returns {mat3} out -*/ -mat3.fromQuat = function (out, q) { - var x = q[0], y = q[1], z = q[2], w = q[3], - x2 = x + x, - y2 = y + y, - z2 = z + z, - - xx = x * x2, - yx = y * x2, - yy = y * y2, - zx = z * x2, - zy = z * y2, - zz = z * z2, - wx = w * x2, - wy = w * y2, - wz = w * z2; - - out[0] = 1 - yy - zz; - out[3] = yx - wz; - out[6] = zx + wy; - - out[1] = yx + wz; - out[4] = 1 - xx - zz; - out[7] = zy - wx; - - out[2] = zx - wy; - out[5] = zy + wx; - out[8] = 1 - xx - yy; - - return out; -}; - -/** -* Calculates a 3x3 normal matrix (transpose inverse) from the 4x4 matrix -* -* @param {mat3} out mat3 receiving operation result -* @param {mat4} a Mat4 to derive the normal matrix from -* -* @returns {mat3} out -*/ -mat3.normalFromMat4 = function (out, a) { - var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], - a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], - a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], - a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], - - b00 = a00 * a11 - a01 * a10, - b01 = a00 * a12 - a02 * a10, - b02 = a00 * a13 - a03 * a10, - b03 = a01 * a12 - a02 * a11, - b04 = a01 * a13 - a03 * a11, - b05 = a02 * a13 - a03 * a12, - b06 = a20 * a31 - a21 * a30, - b07 = a20 * a32 - a22 * a30, - b08 = a20 * a33 - a23 * a30, - b09 = a21 * a32 - a22 * a31, - b10 = a21 * a33 - a23 * a31, - b11 = a22 * a33 - a23 * a32, - - // Calculate the determinant - det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; - - if (!det) { - return null; - } - det = 1.0 / det; - - out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; - out[1] = (a12 * b08 - a10 * b11 - a13 * b07) * det; - out[2] = (a10 * b10 - a11 * b08 + a13 * b06) * det; - - out[3] = (a02 * b10 - a01 * b11 - a03 * b09) * det; - out[4] = (a00 * b11 - a02 * b08 + a03 * b07) * det; - out[5] = (a01 * b08 - a00 * b10 - a03 * b06) * det; - - out[6] = (a31 * b05 - a32 * b04 + a33 * b03) * det; - out[7] = (a32 * b02 - a30 * b05 - a33 * b01) * det; - out[8] = (a30 * b04 - a31 * b02 + a33 * b00) * det; - - return out; -}; - -/** - * Returns a string representation of a mat3 - * - * @param {mat3} mat matrix to represent as a string - * @returns {String} string representation of the matrix - */ -mat3.str = function (a) { - return 'mat3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + - a[3] + ', ' + a[4] + ', ' + a[5] + ', ' + - a[6] + ', ' + a[7] + ', ' + a[8] + ')'; -}; - -if(typeof(exports) !== 'undefined') { - exports.mat3 = mat3; -} -; -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -/** - * @class 4x4 Matrix - * @name mat4 - */ - -var mat4 = {}; - -/** - * Creates a new identity mat4 - * - * @returns {mat4} a new 4x4 matrix - */ -mat4.create = function() { - var out = new GLMAT_ARRAY_TYPE(16); - out[0] = 1; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 0; - out[5] = 1; - out[6] = 0; - out[7] = 0; - out[8] = 0; - out[9] = 0; - out[10] = 1; - out[11] = 0; - out[12] = 0; - out[13] = 0; - out[14] = 0; - out[15] = 1; - return out; -}; - -/** - * Creates a new mat4 initialized with values from an existing matrix - * - * @param {mat4} a matrix to clone - * @returns {mat4} a new 4x4 matrix - */ -mat4.clone = function(a) { - var out = new GLMAT_ARRAY_TYPE(16); - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - out[4] = a[4]; - out[5] = a[5]; - out[6] = a[6]; - out[7] = a[7]; - out[8] = a[8]; - out[9] = a[9]; - out[10] = a[10]; - out[11] = a[11]; - out[12] = a[12]; - out[13] = a[13]; - out[14] = a[14]; - out[15] = a[15]; - return out; -}; - -/** - * Copy the values from one mat4 to another - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the source matrix - * @returns {mat4} out - */ -mat4.copy = function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - out[4] = a[4]; - out[5] = a[5]; - out[6] = a[6]; - out[7] = a[7]; - out[8] = a[8]; - out[9] = a[9]; - out[10] = a[10]; - out[11] = a[11]; - out[12] = a[12]; - out[13] = a[13]; - out[14] = a[14]; - out[15] = a[15]; - return out; -}; - -/** - * Set a mat4 to the identity matrix - * - * @param {mat4} out the receiving matrix - * @returns {mat4} out - */ -mat4.identity = function(out) { - out[0] = 1; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 0; - out[5] = 1; - out[6] = 0; - out[7] = 0; - out[8] = 0; - out[9] = 0; - out[10] = 1; - out[11] = 0; - out[12] = 0; - out[13] = 0; - out[14] = 0; - out[15] = 1; - return out; -}; - -/** - * Transpose the values of a mat4 - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the source matrix - * @returns {mat4} out - */ -mat4.transpose = function(out, a) { - // If we are transposing ourselves we can skip a few steps but have to cache some values - if (out === a) { - var a01 = a[1], a02 = a[2], a03 = a[3], - a12 = a[6], a13 = a[7], - a23 = a[11]; - - out[1] = a[4]; - out[2] = a[8]; - out[3] = a[12]; - out[4] = a01; - out[6] = a[9]; - out[7] = a[13]; - out[8] = a02; - out[9] = a12; - out[11] = a[14]; - out[12] = a03; - out[13] = a13; - out[14] = a23; - } else { - out[0] = a[0]; - out[1] = a[4]; - out[2] = a[8]; - out[3] = a[12]; - out[4] = a[1]; - out[5] = a[5]; - out[6] = a[9]; - out[7] = a[13]; - out[8] = a[2]; - out[9] = a[6]; - out[10] = a[10]; - out[11] = a[14]; - out[12] = a[3]; - out[13] = a[7]; - out[14] = a[11]; - out[15] = a[15]; - } - - return out; -}; - -/** - * Inverts a mat4 - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the source matrix - * @returns {mat4} out - */ -mat4.invert = function(out, a) { - var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], - a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], - a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], - a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], - - b00 = a00 * a11 - a01 * a10, - b01 = a00 * a12 - a02 * a10, - b02 = a00 * a13 - a03 * a10, - b03 = a01 * a12 - a02 * a11, - b04 = a01 * a13 - a03 * a11, - b05 = a02 * a13 - a03 * a12, - b06 = a20 * a31 - a21 * a30, - b07 = a20 * a32 - a22 * a30, - b08 = a20 * a33 - a23 * a30, - b09 = a21 * a32 - a22 * a31, - b10 = a21 * a33 - a23 * a31, - b11 = a22 * a33 - a23 * a32, - - // Calculate the determinant - det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; - - if (!det) { - return null; - } - det = 1.0 / det; - - out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; - out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; - out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; - out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; - out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; - out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; - out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; - out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; - out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; - out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; - out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; - out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; - out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; - out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; - out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; - out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; - - return out; -}; - -/** - * Calculates the adjugate of a mat4 - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the source matrix - * @returns {mat4} out - */ -mat4.adjoint = function(out, a) { - var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], - a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], - a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], - a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; - - out[0] = (a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22)); - out[1] = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22)); - out[2] = (a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12)); - out[3] = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12)); - out[4] = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22)); - out[5] = (a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22)); - out[6] = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12)); - out[7] = (a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12)); - out[8] = (a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21)); - out[9] = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21)); - out[10] = (a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11)); - out[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11)); - out[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21)); - out[13] = (a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21)); - out[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11)); - out[15] = (a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11)); - return out; -}; - -/** - * Calculates the determinant of a mat4 - * - * @param {mat4} a the source matrix - * @returns {Number} determinant of a - */ -mat4.determinant = function (a) { - var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], - a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], - a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], - a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], - - b00 = a00 * a11 - a01 * a10, - b01 = a00 * a12 - a02 * a10, - b02 = a00 * a13 - a03 * a10, - b03 = a01 * a12 - a02 * a11, - b04 = a01 * a13 - a03 * a11, - b05 = a02 * a13 - a03 * a12, - b06 = a20 * a31 - a21 * a30, - b07 = a20 * a32 - a22 * a30, - b08 = a20 * a33 - a23 * a30, - b09 = a21 * a32 - a22 * a31, - b10 = a21 * a33 - a23 * a31, - b11 = a22 * a33 - a23 * a32; - - // Calculate the determinant - return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; -}; - -/** - * Multiplies two mat4's - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the first operand - * @param {mat4} b the second operand - * @returns {mat4} out - */ -mat4.multiply = function (out, a, b) { - var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], - a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], - a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], - a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; - - // Cache only the current line of the second matrix - var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; - out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; - out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; - out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; - out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; - - b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; - out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; - out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; - out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; - out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; - - b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; - out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; - out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; - out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; - out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; - - b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; - out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; - out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; - out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; - out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; - return out; -}; - -/** - * Alias for {@link mat4.multiply} - * @function - */ -mat4.mul = mat4.multiply; - -/** - * Translate a mat4 by the given vector - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the matrix to translate - * @param {vec3} v vector to translate by - * @returns {mat4} out - */ -mat4.translate = function (out, a, v) { - var x = v[0], y = v[1], z = v[2], - a00, a01, a02, a03, - a10, a11, a12, a13, - a20, a21, a22, a23, - a30, a31, a32, a33; - - a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; - a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; - a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; - a30 = a[12]; a31 = a[13]; a32 = a[14]; a33 = a[15]; - - out[0] = a00 + a03*x; - out[1] = a01 + a03*y; - out[2] = a02 + a03*z; - out[3] = a03; - - out[4] = a10 + a13*x; - out[5] = a11 + a13*y; - out[6] = a12 + a13*z; - out[7] = a13; - - out[8] = a20 + a23*x; - out[9] = a21 + a23*y; - out[10] = a22 + a23*z; - out[11] = a23; - out[12] = a30 + a33*x; - out[13] = a31 + a33*y; - out[14] = a32 + a33*z; - out[15] = a33; - - return out; -}; -/** - * Scales the mat4 by the dimensions in the given vec3 - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the matrix to scale - * @param {vec3} v the vec3 to scale the matrix by - * @returns {mat4} out - **/ -mat4.scale = function(out, a, v) { - var x = v[0], y = v[1], z = v[2]; - - out[0] = a[0] * x; - out[1] = a[1] * x; - out[2] = a[2] * x; - out[3] = a[3] * x; - out[4] = a[4] * y; - out[5] = a[5] * y; - out[6] = a[6] * y; - out[7] = a[7] * y; - out[8] = a[8] * z; - out[9] = a[9] * z; - out[10] = a[10] * z; - out[11] = a[11] * z; - out[12] = a[12]; - out[13] = a[13]; - out[14] = a[14]; - out[15] = a[15]; - return out; -}; - -/** - * Rotates a mat4 by the given angle - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the matrix to rotate - * @param {Number} rad the angle to rotate the matrix by - * @param {vec3} axis the axis to rotate around - * @returns {mat4} out - */ -mat4.rotate = function (out, a, rad, axis) { - var x = axis[0], y = axis[1], z = axis[2], - len = Math.sqrt(x * x + y * y + z * z), - s, c, t, - a00, a01, a02, a03, - a10, a11, a12, a13, - a20, a21, a22, a23, - b00, b01, b02, - b10, b11, b12, - b20, b21, b22; - - if (Math.abs(len) < GLMAT_EPSILON) { return null; } - - len = 1 / len; - x *= len; - y *= len; - z *= len; - - s = Math.sin(rad); - c = Math.cos(rad); - t = 1 - c; - - a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; - a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; - a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; - - // Construct the elements of the rotation matrix - b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; - b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; - b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; - - // Perform rotation-specific matrix multiplication - out[0] = a00 * b00 + a10 * b01 + a20 * b02; - out[1] = a01 * b00 + a11 * b01 + a21 * b02; - out[2] = a02 * b00 + a12 * b01 + a22 * b02; - out[3] = a03 * b00 + a13 * b01 + a23 * b02; - out[4] = a00 * b10 + a10 * b11 + a20 * b12; - out[5] = a01 * b10 + a11 * b11 + a21 * b12; - out[6] = a02 * b10 + a12 * b11 + a22 * b12; - out[7] = a03 * b10 + a13 * b11 + a23 * b12; - out[8] = a00 * b20 + a10 * b21 + a20 * b22; - out[9] = a01 * b20 + a11 * b21 + a21 * b22; - out[10] = a02 * b20 + a12 * b21 + a22 * b22; - out[11] = a03 * b20 + a13 * b21 + a23 * b22; - - if (a !== out) { // If the source and destination differ, copy the unchanged last row - out[12] = a[12]; - out[13] = a[13]; - out[14] = a[14]; - out[15] = a[15]; - } - return out; -}; - -/** - * Rotates a matrix by the given angle around the X axis - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the matrix to rotate - * @param {Number} rad the angle to rotate the matrix by - * @returns {mat4} out - */ -mat4.rotateX = function (out, a, rad) { - var s = Math.sin(rad), - c = Math.cos(rad), - a10 = a[4], - a11 = a[5], - a12 = a[6], - a13 = a[7], - a20 = a[8], - a21 = a[9], - a22 = a[10], - a23 = a[11]; - - if (a !== out) { // If the source and destination differ, copy the unchanged rows - out[0] = a[0]; - out[1] = a[1]; - out[2] = a[2]; - out[3] = a[3]; - out[12] = a[12]; - out[13] = a[13]; - out[14] = a[14]; - out[15] = a[15]; - } - - // Perform axis-specific matrix multiplication - out[4] = a10 * c + a20 * s; - out[5] = a11 * c + a21 * s; - out[6] = a12 * c + a22 * s; - out[7] = a13 * c + a23 * s; - out[8] = a20 * c - a10 * s; - out[9] = a21 * c - a11 * s; - out[10] = a22 * c - a12 * s; - out[11] = a23 * c - a13 * s; - return out; -}; - -/** - * Rotates a matrix by the given angle around the Y axis - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the matrix to rotate - * @param {Number} rad the angle to rotate the matrix by - * @returns {mat4} out - */ -mat4.rotateY = function (out, a, rad) { - var s = Math.sin(rad), - c = Math.cos(rad), - a00 = a[0], - a01 = a[1], - a02 = a[2], - a03 = a[3], - a20 = a[8], - a21 = a[9], - a22 = a[10], - a23 = a[11]; - - if (a !== out) { // If the source and destination differ, copy the unchanged rows - out[4] = a[4]; - out[5] = a[5]; - out[6] = a[6]; - out[7] = a[7]; - out[12] = a[12]; - out[13] = a[13]; - out[14] = a[14]; - out[15] = a[15]; - } - - // Perform axis-specific matrix multiplication - out[0] = a00 * c - a20 * s; - out[1] = a01 * c - a21 * s; - out[2] = a02 * c - a22 * s; - out[3] = a03 * c - a23 * s; - out[8] = a00 * s + a20 * c; - out[9] = a01 * s + a21 * c; - out[10] = a02 * s + a22 * c; - out[11] = a03 * s + a23 * c; - return out; -}; - -/** - * Rotates a matrix by the given angle around the Z axis - * - * @param {mat4} out the receiving matrix - * @param {mat4} a the matrix to rotate - * @param {Number} rad the angle to rotate the matrix by - * @returns {mat4} out - */ -mat4.rotateZ = function (out, a, rad) { - var s = Math.sin(rad), - c = Math.cos(rad), - a00 = a[0], - a01 = a[1], - a02 = a[2], - a03 = a[3], - a10 = a[4], - a11 = a[5], - a12 = a[6], - a13 = a[7]; - - if (a !== out) { // If the source and destination differ, copy the unchanged last row - out[8] = a[8]; - out[9] = a[9]; - out[10] = a[10]; - out[11] = a[11]; - out[12] = a[12]; - out[13] = a[13]; - out[14] = a[14]; - out[15] = a[15]; - } - - // Perform axis-specific matrix multiplication - out[0] = a00 * c + a10 * s; - out[1] = a01 * c + a11 * s; - out[2] = a02 * c + a12 * s; - out[3] = a03 * c + a13 * s; - out[4] = a10 * c - a00 * s; - out[5] = a11 * c - a01 * s; - out[6] = a12 * c - a02 * s; - out[7] = a13 * c - a03 * s; - return out; -}; - -/** - * Creates a matrix from a quaternion rotation and vector translation - * This is equivalent to (but much faster than): - * - * mat4.identity(dest); - * mat4.translate(dest, vec); - * var quatMat = mat4.create(); - * quat4.toMat4(quat, quatMat); - * mat4.multiply(dest, quatMat); - * - * @param {mat4} out mat4 receiving operation result - * @param {quat4} q Rotation quaternion - * @param {vec3} v Translation vector - * @returns {mat4} out - */ -mat4.fromRotationTranslation = function (out, q, v) { - // Quaternion math - var x = q[0], y = q[1], z = q[2], w = q[3], - x2 = x + x, - y2 = y + y, - z2 = z + z, - - xx = x * x2, - xy = x * y2, - xz = x * z2, - yy = y * y2, - yz = y * z2, - zz = z * z2, - wx = w * x2, - wy = w * y2, - wz = w * z2; - - out[0] = 1 - (yy + zz); - out[1] = xy + wz; - out[2] = xz - wy; - out[3] = 0; - out[4] = xy - wz; - out[5] = 1 - (xx + zz); - out[6] = yz + wx; - out[7] = 0; - out[8] = xz + wy; - out[9] = yz - wx; - out[10] = 1 - (xx + yy); - out[11] = 0; - out[12] = v[0]; - out[13] = v[1]; - out[14] = v[2]; - out[15] = 1; - - return out; -}; - -mat4.fromQuat = function (out, q) { - var x = q[0], y = q[1], z = q[2], w = q[3], - x2 = x + x, - y2 = y + y, - z2 = z + z, - - xx = x * x2, - yx = y * x2, - yy = y * y2, - zx = z * x2, - zy = z * y2, - zz = z * z2, - wx = w * x2, - wy = w * y2, - wz = w * z2; - - out[0] = 1 - yy - zz; - out[1] = yx + wz; - out[2] = zx - wy; - out[3] = 0; - - out[4] = yx - wz; - out[5] = 1 - xx - zz; - out[6] = zy + wx; - out[7] = 0; - - out[8] = zx + wy; - out[9] = zy - wx; - out[10] = 1 - xx - yy; - out[11] = 0; - - out[12] = 0; - out[13] = 0; - out[14] = 0; - out[15] = 1; - - return out; -}; - -/** - * Generates a frustum matrix with the given bounds - * - * @param {mat4} out mat4 frustum matrix will be written into - * @param {Number} left Left bound of the frustum - * @param {Number} right Right bound of the frustum - * @param {Number} bottom Bottom bound of the frustum - * @param {Number} top Top bound of the frustum - * @param {Number} near Near bound of the frustum - * @param {Number} far Far bound of the frustum - * @returns {mat4} out - */ -mat4.frustum = function (out, left, right, bottom, top, near, far) { - var rl = 1 / (right - left), - tb = 1 / (top - bottom), - nf = 1 / (near - far); - out[0] = (near * 2) * rl; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 0; - out[5] = (near * 2) * tb; - out[6] = 0; - out[7] = 0; - out[8] = (right + left) * rl; - out[9] = (top + bottom) * tb; - out[10] = (far + near) * nf; - out[11] = -1; - out[12] = 0; - out[13] = 0; - out[14] = (far * near * 2) * nf; - out[15] = 0; - return out; -}; - -/** - * Generates a perspective projection matrix with the given bounds - * - * @param {mat4} out mat4 frustum matrix will be written into - * @param {number} fovy Vertical field of view in radians - * @param {number} aspect Aspect ratio. typically viewport width/height - * @param {number} near Near bound of the frustum - * @param {number} far Far bound of the frustum - * @returns {mat4} out - */ -mat4.perspective = function (out, fovy, aspect, near, far) { - var f = 1.0 / Math.tan(fovy / 2), - nf = 1 / (near - far); - out[0] = f / aspect; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 0; - out[5] = f; - out[6] = 0; - out[7] = 0; - out[8] = 0; - out[9] = 0; - out[10] = (far + near) * nf; - out[11] = -1; - out[12] = 0; - out[13] = 0; - out[14] = (2 * far * near) * nf; - out[15] = 0; - return out; -}; - -/** - * Generates a orthogonal projection matrix with the given bounds - * - * @param {mat4} out mat4 frustum matrix will be written into - * @param {number} left Left bound of the frustum - * @param {number} right Right bound of the frustum - * @param {number} bottom Bottom bound of the frustum - * @param {number} top Top bound of the frustum - * @param {number} near Near bound of the frustum - * @param {number} far Far bound of the frustum - * @returns {mat4} out - */ -mat4.ortho = function (out, left, right, bottom, top, near, far) { - var lr = 1 / (left - right), - bt = 1 / (bottom - top), - nf = 1 / (near - far); - out[0] = -2 * lr; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 0; - out[5] = -2 * bt; - out[6] = 0; - out[7] = 0; - out[8] = 0; - out[9] = 0; - out[10] = 2 * nf; - out[11] = 0; - out[12] = (left + right) * lr; - out[13] = (top + bottom) * bt; - out[14] = (far + near) * nf; - out[15] = 1; - return out; -}; - -/** - * Generates a look-at matrix with the given eye position, focal point, and up axis - * - * @param {mat4} out mat4 frustum matrix will be written into - * @param {vec3} eye Position of the viewer - * @param {vec3} center Point the viewer is looking at - * @param {vec3} up vec3 pointing up - * @returns {mat4} out - */ -mat4.lookAt = function (out, eye, center, up) { - var x0, x1, x2, y0, y1, y2, z0, z1, z2, len, - eyex = eye[0], - eyey = eye[1], - eyez = eye[2], - upx = up[0], - upy = up[1], - upz = up[2], - centerx = center[0], - centery = center[1], - centerz = center[2]; - - if (Math.abs(eyex - centerx) < GLMAT_EPSILON && - Math.abs(eyey - centery) < GLMAT_EPSILON && - Math.abs(eyez - centerz) < GLMAT_EPSILON) { - return mat4.identity(out); - } - - z0 = eyex - centerx; - z1 = eyey - centery; - z2 = eyez - centerz; - - len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); - z0 *= len; - z1 *= len; - z2 *= len; - - 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; - } - - 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; - } - - out[0] = x0; - out[1] = y0; - out[2] = z0; - out[3] = 0; - out[4] = x1; - out[5] = y1; - out[6] = z1; - out[7] = 0; - out[8] = x2; - out[9] = y2; - out[10] = z2; - out[11] = 0; - out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); - out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); - out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); - out[15] = 1; - - return out; -}; - -/** - * Returns a string representation of a mat4 - * - * @param {mat4} mat matrix to represent as a string - * @returns {String} string representation of the matrix - */ -mat4.str = function (a) { - return 'mat4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ', ' + - a[4] + ', ' + a[5] + ', ' + a[6] + ', ' + a[7] + ', ' + - a[8] + ', ' + a[9] + ', ' + a[10] + ', ' + a[11] + ', ' + - a[12] + ', ' + a[13] + ', ' + a[14] + ', ' + a[15] + ')'; -}; - -if(typeof(exports) !== 'undefined') { - exports.mat4 = mat4; -} -; -/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -/** - * @class Quaternion - * @name quat - */ - -var quat = {}; - -/** - * Creates a new identity quat - * - * @returns {quat} a new quaternion - */ -quat.create = function() { - var out = new GLMAT_ARRAY_TYPE(4); - out[0] = 0; - out[1] = 0; - out[2] = 0; - out[3] = 1; - return out; -}; - -/** - * Sets a quaternion to represent the shortest rotation from one - * vector to another. - * - * Both vectors are assumed to be unit length. - * - * @param {quat} out the receiving quaternion. - * @param {vec3} a the initial vector - * @param {vec3} b the destination vector - * @returns {quat} out - */ -quat.rotationTo = (function() { - var tmpvec3 = vec3.create(); - var xUnitVec3 = vec3.fromValues(1,0,0); - var yUnitVec3 = vec3.fromValues(0,1,0); - - return function(out, a, b) { - var dot = vec3.dot(a, b); - if (dot < -0.999999) { - vec3.cross(tmpvec3, xUnitVec3, a); - if (vec3.length(tmpvec3) < 0.000001) - vec3.cross(tmpvec3, yUnitVec3, a); - vec3.normalize(tmpvec3, tmpvec3); - quat.setAxisAngle(out, tmpvec3, Math.PI); - return out; - } else if (dot > 0.999999) { - out[0] = 0; - out[1] = 0; - out[2] = 0; - out[3] = 1; - return out; - } else { - vec3.cross(tmpvec3, a, b); - out[0] = tmpvec3[0]; - out[1] = tmpvec3[1]; - out[2] = tmpvec3[2]; - out[3] = 1 + dot; - return quat.normalize(out, out); - } - }; -})(); - -/** - * Sets the specified quaternion with values corresponding to the given - * axes. Each axis is a vec3 and is expected to be unit length and - * perpendicular to all other specified axes. - * - * @param {vec3} view the vector representing the viewing direction - * @param {vec3} right the vector representing the local "right" direction - * @param {vec3} up the vector representing the local "up" direction - * @returns {quat} out - */ -quat.setAxes = (function() { - var matr = mat3.create(); - - return function(out, view, right, up) { - matr[0] = right[0]; - matr[3] = right[1]; - matr[6] = right[2]; - - matr[1] = up[0]; - matr[4] = up[1]; - matr[7] = up[2]; - - matr[2] = -view[0]; - matr[5] = -view[1]; - matr[8] = -view[2]; - - return quat.normalize(out, quat.fromMat3(out, matr)); - }; -})(); - -/** - * Creates a new quat initialized with values from an existing quaternion - * - * @param {quat} a quaternion to clone - * @returns {quat} a new quaternion - * @function - */ -quat.clone = vec4.clone; - -/** - * Creates a new quat initialized with the given values - * - * @param {Number} x X component - * @param {Number} y Y component - * @param {Number} z Z component - * @param {Number} w W component - * @returns {quat} a new quaternion - * @function - */ -quat.fromValues = vec4.fromValues; - -/** - * Copy the values from one quat to another - * - * @param {quat} out the receiving quaternion - * @param {quat} a the source quaternion - * @returns {quat} out - * @function - */ -quat.copy = vec4.copy; - -/** - * Set the components of a quat to the given values - * - * @param {quat} out the receiving quaternion - * @param {Number} x X component - * @param {Number} y Y component - * @param {Number} z Z component - * @param {Number} w W component - * @returns {quat} out - * @function - */ -quat.set = vec4.set; - -/** - * Set a quat to the identity quaternion - * - * @param {quat} out the receiving quaternion - * @returns {quat} out - */ -quat.identity = function(out) { - out[0] = 0; - out[1] = 0; - out[2] = 0; - out[3] = 1; - return out; -}; - -/** - * Sets a quat from the given angle and rotation axis, - * then returns it. - * - * @param {quat} out the receiving quaternion - * @param {vec3} axis the axis around which to rotate - * @param {Number} rad the angle in radians - * @returns {quat} out - **/ -quat.setAxisAngle = function(out, axis, rad) { - rad = rad * 0.5; - var s = Math.sin(rad); - out[0] = s * axis[0]; - out[1] = s * axis[1]; - out[2] = s * axis[2]; - out[3] = Math.cos(rad); - return out; -}; - -/** - * Adds two quat's - * - * @param {quat} out the receiving quaternion - * @param {quat} a the first operand - * @param {quat} b the second operand - * @returns {quat} out - * @function - */ -quat.add = vec4.add; - -/** - * Multiplies two quat's - * - * @param {quat} out the receiving quaternion - * @param {quat} a the first operand - * @param {quat} b the second operand - * @returns {quat} out - */ -quat.multiply = function(out, a, b) { - var ax = a[0], ay = a[1], az = a[2], aw = a[3], - bx = b[0], by = b[1], bz = b[2], bw = b[3]; - - out[0] = ax * bw + aw * bx + ay * bz - az * by; - out[1] = ay * bw + aw * by + az * bx - ax * bz; - out[2] = az * bw + aw * bz + ax * by - ay * bx; - out[3] = aw * bw - ax * bx - ay * by - az * bz; - return out; -}; - -/** - * Alias for {@link quat.multiply} - * @function - */ -quat.mul = quat.multiply; - -/** - * Scales a quat by a scalar number - * - * @param {quat} out the receiving vector - * @param {quat} a the vector to scale - * @param {Number} b amount to scale the vector by - * @returns {quat} out - * @function - */ -quat.scale = vec4.scale; - -/** - * Rotates a quaternion by the given angle about the X axis - * - * @param {quat} out quat receiving operation result - * @param {quat} a quat to rotate - * @param {number} rad angle (in radians) to rotate - * @returns {quat} out - */ -quat.rotateX = function (out, a, rad) { - rad *= 0.5; - - var ax = a[0], ay = a[1], az = a[2], aw = a[3], - bx = Math.sin(rad), bw = Math.cos(rad); - - out[0] = ax * bw + aw * bx; - out[1] = ay * bw + az * bx; - out[2] = az * bw - ay * bx; - out[3] = aw * bw - ax * bx; - return out; -}; - -/** - * Rotates a quaternion by the given angle about the Y axis - * - * @param {quat} out quat receiving operation result - * @param {quat} a quat to rotate - * @param {number} rad angle (in radians) to rotate - * @returns {quat} out - */ -quat.rotateY = function (out, a, rad) { - rad *= 0.5; - - var ax = a[0], ay = a[1], az = a[2], aw = a[3], - by = Math.sin(rad), bw = Math.cos(rad); - - out[0] = ax * bw - az * by; - out[1] = ay * bw + aw * by; - out[2] = az * bw + ax * by; - out[3] = aw * bw - ay * by; - return out; -}; - -/** - * Rotates a quaternion by the given angle about the Z axis - * - * @param {quat} out quat receiving operation result - * @param {quat} a quat to rotate - * @param {number} rad angle (in radians) to rotate - * @returns {quat} out - */ -quat.rotateZ = function (out, a, rad) { - rad *= 0.5; - - var ax = a[0], ay = a[1], az = a[2], aw = a[3], - bz = Math.sin(rad), bw = Math.cos(rad); - - out[0] = ax * bw + ay * bz; - out[1] = ay * bw - ax * bz; - out[2] = az * bw + aw * bz; - out[3] = aw * bw - az * bz; - return out; -}; - -/** - * Calculates the W component of a quat from the X, Y, and Z components. - * Assumes that quaternion is 1 unit in length. - * Any existing W component will be ignored. - * - * @param {quat} out the receiving quaternion - * @param {quat} a quat to calculate W component of - * @returns {quat} out - */ -quat.calculateW = function (out, a) { - var x = a[0], y = a[1], z = a[2]; - - out[0] = x; - out[1] = y; - out[2] = z; - out[3] = -Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z)); - return out; -}; - -/** - * Calculates the dot product of two quat's - * - * @param {quat} a the first operand - * @param {quat} b the second operand - * @returns {Number} dot product of a and b - * @function - */ -quat.dot = vec4.dot; - -/** - * Performs a linear interpolation between two quat's - * - * @param {quat} out the receiving quaternion - * @param {quat} a the first operand - * @param {quat} b the second operand - * @param {Number} t interpolation amount between the two inputs - * @returns {quat} out - * @function - */ -quat.lerp = vec4.lerp; - -/** - * Performs a spherical linear interpolation between two quat - * - * @param {quat} out the receiving quaternion - * @param {quat} a the first operand - * @param {quat} b the second operand - * @param {Number} t interpolation amount between the two inputs - * @returns {quat} out - */ -quat.slerp = function (out, a, b, t) { - // benchmarks: - // http://jsperf.com/quaternion-slerp-implementations - - var ax = a[0], ay = a[1], az = a[2], aw = a[3], - bx = b[0], by = b[1], bz = b[2], bw = b[3]; - - var omega, cosom, sinom, scale0, scale1; - - // calc cosine - cosom = ax * bx + ay * by + az * bz + aw * bw; - // adjust signs (if necessary) - if ( cosom < 0.0 ) { - cosom = -cosom; - bx = - bx; - by = - by; - bz = - bz; - bw = - bw; - } - // calculate coefficients - if ( (1.0 - cosom) > 0.000001 ) { - // standard case (slerp) - omega = Math.acos(cosom); - sinom = Math.sin(omega); - scale0 = Math.sin((1.0 - t) * omega) / sinom; - scale1 = Math.sin(t * omega) / sinom; - } else { - // "from" and "to" quaternions are very close - // ... so we can do a linear interpolation - scale0 = 1.0 - t; - scale1 = t; - } - // calculate final values - out[0] = scale0 * ax + scale1 * bx; - out[1] = scale0 * ay + scale1 * by; - out[2] = scale0 * az + scale1 * bz; - out[3] = scale0 * aw + scale1 * bw; - - return out; -}; - -/** - * Calculates the inverse of a quat - * - * @param {quat} out the receiving quaternion - * @param {quat} a quat to calculate inverse of - * @returns {quat} out - */ -quat.invert = function(out, a) { - var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], - dot = a0*a0 + a1*a1 + a2*a2 + a3*a3, - invDot = dot ? 1.0/dot : 0; - - // TODO: Would be faster to return [0,0,0,0] immediately if dot == 0 - - out[0] = -a0*invDot; - out[1] = -a1*invDot; - out[2] = -a2*invDot; - out[3] = a3*invDot; - return out; -}; - -/** - * Calculates the conjugate of a quat - * If the quaternion is normalized, this function is faster than quat.inverse and produces the same result. - * - * @param {quat} out the receiving quaternion - * @param {quat} a quat to calculate conjugate of - * @returns {quat} out - */ -quat.conjugate = function (out, a) { - out[0] = -a[0]; - out[1] = -a[1]; - out[2] = -a[2]; - out[3] = a[3]; - return out; -}; - -/** - * Calculates the length of a quat - * - * @param {quat} a vector to calculate length of - * @returns {Number} length of a - * @function - */ -quat.length = vec4.length; - -/** - * Alias for {@link quat.length} - * @function - */ -quat.len = quat.length; - -/** - * Calculates the squared length of a quat - * - * @param {quat} a vector to calculate squared length of - * @returns {Number} squared length of a - * @function - */ -quat.squaredLength = vec4.squaredLength; - -/** - * Alias for {@link quat.squaredLength} - * @function - */ -quat.sqrLen = quat.squaredLength; - -/** - * Normalize a quat - * - * @param {quat} out the receiving quaternion - * @param {quat} a quaternion to normalize - * @returns {quat} out - * @function - */ -quat.normalize = vec4.normalize; - -/** - * Creates a quaternion from the given 3x3 rotation matrix. - * - * NOTE: The resultant quaternion is not normalized, so you should be sure - * to renormalize the quaternion yourself where necessary. - * - * @param {quat} out the receiving quaternion - * @param {mat3} m rotation matrix - * @returns {quat} out - * @function - */ -quat.fromMat3 = function(out, m) { - // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes - // article "Quaternion Calculus and Fast Animation". - var fTrace = m[0] + m[4] + m[8]; - var fRoot; - - if ( fTrace > 0.0 ) { - // |w| > 1/2, may as well choose w > 1/2 - fRoot = Math.sqrt(fTrace + 1.0); // 2w - out[3] = 0.5 * fRoot; - fRoot = 0.5/fRoot; // 1/(4w) - out[0] = (m[7]-m[5])*fRoot; - out[1] = (m[2]-m[6])*fRoot; - out[2] = (m[3]-m[1])*fRoot; - } else { - // |w| <= 1/2 - var i = 0; - if ( m[4] > m[0] ) - i = 1; - if ( m[8] > m[i*3+i] ) - i = 2; - var j = (i+1)%3; - var k = (i+2)%3; - - fRoot = Math.sqrt(m[i*3+i]-m[j*3+j]-m[k*3+k] + 1.0); - out[i] = 0.5 * fRoot; - fRoot = 0.5 / fRoot; - out[3] = (m[k*3+j] - m[j*3+k]) * fRoot; - out[j] = (m[j*3+i] + m[i*3+j]) * fRoot; - out[k] = (m[k*3+i] + m[i*3+k]) * fRoot; - } - - return out; -}; - -/** - * Returns a string representation of a quatenion - * - * @param {quat} vec vector to represent as a string - * @returns {String} string representation of the vector - */ -quat.str = function (a) { - return 'quat(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; -}; - -if(typeof(exports) !== 'undefined') { - exports.quat = quat; -} -; - - - - - - - - - - - - - - })(shim.exports); -})(this); - -define('qtek/math/Vector3',['require','../dep/glmatrix'],function(require) { - - - - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - - /** - * @constructor - * @alias qtek.math.Vector3 - * @param {number} x - * @param {number} y - * @param {number} z - */ - var Vector3 = function(x, y, z) { - - x = x || 0; - y = y || 0; - z = z || 0; - - /** - * Storage of Vector3, read and write of x, y, z will change the values in _array - * All methods also operate on the _array instead of x, y, z components - * @type {Float32Array} - */ - this._array = vec3.fromValues(x, y, z); - - /** - * Dirty flag is used by the Node to determine - * if the matrix is updated to latest - * @type {boolean} - */ - this._dirty = true; - }; - - Vector3.prototype= { - - constructor : Vector3, - - /** - * Add b to self - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - add : function(b) { - vec3.add(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Set x, y and z components - * @param {number} x - * @param {number} y - * @param {number} z - * @return {qtek.math.Vector3} - */ - set : function(x, y, z) { - this._array[0] = x; - this._array[1] = y; - this._array[2] = z; - this._dirty = true; - return this; - }, - - /** - * Set x, y and z components from array - * @param {Float32Array|number[]} arr - * @return {qtek.math.Vector3} - */ - setArray : function(arr) { - this._array[0] = arr[0]; - this._array[1] = arr[1]; - this._array[2] = arr[2]; - - this._dirty = true; - return this; - }, - - /** - * Clone a new Vector3 - * @return {qtek.math.Vector3} - */ - clone : function() { - return new Vector3(this.x, this.y, this.z); - }, - - /** - * Copy from b - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - copy : function(b) { - vec3.copy(this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Cross product of self and b, written to a Vector3 out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - cross : function(a, b) { - vec3.cross(this._array, a._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Alias for distance - * @param {qtek.math.Vector3} b - * @return {number} - */ - dist : function(b) { - return vec3.dist(this._array, b._array); - }, - - /** - * Distance between self and b - * @param {qtek.math.Vector3} b - * @return {number} - */ - distance : function(b) { - return vec3.distance(this._array, b._array); - }, - - /** - * Alias for divide - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - div : function(b) { - vec3.div(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Divide self by b - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - divide : function(b) { - vec3.divide(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Dot product of self and b - * @param {qtek.math.Vector3} b - * @return {number} - */ - dot : function(b) { - return vec3.dot(this._array, b._array); - }, - - /** - * Alias of length - * @return {number} - */ - len : function() { - return vec3.len(this._array); - }, - - /** - * Calculate the length - * @return {number} - */ - length : function() { - return vec3.length(this._array); - }, - /** - * Linear interpolation between a and b - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @param {number} t - * @return {qtek.math.Vector3} - */ - lerp : function(a, b, t) { - vec3.lerp(this._array, a._array, b._array, t); - this._dirty = true; - return this; - }, - - /** - * Minimum of self and b - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - min : function(b) { - vec3.min(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Maximum of self and b - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - max : function(b) { - vec3.max(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Alias for multiply - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - mul : function(b) { - vec3.mul(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Mutiply self and b - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - multiply : function(b) { - vec3.multiply(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Negate self - * @return {qtek.math.Vector3} - */ - negate : function() { - vec3.negate(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Normalize self - * @return {qtek.math.Vector3} - */ - normalize : function() { - vec3.normalize(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Generate random x, y, z components with a given scale - * @param {number} scale - * @return {qtek.math.Vector3} - */ - random : function(scale) { - vec3.random(this._array, scale); - this._dirty = true; - return this; - }, - - /** - * Scale self - * @param {number} scale - * @return {qtek.math.Vector3} - */ - scale : function(s) { - vec3.scale(this._array, this._array, s); - this._dirty = true; - return this; - }, - - /** - * Scale b and add to self - * @param {qtek.math.Vector3} b - * @param {number} scale - * @return {qtek.math.Vector3} - */ - scaleAndAdd : function(b, s) { - vec3.scaleAndAdd(this._array, this._array, b._array, s); - this._dirty = true; - return this; - }, - - /** - * Alias for squaredDistance - * @param {qtek.math.Vector3} b - * @return {number} - */ - sqrDist : function(b) { - return vec3.sqrDist(this._array, b._array); - }, - - /** - * Squared distance between self and b - * @param {qtek.math.Vector3} b - * @return {number} - */ - squaredDistance : function(b) { - return vec3.squaredDistance(this._array, b._array); - }, - - /** - * Alias for squaredLength - * @return {number} - */ - sqrLen : function() { - return vec3.sqrLen(this._array); - }, - - /** - * Squared length of self - * @return {number} - */ - squaredLength : function() { - return vec3.squaredLength(this._array); - }, - - /** - * Alias for subtract - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - sub : function(b) { - vec3.sub(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Subtract b from self - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - subtract : function(b) { - vec3.subtract(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Transform self with a Matrix3 m - * @param {qtek.math.Matrix3} m - * @return {qtek.math.Vector3} - */ - transformMat3 : function(m) { - vec3.transformMat3(this._array, this._array, m._array); - this._dirty = true; - return this; - }, - - /** - * Transform self with a Matrix4 m - * @param {qtek.math.Matrix4} m - * @return {qtek.math.Vector3} - */ - transformMat4 : function(m) { - vec3.transformMat4(this._array, this._array, m._array); - this._dirty = true; - return this; - }, - /** - * Transform self with a Quaternion q - * @param {qtek.math.Quaternion} q - * @return {qtek.math.Vector3} - */ - transformQuat : function(q) { - vec3.transformQuat(this._array, this._array, q._array); - this._dirty = true; - return this; - }, - - /** - * Trasnform self into projection space with m - * @param {qtek.math.Matrix4} m - * @return {qtek.math.Vector3} - */ - applyProjection : function(m) { - var v = this._array; - m = m._array; - - // Perspective projection - if (m[15] === 0) { - var w = -1 / v[2]; - v[0] = m[0] * v[0] * w; - v[1] = m[5] * v[1] * w; - v[2] = (m[10] * v[2] + m[14]) * w; - } else { - v[0] = m[0] * v[0] + m[12]; - v[1] = m[5] * v[1] + m[13]; - v[2] = m[10] * v[2] + m[14]; - } - this._dirty = true; - - return this; - }, - - eulerFromQuaternion : function(q, order) { - Vector3.eulerFromQuaternion(this, q, order); - }, - - toString : function() { - return '[' + Array.prototype.join.call(this._array, ',') + ']'; - }, - }; - - // Getter and Setter - if (Object.defineProperty) { - - var proto = Vector3.prototype; - /** - * @name x - * @type {number} - * @memberOf qtek.math.Vector3 - * @instance - */ - Object.defineProperty(proto, 'x', { - get: function () { - return this._array[0]; - }, - set: function (value) { - this._array[0] = value; - this._dirty = true; - } - }); - - /** - * @name y - * @type {number} - * @memberOf qtek.math.Vector3 - * @instance - */ - Object.defineProperty(proto, 'y', { - get: function () { - return this._array[1]; - }, - set: function (value) { - this._array[1] = value; - this._dirty = true; - } - }); - - /** - * @name z - * @type {number} - * @memberOf qtek.math.Vector3 - * @instance - */ - Object.defineProperty(proto, 'z', { - get: function () { - return this._array[2]; - }, - set: function (value) { - this._array[2] = value; - this._dirty = true; - } - }); - } - - - // Supply methods that are not in place - - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - Vector3.add = function(out, a, b) { - vec3.add(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector3} out - * @param {number} x - * @param {number} y - * @param {number} z - * @return {qtek.math.Vector3} - */ - Vector3.set = function(out, x, y, z) { - vec3.set(out._array, x, y, z); - out._dirty = true; - }; - - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - Vector3.copy = function(out, b) { - vec3.copy(out._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - Vector3.cross = function(out, a, b) { - vec3.cross(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {number} - */ - Vector3.dist = function(a, b) { - return vec3.distance(a._array, b._array); - }; - - /** - * @method - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {number} - */ - Vector3.distance = Vector3.dist; - - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - Vector3.div = function(out, a, b) { - vec3.divide(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @method - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - Vector3.divide = Vector3.div; - - /** - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {number} - */ - Vector3.dot = function(a, b) { - return vec3.dot(a._array, b._array); - }; - - /** - * @param {qtek.math.Vector3} a - * @return {number} - */ - Vector3.len = function(b) { - return vec3.length(b._array); - }; - - // Vector3.length = Vector3.len; - - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @param {number} t - * @return {qtek.math.Vector3} - */ - Vector3.lerp = function(out, a, b, t) { - vec3.lerp(out._array, a._array, b._array, t); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - Vector3.min = function(out, a, b) { - vec3.min(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - Vector3.max = function(out, a, b) { - vec3.max(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - Vector3.mul = function(out, a, b) { - vec3.multiply(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - /** - * @method - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - Vector3.multiply = Vector3.mul; - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @return {qtek.math.Vector3} - */ - Vector3.negate = function(out, a) { - vec3.negate(out._array, a._array); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @return {qtek.math.Vector3} - */ - Vector3.normalize = function(out, a) { - vec3.normalize(out._array, a._array); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector3} out - * @param {number} scale - * @return {qtek.math.Vector3} - */ - Vector3.random = function(out, scale) { - vec3.random(out._array, scale); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {number} scale - * @return {qtek.math.Vector3} - */ - Vector3.scale = function(out, a, scale) { - vec3.scale(out._array, a._array, scale); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @param {number} scale - * @return {qtek.math.Vector3} - */ - Vector3.scaleAndAdd = function(out, a, b, scale) { - vec3.scaleAndAdd(out._array, a._array, b._array, scale); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {number} - */ - Vector3.sqrDist = function(a, b) { - return vec3.sqrDist(a._array, b._array); - }; - /** - * @method - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {number} - */ - Vector3.squaredDistance = Vector3.sqrDist; - /** - * @param {qtek.math.Vector3} a - * @return {number} - */ - Vector3.sqrLen = function(a) { - return vec3.sqrLen(a._array); - }; - /** - * @method - * @param {qtek.math.Vector3} a - * @return {number} - */ - Vector3.squaredLength = Vector3.sqrLen; - - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - Vector3.sub = function(out, a, b) { - vec3.subtract(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - /** - * @method - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Vector3} - */ - Vector3.subtract = Vector3.sub; - - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {Matrix3} m - * @return {qtek.math.Vector3} - */ - Vector3.transformMat3 = function(out, a, m) { - vec3.transformMat3(out._array, a._array, m._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Matrix4} m - * @return {qtek.math.Vector3} - */ - Vector3.transformMat4 = function(out, a, m) { - vec3.transformMat4(out._array, a._array, m._array); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Quaternion} q - * @return {qtek.math.Vector3} - */ - Vector3.transformQuat = function(out, a, q) { - vec3.transformQuat(out._array, a._array, q._array); - out._dirty = true; - return out; - }; - - function clamp(val, min, max) { - return val < min ? min : (val > max ? max : val); - }; - /** - * Convert quaternion to euler angle - * Quaternion must be normalized - * From three.js - */ - Vector3.eulerFromQuaternion = function (v, q, order) { - v = v._array; - q = q._array; - var x = q[0], y = q[1], z = q[2], w = q[3]; - var x2 = x * x; - var y2 = y * y; - var z2 = z * z; - var w2 = w * w; - var atan2 = Math.atan2; - var asin = Math.asin; - switch (order && order.toUpperCase()) { - case 'YXZ': - v[0] = asin(clamp(2 * (x * w - y * z), - 1, 1)); - v[1] = atan2(2 * (x * z + y * w), (w2 - x2 - y2 + z2)); - v[2] = atan2(2 * (x * y + z * w), (w2 - x2 + y2 - z2)); - break; - case 'ZXY': - v[0] = asin(clamp(2 * (x * w + y * z), - 1, 1)); - v[1] = atan2(2 * (y * w - z * x), (w2 - x2 - y2 + z2)); - v[2] = atan2(2 * (z * w - x * y), (w2 - x2 + y2 - z2)); - break; - case 'ZYX': - v[0] = atan2(2 * (x * w + z * y), (w2 - x2 - y2 + z2)); - v[1] = asin(clamp(2 * (y * w - x * z), - 1, 1)); - v[2] = atan2(2 * (x * y + z * w), (w2 + x2 - y2 - z2)); - break; - case 'YZX': - v[0] = atan2(2 * (x * w - z * y), (w2 - x2 + y2 - z2)); - v[1] = atan2(2 * (y * w - x * z), (w2 + x2 - y2 - z2)); - v[2] = asin(clamp(2 * (x * y + z * w), - 1, 1)); - break; - case 'XZY': - v[0] = atan2(2 * (x * w + y * z), (w2 - x2 + y2 - z2)); - v[1] = atan2(2 * (x * z + y * w), (w2 + x2 - y2 - z2)); - v[2] = asin(clamp(2 * (z * w - x * y), - 1, 1)); - break; - case 'XYZ': - default: - v[0] = atan2(2 * (x * w - y * z), (w2 - x2 - y2 + z2)); - v[1] = asin(clamp(2 * (x * z + y * w), - 1, 1)); - v[2] = atan2(2 * (z * w - x * y), (w2 + x2 - y2 - z2)); - break; - } - v._dirty = true; - return v; - }; - - /** - * @type {qtek.math.Vector3} - */ - Vector3.POSITIVE_X = new Vector3(1, 0, 0); - /** - * @type {qtek.math.Vector3} - */ - Vector3.NEGATIVE_X = new Vector3(-1, 0, 0); - /** - * @type {qtek.math.Vector3} - */ - Vector3.POSITIVE_Y = new Vector3(0, 1, 0); - /** - * @type {qtek.math.Vector3} - */ - Vector3.NEGATIVE_Y = new Vector3(0, -1, 0); - /** - * @type {qtek.math.Vector3} - */ - Vector3.POSITIVE_Z = new Vector3(0, 0, 1); - /** - * @type {qtek.math.Vector3} - */ - Vector3.NEGATIVE_Z = new Vector3(0, 0, -1); - /** - * @type {qtek.math.Vector3} - */ - Vector3.UP = new Vector3(0, 1, 0); - /** - * @type {qtek.math.Vector3} - */ - Vector3.ZERO = new Vector3(0, 0, 0); - - return Vector3; -}); -define('qtek/math/Quaternion',['require','../dep/glmatrix'],function(require) { - - - - var glMatrix = require('../dep/glmatrix'); - var quat = glMatrix.quat; - - /** - * @constructor - * @alias qtek.math.Quaternion - * @param {number} x - * @param {number} y - * @param {number} z - * @param {number} w - */ - var Quaternion = function(x, y, z, w) { - - x = x || 0; - y = y || 0; - z = z || 0; - w = w === undefined ? 1 : w; - - /** - * Storage of Quaternion, read and write of x, y, z, w will change the values in _array - * All methods also operate on the _array instead of x, y, z, w components - * @type {Float32Array} - */ - this._array = quat.fromValues(x, y, z, w); - - /** - * Dirty flag is used by the Node to determine - * if the matrix is updated to latest - * @type {boolean} - */ - this._dirty = true; - }; - - Quaternion.prototype = { - - constructor: Quaternion, - - /** - * Add b to self - * @param {qtek.math.Quaternion} b - * @return {qtek.math.Quaternion} - */ - add: function(b) { - quat.add( this._array, this._array, b._array ); - this._dirty = true; - return this; - }, - - /** - * Calculate the w component from x, y, z component - * @return {qtek.math.Quaternion} - */ - calculateW: function() { - quat.calculateW(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Set x, y and z components - * @param {number} x - * @param {number} y - * @param {number} z - * @param {number} w - * @return {qtek.math.Quaternion} - */ - set: function(x, y, z, w) { - this._array[0] = x; - this._array[1] = y; - this._array[2] = z; - this._array[3] = w; - this._dirty = true; - return this; - }, - - /** - * Set x, y, z and w components from array - * @param {Float32Array|number[]} arr - * @return {qtek.math.Quaternion} - */ - setArray: function(arr) { - this._array[0] = arr[0]; - this._array[1] = arr[1]; - this._array[2] = arr[2]; - this._array[3] = arr[3]; - - this._dirty = true; - return this; - }, - - /** - * Clone a new Quaternion - * @return {qtek.math.Quaternion} - */ - clone: function() { - return new Quaternion( this.x, this.y, this.z, this.w ); - }, - - /** - * Calculates the conjugate of self If the quaternion is normalized, - * this function is faster than invert and produces the same result. - * - * @return {qtek.math.Quaternion} - */ - conjugate: function() { - quat.conjugate(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Copy from b - * @param {qtek.math.Quaternion} b - * @return {qtek.math.Quaternion} - */ - copy: function(b) { - quat.copy(this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Dot product of self and b - * @param {qtek.math.Quaternion} b - * @return {number} - */ - dot: function(b) { - return quat.dot(this._array, b._array); - }, - - /** - * Set from the given 3x3 rotation matrix - * @param {qtek.math.Matrix3} m - * @return {qtek.math.Quaternion} - */ - fromMat3: function(m) { - quat.fromMat3(this._array, m._array); - this._dirty = true; - return this; - }, - - /** - * Set from the given 4x4 rotation matrix - * The 4th column and 4th row will be droped - * @param {qtek.math.Matrix4} m - * @return {qtek.math.Quaternion} - */ - fromMat4: (function() { - var mat3 = glMatrix.mat3; - var m3 = mat3.create(); - return function(m) { - mat3.fromMat4(m3, m._array); - // TODO Not like mat4, mat3 in glmatrix seems to be row-based - mat3.transpose(m3, m3); - quat.fromMat3(this._array, m3); - this._dirty = true; - return this; - }; - })(), - - /** - * Set to identity quaternion - * @return {qtek.math.Quaternion} - */ - identity: function() { - quat.identity(this._array); - this._dirty = true; - return this; - }, - /** - * Invert self - * @return {qtek.math.Quaternion} - */ - invert: function() { - quat.invert(this._array, this._array); - this._dirty = true; - return this; - }, - /** - * Alias of length - * @return {number} - */ - len: function() { - return quat.len(this._array); - }, - - /** - * Calculate the length - * @return {number} - */ - length: function() { - return quat.length(this._array); - }, - - /** - * Linear interpolation between a and b - * @param {qtek.math.Quaternion} a - * @param {qtek.math.Quaternion} b - * @param {number} t - * @return {qtek.math.Quaternion} - */ - lerp: function(a, b, t) { - quat.lerp(this._array, a._array, b._array, t); - this._dirty = true; - return this; - }, - - /** - * Alias for multiply - * @param {qtek.math.Quaternion} b - * @return {qtek.math.Quaternion} - */ - mul: function(b) { - quat.mul(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Alias for multiplyLeft - * @param {qtek.math.Quaternion} a - * @return {qtek.math.Quaternion} - */ - mulLeft: function(a) { - quat.multiply(this._array, a._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Mutiply self and b - * @param {qtek.math.Quaternion} b - * @return {qtek.math.Quaternion} - */ - multiply: function(b) { - quat.multiply(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Mutiply a and self - * Quaternion mutiply is not commutative, so the result of mutiplyLeft is different with multiply. - * @param {qtek.math.Quaternion} a - * @return {qtek.math.Quaternion} - */ - multiplyLeft: function(a) { - quat.multiply(this._array, a._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Normalize self - * @return {qtek.math.Quaternion} - */ - normalize: function() { - quat.normalize(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Rotate self by a given radian about X axis - * @param {number} rad - * @return {qtek.math.Quaternion} - */ - rotateX: function(rad) { - quat.rotateX(this._array, this._array, rad); - this._dirty = true; - return this; - }, - - /** - * Rotate self by a given radian about Y axis - * @param {number} rad - * @return {qtek.math.Quaternion} - */ - rotateY: function(rad) { - quat.rotateY(this._array, this._array, rad); - this._dirty = true; - return this; - }, - - /** - * Rotate self by a given radian about Z axis - * @param {number} rad - * @return {qtek.math.Quaternion} - */ - rotateZ: function(rad) { - quat.rotateZ(this._array, this._array, rad); - this._dirty = true; - return this; - }, - - /** - * Sets self to represent the shortest rotation from Vector3 a to Vector3 b. - * a and b needs to be normalized - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Quaternion} - */ - rotationTo: function(a, b) { - quat.rotationTo(this._array, a._array, b._array); - this._dirty = true; - return this; - }, - /** - * Sets self with values corresponding to the given axes - * @param {qtek.math.Vector3} view - * @param {qtek.math.Vector3} right - * @param {qtek.math.Vector3} up - * @return {qtek.math.Quaternion} - */ - setAxes: function(view, right, up) { - quat.setAxes(this._array, view._array, right._array, up._array); - this._dirty = true; - return this; - }, - - /** - * Sets self with a rotation axis and rotation angle - * @param {qtek.math.Vector3} axis - * @param {number} rad - * @return {qtek.math.Quaternion} - */ - setAxisAngle: function(axis, rad) { - quat.setAxisAngle(this._array, axis._array, rad); - this._dirty = true; - return this; - }, - /** - * Perform spherical linear interpolation between a and b - * @param {qtek.math.Quaternion} a - * @param {qtek.math.Quaternion} b - * @param {number} t - * @return {qtek.math.Quaternion} - */ - slerp: function(a, b, t) { - quat.slerp(this._array, a._array, b._array, t); - this._dirty = true; - return this; - }, - - /** - * Alias for squaredLength - * @return {number} - */ - sqrLen: function() { - return quat.sqrLen(this._array); - }, - - /** - * Squared length of self - * @return {number} - */ - squaredLength: function() { - return quat.squaredLength(this._array); - }, - - // Set quaternion from euler angle - setFromEuler: function(v) { - - }, - - toString: function() { - return '[' + Array.prototype.join.call(this._array, ',') + ']'; - } - }; - - // Getter and Setter - if (Object.defineProperty) { - - var proto = Quaternion.prototype; - /** - * @name x - * @type {number} - * @memberOf qtek.math.Quaternion - * @instance - */ - Object.defineProperty(proto, 'x', { - get: function () { - return this._array[0]; - }, - set: function (value) { - this._array[0] = value; - this._dirty = true; - } - }); - - /** - * @name y - * @type {number} - * @memberOf qtek.math.Quaternion - * @instance - */ - Object.defineProperty(proto, 'y', { - get: function () { - return this._array[1]; - }, - set: function (value) { - this._array[1] = value; - this._dirty = true; - } - }); - - /** - * @name z - * @type {number} - * @memberOf qtek.math.Quaternion - * @instance - */ - Object.defineProperty(proto, 'z', { - get: function () { - return this._array[2]; - }, - set: function (value) { - this._array[2] = value; - this._dirty = true; - } - }); - - /** - * @name w - * @type {number} - * @memberOf qtek.math.Quaternion - * @instance - */ - Object.defineProperty(proto, 'w', { - get: function () { - return this._array[3]; - }, - set: function (value) { - this._array[3] = value; - this._dirty = true; - } - }); - } - - // Supply methods that are not in place - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @param {qtek.math.Quaternion} b - * @return {qtek.math.Quaternion} - */ - Quaternion.add = function(out, a, b) { - quat.add(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {number} x - * @param {number} y - * @param {number} z - * @param {number} w - * @return {qtek.math.Quaternion} - */ - Quaternion.set = function(out, x, y, z, w) { - quat.set(out._array, x, y, z, w); - out._dirty = true; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} b - * @return {qtek.math.Quaternion} - */ - Quaternion.copy = function(out, b) { - quat.copy(out._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @return {qtek.math.Quaternion} - */ - Quaternion.calculateW = function(out, a) { - quat.calculateW(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @return {qtek.math.Quaternion} - */ - Quaternion.conjugate = function(out, a) { - quat.conjugate(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @return {qtek.math.Quaternion} - */ - Quaternion.identity = function(out) { - quat.identity(out._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @return {qtek.math.Quaternion} - */ - Quaternion.invert = function(out, a) { - quat.invert(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} a - * @param {qtek.math.Quaternion} b - * @return {number} - */ - Quaternion.dot = function(a, b) { - return quat.dot(a._array, b._array); - }; - - /** - * @param {qtek.math.Quaternion} a - * @return {number} - */ - Quaternion.len = function(a) { - return quat.length(a._array); - }; - - // Quaternion.length = Quaternion.len; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @param {qtek.math.Quaternion} b - * @param {number} t - * @return {qtek.math.Quaternion} - */ - Quaternion.lerp = function(out, a, b, t) { - quat.lerp(out._array, a._array, b._array, t); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @param {qtek.math.Quaternion} b - * @param {number} t - * @return {qtek.math.Quaternion} - */ - Quaternion.slerp = function(out, a, b, t) { - quat.slerp(out._array, a._array, b._array, t); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @param {qtek.math.Quaternion} b - * @return {qtek.math.Quaternion} - */ - Quaternion.mul = function(out, a, b) { - quat.multiply(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @method - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @param {qtek.math.Quaternion} b - * @return {qtek.math.Quaternion} - */ - Quaternion.multiply = Quaternion.mul; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @param {number} rad - * @return {qtek.math.Quaternion} - */ - Quaternion.rotateX = function(out, a, rad) { - quat.rotateX(out._array, a._array, rad); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @param {number} rad - * @return {qtek.math.Quaternion} - */ - Quaternion.rotateY = function(out, a, rad) { - quat.rotateY(out._array, a._array, rad); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @param {number} rad - * @return {qtek.math.Quaternion} - */ - Quaternion.rotateZ = function(out, a, rad) { - quat.rotateZ(out._array, a._array, rad); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Vector3} axis - * @param {number} rad - * @return {qtek.math.Quaternion} - */ - Quaternion.setAxisAngle = function(out, axis, rad) { - quat.setAxisAngle(out._array, axis._array, rad); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Quaternion} a - * @return {qtek.math.Quaternion} - */ - Quaternion.normalize = function(out, a) { - quat.normalize(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} a - * @return {number} - */ - Quaternion.sqrLen = function(a) { - return quat.sqrLen(a._array); - }; - - /** - * @method - * @param {qtek.math.Quaternion} a - * @return {number} - */ - Quaternion.squaredLength = Quaternion.sqrLen; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Matrix3} m - * @return {qtek.math.Quaternion} - */ - Quaternion.fromMat3 = function(out, m) { - quat.fromMat3(out._array, m._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Vector3} view - * @param {qtek.math.Vector3} right - * @param {qtek.math.Vector3} up - * @return {qtek.math.Quaternion} - */ - Quaternion.setAxes = function(out, view, right, up) { - quat.setAxes(out._array, view._array, right._array, up._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Quaternion} out - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @return {qtek.math.Quaternion} - */ - Quaternion.rotationTo = function(out, a, b) { - quat.rotationTo(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - return Quaternion; -}); -define('qtek/math/Matrix4',['require','../dep/glmatrix','./Vector3'],function(require) { - - - - var glMatrix = require('../dep/glmatrix'); - var Vector3 = require('./Vector3'); - var mat4 = glMatrix.mat4; - var vec3 = glMatrix.vec3; - var mat3 = glMatrix.mat3; - var quat = glMatrix.quat; - - function makeProperty(n) { - return { - set: function(value) { - this._array[n] = value; - this._dirty = true; - }, - get: function() { - return this._array[n]; - } - }; - } - - /** - * @constructor - * @alias qtek.math.Matrix4 - */ - var Matrix4 = function() { - - this._axisX = new Vector3(); - this._axisY = new Vector3(); - this._axisZ = new Vector3(); - - /** - * Storage of Matrix4 - * @type {Float32Array} - */ - this._array = mat4.create(); - - /** - * @type {boolean} - */ - this._dirty = true; - }; - - Matrix4.prototype = { - - constructor: Matrix4, - - /** - * Calculate the adjugate of self, in-place - * @return {qtek.math.Matrix4} - */ - adjoint: function() { - mat4.adjoint(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Clone a new Matrix4 - * @return {qtek.math.Matrix4} - */ - clone: function() { - return (new Matrix4()).copy(this); - }, - - /** - * Copy from b - * @param {qtek.math.Matrix4} b - * @return {qtek.math.Matrix4} - */ - copy: function(a) { - mat4.copy(this._array, a._array); - this._dirty = true; - return this; - }, - - /** - * Calculate matrix determinant - * @return {number} - */ - determinant: function() { - return mat4.determinant(this._array); - }, - - /** - * Set upper 3x3 part from quaternion - * @param {qtek.math.Quaternion} q - * @return {qtek.math.Matrix4} - */ - fromQuat: function(q) { - mat4.fromQuat(this._array, q._array); - this._dirty = true; - return this; - }, - - /** - * Set from a quaternion rotation and a vector translation - * @param {qtek.math.Quaternion} q - * @param {qtek.math.Vector3} v - * @return {qtek.math.Matrix4} - */ - fromRotationTranslation: function(q, v) { - mat4.fromRotationTranslation(this._array, q._array, v._array); - this._dirty = true; - return this; - }, - - /** - * Set from Matrix2d, it is used when converting a 2d shape to 3d space. - * In 3d space it is equivalent to ranslate on xy plane and rotate about z axis - * @param {qtek.math.Matrix2d} m2d - * @return {qtek.math.Matrix4} - */ - fromMat2d: function(m2d) { - Matrix4.fromMat2d(this, m2d); - return this; - }, - - /** - * Set from frustum bounds - * @param {number} left - * @param {number} right - * @param {number} bottom - * @param {number} top - * @param {number} near - * @param {number} far - * @return {qtek.math.Matrix4} - */ - frustum: function(left, right, bottom, top, near, far) { - mat4.frustum(this._array, left, right, bottom, top, near, far); - this._dirty = true; - return this; - }, - - /** - * Set to a identity matrix - * @return {qtek.math.Matrix4} - */ - identity: function() { - mat4.identity(this._array); - this._dirty = true; - return this; - }, - - /** - * Invert self - * @return {qtek.math.Matrix4} - */ - invert: function() { - mat4.invert(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Set as a matrix with the given eye position, focal point, and up axis - * @param {qtek.math.Vector3} eye - * @param {qtek.math.Vector3} center - * @param {qtek.math.Vector3} up - * @return {qtek.math.Matrix4} - */ - lookAt: function(eye, center, up) { - mat4.lookAt(this._array, eye._array, center._array, up._array); - this._dirty = true; - return this; - }, - - /** - * Alias for mutiply - * @param {qtek.math.Matrix4} b - * @return {qtek.math.Matrix4} - */ - mul: function(b) { - mat4.mul(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Alias for multiplyLeft - * @param {qtek.math.Matrix4} a - * @return {qtek.math.Matrix4} - */ - mulLeft: function(a) { - mat4.mul(this._array, a._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Multiply self and b - * @param {qtek.math.Matrix4} b - * @return {qtek.math.Matrix4} - */ - multiply: function(b) { - mat4.multiply(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Multiply a and self, a is on the left - * @param {qtek.math.Matrix3} a - * @return {qtek.math.Matrix3} - */ - multiplyLeft: function(a) { - mat4.multiply(this._array, a._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Set as a orthographic projection matrix - * @param {number} left - * @param {number} right - * @param {number} bottom - * @param {number} top - * @param {number} near - * @param {number} far - * @return {qtek.math.Matrix4} - */ - ortho: function(left, right, bottom, top, near, far) { - mat4.ortho(this._array, left, right, bottom, top, near, far); - this._dirty = true; - return this; - }, - /** - * Set as a perspective projection matrix - * @param {number} fovy - * @param {number} aspect - * @param {number} near - * @param {number} far - * @return {qtek.math.Matrix4} - */ - perspective: function(fovy, aspect, near, far) { - mat4.perspective(this._array, fovy, aspect, near, far); - this._dirty = true; - return this; - }, - - /** - * Rotate self by rad about axis - * @param {number} rad - * @param {qtek.math.Vector3} axis - * @return {qtek.math.Matrix4} - */ - rotate: function(rad, axis) { - mat4.rotate(this._array, this._array, rad, axis._array); - this._dirty = true; - return this; - }, - - /** - * Rotate self by a given radian about X axis - * @param {number} rad - * @return {qtek.math.Matrix4} - */ - rotateX: function(rad) { - mat4.rotateX(this._array, this._array, rad); - this._dirty = true; - return this; - }, - - /** - * Rotate self by a given radian about Y axis - * @param {number} rad - * @return {qtek.math.Matrix4} - */ - rotateY: function(rad) { - mat4.rotateY(this._array, this._array, rad); - this._dirty = true; - return this; - }, - - /** - * Rotate self by a given radian about Z axis - * @param {number} rad - * @return {qtek.math.Matrix4} - */ - rotateZ: function(rad) { - mat4.rotateZ(this._array, this._array, rad); - this._dirty = true; - return this; - }, - - /** - * Scale self by s - * @param {qtek.math.Vector3} s - * @return {qtek.math.Matrix4} - */ - scale: function(v) { - mat4.scale(this._array, this._array, v._array); - this._dirty = true; - return this; - }, - - /** - * Translate self by v - * @param {qtek.math.Vector3} v - * @return {qtek.math.Matrix4} - */ - translate: function(v) { - mat4.translate(this._array, this._array, v._array); - this._dirty = true; - return this; - }, - - /** - * Transpose self, in-place. - * @return {qtek.math.Matrix2} - */ - transpose: function() { - mat4.transpose(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Decompose a matrix to SRT - * @param {qtek.math.Vector3} [scale] - * @param {qtek.math.Quaternion} rotation - * @param {qtek.math.Vector} position - * @see http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.matrix.decompose.aspx - */ - decomposeMatrix: (function() { - - var x = vec3.create(); - var y = vec3.create(); - var z = vec3.create(); - - var m3 = mat3.create(); - - return function(scale, rotation, position) { - - var el = this._array; - vec3.set(x, el[0], el[1], el[2]); - vec3.set(y, el[4], el[5], el[6]); - vec3.set(z, el[8], el[9], el[10]); - - var sx = vec3.length(x); - var sy = vec3.length(y); - var sz = vec3.length(z); - if (scale) { - scale.x = sx; - scale.y = sy; - scale.z = sz; - scale._dirty = true; - } - - position.set(el[12], el[13], el[14]); - - mat3.fromMat4(m3, el); - // Not like mat4, mat3 in glmatrix seems to be row-based - mat3.transpose(m3, m3); - - m3[0] /= sx; - m3[1] /= sx; - m3[2] /= sx; - - m3[3] /= sy; - m3[4] /= sy; - m3[5] /= sy; - - m3[6] /= sz; - m3[7] /= sz; - m3[8] /= sz; - - quat.fromMat3(rotation._array, m3); - quat.normalize(rotation._array, rotation._array); - - rotation._dirty = true; - position._dirty = true; - }; - })(), - - toString: function() { - return '[' + Array.prototype.join.call(this._array, ',') + ']'; - } - }; - - if (Object.defineProperty) { - var proto = Matrix4.prototype; - /** - * Z Axis of local transform - * @name z - * @type {qtek.math.Vector3} - * @memberOf qtek.math.Matrix4 - * @instance - */ - Object.defineProperty(proto, 'z', { - get: function () { - var el = this._array; - this._axisZ.set(el[8], el[9], el[10]); - return this._axisZ; - }, - set: function (v) { - // TODO Here has a problem - // If only set an item of vector will not work - var el = this._array; - v = v._array; - el[8] = v[0]; - el[9] = v[1]; - el[10] = v[2]; - - this._dirty = true; - } - }); - - /** - * Y Axis of local transform - * @name y - * @type {qtek.math.Vector3} - * @memberOf qtek.math.Matrix4 - * @instance - */ - Object.defineProperty(proto, 'y', { - get: function () { - var el = this._array; - this._axisY.set(el[4], el[5], el[6]); - return this._axisY; - }, - set: function (v) { - var el = this._array; - v = v._array; - el[4] = v[0]; - el[5] = v[1]; - el[6] = v[2]; - - this._dirty = true; - } - }); - - /** - * X Axis of local transform - * @name x - * @type {qtek.math.Vector3} - * @memberOf qtek.math.Matrix4 - * @instance - */ - Object.defineProperty(proto, 'x', { - get: function () { - var el = this._array; - this._axisX.set(el[0], el[1], el[2]); - return this._axisX; - }, - set: function (v) { - var el = this._array; - v = v._array; - el[0] = v[0]; - el[1] = v[1]; - el[2] = v[2]; - - this._dirty = true; - } - }) - } - - // Object.defineProperty(Matrix4.prototype, 'm00', makeProperty(0)); - // Object.defineProperty(Matrix4.prototype, 'm01', makeProperty(1)); - // Object.defineProperty(Matrix4.prototype, 'm02', makeProperty(2)); - // Object.defineProperty(Matrix4.prototype, 'm03', makeProperty(3)); - // Object.defineProperty(Matrix4.prototype, 'm10', makeProperty(4)); - // Object.defineProperty(Matrix4.prototype, 'm11', makeProperty(5)); - // Object.defineProperty(Matrix4.prototype, 'm12', makeProperty(6)); - // Object.defineProperty(Matrix4.prototype, 'm13', makeProperty(7)); - // Object.defineProperty(Matrix4.prototype, 'm20', makeProperty(8)); - // Object.defineProperty(Matrix4.prototype, 'm21', makeProperty(9)); - // Object.defineProperty(Matrix4.prototype, 'm22', makeProperty(10)); - // Object.defineProperty(Matrix4.prototype, 'm23', makeProperty(11)); - // Object.defineProperty(Matrix4.prototype, 'm30', makeProperty(12)); - // Object.defineProperty(Matrix4.prototype, 'm31', makeProperty(13)); - // Object.defineProperty(Matrix4.prototype, 'm32', makeProperty(14)); - // Object.defineProperty(Matrix4.prototype, 'm33', makeProperty(15)); - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @return {qtek.math.Matrix4} - */ - Matrix4.adjoint = function(out, a) { - mat4.adjoint(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @return {qtek.math.Matrix4} - */ - Matrix4.copy = function(out, a) { - mat4.copy(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} a - * @return {number} - */ - Matrix4.determinant = function(a) { - return mat4.determinant(a._array); - }; - - /** - * @param {qtek.math.Matrix4} out - * @return {qtek.math.Matrix4} - */ - Matrix4.identity = function(out) { - mat4.identity(out._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {number} left - * @param {number} right - * @param {number} bottom - * @param {number} top - * @param {number} near - * @param {number} far - * @return {qtek.math.Matrix4} - */ - Matrix4.ortho = function(out, left, right, bottom, top, near, far) { - mat4.ortho(out._array, left, right, bottom, top, near, far); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {number} fovy - * @param {number} aspect - * @param {number} near - * @param {number} far - * @return {qtek.math.Matrix4} - */ - Matrix4.perspective = function(out, fovy, aspect, near, far) { - mat4.perspective(out._array, fovy, aspect, near, far); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Vector3} eye - * @param {qtek.math.Vector3} center - * @param {qtek.math.Vector3} up - * @return {qtek.math.Matrix4} - */ - Matrix4.lookAt = function(out, eye, center, up) { - mat4.lookAt(out._array, eye._array, center._array, up._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @return {qtek.math.Matrix4} - */ - Matrix4.invert = function(out, a) { - mat4.invert(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @param {qtek.math.Matrix4} b - * @return {qtek.math.Matrix4} - */ - Matrix4.mul = function(out, a, b) { - mat4.mul(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @method - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @param {qtek.math.Matrix4} b - * @return {qtek.math.Matrix4} - */ - Matrix4.multiply = Matrix4.mul; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Quaternion} q - * @return {qtek.math.Matrix4} - */ - Matrix4.fromQuat = function(out, q) { - mat4.fromQuat(out._array, q._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Quaternion} q - * @param {qtek.math.Vector3} v - * @return {qtek.math.Matrix4} - */ - Matrix4.fromRotationTranslation = function(out, q, v) { - mat4.fromRotationTranslation(out._array, q._array, v._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} m4 - * @param {qtek.math.Matrix2d} m2d - * @return {qtek.math.Matrix4} - */ - Matrix4.fromMat2d = function(m4, m2d) { - m4._dirty = true; - var m2d = m2d._array; - var m4 = m4._array; - - m4[0] = m2d[0]; - m4[4] = m2d[2]; - m4[12] = m2d[4]; - - m4[1] = m2d[1]; - m4[5] = m2d[3]; - m4[13] = m2d[5]; - - return m4; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @param {number} rad - * @param {qtek.math.Vector3} axis - * @return {qtek.math.Matrix4} - */ - Matrix4.rotate = function(out, a, rad, axis) { - mat4.rotate(out._array, a._array, rad, axis._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @param {number} rad - * @return {qtek.math.Matrix4} - */ - Matrix4.rotateX = function(out, a, rad) { - mat4.rotateX(out._array, a._array, rad); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @param {number} rad - * @return {qtek.math.Matrix4} - */ - Matrix4.rotateY = function(out, a, rad) { - mat4.rotateY(out._array, a._array, rad); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @param {number} rad - * @return {qtek.math.Matrix4} - */ - Matrix4.rotateZ = function(out, a, rad) { - mat4.rotateZ(out._array, a._array, rad); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @param {qtek.math.Vector3} v - * @return {qtek.math.Matrix4} - */ - Matrix4.scale = function(out, a, v) { - mat4.scale(out._array, a._array, v._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @return {qtek.math.Matrix4} - */ - Matrix4.transpose = function(out, a) { - mat4.transpose(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix4} out - * @param {qtek.math.Matrix4} a - * @param {qtek.math.Vector3} v - * @return {qtek.math.Matrix4} - */ - Matrix4.translate = function(out, a, v) { - mat4.translate(out._array, a._array, v._array); - out._dirty = true; - return out; - }; - - return Matrix4; -}); -define('qtek/Node',['require','./core/Base','./math/Vector3','./math/Quaternion','./math/Matrix4','./dep/glmatrix'],function(require) { - - - - var Base = require('./core/Base'); - var Vector3 = require('./math/Vector3'); - var Quaternion = require('./math/Quaternion'); - var Matrix4 = require('./math/Matrix4'); - var glMatrix = require('./dep/glmatrix'); - var mat4 = glMatrix.mat4; - - var nameId = 0; - - /** - * @constructor qtek.Node - * @extends qtek.core.Base - */ - var Node = Base.derive( - /** @lends qtek.Node# */ - { - /** - * Scene node name - * @type {string} - */ - name: '', - - /** - * Position relative to its parent node. aka translation. - * @type {qtek.math.Vector3} - */ - position: null, - - /** - * Rotation relative to its parent node. Represented by a quaternion - * @type {qtek.math.Quaternion} - */ - rotation: null, - - /** - * Scale relative to its parent node - * @type {qtek.math.Vector3} - */ - scale: null, - - /** - * Affine transform matrix relative to its root scene. - * @type {qtek.math.Matrix4} - */ - worldTransform: null, - - /** - * Affine transform matrix relative to its parent node. - * Composite with position, rotation and scale. - * @type {qtek.math.Matrix4} - */ - localTransform: null, - - /** - * If the local transform is update from SRT(scale, rotation, translation, which is position here) each frame - * @type {boolean} - */ - autoUpdateLocalTransform: true, - - /** - * Parent of current scene node - * @type {?qtek.Node} - * @private - */ - _parent: null, - /** - * The root scene mounted. Null if it is a isolated node - * @type {?qtek.Scene} - * @private - */ - _scene: null, - - _needsUpdateWorldTransform: true, - - _inIterating: false, - - // Depth for transparent queue sorting - __depth: 0 - - }, function() { - - if (!this.name) { - this.name = 'NODE_' + (nameId++); - } - - if (!this.position) { - this.position = new Vector3(); - } - if (!this.rotation) { - this.rotation = new Quaternion(); - } - if (!this.scale) { - this.scale = new Vector3(1, 1, 1); - } - - this.worldTransform = new Matrix4(); - this.localTransform = new Matrix4(); - - this._children = []; - - }, - /**@lends qtek.Node.prototype. */ - { - - /** - * If node and its chilren visible - * @type {boolean} - * @memberOf qtek.Node - * @instance - */ - visible: true, - - /** - * Return true if it is a renderable scene node, like Mesh and ParticleSystem - * @return {boolean} - */ - isRenderable: function() { - return false; - }, - - /** - * Set the name of the scene node - * @param {string} name - */ - setName: function(name) { - if (this._scene) { - delete this._scene._nodeRepository[this.name]; - this._scene._nodeRepository[name] = this; - } - this.name = name; - }, - - /** - * Add a child node - * @param {qtek.Node} node - */ - add: function(node) { - if (this._inIterating) { - console.warn('Add operation can cause unpredictable error when in iterating'); - } - if (node._parent === this) { - return; - } - if (node._parent) { - node._parent.remove(node); - } - node._parent = this; - this._children.push(node); - - if (this._scene && this._scene !== node.scene) { - node.traverse(this._addSelfToScene, this); - } - }, - - /** - * Remove the given child scene node - * @param {qtek.Node} node - */ - remove: function(node) { - if (this._inIterating) { - console.warn('Remove operation can cause unpredictable error when in iterating'); - } - - var idx = this._children.indexOf(node); - if (idx < 0) { - return; - } - - this._children.splice(idx, 1); - node._parent = null; - - if (this._scene) { - node.traverse(this._removeSelfFromScene, this); - } - }, - - /** - * Get the scene mounted - * @return {qtek.Scene} - */ - getScene: function () { - return this._scene; - }, - - /** - * Get parent node - * @return {qtek.Scene} - */ - getParent: function () { - return this._parent; - }, - - _removeSelfFromScene: function(descendant) { - descendant._scene.removeFromScene(descendant); - descendant._scene = null; - }, - - _addSelfToScene: function(descendant) { - this._scene.addToScene(descendant); - descendant._scene = this._scene; - }, - - /** - * Return true if it is ancestor of the given scene node - * @param {qtek.Node} node - */ - isAncestor: function(node) { - var parent = node._parent; - while(parent) { - if (parent === this) { - return true; - } - parent = parent._parent; - } - return false; - }, - - /** - * Get a new created array of all its children nodes - * @return {qtek.Node[]} - */ - children: function() { - return this._children.slice(); - }, - - childAt: function(idx) { - return this._children[idx]; - }, - - /** - * Get first child have the given name - * @param {string} name - * @return {qtek.Node} - */ - getChildByName: function(name) { - for (var i = 0; i < this._children.length; i++) { - if (this._children[i].name === name) { - return this._children[i]; - } - } - }, - - /** - * Get first descendant have the given name - * @param {string} name - * @return {qtek.Node} - */ - getDescendantByName: function(name) { - for (var i = 0; i < this._children.length; i++) { - var child = this._children[i]; - if (child.name === name) { - return child; - } else { - var res = child.getDescendantByName(name); - if (res) { - return res; - } - } - } - }, - - /** - * Query descendant node by path - * @param {string} path - * @return {qtek.Node} - */ - queryNode: function (path) { - if (!path) { - return; - } - // TODO Name have slash ? - var pathArr = path.split('/'); - var current = this; - for (var i = 0; i < pathArr.length; i++) { - var name = pathArr[i]; - // Skip empty - if (!name) { - continue; - } - var found = false; - for (var j = 0; j < current._children.length; j++) { - var child = current._children[j]; - if (child.name === name) { - current = child; - found = true; - break; - } - } - // Early return if not found - if (!found) { - return; - } - } - - return current; - }, - - /** - * Get query path, relative to rootNode(default is scene) - * @return {string} - */ - getPath: function (rootNode) { - if (!this._parent) { - return '/'; - } - - var current = this._parent; - var path = this.name; - while (current._parent) { - path = current.name + '/' + path; - if (current._parent == rootNode) { - break; - } - current = current._parent; - } - if (!current._parent && rootNode) { - return null; - } - return path; - }, - - /** - * Depth first traverse all its descendant scene nodes and - * @param {Function} callback - * @param {Node} [context] - * @param {Function} [ctor] - */ - traverse: function(callback, context, ctor) { - - this._inIterating = true; - - if (ctor === undefined || this.constructor === ctor) { - callback.call(context, this); - } - var _children = this._children; - for(var i = 0, len = _children.length; i < len; i++) { - _children[i].traverse(callback, context, ctor); - } - - this._inIterating = false; - }, - - /** - * Set the local transform and decompose to SRT - * @param {qtek.math.Matrix4} matrix - */ - setLocalTransform: function(matrix) { - mat4.copy(this.localTransform._array, matrix._array); - this.decomposeLocalTransform(); - }, - - /** - * Decompose the local transform to SRT - */ - decomposeLocalTransform: function(keepScale) { - var scale = !keepScale ? this.scale: null; - this.localTransform.decomposeMatrix(scale, this.rotation, this.position); - }, - - /** - * Set the world transform and decompose to SRT - * @param {qtek.math.Matrix4} matrix - */ - setWorldTransform: function(matrix) { - mat4.copy(this.worldTransform._array, matrix._array); - this.decomposeWorldTransform(); - }, - - /** - * Decompose the world transform to SRT - * @method - */ - decomposeWorldTransform: (function() { - - var tmp = mat4.create(); - - return function(keepScale) { - // Assume world transform is updated - if (this._parent) { - mat4.invert(tmp, this._parent.worldTransform._array); - mat4.multiply(this.localTransform._array, tmp, this.worldTransform._array); - } else { - mat4.copy(this.localTransform._array, this.worldTransform._array); - } - var scale = !keepScale ? this.scale: null; - this.localTransform.decomposeMatrix(scale, this.rotation, this.position); - }; - })(), - - /** - * Update local transform from SRT - * Notice that local transform will not be updated if _dirty mark of position, rotation, scale is all false - */ - updateLocalTransform: function() { - var position = this.position; - var rotation = this.rotation; - var scale = this.scale; - - if (position._dirty || scale._dirty || rotation._dirty) { - var m = this.localTransform._array; - - // Transform order, scale->rotation->position - mat4.fromRotationTranslation(m, rotation._array, position._array); - - mat4.scale(m, m, scale._array); - - rotation._dirty = false; - scale._dirty = false; - position._dirty = false; - - this._needsUpdateWorldTransform = true; - } - }, - - /** - * Update world transform, assume its parent world transform have been updated - */ - updateWorldTransform: function() { - if (this._parent) { - mat4.multiply( - this.worldTransform._array, - this._parent.worldTransform._array, - this.localTransform._array - ); - } else { - mat4.copy( - this.worldTransform._array, this.localTransform._array - ); - } - }, - - /** - * Update local transform and world transform recursively - * @param {boolean} forceUpdateWorld - */ - update: function(forceUpdateWorld) { - if (this.autoUpdateLocalTransform) { - this.updateLocalTransform(); - } else { - // Transform is manually setted - forceUpdateWorld = true; - } - - if (forceUpdateWorld || this._needsUpdateWorldTransform) { - this.updateWorldTransform(); - forceUpdateWorld = true; - this._needsUpdateWorldTransform = false; - } - - for(var i = 0, len = this._children.length; i < len; i++) { - this._children[i].update(forceUpdateWorld); - } - }, - - /** - * Get world position, extracted from world transform - * @param {math.Vector3} [out] - * @return {math.Vector3} - */ - getWorldPosition: function(out) { - var m = this.worldTransform._array; - if (out) { - out._array[0] = m[12]; - out._array[1] = m[13]; - out._array[2] = m[14]; - return out; - } else { - return new Vector3(m[12], m[13], m[14]); - } - }, - - /** - * Clone a new node - * @return {Node} - */ - clone: function() { - var node = new this.constructor(); - node.setName(this.name); - node.position.copy(this.position); - node.rotation.copy(this.rotation); - node.scale.copy(this.scale); - - for (var i = 0; i < this._children.length; i++) { - node.add(this._children[i].clone()); - } - return node; - }, - - /** - * Rotate the node around a axis by angle degrees, axis passes through point - * @param {math.Vector3} point Center point - * @param {math.Vector3} axis Center axis - * @param {number} angle Rotation angle - * @see http://docs.unity3d.com/Documentation/ScriptReference/Transform.RotateAround.html - * @method - */ - rotateAround: (function() { - var v = new Vector3(); - var RTMatrix = new Matrix4(); - - // TODO improve performance - return function(point, axis, angle) { - - v.copy(this.position).subtract(point); - - this.localTransform.identity(); - // parent node - this.localTransform.translate(point); - this.localTransform.rotate(angle, axis); - - RTMatrix.fromRotationTranslation(this.rotation, v); - this.localTransform.multiply(RTMatrix); - this.localTransform.scale(this.scale); - - this.decomposeLocalTransform(); - this._needsUpdateWorldTransform = true; - }; - })(), - - /** - * @param {math.Vector3} target - * @param {math.Vector3} [up] - * @see http://www.opengl.org/sdk/docs/man2/xhtml/gluLookAt.xml - * @method - */ - lookAt: (function() { - var m = new Matrix4(); - return function(target, up) { - m.lookAt(this.position, target, up || this.localTransform.y).invert(); - m.decomposeMatrix(null, this.rotation, this.position); - }; - })() - }); - - return Node; -}); -define('qtek/math/BoundingBox',['require','./Vector3','../dep/glmatrix'],function(require) { - - - - var Vector3 = require('./Vector3'); - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - - var vec3TransformMat4 = vec3.transformMat4; - var vec3Copy = vec3.copy; - var vec3Set = vec3.set; - - /** - * Axis aligned bounding box - * @constructor - * @alias qtek.math.BoundingBox - * @param {qtek.math.Vector3} [min] - * @param {qtek.math.Vector3} [max] - */ - var BoundingBox = function(min, max) { - - /** - * Minimum coords of bounding box - * @type {qtek.math.Vector3} - */ - this.min = min || new Vector3(Infinity, Infinity, Infinity); - - /** - * Maximum coords of bounding box - * @type {qtek.math.Vector3} - */ - this.max = max || new Vector3(-Infinity, -Infinity, -Infinity); - - // Cube vertices - var vertices = []; - for (var i = 0; i < 8; i++) { - vertices[i] = vec3.fromValues(0, 0, 0); - } - - /** - * Eight coords of bounding box - * @type {Float32Array[]} - */ - this.vertices = vertices; - }; - - BoundingBox.prototype = { - - constructor: BoundingBox, - /** - * Update min and max coords from a vertices array - * @param {array} vertices - */ - updateFromVertices: function(vertices) { - if (vertices.length > 0) { - var _min = this.min._array; - var _max = this.max._array; - vec3Copy(_min, vertices[0]); - vec3Copy(_max, vertices[0]); - for (var i = 1; i < vertices.length; i++) { - var vertex = vertices[i]; - - if (vertex[0] < _min[0]) { _min[0] = vertex[0]; } - if (vertex[1] < _min[1]) { _min[1] = vertex[1]; } - if (vertex[2] < _min[2]) { _min[2] = vertex[2]; } - - if (vertex[0] > _max[0]) { _max[0] = vertex[0]; } - if (vertex[1] > _max[1]) { _max[1] = vertex[1]; } - if (vertex[2] > _max[2]) { _max[2] = vertex[2]; } - } - this.min._dirty = true; - this.max._dirty = true; - } - }, - - /** - * Union operation with another bounding box - * @param {qtek.math.BoundingBox} bbox - */ - union: function(bbox) { - vec3.min(this.min._array, this.min._array, bbox.min._array); - vec3.max(this.max._array, this.max._array, bbox.max._array); - this.min._dirty = true; - this.max._dirty = true; - }, - - /** - * If intersect with another bounding box - * @param {qtek.math.BoundingBox} bbox - * @return {boolean} - */ - intersectBoundingBox: function(bbox) { - var _min = this.min._array; - var _max = this.max._array; - - var _min2 = bbox.min._array; - var _max2 = bbox.max._array; - - return ! (_min[0] > _max2[0] || _min[1] > _max2[1] || _min[2] > _max2[2] - || _max[0] < _min2[0] || _max[1] < _min2[1] || _max[2] < _min2[2]); - }, - - /** - * Apply an affine transform matrix to the bounding box - * @param {qtek.math.Matrix4} matrix - */ - applyTransform: function(matrix) { - if (this.min._dirty || this.max._dirty) { - this.updateVertices(); - this.min._dirty = false; - this.max._dirty = false; - } - - var m4 = matrix._array; - var _min = this.min._array; - var _max = this.max._array; - var vertices = this.vertices; - - var v = vertices[0]; - vec3TransformMat4(v, v, m4); - vec3Copy(_min, v); - vec3Copy(_max, v); - - for (var i = 1; i < 8; i++) { - v = vertices[i]; - vec3TransformMat4(v, v, m4); - - if (v[0] < _min[0]) { _min[0] = v[0]; } - if (v[1] < _min[1]) { _min[1] = v[1]; } - if (v[2] < _min[2]) { _min[2] = v[2]; } - - if (v[0] > _max[0]) { _max[0] = v[0]; } - if (v[1] > _max[1]) { _max[1] = v[1]; } - if (v[2] > _max[2]) { _max[2] = v[2]; } - } - - this.min._dirty = true; - this.max._dirty = true; - }, - - /** - * Apply a projection matrix to the bounding box - * @param {qtek.math.Matrix4} matrix - */ - applyProjection: function(matrix) { - if (this.min._dirty || this.max._dirty) { - this.updateVertices(); - this.min._dirty = false; - this.max._dirty = false; - } - - var m = matrix._array; - // min in min z - var v1 = this.vertices[0]; - // max in min z - var v2 = this.vertices[3]; - // max in max z - var v3 = this.vertices[7]; - - var _min = this.min._array; - var _max = this.max._array; - - if (m[15] === 1) { // Orthographic projection - _min[0] = m[0] * v1[0] + m[12]; - _min[1] = m[5] * v1[1] + m[13]; - _max[2] = m[10] * v1[2] + m[14]; - - _max[0] = m[0] * v3[0] + m[12]; - _max[1] = m[5] * v3[1] + m[13]; - _min[2] = m[10] * v3[2] + m[14]; - } else { - var w = -1 / v1[2]; - _min[0] = m[0] * v1[0] * w; - _min[1] = m[5] * v1[1] * w; - _max[2] = (m[10] * v1[2] + m[14]) * w; - - w = -1 / v2[2]; - _max[0] = m[0] * v2[0] * w; - _max[1] = m[5] * v2[1] * w; - - w = -1 / v3[2]; - _min[2] = (m[10] * v3[2] + m[14]) * w; - } - this.min._dirty = true; - this.max._dirty = true; - }, - - updateVertices: function() { - var min = this.min._array; - var max = this.max._array; - var vertices = this.vertices; - //--- min z - // min x - vec3Set(vertices[0], min[0], min[1], min[2]); - vec3Set(vertices[1], min[0], max[1], min[2]); - // max x - vec3Set(vertices[2], max[0], min[1], min[2]); - vec3Set(vertices[3], max[0], max[1], min[2]); - - //-- max z - vec3Set(vertices[4], min[0], min[1], max[2]); - vec3Set(vertices[5], min[0], max[1], max[2]); - vec3Set(vertices[6], max[0], min[1], max[2]); - vec3Set(vertices[7], max[0], max[1], max[2]); - }, - /** - * Copy values from another bounding box - * @param {qtek.math.BoundingBox} bbox - */ - copy: function(bbox) { - vec3Copy(this.min._array, bbox.min._array); - vec3Copy(this.max._array, bbox.max._array); - this.min._dirty = true; - this.max._dirty = true; - }, - - /** - * Clone a new bounding box - * @return {qtek.math.BoundingBox} - */ - clone: function() { - var boundingBox = new BoundingBox(); - boundingBox.copy(this); - return boundingBox; - } - }; - - return BoundingBox; -}); -define('qtek/math/Plane',['require','./Vector3','../dep/glmatrix'],function(require) { - - - - var Vector3 = require('./Vector3'); - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - var mat4 = glMatrix.mat4; - var vec4 = glMatrix.vec4; - - /** - * @constructor - * @alias qtek.math.Plane - * @param {qtek.math.Vector3} [normal] - * @param {number} [distance] - */ - var Plane = function(normal, distance) { - /** - * Normal of the plane - * @type {qtek.math.Vector3} - */ - this.normal = normal || new Vector3(0, 1, 0); - - /** - * Constant of the plane equation, used as distance to the origin - * @type {number} - */ - this.distance = distance || 0; - }; - - Plane.prototype = { - - constructor: Plane, - - /** - * Distance from given point to plane - * @param {qtek.math.Vector3} point - * @return {number} - */ - distanceToPoint: function(point) { - return vec3.dot(point._array, this.normal._array) - this.distance; - }, - - /** - * Calculate the projection on the plane of point - * @param {qtek.math.Vector3} point - * @param {qtek.math.Vector3} out - * @return {qtek.math.Vector3} - */ - projectPoint: function(point, out) { - if (!out) { - out = new Vector3(); - } - var d = this.distanceToPoint(point); - vec3.scaleAndAdd(out._array, point._array, this.normal._array, -d); - out._dirty = true; - return out; - }, - - /** - * Normalize the plane's normal and calculate distance - */ - normalize: function() { - var invLen = 1 / vec3.len(this.normal._array); - vec3.scale(this.normal._array, invLen); - this.distance *= invLen; - }, - - /** - * If the plane intersect a frustum - * @param {qtek.math.Frustum} Frustum - * @return {boolean} - */ - intersectFrustum: function(frustum) { - // Check if all coords of frustum is on plane all under plane - var coords = frustum.vertices; - var normal = this.normal._array; - var onPlane = vec3.dot(coords[0]._array, normal) > this.distance; - for (var i = 1; i < 8; i++) { - if ((vec3.dot(coords[i]._array, normal) > this.distance) != onPlane) { - return true; - } - } - }, - - /** - * Calculate the intersection point between plane and a given line - * @method - * @param {qtek.math.Vector3} start start point of line - * @param {qtek.math.Vector3} end end point of line - * @param {qtek.math.Vector3} [out] - * @return {qtek.math.Vector3} - */ - intersectLine: (function() { - var rd = vec3.create(); - return function(start, end, out) { - var d0 = this.distanceToPoint(start); - var d1 = this.distanceToPoint(end); - if ((d0 > 0 && d1 > 0) || (d0 < 0 && d1 < 0)) { - return null; - } - // Ray intersection - var pn = this.normal._array; - var d = this.distance; - var ro = start._array; - // direction - vec3.sub(rd, end._array, start._array); - vec3.normalize(rd, rd); - - var divider = vec3.dot(pn, rd); - // ray is parallel to the plane - if (divider === 0) { - return null; - } - if (!out) { - out = new Vector3(); - } - var t = (vec3.dot(pn, ro) - d) / divider; - vec3.scaleAndAdd(out._array, ro, rd, -t); - out._dirty = true; - return out; - }; - })(), - - /** - * Apply an affine transform matrix to plane - * @method - * @return {qtek.math.Matrix4} - */ - applyTransform: (function() { - var inverseTranspose = mat4.create(); - var normalv4 = vec4.create(); - var pointv4 = vec4.create(); - pointv4[3] = 1; - return function(m4) { - m4 = m4._array; - // Transform point on plane - vec3.scale(pointv4, this.normal._array, this.distance); - vec4.transformMat4(pointv4, pointv4, m4); - this.distance = vec3.dot(pointv4, this.normal._array); - // Transform plane normal - mat4.invert(inverseTranspose, m4); - mat4.transpose(inverseTranspose, inverseTranspose); - normalv4[3] = 0; - vec3.copy(normalv4, this.normal._array); - vec4.transformMat4(normalv4, normalv4, inverseTranspose); - vec3.copy(this.normal._array, normalv4); - }; - })(), - - /** - * Copy from another plane - * @param {qtek.math.Vector3} plane - */ - copy: function(plane) { - vec3.copy(this.normal._array, plane.normal._array); - this.normal._dirty = true; - this.distance = plane.distance; - }, - - /** - * Clone a new plane - * @return {qtek.math.Plane} - */ - clone: function() { - var plane = new Plane(); - plane.copy(this); - return plane; - } - }; - - return Plane; -}); -define('qtek/math/Frustum',['require','./Vector3','./BoundingBox','./Plane','../dep/glmatrix'],function(require) { - - - - var Vector3 = require('./Vector3'); - var BoundingBox = require('./BoundingBox'); - var Plane = require('./Plane'); - - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - - /** - * @constructor - * @alias qtek.math.Frustum - */ - var Frustum = function() { - - /** - * Eight planes to enclose the frustum - * @type {qtek.math.Plane[]} - */ - this.planes = []; - - for (var i = 0; i < 6; i++) { - this.planes.push(new Plane()); - } - - /** - * Bounding box of frustum - * @type {qtek.math.BoundingBox} - */ - this.boundingBox = new BoundingBox(); - - /** - * Eight vertices of frustum - * @type {Float32Array[]} - */ - this.vertices = []; - for (var i = 0; i < 8; i++) { - this.vertices[i] = vec3.fromValues(0, 0, 0); - } - }; - - Frustum.prototype = { - - // http://web.archive.org/web/20120531231005/http://crazyjoke.free.fr/doc/3D/plane%20extraction.pdf - /** - * Set frustum from a projection matrix - * @param {qtek.math.Matrix4} projectionMatrix - */ - setFromProjection: function(projectionMatrix) { - - var planes = this.planes; - var m = projectionMatrix._array; - var m0 = m[0], m1 = m[1], m2 = m[2], m3 = m[3]; - var m4 = m[4], m5 = m[5], m6 = m[6], m7 = m[7]; - var m8 = m[8], m9 = m[9], m10 = m[10], m11 = m[11]; - var m12 = m[12], m13 = m[13], m14 = m[14], m15 = m[15]; - - // Update planes - vec3.set(planes[0].normal._array, m3 - m0, m7 - m4, m11 - m8); - planes[0].distance = -(m15 - m12); - planes[0].normalize(); - - vec3.set(planes[1].normal._array, m3 + m0, m7 + m4, m11 + m8); - planes[1].distance = -(m15 + m12); - planes[1].normalize(); - - vec3.set(planes[2].normal._array, m3 + m1, m7 + m5, m11 + m9); - planes[2].distance = -(m15 + m13); - planes[2].normalize(); - - vec3.set(planes[3].normal._array, m3 - m1, m7 - m5, m11 - m9); - planes[3].distance = -(m15 - m13); - planes[3].normalize(); - - vec3.set(planes[4].normal._array, m3 - m2, m7 - m6, m11 - m10); - planes[4].distance = -(m15 - m14); - planes[4].normalize(); - - vec3.set(planes[5].normal._array, m3 + m2, m7 + m6, m11 + m10); - planes[5].distance = -(m15 + m14); - planes[5].normalize(); - - // Perspective projection - if (m15 === 0) { - var aspect = m5 / m0; - var zNear = -m14 / (m10 - 1); - var zFar = -m14 / (m10 + 1); - var farY = -zFar / m5; - var nearY = -zNear / m5; - // Update bounding box - this.boundingBox.min.set(-farY * aspect, -farY, zFar); - this.boundingBox.max.set(farY * aspect, farY, zNear); - // update vertices - var vertices = this.vertices; - //--- min z - // min x - vec3.set(vertices[0], -farY * aspect, -farY, zFar); - vec3.set(vertices[1], -farY * aspect, farY, zFar); - // max x - vec3.set(vertices[2], farY * aspect, -farY, zFar); - vec3.set(vertices[3], farY * aspect, farY, zFar); - //-- max z - vec3.set(vertices[4], -nearY * aspect, -nearY, zNear); - vec3.set(vertices[5], -nearY * aspect, nearY, zNear); - vec3.set(vertices[6], nearY * aspect, -nearY, zNear); - vec3.set(vertices[7], nearY * aspect, nearY, zNear); - } else { // Orthographic projection - var left = (-1 - m12) / m0; - var right = (1 - m12) / m0; - var top = (1 - m13) / m5; - var bottom = (-1 - m13) / m5; - var near = (-1 - m14) / m10; - var far = (1 - m14) / m10; - - this.boundingBox.min.set(left, bottom, far); - this.boundingBox.max.set(right, top, near); - // Copy the vertices from bounding box directly - for (var i = 0; i < 8; i++) { - vec3.copy(this.vertices[i], this.boundingBox.vertices[i]); - } - } - }, - - /** - * Apply a affine transform matrix and set to the given bounding box - * @method - * @param {qtek.math.BoundingBox} - * @param {qtek.math.Matrix4} - * @return {qtek.math.BoundingBox} - */ - getTransformedBoundingBox: (function() { - - var tmpVec3 = vec3.create(); - - return function(bbox, matrix) { - var vertices = this.vertices; - - var m4 = matrix._array; - var _min = bbox.min._array; - var _max = bbox.max._array; - var v = vertices[0]; - vec3.transformMat4(tmpVec3, v, m4); - vec3.copy(_min, tmpVec3); - vec3.copy(_max, tmpVec3); - - for (var i = 1; i < 8; i++) { - v = vertices[i]; - vec3.transformMat4(tmpVec3, v, m4); - - _min[0] = Math.min(tmpVec3[0], _min[0]); - _min[1] = Math.min(tmpVec3[1], _min[1]); - _min[2] = Math.min(tmpVec3[2], _min[2]); - - _max[0] = Math.max(tmpVec3[0], _max[0]); - _max[1] = Math.max(tmpVec3[1], _max[1]); - _max[2] = Math.max(tmpVec3[2], _max[2]); - } - - bbox.min._dirty = true; - bbox.max._dirty = true; - - return bbox; - }; - }) () - }; - return Frustum; -}); -define('qtek/math/Ray',['require','./Vector3','../dep/glmatrix'],function(require) { - - - - var Vector3 = require('./Vector3'); - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - - var EPSILON = 1e-5; - - /** - * @constructor - * @alias qtek.math.Ray - * @param {qtek.math.Vector3} [origin] - * @param {qtek.math.Vector3} [direction] - */ - var Ray = function(origin, direction) { - /** - * @type {qtek.math.Vector3} - */ - this.origin = origin || new Vector3(); - /** - * @type {qtek.math.Vector3} - */ - this.direction = direction || new Vector3(); - }; - - Ray.prototype = { - - constructor: Ray, - - // http://www.siggraph.org/education/materials/HyperGraph/raytrace/rayplane_intersection.htm - /** - * Calculate intersection point between ray and a give plane - * @param {qtek.math.Plane} plane - * @param {qtek.math.Vector3} [out] - * @return {qtek.math.Vector3} - */ - intersectPlane: function(plane, out) { - var pn = plane.normal._array; - var d = plane.distance; - var ro = this.origin._array; - var rd = this.direction._array; - - var divider = vec3.dot(pn, rd); - // ray is parallel to the plane - if (divider === 0) { - return null; - } - if (!out) { - out = new Vector3(); - } - var t = (vec3.dot(pn, ro) - d) / divider; - vec3.scaleAndAdd(out._array, ro, rd, -t); - out._dirty = true; - return out; - }, - - /** - * Mirror the ray against plane - * @param {qtek.math.Plane} plane - */ - mirrorAgainstPlane: function(plane) { - // Distance to plane - var d = vec3.dot(plane.normal._array, this.direction._array); - vec3.scaleAndAdd(this.direction._array, this.direction._array, plane.normal._array, -d * 2); - this.direction._dirty = true; - }, - - distanceToPoint: (function () { - var v = vec3.create(); - return function (point) { - vec3.sub(v, point, this.origin._array); - // Distance from projection point to origin - var b = vec3.dot(v, this.direction._array); - if (b < 0) { - return vec3.distance(this.origin._array, point); - } - // Squared distance from center to origin - var c2 = vec3.lenSquared(v); - // Squared distance from center to projection point - return Math.sqrt(c2 - b * b); - }; - })(), - - /** - * Calculate intersection point between ray and sphere - * @param {qtek.math.Vector3} center - * @param {number} radius - * @param {qtek.math.Vector3} out - * @return {qtek.math.Vector3} - */ - intersectSphere: (function () { - var v = vec3.create(); - return function (center, radius, out) { - var origin = this.origin._array; - var direction = this.direction._array; - center = center._array; - vec3.sub(v, center, origin); - // Distance from projection point to origin - var b = vec3.dot(v, direction); - // Squared distance from center to origin - var c2 = vec3.squaredLength(v); - // Squared distance from center to projection point - var d2 = c2 - b * b; - - var r2 = radius * radius; - // No intersection - if (d2 > r2) { - return; - } - - var a = Math.sqrt(r2 - d2); - // First intersect point - var t0 = b - a; - // Second intersect point - var t1 = b + a; - - if (!out) { - out = new Vector3(); - } - if (t0 < 0) { - if (t1 < 0) { - return null; - } else { - vec3.scaleAndAdd(out._array, origin, direction, t1); - return out; - } - } else { - vec3.scaleAndAdd(out._array, origin, direction, t0); - return out; - } - }; - })(), - - // http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/ - /** - * Calculate intersection point between ray and bounding box - * @param {qtek.math.BoundingBox} bbox - * @param {qtek.math.Vector3} - * @return {qtek.math.Vector3} - */ - intersectBoundingBox: function(bbox, out) { - var dir = this.direction._array; - var origin = this.origin._array; - var min = bbox.min._array; - var max = bbox.max._array; - - var invdirx = 1 / dir[0]; - var invdiry = 1 / dir[1]; - var invdirz = 1 / dir[2]; - - var tmin, tmax, tymin, tymax, tzmin, tzmax; - if (invdirx >= 0) { - tmin = (min[0] - origin[0]) * invdirx; - tmax = (max[0] - origin[0]) * invdirx; - } else { - tmax = (min[0] - origin[0]) * invdirx; - tmin = (max[0] - origin[0]) * invdirx; - } - if (invdiry >= 0) { - tymin = (min[1] - origin[1]) * invdiry; - tymax = (max[1] - origin[1]) * invdiry; - } else { - tymax = (min[1] - origin[1]) * invdiry; - tymin = (max[1] - origin[1]) * invdiry; - } - - if ((tmin > tymax) || (tymin > tmax)) { - return null; - } - - if (tymin > tmin || tmin !== tmin) { - tmin = tymin; - } - if (tymax < tmax || tmax !== tmax) { - tmax = tymax; - } - - if (invdirz >= 0) { - tzmin = (min[2] - origin[2]) * invdirz; - tzmax = (max[2] - origin[2]) * invdirz; - } else { - tzmax = (min[2] - origin[2]) * invdirz; - tzmin = (max[2] - origin[2]) * invdirz; - } - - if ((tmin > tzmax) || (tzmin > tmax)) { - return null; - } - - if (tzmin > tmin || tmin !== tmin) { - tmin = tzmin; - } - if (tzmax < tmax || tmax !== tmax) { - tmax = tzmax; - } - if (tmax < 0) { - return null; - } - - var t = tmin >= 0 ? tmin : tmax; - - if (!out) { - out = new Vector3(); - } - vec3.scaleAndAdd(out._array, origin, dir, t); - return out; - }, - - // http://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm - /** - * Calculate intersection point between ray and three triangle vertices - * @param {qtek.math.Vector3} a - * @param {qtek.math.Vector3} b - * @param {qtek.math.Vector3} c - * @param {boolean} singleSided, CW triangle will be ignored - * @param {qtek.math.Vector3} [out] - * @param {qtek.math.Vector3} [barycenteric] barycentric coords - * @return {qtek.math.Vector3} - */ - intersectTriangle: (function() { - - var eBA = vec3.create(); - var eCA = vec3.create(); - var AO = vec3.create(); - var vCross = vec3.create(); - - return function(a, b, c, singleSided, out, barycenteric) { - var dir = this.direction._array; - var origin = this.origin._array; - a = a._array; - b = b._array; - c = c._array; - - vec3.sub(eBA, b, a); - vec3.sub(eCA, c, a); - - vec3.cross(vCross, eCA, dir); - - var det = vec3.dot(eBA, vCross); - - if (singleSided) { - if (det > -EPSILON) { - return null; - } - } - else { - if (det > -EPSILON && det < EPSILON) { - return null; - } - } - - vec3.sub(AO, origin, a); - var u = vec3.dot(vCross, AO) / det; - if (u < 0 || u > 1) { - return null; - } - - vec3.cross(vCross, eBA, AO); - var v = vec3.dot(dir, vCross) / det; - - if (v < 0 || v > 1 || (u + v > 1)) { - return null; - } - - vec3.cross(vCross, eBA, eCA); - var t = -vec3.dot(AO, vCross) / det; - - if (t < 0) { - return null; - } - - if (!out) { - out = new Vector3(); - } - if (barycenteric) { - Vector3.set(barycenteric, (1 - u - v), u, v); - } - vec3.scaleAndAdd(out._array, origin, dir, t); - - return out; - }; - })(), - - /** - * Apply an affine transform matrix to the ray - * @return {qtek.math.Matrix4} matrix - */ - applyTransform: function(matrix) { - Vector3.add(this.direction, this.direction, this.origin); - Vector3.transformMat4(this.origin, this.origin, matrix); - Vector3.transformMat4(this.direction, this.direction, matrix); - - Vector3.sub(this.direction, this.direction, this.origin); - Vector3.normalize(this.direction, this.direction); - }, - - /** - * Copy values from another ray - * @param {qtek.math.Ray} ray - */ - copy: function(ray) { - Vector3.copy(this.origin, ray.origin); - Vector3.copy(this.direction, ray.direction); - }, - - /** - * Clone a new ray - * @return {qtek.math.Ray} - */ - clone: function() { - var ray = new Ray(); - ray.copy(this); - return ray; - } - }; - - return Ray; -}); -define('qtek/Camera',['require','./Node','./math/Matrix4','./math/Frustum','./math/BoundingBox','./math/Ray','./dep/glmatrix'],function(require) { - - - - var Node = require('./Node'); - var Matrix4 = require('./math/Matrix4'); - var Frustum = require('./math/Frustum'); - var BoundingBox = require('./math/BoundingBox'); - var Ray = require('./math/Ray'); - - var glMatrix = require('./dep/glmatrix'); - var mat4 = glMatrix.mat4; - var vec3 = glMatrix.vec3; - var vec4 = glMatrix.vec4; - - /** - * @constructor qtek.Camera - * @extends qtek.Node - */ - var Camera = Node.derive(function() { - return /** @lends qtek.Camera# */ { - /** - * Camera projection matrix - * @type {qtek.math.Matrix4} - */ - projectionMatrix: new Matrix4(), - - /** - * Inverse of camera projection matrix - * @type {qtek.math.Matrix4} - */ - invProjectionMatrix: new Matrix4(), - - /** - * View matrix, equal to inverse of camera's world matrix - * @type {qtek.math.Matrix4} - */ - viewMatrix: new Matrix4(), - - /** - * Camera frustum in view space - * @type {qtek.math.Frustum} - */ - frustum: new Frustum(), - - /** - * Scene bounding box in view space. - * Used when camera needs to adujst the near and far plane automatically - * so that the view frustum contains the visible objects as tightly as possible. - * Notice: - * It is updated after rendering (in the step of frustum culling passingly). So may be not so accurate, but saves a lot of calculation - * - * @type {qtek.math.BoundingBox} - */ - //TODO In case of one camera to multiple scenes - sceneBoundingBoxLastFrame: new BoundingBox() - }; - }, function() { - this.update(true); - }, - /** @lends qtek.Camera.prototype */ - { - - update: function(force) { - Node.prototype.update.call(this, force); - mat4.invert(this.viewMatrix._array, this.worldTransform._array); - - this.updateProjectionMatrix(); - mat4.invert(this.invProjectionMatrix._array, this.projectionMatrix._array); - - this.frustum.setFromProjection(this.projectionMatrix); - }, - /** - * Update projection matrix, called after update - */ - updateProjectionMatrix: function(){}, - - /** - * Cast a picking ray from camera near plane to far plane - * @method - * @param {qtek.math.Vector2} ndc - * @param {qtek.math.Ray} [out] - * @return {qtek.math.Ray} - */ - castRay: (function() { - var v4 = vec4.create(); - return function(ndc, out) { - var ray = out !== undefined ? out : new Ray(); - var x = ndc._array[0]; - var y = ndc._array[1]; - vec4.set(v4, x, y, -1, 1); - vec4.transformMat4(v4, v4, this.invProjectionMatrix._array); - vec4.transformMat4(v4, v4, this.worldTransform._array); - vec3.scale(ray.origin._array, v4, 1 / v4[3]); - - vec4.set(v4, x, y, 1, 1); - vec4.transformMat4(v4, v4, this.invProjectionMatrix._array); - vec4.transformMat4(v4, v4, this.worldTransform._array); - vec3.scale(v4, v4, 1 / v4[3]); - vec3.sub(ray.direction._array, v4, ray.origin._array); - - vec3.normalize(ray.direction._array, ray.direction._array); - ray.direction._dirty = true; - ray.origin._dirty = true; - - return ray; - }; - })() - - /** - * @method - * @name clone - * @return {qtek.Camera} - * @memberOf qtek.Camera.prototype - */ - }); - - return Camera; -}); -/** - * @namespace qtek.core.glenum - * @see http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14 - */ -define('qtek/core/glenum',[],function() { - -return { - /* ClearBufferMask */ - DEPTH_BUFFER_BIT : 0x00000100, - STENCIL_BUFFER_BIT : 0x00000400, - COLOR_BUFFER_BIT : 0x00004000, - - /* BeginMode */ - POINTS : 0x0000, - LINES : 0x0001, - LINE_LOOP : 0x0002, - LINE_STRIP : 0x0003, - TRIANGLES : 0x0004, - TRIANGLE_STRIP : 0x0005, - TRIANGLE_FAN : 0x0006, - - /* AlphaFunction (not supported in ES20) */ - /* NEVER */ - /* LESS */ - /* EQUAL */ - /* LEQUAL */ - /* GREATER */ - /* NOTEQUAL */ - /* GEQUAL */ - /* ALWAYS */ - - /* BlendingFactorDest */ - ZERO : 0, - ONE : 1, - SRC_COLOR : 0x0300, - ONE_MINUS_SRC_COLOR : 0x0301, - SRC_ALPHA : 0x0302, - ONE_MINUS_SRC_ALPHA : 0x0303, - DST_ALPHA : 0x0304, - ONE_MINUS_DST_ALPHA : 0x0305, - - /* BlendingFactorSrc */ - /* ZERO */ - /* ONE */ - DST_COLOR : 0x0306, - ONE_MINUS_DST_COLOR : 0x0307, - SRC_ALPHA_SATURATE : 0x0308, - /* SRC_ALPHA */ - /* ONE_MINUS_SRC_ALPHA */ - /* DST_ALPHA */ - /* ONE_MINUS_DST_ALPHA */ - - /* BlendEquationSeparate */ - FUNC_ADD : 0x8006, - BLEND_EQUATION : 0x8009, - BLEND_EQUATION_RGB : 0x8009, /* same as BLEND_EQUATION */ - BLEND_EQUATION_ALPHA : 0x883D, - - /* BlendSubtract */ - FUNC_SUBTRACT : 0x800A, - FUNC_REVERSE_SUBTRACT : 0x800B, - - /* Separate Blend Functions */ - BLEND_DST_RGB : 0x80C8, - BLEND_SRC_RGB : 0x80C9, - BLEND_DST_ALPHA : 0x80CA, - BLEND_SRC_ALPHA : 0x80CB, - CONSTANT_COLOR : 0x8001, - ONE_MINUS_CONSTANT_COLOR : 0x8002, - CONSTANT_ALPHA : 0x8003, - ONE_MINUS_CONSTANT_ALPHA : 0x8004, - BLEND_COLOR : 0x8005, - - /* Buffer Objects */ - ARRAY_BUFFER : 0x8892, - ELEMENT_ARRAY_BUFFER : 0x8893, - ARRAY_BUFFER_BINDING : 0x8894, - ELEMENT_ARRAY_BUFFER_BINDING : 0x8895, - - STREAM_DRAW : 0x88E0, - STATIC_DRAW : 0x88E4, - DYNAMIC_DRAW : 0x88E8, - - BUFFER_SIZE : 0x8764, - BUFFER_USAGE : 0x8765, - - CURRENT_VERTEX_ATTRIB : 0x8626, - - /* CullFaceMode */ - FRONT : 0x0404, - BACK : 0x0405, - FRONT_AND_BACK : 0x0408, - - /* DepthFunction */ - /* NEVER */ - /* LESS */ - /* EQUAL */ - /* LEQUAL */ - /* GREATER */ - /* NOTEQUAL */ - /* GEQUAL */ - /* ALWAYS */ - - /* EnableCap */ - /* TEXTURE_2D */ - CULL_FACE : 0x0B44, - BLEND : 0x0BE2, - DITHER : 0x0BD0, - STENCIL_TEST : 0x0B90, - DEPTH_TEST : 0x0B71, - SCISSOR_TEST : 0x0C11, - POLYGON_OFFSET_FILL : 0x8037, - SAMPLE_ALPHA_TO_COVERAGE : 0x809E, - SAMPLE_COVERAGE : 0x80A0, - - /* ErrorCode */ - NO_ERROR : 0, - INVALID_ENUM : 0x0500, - INVALID_VALUE : 0x0501, - INVALID_OPERATION : 0x0502, - OUT_OF_MEMORY : 0x0505, - - /* FrontFaceDirection */ - CW : 0x0900, - CCW : 0x0901, - - /* GetPName */ - LINE_WIDTH : 0x0B21, - ALIASED_POINT_SIZE_RANGE : 0x846D, - ALIASED_LINE_WIDTH_RANGE : 0x846E, - CULL_FACE_MODE : 0x0B45, - FRONT_FACE : 0x0B46, - DEPTH_RANGE : 0x0B70, - DEPTH_WRITEMASK : 0x0B72, - DEPTH_CLEAR_VALUE : 0x0B73, - DEPTH_FUNC : 0x0B74, - STENCIL_CLEAR_VALUE : 0x0B91, - STENCIL_FUNC : 0x0B92, - STENCIL_FAIL : 0x0B94, - STENCIL_PASS_DEPTH_FAIL : 0x0B95, - STENCIL_PASS_DEPTH_PASS : 0x0B96, - STENCIL_REF : 0x0B97, - STENCIL_VALUE_MASK : 0x0B93, - STENCIL_WRITEMASK : 0x0B98, - STENCIL_BACK_FUNC : 0x8800, - STENCIL_BACK_FAIL : 0x8801, - STENCIL_BACK_PASS_DEPTH_FAIL : 0x8802, - STENCIL_BACK_PASS_DEPTH_PASS : 0x8803, - STENCIL_BACK_REF : 0x8CA3, - STENCIL_BACK_VALUE_MASK : 0x8CA4, - STENCIL_BACK_WRITEMASK : 0x8CA5, - VIEWPORT : 0x0BA2, - SCISSOR_BOX : 0x0C10, - /* SCISSOR_TEST */ - COLOR_CLEAR_VALUE : 0x0C22, - COLOR_WRITEMASK : 0x0C23, - UNPACK_ALIGNMENT : 0x0CF5, - PACK_ALIGNMENT : 0x0D05, - MAX_TEXTURE_SIZE : 0x0D33, - MAX_VIEWPORT_DIMS : 0x0D3A, - SUBPIXEL_BITS : 0x0D50, - RED_BITS : 0x0D52, - GREEN_BITS : 0x0D53, - BLUE_BITS : 0x0D54, - ALPHA_BITS : 0x0D55, - DEPTH_BITS : 0x0D56, - STENCIL_BITS : 0x0D57, - POLYGON_OFFSET_UNITS : 0x2A00, - /* POLYGON_OFFSET_FILL */ - POLYGON_OFFSET_FACTOR : 0x8038, - TEXTURE_BINDING_2D : 0x8069, - SAMPLE_BUFFERS : 0x80A8, - SAMPLES : 0x80A9, - SAMPLE_COVERAGE_VALUE : 0x80AA, - SAMPLE_COVERAGE_INVERT : 0x80AB, - - /* GetTextureParameter */ - /* TEXTURE_MAG_FILTER */ - /* TEXTURE_MIN_FILTER */ - /* TEXTURE_WRAP_S */ - /* TEXTURE_WRAP_T */ - - COMPRESSED_TEXTURE_FORMATS : 0x86A3, - - /* HintMode */ - DONT_CARE : 0x1100, - FASTEST : 0x1101, - NICEST : 0x1102, - - /* HintTarget */ - GENERATE_MIPMAP_HINT : 0x8192, - - /* DataType */ - BYTE : 0x1400, - UNSIGNED_BYTE : 0x1401, - SHORT : 0x1402, - UNSIGNED_SHORT : 0x1403, - INT : 0x1404, - UNSIGNED_INT : 0x1405, - FLOAT : 0x1406, - - /* PixelFormat */ - DEPTH_COMPONENT : 0x1902, - ALPHA : 0x1906, - RGB : 0x1907, - RGBA : 0x1908, - LUMINANCE : 0x1909, - LUMINANCE_ALPHA : 0x190A, - - /* PixelType */ - /* UNSIGNED_BYTE */ - UNSIGNED_SHORT_4_4_4_4 : 0x8033, - UNSIGNED_SHORT_5_5_5_1 : 0x8034, - UNSIGNED_SHORT_5_6_5 : 0x8363, - - /* Shaders */ - FRAGMENT_SHADER : 0x8B30, - VERTEX_SHADER : 0x8B31, - MAX_VERTEX_ATTRIBS : 0x8869, - MAX_VERTEX_UNIFORM_VECTORS : 0x8DFB, - MAX_VARYING_VECTORS : 0x8DFC, - MAX_COMBINED_TEXTURE_IMAGE_UNITS : 0x8B4D, - MAX_VERTEX_TEXTURE_IMAGE_UNITS : 0x8B4C, - MAX_TEXTURE_IMAGE_UNITS : 0x8872, - MAX_FRAGMENT_UNIFORM_VECTORS : 0x8DFD, - SHADER_TYPE : 0x8B4F, - DELETE_STATUS : 0x8B80, - LINK_STATUS : 0x8B82, - VALIDATE_STATUS : 0x8B83, - ATTACHED_SHADERS : 0x8B85, - ACTIVE_UNIFORMS : 0x8B86, - ACTIVE_ATTRIBUTES : 0x8B89, - SHADING_LANGUAGE_VERSION : 0x8B8C, - CURRENT_PROGRAM : 0x8B8D, - - /* StencilFunction */ - NEVER : 0x0200, - LESS : 0x0201, - EQUAL : 0x0202, - LEQUAL : 0x0203, - GREATER : 0x0204, - NOTEQUAL : 0x0205, - GEQUAL : 0x0206, - ALWAYS : 0x0207, - - /* StencilOp */ - /* ZERO */ - KEEP : 0x1E00, - REPLACE : 0x1E01, - INCR : 0x1E02, - DECR : 0x1E03, - INVERT : 0x150A, - INCR_WRAP : 0x8507, - DECR_WRAP : 0x8508, - - /* StringName */ - VENDOR : 0x1F00, - RENDERER : 0x1F01, - VERSION : 0x1F02, - - /* TextureMagFilter */ - NEAREST : 0x2600, - LINEAR : 0x2601, - - /* TextureMinFilter */ - /* NEAREST */ - /* LINEAR */ - NEAREST_MIPMAP_NEAREST : 0x2700, - LINEAR_MIPMAP_NEAREST : 0x2701, - NEAREST_MIPMAP_LINEAR : 0x2702, - LINEAR_MIPMAP_LINEAR : 0x2703, - - /* TextureParameterName */ - TEXTURE_MAG_FILTER : 0x2800, - TEXTURE_MIN_FILTER : 0x2801, - TEXTURE_WRAP_S : 0x2802, - TEXTURE_WRAP_T : 0x2803, - - /* TextureTarget */ - TEXTURE_2D : 0x0DE1, - TEXTURE : 0x1702, - - TEXTURE_CUBE_MAP : 0x8513, - TEXTURE_BINDING_CUBE_MAP : 0x8514, - TEXTURE_CUBE_MAP_POSITIVE_X : 0x8515, - TEXTURE_CUBE_MAP_NEGATIVE_X : 0x8516, - TEXTURE_CUBE_MAP_POSITIVE_Y : 0x8517, - TEXTURE_CUBE_MAP_NEGATIVE_Y : 0x8518, - TEXTURE_CUBE_MAP_POSITIVE_Z : 0x8519, - TEXTURE_CUBE_MAP_NEGATIVE_Z : 0x851A, - MAX_CUBE_MAP_TEXTURE_SIZE : 0x851C, - - /* TextureUnit */ - TEXTURE0 : 0x84C0, - TEXTURE1 : 0x84C1, - TEXTURE2 : 0x84C2, - TEXTURE3 : 0x84C3, - TEXTURE4 : 0x84C4, - TEXTURE5 : 0x84C5, - TEXTURE6 : 0x84C6, - TEXTURE7 : 0x84C7, - TEXTURE8 : 0x84C8, - TEXTURE9 : 0x84C9, - TEXTURE10 : 0x84CA, - TEXTURE11 : 0x84CB, - TEXTURE12 : 0x84CC, - TEXTURE13 : 0x84CD, - TEXTURE14 : 0x84CE, - TEXTURE15 : 0x84CF, - TEXTURE16 : 0x84D0, - TEXTURE17 : 0x84D1, - TEXTURE18 : 0x84D2, - TEXTURE19 : 0x84D3, - TEXTURE20 : 0x84D4, - TEXTURE21 : 0x84D5, - TEXTURE22 : 0x84D6, - TEXTURE23 : 0x84D7, - TEXTURE24 : 0x84D8, - TEXTURE25 : 0x84D9, - TEXTURE26 : 0x84DA, - TEXTURE27 : 0x84DB, - TEXTURE28 : 0x84DC, - TEXTURE29 : 0x84DD, - TEXTURE30 : 0x84DE, - TEXTURE31 : 0x84DF, - ACTIVE_TEXTURE : 0x84E0, - - /* TextureWrapMode */ - REPEAT : 0x2901, - CLAMP_TO_EDGE : 0x812F, - MIRRORED_REPEAT : 0x8370, - - /* Uniform Types */ - FLOAT_VEC2 : 0x8B50, - FLOAT_VEC3 : 0x8B51, - FLOAT_VEC4 : 0x8B52, - INT_VEC2 : 0x8B53, - INT_VEC3 : 0x8B54, - INT_VEC4 : 0x8B55, - BOOL : 0x8B56, - BOOL_VEC2 : 0x8B57, - BOOL_VEC3 : 0x8B58, - BOOL_VEC4 : 0x8B59, - FLOAT_MAT2 : 0x8B5A, - FLOAT_MAT3 : 0x8B5B, - FLOAT_MAT4 : 0x8B5C, - SAMPLER_2D : 0x8B5E, - SAMPLER_CUBE : 0x8B60, - - /* Vertex Arrays */ - VERTEX_ATTRIB_ARRAY_ENABLED : 0x8622, - VERTEX_ATTRIB_ARRAY_SIZE : 0x8623, - VERTEX_ATTRIB_ARRAY_STRIDE : 0x8624, - VERTEX_ATTRIB_ARRAY_TYPE : 0x8625, - VERTEX_ATTRIB_ARRAY_NORMALIZED : 0x886A, - VERTEX_ATTRIB_ARRAY_POINTER : 0x8645, - VERTEX_ATTRIB_ARRAY_BUFFER_BINDING : 0x889F, - - /* Shader Source */ - COMPILE_STATUS : 0x8B81, - - /* Shader Precision-Specified Types */ - LOW_FLOAT : 0x8DF0, - MEDIUM_FLOAT : 0x8DF1, - HIGH_FLOAT : 0x8DF2, - LOW_INT : 0x8DF3, - MEDIUM_INT : 0x8DF4, - HIGH_INT : 0x8DF5, - - /* Framebuffer Object. */ - FRAMEBUFFER : 0x8D40, - RENDERBUFFER : 0x8D41, - - RGBA4 : 0x8056, - RGB5_A1 : 0x8057, - RGB565 : 0x8D62, - DEPTH_COMPONENT16 : 0x81A5, - STENCIL_INDEX : 0x1901, - STENCIL_INDEX8 : 0x8D48, - DEPTH_STENCIL : 0x84F9, - - RENDERBUFFER_WIDTH : 0x8D42, - RENDERBUFFER_HEIGHT : 0x8D43, - RENDERBUFFER_INTERNAL_FORMAT : 0x8D44, - RENDERBUFFER_RED_SIZE : 0x8D50, - RENDERBUFFER_GREEN_SIZE : 0x8D51, - RENDERBUFFER_BLUE_SIZE : 0x8D52, - RENDERBUFFER_ALPHA_SIZE : 0x8D53, - RENDERBUFFER_DEPTH_SIZE : 0x8D54, - RENDERBUFFER_STENCIL_SIZE : 0x8D55, - - FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE : 0x8CD0, - FRAMEBUFFER_ATTACHMENT_OBJECT_NAME : 0x8CD1, - FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL : 0x8CD2, - FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE : 0x8CD3, - - COLOR_ATTACHMENT0 : 0x8CE0, - DEPTH_ATTACHMENT : 0x8D00, - STENCIL_ATTACHMENT : 0x8D20, - DEPTH_STENCIL_ATTACHMENT : 0x821A, - - NONE : 0, - - FRAMEBUFFER_COMPLETE : 0x8CD5, - FRAMEBUFFER_INCOMPLETE_ATTACHMENT : 0x8CD6, - FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT : 0x8CD7, - FRAMEBUFFER_INCOMPLETE_DIMENSIONS : 0x8CD9, - FRAMEBUFFER_UNSUPPORTED : 0x8CDD, - - FRAMEBUFFER_BINDING : 0x8CA6, - RENDERBUFFER_BINDING : 0x8CA7, - MAX_RENDERBUFFER_SIZE : 0x84E8, - - INVALID_FRAMEBUFFER_OPERATION : 0x0506, - - /* WebGL-specific enums */ - UNPACK_FLIP_Y_WEBGL : 0x9240, - UNPACK_PREMULTIPLY_ALPHA_WEBGL : 0x9241, - CONTEXT_LOST_WEBGL : 0x9242, - UNPACK_COLORSPACE_CONVERSION_WEBGL : 0x9243, - BROWSER_DEFAULT_WEBGL : 0x9244, -}; -}); -define('qtek/core/Cache',[],function() { - - - - var Cache = function() { - - this._contextId = 0; - - this._caches = []; - - this._context = {}; - }; - - Cache.prototype = { - - use: function(contextId, documentSchema) { - - if (! this._caches[contextId]) { - this._caches[contextId] = {}; - - if (documentSchema) { - this._caches[contextId] = documentSchema(); - } - } - this._contextId = contextId; - - this._context = this._caches[contextId]; - }, - - put: function(key, value) { - this._context[key] = value; - }, - - get: function(key) { - return this._context[key]; - }, - - dirty: function(field) { - field = field || ''; - var key = '__dirty__' + field; - this.put(key, true); - }, - - dirtyAll: function(field) { - field = field || ''; - var key = '__dirty__' + field; - for (var i = 0; i < this._caches.length; i++) { - if (this._caches[i]) { - this._caches[i][key] = true; - } - } - }, - - fresh: function(field) { - field = field || ''; - var key = '__dirty__' + field; - this.put(key, false); - }, - - freshAll: function(field) { - field = field || ''; - var key = '__dirty__' + field; - for (var i = 0; i < this._caches.length; i++) { - if (this._caches[i]) { - this._caches[i][key] = false; - } - } - }, - - isDirty: function(field) { - field = field || ''; - var key = '__dirty__' + field; - return !this._context.hasOwnProperty(key) - || this._context[key] === true; - }, - - deleteContext: function(contextId) { - delete this._caches[contextId]; - this._context = {}; - }, - - 'delete': function(key) { - delete this._context[key]; - }, - - clearAll: function() { - this._caches = {}; - }, - - getContext: function() { - return this._context; - }, - - miss: function(key) { - return ! this._context.hasOwnProperty(key); - } - }; - - Cache.prototype.constructor = Cache; - - return Cache; + module.exports = exportsObject; + + +/***/ }, +/* 2 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Base = __webpack_require__(3); + var Animator = __webpack_require__(7); + + var requestAnimationFrame = window.requestAnimationFrame + || window.msRequestAnimationFrame + || window.mozRequestAnimationFrame + || window.webkitRequestAnimationFrame + || function (func){ setTimeout(func, 16); }; + + /** + * Animation is global timeline that schedule all clips. each frame animation will set the time of clips to current and update the states of clips + * @constructor qtek.animation.Animation + * @extends qtek.core.Base + * + * @example + * var animation = new qtek.animation.Animation(); + * var node = new qtek.Node(); + * animation.animate(node.position) + * .when(1000, { + * x: 500, + * y: 500 + * }) + * .when(2000, { + * x: 100, + * y: 100 + * }) + * .when(3000, { + * z: 10 + * }) + * .start('spline'); + */ + var Animation = Base.extend(function () { + return /** @lends qtek.animation.Animation# */{ + /** + * stage is an object with render method, each frame if there exists any animating clips, stage.render will be called + * @type {Object} + */ + stage: null, + + _clips: [], + + _running: false, + + _time: 0, + + _paused: false, + + _pausedTime: 0 + }; + }, + /** @lends qtek.animation.Animation.prototype */ + { + + /** + * Add animator + * @param {qtek.animate.Animator} animator + */ + addAnimator: function (animator) { + animator.animation = this; + var clips = animator.getClips(); + for (var i = 0; i < clips.length; i++) { + this.addClip(clips[i]); + } + }, + + /** + * @param {qtek.animation.Clip} clip + */ + addClip: function (clip) { + if (this._clips.indexOf(clip) < 0) { + this._clips.push(clip); + } + }, + + /** + * @param {qtek.animation.Clip} clip + */ + removeClip: function (clip) { + var idx = this._clips.indexOf(clip); + if (idx >= 0) { + this._clips.splice(idx, 1); + } + }, + + /** + * Remove animator + * @param {qtek.animate.Animator} animator + */ + removeAnimator: function (animator) { + var clips = animator.getClips(); + for (var i = 0; i < clips.length; i++) { + this.removeClip(clips[i]); + } + animator.animation = null; + }, + + _update: function () { + + var time = new Date().getTime() - this._pausedTime; + var delta = time - this._time; + var clips = this._clips; + var len = clips.length; + + var deferredEvents = []; + var deferredClips = []; + for (var i = 0; i < len; i++) { + var clip = clips[i]; + var e = clip.step(time); + // Throw out the events need to be called after + // stage.render, like finish + if (e) { + deferredEvents.push(e); + deferredClips.push(clip); + } + } + + // Remove the finished clip + for (var i = 0; i < len;) { + if (clips[i]._needsRemove) { + clips[i] = clips[len-1]; + clips.pop(); + len--; + } else { + i++; + } + } + + len = deferredEvents.length; + for (var i = 0; i < len; i++) { + deferredClips[i].fire(deferredEvents[i]); + } + + this._time = time; + + this.trigger('frame', delta); + + if (this.stage && this.stage.render) { + this.stage.render(); + } + }, + /** + * Start running animation + */ + start: function () { + var self = this; + + this._running = true; + this._time = new Date().getTime(); + + this._pausedTime = 0; + + function step() { + if (self._running) { + + requestAnimationFrame(step); + + self._update(); + } + } + + requestAnimationFrame(step); + + }, + /** + * Stop running animation + */ + stop: function () { + this._running = false; + }, + + /** + * Pause + */ + pause: function () { + if (!this._paused) { + this._pauseStart = new Date().getTime(); + this._paused = true; + } + }, + + /** + * Resume + */ + resume: function () { + if (this._paused) { + this._pausedTime += (new Date().getTime()) - this._pauseStart; + this._paused = false; + } + }, + + /** + * Remove all clips + */ + removeClipsAll: function () { + this._clips = []; + }, + /** + * Create a animator + * @param {Object} target + * @param {Object} [options] + * @param {boolean} [options.loop] + * @param {Function} [options.getter] + * @param {Function} [options.setter] + * @param {Function} [options.interpolater] + * @return {qtek.animation.Animator} + */ + animate: function (target, options) { + options = options || {}; + var animator = new Animator( + target, + options.loop, + options.getter, + options.setter, + options.interpolater + ); + animator.animation = this; + return animator; + } + }); + + module.exports = Animation; + + + +/***/ }, +/* 3 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var extendMixin = __webpack_require__(4); + var notifierMixin = __webpack_require__(5); + var util = __webpack_require__(6); + + /** + * Base class of all objects + * @constructor + * @alias qtek.core.Base + * @mixes qtek.core.mixin.notifier + */ + var Base = function () { + /** + * @type {number} + */ + this.__GUID__ = util.genGUID(); + }; + + Base.__initializers__ = [ + function (opts) { + util.extend(this, opts); + } + ]; + + util.extend(Base, extendMixin); + util.extend(Base.prototype, notifierMixin); + + module.exports = Base; + + +/***/ }, +/* 4 */ +/***/ function(module, exports) { + + 'use strict'; + + + /** + * Extend a sub class from base class + * @param {object|Function} makeDefaultOpt default option of this sub class, method of the sub can use this.xxx to access this option + * @param {Function} [initialize] Initialize after the sub class is instantiated + * @param {Object} [proto] Prototype methods/properties of the sub class + * @memberOf qtek.core.mixin.extend + * @return {Function} + */ + function derive(makeDefaultOpt, initialize/*optional*/, proto/*optional*/) { + + if (typeof initialize == 'object') { + proto = initialize; + initialize = null; + } + + var _super = this; + + var propList; + if (!(makeDefaultOpt instanceof Function)) { + // Optimize the property iterate if it have been fixed + propList = []; + for (var propName in makeDefaultOpt) { + if (makeDefaultOpt.hasOwnProperty(propName)) { + propList.push(propName); + } + } + } + + var sub = function(options) { + + // call super constructor + _super.apply(this, arguments); + + if (makeDefaultOpt instanceof Function) { + // Invoke makeDefaultOpt each time if it is a function, So we can make sure each + // property in the object will not be shared by mutiple instances + extend(this, makeDefaultOpt.call(this, options)); + } + else { + extendWithPropList(this, makeDefaultOpt, propList); + } + + if (this.constructor === sub) { + // Initialize function will be called in the order of inherit + var initializers = sub.__initializers__; + for (var i = 0; i < initializers.length; i++) { + initializers[i].apply(this, arguments); + } + } + }; + // save super constructor + sub.__super__ = _super; + // Initialize function will be called after all the super constructor is called + if (!_super.__initializers__) { + sub.__initializers__ = []; + } else { + sub.__initializers__ = _super.__initializers__.slice(); + } + if (initialize) { + sub.__initializers__.push(initialize); + } + + var Ctor = function() {}; + Ctor.prototype = _super.prototype; + sub.prototype = new Ctor(); + sub.prototype.constructor = sub; + extend(sub.prototype, proto); + + // extend the derive method as a static method; + sub.extend = _super.extend; + + // DEPCRATED + sub.derive = _super.extend; + + return sub; + } + + function extend(target, source) { + if (!source) { + return; + } + for (var name in source) { + if (source.hasOwnProperty(name)) { + target[name] = source[name]; + } + } + } + + function extendWithPropList(target, source, propList) { + for (var i = 0; i < propList.length; i++) { + var propName = propList[i]; + target[propName] = source[propName]; + } + } + + /** + * @alias qtek.core.mixin.extend + * @mixin + */ + module.exports = { + + extend: derive, + + // DEPCRATED + derive: derive + }; + + +/***/ }, +/* 5 */ +/***/ function(module, exports) { -}); -define('qtek/Geometry',['require','./core/Base','./core/glenum','./core/Cache','./dep/glmatrix'],function(require) { - - - - var Base = require('./core/Base'); - var glenum = require('./core/glenum'); - var Cache = require('./core/Cache'); - var glmatrix = require('./dep/glmatrix'); - var vec2 = glmatrix.vec2; - var vec3 = glmatrix.vec3; - var vec4 = glmatrix.vec4; - - // PENDING put the buffer data in attribute ? - function Attribute(name, type, size, semantic, isDynamic) { - this.name = name; - this.type = type; - this.size = size; - if (semantic) { - this.semantic = semantic; - } - if (isDynamic) { - this._isDynamic = true; - this.value = []; - } else { - this._isDynamic = false; - this.value = null; - } - - // Init getter setter - switch (size) { - case 1: - this.get = function (idx) { - return this.value[idx]; - }; - this.set = function (idx, value) { - this.value[idx] = value; - }; - break; - case 2: - if (isDynamic) { - this.get = function (idx, out) { - out = out._array || out; - var item = this.value[idx]; - if (item) { - vec2.copy(out, item); - } - return out; - }; - this.set = function (idx, val) { - val = val._array || val; - var item = this.value[idx]; - if (!item) { - item = this.value[idx] = vec2.create(); - } - vec2.copy(item, val); - }; - } else { - this.get = function (idx, out) { - out = out._array || out; - out[0] = this.value[idx * 2]; - out[1] = this.value[idx * 2 + 1]; - return out; - }; - this.set = function (idx, val) { - val = val._array || val; - this.value[idx * 2] = val[0]; - this.value[idx * 2 + 1] = val[1]; - }; - } - break; - case 3: - if (isDynamic) { - this.get = function (idx, out) { - out = out._array || out; - var item = this.value[idx]; - if (item) { - vec3.copy(out, item); - } - return out; - }; - this.set = function (idx, val) { - val = val._array || val; - var item = this.value[idx]; - if (!item) { - item = this.value[idx] = vec3.create(); - } - vec3.copy(item, val); - }; - } else { - this.get = function (idx, out) { - out = out._array || out; - out[0] = this.value[idx * 3]; - out[1] = this.value[idx * 3 + 1]; - out[2] = this.value[idx * 3 + 2]; - return out; - }; - this.set = function (idx, val) { - val = val._array || val; - this.value[idx * 3] = val[0]; - this.value[idx * 3 + 1] = val[1]; - this.value[idx * 3 + 2] = val[2]; - }; - } - break; - case 4: - if (isDynamic) { - this.get = function (idx, out) { - out = out._array || out; - var item = this.value[idx]; - if (item) { - vec4.copy(out, item); - } - return out; - }; - this.set = function (idx, val) { - val = val._array || val; - var item = this.value[idx]; - if (!item) { - item = this.value[idx] = vec4.create(); - } - vec4.copy(item, val); - }; - } else { - this.get = function (idx, out) { - out = out._array || out; - out[0] = this.value[idx * 4]; - out[1] = this.value[idx * 4 + 1]; - out[2] = this.value[idx * 4 + 2]; - out[3] = this.value[idx * 4 + 3]; - return out; - }; - this.set = function (idx, val) { - val = val._array || val; - this.value[idx * 4] = val[0]; - this.value[idx * 4 + 1] = val[1]; - this.value[idx * 4 + 2] = val[2]; - this.value[idx * 4 + 3] = val[3]; - }; - } - break; - } - } - - Attribute.prototype.init = function(nVertex) { - if (!this._isDynamic) { - if (!this.value || this.value.length != nVertex * this.size) { - var ArrayConstructor; - switch(this.type) { - case 'byte': - ArrayConstructor = Int8Array; - break; - case 'ubyte': - ArrayConstructor = Uint8Array; - break; - case 'short': - ArrayConstructor = Int16Array; - break; - case 'ushort': - ArrayConstructor = Uint16Array; - break; - default: - ArrayConstructor = Float32Array; - break; - } - this.value = new ArrayConstructor(nVertex * this.size); - } - } else { - console.warn('Dynamic geometry not support init method'); - } - }; - - Attribute.prototype.clone = function(copyValue) { - var ret = new Attribute(this.name, this.type, this.size, this.semantic, this._isDynamic); - if (copyValue) { - console.warn('todo'); - } - - return ret; - }; - - - function AttributeBuffer(name, type, buffer, size, semantic) { - this.name = name; - this.type = type; - this.buffer = buffer; - this.size = size; - this.semantic = semantic; - - // To be set in mesh - // symbol in the shader - this.symbol = ''; - } - - function IndicesBuffer(buffer) { - this.buffer = buffer; - this.count = 0; - } - - function notImplementedWarn() { - console.warn('Geometry doesn\'t implement this method, use DynamicGeometry or StaticGeometry instead'); - } - - /** - * @constructor qtek.Geometry - * @extends qtek.core.Base - */ - var Geometry = Base.derive( - /** @lends qtek.Geometry# */ - { - /** - * @type {qtek.math.BoundingBox} - */ - boundingBox : null, - - /** - * Vertex attributes - * @type {Object} - */ - attributes : {}, - - faces : null, - - /** - * Is vertices data dynamically updated - * @type {boolean} - */ - dynamic: false, - - /** - * @type {boolean} - */ - useFace : true - - }, function() { - // Use cache - this._cache = new Cache(); - - this._attributeList = Object.keys(this.attributes); - }, - /** @lends qtek.Geometry.prototype */ - { - /** - * User defined ray picking algorithm instead of default - * triangle ray intersection - * @type {Function} - */ - pickByRay: null, - - /** - * Main attribute will be used to count vertex number - * @type {string} - */ - mainAttribute: 'position', - /** - * Mark attributes in geometry is dirty - * @method - */ - dirty: notImplementedWarn, - /** - * Create a new attribute - * @method - * @param {string} name - * @param {string} type - * @param {number} size - * @param {string} [semantic] - */ - createAttribute: notImplementedWarn, - /** - * Remove attribute - * @method - * @param {string} name - */ - removeAttribute: notImplementedWarn, - /** - * @method - * @return {number} - */ - getVertexNumber: notImplementedWarn, - /** - * @method - * @return {number} - */ - getFaceNumber: notImplementedWarn, - /** - * @method - * @param {number} idx - * @param {qtek.math.Vector3} out - * @return {qtek.math.Vector3} - */ - getFace: notImplementedWarn, - /** - * @method - * @return {boolean} - */ - isUseFace: notImplementedWarn, - - getEnabledAttributes: notImplementedWarn, - getBufferChunks: notImplementedWarn, - - /** - * @method - */ - generateVertexNormals: notImplementedWarn, - /** - * @method - */ - generateFaceNormals: notImplementedWarn, - /** - * @method - * @return {boolean} - */ - isUniqueVertex: notImplementedWarn, - /** - * @method - */ - generateUniqueVertex: notImplementedWarn, - /** - * @method - */ - generateTangents: notImplementedWarn, - /** - * @method - */ - generateBarycentric: notImplementedWarn, - /** - * @method - * @param {qtek.math.Matrix4} matrix - */ - applyTransform: notImplementedWarn, - /** - * @method - * @param {WebGLRenderingContext} gl - */ - dispose: notImplementedWarn - }); - - Geometry.STATIC_DRAW = glenum.STATIC_DRAW; - Geometry.DYNAMIC_DRAW = glenum.DYNAMIC_DRAW; - Geometry.STREAM_DRAW = glenum.STREAM_DRAW; - - Geometry.AttributeBuffer = AttributeBuffer; - Geometry.IndicesBuffer = IndicesBuffer; - Geometry.Attribute = Attribute; - - return Geometry; -}); -/** - * @namespace qtek.core.glinfo - * @see http://www.khronos.org/registry/webgl/extensions/ - */ -define('qtek/core/glinfo',[],function() { - - - - var EXTENSION_LIST = [ - 'OES_texture_float', - 'OES_texture_half_float', - 'OES_texture_float_linear', - 'OES_texture_half_float_linear', - 'OES_standard_derivatives', - 'OES_vertex_array_object', - 'OES_element_index_uint', - 'WEBGL_compressed_texture_s3tc', - 'WEBGL_depth_texture', - 'EXT_texture_filter_anisotropic', - 'EXT_shader_texture_lod', - 'WEBGL_draw_buffers' - ]; - - var PARAMETER_NAMES = [ - 'MAX_TEXTURE_SIZE', - 'MAX_CUBE_MAP_TEXTURE_SIZE' - ] - - var extensions = {}; - var parameters = {}; - - var glinfo = { - /** - * Initialize all extensions and parameters in context - * @param {WebGLRenderingContext} _gl - * @memberOf qtek.core.glinfo - */ - initialize: function(_gl) { - var glid = _gl.__GLID__; - if (extensions[glid]) { - return; - } - extensions[glid] = {}; - parameters[glid] = {}; - // Get webgl extension - for (var i = 0; i < EXTENSION_LIST.length; i++) { - var extName = EXTENSION_LIST[i]; - - this._createExtension(_gl, extName); - } - // Get parameters - for (var i = 0; i < PARAMETER_NAMES.length; i++) { - var name = PARAMETER_NAMES[i]; - parameters[glid][name] = _gl.getParameter(_gl[name]); - } - }, - - /** - * Get extension - * @param {WebGLRenderingContext} _gl - * @param {string} name - Extension name, vendorless - * @return {WebGLExtension} - * @memberOf qtek.core.glinfo - */ - getExtension: function(_gl, name) { - var glid = _gl.__GLID__; - if (extensions[glid]) { - if (typeof(extensions[glid][name]) == 'undefined') { - this._createExtension(_gl, name); - } - return extensions[glid][name]; - } - }, - - /** - * Get parameter - * @param {WebGLRenderingContext} _gl - * @param {string} name Parameter name - * @return {*} - */ - getParameter: function(_gl, name) { - var glid = _gl.__GLID__; - if (parameters[glid]) { - return parameters[glid][name]; - } - }, - - /** - * Dispose context - * @param {WebGLRenderingContext} _gl - * @memberOf qtek.core.glinfo - */ - dispose: function(_gl) { - delete extensions[_gl.__GLID__]; - delete parameters[_gl.__GLID__]; - }, - - _createExtension: function(_gl, name) { - var ext = _gl.getExtension(name); - if (!ext) { - ext = _gl.getExtension('MOZ_' + name); - } - if (!ext) { - ext = _gl.getExtension('WEBKIT_' + name); - } - - extensions[_gl.__GLID__][name] = ext; - } - }; - - return glinfo; -}); -/** - * - * PENDING: use perfermance hint and remove the array after the data is transfered? - * static draw & dynamic draw? - */ -define('qtek/DynamicGeometry',['require','./Geometry','./math/BoundingBox','./core/glenum','./core/glinfo','./dep/glmatrix'],function(require) { - - - - var Geometry = require('./Geometry'); - var BoundingBox = require('./math/BoundingBox'); - var glenum = require('./core/glenum'); - var glinfo = require('./core/glinfo'); - - var glMatrix = require('./dep/glmatrix'); - var vec3 = glMatrix.vec3; - var mat4 = glMatrix.mat4; - - var arrSlice = Array.prototype.slice; - /** - * @constructor qtek.DynamicGeometry - * @extends qtek.Geometry - */ - var DynamicGeometry = Geometry.derive(function() { - return /** @lends qtek.DynamicGeometry# */ { - attributes: { - position: new Geometry.Attribute('position', 'float', 3, 'POSITION', true), - texcoord0: new Geometry.Attribute('texcoord0', 'float', 2, 'TEXCOORD_0', true), - texcoord1: new Geometry.Attribute('texcoord1', 'float', 2, 'TEXCOORD_1', true), - normal: new Geometry.Attribute('normal', 'float', 3, 'NORMAL', true), - tangent: new Geometry.Attribute('tangent', 'float', 4, 'TANGENT', true), - color: new Geometry.Attribute('color', 'float', 4, 'COLOR', true), - // Skinning attributes - // Each vertex can be bind to 4 bones, because the - // sum of weights is 1, so the weights is stored in vec3 and the last - // can be calculated by 1-w.x-w.y-w.z - weight: new Geometry.Attribute('weight', 'float', 3, 'WEIGHT', true), - joint: new Geometry.Attribute('joint', 'float', 4, 'JOINT', true), - // For wireframe display - // http://codeflow.org/entries/2012/aug/02/easy-wireframe-display-with-barycentric-coordinates/ - barycentric: new Geometry.Attribute('barycentric', 'float', 3, null, true) - }, - - dynamic: true, - - hint: glenum.DYNAMIC_DRAW, - - // Face is list of triangles, each face - // is an array of the vertex indices of triangle - - /** - * @type {array} - */ - faces: [], - - _enabledAttributes: null, - - // Typed Array of each geometry chunk - // [{ - // attributeArrays:{ - // position: TypedArray - // }, - // indicesArray: null - // }] - _arrayChunks: [] - }; - }, - /** @lends qtek.DynamicGeometry.prototype */ - { - updateBoundingBox: function() { - if (!this.boundingBox) { - this.boundingBox = new BoundingBox(); - } - this.boundingBox.updateFromVertices(this.attributes.position.value); - }, - // Overwrite the dirty method - dirty: function(field) { - if (!field) { - this.dirty('indices'); - for (var name in this.attributes) { - this.dirty(name); - } - return; - } - this._cache.dirtyAll(field); - - this._cache.dirtyAll(); - - this._enabledAttributes = null; - }, - - getVertexNumber: function() { - var mainAttribute = this.attributes[this.mainAttribute]; - if (!mainAttribute || !mainAttribute.value) { - return 0; - } - return mainAttribute.value.length; - }, - - getFaceNumber: function() { - return this.faces.length; - }, - - getFace: function (idx, out) { - if (idx < this.getFaceNumber() && idx >= 0) { - if (!out) { - out = vec3.create(); - } - vec3.copy(out, this.faces[idx]); - - return out; - } - }, - - isUseFace: function() { - return this.useFace && (this.faces.length > 0); - }, - - isSplitted: function() { - return this.getVertexNumber() > 0xffff; - }, - - createAttribute: function(name, type, size, semantic) { - var attrib = new Geometry.Attribute(name, type, size, semantic, true); - this.attributes[name] = attrib; - this._attributeList.push(name); - return attrib; - }, - - removeAttribute: function(name) { - var idx = this._attributeList.indexOf(name); - if (idx >= 0) { - this._attributeList.splice(idx, 1); - delete this.attributes[name]; - return true; - } - return false; - }, - - /** - * Get enabled attributes map. - * Attribute that has same vertex number with position is treated as an enabled attribute - * @return {Object} - */ - getEnabledAttributes: function() { - // Cache - if (this._enabledAttributes) { - return this._enabledAttributes; - } - - var result = {}; - var nVertex = this.getVertexNumber(); - - for (var i = 0; i < this._attributeList.length; i++) { - var name = this._attributeList[i]; - var attrib = this.attributes[name]; - if (attrib.value.length) { - if (attrib.value.length === nVertex) { - result[name] = attrib; - } - } - } - - this._enabledAttributes = result; - - return result; - }, - - _getDirtyAttributes: function() { - - var attributes = this.getEnabledAttributes(); - - if (this._cache.miss('chunks')) { - return attributes; - } else { - var result = {}; - var noDirtyAttributes = true; - for (var name in attributes) { - if (this._cache.isDirty(name)) { - result[name] = attributes[name]; - noDirtyAttributes = false; - } - } - if (! noDirtyAttributes) { - return result; - } - } - }, - - getChunkNumber: function() { - return this._arrayChunks.length; - }, - - getBufferChunks: function(_gl) { - - this._cache.use(_gl.__GLID__); - - if (this._cache.isDirty()) { - var dirtyAttributes = this._getDirtyAttributes(); - - var isFacesDirty = this._cache.isDirty('indices'); - isFacesDirty = isFacesDirty && this.isUseFace(); - - if (dirtyAttributes) { - this._updateAttributesAndIndicesArrays( - dirtyAttributes, isFacesDirty, - glinfo.getExtension(_gl, 'OES_element_index_uint') != null - ); - this._updateBuffer(_gl, dirtyAttributes, isFacesDirty); - - for (var name in dirtyAttributes) { - this._cache.fresh(name); - } - this._cache.fresh('indices'); - this._cache.fresh(); - } - } - return this._cache.get('chunks'); - }, - - _updateAttributesAndIndicesArrays: function(attributes, isFacesDirty, useUintExtension) { - - var self = this; - var nVertex = this.getVertexNumber(); - - var verticesReorganizedMap = []; - var reorganizedFaces = []; - - var ArrayConstructors = {}; - for (var name in attributes) { - // Type can be byte, ubyte, short, ushort, float - switch(type) { - case 'byte': - ArrayConstructors[name] = Int8Array; - break; - case 'ubyte': - ArrayConstructors[name] = Uint8Array; - break; - case 'short': - ArrayConstructors[name] = Int16Array; - break; - case 'ushort': - ArrayConstructors[name] = Uint16Array; - break; - default: - ArrayConstructors[name] = Float32Array; - break; - } - } - - var newChunk = function(chunkIdx) { - if (self._arrayChunks[chunkIdx]) { - return self._arrayChunks[chunkIdx]; - } - var chunk = { - attributeArrays: {}, - indicesArray: null - }; - - for (var name in attributes) { - chunk.attributeArrays[name] = null; - } - - for (var i = 0; i < nVertex; i++) { - verticesReorganizedMap[i] = -1; - } - - self._arrayChunks.push(chunk); - return chunk; - }; - - var attribNameList = Object.keys(attributes); - // Split large geometry into chunks because index buffer - // only can use uint16 which means each draw call can only - // have at most 65535 vertex data - // But now most browsers support OES_element_index_uint extension - if ( - nVertex > 0xffff && this.isUseFace() && !useUintExtension - ) { - var chunkIdx = 0; - var currentChunk; - - var chunkFaceStart = [0]; - var vertexUseCount = []; - - for (i = 0; i < nVertex; i++) { - vertexUseCount[i] = -1; - verticesReorganizedMap[i] = -1; - } - if (isFacesDirty) { - for (i = 0; i < this.faces.length; i++) { - reorganizedFaces[i] = [0, 0, 0]; - } - } - - currentChunk = newChunk(chunkIdx); - - var vertexCount = 0; - for (var i = 0; i < this.faces.length; i++) { - var face = this.faces[i]; - var reorganizedFace = reorganizedFaces[i]; - - // newChunk - if (vertexCount+3 > 0xffff) { - chunkIdx++; - chunkFaceStart[chunkIdx] = i; - vertexCount = 0; - currentChunk = newChunk(chunkIdx); - } - - for (var f = 0; f < 3; f++) { - var ii = face[f]; - var isNew = verticesReorganizedMap[ii] === -1; - - for (var k = 0; k < attribNameList.length; k++) { - var name = attribNameList[k]; - var attribArray = currentChunk.attributeArrays[name]; - var values = attributes[name].value; - var size = attributes[name].size; - if (! attribArray) { - // Here use array to put data temporary because i can't predict - // the size of chunk precisely. - attribArray = currentChunk.attributeArrays[name] = []; - } - if (isNew) { - if (size === 1) { - attribArray[vertexCount] = values[ii]; - } - for (var j = 0; j < size; j++) { - attribArray[vertexCount * size + j] = values[ii][j]; - } - } - } - if (isNew) { - verticesReorganizedMap[ii] = vertexCount; - reorganizedFace[f] = vertexCount; - vertexCount++; - } else { - reorganizedFace[f] = verticesReorganizedMap[ii]; - } - } - } - //Create typedArray from existed array - for (var c = 0; c < this._arrayChunks.length; c++) { - var chunk = this._arrayChunks[c]; - for (var name in chunk.attributeArrays) { - var array = chunk.attributeArrays[name]; - if (array instanceof Array) { - chunk.attributeArrays[name] = new ArrayConstructors[name](array); - } - } - } - - if (isFacesDirty) { - var chunkStart, chunkEnd, cursor, chunk; - for (var c = 0; c < this._arrayChunks.length; c++) { - chunkStart = chunkFaceStart[c]; - chunkEnd = chunkFaceStart[c+1] || this.faces.length; - cursor = 0; - chunk = this._arrayChunks[c]; - var indicesArray = chunk.indicesArray; - if (! indicesArray) { - indicesArray = chunk.indicesArray = new Uint16Array((chunkEnd-chunkStart)*3); - } - - for (var i = chunkStart; i < chunkEnd; i++) { - indicesArray[cursor++] = reorganizedFaces[i][0]; - indicesArray[cursor++] = reorganizedFaces[i][1]; - indicesArray[cursor++] = reorganizedFaces[i][2]; - } - } - } - } else { - var chunk = newChunk(0); - // Use faces - if (isFacesDirty) { - var indicesArray = chunk.indicesArray; - var nFace = this.faces.length; - if (!indicesArray || (nFace * 3 !== indicesArray.length)) { - var ArrayCtor = nVertex > 0xffff ? Uint32Array : Uint16Array; - indicesArray = chunk.indicesArray = new ArrayCtor(this.faces.length * 3); - } - var cursor = 0; - for (var i = 0; i < nFace; i++) { - indicesArray[cursor++] = this.faces[i][0]; - indicesArray[cursor++] = this.faces[i][1]; - indicesArray[cursor++] = this.faces[i][2]; - } - } - for (var name in attributes) { - var values = attributes[name].value; - var type = attributes[name].type; - var size = attributes[name].size; - var attribArray = chunk.attributeArrays[name]; - - var arrSize = nVertex * size; - if (! attribArray || attribArray.length !== arrSize) { - attribArray = new ArrayConstructors[name](arrSize); - chunk.attributeArrays[name] = attribArray; - } - - if (size === 1) { - for (var i = 0; i < values.length; i++) { - attribArray[i] = values[i]; - } - } else { - var cursor = 0; - for (var i = 0; i < values.length; i++) { - for (var j = 0; j < size; j++) { - attribArray[cursor++] = values[i][j]; - } - } - } - } - } - }, - - _updateBuffer: function(_gl, dirtyAttributes, isFacesDirty) { - var chunks = this._cache.get('chunks'); - var firstUpdate = false; - if (! chunks) { - chunks = []; - // Intialize - for (var i = 0; i < this._arrayChunks.length; i++) { - chunks[i] = { - attributeBuffers: [], - indicesBuffer: null - }; - } - this._cache.put('chunks', chunks); - firstUpdate = true; - } - for (var cc = 0; cc < this._arrayChunks.length; cc++) { - var chunk = chunks[cc]; - if (! chunk) { - chunk = chunks[cc] = { - attributeBuffers: [], - indicesBuffer: null - }; - } - var attributeBuffers = chunk.attributeBuffers; - var indicesBuffer = chunk.indicesBuffer; - - var arrayChunk = this._arrayChunks[cc]; - var attributeArrays = arrayChunk.attributeArrays; - var indicesArray = arrayChunk.indicesArray; - - var count = 0; - var prevSearchIdx = 0; - for (var name in dirtyAttributes) { - var attribute = dirtyAttributes[name]; - var type = attribute.type; - var semantic = attribute.semantic; - var size = attribute.size; - - var bufferInfo; - if (!firstUpdate) { - for (var i = prevSearchIdx; i < attributeBuffers.length; i++) { - if (attributeBuffers[i].name === name) { - bufferInfo = attributeBuffers[i]; - prevSearchIdx = i + 1; - break; - } - } - if (!bufferInfo) { - for (var i = prevSearchIdx - 1; i >= 0; i--) { - if (attributeBuffers[i].name === name) { - bufferInfo = attributeBuffers[i]; - prevSearchIdx = i; - break; - } - } - } - } - - var buffer; - if (bufferInfo) { - buffer = bufferInfo.buffer; - } else { - buffer = _gl.createBuffer(); - } - //TODO: Use BufferSubData? - _gl.bindBuffer(_gl.ARRAY_BUFFER, buffer); - _gl.bufferData(_gl.ARRAY_BUFFER, attributeArrays[name], this.hint); - - attributeBuffers[count++] = new Geometry.AttributeBuffer(name, type, buffer, size, semantic); - } - attributeBuffers.length = count; - - if (isFacesDirty) { - if (! indicesBuffer) { - indicesBuffer = new Geometry.IndicesBuffer(_gl.createBuffer()); - chunk.indicesBuffer = indicesBuffer; - } - indicesBuffer.count = indicesArray.length; - _gl.bindBuffer(_gl.ELEMENT_ARRAY_BUFFER, indicesBuffer.buffer); - _gl.bufferData(_gl.ELEMENT_ARRAY_BUFFER, indicesArray, this.hint); - } - } - }, - - generateVertexNormals: function() { - var faces = this.faces; - var len = faces.length; - var positions = this.attributes.position.value; - var normals = this.attributes.normal.value; - var normal = vec3.create(); - - var v21 = vec3.create(), v32 = vec3.create(); - - for (var i = 0; i < normals.length; i++) { - vec3.set(normals[i], 0.0, 0.0, 0.0); - } - for (var i = normals.length; i < positions.length; i++) { - //Use array instead of Float32Array - normals[i] = [0.0, 0.0, 0.0]; - } - - for (var f = 0; f < len; f++) { - - var face = faces[f]; - var i1 = face[0]; - var i2 = face[1]; - var i3 = face[2]; - var p1 = positions[i1]; - var p2 = positions[i2]; - var p3 = positions[i3]; - - vec3.sub(v21, p1, p2); - vec3.sub(v32, p2, p3); - vec3.cross(normal, v21, v32); - // Weighted by the triangle area - vec3.add(normals[i1], normals[i1], normal); - vec3.add(normals[i2], normals[i2], normal); - vec3.add(normals[i3], normals[i3], normal); - } - for (var i = 0; i < normals.length; i++) { - vec3.normalize(normals[i], normals[i]); - } - }, - - generateFaceNormals: function() { - if (! this.isUniqueVertex()) { - this.generateUniqueVertex(); - } - - var faces = this.faces; - var len = faces.length; - var positions = this.attributes.position.value; - var normals = this.attributes.normal.value; - var normal = vec3.create(); - - var v21 = vec3.create(), v32 = vec3.create(); - - var isCopy = normals.length === positions.length; - - for (var i = 0; i < len; i++) { - var face = faces[i]; - var i1 = face[0]; - var i2 = face[1]; - var i3 = face[2]; - var p1 = positions[i1]; - var p2 = positions[i2]; - var p3 = positions[i3]; - - vec3.sub(v21, p1, p2); - vec3.sub(v32, p2, p3); - vec3.cross(normal, v21, v32); - - if (isCopy) { - vec3.copy(normals[i1], normal); - vec3.copy(normals[i2], normal); - vec3.copy(normals[i3], normal); - } else { - normals[i1] = normals[i2] = normals[i3] = arrSlice.call(normal); - } - } - }, - // 'Mathmatics for 3D programming and computer graphics, third edition' - // section 7.8.2 - // http://www.crytek.com/download/Triangle_mesh_tangent_space_calculation.pdf - generateTangents: function() { - - var texcoords = this.attributes.texcoord0.value; - var positions = this.attributes.position.value; - var tangents = this.attributes.tangent.value; - var normals = this.attributes.normal.value; - - var tan1 = []; - var tan2 = []; - var nVertex = this.getVertexNumber(); - for (var i = 0; i < nVertex; i++) { - tan1[i] = [0.0, 0.0, 0.0]; - tan2[i] = [0.0, 0.0, 0.0]; - } - - var sdir = [0.0, 0.0, 0.0]; - var tdir = [0.0, 0.0, 0.0]; - for (var i = 0; i < this.faces.length; i++) { - var face = this.faces[i], - i1 = face[0], - i2 = face[1], - i3 = face[2], - - st1 = texcoords[i1], - st2 = texcoords[i2], - st3 = texcoords[i3], - - p1 = positions[i1], - p2 = positions[i2], - p3 = positions[i3]; - - var x1 = p2[0] - p1[0], - x2 = p3[0] - p1[0], - y1 = p2[1] - p1[1], - y2 = p3[1] - p1[1], - z1 = p2[2] - p1[2], - z2 = p3[2] - p1[2]; - - var s1 = st2[0] - st1[0], - s2 = st3[0] - st1[0], - t1 = st2[1] - st1[1], - t2 = st3[1] - st1[1]; - - var r = 1.0 / (s1 * t2 - t1 * s2); - sdir[0] = (t2 * x1 - t1 * x2) * r; - sdir[1] = (t2 * y1 - t1 * y2) * r; - sdir[2] = (t2 * z1 - t1 * z2) * r; - - tdir[0] = (s1 * x2 - s2 * x1) * r; - tdir[1] = (s1 * y2 - s2 * y1) * r; - tdir[2] = (s1 * z2 - s2 * z1) * r; - - vec3.add(tan1[i1], tan1[i1], sdir); - vec3.add(tan1[i2], tan1[i2], sdir); - vec3.add(tan1[i3], tan1[i3], sdir); - vec3.add(tan2[i1], tan2[i1], tdir); - vec3.add(tan2[i2], tan2[i2], tdir); - vec3.add(tan2[i3], tan2[i3], tdir); - } - var tmp = [0, 0, 0, 0]; - var nCrossT = [0, 0, 0]; - for (var i = 0; i < nVertex; i++) { - var n = normals[i]; - var t = tan1[i]; - - // Gram-Schmidt orthogonalize - vec3.scale(tmp, n, vec3.dot(n, t)); - vec3.sub(tmp, t, tmp); - vec3.normalize(tmp, tmp); - // Calculate handedness. - vec3.cross(nCrossT, n, t); - tmp[3] = vec3.dot(nCrossT, tan2[i]) < 0.0 ? -1.0 : 1.0; - tangents[i] = tmp.slice(); - } - }, - - isUniqueVertex: function() { - if (this.isUseFace()) { - return this.getVertexNumber() === this.faces.length * 3; - } else { - return true; - } - }, - - generateUniqueVertex: function() { - - var vertexUseCount = []; - // Intialize with empty value, read undefined value from array - // is slow - // http://jsperf.com/undefined-array-read - for (var i = 0; i < this.getVertexNumber(); i++) { - vertexUseCount[i] = 0; - } - - var cursor = this.getVertexNumber(); - var attributes = this.getEnabledAttributes(); - var faces = this.faces; - - var attributeNameList = Object.keys(attributes); - - for (var i = 0; i < faces.length; i++) { - var face = faces[i]; - for (var j = 0; j < 3; j++) { - var ii = face[j]; - if (vertexUseCount[ii] > 0) { - for (var a = 0; a < attributeNameList.length; a++) { - var name = attributeNameList[a]; - var array = attributes[name].value; - var size = attributes[name].size; - if (size === 1) { - array.push(array[ii]); - } else { - array.push(arrSlice.call(array[ii])); - } - } - face[j] = cursor; - cursor++; - } - vertexUseCount[ii]++; - } - } - - this.dirty(); - }, - - // http://codeflow.org/entries/2012/aug/02/easy-wireframe-display-with-barycentric-coordinates/ - // http://en.wikipedia.org/wiki/Barycentric_coordinate_system_(mathematics) - generateBarycentric: (function() { - var a = [1, 0, 0]; - var b = [0, 0, 1]; - var c = [0, 1, 0]; - return function() { - - if (! this.isUniqueVertex()) { - this.generateUniqueVertex(); - } - - var array = this.attributes.barycentric.value; - // Already existed; - if (array.length == this.faces.length * 3) { - return; - } - var i1, i2, i3, face; - for (var i = 0; i < this.faces.length; i++) { - face = this.faces[i]; - i1 = face[0]; - i2 = face[1]; - i3 = face[2]; - array[i1] = a; - array[i2] = b; - array[i3] = c; - } - }; - })(), - - convertToStatic: function(geometry, useUintExtension) { - this._updateAttributesAndIndicesArrays(this.getEnabledAttributes(), true, useUintExtension); - - if (this._arrayChunks.length > 1) { - console.warn('Large geometry will discard chunks when convert to StaticGeometry'); - } - else if (this._arrayChunks.length === 0) { - return geometry; - } - var chunk = this._arrayChunks[0]; - - var attributes = this.getEnabledAttributes(); - for (var name in attributes) { - var attrib = attributes[name]; - var geoAttrib = geometry.attributes[name]; - if (!geoAttrib) { - geoAttrib = geometry.attributes[name] = { - type: attrib.type, - size: attrib.size, - value: null - }; - if (attrib.semantic) { - geoAttrib.semantic = attrib.semantic; - } - } - geoAttrib.value = chunk.attributeArrays[name]; - } - geometry.faces = chunk.indicesArray; - - if (this.boundingBox) { - geometry.boundingBox = new BoundingBox(); - geometry.boundingBox.min.copy(this.boundingBox.min); - geometry.boundingBox.max.copy(this.boundingBox.max); - } - // PENDING copy buffer ? - return geometry; - }, - - applyTransform: function(matrix) { - - var positions = this.attributes.position.value; - var normals = this.attributes.normal.value; - var tangents = this.attributes.tangent.value; - - matrix = matrix._array; - for (var i = 0; i < positions.length; i++) { - vec3.transformMat4(positions[i], positions[i], matrix); - } - // Normal Matrix - var inverseTransposeMatrix = mat4.create(); - mat4.invert(inverseTransposeMatrix, matrix); - mat4.transpose(inverseTransposeMatrix, inverseTransposeMatrix); - - for (var i = 0; i < normals.length; i++) { - vec3.transformMat4(normals[i], normals[i], inverseTransposeMatrix); - } - - for (var i = 0; i < tangents.length; i++) { - vec3.transformMat4(tangents[i], tangents[i], inverseTransposeMatrix); - } - - if (this.boundingBox) { - this.updateBoundingBox(); - } - }, - - dispose: function(_gl) { - this._cache.use(_gl.__GLID__); - var chunks = this._cache.get('chunks'); - if (chunks) { - for (var c = 0; c < chunks.length; c++) { - var chunk = chunks[c]; - for (var k = 0; k < chunk.attributeBuffers.length; k++) { - var attribs = chunk.attributeBuffers[k]; - _gl.deleteBuffer(attribs.buffer); - } - } - } - this._cache.deleteContext(_gl.__GLID__); - } - }); - - return DynamicGeometry; -}); -/** - * Base class for all textures like compressed texture, texture2d, texturecube - * TODO mapping - */ -define('qtek/Texture',['require','./core/Base','./core/glenum','./core/Cache'],function(require) { - - - - var Base = require('./core/Base'); - var glenum = require('./core/glenum'); - var Cache = require('./core/Cache'); - - /** - * @constructor qtek.Texture - * @extends qtek.core.Base - */ - var Texture = Base.derive( - /** @lends qtek.Texture# */ - { - /** - * Texture width, only needed when the texture is used as a render target - * @type {number} - */ - width: 512, - /** - * Texture height, only needed when the texture is used as a render target - * @type {number} - */ - height: 512, - /** - * Texel data type - * @type {number} - */ - type: glenum.UNSIGNED_BYTE, - /** - * Format of texel data - * @type {number} - */ - format: glenum.RGBA, - /** - * @type {number} - */ - wrapS: glenum.CLAMP_TO_EDGE, - /** - * @type {number} - */ - wrapT: glenum.CLAMP_TO_EDGE, - /** - * @type {number} - */ - minFilter: glenum.LINEAR_MIPMAP_LINEAR, - /** - * @type {number} - */ - magFilter: glenum.LINEAR, - /** - * @type {boolean} - */ - useMipmap: true, - - /** - * Anisotropic filtering, enabled if value is larger than 1 - * @see http://blog.tojicode.com/2012/03/anisotropic-filtering-in-webgl.html - * @type {number} - */ - anisotropic: 1, - // pixelStorei parameters - // http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml - /** - * @type {boolean} - */ - flipY: true, - /** - * @type {number} - */ - unpackAlignment: 4, - /** - * @type {boolean} - */ - premultiplyAlpha: false, - - /** - * Dynamic option for texture like video - * @type {boolean} - */ - dynamic: false, - - NPOT: false - }, function() { - this._cache = new Cache(); - }, - /** @lends qtek.Texture.prototype */ - { - - getWebGLTexture: function(_gl) { - var cache = this._cache; - cache.use(_gl.__GLID__); - - if (cache.miss('webgl_texture')) { - // In a new gl context, create new texture and set dirty true - cache.put('webgl_texture', _gl.createTexture()); - } - if (this.dynamic) { - this.update(_gl); - } - else if (cache.isDirty()) { - this.update(_gl); - cache.fresh(); - } - - return cache.get('webgl_texture'); - }, - - bind: function() {}, - unbind: function() {}, - - /** - * Mark texture is dirty and update in the next frame - */ - dirty: function() { - this._cache.dirtyAll(); - }, - - update: function(_gl) {}, - - // Update the common parameters of texture - beforeUpdate: function(_gl) { - _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, this.flipY); - _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this.premultiplyAlpha); - _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, this.unpackAlignment); - - this.fallBack(); - }, - - fallBack: function() { - // Use of none-power of two texture - // http://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences - - var isPowerOfTwo = this.isPowerOfTwo(); - - if (this.format === glenum.DEPTH_COMPONENT) { - this.useMipmap = false; - } - - if (! isPowerOfTwo || ! this.useMipmap) { - // none-power of two flag - this.NPOT = true; - // Save the original value for restore - this._minFilterOriginal = this.minFilter; - this._magFilterOriginal = this.magFilter; - this._wrapSOriginal = this.wrapS; - this._wrapTOriginal = this.wrapT; - - if (this.minFilter == glenum.NEAREST_MIPMAP_NEAREST || - this.minFilter == glenum.NEAREST_MIPMAP_LINEAR) { - this.minFilter = glenum.NEAREST; - } else if ( - this.minFilter == glenum.LINEAR_MIPMAP_LINEAR || - this.minFilter == glenum.LINEAR_MIPMAP_NEAREST - ) { - this.minFilter = glenum.LINEAR; - } - - this.wrapS = glenum.CLAMP_TO_EDGE; - this.wrapT = glenum.CLAMP_TO_EDGE; - } else { - this.NPOT = false; - if (this._minFilterOriginal) { - this.minFilter = this._minFilterOriginal; - } - if (this._magFilterOriginal) { - this.magFilter = this._magFilterOriginal; - } - if (this._wrapSOriginal) { - this.wrapS = this._wrapSOriginal; - } - if (this._wrapTOriginal) { - this.wrapT = this._wrapTOriginal; - } - } - - }, - - nextHighestPowerOfTwo: function(x) { - --x; - for (var i = 1; i < 32; i <<= 1) { - x = x | x >> i; - } - return x + 1; - }, - /** - * @param {WebGLRenderingContext} _gl - */ - dispose: function(_gl) { - var cache = this._cache; - cache.use(_gl.__GLID__); - - var webglTexture = cache.get('webgl_texture'); - if (webglTexture){ - _gl.deleteTexture(webglTexture); - } - cache.deleteContext(_gl.__GLID__); - }, - /** - * Test if image of texture is valid and loaded. - * @return {boolean} - */ - isRenderable: function() {}, - - isPowerOfTwo: function() {} - }); - - /* DataType */ - Texture.BYTE = glenum.BYTE; - Texture.UNSIGNED_BYTE = glenum.UNSIGNED_BYTE; - Texture.SHORT = glenum.SHORT; - Texture.UNSIGNED_SHORT = glenum.UNSIGNED_SHORT; - Texture.INT = glenum.INT; - Texture.UNSIGNED_INT = glenum.UNSIGNED_INT; - Texture.FLOAT = glenum.FLOAT; - Texture.HALF_FLOAT = 0x8D61; - - /* PixelFormat */ - Texture.DEPTH_COMPONENT = glenum.DEPTH_COMPONENT; - Texture.ALPHA = glenum.ALPHA; - Texture.RGB = glenum.RGB; - Texture.RGBA = glenum.RGBA; - Texture.LUMINANCE = glenum.LUMINANCE; - Texture.LUMINANCE_ALPHA = glenum.LUMINANCE_ALPHA; - - /* Compressed Texture */ - Texture.COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0; - Texture.COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1; - Texture.COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2; - Texture.COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3; - - /* TextureMagFilter */ - Texture.NEAREST = glenum.NEAREST; - Texture.LINEAR = glenum.LINEAR; - - /* TextureMinFilter */ - /* NEAREST */ - /* LINEAR */ - Texture.NEAREST_MIPMAP_NEAREST = glenum.NEAREST_MIPMAP_NEAREST; - Texture.LINEAR_MIPMAP_NEAREST = glenum.LINEAR_MIPMAP_NEAREST; - Texture.NEAREST_MIPMAP_LINEAR = glenum.NEAREST_MIPMAP_LINEAR; - Texture.LINEAR_MIPMAP_LINEAR = glenum.LINEAR_MIPMAP_LINEAR; - - /* TextureParameterName */ - Texture.TEXTURE_MAG_FILTER = glenum.TEXTURE_MAG_FILTER; - Texture.TEXTURE_MIN_FILTER = glenum.TEXTURE_MIN_FILTER; - - /* TextureWrapMode */ - Texture.REPEAT = glenum.REPEAT; - Texture.CLAMP_TO_EDGE = glenum.CLAMP_TO_EDGE; - Texture.MIRRORED_REPEAT = glenum.MIRRORED_REPEAT; - - - return Texture; -}); -define('qtek/TextureCube',['require','./Texture','./core/glinfo','./core/glenum','./core/util'],function(require) { - - var Texture = require('./Texture'); - var glinfo = require('./core/glinfo'); - var glenum = require('./core/glenum'); - var util = require('./core/util'); - - var targetList = ['px', 'nx', 'py', 'ny', 'pz', 'nz']; - - /** - * @constructor qtek.TextureCube - * @extends qtek.Texture - * - * @example - * ... - * var mat = new qtek.Material({ - * shader: qtek.shader.library.get('buildin.phong', 'environmentMap') - * }); - * var envMap = new qtek.TextureCube(); - * envMap.load({ - * 'px': 'assets/textures/sky/px.jpg', - * 'nx': 'assets/textures/sky/nx.jpg' - * 'py': 'assets/textures/sky/py.jpg' - * 'ny': 'assets/textures/sky/ny.jpg' - * 'pz': 'assets/textures/sky/pz.jpg' - * 'nz': 'assets/textures/sky/nz.jpg' - * }); - * mat.set('environmentMap', envMap); - * ... - * envMap.success(function() { - * // Wait for the sky texture loaded - * animation.on('frame', function(frameTime) { - * renderer.render(scene, camera); - * }); - * }); - */ - var TextureCube = Texture.derive(function() { - return /** @lends qtek.TextureCube# */{ - /** - * @type {Object} - * @property {HTMLImageElement|HTMLCanvasElemnet} px - * @property {HTMLImageElement|HTMLCanvasElemnet} nx - * @property {HTMLImageElement|HTMLCanvasElemnet} py - * @property {HTMLImageElement|HTMLCanvasElemnet} ny - * @property {HTMLImageElement|HTMLCanvasElemnet} pz - * @property {HTMLImageElement|HTMLCanvasElemnet} nz - */ - image: { - px: null, - nx: null, - py: null, - ny: null, - pz: null, - nz: null - }, - /** - * @type {Object} - * @property {Uint8Array} px - * @property {Uint8Array} nx - * @property {Uint8Array} py - * @property {Uint8Array} ny - * @property {Uint8Array} pz - * @property {Uint8Array} nz - */ - pixels: { - px: null, - nx: null, - py: null, - ny: null, - pz: null, - nz: null - }, - - /** - * @type {Array.} - */ - mipmaps: [] - }; - }, { - update: function(_gl) { - - _gl.bindTexture(_gl.TEXTURE_CUBE_MAP, this._cache.get('webgl_texture')); - - this.beforeUpdate(_gl); - - var glFormat = this.format; - var glType = this.type; - - _gl.texParameteri(_gl.TEXTURE_CUBE_MAP, _gl.TEXTURE_WRAP_S, this.wrapS); - _gl.texParameteri(_gl.TEXTURE_CUBE_MAP, _gl.TEXTURE_WRAP_T, this.wrapT); - - _gl.texParameteri(_gl.TEXTURE_CUBE_MAP, _gl.TEXTURE_MAG_FILTER, this.magFilter); - _gl.texParameteri(_gl.TEXTURE_CUBE_MAP, _gl.TEXTURE_MIN_FILTER, this.minFilter); - - var anisotropicExt = glinfo.getExtension(_gl, 'EXT_texture_filter_anisotropic'); - if (anisotropicExt && this.anisotropic > 1) { - _gl.texParameterf(_gl.TEXTURE_CUBE_MAP, anisotropicExt.TEXTURE_MAX_ANISOTROPY_EXT, this.anisotropic); - } - - // Fallback to float type if browser don't have half float extension - if (glType === 36193) { - var halfFloatExt = glinfo.getExtension(_gl, 'OES_texture_half_float'); - if (!halfFloatExt) { - glType = glenum.FLOAT; - } - } - - if (this.mipmaps.length) { - var width = this.width; - var height = this.height; - for (var i = 0; i < this.mipmaps.length; i++) { - var mipmap = this.mipmaps[i]; - this._updateTextureData(_gl, mipmap, i, width, height, glFormat, glType); - width /= 2; - height /= 2; - } - } - else { - this._updateTextureData(_gl, this, 0, this.width, this.height, glFormat, glType); - - if (!this.NPOT && this.useMipmap) { - _gl.generateMipmap(_gl.TEXTURE_CUBE_MAP); - } - } - - _gl.bindTexture(_gl.TEXTURE_CUBE_MAP, null); - }, - - _updateTextureData: function (_gl, data, level, width, height, glFormat, glType) { - for (var i = 0; i < 6; i++) { - var target = targetList[i]; - var img = data.image && data.image[target]; - if (img) { - _gl.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, level, glFormat, glFormat, glType, img); - } - else { - _gl.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, level, glFormat, width, height, 0, glFormat, glType, data.pixels && data.pixels[target]); - } - } - }, - - /** - * @param {WebGLRenderingContext} _gl - * @memberOf qtek.TextureCube.prototype - */ - generateMipmap: function(_gl) { - if (this.useMipmap && !this.NPOT) { - _gl.bindTexture(_gl.TEXTURE_CUBE_MAP, this._cache.get('webgl_texture')); - _gl.generateMipmap(_gl.TEXTURE_CUBE_MAP); - } - }, - - bind: function(_gl) { - - _gl.bindTexture(_gl.TEXTURE_CUBE_MAP, this.getWebGLTexture(_gl)); - }, - - unbind: function(_gl) { - _gl.bindTexture(_gl.TEXTURE_CUBE_MAP, null); - }, - - // Overwrite the isPowerOfTwo method - isPowerOfTwo: function() { - if (this.image.px) { - return isPowerOfTwo(this.image.px.width) - && isPowerOfTwo(this.image.px.height); - } else { - return isPowerOfTwo(this.width) - && isPowerOfTwo(this.height); - } - - function isPowerOfTwo(value) { - return (value & (value-1)) === 0; - } - }, - - isRenderable: function() { - if (this.image.px) { - return isImageRenderable(this.image.px) - && isImageRenderable(this.image.nx) - && isImageRenderable(this.image.py) - && isImageRenderable(this.image.ny) - && isImageRenderable(this.image.pz) - && isImageRenderable(this.image.nz); - } else { - return this.width && this.height; - } - }, - - load: function(imageList) { - var loading = 0; - var self = this; - util.each(imageList, function(src, target){ - var image = new Image(); - image.onload = function() { - loading --; - if (loading === 0){ - self.dirty(); - self.trigger('success', self); - } - image.onload = null; - }; - image.onerror = function() { - loading --; - image.onerror = null; - }; - - loading++; - image.src = src; - self.image[target] = image; - }); - - return this; - } - }); - - function isImageRenderable(image) { - return image.nodeName === 'CANVAS' || - image.complete; - } - - return TextureCube; -}); -define('qtek/FrameBuffer',['require','./core/Base','./TextureCube','./core/glinfo','./core/glenum','./core/Cache'],function(require) { - - - - var Base = require('./core/Base'); - var TextureCube = require('./TextureCube'); - var glinfo = require('./core/glinfo'); - var glenum = require('./core/glenum'); - var Cache = require('./core/Cache'); - - /** - * @constructor qtek.FrameBuffer - * @extends qtek.core.Base - */ - var FrameBuffer = Base.derive( - /** @lends qtek.FrameBuffer# */ - { - /** - * If use depth buffer - * @type {boolean} - */ - depthBuffer: true, - - //Save attached texture and target - _attachedTextures: null, - - _width: 0, - _height: 0, - - _binded: false, - }, function() { - // Use cache - this._cache = new Cache(); - - this._attachedTextures = {}; - }, - - /**@lends qtek.FrameBuffer.prototype. */ - { - - /** - * Resize framebuffer. - * It is not recommanded use this methods to change the framebuffer size because the size will still be changed when attaching a new texture - * @param {number} width - * @param {number} height - */ - resize: function(width, height) { - this._width = width; - this._height = height; - }, - - /** - * Bind the framebuffer to given renderer before rendering - * @param {qtek.Renderer} renderer - */ - bind: function(renderer) { - - var _gl = renderer.gl; - - if (!this._binded) { - _gl.bindFramebuffer(_gl.FRAMEBUFFER, this.getFrameBuffer(_gl)); - this._binded = true; - } - var cache = this._cache; - - cache.put('viewport', renderer.viewport); - renderer.setViewport(0, 0, this._width, this._height, 1); - if (! cache.get('depthtexture_attached') && this.depthBuffer) { - // Create a new render buffer - if (cache.miss('renderbuffer')) { - cache.put('renderbuffer', _gl.createRenderbuffer()); - } - var width = this._width; - var height = this._height; - var renderbuffer = cache.get('renderbuffer'); - - if (width !== cache.get('renderbuffer_width') - || height !== cache.get('renderbuffer_height')) { - _gl.bindRenderbuffer(_gl.RENDERBUFFER, renderbuffer); - _gl.renderbufferStorage(_gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, width, height); - cache.put('renderbuffer_width', width); - cache.put('renderbuffer_height', height); - _gl.bindRenderbuffer(_gl.RENDERBUFFER, null); - } - if (! cache.get('renderbuffer_attached')) { - _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer); - cache.put('renderbuffer_attached', true); - } - } - }, - /** - * Unbind the frame buffer after rendering - * @param {qtek.Renderer} renderer - */ - unbind: function(renderer) { - var _gl = renderer.gl; - - _gl.bindFramebuffer(_gl.FRAMEBUFFER, null); - this._binded = false; - - this._cache.use(_gl.__GLID__); - var viewport = this._cache.get('viewport'); - // Reset viewport; - if (viewport) { - renderer.setViewport( - viewport.x, viewport.y, viewport.width, viewport.height - ); - } - - // Because the data of texture is changed over time, - // Here update the mipmaps of texture each time after rendered; - for (var attachment in this._attachedTextures) { - var texture = this._attachedTextures[attachment]; - if (! texture.NPOT && texture.useMipmap) { - var target = texture instanceof TextureCube ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; - _gl.bindTexture(target, texture.getWebGLTexture(_gl)); - _gl.generateMipmap(target); - _gl.bindTexture(target, null); - } - } - }, - - getFrameBuffer: function(_gl) { - - this._cache.use(_gl.__GLID__); - - if (this._cache.miss('framebuffer')) { - this._cache.put('framebuffer', _gl.createFramebuffer()); - } - - return this._cache.get('framebuffer'); - }, - - /** - * Attach a texture(RTT) to the framebuffer - * @param {WebGLRenderingContext} _gl - * @param {qtek.Texture} texture - * @param {number} [attachment=gl.COLOR_ATTACHMENT0] - * @param {number} [target=gl.TEXTURE_2D] - * @param {number} [mipmapLevel=0] - */ - attach: function(_gl, texture, attachment, target, mipmapLevel) { - - if (! texture.width) { - throw new Error('The texture attached to color buffer is not a valid.'); - } - - if (!this._binded) { - _gl.bindFramebuffer(_gl.FRAMEBUFFER, this.getFrameBuffer(_gl)); - this._binded = true; - } - - this._width = texture.width; - this._height = texture.height; - - // If the depth_texture extension is enabled, developers - // Can attach a depth texture to the depth buffer - // http://blog.tojicode.com/2012/07/using-webgldepthtexture.html - attachment = attachment || _gl.COLOR_ATTACHMENT0; - target = target || _gl.TEXTURE_2D; - mipmapLevel = mipmapLevel || 0; - - if (attachment === _gl.DEPTH_ATTACHMENT) { - - var extension = glinfo.getExtension(_gl, 'WEBGL_depth_texture'); - - if (!extension) { - console.error(' Depth texture is not supported by the browser'); - return; - } - if (texture.format !== glenum.DEPTH_COMPONENT) { - console.error('The texture attached to depth buffer is not a valid.'); - return; - } - this._cache.put('renderbuffer_attached', false); - this._cache.put('depthtexture_attached', true); - } - - this._attachedTextures[attachment] = texture; - - _gl.framebufferTexture2D(_gl.FRAMEBUFFER, attachment, target, texture.getWebGLTexture(_gl), mipmapLevel); - }, - // TODO - detach: function() {}, - /** - * Dispose - * @param {WebGLRenderingContext} _gl - */ - dispose: function(_gl) { - this._cache.use(_gl.__GLID__); - - var renderBuffer = this._cache.get('renderbuffer'); - if (renderBuffer) { - _gl.deleteRenderbuffer(renderBuffer); - } - var frameBuffer = this._cache.get('framebuffer'); - if (frameBuffer) { - _gl.deleteFramebuffer(frameBuffer); - } - - // Clear cache for reusing - this._attachedTextures = {}; - this._width = this._height = 0; - - this._cache.deleteContext(_gl.__GLID__); - } - }); - - FrameBuffer.COLOR_ATTACHMENT0 = glenum.COLOR_ATTACHMENT0; - FrameBuffer.DEPTH_ATTACHMENT = glenum.DEPTH_ATTACHMENT; - FrameBuffer.STENCIL_ATTACHMENT = glenum.STENCIL_ATTACHMENT; - FrameBuffer.DEPTH_STENCIL_ATTACHMENT = glenum.DEPTH_STENCIL_ATTACHMENT; - - return FrameBuffer; -}); -define('qtek/Joint',['require','./core/Base'],function(require) { - - - - var Base = require('./core/Base'); - - /** - * @constructor qtek.Joint - * @extends qtek.core.Base - */ - var Joint = Base.derive( - /** @lends qtek.Joint# */ - { - // https://github.com/KhronosGroup/glTF/issues/193#issuecomment-29216576 - /** - * Joint name - * @type {string} - */ - name: '', - /** - * Index of joint in the skeleton - * @type {number} - */ - index: -1, - /** - * Index of parent joint index, -1 if it is a root joint - * @type {number} - */ - parentIndex: -1, - - /** - * Scene node attached to - * @type {qtek.Node} - */ - node: null, - - /** - * Root scene node of the skeleton, which parent node is null or don't have a joint - * @type {qtek.Node} - */ - rootNode: null - }); - - return Joint; -}); -/** - * Mainly do the parse and compile of shader string - * Support shader code chunk import and export - * Support shader semantics - * http://www.nvidia.com/object/using_sas.html - * https://github.com/KhronosGroup/collada2json/issues/45 - * - */ -define('qtek/Shader',['require','./core/Base','./core/util','./core/Cache','./dep/glmatrix'],function(require) { - - - - var Base = require('./core/Base'); - var util = require('./core/util'); - var Cache = require('./core/Cache'); - var glMatrix = require('./dep/glmatrix'); - var mat2 = glMatrix.mat2; - var mat3 = glMatrix.mat3; - var mat4 = glMatrix.mat4; - - var uniformRegex = /uniform\s+(bool|float|int|vec2|vec3|vec4|ivec2|ivec3|ivec4|mat2|mat3|mat4|sampler2D|samplerCube)\s+([\w\,]+)?(\[.*?\])?\s*(:\s*([\S\s]+?))?;/g; - var attributeRegex = /attribute\s+(float|int|vec2|vec3|vec4)\s+(\w*)\s*(:\s*(\w+))?;/g; - var defineRegex = /#define\s+(\w+)?(\s+[\w-.]+)?\s*\n/g; - - var uniformTypeMap = { - 'bool': '1i', - 'int': '1i', - 'sampler2D': 't', - 'samplerCube': 't', - 'float': '1f', - 'vec2': '2f', - 'vec3': '3f', - 'vec4': '4f', - 'ivec2': '2i', - 'ivec3': '3i', - 'ivec4': '4i', - 'mat2': 'm2', - 'mat3': 'm3', - 'mat4': 'm4' - }; - - var uniformValueConstructor = { - 'bool': function() {return true;}, - 'int': function() {return 0;}, - 'float': function() {return 0;}, - 'sampler2D': function() {return null;}, - 'samplerCube': function() {return null;}, - - 'vec2': function() {return [0, 0];}, - 'vec3': function() {return [0, 0, 0];}, - 'vec4': function() {return [0, 0, 0, 0];}, - - 'ivec2': function() {return [0, 0];}, - 'ivec3': function() {return [0, 0, 0];}, - 'ivec4': function() {return [0, 0, 0, 0];}, - - 'mat2': function() {return mat2.create();}, - 'mat3': function() {return mat3.create();}, - 'mat4': function() {return mat4.create();}, - - 'array': function() {return [];} - }; - - var attribSemantics = [ - 'POSITION', - 'NORMAL', - 'BINORMAL', - 'TANGENT', - 'TEXCOORD', - 'TEXCOORD_0', - 'TEXCOORD_1', - 'COLOR', - // Skinning - // https://github.com/KhronosGroup/glTF/blob/master/specification/README.md#semantics - 'JOINT', - 'WEIGHT', - 'SKIN_MATRIX' - ]; - var matrixSemantics = [ - 'WORLD', - 'VIEW', - 'PROJECTION', - 'WORLDVIEW', - 'VIEWPROJECTION', - 'WORLDVIEWPROJECTION', - 'WORLDINVERSE', - 'VIEWINVERSE', - 'PROJECTIONINVERSE', - 'WORLDVIEWINVERSE', - 'VIEWPROJECTIONINVERSE', - 'WORLDVIEWPROJECTIONINVERSE', - 'WORLDTRANSPOSE', - 'VIEWTRANSPOSE', - 'PROJECTIONTRANSPOSE', - 'WORLDVIEWTRANSPOSE', - 'VIEWPROJECTIONTRANSPOSE', - 'WORLDVIEWPROJECTIONTRANSPOSE', - 'WORLDINVERSETRANSPOSE', - 'VIEWINVERSETRANSPOSE', - 'PROJECTIONINVERSETRANSPOSE', - 'WORLDVIEWINVERSETRANSPOSE', - 'VIEWPROJECTIONINVERSETRANSPOSE', - 'WORLDVIEWPROJECTIONINVERSETRANSPOSE' - ]; - - // Enable attribute operation is global to all programs - // Here saved the list of all enabled attribute index - // http://www.mjbshaw.com/2013/03/webgl-fixing-invalidoperation.html - var enabledAttributeList = {}; - - var SHADER_STATE_TO_ENABLE = 1; - var SHADER_STATE_KEEP_ENABLE = 2; - var SHADER_STATE_PENDING = 3; - - /** - * @constructor qtek.Shader - * @extends qtek.core.Base - * - * @example - * // Create a phong shader - * var shader = new qtek.Shader({ - * vertex: qtek.Shader.source('buildin.phong.vertex'), - * fragment: qtek.Shader.source('buildin.phong.fragment') - * }); - * // Enable diffuse texture - * shader.enableTexture('diffuseMap'); - * // Use alpha channel in diffuse texture - * shader.define('fragment', 'DIFFUSEMAP_ALPHA_ALPHA'); - */ - var Shader = Base.derive(function() { - return /** @lends qtek.Shader# */ { - /** - * Vertex shader code - * @type {string} - */ - vertex: '', - - /** - * Fragment shader code - * @type {string} - */ - fragment: '', - - - precision: 'mediump', - // Properties follow will be generated by the program - attribSemantics: {}, - matrixSemantics: {}, - matrixSemanticKeys: [], - - uniformTemplates: {}, - attributeTemplates: {}, - - /** - * Custom defined values in the vertex shader - * @type {Object} - */ - vertexDefines: {}, - /** - * Custom defined values in the vertex shader - * @type {Object} - */ - fragmentDefines: {}, - - // Glue code - // Defines the each type light number in the scene - // AMBIENT_LIGHT - // POINT_LIGHT - // SPOT_LIGHT - // AREA_LIGHT - lightNumber: {}, - - _attacheMaterialNumber: 0, - - _uniformList: [], - // { - // enabled: true - // shaderType: "vertex", - // } - _textureStatus: {}, - - _vertexProcessed: '', - _fragmentProcessed: '', - - _currentLocationsMap: {} - }; - }, function() { - - this._cache = new Cache(); - - this._updateShaderString(); - }, - /** @lends qtek.Shader.prototype */ - { - /** - * Set vertex shader code - * @param {string} str - */ - setVertex: function(str) { - this.vertex = str; - this._updateShaderString(); - this.dirty(); - }, - - /** - * Set fragment shader code - * @param {string} str - */ - setFragment: function(str) { - this.fragment = str; - this._updateShaderString(); - this.dirty(); - }, - - /** - * Bind shader program - * Return true or error msg if error happened - * @param {WebGLRenderingContext} _gl - */ - bind: function(_gl) { - this._cache.use(_gl.__GLID__, getCacheSchema); - - this._currentLocationsMap = this._cache.get('locations'); - - if (this._cache.isDirty()) { - this._updateShaderString(); - var errMsg = this._buildProgram(_gl, this._vertexProcessed, this._fragmentProcessed); - this._cache.fresh(); - - if (errMsg) { - return errMsg; - } - } - - _gl.useProgram(this._cache.get('program')); - }, - - /** - * Mark dirty and update program in next frame - */ - dirty: function() { - this._cache.dirtyAll(); - for (var i = 0; i < this._cache._caches.length; i++) { - if (this._cache._caches[i]) { - var context = this._cache._caches[i]; - context['locations'] = {}; - context['attriblocations'] = {}; - } - } - }, - - _updateShaderString: function() { - - if (this.vertex !== this._vertexPrev || - this.fragment !== this._fragmentPrev) { - - this._parseImport(); - - this.attribSemantics = {}; - this.matrixSemantics = {}; - this._textureStatus = {}; - - this._parseUniforms(); - this._parseAttributes(); - this._parseDefines(); - - this._vertexPrev = this.vertex; - this._fragmentPrev = this.fragment; - } - this._addDefine(); - }, - - /** - * Add a #define micro in shader code - * @param {string} shaderType Can be vertex, fragment or both - * @param {string} symbol - * @param {number} [val] - */ - define: function(shaderType, symbol, val) { - val = val !== undefined ? val : null; - if (shaderType == 'vertex' || shaderType == 'both') { - if (this.vertexDefines[symbol] !== val) { - this.vertexDefines[symbol] = val; - // Mark as dirty - this.dirty(); - } - } - if (shaderType == 'fragment' || shaderType == 'both') { - if (this.fragmentDefines[symbol] !== val) { - this.fragmentDefines[symbol] = val; - if (shaderType !== 'both') { - this.dirty(); - } - } - } - }, - - /** - * @param {string} shaderType Can be vertex, fragment or both - * @param {string} symbol - */ - unDefine: function(shaderType, symbol) { - if (shaderType == 'vertex' || shaderType == 'both') { - if (this.isDefined('vertex', symbol)) { - delete this.vertexDefines[symbol]; - // Mark as dirty - this.dirty(); - } - } - if (shaderType == 'fragment' || shaderType == 'both') { - if (this.isDefined('fragment', symbol)) { - delete this.fragmentDefines[symbol]; - if (shaderType !== 'both') { - this.dirty(); - } - } - } - }, - - /** - * @param {string} shaderType Can be vertex, fragment or both - * @param {string} symbol - */ - isDefined: function(shaderType, symbol) { - switch(shaderType) { - case 'vertex': - return this.vertexDefines[symbol] !== undefined; - case 'fragment': - return this.fragmentDefines[symbol] !== undefined; - } - }, - /** - * @param {string} shaderType Can be vertex, fragment or both - * @param {string} symbol - */ - getDefine: function(shaderType, symbol) { - switch(shaderType) { - case 'vertex': - return this.vertexDefines[symbol]; - case 'fragment': - return this.fragmentDefines[symbol]; - } - }, - /** - * Enable a texture, actually it will add a #define micro in the shader code - * For example, if texture symbol is diffuseMap, it will add a line `#define DIFFUSEMAP_ENABLED` in the shader code - * @param {string} symbol - */ - enableTexture: function(symbol) { - var status = this._textureStatus[symbol]; - if (status) { - var isEnabled = status.enabled; - if (!isEnabled) { - status.enabled = true; - this.dirty(); - } - } - }, - /** - * Enable all textures used in the shader - */ - enableTexturesAll: function() { - for (var symbol in this._textureStatus) { - this._textureStatus[symbol].enabled = true; - } - - this.dirty(); - }, - /** - * Disable a texture, it remove a #define micro in the shader - * @param {string} symbol - */ - disableTexture: function(symbol) { - var status = this._textureStatus[symbol]; - if (status) { - var isDisabled = ! status.enabled; - if (!isDisabled) { - status.enabled = false; - this.dirty(); - } - } - }, - /** - * Disable all textures used in the shader - */ - disableTexturesAll: function() { - for (var symbol in this._textureStatus) { - this._textureStatus[symbol].enabled = false; - } - - this.dirty(); - }, - /** - * @param {string} symbol - * @return {boolean} - */ - isTextureEnabled: function(symbol) { - return this._textureStatus[symbol].enabled; - }, - - getEnabledTextures: function () { - var enabledTextures = []; - for (var symbol in this._textureStatus) { - if (this._textureStatus[symbol].enabled) { - enabledTextures.push(symbol); - } - } - return enabledTextures; - }, - - hasUniform: function(symbol) { - var location = this._currentLocationsMap[symbol]; - return location !== null && location !== undefined; - }, - - setUniform: function(_gl, type, symbol, value) { - var locationMap = this._currentLocationsMap; - var location = locationMap[symbol]; - // Uniform is not existed in the shader - if (location === null || location === undefined) { - return false; - } - switch (type) { - case 'm4': - // The matrix must be created by glmatrix and can pass it directly. - _gl.uniformMatrix4fv(location, false, value); - break; - case '2i': - _gl.uniform2i(location, value[0], value[1]); - break; - case '2f': - _gl.uniform2f(location, value[0], value[1]); - break; - case '3i': - _gl.uniform3i(location, value[0], value[1], value[2]); - break; - case '3f': - _gl.uniform3f(location, value[0], value[1], value[2]); - break; - case '4i': - _gl.uniform4i(location, value[0], value[1], value[2], value[3]); - break; - case '4f': - _gl.uniform4f(location, value[0], value[1], value[2], value[3]); - break; - case '1i': - _gl.uniform1i(location, value); - break; - case '1f': - _gl.uniform1f(location, value); - break; - case '1fv': - _gl.uniform1fv(location, value); - break; - case '1iv': - _gl.uniform1iv(location, value); - break; - case '2iv': - _gl.uniform2iv(location, value); - break; - case '2fv': - _gl.uniform2fv(location, value); - break; - case '3iv': - _gl.uniform3iv(location, value); - break; - case '3fv': - _gl.uniform3fv(location, value); - break; - case '4iv': - _gl.uniform4iv(location, value); - break; - case '4fv': - _gl.uniform4fv(location, value); - break; - case 'm2': - case 'm2v': - _gl.uniformMatrix2fv(location, false, value); - break; - case 'm3': - case 'm3v': - _gl.uniformMatrix3fv(location, false, value); - break; - case 'm4v': - if (value instanceof Array) { - var array = new Float32Array(value.length * 16); - var cursor = 0; - for (var i = 0; i < value.length; i++) { - var item = value[i]; - for (var j = 0; j < 16; j++) { - array[cursor++] = item[j]; - } - } - _gl.uniformMatrix4fv(location, false, array); - // Raw value - } else if (value instanceof Float32Array) { // ArrayBufferView - _gl.uniformMatrix4fv(location, false, value); - } - break; - } - return true; - }, - - setUniformBySemantic: function(_gl, semantic, val) { - var semanticInfo = this.attribSemantics[semantic]; - if (semanticInfo) { - return this.setUniform(_gl, semanticInfo.type, semanticInfo.symbol, val); - } - return false; - }, - - // Enable the attributes passed in and disable the rest - // Example Usage: - // enableAttributes(_gl, ["position", "texcoords"]) - enableAttributes: function(_gl, attribList, vao) { - - var program = this._cache.get('program'); - - var locationMap = this._cache.get('attriblocations'); - - var enabledAttributeListInContext; - if (vao) { - enabledAttributeListInContext = vao.__enabledAttributeList; - } else { - enabledAttributeListInContext = enabledAttributeList[_gl.__GLID__]; - } - if (! enabledAttributeListInContext) { - // In vertex array object context - // PENDING Each vao object needs to enable attributes again? - if (vao) { - enabledAttributeListInContext - = vao.__enabledAttributeList - = []; - } else { - enabledAttributeListInContext - = enabledAttributeList[_gl.__GLID__] - = []; - } - } - var locationList = []; - for (var i = 0; i < attribList.length; i++) { - var symbol = attribList[i]; - if (!this.attributeTemplates[symbol]) { - locationList[i] = -1; - continue; - } - var location = locationMap[symbol]; - if (location === undefined) { - location = _gl.getAttribLocation(program, symbol); - // Attrib location is a number from 0 to ... - if (location === -1) { - locationList[i] = -1; - continue; - } - locationMap[symbol] = location; - } - locationList[i] = location; - - if (!enabledAttributeListInContext[location]) { - enabledAttributeListInContext[location] = SHADER_STATE_TO_ENABLE; - } else { - enabledAttributeListInContext[location] = SHADER_STATE_KEEP_ENABLE; - } - } - - for (var i = 0; i < enabledAttributeListInContext.length; i++) { - switch(enabledAttributeListInContext[i]){ - case SHADER_STATE_TO_ENABLE: - _gl.enableVertexAttribArray(i); - enabledAttributeListInContext[i] = SHADER_STATE_PENDING; - break; - case SHADER_STATE_KEEP_ENABLE: - enabledAttributeListInContext[i] = SHADER_STATE_PENDING; - break; - // Expired - case SHADER_STATE_PENDING: - _gl.disableVertexAttribArray(i); - enabledAttributeListInContext[i] = 0; - break; - } - } - - return locationList; - }, - - _parseImport: function() { - - this._vertexProcessedNoDefine = Shader.parseImport(this.vertex); - this._fragmentProcessedNoDefine = Shader.parseImport(this.fragment); - - }, - - _addDefine: function() { - - // Add defines - // VERTEX - var defineStr = []; - for (var lightType in this.lightNumber) { - var count = this.lightNumber[lightType]; - if (count > 0) { - defineStr.push('#define ' + lightType.toUpperCase() + '_NUMBER ' + count); - } - } - for (var symbol in this._textureStatus) { - var status = this._textureStatus[symbol]; - if (status.enabled) { - defineStr.push('#define ' + symbol.toUpperCase() + '_ENABLED'); - } - } - // Custom Defines - for (var symbol in this.vertexDefines) { - var value = this.vertexDefines[symbol]; - if (value === null) { - defineStr.push('#define ' + symbol); - }else{ - defineStr.push('#define ' + symbol + ' ' + value.toString()); - } - } - this._vertexProcessed = defineStr.join('\n') + '\n' + this._vertexProcessedNoDefine; - - // FRAGMENT - defineStr = []; - for (var lightType in this.lightNumber) { - var count = this.lightNumber[lightType]; - if (count > 0) { - defineStr.push('#define ' + lightType.toUpperCase() + '_NUMBER ' + count); - } - } - for (var symbol in this._textureStatus) { - var status = this._textureStatus[symbol]; - if (status.enabled) { - defineStr.push('#define ' + symbol.toUpperCase() + '_ENABLED'); - } - } - // Custom Defines - for (var symbol in this.fragmentDefines) { - var value = this.fragmentDefines[symbol]; - if (value === null) { - defineStr.push('#define ' + symbol); - }else{ - defineStr.push('#define ' + symbol + ' ' + value.toString()); - } - } - var code = defineStr.join('\n') + '\n' + this._fragmentProcessedNoDefine; - - // Add precision - this._fragmentProcessed = ['precision', this.precision, 'float'].join(' ') + ';\n' + code; - }, - - _parseUniforms: function() { - var uniforms = {}; - var self = this; - var shaderType = 'vertex'; - this._uniformList = []; - - this._vertexProcessedNoDefine = this._vertexProcessedNoDefine.replace(uniformRegex, _uniformParser); - shaderType = 'fragment'; - this._fragmentProcessedNoDefine = this._fragmentProcessedNoDefine.replace(uniformRegex, _uniformParser); - - self.matrixSemanticKeys = Object.keys(this.matrixSemantics); - - function _uniformParser(str, type, symbol, isArray, semanticWrapper, semantic) { - if (type && symbol) { - var uniformType = uniformTypeMap[type]; - var isConfigurable = true; - var defaultValueFunc; - if (uniformType) { - self._uniformList.push(symbol); - if (type === 'sampler2D' || type === 'samplerCube') { - // Texture is default disabled - self._textureStatus[symbol] = { - enabled: false, - shaderType: shaderType - }; - } - if (isArray) { - uniformType += 'v'; - } - if (semantic) { - // This case is only for SKIN_MATRIX - // TODO - if (attribSemantics.indexOf(semantic) >= 0) { - self.attribSemantics[semantic] = { - symbol: symbol, - type: uniformType - }; - isConfigurable = false; - } else if (matrixSemantics.indexOf(semantic) >= 0) { - var isTranspose = false; - var semanticNoTranspose = semantic; - if (semantic.match(/TRANSPOSE$/)) { - isTranspose = true; - semanticNoTranspose = semantic.slice(0, -9); - } - self.matrixSemantics[semantic] = { - symbol: symbol, - type: uniformType, - isTranspose: isTranspose, - semanticNoTranspose: semanticNoTranspose - }; - isConfigurable = false; - } else { - // The uniform is not configurable, which means it will not appear - // in the material uniform properties - if (semantic === 'unconfigurable') { - isConfigurable = false; - } else { - // Uniform have a defalut value, like - // uniform vec3 color: [1, 1, 1]; - defaultValueFunc = self._parseDefaultValue(type, semantic); - if (!defaultValueFunc) { - throw new Error('Unkown semantic "' + semantic + '"'); - } - else { - semantic = ''; - } - } - } - } - if (isConfigurable) { - uniforms[symbol] = { - type: uniformType, - value: isArray ? uniformValueConstructor['array'] : (defaultValueFunc || uniformValueConstructor[type]), - semantic: semantic || null - }; - } - } - return ['uniform', type, symbol, isArray].join(' ') + ';\n'; - } - } - - this.uniformTemplates = uniforms; - }, - - _parseDefaultValue: function(type, str) { - var arrayRegex = /\[\s*(.*)\s*\]/; - if (type === 'vec2' || type === 'vec3' || type === 'vec4') { - var arrayStr = arrayRegex.exec(str)[1]; - if (arrayStr) { - var arr = arrayStr.split(/\s*,\s*/); - return function() { - return new Float32Array(arr); - }; - } else { - // Invalid value - return; - } - } else if (type === 'bool') { - return function() { - return str.toLowerCase() === 'true' ? true : false; - }; - } else if (type === 'float') { - return function() { - return parseFloat(str); - }; - } - }, - - // Create a new uniform instance for material - createUniforms: function() { - var uniforms = {}; - - for (var symbol in this.uniformTemplates){ - var uniformTpl = this.uniformTemplates[symbol]; - uniforms[symbol] = { - type: uniformTpl.type, - value: uniformTpl.value() - }; - } - - return uniforms; - }, - - // Attached to material - attached: function () { - this._attacheMaterialNumber++; - }, - - // Detached to material - detached: function () { - this._attacheMaterialNumber--; - }, - - isAttachedToAny: function () { - return this._attacheMaterialNumber !== 0; - }, - - _parseAttributes: function() { - var attributes = {}; - var self = this; - this._vertexProcessedNoDefine = this._vertexProcessedNoDefine.replace( - attributeRegex, _attributeParser - ); - - function _attributeParser(str, type, symbol, semanticWrapper, semantic) { - if (type && symbol) { - var size = 1; - switch (type) { - case 'vec4': - size = 4; - break; - case 'vec3': - size = 3; - break; - case 'vec2': - size = 2; - break; - case 'float': - size = 1; - break; - } - - attributes[symbol] = { - // Can only be float - type: 'float', - size: size, - semantic: semantic || null - }; - - if (semantic) { - if (attribSemantics.indexOf(semantic) < 0) { - throw new Error('Unkown semantic "' + semantic + '"'); - }else{ - self.attribSemantics[semantic] = { - symbol: symbol, - type: type - }; - } - } - } - - return ['attribute', type, symbol].join(' ') + ';\n'; - } - - this.attributeTemplates = attributes; - }, - - _parseDefines: function() { - var self = this; - var shaderType = 'vertex'; - this._vertexProcessedNoDefine = this._vertexProcessedNoDefine.replace(defineRegex, _defineParser); - shaderType = 'fragment'; - this._fragmentProcessedNoDefine = this._fragmentProcessedNoDefine.replace(defineRegex, _defineParser); - - function _defineParser(str, symbol, value) { - var defines = shaderType === 'vertex' ? self.vertexDefines : self.fragmentDefines; - if (!defines[symbol]) { // Haven't been defined by user - if (value == 'false') { - defines[symbol] = false; - } else if (value == 'true') { - defines[symbol] = true; - } else { - defines[symbol] = value ? parseFloat(value) : null; - } - } - return ''; - } - }, - - // Return true or error msg if error happened - _buildProgram: function(_gl, vertexShaderString, fragmentShaderString) { - - if (this._cache.get('program')) { - _gl.deleteProgram(this._cache.get('program')); - } - var program = _gl.createProgram(); - - var vertexShader = _gl.createShader(_gl.VERTEX_SHADER); - _gl.shaderSource(vertexShader, vertexShaderString); - _gl.compileShader(vertexShader); - - var fragmentShader = _gl.createShader(_gl.FRAGMENT_SHADER); - _gl.shaderSource(fragmentShader, fragmentShaderString); - _gl.compileShader(fragmentShader); - - var msg = this._checkShaderErrorMsg(_gl, vertexShader, vertexShaderString); - if (msg) { - return msg; - } - msg = this._checkShaderErrorMsg(_gl, fragmentShader, fragmentShaderString); - if (msg) { - return msg; - } - - _gl.attachShader(program, vertexShader); - _gl.attachShader(program, fragmentShader); - // Force the position bind to location 0; - if (this.attribSemantics['POSITION']) { - _gl.bindAttribLocation(program, 0, this.attribSemantics['POSITION'].symbol); - } else { - // Else choose an attribute and bind to location 0; - var keys = Object.keys(this.attributeTemplates); - _gl.bindAttribLocation(program, 0, keys[0]); - } - - _gl.linkProgram(program); - - if (!_gl.getProgramParameter(program, _gl.LINK_STATUS)) { - return 'Could not link program\n' + 'VALIDATE_STATUS: ' + _gl.getProgramParameter(program, _gl.VALIDATE_STATUS) + ', gl error [' + _gl.getError() + ']'; - } - - // Cache uniform locations - for (var i = 0; i < this._uniformList.length; i++) { - var uniformSymbol = this._uniformList[i]; - var locationMap = this._cache.get('locations'); - locationMap[uniformSymbol] = _gl.getUniformLocation(program, uniformSymbol); - } - - _gl.deleteShader(vertexShader); - _gl.deleteShader(fragmentShader); - - this._cache.put('program', program); - }, - - // Return true or error msg if error happened - _checkShaderErrorMsg: function(_gl, shader, shaderString) { - if (!_gl.getShaderParameter(shader, _gl.COMPILE_STATUS)) { - return [_gl.getShaderInfoLog(shader), addLineNumbers(shaderString)].join('\n'); - } - }, - - /** - * Clone a new shader - * @return {qtek.Shader} - */ - clone: function() { - var shader = new Shader({ - vertex: this.vertex, - fragment: this.fragment, - vertexDefines: util.clone(this.vertexDefines), - fragmentDefines: util.clone(this.fragmentDefines) - }); - for (var name in this._textureStatus) { - shader._textureStatus[name] = util.clone(this._textureStatus[name]); - } - return shader; - }, - /** - * @param {WebGLRenderingContext} _gl - */ - dispose: function(_gl) { - this._cache.use(_gl.__GLID__); - var program = this._cache.get('program'); - if (program) { - _gl.deleteProgram(program); - } - this._cache.deleteContext(_gl.__GLID__); - this._locations = {}; - } - }); - - function getCacheSchema() { - return { - locations: {}, - attriblocations: {} - }; - } - - // some util functions - function addLineNumbers(string) { - var chunks = string.split('\n'); - for (var i = 0, il = chunks.length; i < il; i ++) { - // Chrome reports shader errors on lines - // starting counting from 1 - chunks[i] = (i + 1) + ': ' + chunks[i]; - } - return chunks.join('\n'); - } - - var importRegex = /(@import)\s*([0-9a-zA-Z_\-\.]*)/g; - Shader.parseImport = function(shaderStr) { - shaderStr = shaderStr.replace(importRegex, function(str, importSymbol, importName) { - var str = Shader.source(importName); - if (str) { - // Recursively parse - return Shader.parseImport(str); - } else { - console.warn('Shader chunk "' + importName + '" not existed in library'); - return ''; - } - }); - return shaderStr; - }; - - var exportRegex = /(@export)\s*([0-9a-zA-Z_\-\.]*)\s*\n([\s\S]*?)@end/g; - - /** - * Import shader source - * @param {string} shaderStr - * @memberOf qtek.Shader - */ - Shader['import'] = function(shaderStr) { - shaderStr.replace(exportRegex, function(str, exportSymbol, exportName, code) { - var code = code.replace(/(^[\s\t\xa0\u3000]+)|([\u3000\xa0\s\t]+\x24)/g, ''); - if (code) { - var parts = exportName.split('.'); - var obj = Shader.codes; - var i = 0; - var key; - while (i < parts.length - 1) { - key = parts[i++]; - if (!obj[key]) { - obj[key] = {}; - } - obj = obj[key]; - } - key = parts[i]; - obj[key] = code; - } - return code; - }); - }; - - /** - * Library to store all the loaded shader codes - * @type {Object} - * @readOnly - * @memberOf qtek.Shader - */ - Shader.codes = {}; - - /** - * Get shader source - * @param {string} name - * @return {string} - * @memberOf qtek.Shader - */ - Shader.source = function(name) { - var parts = name.split('.'); - var obj = Shader.codes; - var i = 0; - while(obj && i < parts.length) { - var key = parts[i++]; - obj = obj[key]; - } - if (!obj) { - console.warn('Shader "' + name + '" not existed in library'); - return; - } - return obj; - }; - - return Shader; -}); -define('qtek/light/light.essl',[],function () { return '@export buildin.header.directional_light\nuniform vec3 directionalLightDirection[ DIRECTIONAL_LIGHT_NUMBER ] : unconfigurable;\nuniform vec3 directionalLightColor[ DIRECTIONAL_LIGHT_NUMBER ] : unconfigurable;\n@end\n\n@export buildin.header.ambient_light\nuniform vec3 ambientLightColor[ AMBIENT_LIGHT_NUMBER ] : unconfigurable;\n@end\n\n@export buildin.header.point_light\nuniform vec3 pointLightPosition[ POINT_LIGHT_NUMBER ] : unconfigurable;\nuniform float pointLightRange[ POINT_LIGHT_NUMBER ] : unconfigurable;\nuniform vec3 pointLightColor[ POINT_LIGHT_NUMBER ] : unconfigurable;\n@end\n\n@export buildin.header.spot_light\nuniform vec3 spotLightPosition[SPOT_LIGHT_NUMBER] : unconfigurable;\nuniform vec3 spotLightDirection[SPOT_LIGHT_NUMBER] : unconfigurable;\nuniform float spotLightRange[SPOT_LIGHT_NUMBER] : unconfigurable;\nuniform float spotLightUmbraAngleCosine[SPOT_LIGHT_NUMBER] : unconfigurable;\nuniform float spotLightPenumbraAngleCosine[SPOT_LIGHT_NUMBER] : unconfigurable;\nuniform float spotLightFalloffFactor[SPOT_LIGHT_NUMBER] : unconfigurable;\nuniform vec3 spotLightColor[SPOT_LIGHT_NUMBER] : unconfigurable;\n@end';}); - -define('qtek/Light',['require','./Node','./Shader','./light/light.essl'],function(require){ - - - - var Node = require('./Node'); - var Shader = require('./Shader'); - - /** - * @constructor qtek.Light - * @extends qtek.Node - */ - var Light = Node.derive(function(){ - return /** @lends qtek.Light# */ { - /** - * Light RGB color - * @type {number[]} - */ - color: [1, 1, 1], - - /** - * Light intensity - * @type {number} - */ - intensity: 1.0, - - // Config for shadow map - /** - * If light cast shadow - * @type {boolean} - */ - castShadow: true, - - /** - * Shadow map size - * @type {number} - */ - shadowResolution: 512 - }; - }, - /** @lends qtek.Light.prototype. */ - { - /** - * Light type - * @type {string} - * @memberOf qtek.Light# - */ - type: '', - - /** - * @return {qtek.Light} - * @memberOf qtek.Light.prototype - */ - clone: function() { - var light = Node.prototype.clone.call(this); - light.color = Array.prototype.slice.call(this.color); - light.intensity = this.intensity; - light.castShadow = this.castShadow; - light.shadowResolution = this.shadowResolution; - - return light; - } - }); - - Shader['import'](require('./light/light.essl')); - - return Light; -}); -define('qtek/Material',['require','./core/Base','./Texture'],function(require) { - - - - var Base = require('./core/Base'); - var Texture = require('./Texture'); - - /** - * #constructor qtek.Material - * @extends qtek.core.Base - */ - var Material = Base.derive( - /** @lends qtek.Material# */ - { - /** - * @type {string} - */ - name: '', - - /** - * @type {Object} - */ - uniforms: null, - - /** - * @type {qtek.Shader} - */ - shader: null, - - /** - * @type {boolean} - */ - depthTest: true, - - /** - * @type {boolean} - */ - depthMask: true, - - /** - * @type {boolean} - */ - transparent: false, - /** - * Blend func is a callback function when the material - * have custom blending - * The gl context will be the only argument passed in tho the - * blend function - * Detail of blend function in WebGL: - * http://www.khronos.org/registry/gles/specs/2.0/es_full_spec_2.0.25.pdf - * - * Example : - * function(_gl) { - * _gl.blendEquation(_gl.FUNC_ADD); - * _gl.blendFunc(_gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA); - * } - */ - blend: null, - - // shadowTransparentMap : null - - _enabledUniforms: null, - }, function() { - if (!this.name) { - this.name = 'MATERIAL_' + this.__GUID__; - } - if (this.shader) { - this.attachShader(this.shader); - } - if (! this.uniforms) { - this.uniforms = {}; - } - }, - /** @lends qtek.Material.prototype */ - { - - bind: function(_gl, prevMaterial) { - - var slot = 0; - - var sameShader = prevMaterial && prevMaterial.shader === this.shader; - // Set uniforms - for (var u = 0; u < this._enabledUniforms.length; u++) { - var symbol = this._enabledUniforms[u]; - var uniform = this.uniforms[symbol]; - // When binding two materials with the same shader - // Many uniforms will be be set twice even if they have the same value - // So add a evaluation to see if the uniform is really needed to be set - // - // FIXME Small possibility enabledUniforms are not the same - if (sameShader) { - if (prevMaterial.uniforms[symbol].value === uniform.value) { - continue; - } - } - - if (uniform.value === undefined) { - console.warn('Uniform value "' + symbol + '" is undefined'); - continue; - } - else if (uniform.value === null) { - // if (uniform.type == 't') { - // // PENDING - // _gl.bindTexture(_gl.TEXTURE_2D, null); - // _gl.bindTexture(_gl.TEXTURE_CUBE, null); - // } - continue; - } - else if (uniform.value instanceof Array - && ! uniform.value.length) { - continue; - } - else if (uniform.value instanceof Texture) { - var res = this.shader.setUniform(_gl, '1i', symbol, slot); - if (!res) { // Texture is not enabled - continue; - } - var texture = uniform.value; - _gl.activeTexture(_gl.TEXTURE0 + slot); - // Maybe texture is not loaded yet; - if (texture.isRenderable()) { - texture.bind(_gl); - } else { - // Bind texture to null - texture.unbind(_gl); - } - - slot++; - } - else if (uniform.value instanceof Array) { - if (uniform.value.length === 0) { - continue; - } - // Texture Array - var exampleValue = uniform.value[0]; - - if (exampleValue instanceof Texture) { - if (!this.shader.hasUniform(symbol)) { - continue; - } - - var arr = []; - for (var i = 0; i < uniform.value.length; i++) { - var texture = uniform.value[i]; - _gl.activeTexture(_gl.TEXTURE0 + slot); - // Maybe texture is not loaded yet; - if (texture.isRenderable()) { - texture.bind(_gl); - } else { - texture.unbind(_gl); - } - - arr.push(slot++); - } - - this.shader.setUniform(_gl, '1iv', symbol, arr); - } else { - this.shader.setUniform(_gl, uniform.type, symbol, uniform.value); - } - } - else{ - this.shader.setUniform(_gl, uniform.type, symbol, uniform.value); - } - } - }, - - /** - * @param {string} symbol - * @param {number|array|qtek.Texture|ArrayBufferView} value - */ - setUniform: function(symbol, value) { - var uniform = this.uniforms[symbol]; - if (uniform) { - uniform.value = value; - } - }, - - /** - * @param {Object} obj - */ - setUniforms: function(obj) { - for (var key in obj) { - var val = obj[key]; - this.setUniform(key, val); - } - }, - - /** - * Enable a uniform - * It only have effect on the uniform exists in shader. - * @param {string} symbol - */ - enableUniform: function(symbol) { - if (this.uniforms[symbol] && !this.isUniformEnabled(symbol)) { - this._enabledUniforms.push(symbol); - } - }, - - /** - * Disable a uniform - * It will not affect the uniform state in the shader. Because the shader uniforms is parsed from shader code with naive regex. When using micro to disable some uniforms in the shader. It will still try to set these uniforms in each rendering pass. We can disable these uniforms manually if we need this bit performance improvement. Mostly we can simply ignore it. - * @param {string} symbol - */ - disableUniform: function(symbol) { - var idx = this._enabledUniforms.indexOf(symbol); - if (idx >= 0) { - this._enabledUniforms.splice(idx, 1); - } - }, - - /** - * @param {string} symbol - * @return {boolean} - */ - isUniformEnabled: function(symbol) { - return this._enabledUniforms.indexOf(symbol) >= 0; - }, - - /** - * Alias of setUniform and setUniforms - * @param {object|string} symbol - * @param {number|array|qtek.Texture|ArrayBufferView} [value] - */ - set: function(symbol, value) { - if (typeof(symbol) === 'object') { - for (var key in symbol) { - var val = symbol[key]; - this.set(key, val); - } - } else { - var uniform = this.uniforms[symbol]; - if (uniform) { - uniform.value = value; - } - } - }, - /** - * Get uniform value - * @param {string} symbol - * @return {number|array|qtek.Texture|ArrayBufferView} - */ - get: function(symbol) { - var uniform = this.uniforms[symbol]; - if (uniform) { - return uniform.value; - } - }, - /** - * Attach a shader instance - * @param {qtek.Shader} shader - * @param {boolean} keepUniform If try to keep uniform value - */ - attachShader: function(shader, keepUniform) { - if (this.shader) { - this.shader.detached(); - } - - var originalUniforms = this.uniforms; - this.uniforms = shader.createUniforms(); - this.shader = shader; - - this._enabledUniforms = Object.keys(this.uniforms); - - if (keepUniform) { - for (var symbol in originalUniforms) { - if (this.uniforms[symbol]) { - this.uniforms[symbol].value = originalUniforms[symbol].value; - } - } - } - - shader.attached(); - }, - - /** - * Detach a shader instance - */ - detachShader: function() { - this.shader.detached(); - this.shader = null; - this.uniforms = {}; - }, - - /** - * Clone a new material and keep uniforms, shader will not be cloned - * @return {qtek.Material} - */ - clone: function () { - var material = new Material({ - name: this.name, - shader: this.shader - }); - for (var symbol in this.uniforms) { - material.uniforms[symbol].value = this.uniforms[symbol].value; - } - material.depthTest = this.depthTest; - material.depthMask = this.depthMask; - material.transparent = this.transparent; - material.blend = this.blend; - - return material; - }, - - /** - * Dispose material, if material shader is not attached to any other materials - * Shader will also be disposed - * @param {WebGLRenderingContext} gl - * @param {boolean} [disposeTexture=false] If dispose the textures used in the material - */ - dispose: function(_gl, disposeTexture) { - if (disposeTexture) { - for (var name in this.uniforms) { - var val = this.uniforms[name].value; - if (!val ) { - continue; - } - if (val instanceof Texture) { - val.dispose(_gl); - } - else if (val instanceof Array) { - for (var i = 0; i < val.length; i++) { - if (val[i] instanceof Texture) { - val[i].dispose(_gl); - } - } - } - } - } - var shader = this.shader; - if (shader) { - this.detachShader(); - if (!shader.isAttachedToAny()) { - shader.dispose(_gl); - } - } - } - }); - - return Material; -}); -define('qtek/Renderable',['require','./Node','./core/glenum','./core/glinfo','./DynamicGeometry'],function(require) { - - - - var Node = require('./Node'); - var glenum = require('./core/glenum'); - var glinfo = require('./core/glinfo'); - var DynamicGeometry = require('./DynamicGeometry'); - - // Cache - var prevDrawID = 0; - var prevDrawIndicesBuffer = null; - var prevDrawIsUseFace = true; - - var currentDrawID; - - var RenderInfo = function() { - this.faceNumber = 0; - this.vertexNumber = 0; - this.drawCallNumber = 0; - }; - - function VertexArrayObject( - availableAttributes, - availableAttributeSymbols, - indicesBuffer - ) { - this.availableAttributes = availableAttributes; - this.availableAttributeSymbols = availableAttributeSymbols; - this.indicesBuffer = indicesBuffer; - - this.vao = null; - } - /** - * @constructor qtek.Renderable - * @extends qtek.Node - */ - var Renderable = Node.derive( - /** @lends qtek.Renderable# */ - { - /** - * @type {qtek.Material} - */ - material: null, - - /** - * @type {qtek.Geometry} - */ - geometry: null, - - /** - * @type {number} - */ - mode: glenum.TRIANGLES, - - _drawCache: null, - - _renderInfo: null - }, function() { - this._drawCache = {}; - this._renderInfo = new RenderInfo(); - }, - /** @lends qtek.Renderable.prototype */ - { - - /** - * Used when mode is LINES, LINE_STRIP or LINE_LOOP - * @type {number} - */ - lineWidth: 1, - - /** - * @type {boolean} - */ - culling: true, - /** - * @type {number} - */ - cullFace: glenum.BACK, - /** - * @type {number} - */ - frontFace: glenum.CCW, - - /** - * Software frustum culling - * @type {boolean} - */ - frustumCulling: true, - /** - * @type {boolean} - */ - receiveShadow: true, - /** - * @type {boolean} - */ - castShadow: true, - /** - * @type {boolean} - */ - ignorePicking: false, - - /** - * @return {boolean} - */ - isRenderable: function() { - return this.geometry && this.material && this.material.shader && this.visible; - }, - - /** - * @param {WebGLRenderingContext} _gl - * @param {qtek.Material} [globalMaterial] - * @return {Object} - */ - render: function(_gl, globalMaterial) { - var material = globalMaterial || this.material; - var shader = material.shader; - var geometry = this.geometry; - - var glDrawMode = this.mode; - - var nVertex = geometry.getVertexNumber(); - var isUseFace = geometry.isUseFace(); - - var uintExt = glinfo.getExtension(_gl, 'OES_element_index_uint'); - var useUintExt = uintExt && nVertex > 0xffff; - var indicesType = useUintExt ? _gl.UNSIGNED_INT : _gl.UNSIGNED_SHORT; - - var vaoExt = glinfo.getExtension(_gl, 'OES_vertex_array_object'); - - var isStatic = !geometry.dynamic; - - var renderInfo = this._renderInfo; - renderInfo.vertexNumber = nVertex; - renderInfo.faceNumber = 0; - renderInfo.drawCallNumber = 0; - // Draw each chunk - var drawHashChanged = false; - // Hash with shader id in case previous material has less attributes than next material - currentDrawID = _gl.__GLID__ + '-' + geometry.__GUID__ + '-' + shader.__GUID__; - - if (currentDrawID !== prevDrawID) { - drawHashChanged = true; - } else { - // The cache will be invalid in the following cases - // 1. Geometry is splitted to multiple chunks - // 2. VAO is enabled and is binded to null after render - // 3. Geometry needs update - if ( - ((geometry instanceof DynamicGeometry) && (nVertex > 0xffff && !uintExt) && isUseFace) - || (vaoExt && isStatic) - || geometry._cache.isDirty() - ) { - drawHashChanged = true; - } - } - prevDrawID = currentDrawID; - - if (!drawHashChanged) { - // Direct draw - if (prevDrawIsUseFace) { - _gl.drawElements(glDrawMode, prevDrawIndicesBuffer.count, indicesType, 0); - renderInfo.faceNumber = prevDrawIndicesBuffer.count / 3; - } - else { - // FIXME Use vertex number in buffer - // getVertexNumber may get the wrong value when geometry forget to mark dirty after update - _gl.drawArrays(glDrawMode, 0, nVertex); - } - renderInfo.drawCallNumber = 1; - } else { - // Use the cache of static geometry - var vaoList = this._drawCache[currentDrawID]; - if (!vaoList) { - var chunks = geometry.getBufferChunks(_gl); - if (!chunks) { // Empty mesh - return; - } - vaoList = []; - for (var c = 0; c < chunks.length; c++) { - var chunk = chunks[c]; - var attributeBuffers = chunk.attributeBuffers; - var indicesBuffer = chunk.indicesBuffer; - - var availableAttributes = []; - var availableAttributeSymbols = []; - for (var a = 0; a < attributeBuffers.length; a++) { - var attributeBufferInfo = attributeBuffers[a]; - var name = attributeBufferInfo.name; - var semantic = attributeBufferInfo.semantic; - var symbol; - if (semantic) { - var semanticInfo = shader.attribSemantics[semantic]; - symbol = semanticInfo && semanticInfo.symbol; - } else { - symbol = name; - } - if (symbol && shader.attributeTemplates[symbol]) { - availableAttributes.push(attributeBufferInfo); - availableAttributeSymbols.push(symbol); - } - } - - var vao = new VertexArrayObject( - availableAttributes, - availableAttributeSymbols, - indicesBuffer - ); - vaoList.push(vao); - } - if (isStatic) { - this._drawCache[currentDrawID] = vaoList; - } - } - - for (var i = 0; i < vaoList.length; i++) { - var vao = vaoList[i]; - var needsBindAttributes = true; - - // Create vertex object array cost a lot - // So we don't use it on the dynamic object - if (vaoExt && isStatic) { - // Use vertex array object - // http://blog.tojicode.com/2012/10/oesvertexarrayobject-extension.html - if (vao.vao == null) { - vao.vao = vaoExt.createVertexArrayOES(); - } else { - needsBindAttributes = false; - } - vaoExt.bindVertexArrayOES(vao.vao); - } - - var availableAttributes = vao.availableAttributes; - var indicesBuffer = vao.indicesBuffer; - - if (needsBindAttributes) { - var locationList = shader.enableAttributes(_gl, vao.availableAttributeSymbols, (vaoExt && isStatic && vao.vao)); - // Setting attributes; - for (var a = 0; a < availableAttributes.length; a++) { - var location = locationList[a]; - if (location === -1) { - continue; - } - var attributeBufferInfo = availableAttributes[a]; - var buffer = attributeBufferInfo.buffer; - var size = attributeBufferInfo.size; - var glType; - switch (attributeBufferInfo.type) { - case 'float': - glType = _gl.FLOAT; - break; - case 'byte': - glType = _gl.BYTE; - break; - case 'ubyte': - glType = _gl.UNSIGNED_BYTE; - break; - case 'short': - glType = _gl.SHORT; - break; - case 'ushort': - glType = _gl.UNSIGNED_SHORT; - break; - default: - glType = _gl.FLOAT; - break; - } - - _gl.bindBuffer(_gl.ARRAY_BUFFER, buffer); - _gl.vertexAttribPointer(location, size, glType, false, 0, 0); - } - } - if ( - glDrawMode == glenum.LINES || - glDrawMode == glenum.LINE_STRIP || - glDrawMode == glenum.LINE_LOOP - ) { - _gl.lineWidth(this.lineWidth); - } - - prevDrawIndicesBuffer = indicesBuffer; - prevDrawIsUseFace = geometry.isUseFace(); - //Do drawing - if (prevDrawIsUseFace) { - if (needsBindAttributes) { - _gl.bindBuffer(_gl.ELEMENT_ARRAY_BUFFER, indicesBuffer.buffer); - } - _gl.drawElements(glDrawMode, indicesBuffer.count, indicesType, 0); - renderInfo.faceNumber += indicesBuffer.count / 3; - } else { - _gl.drawArrays(glDrawMode, 0, nVertex); - } - - if (vaoExt && isStatic) { - vaoExt.bindVertexArrayOES(null); - } - - renderInfo.drawCallNumber++; - } - } - - return renderInfo; - }, - - /** - * Clone a new renderable - * @method - * @return {qtek.Renderable} - */ - clone: (function() { - var properties = [ - 'castShadow', 'receiveShadow', - 'mode', 'culling', 'cullFace', 'frontFace', - 'frustumCulling' - ]; - return function() { - var renderable = Node.prototype.clone.call(this); - - renderable.geometry = this.geometry; - renderable.material = this.material; - - for (var i = 0; i < properties.length; i++) { - var name = properties[i]; - // Try not to overwrite the prototype property - if (renderable[name] !== this[name]) { - renderable[name] = this[name]; - } - } - - return renderable; - }; - })() - }); - - Renderable.beforeFrame = function() { - prevDrawID = 0; - }; - - // Enums - Renderable.POINTS = glenum.POINTS; - Renderable.LINES = glenum.LINES; - Renderable.LINE_LOOP = glenum.LINE_LOOP; - Renderable.LINE_STRIP = glenum.LINE_STRIP; - Renderable.TRIANGLES = glenum.TRIANGLES; - Renderable.TRIANGLE_STRIP = glenum.TRIANGLE_STRIP; - Renderable.TRIANGLE_FAN = glenum.TRIANGLE_FAN; - - Renderable.BACK = glenum.BACK; - Renderable.FRONT = glenum.FRONT; - Renderable.FRONT_AND_BACK = glenum.FRONT_AND_BACK; - Renderable.CW = glenum.CW; - Renderable.CCW = glenum.CCW; - - Renderable.RenderInfo = RenderInfo; - - return Renderable; -}); -define('qtek/Mesh',['require','./Renderable','./core/glenum'],function(require) { - - - - var Renderable = require('./Renderable'); - var glenum = require('./core/glenum'); - - /** - * @constructor qtek.Mesh - * @extends qtek.Renderable - */ - var Mesh = Renderable.derive( - /** @lends qtek.Mesh# */ - { - /** - * Used when it is a skinned mesh - * @type {qtek.Skeleton} - */ - skeleton: null, - /** - * Joints indices Meshes can share the one skeleton instance and each mesh can use one part of joints. Joints indices indicate the index of joint in the skeleton instance - * @type {number[]} - */ - joints: null - - }, function() { - if (!this.joints) { - this.joints = []; - } - }, { - render: function(_gl, globalMaterial) { - var material = globalMaterial || this.material; - // Set pose matrices of skinned mesh - if (this.skeleton) { - var skinMatricesArray = this.skeleton.getSubSkinMatrices(this.__GUID__, this.joints); - material.shader.setUniformBySemantic(_gl, 'SKIN_MATRIX', skinMatricesArray); - } - - return Renderable.prototype.render.call(this, _gl, globalMaterial); - } - }); - - // Enums - Mesh.POINTS = glenum.POINTS; - Mesh.LINES = glenum.LINES; - Mesh.LINE_LOOP = glenum.LINE_LOOP; - Mesh.LINE_STRIP = glenum.LINE_STRIP; - Mesh.TRIANGLES = glenum.TRIANGLES; - Mesh.TRIANGLE_STRIP = glenum.TRIANGLE_STRIP; - Mesh.TRIANGLE_FAN = glenum.TRIANGLE_FAN; - - Mesh.BACK = glenum.BACK; - Mesh.FRONT = glenum.FRONT; - Mesh.FRONT_AND_BACK = glenum.FRONT_AND_BACK; - Mesh.CW = glenum.CW; - Mesh.CCW = glenum.CCW; - - return Mesh; -}); -/** - * @export{Object} library - */ -define('qtek/shader/library',['require','../Shader','../core/util'],function(require) { - - var Shader = require('../Shader'); - var util = require('../core/util'); - - var _library = {}; - - /** - * @export qtek.shader.library~Libaray - */ - function ShaderLibrary () { - this._pool = {}; - } - - /** - * ### Builin shaders - * + buildin.standard - * + buildin.basic - * + buildin.lambert - * + buildin.phong - * + buildin.wireframe - * - * @namespace qtek.shader.library - */ - /** - * - * Get shader from library. use shader name and option as hash key. - * - * @param {string} name - * @param {Object|string|Array.} [option] - * @return {qtek.Shader} - * - * @example - * qtek.shader.library.get('buildin.phong', 'diffuseMap', 'normalMap'); - * qtek.shader.library.get('buildin.phong', ['diffuseMap', 'normalMap']); - * qtek.shader.library.get('buildin.phong', { - * textures: ['diffuseMap'], - * vertexDefines: {}, - * fragmentDefines: {} - */ - ShaderLibrary.prototype.get = function(name, option) { - var enabledTextures = []; - var vertexDefines = {}; - var fragmentDefines = {}; - if (typeof(option) === 'string') { - enabledTextures = Array.prototype.slice.call(arguments, 1); - } - else if (Object.prototype.toString.call(option) == '[object Object]') { - enabledTextures = option.textures || []; - vertexDefines = option.vertexDefines || {}; - fragmentDefines = option.fragmentDefines || {}; - } - else if(option instanceof Array) { - enabledTextures = option; - } - var vertexDefineKeys = Object.keys(vertexDefines); - var fragmentDefineKeys = Object.keys(fragmentDefines); - enabledTextures.sort(); - vertexDefineKeys.sort(); - fragmentDefineKeys.sort(); - - var keyArr = [name]; - keyArr = keyArr.concat(enabledTextures); - for (var i = 0; i < vertexDefineKeys.length; i++) { - keyArr.push(vertexDefines[vertexDefineKeys[i]]); - } - for (var i = 0; i < fragmentDefineKeys.length; i++) { - keyArr.push(fragmentDefines[fragmentDefineKeys[i]]); - } - var key = keyArr.join('_'); - - if (this._pool[key]) { - return this._pool[key]; - } else { - var source = _library[name]; - if (!source) { - console.error('Shader "' + name + '"' + ' is not in the library'); - return; - } - var shader = new Shader({ - 'vertex': source.vertex, - 'fragment': source.fragment - }); - for (var i = 0; i < enabledTextures.length; i++) { - shader.enableTexture(enabledTextures[i]); - } - for (var name in vertexDefines) { - shader.define('vertex', name, vertexDefines[name]); - } - for (var name in fragmentDefines) { - shader.define('fragment', name, fragmentDefines[name]); - } - this._pool[key] = shader; - return shader; - } - }; - - /** - * Clear shaders - */ - ShaderLibrary.prototype.clear = function() { - this._pool = {}; - }; - - /** - * @memberOf qtek.shader.library - * @param {string} name - * @param {string} vertex - Vertex shader code - * @param {string} fragment - Fragment shader code - */ - function template(name, vertex, fragment) { - _library[name] = { - vertex: vertex, - fragment: fragment - }; - } - - var defaultLibrary = new ShaderLibrary(); - - return { - createLibrary: function () { - return new ShaderLibrary(); - }, - get: function () { - return defaultLibrary.get.apply(defaultLibrary, arguments); - }, - template: template, - clear: function () { - return defaultLibrary.clear(); - } - }; -}); -define('qtek/math/Vector2',['require','../dep/glmatrix'],function(require) { - - - - var glMatrix = require('../dep/glmatrix'); - var vec2 = glMatrix.vec2; - - /** - * @constructor - * @alias qtek.math.Vector2 - * @param {number} x - * @param {number} y - */ - var Vector2 = function(x, y) { - - x = x || 0; - y = y || 0; - - /** - * Storage of Vector2, read and write of x, y will change the values in _array - * All methods also operate on the _array instead of x, y components - * @type {Float32Array} - */ - this._array = vec2.fromValues(x, y); - - /** - * Dirty flag is used by the Node to determine - * if the matrix is updated to latest - * @type {boolean} - */ - this._dirty = true; - }; - - Vector2.prototype = { - - constructor: Vector2, - - /** - * Add b to self - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - add: function(b) { - vec2.add(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Set x and y components - * @param {number} x - * @param {number} y - * @return {qtek.math.Vector2} - */ - set: function(x, y) { - this._array[0] = x; - this._array[1] = y; - this._dirty = true; - return this; - }, - - /** - * Set x and y components from array - * @param {Float32Array|number[]} arr - * @return {qtek.math.Vector2} - */ - setArray: function(arr) { - this._array[0] = arr[0]; - this._array[1] = arr[1]; - - this._dirty = true; - return this; - }, - - /** - * Clone a new Vector2 - * @return {qtek.math.Vector2} - */ - clone: function() { - return new Vector2(this.x, this.y); - }, - - /** - * Copy x, y from b - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - copy: function(b) { - vec2.copy(this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Cross product of self and b, written to a Vector3 out - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - cross: function(out, b) { - vec2.cross(out._array, this._array, b._array); - out._dirty = true; - return this; - }, - - /** - * Alias for distance - * @param {qtek.math.Vector2} b - * @return {number} - */ - dist: function(b) { - return vec2.dist(this._array, b._array); - }, - - /** - * Distance between self and b - * @param {qtek.math.Vector2} b - * @return {number} - */ - distance: function(b) { - return vec2.distance(this._array, b._array); - }, - - /** - * Alias for divide - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - div: function(b) { - vec2.div(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Divide self by b - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - divide: function(b) { - vec2.divide(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Dot product of self and b - * @param {qtek.math.Vector2} b - * @return {number} - */ - dot: function(b) { - return vec2.dot(this._array, b._array); - }, - - /** - * Alias of length - * @return {number} - */ - len: function() { - return vec2.len(this._array); - }, - - /** - * Calculate the length - * @return {number} - */ - length: function() { - return vec2.length(this._array); - }, - - /** - * Linear interpolation between a and b - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @param {number} t - * @return {qtek.math.Vector2} - */ - lerp: function(a, b, t) { - vec2.lerp(this._array, a._array, b._array, t); - this._dirty = true; - return this; - }, - - /** - * Minimum of self and b - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - min: function(b) { - vec2.min(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Maximum of self and b - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - max: function(b) { - vec2.max(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Alias for multiply - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - mul: function(b) { - vec2.mul(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Mutiply self and b - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - multiply: function(b) { - vec2.multiply(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Negate self - * @return {qtek.math.Vector2} - */ - negate: function() { - vec2.negate(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Normalize self - * @return {qtek.math.Vector2} - */ - normalize: function() { - vec2.normalize(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Generate random x, y components with a given scale - * @param {number} scale - * @return {qtek.math.Vector2} - */ - random: function(scale) { - vec2.random(this._array, scale); - this._dirty = true; - return this; - }, - - /** - * Scale self - * @param {number} scale - * @return {qtek.math.Vector2} - */ - scale: function(s) { - vec2.scale(this._array, this._array, s); - this._dirty = true; - return this; - }, - - /** - * Scale b and add to self - * @param {qtek.math.Vector2} b - * @param {number} scale - * @return {qtek.math.Vector2} - */ - scaleAndAdd: function(b, s) { - vec2.scaleAndAdd(this._array, this._array, b._array, s); - this._dirty = true; - return this; - }, - - /** - * Alias for squaredDistance - * @param {qtek.math.Vector2} b - * @return {number} - */ - sqrDist: function(b) { - return vec2.sqrDist(this._array, b._array); - }, - - /** - * Squared distance between self and b - * @param {qtek.math.Vector2} b - * @return {number} - */ - squaredDistance: function(b) { - return vec2.squaredDistance(this._array, b._array); - }, - - /** - * Alias for squaredLength - * @return {number} - */ - sqrLen: function() { - return vec2.sqrLen(this._array); - }, - - /** - * Squared length of self - * @return {number} - */ - squaredLength: function() { - return vec2.squaredLength(this._array); - }, - - /** - * Alias for subtract - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - sub: function(b) { - vec2.sub(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Subtract b from self - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - subtract: function(b) { - vec2.subtract(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Transform self with a Matrix2 m - * @param {qtek.math.Matrix2} m - * @return {qtek.math.Vector2} - */ - transformMat2: function(m) { - vec2.transformMat2(this._array, this._array, m._array); - this._dirty = true; - return this; - }, - - /** - * Transform self with a Matrix2d m - * @param {qtek.math.Matrix2d} m - * @return {qtek.math.Vector2} - */ - transformMat2d: function(m) { - vec2.transformMat2d(this._array, this._array, m._array); - this._dirty = true; - return this; - }, - - /** - * Transform self with a Matrix3 m - * @param {qtek.math.Matrix3} m - * @return {qtek.math.Vector2} - */ - transformMat3: function(m) { - vec2.transformMat3(this._array, this._array, m._array); - this._dirty = true; - return this; - }, - - /** - * Transform self with a Matrix4 m - * @param {qtek.math.Matrix4} m - * @return {qtek.math.Vector2} - */ - transformMat4: function(m) { - vec2.transformMat4(this._array, this._array, m._array); - this._dirty = true; - return this; - }, - - toString: function() { - return '[' + Array.prototype.join.call(this._array, ',') + ']'; - }, - }; - - // Getter and Setter - if (Object.defineProperty) { - - var proto = Vector2.prototype; - /** - * @name x - * @type {number} - * @memberOf qtek.math.Vector2 - * @instance - */ - Object.defineProperty(proto, 'x', { - get: function () { - return this._array[0]; - }, - set: function (value) { - this._array[0] = value; - this._dirty = true; - } - }); - - /** - * @name y - * @type {number} - * @memberOf qtek.math.Vector2 - * @instance - */ - Object.defineProperty(proto, 'y', { - get: function () { - return this._array[1]; - }, - set: function (value) { - this._array[1] = value; - this._dirty = true; - } - }); - } - - // Supply methods that are not in place - - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - Vector2.add = function(out, a, b) { - vec2.add(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector2} out - * @param {number} x - * @param {number} y - * @return {qtek.math.Vector2} - */ - Vector2.set = function(out, x, y) { - vec2.set(out._array, x, y); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - Vector2.copy = function(out, b) { - vec2.copy(out._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector3} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - Vector2.cross = function(out, a, b) { - vec2.cross(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {number} - */ - Vector2.dist = function(a, b) { - return vec2.distance(a._array, b._array); - }; - /** - * @method - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {number} - */ - Vector2.distance = Vector2.dist; - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - Vector2.div = function(out, a, b) { - vec2.divide(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - /** - * @method - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - Vector2.divide = Vector2.div; - /** - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {number} - */ - Vector2.dot = function(a, b) { - return vec2.dot(a._array, b._array); - }; - - /** - * @param {qtek.math.Vector2} a - * @return {number} - */ - Vector2.len = function(b) { - return vec2.length(b._array); - }; - - // Vector2.length = Vector2.len; - - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @param {number} t - * @return {qtek.math.Vector2} - */ - Vector2.lerp = function(out, a, b, t) { - vec2.lerp(out._array, a._array, b._array, t); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - Vector2.min = function(out, a, b) { - vec2.min(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - Vector2.max = function(out, a, b) { - vec2.max(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - Vector2.mul = function(out, a, b) { - vec2.multiply(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - /** - * @method - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - Vector2.multiply = Vector2.mul; - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @return {qtek.math.Vector2} - */ - Vector2.negate = function(out, a) { - vec2.negate(out._array, a._array); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @return {qtek.math.Vector2} - */ - Vector2.normalize = function(out, a) { - vec2.normalize(out._array, a._array); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector2} out - * @param {number} scale - * @return {qtek.math.Vector2} - */ - Vector2.random = function(out, scale) { - vec2.random(out._array, scale); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {number} scale - * @return {qtek.math.Vector2} - */ - Vector2.scale = function(out, a, scale) { - vec2.scale(out._array, a._array, scale); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @param {number} scale - * @return {qtek.math.Vector2} - */ - Vector2.scaleAndAdd = function(out, a, b, scale) { - vec2.scaleAndAdd(out._array, a._array, b._array, scale); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {number} - */ - Vector2.sqrDist = function(a, b) { - return vec2.sqrDist(a._array, b._array); - }; - /** - * @method - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {number} - */ - Vector2.squaredDistance = Vector2.sqrDist; - - /** - * @param {qtek.math.Vector2} a - * @return {number} - */ - Vector2.sqrLen = function(a) { - return vec2.sqrLen(a._array); - }; - /** - * @method - * @param {qtek.math.Vector2} a - * @return {number} - */ - Vector2.squaredLength = Vector2.sqrLen; - - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - Vector2.sub = function(out, a, b) { - vec2.subtract(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - /** - * @method - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Vector2} b - * @return {qtek.math.Vector2} - */ - Vector2.subtract = Vector2.sub; - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Matrix2} m - * @return {qtek.math.Vector2} - */ - Vector2.transformMat2 = function(out, a, m) { - vec2.transformMat2(out._array, a._array, m._array); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Matrix2d} m - * @return {qtek.math.Vector2} - */ - Vector2.transformMat2d = function(out, a, m) { - vec2.transformMat2d(out._array, a._array, m._array); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {Matrix3} m - * @return {qtek.math.Vector2} - */ - Vector2.transformMat3 = function(out, a, m) { - vec2.transformMat3(out._array, a._array, m._array); - out._dirty = true; - return out; - }; - /** - * @param {qtek.math.Vector2} out - * @param {qtek.math.Vector2} a - * @param {qtek.math.Matrix4} m - * @return {qtek.math.Vector2} - */ - Vector2.transformMat4 = function(out, a, m) { - vec2.transformMat4(out._array, a._array, m._array); - out._dirty = true; - return out; - }; - - return Vector2; + -}); -// TODO Resources like shader, texture, geometry reference management -// Trace and find out which shader, texture, geometry can be destroyed -define('qtek/Renderer',['require','./core/Base','./Texture','./core/glinfo','./core/glenum','./math/BoundingBox','./math/Matrix4','./shader/library','./Material','./math/Vector2','./dep/glmatrix'],function(require) { - - - - var Base = require('./core/Base'); - var Texture = require('./Texture'); - var glinfo = require('./core/glinfo'); - var glenum = require('./core/glenum'); - var BoundingBox = require('./math/BoundingBox'); - var Matrix4 = require('./math/Matrix4'); - var shaderLibrary = require('./shader/library'); - var Material = require('./Material'); - var Vector2 = require('./math/Vector2'); - - var glMatrix = require('./dep/glmatrix'); - var mat4 = glMatrix.mat4; - var vec3 = glMatrix.vec3; - - var glid = 0; - - var errorShader = {}; - - /** - * @constructor qtek.Renderer - */ - var Renderer = Base.derive(function() { - return /** @lends qtek.Renderer# */ { - - /** - * @type {HTMLCanvasElement} - * @readonly - */ - canvas: null, - - /** - * Canvas width, set by resize method - * @type {number} - * @private - */ - width: 100, - - /** - * Canvas width, set by resize method - * @type {number} - * @private - */ - height: 100, - - /** - * Device pixel ratio, set by setDevicePixelRatio method - * Specially for high defination display - * @see http://www.khronos.org/webgl/wiki/HandlingHighDPI - * @type {number} - * @private - */ - devicePixelRatio: window.devicePixelRatio || 1.0, - - /** - * Clear color - * @type {number[]} - */ - color: [0.0, 0.0, 0.0, 0.0], - - /** - * Default: - * _gl.COLOR_BUFFER_BIT | _gl.DEPTH_BUFFER_BIT | _gl.STENCIL_BUFFER_BIT - * @type {number} - */ - clear: 17664, - - // Settings when getting context - // http://www.khronos.org/registry/webgl/specs/latest/#2.4 - - /** - * If enable alpha, default true - * @type {boolean} - */ - alhpa: true, - /** - * If enable depth buffer, default true - * @type {boolean} - */ - depth: true, - /** - * If enable stencil buffer, default false - * @type {boolean} - */ - stencil: false, - /** - * If enable antialias, default true - * @type {boolean} - */ - antialias: true, - /** - * If enable premultiplied alpha, default true - * @type {boolean} - */ - premultipliedAlpha: true, - /** - * If preserve drawing buffer, default false - * @type {boolean} - */ - preserveDrawingBuffer: false, - /** - * If throw context error, usually turned on in debug mode - * @type {boolean} - */ - throwError: true, - /** - * WebGL Context created from given canvas - * @type {WebGLRenderingContext} - */ - gl: null, - /** - * Renderer viewport, read-only, can be set by setViewport method - * @type {Object} - */ - viewport: {}, - - _viewportSettings: [], - _clearSettings: [], - - _sceneRendering: null - }; - }, function() { - - if (!this.canvas) { - this.canvas = document.createElement('canvas'); - this.canvas.width = this.width; - this.canvas.height = this.height; - } - try { - var opts = { - alhpa: this.alhpa, - depth: this.depth, - stencil: this.stencil, - antialias: this.antialias, - premultipliedAlpha: this.premultipliedAlpha, - preserveDrawingBuffer: this.preserveDrawingBuffer - }; - - this.gl = this.canvas.getContext('webgl', opts) - || this.canvas.getContext('experimental-webgl', opts); - - if (!this.gl) { - throw new Error(); - } - - this.gl.__GLID__ = glid++; - - this.width = this.canvas.width; - this.height = this.canvas.height; - this.resize(this.width, this.height); - - glinfo.initialize(this.gl); - } catch(e) { - throw 'Error creating WebGL Context ' + e; - } - }, - /** @lends qtek.Renderer.prototype. **/ - { - /** - * Resize the canvas - * @param {number} width - * @param {number} height - */ - resize: function(width, height) { - var canvas = this.canvas; - // http://www.khronos.org/webgl/wiki/HandlingHighDPI - // set the display size of the canvas. - if (typeof(width) !== 'undefined') { - canvas.style.width = width + 'px'; - canvas.style.height = height + 'px'; - // set the size of the drawingBuffer - canvas.width = width * this.devicePixelRatio; - canvas.height = height * this.devicePixelRatio; - - this.width = width; - this.height = height; - } else { - this.width = canvas.width / this.devicePixelRatio; - this.height = canvas.height / this.devicePixelRatio; - } - - this.setViewport(0, 0, width, height); - }, - - /** - * Get renderer width - * @return {number} - */ - getWidth: function () { - return this.width; - }, - - /** - * Get renderer height - * @return {number} - */ - getHeight: function () { - return this.height; - }, - - /** - * Set devicePixelRatio - * @param {number} devicePixelRatio - */ - setDevicePixelRatio: function(devicePixelRatio) { - this.devicePixelRatio = devicePixelRatio; - this.resize(this.width, this.height); - }, - - /** - * Get devicePixelRatio - * @param {number} devicePixelRatio - */ - getDevicePixelRatio: function () { - return this.devicePixelRatio; - }, - - /** - * Get WebGL extionsion - * @return {[type]} [description] - */ - getExtension: function (name) { - return glinfo.getExtension(this.gl, name); - }, - - /** - * Set rendering viewport - * @param {number|Object} x - * @param {number} [y] - * @param {number} [width] - * @param {number} [height] - * @param {number} [devicePixelRatio] - * Defaultly use the renderere devicePixelRatio - * It needs to be 1 when setViewport is called by frameBuffer - */ - setViewport: function(x, y, width, height, dpr) { - - if (typeof(x) === 'object') { - var obj = x; - x = obj.x; - y = obj.y; - width = obj.width; - height = obj.height; - } - dpr = dpr || this.devicePixelRatio; - - this.gl.viewport( - x * dpr, y * dpr, width * dpr, height * dpr - ); - - this.viewport = { - x: x, - y: y, - width: width, - height: height - }; - }, - - /** - * Push current viewport into a stack - */ - saveViewport: function() { - this._viewportSettings.push(this.viewport); - }, - - /** - * Pop viewport from stack, restore in the renderer - */ - restoreViewport: function() { - if (this._viewportSettings.length > 0) { - this.setViewport(this._viewportSettings.pop()); - } - }, - - /** - * Push current clear into a stack - */ - saveClear: function() { - this._clearSettings.push(this.clear); - }, - - /** - * Pop clear from stack, restore in the renderer - */ - restoreClear: function() { - if (this._clearSettings.length > 0) { - this.clear = this._clearSettings.pop(); - } - }, - /** - * Render the scene in camera to the screen or binded offline framebuffer - * @param {qtek.Scene} scene - * @param {qtek.Camera} camera - * @param {boolean} [notUpdateScene] If not call the scene.update methods in the rendering, default true - * @param {boolean} [preZ] If use preZ optimization, default false - * @return {IRenderInfo} - */ - render: function(scene, camera, notUpdateScene, preZ) { - var _gl = this.gl; - - this._sceneRendering = scene; - - var color = this.color; - - if (this.clear) { - // Must set depth and color mask true before clear - _gl.colorMask(true, true, true, true); - _gl.depthMask(true); - - _gl.clearColor(color[0], color[1], color[2], color[3]); - _gl.clear(this.clear); - } - - // If the scene have been updated in the prepass like shadow map - // There is no need to update it again - if (!notUpdateScene) { - scene.update(false); - } - // Update if camera not mounted on the scene - if (!camera.getScene()) { - camera.update(true); - } - - var opaqueQueue = scene.opaqueQueue; - var transparentQueue = scene.transparentQueue; - var sceneMaterial = scene.material; - - scene.trigger('beforerender', this, scene, camera); - // Sort render queue - // Calculate the object depth - if (transparentQueue.length > 0) { - var worldViewMat = mat4.create(); - var posViewSpace = vec3.create(); - for (var i = 0; i < transparentQueue.length; i++) { - var node = transparentQueue[i]; - mat4.multiply(worldViewMat, camera.viewMatrix._array, node.worldTransform._array); - vec3.transformMat4(posViewSpace, node.position._array, worldViewMat); - node.__depth = posViewSpace[2]; - } - } - opaqueQueue.sort(Renderer.opaqueSortFunc); - transparentQueue.sort(Renderer.transparentSortFunc); - - // Render Opaque queue - scene.trigger('beforerender:opaque', this, opaqueQueue); - - // Reset the scene bounding box; - camera.sceneBoundingBoxLastFrame.min.set(Infinity, Infinity, Infinity); - camera.sceneBoundingBoxLastFrame.max.set(-Infinity, -Infinity, -Infinity); - - _gl.disable(_gl.BLEND); - _gl.enable(_gl.DEPTH_TEST); - var opaqueRenderInfo = this.renderQueue(opaqueQueue, camera, sceneMaterial, preZ); - - scene.trigger('afterrender:opaque', this, opaqueQueue, opaqueRenderInfo); - scene.trigger('beforerender:transparent', this, transparentQueue); - - // Render Transparent Queue - _gl.enable(_gl.BLEND); - var transparentRenderInfo = this.renderQueue(transparentQueue, camera, sceneMaterial); - - scene.trigger('afterrender:transparent', this, transparentQueue, transparentRenderInfo); - var renderInfo = {}; - for (var name in opaqueRenderInfo) { - renderInfo[name] = opaqueRenderInfo[name] + transparentRenderInfo[name]; - } - - scene.trigger('afterrender', this, scene, camera, renderInfo); - - // Cleanup - this._sceneRendering = null; - return renderInfo; - }, - - /** - * Render a single renderable list in camera in sequence - * @param {qtek.Renderable[]} queue List of all renderables. - * Best to be sorted by Renderer.opaqueSortFunc or Renderer.transparentSortFunc - * @param {qtek.Camera} camera - * @param {qtek.Material} [globalMaterial] globalMaterial will override the material of each renderable - * @param {boolean} [preZ] If use preZ optimization, default false - * @return {IRenderInfo} - */ - renderQueue: function(queue, camera, globalMaterial, preZ) { - var renderInfo = { - faceNumber: 0, - vertexNumber: 0, - drawCallNumber: 0, - meshNumber: queue.length, - renderedMeshNumber: 0 - }; - - // Calculate view and projection matrix - mat4.copy(matrices.VIEW, camera.viewMatrix._array); - mat4.copy(matrices.PROJECTION, camera.projectionMatrix._array); - mat4.multiply(matrices.VIEWPROJECTION, camera.projectionMatrix._array, matrices.VIEW); - mat4.copy(matrices.VIEWINVERSE, camera.worldTransform._array); - mat4.invert(matrices.PROJECTIONINVERSE, matrices.PROJECTION); - mat4.invert(matrices.VIEWPROJECTIONINVERSE, matrices.VIEWPROJECTION); - - var _gl = this.gl; - var scene = this._sceneRendering; - - var prevMaterial; - var prevShader; - - // Status - var depthTest, depthMask; - var culling, cullFace, frontFace; - - var culledRenderQueue; - if (preZ) { - var preZPassMaterial = new Material({ - shader: shaderLibrary.get('buildin.prez') - }); - var preZPassShader = preZPassMaterial.shader; - - culledRenderQueue = []; - preZPassShader.bind(_gl); - _gl.colorMask(false, false, false, false); - _gl.depthMask(true); - _gl.enable(_gl.DEPTH_TEST); - for (var i = 0; i < queue.length; i++) { - var renderable = queue[i]; - var worldM = renderable.worldTransform._array; - var geometry = renderable.geometry; - mat4.multiply(matrices.WORLDVIEW, matrices.VIEW , worldM); - mat4.multiply(matrices.WORLDVIEWPROJECTION, matrices.VIEWPROJECTION , worldM); - - if (geometry.boundingBox) { - if (this.isFrustumCulled( - renderable, camera, matrices.WORLDVIEW, matrices.PROJECTION - )) { - continue; - } - } - if (renderable.skeleton) { // Skip skinned mesh - continue; - } - if (renderable.cullFace !== cullFace) { - cullFace = renderable.cullFace; - _gl.cullFace(cullFace); - } - if (renderable.frontFace !== frontFace) { - frontFace = renderable.frontFace; - _gl.frontFace(frontFace); - } - if (renderable.culling !== culling) { - culling = renderable.culling; - culling ? _gl.enable(_gl.CULL_FACE) : _gl.disable(_gl.CULL_FACE); - } - - var semanticInfo = preZPassShader.matrixSemantics.WORLDVIEWPROJECTION; - preZPassShader.setUniform(_gl, semanticInfo.type, semanticInfo.symbol, matrices.WORLDVIEWPROJECTION); - renderable.render(_gl, preZPassMaterial); - culledRenderQueue.push(renderable); - } - _gl.depthFunc(_gl.LEQUAL); - _gl.colorMask(true, true, true, true); - _gl.depthMask(false); - } else { - culledRenderQueue = queue; - } - - culling = null; - cullFace = null; - frontFace = null; - - for (var i =0; i < culledRenderQueue.length; i++) { - var renderable = culledRenderQueue[i]; - var material = globalMaterial || renderable.material; - var shader = material.shader; - var geometry = renderable.geometry; - - var worldM = renderable.worldTransform._array; - // All matrices ralated to world matrix will be updated on demand; - mat4.copy(matrices.WORLD, worldM); - mat4.multiply(matrices.WORLDVIEW, matrices.VIEW , worldM); - mat4.multiply(matrices.WORLDVIEWPROJECTION, matrices.VIEWPROJECTION , worldM); - if (shader.matrixSemantics.WORLDINVERSE || - shader.matrixSemantics.WORLDINVERSETRANSPOSE) { - mat4.invert(matrices.WORLDINVERSE, worldM); - } - if (shader.matrixSemantics.WORLDVIEWINVERSE || - shader.matrixSemantics.WORLDVIEWINVERSETRANSPOSE) { - mat4.invert(matrices.WORLDVIEWINVERSE, matrices.WORLDVIEW); - } - if (shader.matrixSemantics.WORLDVIEWPROJECTIONINVERSE || - shader.matrixSemantics.WORLDVIEWPROJECTIONINVERSETRANSPOSE) { - mat4.invert(matrices.WORLDVIEWPROJECTIONINVERSE, matrices.WORLDVIEWPROJECTION); - } - if (geometry.boundingBox && ! preZ) { - if (this.isFrustumCulled( - renderable, camera, matrices.WORLDVIEW, matrices.PROJECTION - )) { - continue; - } - } - - if (prevShader !== shader) { - // Set lights number - if (scene && scene.isShaderLightNumberChanged(shader)) { - scene.setShaderLightNumber(shader); - } - - var errMsg = shader.bind(_gl); - if (errMsg) { - - if (errorShader[shader.__GUID__]) { - continue; - } - errorShader[shader.__GUID__] = true; - - if (this.throwError) { - throw new Error(errMsg); - } else { - this.trigger('error', errMsg); - } - } - - // Set lights uniforms - // TODO needs optimized - if (scene) { - scene.setLightUniforms(shader, _gl); - } - prevShader = shader; - } - if (prevMaterial !== material) { - if (!preZ) { - if (material.depthTest !== depthTest) { - material.depthTest ? - _gl.enable(_gl.DEPTH_TEST) : - _gl.disable(_gl.DEPTH_TEST); - depthTest = material.depthTest; - } - if (material.depthMask !== depthMask) { - _gl.depthMask(material.depthMask); - depthMask = material.depthMask; - } - } - material.bind(_gl, prevMaterial); - prevMaterial = material; - - // TODO cache blending - if (material.transparent) { - if (material.blend) { - material.blend(_gl); - } else { // Default blend function - _gl.blendEquationSeparate(_gl.FUNC_ADD, _gl.FUNC_ADD); - _gl.blendFuncSeparate(_gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA, _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA); - } - } - } - - var matrixSemanticKeys = shader.matrixSemanticKeys; - for (var k = 0; k < matrixSemanticKeys.length; k++) { - var semantic = matrixSemanticKeys[k]; - var semanticInfo = shader.matrixSemantics[semantic]; - var matrix = matrices[semantic]; - if (semanticInfo.isTranspose) { - var matrixNoTranspose = matrices[semanticInfo.semanticNoTranspose]; - mat4.transpose(matrix, matrixNoTranspose); - } - shader.setUniform(_gl, semanticInfo.type, semanticInfo.symbol, matrix); - } - - if (renderable.cullFace !== cullFace) { - cullFace = renderable.cullFace; - _gl.cullFace(cullFace); - } - if (renderable.frontFace !== frontFace) { - frontFace = renderable.frontFace; - _gl.frontFace(frontFace); - } - if (renderable.culling !== culling) { - culling = renderable.culling; - culling ? _gl.enable(_gl.CULL_FACE) : _gl.disable(_gl.CULL_FACE); - } - - var objectRenderInfo = renderable.render(_gl, globalMaterial); - - if (objectRenderInfo) { - renderInfo.faceNumber += objectRenderInfo.faceNumber; - renderInfo.vertexNumber += objectRenderInfo.vertexNumber; - renderInfo.drawCallNumber += objectRenderInfo.drawCallNumber; - renderInfo.renderedMeshNumber ++; - } - } - - if (preZ) { - // default depth func - _gl.depthFunc(_gl.LESS); - } - - return renderInfo; - }, - - /** - * Evaluate if an scene object is culled by camera frustum - * - * Object can be a renderable or a light - * - * @param {qtek.Node} Scene object - * @param {qtek.Camera} camera - * @param {Array.} worldViewMat represented with array - * @param {Array.} projectionMat represented with array - */ - isFrustumCulled: (function() { - // Frustum culling - // http://www.cse.chalmers.se/~uffe/vfc_bbox.pdf - var cullingBoundingBox = new BoundingBox(); - var cullingMatrix = new Matrix4(); - return function(object, camera, worldViewMat, projectionMat) { - // Bounding box can be a property of object(like light) or renderable.geometry - var geoBBox = object.boundingBox || object.geometry.boundingBox; - cullingMatrix._array = worldViewMat; - cullingBoundingBox.copy(geoBBox); - cullingBoundingBox.applyTransform(cullingMatrix); - - // Passingly update the scene bounding box - // FIXME exclude very large mesh like ground plane or terrain ? - // FIXME Only rendererable which cast shadow ? - if (object.isRenderable() && object.castShadow) { - camera.sceneBoundingBoxLastFrame.union(cullingBoundingBox); - } - - if (object.frustumCulling) { - if (!cullingBoundingBox.intersectBoundingBox(camera.frustum.boundingBox)) { - return true; - } - - cullingMatrix._array = projectionMat; - if ( - cullingBoundingBox.max._array[2] > 0 && - cullingBoundingBox.min._array[2] < 0 - ) { - // Clip in the near plane - cullingBoundingBox.max._array[2] = -1e-20; - } - - cullingBoundingBox.applyProjection(cullingMatrix); - - var min = cullingBoundingBox.min._array; - var max = cullingBoundingBox.max._array; - - if ( - max[0] < -1 || min[0] > 1 - || max[1] < -1 || min[1] > 1 - || max[2] < -1 || min[2] > 1 - ) { - return true; - } - } - return false; - }; - })(), - - /** - * Dispose given scene, including all geometris, textures and shaders in the scene - * @param {qtek.Scene} scene - */ - disposeScene: function(scene) { - this.disposeNode(scene, true, true); - scene.dispose(); - }, - - /** - * Dispose given node, including all geometries, textures and shaders attached on it or its descendant - * @param {qtek.Node} node - * @param {boolean} [disposeGeometry=false] If dispose the geometries used in the descendant mesh - * @param {boolean} [disposeTexture=false] If dispose the textures used in the descendant mesh - */ - disposeNode: function(root, disposeGeometry, disposeTexture) { - var materials = {}; - var _gl = this.gl; - // Dettached from parent - if (root.getParent()) { - root.getParent().remove(root); - } - root.traverse(function(node) { - if (node.geometry && disposeGeometry) { - node.geometry.dispose(_gl); - } - if (node.material) { - materials[node.material.__GUID__] = node.material; - } - // Particle system need to dispose - if (node.dispose) { - node.dispose(_gl); - } - }); - for (var guid in materials) { - var mat = materials[guid]; - mat.dispose(_gl, disposeTexture); - } - }, - - /** - * Dispose given shader - * @param {qtek.Shader} shader - */ - disposeShader: function(shader) { - shader.dispose(this.gl); - }, - - /** - * Dispose given geometry - * @param {qtek.Geometry} geometry - */ - disposeGeometry: function(geometry) { - geometry.dispose(this.gl); - }, - - /** - * Dispose given texture - * @param {qtek.Texture} texture - */ - disposeTexture: function(texture) { - texture.dispose(this.gl); - }, - - /** - * Dispose given frame buffer - * @param {qtek.FrameBuffer} frameBuffer - */ - disposeFrameBuffer: function(frameBuffer) { - frameBuffer.dispose(this.gl); - }, - - /** - * Dispose renderer - */ - dispose: function() { - glinfo.dispose(this.gl); - }, - - /** - * Convert screen coords to normalized device coordinates(NDC) - * Screen coords can get from mouse event, it is positioned relative to canvas element - * NDC can be used in ray casting with Camera.prototype.castRay methods - * - * @param {number} x - * @param {number} y - * @param {qtek.math.Vector2} [out] - * @return {qtek.math.Vector2} - */ - screenToNdc: function(x, y, out) { - if (!out) { - out = new Vector2(); - } - // Invert y; - y = this.height - y; - - var viewport = this.viewport; - var arr = out._array; - arr[0] = (x - viewport.x) / viewport.width; - arr[0] = arr[0] * 2 - 1; - arr[1] = (y - viewport.y) / viewport.height; - arr[1] = arr[1] * 2 - 1; - - return out; - } - }); - - /** - * Opaque renderables compare function - * @param {qtek.Renderable} x - * @param {qtek.Renderable} y - * @return {boolean} - * @static - */ - Renderer.opaqueSortFunc = function(x, y) { - // Priority shader -> material -> geometry - if (x.material.shader === y.material.shader) { - if (x.material === y.material) { - return x.geometry.__GUID__ - y.geometry.__GUID__; - } - return x.material.__GUID__ - y.material.__GUID__; - } - return x.material.shader.__GUID__ - y.material.shader.__GUID__; - }; - - /** - * Transparent renderables compare function - * @param {qtek.Renderable} a - * @param {qtek.Renderable} b - * @return {boolean} - * @static - */ - Renderer.transparentSortFunc = function(x, y) { - // Priority depth -> shader -> material -> geometry - if (x.__depth === y.__depth) { - if (x.material.shader === y.material.shader) { - if (x.material === y.material) { - return x.geometry.__GUID__ - y.geometry.__GUID__; - } - return x.material.__GUID__ - y.material.__GUID__; - } - return x.material.shader.__GUID__ - y.material.shader.__GUID__; - } - // Depth is negative - // So farther object has smaller depth value - return x.__depth - y.__depth; - }; - - // Temporary variables - var matrices = { - WORLD: mat4.create(), - VIEW: mat4.create(), - PROJECTION: mat4.create(), - WORLDVIEW: mat4.create(), - VIEWPROJECTION: mat4.create(), - WORLDVIEWPROJECTION: mat4.create(), - - WORLDINVERSE: mat4.create(), - VIEWINVERSE: mat4.create(), - PROJECTIONINVERSE: mat4.create(), - WORLDVIEWINVERSE: mat4.create(), - VIEWPROJECTIONINVERSE: mat4.create(), - WORLDVIEWPROJECTIONINVERSE: mat4.create(), - - WORLDTRANSPOSE: mat4.create(), - VIEWTRANSPOSE: mat4.create(), - PROJECTIONTRANSPOSE: mat4.create(), - WORLDVIEWTRANSPOSE: mat4.create(), - VIEWPROJECTIONTRANSPOSE: mat4.create(), - WORLDVIEWPROJECTIONTRANSPOSE: mat4.create(), - WORLDINVERSETRANSPOSE: mat4.create(), - VIEWINVERSETRANSPOSE: mat4.create(), - PROJECTIONINVERSETRANSPOSE: mat4.create(), - WORLDVIEWINVERSETRANSPOSE: mat4.create(), - VIEWPROJECTIONINVERSETRANSPOSE: mat4.create(), - WORLDVIEWPROJECTIONINVERSETRANSPOSE: mat4.create() - }; - - Renderer.COLOR_BUFFER_BIT = glenum.COLOR_BUFFER_BIT; - Renderer.DEPTH_BUFFER_BIT = glenum.DEPTH_BUFFER_BIT; - Renderer.STENCIL_BUFFER_BIT = glenum.STENCIL_BUFFER_BIT; - - return Renderer; -}); -define('qtek/Scene',['require','./Node','./Light'],function(require) { - - - - var Node = require('./Node'); - var Light = require('./Light'); - - /** - * @constructor qtek.Scene - * @extends qtek.Node - */ - var Scene = Node.derive(function() { - return /** @lends qtek.Scene# */ { - /** - * Global material of scene - * @type {Material} - */ - material: null, - - /** - * @type {boolean} - */ - autoUpdate: true, - - /** - * Opaque renderable list, it will be updated automatically - * @type {Renderable[]} - * @readonly - */ - opaqueQueue: [], - - /** - * Opaque renderable list, it will be updated automatically - * @type {Renderable[]} - * @readonly - */ - transparentQueue: [], - - lights: [], - - // Properties to save the light information in the scene - // Will be set in the render function - _lightUniforms: {}, - - _lightNumber: { - 'POINT_LIGHT': 0, - 'DIRECTIONAL_LIGHT': 0, - 'SPOT_LIGHT': 0, - 'AMBIENT_LIGHT': 0 - }, - - _opaqueObjectCount: 0, - _transparentObjectCount: 0, - - _nodeRepository: {} - }; - }, function() { - this._scene = this; - }, - /** @lends qtek.Scene.prototype. */ - { - /** - * Add node to scene - * @param {Node} node - */ - addToScene: function(node) { - if (node.name) { - this._nodeRepository[node.name] = node; - } - }, - - /** - * Remove node from scene - * @param {Node} node - */ - removeFromScene: function(node) { - if (node.name) { - delete this._nodeRepository[node.name]; - } - }, - - /** - * Get node by name - * @param {string} name - * @return {Node} - * @DEPRECATED - */ - getNode: function(name) { - return this._nodeRepository[name]; - }, - - /** - * Clone a new scene node recursively, including material, skeleton. - * Shader and geometry instances will not been cloned - * @param {qtek.Node} node - * @return {qtek.Node} - */ - cloneNode: function (node) { - var newNode = node.clone(); - var materialsMap = {}; - - var cloneSkeleton = function (current, currentNew) { - if (current.skeleton) { - currentNew.skeleton = current.skeleton.clone(node, newNode); - currentNew.joints = current.joints.slice(); - } - if (current.material) { - materialsMap[current.material.__GUID__] = { - oldMat: current.material - }; - } - for (var i = 0; i < current._children.length; i++) { - cloneSkeleton(current._children[i], currentNew._children[i]); - } - }; - - cloneSkeleton(node, newNode); - - for (var guid in materialsMap) { - materialsMap[guid].newMat = materialsMap[guid].oldMat.clone(); - } - - // Replace material - newNode.traverse(function (current) { - if (current.material) { - current.material = materialsMap[current.material.__GUID__].newMat; - } - }); - - return newNode; - }, - - - /** - * Scene update - * @param {boolean} force - * @param {boolean} notUpdateLights - * Useful in deferred pipeline - */ - update: function(force, notUpdateLights) { - if (!(this.autoUpdate || force)) { - return; - } - Node.prototype.update.call(this, force); - - var lights = this.lights; - var sceneMaterialTransparent = this.material && this.material.transparent; - - this._opaqueObjectCount = 0; - this._transparentObjectCount = 0; - - lights.length = 0; - - this._updateRenderQueue(this, sceneMaterialTransparent); - - this.opaqueQueue.length = this._opaqueObjectCount; - this.transparentQueue.length = this._transparentObjectCount; - - // reset - if (!notUpdateLights) { - for (var type in this._lightNumber) { - this._lightNumber[type] = 0; - } - for (var i = 0; i < lights.length; i++) { - var light = lights[i]; - this._lightNumber[light.type]++; - } - this._updateLightUniforms(); - } - }, - - // Traverse the scene and add the renderable - // object to the render queue - _updateRenderQueue: function(parent, sceneMaterialTransparent) { - if (!parent.visible) { - return; - } - - for (var i = 0; i < parent._children.length; i++) { - var child = parent._children[i]; - - if (child instanceof Light) { - this.lights.push(child); - } - if (child.isRenderable()) { - if (child.material.transparent || sceneMaterialTransparent) { - this.transparentQueue[this._transparentObjectCount++] = child; - } else { - this.opaqueQueue[this._opaqueObjectCount++] = child; - } - } - if (child._children.length > 0) { - this._updateRenderQueue(child); - } - } - }, - - _updateLightUniforms: function() { - var lights = this.lights; - // Put the light cast shadow before the light not cast shadow - lights.sort(lightSortFunc); - - var lightUniforms = this._lightUniforms; - for (var symbol in lightUniforms) { - lightUniforms[symbol].value.length = 0; - } - for (var i = 0; i < lights.length; i++) { - - var light = lights[i]; - - for (symbol in light.uniformTemplates) { - - var uniformTpl = light.uniformTemplates[symbol]; - if (! lightUniforms[symbol]) { - lightUniforms[symbol] = { - type: '', - value: [] - }; - } - var value = uniformTpl.value(light); - var lu = lightUniforms[symbol]; - lu.type = uniformTpl.type + 'v'; - switch (uniformTpl.type) { - case '1i': - case '1f': - lu.value.push(value); - break; - case '2f': - case '3f': - case '4f': - for (var j =0; j < value.length; j++) { - lu.value.push(value[j]); - } - break; - default: - console.error('Unkown light uniform type '+uniformTpl.type); - } - } - } - }, - - isShaderLightNumberChanged: function(shader) { - return shader.lightNumber.POINT_LIGHT !== this._lightNumber.POINT_LIGHT - || shader.lightNumber.DIRECTIONAL_LIGHT !== this._lightNumber.DIRECTIONAL_LIGHT - || shader.lightNumber.SPOT_LIGHT !== this._lightNumber.SPOT_LIGHT - || shader.lightNumber.AMBIENT_LIGHT !== this._lightNumber.AMBIENT_LIGHT; - }, - - setShaderLightNumber: function(shader) { - for (var type in this._lightNumber) { - shader.lightNumber[type] = this._lightNumber[type]; - } - shader.dirty(); - }, - - setLightUniforms: function(shader, _gl) { - for (var symbol in this._lightUniforms) { - var lu = this._lightUniforms[symbol]; - shader.setUniform(_gl, lu.type, symbol, lu.value); - } - }, - - /** - * Dispose self, clear all the scene objects - * But resources of gl like texuture, shader will not be disposed. - * Mostly you should use disposeScene method in Renderer to do dispose. - */ - dispose: function() { - this.material = null; - this.opaqueQueue = []; - this.transparentQueue = []; - - this.lights = []; - - this._lightUniforms = {}; - - this._lightNumber = {}; - this._nodeRepository = {}; - } - }); - - function lightSortFunc(a, b) { - if (b.castShadow && !a.castShadow) { - return true; - } - } - - return Scene; -}); -define('qtek/Skeleton',['require','./core/Base','./Joint','./dep/glmatrix'],function(require) { - - - - var Base = require('./core/Base'); - var Joint = require('./Joint'); - - var glMatrix = require('./dep/glmatrix'); - var quat = glMatrix.quat; - var vec3 = glMatrix.vec3; - var mat4 = glMatrix.mat4; - - /** - * @constructor qtek.Skeleton - */ - var Skeleton = Base.derive(function() { - return /** @lends qtek.Skeleton# */{ - /** - * @type {string} - */ - name: '', - - /** - * root joints - * @type {Array.} - */ - roots: [], - - /** - * joints - * @type {Array.} - */ - joints: [], - - _clips: [], - - // Matrix to joint space (relative to root joint) - _invBindPoseMatricesArray: null, - - // Use subarray instead of copy back each time computing matrix - // http://jsperf.com/subarray-vs-copy-for-array-transform/5 - _jointMatricesSubArrays: [], - - // jointMatrix * currentPoseMatrix - // worldTransform is relative to the root bone - // still in model space not world space - _skinMatricesArray: null, - - _skinMatricesSubArrays: [], - - _subSkinMatricesArray: {} - }; - }, - /** @lends qtek.Skeleton.prototype */ - { - /** - * Update joints hierarchy - */ - updateHierarchy: function() { - this.roots = []; - var joints = this.joints; - for (var i = 0; i < joints.length; i++) { - var joint = joints[i]; - if (joint.parentIndex >= 0) { - var parent = joints[joint.parentIndex].node; - parent.add(joint.node); - }else{ - this.roots.push(joint); - } - } - }, - - /** - * Add a skinning clip and create a map between clip and skeleton - * @param {qtek.animation.SkinningClip} clip - * @param {Object} [mapRule] Map between joint name in skeleton and joint name in clip - */ - addClip: function(clip, mapRule) { - // Clip have been exists in - for (var i = 0; i < this._clips.length; i++) { - if (this._clips[i].clip === clip) { - return; - } - } - // Map the joint index in skeleton to joint pose index in clip - var maps = []; - for (var i = 0; i < this.joints.length; i++) { - maps[i] = -1; - } - // Create avatar - for (var i = 0; i < clip.jointClips.length; i++) { - for (var j = 0; j < this.joints.length; j++) { - var joint = this.joints[j]; - var jointPose = clip.jointClips[i]; - var jointName = joint.name; - if (mapRule) { - jointName = mapRule[jointName]; - } - if (jointPose.name === jointName) { - maps[j] = i; - break; - } - } - } - - this._clips.push({ - maps: maps, - clip: clip - }); - - return this._clips.length - 1; - }, - - /** - * @param {qtek.animation.SkinningClip} clip - */ - removeClip: function(clip) { - var idx = -1; - for (var i = 0; i < this._clips.length; i++) { - if (this._clips[i].clip === clip) { - idx = i; - break; - } - } - if (idx > 0) { - this._clips.splice(idx, 1); - } - }, - /** - * Remove all clips - */ - removeClipsAll: function() { - this._clips = []; - }, - - /** - * Get clip by index - * @param {number} index - */ - getClip: function(index) { - if (this._clips[index]) { - return this._clips[index].clip; - } - }, - - /** - * @return {number} - */ - getClipNumber: function() { - return this._clips.length; - }, - - /** - * Calculate joint matrices from node transform - * @method - */ - updateJointMatrices: (function() { - - var m4 = mat4.create(); - - return function() { - for (var i = 0; i < this.roots.length; i++) { - this.roots[i].node.update(true); - } - this._invBindPoseMatricesArray = new Float32Array(this.joints.length * 16); - this._skinMatricesArray = new Float32Array(this.joints.length * 16); - - for (var i = 0; i < this.joints.length; i++) { - var joint = this.joints[i]; - // Joint space is relative to root joint's parent, if have - // !!Parent node and joint node must all be updated - if (joint.rootNode && joint.rootNode.getParent()) { - mat4.invert(m4, joint.rootNode.getParent().worldTransform._array); - mat4.multiply( - m4, - m4, - joint.node.worldTransform._array - ); - mat4.invert(m4, m4); - } else { - mat4.copy(m4, joint.node.worldTransform._array); - mat4.invert(m4, m4); - } - - var offset = i * 16; - for (var j = 0; j < 16; j++) { - this._invBindPoseMatricesArray[offset + j] = m4[j]; - } - } - - this.updateMatricesSubArrays(); - }; - })(), - - updateMatricesSubArrays: function() { - for (var i = 0; i < this.joints.length; i++) { - this._jointMatricesSubArrays[i] = this._invBindPoseMatricesArray.subarray(i * 16, (i+1) * 16); - this._skinMatricesSubArrays[i] = this._skinMatricesArray.subarray(i * 16, (i+1) * 16); - } - }, - - /** - * Update skinning matrices - */ - update: (function() { - var m4 = mat4.create(); - return function() { - for (var i = 0; i < this.roots.length; i++) { - this.roots[i].node.update(true); - } - - for (var i = 0; i < this.joints.length; i++) { - var joint = this.joints[i]; - mat4.multiply( - this._skinMatricesSubArrays[i], - joint.node.worldTransform._array, - this._jointMatricesSubArrays[i] - ); - - // Joint space is relative to root joint's parent, if have - // PENDING - if (joint.rootNode && joint.rootNode.getParent()) { - mat4.invert(m4, joint.rootNode.getParent().worldTransform._array); - mat4.multiply( - this._skinMatricesSubArrays[i], - m4, - this._skinMatricesSubArrays[i] - ); - } - } - }; - })(), - - getSubSkinMatrices: function(meshId, joints) { - var subArray = this._subSkinMatricesArray[meshId]; - if (!subArray) { - subArray - = this._subSkinMatricesArray[meshId] - = new Float32Array(joints.length * 16); - } - var cursor = 0; - for (var i = 0; i < joints.length; i++) { - var idx = joints[i]; - for (var j = 0; j < 16; j++) { - subArray[cursor++] = this._skinMatricesArray[idx * 16 + j]; - } - } - return subArray; - }, - - /** - * Set pose and update skinning matrices - * @param {number} clipIndex - */ - setPose: function(clipIndex) { - if (!this._clips[clipIndex]) { - return; - } - - var clip = this._clips[clipIndex].clip; - var maps = this._clips[clipIndex].maps; - - for (var i = 0; i < this.joints.length; i++) { - var joint = this.joints[i]; - if (maps[i] === -1) { - continue; - } - var pose = clip.jointClips[maps[i]]; - - vec3.copy(joint.node.position._array, pose.position); - quat.copy(joint.node.rotation._array, pose.rotation); - vec3.copy(joint.node.scale._array, pose.scale); - - joint.node.position._dirty = true; - joint.node.rotation._dirty = true; - joint.node.scale._dirty = true; - } - this.update(); - }, - - clone: function (rootNode, newRootNode) { - var skeleton = new Skeleton(); - skeleton.name = this.name; - - for (var i = 0; i < this.joints.length; i++) { - var newJoint = new Joint(); - newJoint.name = this.joints[i].name; - newJoint.index = this.joints[i].index; - newJoint.parentIndex = this.joints[i].parentIndex; - - var path = this.joints[i].node.getPath(rootNode); - var rootNodePath = this.joints[i].rootNode.getPath(rootNode); - - if (path != null && rootNodePath != null) { - newJoint.node = newRootNode.queryNode(path); - newJoint.rootNode = newRootNode.queryNode(rootNodePath); - } else { - // PENDING - console.warn('Something wrong in clone, may be the skeleton root nodes is not mounted on the cloned root node.') - } - skeleton.joints.push(newJoint); - } - for (var i = 0; i < this.roots.length; i++) { - skeleton.roots.push(skeleton.joints[this.roots[i].index]); - } - - if (this._invBindPoseMatricesArray) { - var len = this._invBindPoseMatricesArray.length; - skeleton._invBindPoseMatricesArray = new Float32Array(len); - for (var i = 0; i < len; i++) { - skeleton._invBindPoseMatricesArray[i] = this._invBindPoseMatricesArray[i]; - } - - skeleton._skinMatricesArray = new Float32Array(len); - - skeleton.updateMatricesSubArrays(); - } - - skeleton.update(); - - return skeleton; - } - }); - - return Skeleton; -}); -/** - * StaticGeometry can not be changed once they've been setup - */ -define('qtek/StaticGeometry',['require','./Geometry','./math/BoundingBox','./dep/glmatrix','./core/glenum'],function(require) { - - - - var Geometry = require('./Geometry'); - var BoundingBox = require('./math/BoundingBox'); - var glMatrix = require('./dep/glmatrix'); - var glenum = require('./core/glenum'); - var mat4 = glMatrix.mat4; - var vec3 = glMatrix.vec3; - - /** - * @constructor qtek.StaticGeometry - * @extends qtek.Geometry - */ - var StaticGeometry = Geometry.derive(function() { - return /** @lends qtek.StaticGeometry# */ { - attributes: { - position: new Geometry.Attribute('position', 'float', 3, 'POSITION', false), - texcoord0: new Geometry.Attribute('texcoord0', 'float', 2, 'TEXCOORD_0', false), - texcoord1: new Geometry.Attribute('texcoord1', 'float', 2, 'TEXCOORD_1', false), - normal: new Geometry.Attribute('normal', 'float', 3, 'NORMAL', false), - tangent: new Geometry.Attribute('tangent', 'float', 4, 'TANGENT', false), - color: new Geometry.Attribute('color', 'float', 4, 'COLOR', false), - // Skinning attributes - // Each vertex can be bind to 4 bones, because the - // sum of weights is 1, so the weights is stored in vec3 and the last - // can be calculated by 1-w.x-w.y-w.z - weight: new Geometry.Attribute('weight', 'float', 3, 'WEIGHT', false), - joint: new Geometry.Attribute('joint', 'float', 4, 'JOINT', false), - // For wireframe display - // http://codeflow.org/entries/2012/aug/02/easy-wireframe-display-with-barycentric-coordinates/ - barycentric: new Geometry.Attribute('barycentric', 'float', 3, null, false), - }, - - hint: glenum.STATIC_DRAW, - - /** - * @type {Uint16Array} - */ - faces: null, - - _normalType: 'vertex', - - _enabledAttributes: null - }; - }, - /** @lends qtek.StaticGeometry.prototype */ - { - dirty: function() { - this._cache.dirtyAll(); - this._enabledAttributes = null; - }, - - getVertexNumber: function() { - var mainAttribute = this.attributes[this.mainAttribute]; - if (!mainAttribute || !mainAttribute.value) { - return 0; - } - return mainAttribute.value.length / mainAttribute.size; - }, - - getFaceNumber: function() { - if (!this.faces) { - return 0; - } else { - return this.faces.length / 3; - } - }, - - getFace: function (idx, out) { - if (idx < this.getFaceNumber() && idx >= 0) { - if (!out) { - out = vec3.create(); - } - out[0] = this.faces[idx * 3]; - out[1] = this.faces[idx * 3 + 1]; - out[2] = this.faces[idx * 3 + 2]; - return out; - } - }, - - isUseFace: function() { - return this.useFace && (this.faces != null); - }, - - createAttribute: function(name, type, size, semantic) { - var attrib = new Geometry.Attribute(name, type, size, semantic, false); - this.attributes[name] = attrib; - this._attributeList.push(name); - return attrib; - }, - - removeAttribute: function(name) { - var idx = this._attributeList.indexOf(name); - if (idx >= 0) { - this._attributeList.splice(idx, 1); - delete this.attributes[name]; - return true; - } - return false; - }, - - /** - * Get enabled attributes name list - * Attribute which has the same vertex number with position is treated as a enabled attribute - * @return {string[]} - */ - getEnabledAttributes: function() { - // Cache - if (this._enabledAttributes) { - return this._enabledAttributes; - } - - var result = []; - var nVertex = this.getVertexNumber(); - - for (var i = 0; i < this._attributeList.length; i++) { - var name = this._attributeList[i]; - var attrib = this.attributes[name]; - if (attrib.value) { - if (attrib.value.length === nVertex * attrib.size) { - result.push(name); - } - } - } - - this._enabledAttributes = result; - - return result; - }, - - getBufferChunks: function(_gl) { - this._cache.use(_gl.__GLID__); - if (this._cache.isDirty()) { - this._updateBuffer(_gl); - this._cache.fresh(); - } - return this._cache.get('chunks'); - }, - - _updateBuffer: function(_gl) { - var chunks = this._cache.get('chunks'); - var firstUpdate = false; - if (! chunks) { - chunks = []; - // Intialize - chunks[0] = { - attributeBuffers: [], - indicesBuffer: null - }; - this._cache.put('chunks', chunks); - firstUpdate = true; - } - var chunk = chunks[0]; - var attributeBuffers = chunk.attributeBuffers; - var indicesBuffer = chunk.indicesBuffer; - - var attributeList = this.getEnabledAttributes(); - var prevSearchIdx = 0; - var count = 0; - for (var k = 0; k < attributeList.length; k++) { - var name = attributeList[k]; - var attribute = this.attributes[name]; - - var bufferInfo; - - if (!firstUpdate) { - // Search for created buffer - for (var i = prevSearchIdx; i < attributeBuffers.length; i++) { - if (attributeBuffers[i].name === name) { - bufferInfo = attributeBuffers[i]; - prevSearchIdx = i + 1; - break; - } - } - if (!bufferInfo) { - for (var i = prevSearchIdx - 1; i >= 0; i--) { - if (attributeBuffers[i].name === name) { - bufferInfo = attributeBuffers[i]; - prevSearchIdx = i; - break; - } - } - } - } - var buffer; - if (bufferInfo) { - buffer = bufferInfo.buffer; - } else { - buffer = _gl.createBuffer(); - } - //TODO: Use BufferSubData? - _gl.bindBuffer(_gl.ARRAY_BUFFER, buffer); - _gl.bufferData(_gl.ARRAY_BUFFER, attribute.value, this.hint); - - attributeBuffers[count++] = new Geometry.AttributeBuffer(name, attribute.type, buffer, attribute.size, attribute.semantic); - } - attributeBuffers.length = count; - - if (this.isUseFace()) { - if (!indicesBuffer) { - indicesBuffer = new Geometry.IndicesBuffer(_gl.createBuffer()); - chunk.indicesBuffer = indicesBuffer; - } - indicesBuffer.count = this.faces.length; - _gl.bindBuffer(_gl.ELEMENT_ARRAY_BUFFER, indicesBuffer.buffer); - _gl.bufferData(_gl.ELEMENT_ARRAY_BUFFER, this.faces, this.hint); - } - }, - - generateVertexNormals: function() { - var faces = this.faces; - var positions = this.attributes.position.value; - var normals = this.attributes.normal.value; - - if (!normals || normals.length !== positions.length) { - normals = this.attributes.normal.value = new Float32Array(positions.length); - } else { - // Reset - for (var i = 0; i < normals.length; i++) { - normals[i] = 0; - } - } - - var p1 = vec3.create(); - var p2 = vec3.create(); - var p3 = vec3.create(); - - var v21 = vec3.create(); - var v32 = vec3.create(); - - var n = vec3.create(); - - for (var f = 0; f < faces.length;) { - var i1 = faces[f++]; - var i2 = faces[f++]; - var i3 = faces[f++]; - - vec3.set(p1, positions[i1*3], positions[i1*3+1], positions[i1*3+2]); - vec3.set(p2, positions[i2*3], positions[i2*3+1], positions[i2*3+2]); - vec3.set(p3, positions[i3*3], positions[i3*3+1], positions[i3*3+2]); - - vec3.sub(v21, p1, p2); - vec3.sub(v32, p2, p3); - vec3.cross(n, v21, v32); - // Weighted by the triangle area - for (var i = 0; i < 3; i++) { - normals[i1*3+i] = normals[i1*3+i] + n[i]; - normals[i2*3+i] = normals[i2*3+i] + n[i]; - normals[i3*3+i] = normals[i3*3+i] + n[i]; - } - } - - for (var i = 0; i < normals.length;) { - vec3.set(n, normals[i], normals[i+1], normals[i+2]); - vec3.normalize(n, n); - normals[i++] = n[0]; - normals[i++] = n[1]; - normals[i++] = n[2]; - } - }, - - generateFaceNormals: function() { - if (!this.isUniqueVertex()) { - this.generateUniqueVertex(); - } - - var faces = this.faces; - var positions = this.attributes.position.value; - var normals = this.attributes.normal.value; - - var p1 = vec3.create(); - var p2 = vec3.create(); - var p3 = vec3.create(); - - var v21 = vec3.create(); - var v32 = vec3.create(); - var n = vec3.create(); - - if (!normals) { - normals = this.attributes.position.value = new Float32Array(positions.length); - } - for (var f = 0; f < faces.length;) { - var i1 = faces[f++]; - var i2 = faces[f++]; - var i3 = faces[f++]; - - vec3.set(p1, positions[i1*3], positions[i1*3+1], positions[i1*3+2]); - vec3.set(p2, positions[i2*3], positions[i2*3+1], positions[i2*3+2]); - vec3.set(p3, positions[i3*3], positions[i3*3+1], positions[i3*3+2]); - - vec3.sub(v21, p1, p2); - vec3.sub(v32, p2, p3); - vec3.cross(n, v21, v32); - - vec3.normalize(n, n); - - for (var i = 0; i < 3; i++) { - normals[i1*3+i] = n[i]; - normals[i2*3+i] = n[i]; - normals[i3*3+i] = n[i]; - } - } - }, - - generateTangents: function() { - var nVertex = this.getVertexNumber(); - if (!this.attributes.tangent.value) { - this.attributes.tangent.value = new Float32Array(nVertex * 4); - } - var texcoords = this.attributes.texcoord0.value; - var positions = this.attributes.position.value; - var tangents = this.attributes.tangent.value; - var normals = this.attributes.normal.value; - - var tan1 = []; - var tan2 = []; - for (var i = 0; i < nVertex; i++) { - tan1[i] = [0.0, 0.0, 0.0]; - tan2[i] = [0.0, 0.0, 0.0]; - } - - var sdir = [0.0, 0.0, 0.0]; - var tdir = [0.0, 0.0, 0.0]; - for (var i = 0; i < this.faces.length;) { - var i1 = this.faces[i++], - i2 = this.faces[i++], - i3 = this.faces[i++], - - st1s = texcoords[i1 * 2], - st2s = texcoords[i2 * 2], - st3s = texcoords[i3 * 2], - st1t = texcoords[i1 * 2 + 1], - st2t = texcoords[i2 * 2 + 1], - st3t = texcoords[i3 * 2 + 1], - - p1x = positions[i1 * 3], - p2x = positions[i2 * 3], - p3x = positions[i3 * 3], - p1y = positions[i1 * 3 + 1], - p2y = positions[i2 * 3 + 1], - p3y = positions[i3 * 3 + 1], - p1z = positions[i1 * 3 + 2], - p2z = positions[i2 * 3 + 2], - p3z = positions[i3 * 3 + 2]; - - var x1 = p2x - p1x, - x2 = p3x - p1x, - y1 = p2y - p1y, - y2 = p3y - p1y, - z1 = p2z - p1z, - z2 = p3z - p1z; - - var s1 = st2s - st1s, - s2 = st3s - st1s, - t1 = st2t - st1t, - t2 = st3t - st1t; - - var r = 1.0 / (s1 * t2 - t1 * s2); - sdir[0] = (t2 * x1 - t1 * x2) * r; - sdir[1] = (t2 * y1 - t1 * y2) * r; - sdir[2] = (t2 * z1 - t1 * z2) * r; - - tdir[0] = (s1 * x2 - s2 * x1) * r; - tdir[1] = (s1 * y2 - s2 * y1) * r; - tdir[2] = (s1 * z2 - s2 * z1) * r; - - vec3.add(tan1[i1], tan1[i1], sdir); - vec3.add(tan1[i2], tan1[i2], sdir); - vec3.add(tan1[i3], tan1[i3], sdir); - vec3.add(tan2[i1], tan2[i1], tdir); - vec3.add(tan2[i2], tan2[i2], tdir); - vec3.add(tan2[i3], tan2[i3], tdir); - } - var tmp = vec3.create(); - var nCrossT = vec3.create(); - var n = vec3.create(); - for (var i = 0; i < nVertex; i++) { - n[0] = normals[i * 3]; - n[1] = normals[i * 3 + 1]; - n[2] = normals[i * 3 + 2]; - var t = tan1[i]; - - // Gram-Schmidt orthogonalize - vec3.scale(tmp, n, vec3.dot(n, t)); - vec3.sub(tmp, t, tmp); - vec3.normalize(tmp, tmp); - // Calculate handedness. - vec3.cross(nCrossT, n, t); - tangents[i * 4] = tmp[0]; - tangents[i * 4 + 1] = tmp[1]; - tangents[i * 4 + 2] = tmp[2]; - tangents[i * 4 + 3] = vec3.dot(nCrossT, tan2[i]) < 0.0 ? -1.0 : 1.0; - } - }, - - isUniqueVertex: function() { - if (this.isUseFace()) { - return this.getVertexNumber() === this.faces.length; - } else { - return true; - } - }, - - generateUniqueVertex: function() { - var vertexUseCount = []; - - for (var i = 0, len = this.getVertexNumber(); i < len; i++) { - vertexUseCount[i] = 0; - } - - var cursor = this.getVertexNumber(); - var attributes = this.attributes; - var faces = this.faces; - - var attributeNameList = this.getEnabledAttributes(); - - for (var a = 0; a < attributeNameList.length; a++) { - var name = attributeNameList[a]; - var expandedArray = new Float32Array(this.faces.length * attributes[name].size); - var len = attributes[name].value.length; - for (var i = 0; i < len; i++) { - expandedArray[i] = attributes[name].value[i]; - } - attributes[name].value = expandedArray; - } - - for (var i = 0; i < faces.length; i++) { - var ii = faces[i]; - if (vertexUseCount[ii] > 0) { - for (var a = 0; a < attributeNameList.length; a++) { - var name = attributeNameList[a]; - var array = attributes[name].value; - var size = attributes[name].size; - - for (var k = 0; k < size; k++) { - array[cursor * size + k] = array[ii * size + k]; - } - } - faces[i] = cursor; - cursor++; - } - vertexUseCount[ii]++; - } - }, - - generateBarycentric: function() { - - if (! this.isUniqueVertex()) { - this.generateUniqueVertex(); - } - - var array = this.attributes.barycentric.value; - // Already existed; - if (array && array.length === this.faces.length * 3) { - return; - } - array = this.attributes.barycentric.value = new Float32Array(this.faces.length * 3); - for (var i = 0; i < this.faces.length;) { - for (var j = 0; j < 3; j++) { - var ii = this.faces[i++]; - array[ii + j] = 1; - } - } - }, - - convertToDynamic: function(geometry) { - for (var i = 0; i < this.faces.length; i+=3) { - geometry.faces.push(this.face.subarray(i, i + 3)); - } - - var attributes = this.getEnabledAttributes(); - for (var name in attributes) { - var attrib = attributes[name]; - var geoAttrib = geometry.attributes[name]; - if (!geoAttrib) { - geoAttrib = geometry.attributes[name] = { - type: attrib.type, - size: attrib.size, - value: [] - }; - if (attrib.semantic) { - geoAttrib.semantic = attrib.semantic; - } - } - for (var i = 0; i < attrib.value.length; i+= attrib.size) { - if (attrib.size === 1) { - geoAttrib.value.push(attrib.array[i]); - } else { - geoAttrib.value.push(attrib.subarray(i, i + attrib.size)); - } - } - } - - if (this.boundingBox) { - geometry.boundingBox = new BoundingBox(); - geometry.boundingBox.min.copy(this.boundingBox.min); - geometry.boundingBox.max.copy(this.boundingBox.max); - } - // PENDING copy buffer ? - - return geometry; - }, - - applyTransform: function(matrix) { - - var positions = this.attributes.position.value; - var normals = this.attributes.normal.value; - var tangents = this.attributes.tangent.value; - - matrix = matrix._array; - // Normal Matrix - var inverseTransposeMatrix = mat4.create(); - mat4.invert(inverseTransposeMatrix, matrix); - mat4.transpose(inverseTransposeMatrix, inverseTransposeMatrix); - - vec3.forEach(positions, 3, 0, null, vec3.transformMat4, matrix); - if (normals) { - vec3.forEach(normals, 3, 0, null, vec3.transformMat4, inverseTransposeMatrix); - } - if (tangents) { - vec3.forEach(tangents, 4, 0, null, vec3.transformMat4, inverseTransposeMatrix); - } - - if (this.boundingBox) { - this.updateBoundingBox(); - } - }, - - dispose: function(_gl) { - this._cache.use(_gl.__GLID__); - var chunks = this._cache.get('chunks'); - if (chunks) { - for (var c = 0; c < chunks.length; c++) { - var chunk = chunks[c]; - - for (var k = 0; k < chunk.attributeBuffers.length; k++) { - var attribs = chunk.attributeBuffers[k]; - _gl.deleteBuffer(attribs.buffer); - } - } - } - this._cache.deleteContext(_gl.__GLID__); - } - }); - - return StaticGeometry; -}); -define('qtek/Texture2D',['require','./Texture','./core/glinfo','./core/glenum'],function(require) { - - var Texture = require('./Texture'); - var glinfo = require('./core/glinfo'); - var glenum = require('./core/glenum'); - - /** - * @constructor qtek.Texture2D - * @extends qtek.Texture - * - * @example - * ... - * var mat = new qtek.Material({ - * shader: qtek.shader.library.get('buildin.phong', 'diffuseMap') - * }); - * var diffuseMap = new qtek.Texture2D(); - * diffuseMap.load('assets/textures/diffuse.jpg'); - * mat.set('diffuseMap', diffuseMap); - * ... - * diffuseMap.success(function() { - * // Wait for the diffuse texture loaded - * animation.on('frame', function(frameTime) { - * renderer.render(scene, camera); - * }); - * }); - */ - var Texture2D = Texture.derive(function() { - return /** @lends qtek.Texture2D# */ { - /** - * @type {HTMLImageElement|HTMLCanvasElemnet} - */ - image: null, - /** - * @type {Uint8Array|Float32Array} - */ - pixels: null, - /** - * @type {Array.} - * @example - * [{ - * image: mipmap0, - * pixels: null - * }, { - * image: mipmap1, - * pixels: null - * }, ....] - */ - mipmaps: [] - }; - }, { - update: function(_gl) { - - _gl.bindTexture(_gl.TEXTURE_2D, this._cache.get('webgl_texture')); - - this.beforeUpdate( _gl); - - var glFormat = this.format; - var glType = this.type; - - _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, this.wrapS); - _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, this.wrapT); - - _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, this.magFilter); - _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, this.minFilter); - - var anisotropicExt = glinfo.getExtension(_gl, 'EXT_texture_filter_anisotropic'); - if (anisotropicExt && this.anisotropic > 1) { - _gl.texParameterf(_gl.TEXTURE_2D, anisotropicExt.TEXTURE_MAX_ANISOTROPY_EXT, this.anisotropic); - } - - // Fallback to float type if browser don't have half float extension - if (glType === 36193) { - var halfFloatExt = glinfo.getExtension(_gl, 'OES_texture_half_float'); - if (!halfFloatExt) { - glType = glenum.FLOAT; - } - } - - if (this.mipmaps.length) { - var width = this.width; - var height = this.height; - for (var i = 0; i < this.mipmaps.length; i++) { - var mipmap = this.mipmaps[i]; - this._updateTextureData(_gl, mipmap, i, width, height, glFormat, glType); - width /= 2; - height /= 2; - } - } - else { - this._updateTextureData(_gl, this, 0, this.width, this.height, glFormat, glType); - - if (this.useMipmap && !this.NPOT) { - _gl.generateMipmap(_gl.TEXTURE_2D); - } - } - - _gl.bindTexture(_gl.TEXTURE_2D, null); - }, - - _updateTextureData: function (_gl, data, level, width, height, glFormat, glType) { - if (data.image) { - _gl.texImage2D(_gl.TEXTURE_2D, level, glFormat, glFormat, glType, data.image); - } - else { - // Can be used as a blank texture when writing render to texture(RTT) - if ( - glFormat <= Texture.COMPRESSED_RGBA_S3TC_DXT5_EXT - && glFormat >= Texture.COMPRESSED_RGB_S3TC_DXT1_EXT - ) { - _gl.compressedTexImage2D(_gl.TEXTURE_2D, level, glFormat, width, height, 0, data.pixels); - } else { - _gl.texImage2D(_gl.TEXTURE_2D, level, glFormat, width, height, 0, glFormat, glType, data.pixels); - } - } - }, - - /** - * @param {WebGLRenderingContext} _gl - * @memberOf qtek.Texture2D.prototype - */ - generateMipmap: function(_gl) { - if (this.useMipmap && !this.NPOT) { - _gl.bindTexture(_gl.TEXTURE_2D, this._cache.get('webgl_texture')); - _gl.generateMipmap(_gl.TEXTURE_2D); - } - }, - - isPowerOfTwo: function() { - var width; - var height; - if (this.image) { - width = this.image.width; - height = this.image.height; - } else { - width = this.width; - height = this.height; - } - return (width & (width-1)) === 0 - && (height & (height-1)) === 0; - }, - - isRenderable: function() { - if (this.image) { - return this.image.nodeName === 'CANVAS' - || this.image.complete; - } else { - return this.width && this.height; - } - }, - - bind: function(_gl) { - _gl.bindTexture(_gl.TEXTURE_2D, this.getWebGLTexture(_gl)); - }, - - unbind: function(_gl) { - _gl.bindTexture(_gl.TEXTURE_2D, null); - }, - - load: function(src) { - var image = new Image(); - var self = this; - image.onload = function() { - self.dirty(); - self.trigger('success', self); - image.onload = null; - }; - image.onerror = function() { - self.trigger('error', self); - image.onerror = null; - }; - - image.src = src; - this.image = image; - - return this; - } - }); - - return Texture2D; -}); -// 缓动函数来自 https://github.com/sole/tween.js/blob/master/src/Tween.js -define('qtek/animation/easing',[],function() { - - /** - * @namespace qtek.animation.easing - */ - var easing = { - /** - * @alias qtek.animation.easing.Linear - * @param {number} k - * @return {number} - */ - Linear: function(k) { - return k; - }, - /** - * @alias qtek.animation.easing.QuadraticIn - * @param {number} k - * @return {number} - */ - QuadraticIn: function(k) { - return k * k; - }, - /** - * @alias qtek.animation.easing.QuadraticOut - * @param {number} k - * @return {number} - */ - QuadraticOut: function(k) { - return k * (2 - k); - }, - /** - * @alias qtek.animation.easing.QuadraticInOut - * @param {number} k - * @return {number} - */ - QuadraticInOut: function(k) { - if ((k *= 2) < 1) { - return 0.5 * k * k; - } - return - 0.5 * (--k * (k - 2) - 1); - }, - /** - * @alias qtek.animation.easing.CubicIn - * @param {number} k - * @return {number} - */ - CubicIn: function(k) { - return k * k * k; - }, - /** - * @alias qtek.animation.easing.CubicOut - * @param {number} k - * @return {number} - */ - CubicOut: function(k) { - return --k * k * k + 1; - }, - /** - * @alias qtek.animation.easing.CubicInOut - * @param {number} k - * @return {number} - */ - CubicInOut: function(k) { - if ((k *= 2) < 1) { - return 0.5 * k * k * k; - } - return 0.5 * ((k -= 2) * k * k + 2); - }, - /** - * @alias qtek.animation.easing.QuarticIn - * @param {number} k - * @return {number} - */ - QuarticIn: function(k) { - return k * k * k * k; - }, - /** - * @alias qtek.animation.easing.QuarticOut - * @param {number} k - * @return {number} - */ - QuarticOut: function(k) { - return 1 - (--k * k * k * k); - }, - /** - * @alias qtek.animation.easing.QuarticInOut - * @param {number} k - * @return {number} - */ - QuarticInOut: function(k) { - if ((k *= 2) < 1) { - return 0.5 * k * k * k * k; - } - return - 0.5 * ((k -= 2) * k * k * k - 2); - }, - /** - * @alias qtek.animation.easing.QuinticIn - * @param {number} k - * @return {number} - */ - QuinticIn: function(k) { - return k * k * k * k * k; - }, - /** - * @alias qtek.animation.easing.QuinticOut - * @param {number} k - * @return {number} - */ - QuinticOut: function(k) { - return --k * k * k * k * k + 1; - }, - /** - * @alias qtek.animation.easing.QuinticInOut - * @param {number} k - * @return {number} - */ - QuinticInOut: function(k) { - if ((k *= 2) < 1) { - return 0.5 * k * k * k * k * k; - } - return 0.5 * ((k -= 2) * k * k * k * k + 2); - }, - /** - * @alias qtek.animation.easing.SinusoidalIn - * @param {number} k - * @return {number} - */ - SinusoidalIn: function(k) { - return 1 - Math.cos(k * Math.PI / 2); - }, - /** - * @alias qtek.animation.easing.SinusoidalOut - * @param {number} k - * @return {number} - */ - SinusoidalOut: function(k) { - return Math.sin(k * Math.PI / 2); - }, - /** - * @alias qtek.animation.easing.SinusoidalInOut - * @param {number} k - * @return {number} - */ - SinusoidalInOut: function(k) { - return 0.5 * (1 - Math.cos(Math.PI * k)); - }, - /** - * @alias qtek.animation.easing.ExponentialIn - * @param {number} k - * @return {number} - */ - ExponentialIn: function(k) { - return k === 0 ? 0 : Math.pow(1024, k - 1); - }, - /** - * @alias qtek.animation.easing.ExponentialOut - * @param {number} k - * @return {number} - */ - ExponentialOut: function(k) { - return k === 1 ? 1 : 1 - Math.pow(2, - 10 * k); - }, - /** - * @alias qtek.animation.easing.ExponentialInOut - * @param {number} k - * @return {number} - */ - ExponentialInOut: function(k) { - if (k === 0) { - return 0; - } - if (k === 1) { - return 1; - } - if ((k *= 2) < 1) { - return 0.5 * Math.pow(1024, k - 1); - } - return 0.5 * (- Math.pow(2, - 10 * (k - 1)) + 2); - }, - /** - * @alias qtek.animation.easing.CircularIn - * @param {number} k - * @return {number} - */ - CircularIn: function(k) { - return 1 - Math.sqrt(1 - k * k); - }, - /** - * @alias qtek.animation.easing.CircularOut - * @param {number} k - * @return {number} - */ - CircularOut: function(k) { - return Math.sqrt(1 - (--k * k)); - }, - /** - * @alias qtek.animation.easing.CircularInOut - * @param {number} k - * @return {number} - */ - CircularInOut: function(k) { - if ((k *= 2) < 1) { - return - 0.5 * (Math.sqrt(1 - k * k) - 1); - } - return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1); - }, - /** - * @alias qtek.animation.easing.ElasticIn - * @param {number} k - * @return {number} - */ - ElasticIn: function(k) { - var s, a = 0.1, p = 0.4; - if (k === 0) { - return 0; - } - if (k === 1) { - return 1; - } - if (!a || a < 1) { - a = 1; s = p / 4; - }else{ - s = p * Math.asin(1 / a) / (2 * Math.PI); - } - return - (a * Math.pow(2, 10 * (k -= 1)) * - Math.sin((k - s) * (2 * Math.PI) / p)); - }, - /** - * @alias qtek.animation.easing.ElasticOut - * @param {number} k - * @return {number} - */ - ElasticOut: function(k) { - var s, a = 0.1, p = 0.4; - if (k === 0) { - return 0; - } - if (k === 1) { - return 1; - } - if (!a || a < 1) { - a = 1; s = p / 4; - } - else{ - s = p * Math.asin(1 / a) / (2 * Math.PI); - } - return (a * Math.pow(2, - 10 * k) * - Math.sin((k - s) * (2 * Math.PI) / p) + 1); - }, - /** - * @alias qtek.animation.easing.ElasticInOut - * @param {number} k - * @return {number} - */ - ElasticInOut: function(k) { - var s, a = 0.1, p = 0.4; - if (k === 0) { - return 0; - } - if (k === 1) { - return 1; - } - if (!a || a < 1) { - a = 1; s = p / 4; - } - else{ - s = p * Math.asin(1 / a) / (2 * Math.PI); - } - if ((k *= 2) < 1) { - return - 0.5 * (a * Math.pow(2, 10 * (k -= 1)) - * Math.sin((k - s) * (2 * Math.PI) / p)); - } - return a * Math.pow(2, -10 * (k -= 1)) - * Math.sin((k - s) * (2 * Math.PI) / p) * 0.5 + 1; - - }, - /** - * @alias qtek.animation.easing.BackIn - * @param {number} k - * @return {number} - */ - BackIn: function(k) { - var s = 1.70158; - return k * k * ((s + 1) * k - s); - }, - /** - * @alias qtek.animation.easing.BackOut - * @param {number} k - * @return {number} - */ - BackOut: function(k) { - var s = 1.70158; - return --k * k * ((s + 1) * k + s) + 1; - }, - /** - * @alias qtek.animation.easing.BackInOut - * @param {number} k - * @return {number} - */ - BackInOut: function(k) { - var s = 1.70158 * 1.525; - if ((k *= 2) < 1) { - return 0.5 * (k * k * ((s + 1) * k - s)); - } - return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2); - }, - /** - * @alias qtek.animation.easing.BounceIn - * @param {number} k - * @return {number} - */ - BounceIn: function(k) { - return 1 - easing.BounceOut(1 - k); - }, - /** - * @alias qtek.animation.easing.BounceOut - * @param {number} k - * @return {number} - */ - BounceOut: function(k) { - if (k < (1 / 2.75)) { - return 7.5625 * k * k; - } - else if (k < (2 / 2.75)) { - return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75; - } else if (k < (2.5 / 2.75)) { - return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375; - } else { - return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375; - } - }, - /** - * @alias qtek.animation.easing.BounceInOut - * @param {number} k - * @return {number} - */ - BounceInOut: function(k) { - if (k < 0.5) { - return easing.BounceIn(k * 2) * 0.5; - } - return easing.BounceOut(k * 2 - 1) * 0.5 + 0.5; - } - }; - - return easing; -}); + function Handler(action, context) { + this.action = action; + this.context = context; + } + /** + * @mixin + * @alias qtek.core.mixin.notifier + */ + var notifier = { + /** + * Trigger event + * @param {string} name + */ + trigger: function(name) { + if (!this.hasOwnProperty('__handlers__')) { + return; + } + if (!this.__handlers__.hasOwnProperty(name)) { + return; + } + + var hdls = this.__handlers__[name]; + var l = hdls.length, i = -1, args = arguments; + // Optimize advise from backbone + switch (args.length) { + case 1: + while (++i < l) { + hdls[i].action.call(hdls[i].context); + } + return; + case 2: + while (++i < l) { + hdls[i].action.call(hdls[i].context, args[1]); + } + return; + case 3: + while (++i < l) { + hdls[i].action.call(hdls[i].context, args[1], args[2]); + } + return; + case 4: + while (++i < l) { + hdls[i].action.call(hdls[i].context, args[1], args[2], args[3]); + } + return; + case 5: + while (++i < l) { + hdls[i].action.call(hdls[i].context, args[1], args[2], args[3], args[4]); + } + return; + default: + while (++i < l) { + hdls[i].action.apply(hdls[i].context, Array.prototype.slice.call(args, 1)); + } + return; + } + }, + /** + * Register event handler + * @param {string} name + * @param {Function} action + * @param {Object} [context] + * @chainable + */ + on: function(name, action, context) { + if (!name || !action) { + return; + } + var handlers = this.__handlers__ || (this.__handlers__={}); + if (!handlers[name]) { + handlers[name] = []; + } + else { + if (this.has(name, action)) { + return; + } + } + var handler = new Handler(action, context || this); + handlers[name].push(handler); + + return this; + }, + + /** + * Register event, event will only be triggered once and then removed + * @param {string} name + * @param {Function} action + * @param {Object} [context] + * @chainable + */ + once: function(name, action, context) { + if (!name || !action) { + return; + } + var self = this; + function wrapper() { + self.off(name, wrapper); + action.apply(this, arguments); + } + return this.on(name, wrapper, context); + }, + + /** + * Alias of once('before' + name) + * @param {string} name + * @param {Function} action + * @param {Object} [context] + * @chainable + */ + before: function(name, action, context) { + if (!name || !action) { + return; + } + name = 'before' + name; + return this.on(name, action, context); + }, + + /** + * Alias of once('after' + name) + * @param {string} name + * @param {Function} action + * @param {Object} [context] + * @chainable + */ + after: function(name, action, context) { + if (!name || !action) { + return; + } + name = 'after' + name; + return this.on(name, action, context); + }, + + /** + * Alias of on('success') + * @param {Function} action + * @param {Object} [context] + * @chainable + */ + success: function(action, context) { + return this.once('success', action, context); + }, + + /** + * Alias of on('error') + * @param {Function} action + * @param {Object} [context] + * @chainable + */ + error: function(action, context) { + return this.once('error', action, context); + }, + + /** + * Alias of on('success') + * @param {Function} action + * @param {Object} [context] + * @chainable + */ + off: function(name, action) { + + var handlers = this.__handlers__ || (this.__handlers__={}); + + if (!action) { + handlers[name] = []; + return; + } + if (handlers[name]) { + var hdls = handlers[name]; + var retains = []; + for (var i = 0; i < hdls.length; i++) { + if (action && hdls[i].action !== action) { + retains.push(hdls[i]); + } + } + handlers[name] = retains; + } + + return this; + }, + + /** + * If registered the event handler + * @param {string} name + * @param {Function} action + * @return {boolean} + */ + has: function(name, action) { + var handlers = this.__handlers__; + + if (! handlers || + ! handlers[name]) { + return false; + } + var hdls = handlers[name]; + for (var i = 0; i < hdls.length; i++) { + if (hdls[i].action === action) { + return true; + } + } + } + }; + + module.exports = notifier; + + +/***/ }, +/* 6 */ +/***/ function(module, exports) { + + 'use strict'; + + + var guid = 0; + + var ArrayProto = Array.prototype; + var nativeForEach = ArrayProto.forEach; + + /** + * Util functions + * @namespace qtek.core.util + */ + var util = { + + /** + * Generate GUID + * @return {number} + * @memberOf qtek.core.util + */ + genGUID: function() { + return ++guid; + }, + /** + * Relative path to absolute path + * @param {string} path + * @param {string} basePath + * @return {string} + * @memberOf qtek.core.util + */ + relative2absolute: function(path, basePath) { + if (!basePath || path.match(/^\//)) { + return path; + } + var pathParts = path.split('/'); + var basePathParts = basePath.split('/'); + + var item = pathParts[0]; + while(item === '.' || item === '..') { + if (item === '..') { + basePathParts.pop(); + } + pathParts.shift(); + item = pathParts[0]; + } + return basePathParts.join('/') + '/' + pathParts.join('/'); + }, + + /** + * Extend target with source + * @param {Object} target + * @param {Object} source + * @return {Object} + * @memberOf qtek.core.util + */ + extend: function(target, source) { + if (source) { + for (var name in source) { + if (source.hasOwnProperty(name)) { + target[name] = source[name]; + } + } + } + return target; + }, + + /** + * Extend properties to target if not exist. + * @param {Object} target + * @param {Object} source + * @return {Object} + * @memberOf qtek.core.util + */ + defaults: function(target, source) { + if (source) { + for (var propName in source) { + if (target[propName] === undefined) { + target[propName] = source[propName]; + } + } + } + return target; + }, + /** + * Extend properties with a given property list to avoid for..in.. iteration. + * @param {Object} target + * @param {Object} source + * @param {Array.} propList + * @return {Object} + * @memberOf qtek.core.util + */ + extendWithPropList: function(target, source, propList) { + if (source) { + for (var i = 0; i < propList.length; i++) { + var propName = propList[i]; + target[propName] = source[propName]; + } + } + return target; + }, + /** + * Extend properties to target if not exist. With a given property list avoid for..in.. iteration. + * @param {Object} target + * @param {Object} source + * @param {Array.} propList + * @return {Object} + * @memberOf qtek.core.util + */ + defaultsWithPropList: function(target, source, propList) { + if (source) { + for (var i = 0; i < propList.length; i++) { + var propName = propList[i]; + if (target[propName] == null) { + target[propName] = source[propName]; + } + } + } + return target; + }, + /** + * @param {Object|Array} obj + * @param {Function} iterator + * @param {Object} [context] + * @memberOf qtek.core.util + */ + each: function(obj, iterator, context) { + if (!(obj && iterator)) { + return; + } + if (obj.forEach && obj.forEach === nativeForEach) { + obj.forEach(iterator, context); + } else if (obj.length === + obj.length) { + for (var i = 0, len = obj.length; i < len; i++) { + iterator.call(context, obj[i], i, obj); + } + } else { + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + iterator.call(context, obj[key], key, obj); + } + } + } + }, + + /** + * Is object ? + * @param {} obj + * @return {boolean} + * @memberOf qtek.core.util + */ + isObject: function(obj) { + return obj === Object(obj); + }, + + /** + * Is array ? + * @param {} obj + * @return {boolean} + * @memberOf qtek.core.util + */ + isArray: function(obj) { + return obj instanceof Array; + }, + + /** + * Is array like, which have a length property + * @param {} obj + * @return {boolean} + * @memberOf qtek.core.util + */ + isArrayLike: function(obj) { + if (!obj) { + return false; + } else { + return obj.length === + obj.length; + } + }, + + /** + * @param {} obj + * @return {} + * @memberOf qtek.core.util + */ + clone: function(obj) { + if (!util.isObject(obj)) { + return obj; + } else if (util.isArray(obj)) { + return obj.slice(); + } else if (util.isArrayLike(obj)) { // is typed array + var ret = new obj.constructor(obj.length); + for (var i = 0; i < obj.length; i++) { + ret[i] = obj[i]; + } + return ret; + } else { + return util.extend({}, obj); + } + } + }; + + module.exports = util; + + +/***/ }, +/* 7 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/animation/Animator + */ + + + var Clip = __webpack_require__(8); + + var arraySlice = Array.prototype.slice; + + function defaultGetter(target, key) { + return target[key]; + } + function defaultSetter(target, key, value) { + target[key] = value; + } + + function interpolateNumber(p0, p1, percent) { + return (p1 - p0) * percent + p0; + } + + function interpolateArray(p0, p1, percent, out, arrDim) { + var len = p0.length; + if (arrDim == 1) { + for (var i = 0; i < len; i++) { + out[i] = interpolateNumber(p0[i], p1[i], percent); + } + } else { + var len2 = p0[0].length; + for (var i = 0; i < len; i++) { + for (var j = 0; j < len2; j++) { + out[i][j] = interpolateNumber( + p0[i][j], p1[i][j], percent + ); + } + } + } + } + + function isArrayLike(data) { + if (typeof(data) == 'undefined') { + return false; + } else if (typeof(data) == 'string') { + return false; + } else { + return typeof(data.length) == 'number'; + } + } + + function cloneValue(value) { + if (isArrayLike(value)) { + var len = value.length; + if (isArrayLike(value[0])) { + var ret = []; + for (var i = 0; i < len; i++) { + ret.push(arraySlice.call(value[i])); + } + return ret; + } else { + return arraySlice.call(value); + } + } else { + return value; + } + } + + function catmullRomInterpolateArray( + p0, p1, p2, p3, t, t2, t3, out, arrDim + ) { + var len = p0.length; + if (arrDim == 1) { + for (var i = 0; i < len; i++) { + out[i] = catmullRomInterpolate( + p0[i], p1[i], p2[i], p3[i], t, t2, t3 + ); + } + } else { + var len2 = p0[0].length; + for (var i = 0; i < len; i++) { + for (var j = 0; j < len2; j++) { + out[i][j] = catmullRomInterpolate( + p0[i][j], p1[i][j], p2[i][j], p3[i][j], + t, t2, t3 + ); + } + } + } + } + + function catmullRomInterpolate(p0, p1, p2, p3, t, t2, t3) { + var v0 = (p2 - p0) * 0.5; + var v1 = (p3 - p1) * 0.5; + return (2 * (p1 - p2) + v0 + v1) * t3 + + (- 3 * (p1 - p2) - 2 * v0 - v1) * t2 + + v0 * t + p1; + } + + // arr0 is source array, arr1 is target array. + // Do some preprocess to avoid error happened when interpolating from arr0 to arr1 + function fillArr(arr0, arr1, arrDim) { + var arr0Len = arr0.length; + var arr1Len = arr1.length; + if (arr0Len !== arr1Len) { + // FIXME Not work for TypedArray + var isPreviousLarger = arr0Len > arr1Len; + if (isPreviousLarger) { + // Cut the previous + arr0.length = arr1Len; + } + else { + // Fill the previous + for (var i = arr0Len; i < arr1Len; i++) { + arr0.push( + arrDim === 1 ? arr1[i] : arraySlice.call(arr1[i]) + ); + } + } + } + // Handling NaN value + var len2 = arr0[0] && arr0[0].length; + for (var i = 0; i < arr0.length; i++) { + if (arrDim === 1) { + if (isNaN(arr0[i])) { + arr0[i] = arr1[i]; + } + } + else { + for (var j = 0; j < len2; j++) { + if (isNaN(arr0[i][j])) { + arr0[i][j] = arr1[i][j]; + } + } + } + } + } + + /** + * @param {Array} arr0 + * @param {Array} arr1 + * @param {number} arrDim + * @return {boolean} + */ + function isArraySame(arr0, arr1, arrDim) { + if (arr0 === arr1) { + return true; + } + var len = arr0.length; + if (len !== arr1.length) { + return false; + } + if (arrDim === 1) { + for (var i = 0; i < len; i++) { + if (arr0[i] !== arr1[i]) { + return false; + } + } + } + else { + var len2 = arr0[0].length; + for (var i = 0; i < len; i++) { + for (var j = 0; j < len2; j++) { + if (arr0[i][j] !== arr1[i][j]) { + return false; + } + } + } + } + return true; + } + + function createTrackClip(animator, easing, oneTrackDone, keyframes, propName, interpolater) { + var getter = animator._getter; + var setter = animator._setter; + var useSpline = easing === 'spline'; + + var trackLen = keyframes.length; + if (!trackLen) { + return; + } + // Guess data type + var firstVal = keyframes[0].value; + var isValueArray = isArrayLike(firstVal); + + // For vertices morphing + var arrDim = ( + isValueArray + && isArrayLike(firstVal[0]) + ) + ? 2 : 1; + // Sort keyframe as ascending + keyframes.sort(function(a, b) { + return a.time - b.time; + }); + + var trackMaxTime = keyframes[trackLen - 1].time; + // Percents of each keyframe + var kfPercents = []; + // Value of each keyframe + var kfValues = []; + + var prevValue = keyframes[0].value; + var isAllValueEqual = true; + for (var i = 0; i < trackLen; i++) { + kfPercents.push(keyframes[i].time / trackMaxTime); + + // Assume value is a color when it is a string + var value = keyframes[i].value; + + // Check if value is equal, deep check if value is array + if (!((isValueArray && isArraySame(value, prevValue, arrDim)) + || (!isValueArray && value === prevValue))) { + isAllValueEqual = false; + } + prevValue = value; + + kfValues.push(value); + } + if (isAllValueEqual) { + return; + } + + var lastValue = kfValues[trackLen - 1]; + // Polyfill array and NaN value + for (var i = 0; i < trackLen - 1; i++) { + if (isValueArray) { + fillArr(kfValues[i], lastValue, arrDim); + } + else { + if (isNaN(kfValues[i]) && !isNaN(lastValue)) { + kfValues[i] = lastValue; + } + } + } + isValueArray && fillArr(getter(animator._target, propName), lastValue, arrDim); + + // Cache the key of last frame to speed up when + // animation playback is sequency + var cacheKey = 0; + var cachePercent = 0; + var start; + var i, w; + var p0, p1, p2, p3; + + var onframe = function(target, percent) { + // Find the range keyframes + // kf1-----kf2---------current--------kf3 + // find kf2(i) and kf3(i+1) and do interpolation + if (percent < cachePercent) { + // Start from next key + start = Math.min(cacheKey + 1, trackLen - 1); + for (i = start; i >= 0; i--) { + if (kfPercents[i] <= percent) { + break; + } + } + i = Math.min(i, trackLen-2); + } else { + for (i = cacheKey; i < trackLen; i++) { + if (kfPercents[i] > percent) { + break; + } + } + i = Math.min(i-1, trackLen-2); + } + cacheKey = i; + cachePercent = percent; + + var range = (kfPercents[i+1] - kfPercents[i]); + if (range === 0) { + return; + } else { + w = (percent - kfPercents[i]) / range; + } + if (useSpline) { + p1 = kfValues[i]; + p0 = kfValues[i === 0 ? i : i - 1]; + p2 = kfValues[i > trackLen - 2 ? trackLen - 1 : i + 1]; + p3 = kfValues[i > trackLen - 3 ? trackLen - 1 : i + 2]; + if (interpolater) { + setter( + target, + propName, + interpolater( + getter(target, propName), + p0, p1, p2, p3, w + ) + ); + } else if (isValueArray) { + catmullRomInterpolateArray( + p0, p1, p2, p3, w, w*w, w*w*w, + getter(target, propName), + arrDim + ); + } else { + setter( + target, + propName, + catmullRomInterpolate(p0, p1, p2, p3, w, w*w, w*w*w) + ); + } + } else { + if (interpolater) { + setter( + target, + propName, + interpolater( + getter(target, propName), + kfValues[i], + kfValues[i + 1], + w + ) + ); + } + else if (isValueArray) { + interpolateArray( + kfValues[i], kfValues[i+1], w, + getter(target, propName), + arrDim + ); + } else { + setter( + target, + propName, + interpolateNumber(kfValues[i], kfValues[i+1], w) + ); + } + } + }; + + var clip = new Clip({ + target: animator._target, + life: trackMaxTime, + loop: animator._loop, + delay: animator._delay, + onframe: onframe, + onfinish: oneTrackDone + }); + + if (easing && easing !== 'spline') { + clip.setEasing(easing); + } + + return clip; + } + + /** + * @description Animator object can only be created by Animation.prototype.animate method. + * After created, we can use {@link qtek.animation.Animator#when} to add all keyframes and {@link qtek.animation.Animator#start} it. + * Clips will be automatically created and added to the animation instance which created this deferred object. + * + * @constructor qtek.animation.Animator + * + * @param {Object} target + * @param {boolean} loop + * @param {Function} getter + * @param {Function} setter + * @param {Function} interpolater + */ + function Animator(target, loop, getter, setter, interpolater) { + this._tracks = {}; + this._target = target; + + this._loop = loop || false; + + this._getter = getter || defaultGetter; + this._setter = setter || defaultSetter; + + this._interpolater = interpolater || null; + + this._delay = 0; + + this._doneList = []; + + this._onframeList = []; + + this._clipList = []; + } + + Animator.prototype = { + + constructor: Animator, + + /** + * @param {number} time Keyframe time using millisecond + * @param {Object} props A key-value object. Value can be number, 1d and 2d array + * @return {qtek.animation.Animator} + * @memberOf qtek.animation.Animator.prototype + */ + when: function (time, props) { + for (var propName in props) { + if (!this._tracks[propName]) { + this._tracks[propName] = []; + // If time is 0 + // Then props is given initialize value + // Else + // Initialize value from current prop value + if (time !== 0) { + this._tracks[propName].push({ + time: 0, + value: cloneValue( + this._getter(this._target, propName) + ) + }); + } + } + this._tracks[propName].push({ + time: parseInt(time), + value: props[propName] + }); + } + return this; + }, + /** + * callback when running animation + * @param {Function} callback callback have two args, animating target and current percent + * @return {qtek.animation.Animator} + * @memberOf qtek.animation.Animator.prototype + */ + during: function (callback) { + this._onframeList.push(callback); + return this; + }, + + _doneCallback: function () { + // Clear all tracks + this._tracks = {}; + // Clear all clips + this._clipList.length = 0; + + var doneList = this._doneList; + var len = doneList.length; + for (var i = 0; i < len; i++) { + doneList[i].call(this); + } + }, + /** + * Start the animation + * @param {string|function} easing + * @return {qtek.animation.Animator} + * @memberOf qtek.animation.Animator.prototype + */ + start: function (easing) { + + var self = this; + var clipCount = 0; + + var oneTrackDone = function() { + clipCount--; + if (clipCount === 0) { + self._doneCallback(); + } + }; + + var lastClip; + for (var propName in this._tracks) { + var clip = createTrackClip( + this, easing, oneTrackDone, + this._tracks[propName], propName, self._interpolater + ); + if (clip) { + this._clipList.push(clip); + clipCount++; + + // If start after added to animation + if (this.animation) { + this.animation.addClip(clip); + } + + lastClip = clip; + } + } + + // Add during callback on the last clip + if (lastClip) { + var oldOnFrame = lastClip.onframe; + lastClip.onframe = function (target, percent) { + oldOnFrame(target, percent); + + for (var i = 0; i < self._onframeList.length; i++) { + self._onframeList[i](target, percent); + } + }; + } + + if (!clipCount) { + this._doneCallback(); + } + return this; + }, + + /** + * Stop the animation + * @memberOf qtek.animation.Animator.prototype + */ + stop: function () { + for (var i = 0; i < this._clipList.length; i++) { + var clip = this._clipList[i]; + this.animation.removeClip(clip); + } + this._clipList = []; + }, + /** + * Delay given milliseconds + * @param {number} time + * @return {qtek.animation.Animator} + * @memberOf qtek.animation.Animator.prototype + */ + delay: function (time){ + this._delay = time; + return this; + }, + /** + * Callback after animation finished + * @param {Function} func + * @return {qtek.animation.Animator} + * @memberOf qtek.animation.Animator.prototype + */ + done: function (func) { + if (func) { + this._doneList.push(func); + } + return this; + }, + /** + * Get all clips created in start method. + * @return {qtek.animation.Clip[]} + * @memberOf qtek.animation.Animator.prototype + */ + getClips: function () { + return this._clipList; + } + }; + + module.exports = Animator; + + +/***/ }, +/* 8 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Easing = __webpack_require__(9); + + function noop () {} + /** + * @constructor + * @alias qtek.animation.Clip + * @param {Object} [opts] + * @param {Object} [opts.target] + * @param {number} [opts.life] + * @param {number} [opts.delay] + * @param {number} [opts.gap] + * @param {number} [opts.playbackRate] + * @param {boolean|number} [opts.loop] If loop is a number, it indicate the loop count of animation + * @param {string|Function} [opts.easing] + * @param {Function} [opts.onframe] + * @param {Function} [opts.onfinish] + * @param {Function} [opts.onrestart] + */ + var Clip = function (opts) { + + opts = opts || {}; + + /** + * @type {string} + */ + this.name = opts.name || ''; + + /** + * @type {Object} + */ + this.target = opts.target; + + /** + * @type {number} + */ + this.life = opts.life || 1000; + + /** + * @type {number} + */ + this.delay = opts.delay || 0; + + /** + * @type {number} + */ + this.gap = opts.gap || 0; + + /** + * @type {number} + */ + this.playbackRate = opts.playbackRate || 1; + + + this._initialized = false; + + this._elapsedTime = 0; + + this._loop = opts.loop == null ? false : opts.loop; + this.setLoop(this._loop); + + if (opts.easing != null) { + this.setEasing(opts.easing); + } + + /** + * @type {Function} + */ + this.onframe = opts.onframe || noop; + + /** + * @type {Function} + */ + this.onfinish = opts.onfinish || noop; + + /** + * @type {Function} + */ + this.onrestart = opts.onrestart || noop; + + }; + + Clip.prototype = { + + gap: 0, + + life: 0, + + delay: 0, + + /** + * @param {number|boolean} loop + */ + setLoop: function (loop) { + this._loop = loop; + if (loop) { + if (typeof(loop) == 'number') { + this._loopRemained = loop; + } + else { + this._loopRemained = 1e8; + } + } + }, + + /** + * @param {string|function} easing + */ + setEasing: function (easing) { + if (typeof(easing) === 'string') { + easing = Easing[easing]; + } + this.easing = easing; + }, + + /** + * @param {number} time + * @return {string} + */ + step: function (time) { + if (!this._initialized) { + this._currentTime = time; + this._startTime = this._currentTime + this.delay; + this._initialized = true; + } + + if (time < this._startTime) { + this._currentTime = time; + return; + } + + this._elapse(time); + + var percent = this._elapsedTime / this.life; + + if (percent < 0) { + return; + } + if (percent > 1) { + percent = 1; + } + + var schedule; + if (this.easing) { + schedule = this.easing(percent); + } + else { + schedule = percent; + } + this.fire('frame', schedule); + + if (percent === 1) { + if (this._loop && this._loopRemained > 0) { + this._restartInLoop(time); + this._loopRemained--; + return 'restart'; + } + else { + // Mark this clip to be deleted + // In the animation.update + this._needsRemove = true; + + return 'finish'; + } + } + else { + return null; + } + }, + + /** + * @param {number} time + * @return {string} + */ + setTime: function (time) { + return this.step(time + this._startTime); + }, + + restart: function (time) { + // If user leave the page for a while, when he gets back + // All clips may be expired and all start from the beginning value(position) + // It is clearly wrong, so we use remainder to add a offset + + var remainder = 0; + + time = time; + // Remainder ignored if restart is invoked manually + if (time) { + this._elapse(time); + remainder = this._elapsedTime % this.life; + } + time = time || new Date().getTime(); + + this._startTime = time - remainder + this.delay; + this._elapsedTime = 0; + this._currentTime = time - remainder; + + this._needsRemove = false; + }, + + _restartInLoop: function (time) { + this._startTime = time + this.gap; + this._currentTime = time; + this._elapsedTime = 0; + }, + + _elapse: function (time) { + this._elapsedTime += (time - this._currentTime) * this.playbackRate; + this._currentTime = time; + }, + + fire: function (eventType, arg) { + var eventName = 'on' + eventType; + if (this[eventName]) { + this[eventName](this.target, arg); + } + }, + + clone: function () { + var clip = new this.constructor(); + clip.name = this.name; + clip._loop = this._loop; + clip._loopRemained = this._loopRemained; + + clip.life = this.life; + clip.gap = this.gap; + clip.delay = this.delay; + + return clip; + } + }; + Clip.prototype.constructor = Clip; + + module.exports = Clip; + + +/***/ }, +/* 9 */ +/***/ function(module, exports) { + + // 缓动函数来自 https://github.com/sole/tween.js/blob/master/src/Tween.js + + + /** + * @namespace qtek.animation.easing + */ + var easing = { + /** + * @alias qtek.animation.easing.linear + * @param {number} k + * @return {number} + */ + linear: function(k) { + return k; + }, + /** + * @alias qtek.animation.easing.quadraticIn + * @param {number} k + * @return {number} + */ + quadraticIn: function(k) { + return k * k; + }, + /** + * @alias qtek.animation.easing.quadraticOut + * @param {number} k + * @return {number} + */ + quadraticOut: function(k) { + return k * (2 - k); + }, + /** + * @alias qtek.animation.easing.quadraticInOut + * @param {number} k + * @return {number} + */ + quadraticInOut: function(k) { + if ((k *= 2) < 1) { + return 0.5 * k * k; + } + return - 0.5 * (--k * (k - 2) - 1); + }, + /** + * @alias qtek.animation.easing.cubicIn + * @param {number} k + * @return {number} + */ + cubicIn: function(k) { + return k * k * k; + }, + /** + * @alias qtek.animation.easing.cubicOut + * @param {number} k + * @return {number} + */ + cubicOut: function(k) { + return --k * k * k + 1; + }, + /** + * @alias qtek.animation.easing.cubicInOut + * @param {number} k + * @return {number} + */ + cubicInOut: function(k) { + if ((k *= 2) < 1) { + return 0.5 * k * k * k; + } + return 0.5 * ((k -= 2) * k * k + 2); + }, + /** + * @alias qtek.animation.easing.quarticIn + * @param {number} k + * @return {number} + */ + quarticIn: function(k) { + return k * k * k * k; + }, + /** + * @alias qtek.animation.easing.quarticOut + * @param {number} k + * @return {number} + */ + quarticOut: function(k) { + return 1 - (--k * k * k * k); + }, + /** + * @alias qtek.animation.easing.quarticInOut + * @param {number} k + * @return {number} + */ + quarticInOut: function(k) { + if ((k *= 2) < 1) { + return 0.5 * k * k * k * k; + } + return - 0.5 * ((k -= 2) * k * k * k - 2); + }, + /** + * @alias qtek.animation.easing.quinticIn + * @param {number} k + * @return {number} + */ + quinticIn: function(k) { + return k * k * k * k * k; + }, + /** + * @alias qtek.animation.easing.quinticOut + * @param {number} k + * @return {number} + */ + quinticOut: function(k) { + return --k * k * k * k * k + 1; + }, + /** + * @alias qtek.animation.easing.quinticInOut + * @param {number} k + * @return {number} + */ + quinticInOut: function(k) { + if ((k *= 2) < 1) { + return 0.5 * k * k * k * k * k; + } + return 0.5 * ((k -= 2) * k * k * k * k + 2); + }, + /** + * @alias qtek.animation.easing.sinusoidalIn + * @param {number} k + * @return {number} + */ + sinusoidalIn: function(k) { + return 1 - Math.cos(k * Math.PI / 2); + }, + /** + * @alias qtek.animation.easing.sinusoidalOut + * @param {number} k + * @return {number} + */ + sinusoidalOut: function(k) { + return Math.sin(k * Math.PI / 2); + }, + /** + * @alias qtek.animation.easing.sinusoidalInOut + * @param {number} k + * @return {number} + */ + sinusoidalInOut: function(k) { + return 0.5 * (1 - Math.cos(Math.PI * k)); + }, + /** + * @alias qtek.animation.easing.exponentialIn + * @param {number} k + * @return {number} + */ + exponentialIn: function(k) { + return k === 0 ? 0 : Math.pow(1024, k - 1); + }, + /** + * @alias qtek.animation.easing.exponentialOut + * @param {number} k + * @return {number} + */ + exponentialOut: function(k) { + return k === 1 ? 1 : 1 - Math.pow(2, - 10 * k); + }, + /** + * @alias qtek.animation.easing.exponentialInOut + * @param {number} k + * @return {number} + */ + exponentialInOut: function(k) { + if (k === 0) { + return 0; + } + if (k === 1) { + return 1; + } + if ((k *= 2) < 1) { + return 0.5 * Math.pow(1024, k - 1); + } + return 0.5 * (- Math.pow(2, - 10 * (k - 1)) + 2); + }, + /** + * @alias qtek.animation.easing.circularIn + * @param {number} k + * @return {number} + */ + circularIn: function(k) { + return 1 - Math.sqrt(1 - k * k); + }, + /** + * @alias qtek.animation.easing.circularOut + * @param {number} k + * @return {number} + */ + circularOut: function(k) { + return Math.sqrt(1 - (--k * k)); + }, + /** + * @alias qtek.animation.easing.circularInOut + * @param {number} k + * @return {number} + */ + circularInOut: function(k) { + if ((k *= 2) < 1) { + return - 0.5 * (Math.sqrt(1 - k * k) - 1); + } + return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1); + }, + /** + * @alias qtek.animation.easing.elasticIn + * @param {number} k + * @return {number} + */ + elasticIn: function(k) { + var s, a = 0.1, p = 0.4; + if (k === 0) { + return 0; + } + if (k === 1) { + return 1; + } + if (!a || a < 1) { + a = 1; s = p / 4; + }else{ + s = p * Math.asin(1 / a) / (2 * Math.PI); + } + return - (a * Math.pow(2, 10 * (k -= 1)) * + Math.sin((k - s) * (2 * Math.PI) / p)); + }, + /** + * @alias qtek.animation.easing.elasticOut + * @param {number} k + * @return {number} + */ + elasticOut: function(k) { + var s, a = 0.1, p = 0.4; + if (k === 0) { + return 0; + } + if (k === 1) { + return 1; + } + if (!a || a < 1) { + a = 1; s = p / 4; + } + else{ + s = p * Math.asin(1 / a) / (2 * Math.PI); + } + return (a * Math.pow(2, - 10 * k) * + Math.sin((k - s) * (2 * Math.PI) / p) + 1); + }, + /** + * @alias qtek.animation.easing.elasticInOut + * @param {number} k + * @return {number} + */ + elasticInOut: function(k) { + var s, a = 0.1, p = 0.4; + if (k === 0) { + return 0; + } + if (k === 1) { + return 1; + } + if (!a || a < 1) { + a = 1; s = p / 4; + } + else{ + s = p * Math.asin(1 / a) / (2 * Math.PI); + } + if ((k *= 2) < 1) { + return - 0.5 * (a * Math.pow(2, 10 * (k -= 1)) + * Math.sin((k - s) * (2 * Math.PI) / p)); + } + return a * Math.pow(2, -10 * (k -= 1)) + * Math.sin((k - s) * (2 * Math.PI) / p) * 0.5 + 1; + + }, + /** + * @alias qtek.animation.easing.backIn + * @param {number} k + * @return {number} + */ + backIn: function(k) { + var s = 1.70158; + return k * k * ((s + 1) * k - s); + }, + /** + * @alias qtek.animation.easing.backOut + * @param {number} k + * @return {number} + */ + backOut: function(k) { + var s = 1.70158; + return --k * k * ((s + 1) * k + s) + 1; + }, + /** + * @alias qtek.animation.easing.backInOut + * @param {number} k + * @return {number} + */ + backInOut: function(k) { + var s = 1.70158 * 1.525; + if ((k *= 2) < 1) { + return 0.5 * (k * k * ((s + 1) * k - s)); + } + return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2); + }, + /** + * @alias qtek.animation.easing.bounceIn + * @param {number} k + * @return {number} + */ + bounceIn: function(k) { + return 1 - easing.bounceOut(1 - k); + }, + /** + * @alias qtek.animation.easing.bounceOut + * @param {number} k + * @return {number} + */ + bounceOut: function(k) { + if (k < (1 / 2.75)) { + return 7.5625 * k * k; + } + else if (k < (2 / 2.75)) { + return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75; + } else if (k < (2.5 / 2.75)) { + return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375; + } else { + return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375; + } + }, + /** + * @alias qtek.animation.easing.bounceInOut + * @param {number} k + * @return {number} + */ + bounceInOut: function(k) { + if (k < 0.5) { + return easing.bounceIn(k * 2) * 0.5; + } + return easing.bounceOut(k * 2 - 1) * 0.5 + 0.5; + } + }; + + module.exports = easing; + + + + +/***/ }, +/* 10 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + // 1D Blend clip of blend tree + // http://docs.unity3d.com/Documentation/Manual/1DBlending.html + + + var Clip = __webpack_require__(8); + + var clipSortFunc = function(a, b) { + return a.position < b.position; + }; + + /** + * @typedef {Object} qtek.animation.Blend1DClip.IClipInput + * @property {number} position + * @property {qtek.animation.Clip} clip + * @property {number} offset + */ + + /** + * 1d blending node in animation blend tree. + * output clip must have blend1D and copy method + * @constructor + * @alias qtek.animation.Blend1DClip + * @extends qtek.animation.Clip + * + * @param {Object} [opts] + * @param {string} [opts.name] + * @param {Object} [opts.target] + * @param {number} [opts.life] + * @param {number} [opts.delay] + * @param {number} [opts.gap] + * @param {number} [opts.playbackRatio] + * @param {boolean|number} [opts.loop] If loop is a number, it indicate the loop count of animation + * @param {string|Function} [opts.easing] + * @param {Function} [opts.onframe] + * @param {Function} [opts.onfinish] + * @param {Function} [opts.onrestart] + * @param {object[]} [opts.inputs] + * @param {number} [opts.position] + * @param {qtek.animation.Clip} [opts.output] + */ + var Blend1DClip = function(opts) { + + opts = opts || {}; + + Clip.call(this, opts); + /** + * Output clip must have blend1D and copy method + * @type {qtek.animation.Clip} + */ + this.output = opts.output || null; + /** + * @type {qtek.animation.Blend1DClip.IClipInput[]} + */ + this.inputs = opts.inputs || []; + /** + * @type {number} + */ + this.position = 0; + + this._cacheKey = 0; + this._cachePosition = -Infinity; + + this.inputs.sort(clipSortFunc); + }; + + Blend1DClip.prototype = new Clip(); + Blend1DClip.prototype.constructor = Blend1DClip; + + /** + * @param {number} position + * @param {qtek.animation.Clip} inputClip + * @param {number} [offset] + * @return {qtek.animation.Blend1DClip.IClipInput} + */ + Blend1DClip.prototype.addInput = function(position, inputClip, offset) { + var obj = { + position: position, + clip: inputClip, + offset: offset || 0 + }; + this.life = Math.max(inputClip.life, this.life); + + if (!this.inputs.length) { + this.inputs.push(obj); + return obj; + } + var len = this.inputs.length; + if (this.inputs[0].position > position) { + this.inputs.unshift(obj); + } else if (this.inputs[len - 1].position <= position) { + this.inputs.push(obj); + } else { + var key = this._findKey(position); + this.inputs.splice(key, obj); + } + + return obj; + }; + + Blend1DClip.prototype.step = function(time) { + + var ret = Clip.prototype.step.call(this, time); + + if (ret !== 'finish') { + this.setTime(this._elapsedTime); + } + + return ret; + }; + + Blend1DClip.prototype.setTime = function(time) { + var position = this.position; + var inputs = this.inputs; + var len = inputs.length; + var min = inputs[0].position; + var max = inputs[len-1].position; + + if (position <= min || position >= max) { + var in0 = position <= min ? inputs[0] : inputs[len-1]; + var clip = in0.clip; + var offset = in0.offset; + clip.setTime((time + offset) % clip.life); + // Input clip is a blend clip + // PENDING + if (clip.output instanceof Clip) { + this.output.copy(clip.output); + } else { + this.output.copy(clip); + } + } else { + var key = this._findKey(position); + var in1 = inputs[key]; + var in2 = inputs[key + 1]; + var clip1 = in1.clip; + var clip2 = in2.clip; + // Set time on input clips + clip1.setTime((time + in1.offset) % clip1.life); + clip2.setTime((time + in2.offset) % clip2.life); + + var w = (this.position - in1.position) / (in2.position - in1.position); + + var c1 = clip1.output instanceof Clip ? clip1.output : clip1; + var c2 = clip2.output instanceof Clip ? clip2.output : clip2; + this.output.blend1D(c1, c2, w); + } + }; + + /** + * Clone a new Blend1D clip + * @param {boolean} cloneInputs True if clone the input clips + * @return {qtek.animation.Blend1DClip} + */ + Blend1DClip.prototype.clone = function (cloneInputs) { + var clip = Clip.prototype.clone.call(this); + clip.output = this.output.clone(); + for (var i = 0; i < this.inputs.length; i++) { + var inputClip = cloneInputs ? this.inputs[i].clip.clone(true) : this.inputs[i].clip; + clip.addInput(this.inputs[i].position, inputClip, this.inputs[i].offset); + } + return clip; + }; + + // Find the key where position in range [inputs[key].position, inputs[key+1].position) + Blend1DClip.prototype._findKey = function(position) { + var key = -1; + var inputs = this.inputs; + var len = inputs.length; + if (this._cachePosition < position) { + for (var i = this._cacheKey; i < len-1; i++) { + if (position >= inputs[i].position && position < inputs[i+1].position) { + key = i; + } + } + } else { + var s = Math.min(len-2, this._cacheKey); + for (var i = s; i >= 0; i--) { + if (position >= inputs[i].position && position < inputs[i+1].position) { + key = i; + } + } + } + if (key >= 0) { + this._cacheKey = key; + this._cachePosition = position; + } + + return key; + }; + + module.exports = Blend1DClip; + + +/***/ }, +/* 11 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + // 2D Blend clip of blend tree + // http://docs.unity3d.com/Documentation/Manual/2DBlending.html + + + var Clip = __webpack_require__(8); + + var delaunay = __webpack_require__(12); + var Vector2 = __webpack_require__(13); + + /** + * @typedef {Object} qtek.animation.Blend2DClip.IClipInput + * @property {qtek.math.Vector2} position + * @property {qtek.animation.Clip} clip + * @property {number} offset + */ + + /** + * 2d blending node in animation blend tree. + * output clip must have blend2D method + * @constructor + * @alias qtek.animation.Blend2DClip + * @extends qtek.animation.Clip + * + * @param {Object} [opts] + * @param {string} [opts.name] + * @param {Object} [opts.target] + * @param {number} [opts.life] + * @param {number} [opts.delay] + * @param {number} [opts.gap] + * @param {number} [opts.playbackRatio] + * @param {boolean|number} [opts.loop] If loop is a number, it indicate the loop count of animation + * @param {string|Function} [opts.easing] + * @param {Function} [opts.onframe] + * @param {Function} [opts.onfinish] + * @param {Function} [opts.onrestart] + * @param {object[]} [opts.inputs] + * @param {qtek.math.Vector2} [opts.position] + * @param {qtek.animation.Clip} [opts.output] + */ + var Blend2DClip = function(opts) { + + opts = opts || {}; + + Clip.call(this, opts); + /** + * Output clip must have blend2D method + * @type {qtek.animation.Clip} + */ + this.output = opts.output || null; + /** + * @type {qtek.animation.Blend2DClip.IClipInput[]} + */ + this.inputs = opts.inputs || []; + /** + * @type {qtek.math.Vector2} + */ + this.position = new Vector2(); + + this._cacheTriangle = null; + + this._triangles = []; + + this._updateTriangles(); + }; + + Blend2DClip.prototype = new Clip(); + Blend2DClip.prototype.constructor = Blend2DClip; + /** + * @param {qtek.math.Vector2} position + * @param {qtek.animation.Clip} inputClip + * @param {number} [offset] + * @return {qtek.animation.Blend2DClip.IClipInput} + */ + Blend2DClip.prototype.addInput = function(position, inputClip, offset) { + var obj = { + position : position, + clip : inputClip, + offset : offset || 0 + }; + this.inputs.push(obj); + this.life = Math.max(inputClip.life, this.life); + // TODO Change to incrementally adding + this._updateTriangles(); + + return obj; + }; + + // Delaunay triangulate + Blend2DClip.prototype._updateTriangles = function() { + var inputs = this.inputs.map(function(a) { + return a.position; + }); + this._triangles = delaunay.triangulate(inputs, '_array'); + }; + + Blend2DClip.prototype.step = function(time) { + + var ret = Clip.prototype.step.call(this, time); + + if (ret !== 'finish') { + this.setTime(this._elapsedTime); + } + + return ret; + }; + + Blend2DClip.prototype.setTime = function(time) { + var res = this._findTriangle(this.position); + if (!res) { + return; + } + // In Barycentric + var a = res[1]; // Percent of clip2 + var b = res[2]; // Percent of clip3 + + var tri = res[0]; + + var in1 = this.inputs[tri.indices[0]]; + var in2 = this.inputs[tri.indices[1]]; + var in3 = this.inputs[tri.indices[2]]; + var clip1 = in1.clip; + var clip2 = in2.clip; + var clip3 = in3.clip; + + clip1.setTime((time + in1.offset) % clip1.life); + clip2.setTime((time + in2.offset) % clip2.life); + clip3.setTime((time + in3.offset) % clip3.life); + + var c1 = clip1.output instanceof Clip ? clip1.output : clip1; + var c2 = clip2.output instanceof Clip ? clip2.output : clip2; + var c3 = clip3.output instanceof Clip ? clip3.output : clip3; + + this.output.blend2D(c1, c2, c3, a, b); + }; + + /** + * Clone a new Blend2D clip + * @param {boolean} cloneInputs True if clone the input clips + * @return {qtek.animation.Blend2DClip} + */ + Blend2DClip.prototype.clone = function (cloneInputs) { + var clip = Clip.prototype.clone.call(this); + clip.output = this.output.clone(); + for (var i = 0; i < this.inputs.length; i++) { + var inputClip = cloneInputs ? this.inputs[i].clip.clone(true) : this.inputs[i].clip; + clip.addInput(this.inputs[i].position, inputClip, this.inputs[i].offset); + } + return clip; + }; + + Blend2DClip.prototype._findTriangle = function(position) { + if (this._cacheTriangle) { + var res = delaunay.contains(this._cacheTriangle.vertices, position._array); + if (res) { + return [this._cacheTriangle, res[0], res[1]]; + } + } + for (var i = 0; i < this._triangles.length; i++) { + var tri = this._triangles[i]; + var res = delaunay.contains(tri.vertices, this.position._array); + if (res) { + this._cacheTriangle = tri; + return [tri, res[0], res[1]]; + } + } + }; + + module.exports = Blend2DClip; + + +/***/ }, +/* 12 */ +/***/ function(module, exports) { + + 'use strict'; + // Delaunay Triangulation + // Modified from https://github.com/ironwallaby/delaunay + + + + function appendSupertriangleVertices(vertices) { + var xmin = Number.POSITIVE_INFINITY, + ymin = Number.POSITIVE_INFINITY, + xmax = Number.NEGATIVE_INFINITY, + ymax = Number.NEGATIVE_INFINITY, + i, dx, dy, dmax, xmid, ymid; + + for(i = vertices.length; i--; ) { + if(vertices[i][0] < xmin) xmin = vertices[i][0]; + if(vertices[i][0] > xmax) xmax = vertices[i][0]; + if(vertices[i][1] < ymin) ymin = vertices[i][1]; + if(vertices[i][1] > ymax) ymax = vertices[i][1]; + } + + dx = xmax - xmin; + dy = ymax - ymin; + dmax = Math.max(dx, dy); + xmid = xmin + dx * 0.5; + ymid = ymin + dy * 0.5; + + vertices.push( + [xmid - 20 * dmax, ymid - dmax], + [xmid , ymid + 20 * dmax], + [xmid + 20 * dmax, ymid - dmax] + ); + } + + function triangle(vertices, i, j, k) { + var a = vertices[i], + b = vertices[j], + c = vertices[k], + A = b[0] - a[0], + B = b[1] - a[1], + C = c[0] - a[0], + D = c[1] - a[1], + E = A * (a[0] + b[0]) + B * (a[1] + b[1]), + F = C * (a[0] + c[0]) + D * (a[1] + c[1]), + G = 2 * (A * (c[1] - b[1]) - B * (c[0] - b[0])), + minx, miny, dx, dy, x, y; + + /* If the points of the triangle are collinear, then just find the + * extremes and use the midpoint as the center of the circumcircle. */ + if (Math.abs(G) < 0.000001) { + minx = Math.min(a[0], b[0], c[0]); + miny = Math.min(a[1], b[1], c[1]); + dx = (Math.max(a[0], b[0], c[0]) - minx) * 0.5; + dy = (Math.max(a[1], b[1], c[1]) - miny) * 0.5; + x = minx + dx; + y = miny + dy; + } + else { + x = (D*E - B*F) / G; + y = (A*F - C*E) / G; + dx = x - a[0]; + dy = y - a[1]; + } + + return {i: i, j: j, k: k, x: x, y: y, r: dx * dx + dy * dy}; + } + + function dedup(edges) { + var j = edges.length, + a, b, i, m, n; + + outer: while (j) { + b = edges[--j]; + a = edges[--j]; + i = j; + while (i) { + n = edges[--i] + m = edges[--i] + if ((a === m && b === n) || (a === n && b === m)) { + edges.splice(j, 2); + edges.splice(i, 2); + j -= 2; + continue outer; + } + } + } + } + + var delaunay = { + triangulate: function(vertices, key) { + var n = vertices.length, + i, j, indices, open, closed, edges, dx, dy, a, b, c; + + /* Bail if there aren't enough vertices to form any triangles. */ + if (n < 3) { + return []; + } + + /* Slice out the actual vertices from the passed objects. (Duplicate the + * array even if we don't, though, since we need to make a supertriangle + * later on!) */ + vertices = vertices.slice(0); + + if (key) { + for (i = n; i--; ) { + vertices[i] = vertices[i][key]; + } + } + + /* Make an array of indices into the vertex array, sorted by the vertices' + * x-position. */ + indices = new Array(n); + + for (i = n; i--; ) { + indices[i] = i; + } + + indices.sort(function(i, j) { return vertices[j][0] - vertices[i][0]; }); + + /* Next, find the vertices of the supertriangle (which contains all other + * triangles), and append them onto the end of a (copy of) the vertex + * array. */ + appendSupertriangleVertices(vertices); + + /* Initialize the open list (containing the supertriangle and nothing else) + * and the closed list (which is empty since we havn't processed any + * triangles yet). */ + open = [triangle(vertices, n + 0, n + 1, n + 2)]; + closed = []; + edges = []; + + /* Incrementally add each vertex to the mesh. */ + for (i = indices.length; i--; ) { + c = indices[i]; + edges.length = 0; + + /* For each open triangle, check to see if the current point is + * inside it's circumcircle. If it is, remove the triangle and add + * it's edges to an edge list. */ + for (j = open.length; j--; ) { + /* If this point is to the right of this triangle's circumcircle, + * then this triangle should never get checked again. Remove it + * from the open list, add it to the closed list, and skip. */ + dx = vertices[c][0] - open[j].x; + if (dx > 0.0 && dx * dx > open[j].r) { + closed.push(open[j]); + open.splice(j, 1); + continue; + } + + /* If we're outside the circumcircle, skip this triangle. */ + dy = vertices[c][1] - open[j].y; + if (dx * dx + dy * dy > open[j].r) { + continue; + } + + /* Remove the triangle and add it's edges to the edge list. */ + edges.push( + open[j].i, open[j].j, + open[j].j, open[j].k, + open[j].k, open[j].i + ); + open.splice(j, 1); + } + + /* Remove any doubled edges. */ + dedup(edges); + + /* Add a new triangle for each edge. */ + for(j = edges.length; j; ) { + b = edges[--j]; + a = edges[--j]; + open.push(triangle(vertices, a, b, c)); + } + } + + /* Copy any remaining open triangles to the closed list, and then + * remove any triangles that share a vertex with the supertriangle, building + * a list of triplets that represent triangles. */ + for (i = open.length; i--; ) { + closed.push(open[i]); + } + open.length = 0; + + for(i = closed.length; i--; ) { + if(closed[i].i < n && closed[i].j < n && closed[i].k < n) { + var i1 = closed[i].i, + i2 = closed[i].j, + i3 = closed[i].k; + var tri = { + indices : [i1, i2, i3], + vertices : [vertices[i1], vertices[i2], vertices[i3]] + }; + open.push(tri); + } + } + + /* Yay, we're done! */ + return open; + }, + + contains: function(tri, p) { + /* Bounding box test first, for quick rejections. */ + if((p[0] < tri[0][0] && p[0] < tri[1][0] && p[0] < tri[2][0]) || + (p[0] > tri[0][0] && p[0] > tri[1][0] && p[0] > tri[2][0]) || + (p[1] < tri[0][1] && p[1] < tri[1][1] && p[1] < tri[2][1]) || + (p[1] > tri[0][1] && p[1] > tri[1][1] && p[1] > tri[2][1])) { + + return null; + } + + var a = tri[1][0] - tri[0][0], + b = tri[2][0] - tri[0][0], + c = tri[1][1] - tri[0][1], + d = tri[2][1] - tri[0][1], + i = a * d - b * c; + + /* Degenerate tri. */ + if(i === 0.0) { + return null; + } + + var u = (d * (p[0] - tri[0][0]) - b * (p[1] - tri[0][1])) / i, + v = (a * (p[1] - tri[0][1]) - c * (p[0] - tri[0][0])) / i; + + /* If we're outside the tri, fail. */ + if(u < 0.0 || v < 0.0 || (u + v) > 1.0) { + return null; + } + + // normalize + // u = Math.max(0.0, u); + // v = Math.max(0.0, v); + // var s = u + v; + // if (s > 1.0) { + // u = u / s; + // v = v / s; + // } + return [u, v]; + } + } + + module.exports = delaunay; + + +/***/ }, +/* 13 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var glMatrix = __webpack_require__(14); + var vec2 = glMatrix.vec2; + + /** + * @constructor + * @alias qtek.math.Vector2 + * @param {number} x + * @param {number} y + */ + var Vector2 = function(x, y) { + + x = x || 0; + y = y || 0; + + /** + * Storage of Vector2, read and write of x, y will change the values in _array + * All methods also operate on the _array instead of x, y components + * @name _array + * @type {Float32Array} + */ + this._array = vec2.fromValues(x, y); + + /** + * Dirty flag is used by the Node to determine + * if the matrix is updated to latest + * @name _dirty + * @type {boolean} + */ + this._dirty = true; + }; + + Vector2.prototype = { + + constructor: Vector2, + + /** + * Add b to self + * @param {qtek.math.Vector2} b + * @return {qtek.math.Vector2} + */ + add: function(b) { + vec2.add(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Set x and y components + * @param {number} x + * @param {number} y + * @return {qtek.math.Vector2} + */ + set: function(x, y) { + this._array[0] = x; + this._array[1] = y; + this._dirty = true; + return this; + }, + + /** + * Set x and y components from array + * @param {Float32Array|number[]} arr + * @return {qtek.math.Vector2} + */ + setArray: function(arr) { + this._array[0] = arr[0]; + this._array[1] = arr[1]; + + this._dirty = true; + return this; + }, + + /** + * Clone a new Vector2 + * @return {qtek.math.Vector2} + */ + clone: function() { + return new Vector2(this.x, this.y); + }, + + /** + * Copy x, y from b + * @param {qtek.math.Vector2} b + * @return {qtek.math.Vector2} + */ + copy: function(b) { + vec2.copy(this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Cross product of self and b, written to a Vector3 out + * @param {qtek.math.Vector3} out + * @param {qtek.math.Vector2} b + * @return {qtek.math.Vector2} + */ + cross: function(out, b) { + vec2.cross(out._array, this._array, b._array); + out._dirty = true; + return this; + }, + + /** + * Alias for distance + * @param {qtek.math.Vector2} b + * @return {number} + */ + dist: function(b) { + return vec2.dist(this._array, b._array); + }, + + /** + * Distance between self and b + * @param {qtek.math.Vector2} b + * @return {number} + */ + distance: function(b) { + return vec2.distance(this._array, b._array); + }, + + /** + * Alias for divide + * @param {qtek.math.Vector2} b + * @return {qtek.math.Vector2} + */ + div: function(b) { + vec2.div(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Divide self by b + * @param {qtek.math.Vector2} b + * @return {qtek.math.Vector2} + */ + divide: function(b) { + vec2.divide(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Dot product of self and b + * @param {qtek.math.Vector2} b + * @return {number} + */ + dot: function(b) { + return vec2.dot(this._array, b._array); + }, + + /** + * Alias of length + * @return {number} + */ + len: function() { + return vec2.len(this._array); + }, + + /** + * Calculate the length + * @return {number} + */ + length: function() { + return vec2.length(this._array); + }, + + /** + * Linear interpolation between a and b + * @param {qtek.math.Vector2} a + * @param {qtek.math.Vector2} b + * @param {number} t + * @return {qtek.math.Vector2} + */ + lerp: function(a, b, t) { + vec2.lerp(this._array, a._array, b._array, t); + this._dirty = true; + return this; + }, + + /** + * Minimum of self and b + * @param {qtek.math.Vector2} b + * @return {qtek.math.Vector2} + */ + min: function(b) { + vec2.min(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Maximum of self and b + * @param {qtek.math.Vector2} b + * @return {qtek.math.Vector2} + */ + max: function(b) { + vec2.max(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Alias for multiply + * @param {qtek.math.Vector2} b + * @return {qtek.math.Vector2} + */ + mul: function(b) { + vec2.mul(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Mutiply self and b + * @param {qtek.math.Vector2} b + * @return {qtek.math.Vector2} + */ + multiply: function(b) { + vec2.multiply(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Negate self + * @return {qtek.math.Vector2} + */ + negate: function() { + vec2.negate(this._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Normalize self + * @return {qtek.math.Vector2} + */ + normalize: function() { + vec2.normalize(this._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Generate random x, y components with a given scale + * @param {number} scale + * @return {qtek.math.Vector2} + */ + random: function(scale) { + vec2.random(this._array, scale); + this._dirty = true; + return this; + }, + + /** + * Scale self + * @param {number} scale + * @return {qtek.math.Vector2} + */ + scale: function(s) { + vec2.scale(this._array, this._array, s); + this._dirty = true; + return this; + }, + + /** + * Scale b and add to self + * @param {qtek.math.Vector2} b + * @param {number} scale + * @return {qtek.math.Vector2} + */ + scaleAndAdd: function(b, s) { + vec2.scaleAndAdd(this._array, this._array, b._array, s); + this._dirty = true; + return this; + }, + + /** + * Alias for squaredDistance + * @param {qtek.math.Vector2} b + * @return {number} + */ + sqrDist: function(b) { + return vec2.sqrDist(this._array, b._array); + }, + + /** + * Squared distance between self and b + * @param {qtek.math.Vector2} b + * @return {number} + */ + squaredDistance: function(b) { + return vec2.squaredDistance(this._array, b._array); + }, + + /** + * Alias for squaredLength + * @return {number} + */ + sqrLen: function() { + return vec2.sqrLen(this._array); + }, + + /** + * Squared length of self + * @return {number} + */ + squaredLength: function() { + return vec2.squaredLength(this._array); + }, + + /** + * Alias for subtract + * @param {qtek.math.Vector2} b + * @return {qtek.math.Vector2} + */ + sub: function(b) { + vec2.sub(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Subtract b from self + * @param {qtek.math.Vector2} b + * @return {qtek.math.Vector2} + */ + subtract: function(b) { + vec2.subtract(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Transform self with a Matrix2 m + * @param {qtek.math.Matrix2} m + * @return {qtek.math.Vector2} + */ + transformMat2: function(m) { + vec2.transformMat2(this._array, this._array, m._array); + this._dirty = true; + return this; + }, + + /** + * Transform self with a Matrix2d m + * @param {qtek.math.Matrix2d} m + * @return {qtek.math.Vector2} + */ + transformMat2d: function(m) { + vec2.transformMat2d(this._array, this._array, m._array); + this._dirty = true; + return this; + }, + + /** + * Transform self with a Matrix3 m + * @param {qtek.math.Matrix3} m + * @return {qtek.math.Vector2} + */ + transformMat3: function(m) { + vec2.transformMat3(this._array, this._array, m._array); + this._dirty = true; + return this; + }, + + /** + * Transform self with a Matrix4 m + * @param {qtek.math.Matrix4} m + * @return {qtek.math.Vector2} + */ + transformMat4: function(m) { + vec2.transformMat4(this._array, this._array, m._array); + this._dirty = true; + return this; + }, + + toString: function() { + return '[' + Array.prototype.join.call(this._array, ',') + ']'; + }, + + toArray: function () { + return Array.prototype.slice.call(this._array); + } + }; + + // Getter and Setter + if (Object.defineProperty) { + + var proto = Vector2.prototype; + /** + * @name x + * @type {number} + * @memberOf qtek.math.Vector2 + * @instance + */ + Object.defineProperty(proto, 'x', { + get: function () { + return this._array[0]; + }, + set: function (value) { + this._array[0] = value; + this._dirty = true; + } + }); + + /** + * @name y + * @type {number} + * @memberOf qtek.math.Vector2 + * @instance + */ + Object.defineProperty(proto, 'y', { + get: function () { + return this._array[1]; + }, + set: function (value) { + this._array[1] = value; + this._dirty = true; + } + }); + } + + // Supply methods that are not in place + + /** + * @param {qtek.math.Vector2} out + * @param {qtek.math.Vector2} a + * @param {qtek.math.Vector2} b + * @return {qtek.math.Vector2} + */ + Vector2.add = function(out, a, b) { + vec2.add(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Vector2} out + * @param {number} x + * @param {number} y + * @return {qtek.math.Vector2} + */ + Vector2.set = function(out, x, y) { + vec2.set(out._array, x, y); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Vector2} out + * @param {qtek.math.Vector2} b + * @return {qtek.math.Vector2} + */ + Vector2.copy = function(out, b) { + vec2.copy(out._array, b._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Vector3} out + * @param {qtek.math.Vector2} a + * @param {qtek.math.Vector2} b + * @return {qtek.math.Vector2} + */ + Vector2.cross = function(out, a, b) { + vec2.cross(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + /** + * @param {qtek.math.Vector2} a + * @param {qtek.math.Vector2} b + * @return {number} + */ + Vector2.dist = function(a, b) { + return vec2.distance(a._array, b._array); + }; + /** + * @method + * @param {qtek.math.Vector2} a + * @param {qtek.math.Vector2} b + * @return {number} + */ + Vector2.distance = Vector2.dist; + /** + * @param {qtek.math.Vector2} out + * @param {qtek.math.Vector2} a + * @param {qtek.math.Vector2} b + * @return {qtek.math.Vector2} + */ + Vector2.div = function(out, a, b) { + vec2.divide(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + /** + * @method + * @param {qtek.math.Vector2} out + * @param {qtek.math.Vector2} a + * @param {qtek.math.Vector2} b + * @return {qtek.math.Vector2} + */ + Vector2.divide = Vector2.div; + /** + * @param {qtek.math.Vector2} a + * @param {qtek.math.Vector2} b + * @return {number} + */ + Vector2.dot = function(a, b) { + return vec2.dot(a._array, b._array); + }; + + /** + * @param {qtek.math.Vector2} a + * @return {number} + */ + Vector2.len = function(b) { + return vec2.length(b._array); + }; + + // Vector2.length = Vector2.len; + + /** + * @param {qtek.math.Vector2} out + * @param {qtek.math.Vector2} a + * @param {qtek.math.Vector2} b + * @param {number} t + * @return {qtek.math.Vector2} + */ + Vector2.lerp = function(out, a, b, t) { + vec2.lerp(out._array, a._array, b._array, t); + out._dirty = true; + return out; + }; + /** + * @param {qtek.math.Vector2} out + * @param {qtek.math.Vector2} a + * @param {qtek.math.Vector2} b + * @return {qtek.math.Vector2} + */ + Vector2.min = function(out, a, b) { + vec2.min(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Vector2} out + * @param {qtek.math.Vector2} a + * @param {qtek.math.Vector2} b + * @return {qtek.math.Vector2} + */ + Vector2.max = function(out, a, b) { + vec2.max(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + /** + * @param {qtek.math.Vector2} out + * @param {qtek.math.Vector2} a + * @param {qtek.math.Vector2} b + * @return {qtek.math.Vector2} + */ + Vector2.mul = function(out, a, b) { + vec2.multiply(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + /** + * @method + * @param {qtek.math.Vector2} out + * @param {qtek.math.Vector2} a + * @param {qtek.math.Vector2} b + * @return {qtek.math.Vector2} + */ + Vector2.multiply = Vector2.mul; + /** + * @param {qtek.math.Vector2} out + * @param {qtek.math.Vector2} a + * @return {qtek.math.Vector2} + */ + Vector2.negate = function(out, a) { + vec2.negate(out._array, a._array); + out._dirty = true; + return out; + }; + /** + * @param {qtek.math.Vector2} out + * @param {qtek.math.Vector2} a + * @return {qtek.math.Vector2} + */ + Vector2.normalize = function(out, a) { + vec2.normalize(out._array, a._array); + out._dirty = true; + return out; + }; + /** + * @param {qtek.math.Vector2} out + * @param {number} scale + * @return {qtek.math.Vector2} + */ + Vector2.random = function(out, scale) { + vec2.random(out._array, scale); + out._dirty = true; + return out; + }; + /** + * @param {qtek.math.Vector2} out + * @param {qtek.math.Vector2} a + * @param {number} scale + * @return {qtek.math.Vector2} + */ + Vector2.scale = function(out, a, scale) { + vec2.scale(out._array, a._array, scale); + out._dirty = true; + return out; + }; + /** + * @param {qtek.math.Vector2} out + * @param {qtek.math.Vector2} a + * @param {qtek.math.Vector2} b + * @param {number} scale + * @return {qtek.math.Vector2} + */ + Vector2.scaleAndAdd = function(out, a, b, scale) { + vec2.scaleAndAdd(out._array, a._array, b._array, scale); + out._dirty = true; + return out; + }; + /** + * @param {qtek.math.Vector2} a + * @param {qtek.math.Vector2} b + * @return {number} + */ + Vector2.sqrDist = function(a, b) { + return vec2.sqrDist(a._array, b._array); + }; + /** + * @method + * @param {qtek.math.Vector2} a + * @param {qtek.math.Vector2} b + * @return {number} + */ + Vector2.squaredDistance = Vector2.sqrDist; + + /** + * @param {qtek.math.Vector2} a + * @return {number} + */ + Vector2.sqrLen = function(a) { + return vec2.sqrLen(a._array); + }; + /** + * @method + * @param {qtek.math.Vector2} a + * @return {number} + */ + Vector2.squaredLength = Vector2.sqrLen; + + /** + * @param {qtek.math.Vector2} out + * @param {qtek.math.Vector2} a + * @param {qtek.math.Vector2} b + * @return {qtek.math.Vector2} + */ + Vector2.sub = function(out, a, b) { + vec2.subtract(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + /** + * @method + * @param {qtek.math.Vector2} out + * @param {qtek.math.Vector2} a + * @param {qtek.math.Vector2} b + * @return {qtek.math.Vector2} + */ + Vector2.subtract = Vector2.sub; + /** + * @param {qtek.math.Vector2} out + * @param {qtek.math.Vector2} a + * @param {qtek.math.Matrix2} m + * @return {qtek.math.Vector2} + */ + Vector2.transformMat2 = function(out, a, m) { + vec2.transformMat2(out._array, a._array, m._array); + out._dirty = true; + return out; + }; + /** + * @param {qtek.math.Vector2} out + * @param {qtek.math.Vector2} a + * @param {qtek.math.Matrix2d} m + * @return {qtek.math.Vector2} + */ + Vector2.transformMat2d = function(out, a, m) { + vec2.transformMat2d(out._array, a._array, m._array); + out._dirty = true; + return out; + }; + /** + * @param {qtek.math.Vector2} out + * @param {qtek.math.Vector2} a + * @param {Matrix3} m + * @return {qtek.math.Vector2} + */ + Vector2.transformMat3 = function(out, a, m) { + vec2.transformMat3(out._array, a._array, m._array); + out._dirty = true; + return out; + }; + /** + * @param {qtek.math.Vector2} out + * @param {qtek.math.Vector2} a + * @param {qtek.math.Matrix4} m + * @return {qtek.math.Vector2} + */ + Vector2.transformMat4 = function(out, a, m) { + vec2.transformMat4(out._array, a._array, m._array); + out._dirty = true; + return out; + }; + + module.exports = Vector2; + + + +/***/ }, +/* 14 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @fileoverview gl-matrix - High performance matrix and vector operations + * @author Brandon Jones + * @author Colin MacKenzie IV + * @version 2.2.2 + */ + + /* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + + (function(_global) { + "use strict"; + + var shim = {}; + if (false) { + if(typeof define == 'function' && typeof define.amd == 'object' && define.amd) { + shim.exports = {}; + define(function() { + return shim.exports; + }); + } else { + // gl-matrix lives in a browser, define its namespaces in global + shim.exports = typeof(window) !== 'undefined' ? window : _global; + } + } + else { + // gl-matrix lives in commonjs, define its namespaces in exports + shim.exports = exports; + } + + (function(exports) { + /* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + + if(!GLMAT_EPSILON) { + var GLMAT_EPSILON = 0.000001; + } + if(!GLMAT_ARRAY_TYPE) { + var GLMAT_ARRAY_TYPE = (typeof Float32Array !== 'undefined') ? Float32Array : Array; + } -define('qtek/animation/Clip',['require','./easing'],function(require) { - - - - var Easing = require('./easing'); - - /** - * @constructor - * @alias qtek.animation.Clip - * @param {Object} [opts] - * @param {Object} [opts.target] - * @param {number} [opts.life] - * @param {number} [opts.delay] - * @param {number} [opts.gap] - * @param {number} [opts.playbackRatio] - * @param {boolean|number} [opts.loop] If loop is a number, it indicate the loop count of animation - * @param {string|Function} [opts.easing] - * @param {Function} [opts.onframe] - * @param {Function} [opts.ondestroy] - * @param {Function} [opts.onrestart] - */ - var Clip = function(opts) { - - opts = opts || {}; - - /** - * @type {string} - */ - this.name = opts.name || ''; - - /** - * @type {Object} - */ - this.target = opts.target; - - if (typeof(opts.life) !== 'undefined') { - /** - * @type {number} - */ - this.life = opts.life; - } - if (typeof(opts.delay) !== 'undefined') { - /** - * @type {number} - */ - this.delay = opts.delay; - } - if (typeof(opts.gap) !== 'undefined') { - /** - * @type {number} - */ - this.gap = opts.gap; - } - - if (typeof(opts.playbackRatio) !== 'undefined') { - /** - * @type {number} - */ - this.playbackRatio = opts.playbackRatio; - } else { - this.playbackRatio = 1; - } - - this._currentTime = new Date().getTime(); - - this._startTime = this._currentTime + this.delay; - - this._elapsedTime = 0; - - this._loop = opts.loop === undefined ? false : opts.loop; - this.setLoop(this._loop); - - if (typeof(opts.easing) !== 'undefined') { - this.setEasing(opts.easing); - } - - if (typeof(opts.onframe) !== 'undefined') { - /** - * @type {Function} - */ - this.onframe = opts.onframe; - } - - if (typeof(opts.ondestroy) !== 'undefined') { - /** - * @type {Function} - */ - this.ondestroy = opts.ondestroy; - } - - if (typeof(opts.onrestart) !== 'undefined') { - /** - * @type {Function} - */ - this.onrestart = opts.onrestart; - } - - }; - - Clip.prototype = { - - gap: 0, - - life: 0, - - delay: 0, - - /** - * @param {number|boolean} loop - */ - setLoop: function(loop) { - this._loop = loop; - if (loop) { - if (typeof(loop) == 'number') { - this._loopRemained = loop; - } else { - this._loopRemained = 1e8; - } - } - }, - - /** - * @param {string|function} easing - */ - setEasing: function(easing) { - if (typeof(easing) === 'string') { - easing = Easing[easing]; - } - this.easing = easing; - }, - - /** - * @param {number} time - * @return {string} - */ - step: function(time) { - if (time < this._startTime) { - this._currentTime = time; - return; - } - - this._elapse(time); - - var percent = this._elapsedTime / this.life; - - if (percent < 0) { - return; - } - if (percent > 1) { - percent = 1; - } - - var schedule; - if (this.easing) { - schedule = this.easing(percent); - }else{ - schedule = percent; - } - this.fire('frame', schedule); - - if (percent == 1) { - if (this._loop && this._loopRemained > 0) { - this._restartInLoop(); - this._loopRemained--; - return 'restart'; - } else { - // Mark this clip to be deleted - // In the animation.update - this._needsRemove = true; - - return 'destroy'; - } - } else { - return null; - } - }, - - /** - * @param {number} time - * @return {string} - */ - setTime: function(time) { - return this.step(time + this._startTime); - }, - - restart: function() { - // If user leave the page for a while, when he gets back - // All clips may be expired and all start from the beginning value(position) - // It is clearly wrong, so we use remainder to add a offset - var time = new Date().getTime(); - this._elapse(time); - - var remainder = this._elapsedTime % this.life; - this._startTime = time - remainder + this.delay; - this._elapsedTime = 0; - this._currentTime = time - remainder; - - this._needsRemove = false; - }, - - _restartInLoop: function () { - var time = new Date().getTime(); - this._startTime = time + this.gap; - this._currentTime = time; - this._elapsedTime = 0; - }, - - _elapse: function(time) { - this._elapsedTime += (time - this._currentTime) * this.playbackRatio; - this._currentTime = time; - }, - - fire: function(eventType, arg) { - var eventName = 'on' + eventType; - if (this[eventName]) { - this[eventName](this.target, arg); - } - }, - - clone: function () { - var clip = new this.constructor(); - clip.name = this.name; - clip._loop = this._loop; - clip._loopRemained = this._loopRemained; - - clip.life = this.life; - clip.gap = this.gap; - clip.delay = this.delay; - - return clip; - } - }; - Clip.prototype.constructor = Clip; - - return Clip; -}); -define('qtek/animation/Animation',['require','./Clip','../core/Base'],function(require) { - - - - var Clip = require('./Clip'); - var Base = require('../core/Base'); - - var requestAnimationFrame = window.requestAnimationFrame - || window.msRequestAnimationFrame - || window.mozRequestAnimationFrame - || window.webkitRequestAnimationFrame - || function(func){setTimeout(func, 16);}; - - var arraySlice = Array.prototype.slice; - - /** - * Animation is global timeline that schedule all clips. each frame animation will set the time of clips to current and update the states of clips - * @constructor qtek.animation.Animation - * @extends qtek.core.Base - * - * @example - * var animation = new qtek.animation.Animation(); - * var node = new qtek.Node(); - * animation.animate(node.position) - * .when(1000, { - * x: 500, - * y: 500 - * }) - * .when(2000, { - * x: 100, - * y: 100 - * }) - * .when(3000, { - * z: 10 - * }) - * .start('spline'); - */ - var Animation = Base.derive(function() { - return /** @lends qtek.animation.Animation# */{ - /** - * stage is an object with render method, each frame if there exists any animating clips, stage.render will be called - * @type {Object} - */ - stage: null, - - _clips: [], - - _running: false, - - _time: 0 - }; - }, - /** @lends qtek.animation.Animation.prototype */ - { - /** - * @param {qtek.animation.Clip} clip - */ - addClip: function(clip) { - this._clips.push(clip); - }, - /** - * @param {qtek.animation.Clip} clip - */ - removeClip: function(clip) { - var idx = this._clips.indexOf(clip); - this._clips.splice(idx, 1); - }, - - _update: function() { - - var time = new Date().getTime(); - var delta = time - this._time; - var clips = this._clips; - var len = clips.length; - - var deferredEvents = []; - var deferredClips = []; - for (var i = 0; i < len; i++) { - var clip = clips[i]; - var e = clip.step(time); - // Throw out the events need to be called after - // stage.render, like destroy - if (e) { - deferredEvents.push(e); - deferredClips.push(clip); - } - } - - // Remove the finished clip - for (var i = 0; i < len;) { - if (clips[i]._needsRemove) { - clips[i] = clips[len-1]; - clips.pop(); - len--; - } else { - i++; - } - } - - len = deferredEvents.length; - for (var i = 0; i < len; i++) { - deferredClips[i].fire(deferredEvents[i]); - } - - this._time = time; - - this.trigger('frame', delta); - - if (this.stage && this.stage.render) { - this.stage.render(); - } - }, - /** - * Start running animation - */ - start: function() { - var self = this; - - this._running = true; - this._time = new Date().getTime(); - - function step() { - if (self._running) { - - requestAnimationFrame(step); - - self._update(); - } - } - - requestAnimationFrame(step); - }, - /** - * Stop running animation - */ - stop: function() { - this._running = false; - }, - /** - * Remove all clips - */ - removeClipsAll: function() { - this._clips = []; - }, - /** - * Create a deferred animating object - * @param {Object} target - * @param {Object} [options] - * @param {boolean} [options.loop] - * @param {Function} [options.getter] - * @param {Function} [options.setter] - * @param {Function} [options.interpolater] - * @return {qtek.animation.Animation._Deferred} - */ - animate: function(target, options) { - options = options || {}; - var deferred = new Deferred( - target, - options.loop, - options.getter, - options.setter, - options.interpolater - ); - deferred.animation = this; - return deferred; - } - }); - - function _defaultGetter(target, key) { - return target[key]; - } - function _defaultSetter(target, key, value) { - target[key] = value; - } - - function _interpolateNumber(p0, p1, percent) { - return (p1 - p0) * percent + p0; - } - - function _interpolateArray(p0, p1, percent, out, arrDim) { - var len = p0.length; - if (arrDim == 1) { - for (var i = 0; i < len; i++) { - out[i] = _interpolateNumber(p0[i], p1[i], percent); - } - } else { - var len2 = p0[0].length; - for (var i = 0; i < len; i++) { - for (var j = 0; j < len2; j++) { - out[i][j] = _interpolateNumber( - p0[i][j], p1[i][j], percent - ); - } - } - } - } - - function _isArrayLike(data) { - if (typeof(data) == 'undefined') { - return false; - } else if (typeof(data) == 'string') { - return false; - } else { - return typeof(data.length) == 'number'; - } - } - - function _cloneValue(value) { - if (_isArrayLike(value)) { - var len = value.length; - if (_isArrayLike(value[0])) { - var ret = []; - for (var i = 0; i < len; i++) { - ret.push(arraySlice.call(value[i])); - } - return ret; - } else { - return arraySlice.call(value); - } - } else { - return value; - } - } - - function _catmullRomInterpolateArray( - p0, p1, p2, p3, t, t2, t3, out, arrDim - ) { - var len = p0.length; - if (arrDim == 1) { - for (var i = 0; i < len; i++) { - out[i] = _catmullRomInterpolate( - p0[i], p1[i], p2[i], p3[i], t, t2, t3 - ); - } - } else { - var len2 = p0[0].length; - for (var i = 0; i < len; i++) { - for (var j = 0; j < len2; j++) { - out[i][j] = _catmullRomInterpolate( - p0[i][j], p1[i][j], p2[i][j], p3[i][j], - t, t2, t3 - ); - } - } - } - } - - function _catmullRomInterpolate(p0, p1, p2, p3, t, t2, t3) { - var v0 = (p2 - p0) * 0.5; - var v1 = (p3 - p1) * 0.5; - return (2 * (p1 - p2) + v0 + v1) * t3 - + (- 3 * (p1 - p2) - 2 * v0 - v1) * t2 - + v0 * t + p1; - } - - /** - * @description Deferred object can only be created by Animation.prototype.animate method. - * After created, we can use {@link qtek.animation.Animation._Deferred#when} to add all keyframes and {@link qtek.animation.Animation._Deferred#start} it. - * Clips will be automatically created and added to the animation instance which created this deferred object. - * - * @constructor qtek.animation.Animation._Deferred - * - * @param {Object} target - * @param {boolean} loop - * @param {Function} getter - * @param {Function} setter - * @param {Function} interpolater - */ - function Deferred(target, loop, getter, setter, interpolater) { - this._tracks = {}; - this._target = target; - - this._loop = loop || false; - - this._getter = getter || _defaultGetter; - this._setter = setter || _defaultSetter; - - this._interpolater = interpolater || null; - - this._clipCount = 0; - - this._delay = 0; - - this._doneList = []; - - this._onframeList = []; - - this._clipList = []; - } - - Deferred.prototype = { - - constructor: Deferred, - - /** - * @param {number} time Keyframe time using millisecond - * @param {Object} props A key-value object. Value can be number, 1d and 2d array - * @return {qtek.animation.Animation._Deferred} - * @memberOf qtek.animation.Animation._Deferred.prototype - */ - when: function(time, props) { - for (var propName in props) { - if (! this._tracks[propName]) { - this._tracks[propName] = []; - // If time is 0 - // Then props is given initialize value - // Else - // Initialize value from current prop value - if (time !== 0) { - this._tracks[propName].push({ - time: 0, - value: _cloneValue( - this._getter(this._target, propName) - ) - }); - } - } - this._tracks[propName].push({ - time: parseInt(time), - value: props[propName] - }); - } - return this; - }, - /** - * callback when running animation - * @param {Function} callback callback have two args, animating target and current percent - * @return {qtek.animation.Animation._Deferred} - * @memberOf qtek.animation.Animation._Deferred.prototype - */ - during: function(callback) { - this._onframeList.push(callback); - return this; - }, - /** - * Start the animation - * @param {string|function} easing - * @return {qtek.animation.Animation._Deferred} - * @memberOf qtek.animation.Animation._Deferred.prototype - */ - start: function(easing) { - - var self = this; - var setter = this._setter; - var getter = this._getter; - var interpolater = this._interpolater; - var onFrameListLen = self._onframeList.length; - var useSpline = easing === 'spline'; - - var ondestroy = function() { - self._clipCount--; - if (self._clipCount === 0) { - // Clear all tracks - self._tracks = {}; - - var len = self._doneList.length; - for (var i = 0; i < len; i++) { - self._doneList[i].call(self); - } - } - }; - - var createTrackClip = function(keyframes, propName) { - var trackLen = keyframes.length; - if (!trackLen) { - return; - } - // Guess data type - var firstVal = keyframes[0].value; - var isValueArray = _isArrayLike(firstVal); - - // For vertices morphing - var arrDim = ( - isValueArray - && _isArrayLike(firstVal[0]) - ) - ? 2 : 1; - // Sort keyframe as ascending - keyframes.sort(function(a, b) { - return a.time - b.time; - }); - - var trackMaxTime = keyframes[trackLen - 1].time; - // Percents of each keyframe - var kfPercents = []; - // Value of each keyframe - var kfValues = []; - for (var i = 0; i < trackLen; i++) { - kfPercents.push(keyframes[i].time / trackMaxTime); - kfValues.push(keyframes[i].value); - } - - // Cache the key of last frame to speed up when - // animation playback is sequency - var cacheKey = 0; - var cachePercent = 0; - var start; - var i, w; - var p0, p1, p2, p3; - - var onframe = function(target, percent) { - // Find the range keyframes - // kf1-----kf2---------current--------kf3 - // find kf2(i) and kf3(i+1) and do interpolation - if (percent < cachePercent) { - // Start from next key - start = Math.min(cacheKey + 1, trackLen - 1); - for (i = start; i >= 0; i--) { - if (kfPercents[i] <= percent) { - break; - } - } - i = Math.min(i, trackLen-2); - } else { - for (i = cacheKey; i < trackLen; i++) { - if (kfPercents[i] > percent) { - break; - } - } - i = Math.min(i-1, trackLen-2); - } - cacheKey = i; - cachePercent = percent; - - var range = (kfPercents[i+1] - kfPercents[i]); - if (range === 0) { - return; - } else { - w = (percent - kfPercents[i]) / range; - } - if (useSpline) { - p1 = kfValues[i]; - p0 = kfValues[i === 0 ? i : i - 1]; - p2 = kfValues[i > trackLen - 2 ? trackLen - 1 : i + 1]; - p3 = kfValues[i > trackLen - 3 ? trackLen - 1 : i + 2]; - if (interpolater) { - setter( - target, - propName, - interpolater( - getter(target, propName), - p0, p1, p2, p3, w - ) - ); - } else if (isValueArray) { - _catmullRomInterpolateArray( - p0, p1, p2, p3, w, w*w, w*w*w, - getter(target, propName), - arrDim - ); - } else { - setter( - target, - propName, - _catmullRomInterpolate(p0, p1, p2, p3, w, w*w, w*w*w) - ); - } - } else { - if (interpolater) { - setter( - target, - propName, - interpolater( - getter(target, propName), - kfValues[i], - kfValues[i + 1], - w - ) - ); - } - else if (isValueArray) { - _interpolateArray( - kfValues[i], kfValues[i+1], w, - getter(target, propName), - arrDim - ); - } else { - setter( - target, - propName, - _interpolateNumber(kfValues[i], kfValues[i+1], w) - ); - } - } - - for (i = 0; i < onFrameListLen; i++) { - self._onframeList[i](target, percent); - } - }; - - var clip = new Clip({ - target: self._target, - life: trackMaxTime, - loop: self._loop, - delay: self._delay, - onframe: onframe, - ondestroy: ondestroy - }); - - if (easing && easing !== 'spline') { - clip.setEasing(easing); - } - self._clipList.push(clip); - self._clipCount++; - self.animation.addClip(clip); - }; - - for (var propName in this._tracks) { - createTrackClip(this._tracks[propName], propName); - } - return this; - }, - /** - * Stop the animation - * @memberOf qtek.animation.Animation._Deferred.prototype - */ - stop: function() { - for (var i = 0; i < this._clipList.length; i++) { - var clip = this._clipList[i]; - this.animation.removeClip(clip); - } - this._clipList = []; - }, - /** - * Delay given milliseconds - * @param {number} time - * @return {qtek.animation.Animation._Deferred} - * @memberOf qtek.animation.Animation._Deferred.prototype - */ - delay: function(time){ - this._delay = time; - return this; - }, - /** - * Callback after animation finished - * @param {Function} func - * @return {qtek.animation.Animation._Deferred} - * @memberOf qtek.animation.Animation._Deferred.prototype - */ - done: function(func) { - if (func) { - this._doneList.push(func); - } - return this; - }, - /** - * Get all clips created in start method. - * @return {qtek.animation.Clip[]} - * @memberOf qtek.animation.Animation._Deferred.prototype - */ - getClips: function() { - return this._clipList; - } - }; - - Animation._Deferred = Deferred; - - return Animation; -}); + if(!GLMAT_RANDOM) { + var GLMAT_RANDOM = Math.random; + } -// 1D Blend clip of blend tree -// http://docs.unity3d.com/Documentation/Manual/1DBlending.html -define('qtek/animation/Blend1DClip',['require','./Clip'],function(require) { - - - - var Clip = require('./Clip'); - - var clipSortFunc = function(a, b) { - return a.position < b.position; - }; - - /** - * @typedef {Object} qtek.animation.Blend1DClip.IClipInput - * @property {number} position - * @property {qtek.animation.Clip} clip - * @property {number} offset - */ - - /** - * 1d blending node in animation blend tree. - * output clip must have blend1D and copy method - * @constructor - * @alias qtek.animation.Blend1DClip - * @extends qtek.animation.Clip - * - * @param {Object} [opts] - * @param {string} [opts.name] - * @param {Object} [opts.target] - * @param {number} [opts.life] - * @param {number} [opts.delay] - * @param {number} [opts.gap] - * @param {number} [opts.playbackRatio] - * @param {boolean|number} [opts.loop] If loop is a number, it indicate the loop count of animation - * @param {string|Function} [opts.easing] - * @param {Function} [opts.onframe] - * @param {Function} [opts.ondestroy] - * @param {Function} [opts.onrestart] - * @param {object[]} [opts.inputs] - * @param {number} [opts.position] - * @param {qtek.animation.Clip} [opts.output] - */ - var Blend1DClip = function(opts) { - - opts = opts || {}; - - Clip.call(this, opts); - /** - * Output clip must have blend1D and copy method - * @type {qtek.animation.Clip} - */ - this.output = opts.output || null; - /** - * @type {qtek.animation.Blend1DClip.IClipInput[]} - */ - this.inputs = opts.inputs || []; - /** - * @type {number} - */ - this.position = 0; - - this._cacheKey = 0; - this._cachePosition = -Infinity; - - this.inputs.sort(clipSortFunc); - }; - - Blend1DClip.prototype = new Clip(); - Blend1DClip.prototype.constructor = Blend1DClip; - - /** - * @param {number} position - * @param {qtek.animation.Clip} inputClip - * @param {number} [offset] - * @return {qtek.animation.Blend1DClip.IClipInput} - */ - Blend1DClip.prototype.addInput = function(position, inputClip, offset) { - var obj = { - position: position, - clip: inputClip, - offset: offset || 0 - }; - this.life = Math.max(inputClip.life, this.life); - - if (!this.inputs.length) { - this.inputs.push(obj); - return obj; - } - var len = this.inputs.length; - if (this.inputs[0].position > position) { - this.inputs.unshift(obj); - } else if (this.inputs[len - 1].position <= position) { - this.inputs.push(obj); - } else { - var key = this._findKey(position); - this.inputs.splice(key, obj); - } - - return obj; - }; - - Blend1DClip.prototype.step = function(time) { - - var ret = Clip.prototype.step.call(this, time); - - if (ret !== 'destroy') { - this.setTime(this._elapsedTime); - } - - return ret; - }; - - Blend1DClip.prototype.setTime = function(time) { - var position = this.position; - var inputs = this.inputs; - var len = inputs.length; - var min = inputs[0].position; - var max = inputs[len-1].position; - - if (position <= min || position >= max) { - var in0 = position <= min ? inputs[0] : inputs[len-1]; - var clip = in0.clip; - var offset = in0.offset; - clip.setTime((time + offset) % clip.life); - // Input clip is a blend clip - // PENDING - if (clip.output instanceof Clip) { - this.output.copy(clip.output); - } else { - this.output.copy(clip); - } - } else { - var key = this._findKey(position); - var in1 = inputs[key]; - var in2 = inputs[key + 1]; - var clip1 = in1.clip; - var clip2 = in2.clip; - // Set time on input clips - clip1.setTime((time + in1.offset) % clip1.life); - clip2.setTime((time + in2.offset) % clip2.life); - - var w = (this.position - in1.position) / (in2.position - in1.position); - - var c1 = clip1.output instanceof Clip ? clip1.output : clip1; - var c2 = clip2.output instanceof Clip ? clip2.output : clip2; - this.output.blend1D(c1, c2, w); - } - }; - - /** - * Clone a new Blend1D clip - * @param {boolean} cloneInputs True if clone the input clips - * @return {qtek.animation.Blend1DClip} - */ - Blend1DClip.prototype.clone = function (cloneInputs) { - var clip = Clip.prototype.clone.call(this); - clip.output = this.output.clone(); - for (var i = 0; i < this.inputs.length; i++) { - var inputClip = cloneInputs ? this.inputs[i].clip.clone(true) : this.inputs[i].clip; - clip.addInput(this.inputs[i].position, inputClip, this.inputs[i].offset); - } - return clip; - }; - - // Find the key where position in range [inputs[key].position, inputs[key+1].position) - Blend1DClip.prototype._findKey = function(position) { - var key = -1; - var inputs = this.inputs; - var len = inputs.length; - if (this._cachePosition < position) { - for (var i = this._cacheKey; i < len-1; i++) { - if (position >= inputs[i].position && position < inputs[i+1].position) { - key = i; - } - } - } else { - var s = Math.min(len-2, this._cacheKey); - for (var i = s; i >= 0; i--) { - if (position >= inputs[i].position && position < inputs[i+1].position) { - key = i; - } - } - } - if (key >= 0) { - this._cacheKey = key; - this._cachePosition = position; - } - - return key; - }; - - return Blend1DClip; -}); -// Delaunay Triangulation -// Modified from https://github.com/ironwallaby/delaunay - -define('qtek/util/delaunay',['require'],function(require) { - - - - function appendSupertriangleVertices(vertices) { - var xmin = Number.POSITIVE_INFINITY, - ymin = Number.POSITIVE_INFINITY, - xmax = Number.NEGATIVE_INFINITY, - ymax = Number.NEGATIVE_INFINITY, - i, dx, dy, dmax, xmid, ymid; - - for(i = vertices.length; i--; ) { - if(vertices[i][0] < xmin) xmin = vertices[i][0]; - if(vertices[i][0] > xmax) xmax = vertices[i][0]; - if(vertices[i][1] < ymin) ymin = vertices[i][1]; - if(vertices[i][1] > ymax) ymax = vertices[i][1]; - } - - dx = xmax - xmin; - dy = ymax - ymin; - dmax = Math.max(dx, dy); - xmid = xmin + dx * 0.5; - ymid = ymin + dy * 0.5; - - vertices.push( - [xmid - 20 * dmax, ymid - dmax], - [xmid , ymid + 20 * dmax], - [xmid + 20 * dmax, ymid - dmax] - ); - } - - function triangle(vertices, i, j, k) { - var a = vertices[i], - b = vertices[j], - c = vertices[k], - A = b[0] - a[0], - B = b[1] - a[1], - C = c[0] - a[0], - D = c[1] - a[1], - E = A * (a[0] + b[0]) + B * (a[1] + b[1]), - F = C * (a[0] + c[0]) + D * (a[1] + c[1]), - G = 2 * (A * (c[1] - b[1]) - B * (c[0] - b[0])), - minx, miny, dx, dy, x, y; - - /* If the points of the triangle are collinear, then just find the - * extremes and use the midpoint as the center of the circumcircle. */ - if (Math.abs(G) < 0.000001) { - minx = Math.min(a[0], b[0], c[0]); - miny = Math.min(a[1], b[1], c[1]); - dx = (Math.max(a[0], b[0], c[0]) - minx) * 0.5; - dy = (Math.max(a[1], b[1], c[1]) - miny) * 0.5; - x = minx + dx; - y = miny + dy; - } - else { - x = (D*E - B*F) / G; - y = (A*F - C*E) / G; - dx = x - a[0]; - dy = y - a[1]; - } - - return {i: i, j: j, k: k, x: x, y: y, r: dx * dx + dy * dy}; - } - - function dedup(edges) { - var j = edges.length, - a, b, i, m, n; - - outer: while (j) { - b = edges[--j]; - a = edges[--j]; - i = j; - while (i) { - n = edges[--i] - m = edges[--i] - if ((a === m && b === n) || (a === n && b === m)) { - edges.splice(j, 2); - edges.splice(i, 2); - j -= 2; - continue outer; - } - } - } - } - - var delaunay = { - triangulate: function(vertices, key) { - var n = vertices.length, - i, j, indices, open, closed, edges, dx, dy, a, b, c; - - /* Bail if there aren't enough vertices to form any triangles. */ - if (n < 3) { - return []; - } - - /* Slice out the actual vertices from the passed objects. (Duplicate the - * array even if we don't, though, since we need to make a supertriangle - * later on!) */ - vertices = vertices.slice(0); - - if (key) { - for (i = n; i--; ) { - vertices[i] = vertices[i][key]; - } - } - - /* Make an array of indices into the vertex array, sorted by the vertices' - * x-position. */ - indices = new Array(n); - - for (i = n; i--; ) { - indices[i] = i; - } - - indices.sort(function(i, j) { return vertices[j][0] - vertices[i][0]; }); - - /* Next, find the vertices of the supertriangle (which contains all other - * triangles), and append them onto the end of a (copy of) the vertex - * array. */ - appendSupertriangleVertices(vertices); - - /* Initialize the open list (containing the supertriangle and nothing else) - * and the closed list (which is empty since we havn't processed any - * triangles yet). */ - open = [triangle(vertices, n + 0, n + 1, n + 2)]; - closed = []; - edges = []; - - /* Incrementally add each vertex to the mesh. */ - for (i = indices.length; i--; ) { - c = indices[i]; - edges.length = 0; - - /* For each open triangle, check to see if the current point is - * inside it's circumcircle. If it is, remove the triangle and add - * it's edges to an edge list. */ - for (j = open.length; j--; ) { - /* If this point is to the right of this triangle's circumcircle, - * then this triangle should never get checked again. Remove it - * from the open list, add it to the closed list, and skip. */ - dx = vertices[c][0] - open[j].x; - if (dx > 0.0 && dx * dx > open[j].r) { - closed.push(open[j]); - open.splice(j, 1); - continue; - } - - /* If we're outside the circumcircle, skip this triangle. */ - dy = vertices[c][1] - open[j].y; - if (dx * dx + dy * dy > open[j].r) { - continue; - } - - /* Remove the triangle and add it's edges to the edge list. */ - edges.push( - open[j].i, open[j].j, - open[j].j, open[j].k, - open[j].k, open[j].i - ); - open.splice(j, 1); - } - - /* Remove any doubled edges. */ - dedup(edges); - - /* Add a new triangle for each edge. */ - for(j = edges.length; j; ) { - b = edges[--j]; - a = edges[--j]; - open.push(triangle(vertices, a, b, c)); - } - } - - /* Copy any remaining open triangles to the closed list, and then - * remove any triangles that share a vertex with the supertriangle, building - * a list of triplets that represent triangles. */ - for (i = open.length; i--; ) { - closed.push(open[i]); - } - open.length = 0; - - for(i = closed.length; i--; ) { - if(closed[i].i < n && closed[i].j < n && closed[i].k < n) { - var i1 = closed[i].i, - i2 = closed[i].j, - i3 = closed[i].k; - var tri = { - indices : [i1, i2, i3], - vertices : [vertices[i1], vertices[i2], vertices[i3]] - }; - open.push(tri); - } - } - - /* Yay, we're done! */ - return open; - }, - - contains: function(tri, p) { - /* Bounding box test first, for quick rejections. */ - if((p[0] < tri[0][0] && p[0] < tri[1][0] && p[0] < tri[2][0]) || - (p[0] > tri[0][0] && p[0] > tri[1][0] && p[0] > tri[2][0]) || - (p[1] < tri[0][1] && p[1] < tri[1][1] && p[1] < tri[2][1]) || - (p[1] > tri[0][1] && p[1] > tri[1][1] && p[1] > tri[2][1])) { - - return null; - } - - var a = tri[1][0] - tri[0][0], - b = tri[2][0] - tri[0][0], - c = tri[1][1] - tri[0][1], - d = tri[2][1] - tri[0][1], - i = a * d - b * c; - - /* Degenerate tri. */ - if(i === 0.0) { - return null; - } - - var u = (d * (p[0] - tri[0][0]) - b * (p[1] - tri[0][1])) / i, - v = (a * (p[1] - tri[0][1]) - c * (p[0] - tri[0][0])) / i; - - /* If we're outside the tri, fail. */ - if(u < 0.0 || v < 0.0 || (u + v) > 1.0) { - return null; - } - - // normalize - // u = Math.max(0.0, u); - // v = Math.max(0.0, v); - // var s = u + v; - // if (s > 1.0) { - // u = u / s; - // v = v / s; - // } - return [u, v]; - } - } - - return delaunay; -}); -// 2D Blend clip of blend tree -// http://docs.unity3d.com/Documentation/Manual/2DBlending.html -define('qtek/animation/Blend2DClip',['require','./Clip','../util/delaunay','../math/Vector2'],function(require) { - - - - var Clip = require('./Clip'); - - var delaunay = require('../util/delaunay'); - var Vector2 = require('../math/Vector2'); - - /** - * @typedef {Object} qtek.animation.Blend2DClip.IClipInput - * @property {qtek.math.Vector2} position - * @property {qtek.animation.Clip} clip - * @property {number} offset - */ - - /** - * 2d blending node in animation blend tree. - * output clip must have blend2D method - * @constructor - * @alias qtek.animation.Blend2DClip - * @extends qtek.animation.Clip - * - * @param {Object} [opts] - * @param {string} [opts.name] - * @param {Object} [opts.target] - * @param {number} [opts.life] - * @param {number} [opts.delay] - * @param {number} [opts.gap] - * @param {number} [opts.playbackRatio] - * @param {boolean|number} [opts.loop] If loop is a number, it indicate the loop count of animation - * @param {string|Function} [opts.easing] - * @param {Function} [opts.onframe] - * @param {Function} [opts.ondestroy] - * @param {Function} [opts.onrestart] - * @param {object[]} [opts.inputs] - * @param {qtek.math.Vector2} [opts.position] - * @param {qtek.animation.Clip} [opts.output] - */ - var Blend2DClip = function(opts) { - - opts = opts || {}; - - Clip.call(this, opts); - /** - * Output clip must have blend2D method - * @type {qtek.animation.Clip} - */ - this.output = opts.output || null; - /** - * @type {qtek.animation.Blend2DClip.IClipInput[]} - */ - this.inputs = opts.inputs || []; - /** - * @type {qtek.math.Vector2} - */ - this.position = new Vector2(); - - this._cacheTriangle = null; - - this._triangles = []; - - this._updateTriangles(); - }; - - Blend2DClip.prototype = new Clip(); - Blend2DClip.prototype.constructor = Blend2DClip; - /** - * @param {qtek.math.Vector2} position - * @param {qtek.animation.Clip} inputClip - * @param {number} [offset] - * @return {qtek.animation.Blend2DClip.IClipInput} - */ - Blend2DClip.prototype.addInput = function(position, inputClip, offset) { - var obj = { - position : position, - clip : inputClip, - offset : offset || 0 - }; - this.inputs.push(obj); - this.life = Math.max(inputClip.life, this.life); - // TODO Change to incrementally adding - this._updateTriangles(); - - return obj; - }; - - // Delaunay triangulate - Blend2DClip.prototype._updateTriangles = function() { - var inputs = this.inputs.map(function(a) { - return a.position; - }); - this._triangles = delaunay.triangulate(inputs, '_array'); - }; - - Blend2DClip.prototype.step = function(time) { - - var ret = Clip.prototype.step.call(this, time); - - if (ret !== 'destroy') { - this.setTime(this._elapsedTime); - } - - return ret; - }; - - Blend2DClip.prototype.setTime = function(time) { - var res = this._findTriangle(this.position); - if (!res) { - return; - } - // In Barycentric - var a = res[1]; // Percent of clip2 - var b = res[2]; // Percent of clip3 - - var tri = res[0]; - - var in1 = this.inputs[tri.indices[0]]; - var in2 = this.inputs[tri.indices[1]]; - var in3 = this.inputs[tri.indices[2]]; - var clip1 = in1.clip; - var clip2 = in2.clip; - var clip3 = in3.clip; - - clip1.setTime((time + in1.offset) % clip1.life); - clip2.setTime((time + in2.offset) % clip2.life); - clip3.setTime((time + in3.offset) % clip3.life); - - var c1 = clip1.output instanceof Clip ? clip1.output : clip1; - var c2 = clip2.output instanceof Clip ? clip2.output : clip2; - var c3 = clip3.output instanceof Clip ? clip3.output : clip3; - - this.output.blend2D(c1, c2, c3, a, b); - }; - - /** - * Clone a new Blend2D clip - * @param {boolean} cloneInputs True if clone the input clips - * @return {qtek.animation.Blend2DClip} - */ - Blend2DClip.prototype.clone = function (cloneInputs) { - var clip = Clip.prototype.clone.call(this); - clip.output = this.output.clone(); - for (var i = 0; i < this.inputs.length; i++) { - var inputClip = cloneInputs ? this.inputs[i].clip.clone(true) : this.inputs[i].clip; - clip.addInput(this.inputs[i].position, inputClip, this.inputs[i].offset); - } - return clip; - }; - - Blend2DClip.prototype._findTriangle = function(position) { - if (this._cacheTriangle) { - var res = delaunay.contains(this._cacheTriangle.vertices, position._array); - if (res) { - return [this._cacheTriangle, res[0], res[1]]; - } - } - for (var i = 0; i < this._triangles.length; i++) { - var tri = this._triangles[i]; - var res = delaunay.contains(tri.vertices, this.position._array); - if (res) { - this._cacheTriangle = tri; - return [tri, res[0], res[1]]; - } - } - }; - - return Blend2DClip; -}); -define('qtek/animation/TransformClip',['require','./Clip','../dep/glmatrix'],function(require) { - - - - var Clip = require('./Clip'); - - var glMatrix = require('../dep/glmatrix'); - var quat = glMatrix.quat; - var vec3 = glMatrix.vec3; - - function keyframeSort(a, b) { - return a.time - b.time; - } - - /** - * @constructor - * @alias qtek.animation.TransformClip - * @extends qtek.animation.Clip - * - * @param {Object} [opts] - * @param {string} [opts.name] - * @param {Object} [opts.target] - * @param {number} [opts.life] - * @param {number} [opts.delay] - * @param {number} [opts.gap] - * @param {number} [opts.playbackRatio] - * @param {boolean|number} [opts.loop] If loop is a number, it indicate the loop count of animation - * @param {string|Function} [opts.easing] - * @param {Function} [opts.onframe] - * @param {Function} [opts.ondestroy] - * @param {Function} [opts.onrestart] - * @param {object[]} [opts.keyFrames] - */ - var TransformClip = function(opts) { - - opts = opts || {}; - - Clip.call(this, opts); - - //[{ - // time: //ms - // position: // optional - // rotation: // optional - // scale: // optional - //}] - this.keyFrames = []; - if (opts.keyFrames) { - this.addKeyFrames(opts.keyFrames); - } - - /** - * @type {Float32Array} - */ - this.position = vec3.create(); - /** - * Rotation is represented by a quaternion - * @type {Float32Array} - */ - this.rotation = quat.create(); - /** - * @type {Float32Array} - */ - this.scale = vec3.fromValues(1, 1, 1); - - this._cacheKey = 0; - this._cacheTime = 0; - }; - - TransformClip.prototype = Object.create(Clip.prototype); - - TransformClip.prototype.constructor = TransformClip; - - TransformClip.prototype.step = function(time) { - - var ret = Clip.prototype.step.call(this, time); - - if (ret !== 'destroy') { - this.setTime(this._elapsedTime); - } - - return ret; - }; - - TransformClip.prototype.setTime = function(time) { - this._interpolateField(time, 'position'); - this._interpolateField(time, 'rotation'); - this._interpolateField(time, 'scale'); - }; - /** - * Add a key frame - * @param {Object} kf - */ - TransformClip.prototype.addKeyFrame = function(kf) { - for (var i = 0; i < this.keyFrames.length - 1; i++) { - var prevFrame = this.keyFrames[i]; - var nextFrame = this.keyFrames[i+1]; - if (prevFrame.time <= kf.time && nextFrame.time >= kf.time) { - this.keyFrames.splice(i, 0, kf); - return i; - } - } - - this.life = kf.time; - this.keyFrames.push(kf); - }; - - /** - * Add keyframes - * @param {object[]} kfs - */ - TransformClip.prototype.addKeyFrames = function(kfs) { - for (var i = 0; i < kfs.length; i++) { - this.keyFrames.push(kfs[i]); - } - - this.keyFrames.sort(keyframeSort); - - this.life = this.keyFrames[this.keyFrames.length - 1].time; - }; - - TransformClip.prototype._interpolateField = function(time, fieldName) { - var kfs = this.keyFrames; - var len = kfs.length; - var start; - var end; - - if (!kfs.length) { - return; - } - if (time < kfs[0].time || time > kfs[kfs.length-1].time) { - return; - } - if (time < this._cacheTime) { - var s = this._cacheKey >= len-1 ? len-1 : this._cacheKey+1; - for (var i = s; i >= 0; i--) { - if (kfs[i].time <= time && kfs[i][fieldName]) { - start = kfs[i]; - this._cacheKey = i; - this._cacheTime = time; - } else if (kfs[i][fieldName]) { - end = kfs[i]; - break; - } - } - } else { - for (var i = this._cacheKey; i < len; i++) { - if (kfs[i].time <= time && kfs[i][fieldName]) { - start = kfs[i]; - this._cacheKey = i; - this._cacheTime = time; - } else if (kfs[i][fieldName]) { - end = kfs[i]; - break; - } - } - } - - if (start && end) { - var percent = (time-start.time) / (end.time-start.time); - percent = Math.max(Math.min(percent, 1), 0); - if (fieldName === 'rotation') { - quat.slerp(this[fieldName], start[fieldName], end[fieldName], percent); - } else { - vec3.lerp(this[fieldName], start[fieldName], end[fieldName], percent); - } - } else { - this._cacheKey = 0; - this._cacheTime = 0; - } - }; - /** - * 1D blending between two clips - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 - * @param {number} w - */ - TransformClip.prototype.blend1D = function(c1, c2, w) { - vec3.lerp(this.position, c1.position, c2.position, w); - vec3.lerp(this.scale, c1.scale, c2.scale, w); - quat.slerp(this.rotation, c1.rotation, c2.rotation, w); - }; - - /** - * 2D blending between three clips - * @method - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c3 - * @param {number} f - * @param {number} g - */ - TransformClip.prototype.blend2D = (function() { - var q1 = quat.create(); - var q2 = quat.create(); - return function(c1, c2, c3, f, g) { - var a = 1 - f - g; - - this.position[0] = c1.position[0] * a + c2.position[0] * f + c3.position[0] * g; - this.position[1] = c1.position[1] * a + c2.position[1] * f + c3.position[1] * g; - this.position[2] = c1.position[2] * a + c2.position[2] * f + c3.position[2] * g; - - this.scale[0] = c1.scale[0] * a + c2.scale[0] * f + c3.scale[0] * g; - this.scale[1] = c1.scale[1] * a + c2.scale[1] * f + c3.scale[1] * g; - this.scale[2] = c1.scale[2] * a + c2.scale[2] * f + c3.scale[2] * g; - - // http://msdn.microsoft.com/en-us/library/windows/desktop/bb205403(v=vs.85).aspx - // http://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.quaternion.xmquaternionbarycentric(v=vs.85).aspx - var s = f + g; - if (s === 0) { - quat.copy(this.rotation, c1.rotation); - } else { - quat.slerp(q1, c1.rotation, c2.rotation, s); - quat.slerp(q2, c1.rotation, c3.rotation, s); - quat.slerp(this.rotation, q1, q2, g / s); - } - }; - })(); - - /** - * Additive blending between two clips - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 - */ - TransformClip.prototype.additiveBlend = function(c1, c2) { - vec3.add(this.position, c1.position, c2.position); - vec3.add(this.scale, c1.scale, c2.scale); - quat.multiply(this.rotation, c2.rotation, c1.rotation); - }; - - /** - * Subtractive blending between two clips - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 - */ - TransformClip.prototype.subtractiveBlend = function(c1, c2) { - vec3.sub(this.position, c1.position, c2.position); - vec3.sub(this.scale, c1.scale, c2.scale); - quat.invert(this.rotation, c2.rotation); - quat.multiply(this.rotation, this.rotation, c1.rotation); - }; - - /** - * @param {number} startTime - * @param {number} endTime - * @param {boolean} isLoop - */ - TransformClip.prototype.getSubClip = function(startTime, endTime) { - // TODO - console.warn('TODO'); - }; - - /** - * Clone a new TransformClip - * @return {qtek.animation.TransformClip} - */ - TransformClip.prototype.clone = function () { - var clip = Clip.prototype.clone.call(this); - clip.keyFrames = this.keyFrames; - - vec3.copy(clip.position, this.position); - quat.copy(clip.rotation, this.rotation); - vec3.copy(clip.scale, this.scale); - - return clip; - }; - - - return TransformClip; -}); -// Sampler clip is especially for the animation sampler in glTF -// Use Typed Array can reduce a lot of heap memory -define('qtek/animation/SamplerClip',['require','./Clip','./TransformClip','../dep/glmatrix'],function(require) { - - - - var Clip = require('./Clip'); - var TransformClip = require('./TransformClip'); - - var glMatrix = require('../dep/glmatrix'); - var quat = glMatrix.quat; - var vec3 = glMatrix.vec3; - - // lerp function with offset in large array - function vec3lerp(out, a, b, t, oa, ob) { - var ax = a[oa]; - var ay = a[oa + 1]; - var az = a[oa + 2]; - out[0] = ax + t * (b[ob] - ax); - out[1] = ay + t * (b[ob + 1] - ay); - out[2] = az + t * (b[ob + 2] - az); - - return out; - } - - function quatSlerp(out, a, b, t, oa, ob) { - // benchmarks: - // http://jsperf.com/quaternion-slerp-implementations - - var ax = a[0 + oa], ay = a[1 + oa], az = a[2 + oa], aw = a[3 + oa], - bx = b[0 + ob], by = b[1 + ob], bz = b[2 + ob], bw = b[3 + ob]; - - var omega, cosom, sinom, scale0, scale1; - - // calc cosine - cosom = ax * bx + ay * by + az * bz + aw * bw; - // adjust signs (if necessary) - if (cosom < 0.0) { - cosom = -cosom; - bx = - bx; - by = - by; - bz = - bz; - bw = - bw; - } - // calculate coefficients - if ((1.0 - cosom) > 0.000001) { - // standard case (slerp) - omega = Math.acos(cosom); - sinom = Math.sin(omega); - scale0 = Math.sin((1.0 - t) * omega) / sinom; - scale1 = Math.sin(t * omega) / sinom; - } else { - // 'from' and 'to' quaternions are very close - // ... so we can do a linear interpolation - scale0 = 1.0 - t; - scale1 = t; - } - // calculate final values - out[0] = scale0 * ax + scale1 * bx; - out[1] = scale0 * ay + scale1 * by; - out[2] = scale0 * az + scale1 * bz; - out[3] = scale0 * aw + scale1 * bw; - - return out; - } - - /** - * @constructor - * @alias qtek.animation.SamplerClip - * @extends qtek.animation.Clip - * - * @param {Object} [opts] - * @param {string} [opts.name] - * @param {Object} [opts.target] - * @param {number} [opts.life] - * @param {number} [opts.delay] - * @param {number} [opts.gap] - * @param {number} [opts.playbackRatio] - * @param {boolean|number} [opts.loop] If loop is a number, it indicate the loop count of animation - * @param {string|Function} [opts.easing] - * @param {Function} [opts.onframe] - * @param {Function} [opts.ondestroy] - * @param {Function} [opts.onrestart] - */ - var SamplerClip = function(opts) { - - opts = opts || {}; - - Clip.call(this, opts); - - /** - * @type {Float32Array} - */ - this.position = vec3.create(); - /** - * Rotation is represented by a quaternion - * @type {Float32Array} - */ - this.rotation = quat.create(); - /** - * @type {Float32Array} - */ - this.scale = vec3.fromValues(1, 1, 1); - - this.channels = { - time: null, - position: null, - rotation: null, - scale: null - }; - - this._cacheKey = 0; - this._cacheTime = 0; - }; - - SamplerClip.prototype = Object.create(Clip.prototype); - - SamplerClip.prototype.constructor = SamplerClip; - - SamplerClip.prototype.step = function(time) { - - var ret = Clip.prototype.step.call(this, time); - - if (ret !== 'destroy') { - this.setTime(this._elapsedTime); - } - - return ret; - }; - - SamplerClip.prototype.setTime = function(time) { - if (!this.channels.time) { - return; - } - var channels = this.channels; - var len = channels.time.length; - var key = -1; - if (time < this._cacheTime) { - var s = Math.min(len - 2, this._cacheKey); - for (var i = s; i >= 0; i--) { - if (channels.time[i - 1] <= time && channels.time[i] > time) { - key = i - 1; - break; - } - } - } else { - for (var i = this._cacheKey; i < len-1; i++) { - if (channels.time[i] <= time && channels.time[i + 1] > time) { - key = i; - break; - } - } - } - - if (key > -1) { - this._cacheKey = key; - this._cacheTime = time; - var start = key; - var end = key + 1; - var startTime = channels.time[start]; - var endTime = channels.time[end]; - var percent = (time - startTime) / (endTime - startTime); - - if (channels.rotation) { - quatSlerp(this.rotation, channels.rotation, channels.rotation, percent, start * 4, end * 4); - } - if (channels.position) { - vec3lerp(this.position, channels.position, channels.position, percent, start * 3, end * 3); - } - if (channels.scale) { - vec3lerp(this.scale, channels.scale, channels.scale, percent, start * 3, end * 3); - } - } - // Loop handling - if (key == len - 2) { - this._cacheKey = 0; - this._cacheTime = 0; - } - }; - - /** - * @param {number} startTime - * @param {number} endTime - * @return {qtek.animation.SamplerClip} - */ - SamplerClip.prototype.getSubClip = function(startTime, endTime) { - - var subClip = new SamplerClip({ - name: this.name - }); - var minTime = this.channels.time[0]; - startTime = Math.min(Math.max(startTime, minTime), this.life); - endTime = Math.min(Math.max(endTime, minTime), this.life); - - var rangeStart = this._findRange(startTime); - var rangeEnd = this._findRange(endTime); - - var count = rangeEnd[0] - rangeStart[0] + 1; - if (rangeStart[1] === 0 && rangeEnd[1] === 0) { - count -= 1; - } - if (this.channels.rotation) { - subClip.channels.rotation = new Float32Array(count * 4); - } - if (this.channels.position) { - subClip.channels.position = new Float32Array(count * 3); - } - if (this.channels.scale) { - subClip.channels.scale = new Float32Array(count * 3); - } - if (this.channels.time) { - subClip.channels.time = new Float32Array(count); - } - // Clip at the start - this.setTime(startTime); - for (var i = 0; i < 3; i++) { - subClip.channels.rotation[i] = this.rotation[i]; - subClip.channels.position[i] = this.position[i]; - subClip.channels.scale[i] = this.scale[i]; - } - subClip.channels.time[0] = 0; - subClip.channels.rotation[3] = this.rotation[3]; - - for (var i = 1; i < count-1; i++) { - var i2; - for (var j = 0; j < 3; j++) { - i2 = rangeStart[0] + i; - subClip.channels.rotation[i * 4 + j] = this.channels.rotation[i2 * 4 + j]; - subClip.channels.position[i * 3 + j] = this.channels.position[i2 * 3 + j]; - subClip.channels.scale[i * 3 + j] = this.channels.scale[i2 * 3 + j]; - } - subClip.channels.time[i] = this.channels.time[i2] - startTime; - subClip.channels.rotation[i * 4 + 3] = this.channels.rotation[i2 * 4 + 3]; - } - // Clip at the end - this.setTime(endTime); - for (var i = 0; i < 3; i++) { - subClip.channels.rotation[(count - 1) * 4 + i] = this.rotation[i]; - subClip.channels.position[(count - 1) * 3 + i] = this.position[i]; - subClip.channels.scale[(count - 1) * 3 + i] = this.scale[i]; - } - subClip.channels.time[(count - 1)] = endTime - startTime; - subClip.channels.rotation[(count - 1) * 4 + 3] = this.rotation[3]; - - // TODO set back ? - subClip.life = endTime - startTime; - return subClip; - }; - - SamplerClip.prototype._findRange = function(time) { - var channels = this.channels; - var len = channels.time.length; - var start = -1; - for (var i = 0; i < len - 1; i++) { - if (channels.time[i] <= time && channels.time[i+1] > time) { - start = i; - } - } - var percent = 0; - if (start >= 0) { - var startTime = channels.time[start]; - var endTime = channels.time[start+1]; - var percent = (time-startTime) / (endTime-startTime); - } - // Percent [0, 1) - return [start, percent]; - }; - - /** - * 1D blending between two clips - * @method - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 - * @param {number} w - */ - SamplerClip.prototype.blend1D = TransformClip.prototype.blend1D; - /** - * 2D blending between three clips - * @method - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c3 - * @param {number} f - * @param {number} g - */ - SamplerClip.prototype.blend2D = TransformClip.prototype.blend2D; - /** - * Additive blending between two clips - * @method - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 - */ - SamplerClip.prototype.additiveBlend = TransformClip.prototype.additiveBlend; - /** - * Subtractive blending between two clips - * @method - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 - * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 - */ - SamplerClip.prototype.subtractiveBlend = TransformClip.prototype.subtractiveBlend; - - /** - * Clone a new SamplerClip - * @return {qtek.animation.SamplerClip} - */ - SamplerClip.prototype.clone = function () { - var clip = Clip.prototype.clone.call(this); - clip.channels = { - time: this.channels.time || null, - position: this.channels.position || null, - rotation: this.channels.rotation || null, - scale: this.channels.scale || null - }; - vec3.copy(clip.position, this.position); - quat.copy(clip.rotation, this.rotation); - vec3.copy(clip.scale, this.scale); - - return clip; - - }; - - return SamplerClip; -}); -define('qtek/animation/SkinningClip',['require','./Clip','./TransformClip','../dep/glmatrix'],function(require) { - - - - var Clip = require('./Clip'); - - var TransformClip = require('./TransformClip'); - - var glMatrix = require('../dep/glmatrix'); - var quat = glMatrix.quat; - var vec3 = glMatrix.vec3; - - /** - * @constructor - * @alias qtek.animation.SkinningClip - * - * @extends qtek.animation.Clip - * @param {Object} [opts] - * @param {string} [opts.name] - * @param {Object} [opts.target] - * @param {number} [opts.life] - * @param {number} [opts.delay] - * @param {number} [opts.gap] - * @param {number} [opts.playbackRatio] - * @param {boolean|number} [opts.loop] If loop is a number, it indicate the loop count of animation - * @param {string|function} [opts.easing] - * @param {function} [opts.onframe] - * @param {function} [opts.ondestroy] - * @param {function} [opts.onrestart] - */ - var SkinningClip = function(opts) { - - opts = opts || {}; - - Clip.call(this, opts); - - this.jointClips = []; - - this.life = 0; - if (opts.jointClips && opts.jointClips.length > 0) { - for (var j = 0; j < opts.jointClips.length; j++) { - var jointPoseCfg = opts.jointClips[j]; - var jointClip = new TransformClip({ - keyFrames: jointPoseCfg.keyFrames - }); - jointClip.name = jointPoseCfg.name || ''; - this.jointClips[j] = jointClip; - - this.life = Math.max(jointClip.life, this.life); - } - } - }; - - SkinningClip.prototype = Object.create(Clip.prototype); - - SkinningClip.prototype.constructor = SkinningClip; - - SkinningClip.prototype.step = function(time) { - - var ret = Clip.prototype.step.call(this, time); - - if (ret !== 'destroy') { - this.setTime(this._elapsedTime); - } - - return ret; - }; - - SkinningClip.prototype.setTime = function(time) { - for (var i = 0; i < this.jointClips.length; i++) { - this.jointClips[i].setTime(time); - } - }; - - /** - * @param {qtek.animation.TransformClip|qtek.animation.SamplerClip} jointClip - */ - SkinningClip.prototype.addJointClip = function(jointClip) { - this.jointClips.push(jointClip); - this.life = Math.max(jointClip.life, this.life); - }; - - /** - * @param {qtek.animation.TransformClip|qtek.animation.SamplerClip} jointClip - */ - SkinningClip.prototype.removeJointClip = function(jointClip) { - this.jointClips.splice(this.jointClips.indexOf(jointClip), 1); - }; - - /** - * @param {number} startTime - * @param {number} endTime - * @param {boolean} isLoop - * @return {qtek.animation.SkinningClip} - */ - SkinningClip.prototype.getSubClip = function(startTime, endTime, isLoop) { - var subClip = new SkinningClip({ - name: this.name - }); - - for (var i = 0; i < this.jointClips.length; i++) { - var subJointClip = this.jointClips[i].getSubClip(startTime, endTime); - subClip.addJointClip(subJointClip); - } - - if (isLoop !== undefined) { - subClip.setLoop(isLoop); - } - - return subClip; - }; - - /** - * 1d blending between two skinning clips - * @param {qtek.animation.SkinningClip} clip1 - * @param {qtek.animation.SkinningClip} clip2 - * @param {number} w - */ - SkinningClip.prototype.blend1D = function(clip1, clip2, w) { - for (var i = 0; i < this.jointClips.length; i++) { - var c1 = clip1.jointClips[i]; - var c2 = clip2.jointClips[i]; - var tClip = this.jointClips[i]; - - tClip.blend1D(c1, c2, w); - } - }; - - /** - * Additive blending between two skinning clips - * @param {qtek.animation.SkinningClip} clip1 - * @param {qtek.animation.SkinningClip} clip2 - */ - SkinningClip.prototype.additiveBlend = function(clip1, clip2) { - for (var i = 0; i < this.jointClips.length; i++) { - var c1 = clip1.jointClips[i]; - var c2 = clip2.jointClips[i]; - var tClip = this.jointClips[i]; - - tClip.additiveBlend(c1, c2); - } - }; - - /** - * Subtractive blending between two skinning clips - * @param {qtek.animation.SkinningClip} clip1 - * @param {qtek.animation.SkinningClip} clip2 - */ - SkinningClip.prototype.subtractiveBlend = function(clip1, clip2) { - for (var i = 0; i < this.jointClips.length; i++) { - var c1 = clip1.jointClips[i]; - var c2 = clip2.jointClips[i]; - var tClip = this.jointClips[i]; - - tClip.subtractiveBlend(c1, c2); - } - }; - - /** - * 2D blending between three skinning clips - * @param {qtek.animation.SkinningClip} clip1 - * @param {qtek.animation.SkinningClip} clip2 - * @param {qtek.animation.SkinningClip} clip3 - * @param {number} f - * @param {number} g - */ - SkinningClip.prototype.blend2D = function(clip1, clip2, clip3, f, g) { - for (var i = 0; i < this.jointClips.length; i++) { - var c1 = clip1.jointClips[i]; - var c2 = clip2.jointClips[i]; - var c3 = clip3.jointClips[i]; - var tClip = this.jointClips[i]; - - tClip.blend2D(c1, c2, c3, f, g); - } - }; - - /** - * Copy SRT of all joints clips from another SkinningClip - * @param {qtek.animation.SkinningClip} clip - */ - SkinningClip.prototype.copy = function(clip) { - for (var i = 0; i < this.jointClips.length; i++) { - var sClip = clip.jointClips[i]; - var tClip = this.jointClips[i]; - - vec3.copy(tClip.position, sClip.position); - vec3.copy(tClip.scale, sClip.scale); - quat.copy(tClip.rotation, sClip.rotation); - } - }; - - SkinningClip.prototype.clone = function () { - var clip = Clip.prototype.clone.call(this); - for (var i = 0; i < this.jointClips.length; i++) { - clip.addJointClip(this.jointClips[i].clone()); - } - return clip; - }; - - return SkinningClip; -}); -define('qtek/core/request',['require'],function(require) { - - - - function get(options) { - - var xhr = new XMLHttpRequest(); - - xhr.open('get', options.url); - // With response type set browser can get and put binary data - // https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest/Sending_and_Receiving_Binary_Data - // Default is text, and it can be set - // arraybuffer, blob, document, json, text - xhr.responseType = options.responseType || 'text'; - - if (options.onprogress) { - //https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest/Using_XMLHttpRequest - xhr.onprogress = function(e) { - if (e.lengthComputable) { - var percent = e.loaded / e.total; - options.onprogress(percent, e.loaded, e.total); - } else { - options.onprogress(null); - } - }; - } - xhr.onload = function(e) { - options.onload && options.onload(xhr.response); - }; - if (options.onerror) { - xhr.onerror = options.onerror; - } - xhr.send(null); - } - - return { - get : get - }; -}); -define('qtek/async/Task',['require','../core/mixin/notifier','../core/request','../core/util'],function(require) { - - - - var notifier = require('../core/mixin/notifier'); - var request = require('../core/request'); - var util = require('../core/util'); - - /** - * @constructor - * @alias qtek.async.Task - * @mixes qtek.core.mixin.notifier - */ - var Task = function() { - this._fullfilled = false; - this._rejected = false; - }; - /** - * Task successed - * @param {} data - */ - Task.prototype.resolve = function(data) { - this._fullfilled = true; - this._rejected = false; - this.trigger('success', data); - }; - /** - * Task failed - * @param {} err - */ - Task.prototype.reject = function(err) { - this._rejected = true; - this._fullfilled = false; - this.trigger('error', err); - }; - /** - * If task successed - * @return {boolean} - */ - Task.prototype.isFullfilled = function() { - return this._fullfilled; - }; - /** - * If task failed - * @return {boolean} - */ - Task.prototype.isRejected = function() { - return this._rejected; - }; - /** - * If task finished, either successed or failed - * @return {boolean} - */ - Task.prototype.isSettled = function() { - return this._fullfilled || this._rejected; - }; - - util.extend(Task.prototype, notifier); - - function makeRequestTask(url, responseType) { - var task = new Task(); - request.get({ - url: url, - responseType: responseType, - onload: function(res) { - task.resolve(res); - }, - onerror: function(error) { - task.reject(error); - } - }); - return task; - } - /** - * Make a request task - * @param {string|object|object[]|string[]} url - * @param {string} [responseType] - * @example - * var task = Task.makeRequestTask('./a.json'); - * var task = Task.makeRequestTask({ - * url: 'b.bin', - * responseType: 'arraybuffer' - * }); - * var tasks = Task.makeRequestTask(['./a.json', './b.json']); - * var tasks = Task.makeRequestTask([ - * {url: 'a.json'}, - * {url: 'b.bin', responseType: 'arraybuffer'} - * ]); - * @return {qtek.async.Task|qtek.async.Task[]} - */ - Task.makeRequestTask = function(url, responseType) { - if (typeof url === 'string') { - return makeRequestTask(url, responseType); - } else if (url.url) { // Configure object - var obj = url; - return makeRequestTask(obj.url, obj.responseType); - } else if (url instanceof Array) { // Url list - var urlList = url; - var tasks = []; - urlList.forEach(function(obj) { - var url, responseType; - if (typeof obj === 'string') { - url = obj; - } else if (Object(obj) === obj) { - url = obj.url; - responseType = obj.responseType; - } - tasks.push(makeRequestTask(url, responseType)); - }); - return tasks; - } - }; - /** - * @return {qtek.async.Task} - */ - Task.makeTask = function() { - return new Task(); - }; - - util.extend(Task.prototype, notifier); - - return Task; -}); -define('qtek/async/TaskGroup',['require','../core/util','./Task'],function(require) { - - - - var util = require('../core/util'); - var Task = require('./Task'); - - /** - * @constructor - * @alias qtek.async.TaskGroup - * @extends qtek.async.Task - */ - var TaskGroup = function() { - - Task.apply(this, arguments); - - this._tasks = []; - - this._fulfilledNumber = 0; - - this._rejectedNumber = 0; - }; - - var Ctor = function(){}; - Ctor.prototype = Task.prototype; - TaskGroup.prototype = new Ctor(); - - TaskGroup.prototype.constructor = TaskGroup; - - /** - * Wait for all given tasks successed, task can also be any notifier object which will trigger success and error events. Like {@link qtek.Texture2D}, {@link qtek.TextureCube}, {@link qtek.loader.GLTF}. - * @param {Array.} tasks - * @chainable - * @example - * // Load texture list - * var list = ['a.jpg', 'b.jpg', 'c.jpg'] - * var textures = list.map(function(src) { - * var texture = new qtek.Texture2D(); - * texture.load(src); - * return texture; - * }); - * var taskGroup = new qtek.async.TaskGroup(); - * taskGroup.all(textures).success(function() { - * // Do some thing after all textures loaded - * }); - */ - TaskGroup.prototype.all = function(tasks) { - var count = 0; - var self = this; - var data = []; - this._tasks = tasks; - this._fulfilledNumber = 0; - this._rejectedNumber = 0; - - util.each(tasks, function(task, idx) { - if (!task || !task.once) { - return; - } - count++; - task.once('success', function(res) { - count--; - - self._fulfilledNumber++; - // TODO - // Some tasks like texture, loader are not inherited from task - // We need to set the states here - task._fulfilled = true; - task._rejected = false; - - data[idx] = res; - if (count === 0) { - self.resolve(data); - } - }); - task.once('error', function() { - - self._rejectedNumber ++; - - task._fulfilled = false; - task._rejected = true; - - self.reject(task); - }); - }); - if (count === 0) { - setTimeout(function() { - self.resolve(data); - }); - return this; - } - return this; - }; - /** - * Wait for all given tasks finished, either successed or failed - * @param {Array.} tasks - * @return {qtek.async.TaskGroup} - */ - TaskGroup.prototype.allSettled = function(tasks) { - var count = 0; - var self = this; - var data = []; - if (tasks.length === 0) { - setTimeout(function() { - self.trigger('success', data); - }); - return this; - } - this._tasks = tasks; - - util.each(tasks, function(task, idx) { - if (!task || !task.once) { - return; - } - count++; - task.once('success', function(res) { - count--; - - self._fulfilledNumber++; - - task._fulfilled = true; - task._rejected = false; - - data[idx] = res; - if (count === 0) { - self.resolve(data); - } - }); - task.once('error', function(err) { - count--; - - self._rejectedNumber++; - - task._fulfilled = false; - task._rejected = true; - - // TODO - data[idx] = null; - if (count === 0) { - self.resolve(data); - } - }); - }); - return this; - }; - /** - * Get successed sub tasks number, recursive can be true if sub task is also a TaskGroup. - * @param {boolean} [recursive] - * @return {number} - */ - TaskGroup.prototype.getFulfilledNumber = function(recursive) { - if (recursive) { - var nFulfilled = 0; - for (var i = 0; i < this._tasks.length; i++) { - var task = this._tasks[i]; - if (task instanceof TaskGroup) { - nFulfilled += task.getFulfilledNumber(recursive); - } else if(task._fulfilled) { - nFulfilled += 1; - } - } - return nFulfilled; - } else { - return this._fulfilledNumber; - } - }; - - /** - * Get failed sub tasks number, recursive can be true if sub task is also a TaskGroup. - * @param {boolean} [recursive] - * @return {number} - */ - TaskGroup.prototype.getRejectedNumber = function(recursive) { - if (recursive) { - var nRejected = 0; - for (var i = 0; i < this._tasks.length; i++) { - var task = this._tasks[i]; - if (task instanceof TaskGroup) { - nRejected += task.getRejectedNumber(recursive); - } else if(task._rejected) { - nRejected += 1; - } - } - return nRejected; - } else { - return this._rejectedNumber; - } - }; - - /** - * Get finished sub tasks number, recursive can be true if sub task is also a TaskGroup. - * @param {boolean} [recursive] - * @return {number} - */ - TaskGroup.prototype.getSettledNumber = function(recursive) { - - if (recursive) { - var nSettled = 0; - for (var i = 0; i < this._tasks.length; i++) { - var task = this._tasks[i]; - if (task instanceof TaskGroup) { - nSettled += task.getSettledNumber(recursive); - } else if(task._rejected || task._fulfilled) { - nSettled += 1; - } - } - return nSettled; - } else { - return this._fulfilledNumber + this._rejectedNumber; - } - }; - - /** - * Get all sub tasks number, recursive can be true if sub task is also a TaskGroup. - * @param {boolean} [recursive] - * @return {number} - */ - TaskGroup.prototype.getTaskNumber = function(recursive) { - if (recursive) { - var nTask = 0; - for (var i = 0; i < this._tasks.length; i++) { - var task = this._tasks[i]; - if (task instanceof TaskGroup) { - nTask += task.getTaskNumber(recursive); - } else { - nTask += 1; - } - } - return nTask; - } else { - return this._tasks.length; - } - }; - - return TaskGroup; -}); -define('qtek/camera/Orthographic',['require','../Camera'],function(require) { - - - - var Camera = require('../Camera'); - /** - * @constructor qtek.camera.Orthographic - * @extends qtek.Camera - */ - var Orthographic = Camera.derive( - /** @lends qtek.camera.Orthographic# */ - { - /** - * @type {number} - */ - left: -1, - /** - * @type {number} - */ - right: 1, - /** - * @type {number} - */ - near: -1, - /** - * @type {number} - */ - far: 1, - /** - * @type {number} - */ - top: 1, - /** - * @type {number} - */ - bottom: -1 - }, - /** @lends qtek.camera.Orthographic.prototype */ - { - - updateProjectionMatrix: function() { - this.projectionMatrix.ortho(this.left, this.right, this.bottom, this.top, this.near, this.far); - }, - /** - * @return {qtek.camera.Orthographic} - */ - clone: function() { - var camera = Camera.prototype.clone.call(this); - camera.left = this.left; - camera.right = this.right; - camera.near = this.near; - camera.far = this.far; - camera.top = this.top; - camera.bottom = this.bottom; - - return camera; - } - }); - - return Orthographic; -}); -define('qtek/camera/Perspective',['require','../Camera'],function(require) { - - - - var Camera = require('../Camera'); - - /** - * @constructor qtek.camera.Perspective - * @extends qtek.Camera - */ - var Perspective = Camera.derive( - /** @lends qtek.camera.Perspective# */ - { - /** - * @type {number} - */ - fov: 50, - /** - * @type {number} - */ - aspect: 1, - /** - * @type {number} - */ - near: 0.1, - /** - * @type {number} - */ - far: 2000 - }, - /** @lends qtek.camera.Perspective.prototype */ - { - - updateProjectionMatrix: function() { - var rad = this.fov / 180 * Math.PI; - this.projectionMatrix.perspective(rad, this.aspect, this.near, this.far); - }, - /** - * @return {qtek.camera.Perspective} - */ - clone: function() { - var camera = Camera.prototype.clone.call(this); - camera.fov = this.fov; - camera.aspect = this.aspect; - camera.near = this.near; - camera.far = this.far; - - return camera; - } - }); - - return Perspective; -}); -define('qtek/compositor/Graph',['require','../core/Base'],function(require) { - - - - var Base = require('../core/Base'); - - /** - * @constructor qtek.compositor.Graph - * @extends qtek.core.Base - */ - var Graph = Base.derive(function() { - return /** @lends qtek.compositor.Graph# */ { - /** - * @type {Array.} - */ - nodes: [] - }; - }, - /** @lends qtek.compositor.Graph.prototype */ - { - /** - * @param {qtek.compositor.Node} node - */ - addNode: function(node) { - - this.nodes.push(node); - - this._dirty = true; - }, - /** - * @param {qtek.compositor.Node} node - */ - removeNode: function(node) { - this.nodes.splice(this.nodes.indexOf(node), 1); - - this._dirty = true; - }, - /** - * @param {string} name - * @return {qtek.compositor.Node} - */ - findNode: function(name) { - for (var i = 0; i < this.nodes.length; i++) { - if (this.nodes[i].name === name) { - return this.nodes[i]; - } - } - }, - /** - * Update links of graph - */ - update: function() { - for (var i = 0; i < this.nodes.length; i++) { - this.nodes[i].clear(); - } - // Traverse all the nodes and build the graph - for (var i = 0; i < this.nodes.length; i++) { - var node = this.nodes[i]; - - if (!node.inputs) { - continue; - } - for (var inputName in node.inputs) { - var fromPinInfo = node.inputs[inputName]; - - var fromPin = this.findPin(fromPinInfo); - if (fromPin) { - node.link(inputName, fromPin.node, fromPin.pin); - }else{ - console.warn('Pin of ' + fromPinInfo.node + '.' + fromPinInfo.pin + ' not exist'); - } - } - } - }, - - findPin: function(input) { - var node; - if (typeof(input.node) === 'string') { - for (var i = 0; i < this.nodes.length; i++) { - var tmp = this.nodes[i]; - if (tmp.name === input.node) { - node = tmp; - } - } - } else { - node = input.node; - } - if (node) { - if (node.outputs[input.pin]) { - return { - node: node, - pin: input.pin - }; - } - } - } - }); - - return Graph; -}); -define('qtek/compositor/TexturePool',['require','../Texture2D','../core/glenum','../core/util'],function(require) { - - - - var Texture2D = require('../Texture2D'); - var glenum = require('../core/glenum'); - var util = require('../core/util'); - - var TexturePool = function () { - - this._pool = {}; - - this._allocatedTextures = []; - }; - - TexturePool.prototype = { - - constructor: TexturePool, - - get: function(parameters) { - var key = generateKey(parameters); - if (!this._pool.hasOwnProperty(key)) { - this._pool[key] = []; - } - var list = this._pool[key]; - if (!list.length) { - var texture = new Texture2D(parameters); - this._allocatedTextures.push(texture); - return texture; - } - return list.pop(); - }, - - put: function(texture) { - var key = generateKey(texture); - if (!this._pool.hasOwnProperty(key)) { - this._pool[key] = []; - } - var list = this._pool[key]; - list.push(texture); - }, - - clear: function(gl) { - for (var i = 0; i < this._allocatedTextures.length; i++) { - this._allocatedTextures[i].dispose(gl); - } - this._pool = {}; - this._allocatedTextures = []; - } - }; - - var defaultParams = { - width: 512, - height: 512, - type: glenum.UNSIGNED_BYTE, - format: glenum.RGBA, - wrapS: glenum.CLAMP_TO_EDGE, - wrapT: glenum.CLAMP_TO_EDGE, - minFilter: glenum.LINEAR_MIPMAP_LINEAR, - magFilter: glenum.LINEAR, - useMipmap: true, - anisotropic: 1, - flipY: true, - unpackAlignment: 4, - premultiplyAlpha: false - }; - - var defaultParamPropList = Object.keys(defaultParams); - - function generateKey(parameters) { - util.defaultsWithPropList(parameters, defaultParams, defaultParamPropList); - fallBack(parameters); - - var key = ''; - for (var i = 0; i < defaultParamPropList.length; i++) { - var name = defaultParamPropList[i]; - var chunk = parameters[name].toString(); - key += chunk; - } - return key; - } - - function fallBack(target) { - - var IPOT = isPowerOfTwo(target.width, target.height); - - if (target.format === glenum.DEPTH_COMPONENT) { - target.useMipmap = false; - } - - if (!IPOT || !target.useMipmap) { - if (target.minFilter == glenum.NEAREST_MIPMAP_NEAREST || - target.minFilter == glenum.NEAREST_MIPMAP_LINEAR) { - target.minFilter = glenum.NEAREST; - } else if ( - target.minFilter == glenum.LINEAR_MIPMAP_LINEAR || - target.minFilter == glenum.LINEAR_MIPMAP_NEAREST - ) { - target.minFilter = glenum.LINEAR; - } - - target.wrapS = glenum.CLAMP_TO_EDGE; - target.wrapT = glenum.CLAMP_TO_EDGE; - } - } - - function isPowerOfTwo(width, height) { - return (width & (width-1)) === 0 && - (height & (height-1)) === 0; - } - - return TexturePool; -}); -define('qtek/compositor/Compositor',['require','./Graph','./TexturePool'],function(require){ - - - - var Graph = require('./Graph'); - var TexturePool = require('./TexturePool'); - - /** - * Compositor provide graph based post processing - * - * @constructor qtek.compositor.Compositor - * @extends qtek.compositor.Graph - * - */ - var Compositor = Graph.derive(function() { - return { - // Output node - _outputs: [], - - _texturePool: new TexturePool() - }; - }, - /** @lends qtek.compositor.Compositor.prototype */ - { - addNode: function(node) { - Graph.prototype.addNode.call(this, node); - if (!node.outputs) { - this.addOutput(node); - } - node._compositor = this; - }, - /** - * @param {qtek.Renderer} renderer - */ - render: function(renderer, frameBuffer) { - if (this._dirty) { - this.update(); - this._dirty = false; - } - for (var i = 0; i < this.nodes.length; i++) { - // Update the reference number of each output texture - this.nodes[i].beforeFrame(); - } - - for (var i = 0; i < this._outputs.length; i++) { - this._outputs[i].updateReference(); - } - for (var i = 0; i < this._outputs.length; i++) { - this._outputs[i].render(renderer, frameBuffer); - } - - for (var i = 0; i < this.nodes.length; i++) { - // Clear up - this.nodes[i].afterFrame(); - } - }, - - addOutput: function(node) { - if (this._outputs.indexOf(node) < 0) { - this._outputs.push(node); - } - }, - - removeOutput: function(node) { - this._outputs.splice(this._outputs.indexOf(node), 1); - }, - - allocateTexture: function (parameters) { - return this._texturePool.get(parameters); - }, - - releaseTexture: function (parameters) { - this._texturePool.put(parameters); - }, - - dispose: function (renderer) { - this._texturePool.clear(renderer.gl); - } - }); - - return Compositor; -}); -define('qtek/geometry/Plane',['require','../DynamicGeometry','../math/BoundingBox'],function(require) { - - - - var DynamicGeometry = require('../DynamicGeometry'); - var BoundingBox = require('../math/BoundingBox'); - - /** - * @constructor qtek.geometry.Plane - * @extends qtek.DynamicGeometry - * @param {Object} [opt] - * @param {number} [opt.widthSegments] - * @param {number} [opt.heightSegments] - */ - var Plane = DynamicGeometry.derive( - /** @lends qtek.geometry.Plane# */ - { - /** - * @type {number} - */ - widthSegments: 1, - /** - * @type {number} - */ - heightSegments: 1 - }, function() { - this.build(); - }, - /** @lends qtek.geometry.Plane.prototype */ - { - /** - * Build plane geometry - */ - build: function() { - var heightSegments = this.heightSegments; - var widthSegments = this.widthSegments; - var positions = this.attributes.position.value; - var texcoords = this.attributes.texcoord0.value; - var normals = this.attributes.normal.value; - var faces = this.faces; - - positions.length = 0; - texcoords.length = 0; - normals.length = 0; - faces.length = 0; - - for (var y = 0; y <= heightSegments; y++) { - var t = y / heightSegments; - for (var x = 0; x <= widthSegments; x++) { - var s = x / widthSegments; - - positions.push([2 * s - 1, 2 * t - 1, 0]); - if (texcoords) { - texcoords.push([s, t]); - } - if (normals) { - normals.push([0, 0, 1]); - } - if (x < widthSegments && y < heightSegments) { - var i = x + y * (widthSegments + 1); - faces.push([i, i + 1, i + widthSegments + 1]); - faces.push([i + widthSegments + 1, i + 1, i + widthSegments + 2]); - } - } - } - - this.boundingBox = new BoundingBox(); - this.boundingBox.min.set(-1, -1, 0); - this.boundingBox.max.set(1, 1, 0); - } - }); - - return Plane; -}); -define('qtek/shader/source/compositor/vertex.essl',[],function () { return '\n@export buildin.compositor.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n\nvarying vec2 v_Texcoord;\n\nvoid main()\n{\n v_Texcoord = texcoord;\n gl_Position = worldViewProjection * vec4(position, 1.0);\n}\n\n@end';}); - -define('qtek/compositor/Pass',['require','../core/Base','../camera/Orthographic','../geometry/Plane','../Shader','../Material','../Mesh','../core/glinfo','../core/glenum','../shader/source/compositor/vertex.essl'],function(require) { - - - - var Base = require('../core/Base'); - var OrthoCamera = require('../camera/Orthographic'); - var Plane = require('../geometry/Plane'); - var Shader = require('../Shader'); - var Material = require('../Material'); - var Mesh = require('../Mesh'); - var glinfo = require('../core/glinfo'); - var glenum = require('../core/glenum'); - - Shader['import'](require('../shader/source/compositor/vertex.essl')); - - var planeGeo = new Plane(); - var mesh = new Mesh({ - geometry : planeGeo - }); - var camera = new OrthoCamera(); - - /** - * @constructor qtek.compositor.Pass - * @extends qtek.core.Base - */ - var Pass = Base.derive(function() { - return /** @lends qtek.compositor.Pass# */ { - /** - * Fragment shader string - * @type {string} - */ - fragment : '', - - /** - * @type {Object} - */ - outputs : null, - - /** - * @type {qtek.Material} - */ - material : null - }; - }, function() { - - var shader = new Shader({ - vertex : Shader.source('buildin.compositor.vertex'), - fragment : this.fragment - }); - var material = new Material({ - shader : shader - }); - shader.enableTexturesAll(); - - this.material = material; - - }, - /** @lends qtek.compositor.Pass.prototype */ - { - /** - * @param {string} name - * @param {} value - */ - setUniform : function(name, value) { - var uniform = this.material.uniforms[name]; - if (uniform) { - uniform.value = value; - } - }, - /** - * @param {string} name - * @return {} - */ - getUniform : function(name) { - var uniform = this.material.uniforms[name]; - if (uniform) { - return uniform.value; - } - }, - /** - * @param {qtek.Texture} texture - * @param {number} attachment - */ - attachOutput : function(texture, attachment) { - if (!this.outputs) { - this.outputs = {}; - } - attachment = attachment || glenum.COLOR_ATTACHMENT0; - this.outputs[attachment] = texture; - }, - /** - * @param {qtek.Texture} texture - */ - detachOutput : function(texture) { - for (var attachment in this.outputs) { - if (this.outputs[attachment] === texture) { - this.outputs[attachment] = null; - } - } - }, - - bind : function(renderer, frameBuffer) { - - if (this.outputs) { - for (var attachment in this.outputs) { - var texture = this.outputs[attachment]; - if (texture) { - frameBuffer.attach(renderer.gl, texture, attachment); - } - } - } - - if (frameBuffer) { - frameBuffer.bind(renderer); - } - }, - - unbind : function(renderer, frameBuffer) { - frameBuffer.unbind(renderer); - }, - /** - * @param {qtek.Renderer} renderer - * @param {qtek.FrameBuffer} [frameBuffer] - */ - render : function(renderer, frameBuffer) { - - var _gl = renderer.gl; - - if (frameBuffer) { - this.bind(renderer, frameBuffer); - // MRT Support in chrome - // https://www.khronos.org/registry/webgl/sdk/tests/conformance/extensions/ext-draw-buffers.html - var ext = glinfo.getExtension(_gl, 'EXT_draw_buffers'); - if (ext && this.outputs) { - var bufs = []; - for (var attachment in this.outputs) { - attachment = +attachment; - if (attachment >= _gl.COLOR_ATTACHMENT0 && attachment <= _gl.COLOR_ATTACHMENT0 + 8) { - bufs.push(attachment); - } - } - ext.drawBuffersEXT(bufs); - } - } - - this.trigger('beforerender', this, renderer); - // Don't clear in each pass, let the color overwrite the buffer - // PENDING Transparent ? - _gl.disable(_gl.BLEND); - _gl.clear(_gl.DEPTH_BUFFER_BIT); - this.renderQuad(renderer); - this.trigger('afterrender', this, renderer); - - if (frameBuffer) { - this.unbind(renderer, frameBuffer); - } - }, - - /** - * Simply do quad rendering - */ - renderQuad: function (renderer) { - mesh.material = this.material; - renderer.renderQueue([mesh], camera); - } - }); - - return Pass; -}); -define('qtek/compositor/Node',['require','../core/Base','./Pass','../FrameBuffer'],function(require) { - - - - var Base = require('../core/Base'); - var Pass = require('./Pass'); - var FrameBuffer = require('../FrameBuffer'); - - /** - * Node of graph based post processing. - * - * @constructor qtek.compositor.Node - * @extends qtek.core.Base - * - * @example - var node = new qtek.compositor.Node({ - name: 'fxaa', - shader: qtek.Shader.source('buildin.compositor.fxaa'), - inputs: { - texture: { - node: 'scene', - pin: 'color' - } - }, - // Multiple outputs is preserved for MRT support in WebGL2.0 - outputs: { - color: { - attachment: qtek.FrameBuffer.COLOR_ATTACHMENT0 - parameters: { - format: qtek.Texture.RGBA, - width: 512, - height: 512 - }, - // Node will keep the RTT rendered in last frame - keepLastFrame: true, - // Force the node output the RTT rendered in last frame - outputLastFrame: true - } - } - }); - * - */ - var Node = Base.derive(function() { - return /** @lends qtek.compositor.Node# */ { - /** - * @type {string} - */ - name: '', - - /** - * @type {Object} - */ - inputs: {}, - - /** - * @type {Object} - */ - outputs: null, - - /** - * @type {string} - */ - shader: '', - - /** - * Input links, will be updated by the graph - * @example: - * inputName: { - * node: someNode, - * pin: 'xxxx' - * } - * @type {Object} - */ - inputLinks: {}, - - /** - * Output links, will be updated by the graph - * @example: - * outputName: { - * node: someNode, - * pin: 'xxxx' - * } - * @type {Object} - */ - outputLinks: {}, - - /** - * @type {qtek.compositor.Pass} - */ - pass: null, - - // Save the output texture of previous frame - // Will be used when there exist a circular reference - _prevOutputTextures: {}, - _outputTextures: {}, - - // Example: { name: 2 } - _outputReferences: {}, - - _rendering: false, - // If rendered in this frame - _rendered: false, - - _compositor: null - }; - }, function() { - - var pass = new Pass({ - fragment: this.shader - }); - this.pass = pass; - - if (this.outputs) { - this.frameBuffer = new FrameBuffer({ - depthBuffer: false - }); - } - }, - /** @lends qtek.compositor.Node.prototype */ - { - /** - * @param {qtek.Renderer} renderer - */ - render: function(renderer, frameBuffer) { - this.trigger('beforerender', renderer); - - this._rendering = true; - - var _gl = renderer.gl; - - for (var inputName in this.inputLinks) { - var link = this.inputLinks[inputName]; - var inputTexture = link.node.getOutput(renderer, link.pin); - this.pass.setUniform(inputName, inputTexture); - } - // Output - if (! this.outputs) { - this.pass.outputs = null; - this.pass.render(renderer, frameBuffer); - } else { - this.pass.outputs = {}; - - for (var name in this.outputs) { - var parameters = this.updateParameter(name, renderer); - var outputInfo = this.outputs[name]; - var texture = this._compositor.allocateTexture(parameters); - this._outputTextures[name] = texture; - var attachment = outputInfo.attachment || _gl.COLOR_ATTACHMENT0; - if (typeof(attachment) == 'string') { - attachment = _gl[attachment]; - } - this.pass.outputs[attachment] = texture; - } - - this.pass.render(renderer, this.frameBuffer); - } - - for (var inputName in this.inputLinks) { - var link = this.inputLinks[inputName]; - link.node.removeReference(link.pin); - } - - this._rendering = false; - this._rendered = true; - - this.trigger('afterrender', renderer); - }, - - // TODO Remove parameter function callback - updateParameter: function(outputName, renderer) { - var outputInfo = this.outputs[outputName]; - var parameters = outputInfo.parameters; - var parametersCopy = outputInfo._parametersCopy; - if (!parametersCopy) { - parametersCopy = outputInfo._parametersCopy = {}; - } - if (parameters) { - for (var key in parameters) { - if (key !== 'width' && key !== 'height') { - parametersCopy[key] = parameters[key]; - } - } - } - var width, height; - if (parameters.width instanceof Function) { - width = parameters.width(renderer); - } else { - width = parameters.width; - } - if (parameters.height instanceof Function) { - height = parameters.height(renderer); - } else { - height = parameters.height; - } - if ( - parametersCopy.width !== width - || parametersCopy.height !== height - ) { - if (this._outputTextures[outputName]) { - this._outputTextures[outputName].dispose(renderer.gl); - } - } - parametersCopy.width = width; - parametersCopy.height = height; - - return parametersCopy; - }, - - /** - * Set parameter - * @param {string} name - * @param {} value - */ - setParameter: function(name, value) { - this.pass.setUniform(name, value); - }, - /** - * Get parameter value - * @param {string} name - * @return {} - */ - getParameter: function(name) { - return this.pass.getUniform(name); - }, - /** - * Set parameters - * @param {Object} obj - */ - setParameters: function(obj) { - for (var name in obj) { - this.setParameter(name, obj[name]); - } - }, - /** - * Set shader code - * @param {string} shaderStr - */ - setShader: function(shaderStr) { - var material = this.pass.material; - material.shader.setFragment(shaderStr); - material.attachShader(material.shader, true); - }, - - getOutput: function(renderer /*optional*/, name) { - if (name === undefined) { - // Return the output texture without rendering - name = renderer; - return this._outputTextures[name]; - } - var outputInfo = this.outputs[name]; - if (! outputInfo) { - return ; - } - - // Already been rendered in this frame - if (this._rendered) { - // Force return texture in last frame - if (outputInfo.outputLastFrame) { - return this._prevOutputTextures[name]; - } else { - return this._outputTextures[name]; - } - } else if ( - // TODO - this._rendering // Solve Circular Reference - ) { - if (!this._prevOutputTextures[name]) { - // Create a blank texture at first pass - this._prevOutputTextures[name] = this._compositor.allocateTexture(outputInfo.parameters || {}); - } - return this._prevOutputTextures[name]; - } - - this.render(renderer); - - return this._outputTextures[name]; - }, - - removeReference: function(outputName) { - this._outputReferences[outputName]--; - if (this._outputReferences[outputName] === 0) { - var outputInfo = this.outputs[outputName]; - if (outputInfo.keepLastFrame) { - if (this._prevOutputTextures[outputName]) { - this._compositor.releaseTexture(this._prevOutputTextures[outputName]); - } - this._prevOutputTextures[outputName] = this._outputTextures[outputName]; - } else { - // Output of this node have alreay been used by all other nodes - // Put the texture back to the pool. - this._compositor.releaseTexture(this._outputTextures[outputName]); - } - } - }, - - link: function(inputPinName, fromNode, fromPinName) { - - // The relationship from output pin to input pin is one-on-multiple - this.inputLinks[inputPinName] = { - node: fromNode, - pin: fromPinName - }; - if (! fromNode.outputLinks[fromPinName]) { - fromNode.outputLinks[fromPinName] = []; - } - fromNode.outputLinks[ fromPinName ].push({ - node: this, - pin: inputPinName - }); - // Enabled the pin texture in shader - var shader = this.pass.material.shader; - shader.enableTexture(inputPinName); - }, - - clear: function() { - this.inputLinks = {}; - this.outputLinks = {}; - - var shader = this.pass.material.shader; - shader.disableTexturesAll(); - }, - - updateReference: function(outputName) { - if (!this._rendering) { - this._rendering = true; - for (var inputName in this.inputLinks) { - var link = this.inputLinks[inputName]; - link.node.updateReference(link.pin); - } - this._rendering = false; - } - if (outputName) { - this._outputReferences[outputName] ++; - } - }, - - beforeFrame: function() { - this._rendered = false; - - for (var name in this.outputLinks) { - this._outputReferences[name] = 0; - } - }, - - afterFrame: function() { - // Put back all the textures to pool - for (var name in this.outputLinks) { - if (this._outputReferences[name] > 0) { - var outputInfo = this.outputs[name]; - if (outputInfo.keepLastFrame) { - if (this._prevOutputTextures[name]) { - this._compositor.releaseTexture(this._prevOutputTextures[name]); - } - this._prevOutputTextures[name] = this._outputTextures[name]; - } else { - this._compositor.releaseTexture(this._outputTextures[name]); - } - } - } - } - }); - - return Node; -}); -define('qtek/compositor/SceneNode',['require','./Node','../core/glinfo'],function(require) { - - - - var Node = require('./Node'); - var glinfo = require('../core/glinfo'); - - /** - * @constructor qtek.compositor.SceneNode - * @extends qtek.compositor.Node - */ - var SceneNode = Node.derive( - /** @lends qtek.compositor.SceneNode# */ - { - name: 'scene', - /** - * @type {qtek.Scene} - */ - scene: null, - /** - * @type {qtek.Camera} - */ - camera: null, - /** - * @type {boolean} - */ - autoUpdateScene: true, - /** - * @type {boolean} - */ - preZ: false - - }, function() { - if (this.frameBuffer) { - this.frameBuffer.depthBuffer = true; - } - }, { - render: function(renderer) { - - this._rendering = true; - var _gl = renderer.gl; - - this.trigger('beforerender'); - - var renderInfo; - - if (!this.outputs) { - - renderInfo = renderer.render(this.scene, this.camera, !this.autoUpdateScene, this.preZ); - - } else { - - var frameBuffer = this.frameBuffer; - for (var name in this.outputs) { - var parameters = this.updateParameter(name, renderer); - var outputInfo = this.outputs[name]; - var texture = this._compositor.allocateTexture(parameters); - this._outputTextures[name] = texture; - - var attachment = outputInfo.attachment || _gl.COLOR_ATTACHMENT0; - if (typeof(attachment) == 'string') { - attachment = _gl[attachment]; - } - frameBuffer.attach(renderer.gl, texture, attachment); - } - frameBuffer.bind(renderer); - - // MRT Support in chrome - // https://www.khronos.org/registry/webgl/sdk/tests/conformance/extensions/ext-draw-buffers.html - var ext = glinfo.getExtension(_gl, 'EXT_draw_buffers'); - if (ext) { - var bufs = []; - for (var attachment in this.outputs) { - attachment = parseInt(attachment); - if (attachment >= _gl.COLOR_ATTACHMENT0 && attachment <= _gl.COLOR_ATTACHMENT0 + 8) { - bufs.push(attachment); - } - } - ext.drawBuffersEXT(bufs); - } - - renderInfo = renderer.render(this.scene, this.camera, !this.autoUpdateScene, this.preZ); - - frameBuffer.unbind(renderer); - } - - this.trigger('afterrender', renderInfo); - - this._rendering = false; - this._rendered = true; - } - }); - - return SceneNode; -}); -define('qtek/compositor/TextureNode',['require','./Node','../Shader'],function(require) { - - - - var Node = require('./Node'); - var Shader = require('../Shader'); - - /** - * @constructor qtek.compositor.TextureNode - * @extends qtek.compositor.Node - */ - var TextureNode = Node.derive(function() { - return /** @lends qtek.compositor.TextureNode# */ { - shader: Shader.source('buildin.compositor.output'), - /** - * @type {qtek.Texture2D} - */ - texture: null - }; - }, { - render: function(renderer, frameBuffer) { - - this._rendering = true; - - var _gl = renderer.gl; - this.pass.setUniform('texture', this.texture); - - if (! this.outputs) { - this.pass.outputs = null; - this.pass.render(renderer, frameBuffer); - } else { - - this.pass.outputs = {}; - - for (var name in this.outputs) { - var parameters = this.updateParameter(name, renderer); - var outputInfo = this.outputs[name]; - var texture = this._compositor.allocateTexture(parameters); - this._outputTextures[name] = texture; - - var attachment = outputInfo.attachment || _gl.COLOR_ATTACHMENT0; - if (typeof(attachment) == 'string') { - attachment = _gl[attachment]; - } - this.pass.outputs[attachment] = texture; - - } - - this.pass.render(renderer, this.frameBuffer); - } - - this._rendering = false; - this._rendered = true; - } - }); - - return TextureNode; -}); -define('qtek/core/Event',['require','./Base'], function(require) { + /** + * @class Common utilities + * @name glMatrix + */ + var glMatrix = {}; + + /** + * Sets the type of array used when creating new vectors and matrices + * + * @param {Type} type Array type, such as Float32Array or Array + */ + glMatrix.setMatrixArrayType = function(type) { + GLMAT_ARRAY_TYPE = type; + } + + if(typeof(exports) !== 'undefined') { + exports.glMatrix = glMatrix; + } - + var degree = Math.PI / 180; - var Base = require('./Base'); + /** + * Convert Degree To Radian + * + * @param {Number} Angle in Degrees + */ + glMatrix.toRadian = function(a){ + return a * degree; + } + ; + /* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + /** + * @class 2 Dimensional Vector + * @name vec2 + */ + + var vec2 = {}; + + /** + * Creates a new, empty vec2 + * + * @returns {vec2} a new 2D vector + */ + vec2.create = function() { + var out = new GLMAT_ARRAY_TYPE(2); + out[0] = 0; + out[1] = 0; + return out; + }; - var QEvent = Base.derive({ - cancelBubble : false - }, { - stopPropagation : function() { - this.cancelBubble = true; - } - }); + /** + * Creates a new vec2 initialized with values from an existing vector + * + * @param {vec2} a vector to clone + * @returns {vec2} a new 2D vector + */ + vec2.clone = function(a) { + var out = new GLMAT_ARRAY_TYPE(2); + out[0] = a[0]; + out[1] = a[1]; + return out; + }; - QEvent['throw'] = function(eventType, target, props) { - - var e = new QEvent(props); + /** + * Creates a new vec2 initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @returns {vec2} a new 2D vector + */ + vec2.fromValues = function(x, y) { + var out = new GLMAT_ARRAY_TYPE(2); + out[0] = x; + out[1] = y; + return out; + }; - e.type = eventType; - e.target = target; + /** + * Copy the values from one vec2 to another + * + * @param {vec2} out the receiving vector + * @param {vec2} a the source vector + * @returns {vec2} out + */ + vec2.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + return out; + }; - // enable bubbling - while (target && !e.cancelBubble) { - e.currentTarget = target; - target.trigger(eventType, e); + /** + * Set the components of a vec2 to the given values + * + * @param {vec2} out the receiving vector + * @param {Number} x X component + * @param {Number} y Y component + * @returns {vec2} out + */ + vec2.set = function(out, x, y) { + out[0] = x; + out[1] = y; + return out; + }; - target = target.getParent(); - } - }; + /** + * Adds two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ + vec2.add = function(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + return out; + }; - return QEvent; -}); -define('qtek/core/LinkedList',['require'],function(require) { - - - - /** - * Simple double linked list. Compared with array, it has O(1) remove operation. - * @constructor - * @alias qtek.core.LinkedList - */ - var LinkedList = function() { - - /** - * @type {qtek.core.LinkedList.Entry} - */ - this.head = null; - - /** - * @type {qtek.core.LinkedList.Entry} - */ - this.tail = null; - - this._length = 0; - }; - - /** - * Insert a new value at the tail - * @param {} val - * @return {qtek.core.LinkedList.Entry} - */ - LinkedList.prototype.insert = function(val) { - var entry = new LinkedList.Entry(val); - this.insertEntry(entry); - return entry; - }; - - /** - * Insert a new value at idx - * @param {number} idx - * @param {} val - * @return {qtek.core.LinkedList.Entry} - */ - LinkedList.prototype.insertAt = function(idx, val) { - if (idx < 0) { - return; - } - var next = this.head; - var cursor = 0; - while (next && cursor != idx) { - next = next.next; - cursor++; - } - if (next) { - var entry = new LinkedList.Entry(val); - var prev = next.prev; - prev.next = entry; - entry.prev = prev; - entry.next = next; - next.prev = entry; - } else { - this.insert(val); - } - }; - - /** - * Insert an entry at the tail - * @param {qtek.core.LinkedList.Entry} entry - */ - LinkedList.prototype.insertEntry = function(entry) { - if (!this.head) { - this.head = this.tail = entry; - } else { - this.tail.next = entry; - entry.prev = this.tail; - this.tail = entry; - } - this._length++; - }; - - /** - * Remove entry. - * @param {qtek.core.LinkedList.Entry} entry - */ - LinkedList.prototype.remove = function(entry) { - var prev = entry.prev; - var next = entry.next; - if (prev) { - prev.next = next; - } else { - // Is head - this.head = next; - } - if (next) { - next.prev = prev; - } else { - // Is tail - this.tail = prev; - } - entry.next = entry.prev = null; - this._length--; - }; - - /** - * Remove entry at index. - * @param {number} idx - * @return {} - */ - LinkedList.prototype.removeAt = function(idx) { - if (idx < 0) { - return; - } - var curr = this.head; - var cursor = 0; - while (curr && cursor != idx) { - curr = curr.next; - cursor++; - } - if (curr) { - this.remove(curr); - return curr.value; - } - }; - /** - * Get head value - * @return {} - */ - LinkedList.prototype.getHead = function() { - if (this.head) { - return this.head.value; - } - }; - /** - * Get tail value - * @return {} - */ - LinkedList.prototype.getTail = function() { - if (this.tail) { - return this.tail.value; - } - }; - /** - * Get value at idx - * @param {number} idx - * @return {} - */ - LinkedList.prototype.getAt = function(idx) { - if (idx < 0) { - return; - } - var curr = this.head; - var cursor = 0; - while (curr && cursor != idx) { - curr = curr.next; - cursor++; - } - return curr.value; - }; - - /** - * @param {} value - * @return {number} - */ - LinkedList.prototype.indexOf = function(value) { - var curr = this.head; - var cursor = 0; - while (curr) { - if (curr.value === value) { - return cursor; - } - curr = curr.next; - cursor++; - } - }; - - /** - * @return {number} - */ - LinkedList.prototype.length = function() { - return this._length; - }; - - /** - * If list is empty - */ - LinkedList.prototype.isEmpty = function() { - return this._length === 0; - }; - - /** - * @param {Function} cb - * @param {} context - */ - LinkedList.prototype.forEach = function(cb, context) { - var curr = this.head; - var idx = 0; - var haveContext = typeof(context) != 'undefined'; - while (curr) { - if (haveContext) { - cb.call(context, curr.value, idx); - } else { - cb(curr.value, idx); - } - curr = curr.next; - idx++; - } - }; - - /** - * Clear the list - */ - LinkedList.prototype.clear = function() { - this.tail = this.head = null; - this._length = 0; - }; - - /** - * @constructor - * @param {} val - */ - LinkedList.Entry = function(val) { - /** - * @type {} - */ - this.value = val; - - /** - * @type {qtek.core.LinkedList.Entry} - */ - this.next = null; - - /** - * @type {qtek.core.LinkedList.Entry} - */ - this.prev = null; - }; - - return LinkedList; -}); -define('qtek/core/LRU',['require','./LinkedList'],function(require) { - - - - var LinkedList = require('./LinkedList'); - - /** - * LRU Cache - * @constructor - * @alias qtek.core.LRU - */ - var LRU = function(maxSize) { - - this._list = new LinkedList(); - - this._map = {}; - - this._maxSize = maxSize || 10; - }; - - /** - * Set cache max size - * @param {number} size - */ - LRU.prototype.setMaxSize = function(size) { - this._maxSize = size; - }; - - /** - * @param {string} key - * @param {} value - */ - LRU.prototype.put = function(key, value) { - if (typeof(this._map[key]) == 'undefined') { - var len = this._list.length(); - if (len >= this._maxSize && len > 0) { - // Remove the least recently used - var leastUsedEntry = this._list.head; - this._list.remove(leastUsedEntry); - delete this._map[leastUsedEntry.key]; - } - - var entry = this._list.insert(value); - entry.key = key; - this._map[key] = entry; - } - }; - - /** - * @param {string} key - * @return {} - */ - LRU.prototype.get = function(key) { - var entry = this._map[key]; - if (typeof(entry) != 'undefined') { - // Put the latest used entry in the tail - if (entry !== this._list.tail) { - this._list.remove(entry); - this._list.insertEntry(entry); - } - - return entry.value; - } - }; - - /** - * @param {string} key - */ - LRU.prototype.remove = function(key) { - var entry = this._map[key]; - if (typeof(entry) != 'undefined') { - delete this._map[key]; - this._list.remove(entry); - } - }; - - /** - * Clear the cache - */ - LRU.prototype.clear = function() { - this._list.clear(); - this._map = {}; - }; - - return LRU; -}); -define('qtek/deferred/StandardMaterial',['require','../core/Base'],function (require) { - - - - var Base = require('../core/Base'); - - var DeferredDummyShader = function () {}; - - var StandardMaterial = Base.derive({ - - /** - * @type {Array.} - * @default [1, 1, 1] - */ - color: null, - - /** - * @type {Array.} - * @default [0, 0, 0] - */ - emission: null, - - /** - * @type {Array.} - * @default [0.5, 0.5, 0.5] - */ - specularColor: null, - - /** - * @type {number} - * @default 0.5 - */ - glossiness: 0.5, - - /** - * @type {qtek.Texture2D} - */ - diffuseMap: null, - - /** - * @type {qtek.Texture2D} - */ - normalMap: null, - - /** - * @type {qtek.TextureCube} - */ - environmentMap: null, - - /** - * Diffuse alpha channel usage. - * 'none', 'alpha', 'glossiness' - * @type {string} - * @default 'none' - */ - diffuseAlphaUsage: 'none', - - /** - * @type {Array.} - */ - uvRepeat: null, - - /** - * @type {Array.} - */ - uvOffset: null - }, function () { - - this.color = this.color || [1, 1, 1]; - - this.emission = this.emission || [0, 0, 0]; - - this.specularColor = this.specularColor || [0.5, 0.5, 0.5]; - - this.uvRepeat = this.uvRepeat || [1, 1]; - this.uvOffset = this.uvOffset || [0, 0]; - - // Rendererable must need a material with shader - this.shader = new DeferredDummyShader(); - }, {}); - - return StandardMaterial; -}); -define('qtek/geometry/Sphere',['require','../DynamicGeometry','../dep/glmatrix','../math/BoundingBox'],function(require) { - - - - var DynamicGeometry = require('../DynamicGeometry'); - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - var vec2 = glMatrix.vec2; - var BoundingBox = require('../math/BoundingBox'); - - /** - * @constructor qtek.geometry.Sphere - * @extends qtek.DynamicGeometry - * @param {Object} [opt] - * @param {number} [widthSegments] - * @param {number} [heightSegments] - * @param {number} [phiStart] - * @param {number} [phiLength] - * @param {number} [thetaStart] - * @param {number} [thetaLength] - * @param {number} [radius] - */ - var Sphere = DynamicGeometry.derive( - /** @lends qtek.geometry.Sphere# */ - { - /** - * @type {number} - */ - widthSegments: 20, - /** - * @type {number} - */ - heightSegments: 20, - - /** - * @type {number} - */ - phiStart: 0, - /** - * @type {number} - */ - phiLength: Math.PI * 2, - - /** - * @type {number} - */ - thetaStart: 0, - /** - * @type {number} - */ - thetaLength: Math.PI, - - /** - * @type {number} - */ - radius: 1 - - }, function() { - this.build(); - }, - /** @lends qtek.geometry.Sphere.prototype */ - { - /** - * Build sphere geometry - */ - build: function() { - var positions = this.attributes.position.value; - var texcoords = this.attributes.texcoord0.value; - var normals = this.attributes.normal.value; - - positions.length = 0; - texcoords.length = 0; - normals.length = 0; - this.faces.length = 0; - - var x, y, z, - u, v, - i, j; - var normal; - - var heightSegments = this.heightSegments; - var widthSegments = this.widthSegments; - var radius = this.radius; - var phiStart = this.phiStart; - var phiLength = this.phiLength; - var thetaStart = this.thetaStart; - var thetaLength = this.thetaLength; - var radius = this.radius; - - for (j = 0; j <= heightSegments; j ++) { - for (i = 0; i <= widthSegments; i ++) { - u = i / widthSegments; - v = j / heightSegments; - - // X axis is inverted so texture can be mapped from left to right - x = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); - y = radius * Math.cos(thetaStart + v * thetaLength); - z = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); - - positions.push(vec3.fromValues(x, y, z)); - texcoords.push(vec2.fromValues(u, v)); - - normal = vec3.fromValues(x, y, z); - vec3.normalize(normal, normal); - normals.push(normal); - } - } - - var i1, i2, i3, i4; - var faces = this.faces; - - var len = widthSegments + 1; - - for (j = 0; j < heightSegments; j ++) { - for (i = 0; i < widthSegments; i ++) { - i2 = j * len + i; - i1 = (j * len + i + 1); - i4 = (j + 1) * len + i + 1; - i3 = (j + 1) * len + i; - - faces.push(vec3.fromValues(i1, i2, i4)); - faces.push(vec3.fromValues(i2, i3, i4)); - } - } - - this.boundingBox = new BoundingBox(); - this.boundingBox.max.set(radius, radius, radius); - this.boundingBox.min.set(-radius, -radius, -radius); - } - }); - - return Sphere; -}); -define('qtek/geometry/Cone',['require','../DynamicGeometry','../math/BoundingBox','../dep/glmatrix'],function(require) { - - - - var DynamicGeometry = require('../DynamicGeometry'); - var BoundingBox = require('../math/BoundingBox'); - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - var vec2 = glMatrix.vec2; - - /** - * @constructor qtek.geometry.Cone - * @extends qtek.DynamicGeometry - * @param {Object} [opt] - * @param {number} [opt.topRadius] - * @param {number} [opt.bottomRadius] - * @param {number} [opt.height] - * @param {number} [opt.capSegments] - * @param {number} [opt.heightSegments] - */ - var Cone = DynamicGeometry.derive( - /** @lends qtek.geometry.Cone# */ - { - /** - * @type {number} - */ - topRadius: 0, - - /** - * @type {number} - */ - bottomRadius: 1, - - /** - * @type {number} - */ - height: 2, - - /** - * @type {number} - */ - capSegments: 20, - - /** - * @type {number} - */ - heightSegments: 1 - }, function() { - this.build(); - }, - /** @lends qtek.geometry.Cone.prototype */ - { - /** - * Build cone geometry - */ - build: function() { - var positions = this.attributes.position.value; - var texcoords = this.attributes.texcoord0.value; - var faces = this.faces; - positions.length = 0; - texcoords.length = 0; - faces.length = 0; - // Top cap - var capSegRadial = Math.PI * 2 / this.capSegments; - - var topCap = []; - var bottomCap = []; - - var r1 = this.topRadius; - var r2 = this.bottomRadius; - var y = this.height / 2; - - var c1 = vec3.fromValues(0, y, 0); - var c2 = vec3.fromValues(0, -y, 0); - for (var i = 0; i < this.capSegments; i++) { - var theta = i * capSegRadial; - var x = r1 * Math.sin(theta); - var z = r1 * Math.cos(theta); - topCap.push(vec3.fromValues(x, y, z)); - - x = r2 * Math.sin(theta); - z = r2 * Math.cos(theta); - bottomCap.push(vec3.fromValues(x, -y, z)); - } - - // Build top cap - positions.push(c1); - // FIXME - texcoords.push(vec2.fromValues(0, 1)); - var n = this.capSegments; - for (var i = 0; i < n; i++) { - positions.push(topCap[i]); - // FIXME - texcoords.push(vec2.fromValues(i / n, 0)); - faces.push([0, i+1, (i+1) % n + 1]); - } - - // Build bottom cap - var offset = positions.length; - positions.push(c2); - texcoords.push(vec2.fromValues(0, 1)); - for (var i = 0; i < n; i++) { - positions.push(bottomCap[i]); - // FIXME - texcoords.push(vec2.fromValues(i / n, 0)); - faces.push([offset, offset+((i+1) % n + 1), offset+i+1]); - } - - // Build side - offset = positions.length; - var n2 = this.heightSegments; - for (var i = 0; i < n; i++) { - for (var j = 0; j < n2+1; j++) { - var v = j / n2; - positions.push(vec3.lerp(vec3.create(), topCap[i], bottomCap[i], v)); - texcoords.push(vec2.fromValues(i / n, v)); - } - } - for (var i = 0; i < n; i++) { - for (var j = 0; j < n2; j++) { - var i1 = i * (n2 + 1) + j; - var i2 = ((i + 1) % n) * (n2 + 1) + j; - var i3 = ((i + 1) % n) * (n2 + 1) + j + 1; - var i4 = i * (n2 + 1) + j + 1; - faces.push([offset+i2, offset+i1, offset+i4]); - faces.push([offset+i4, offset+i3, offset+i2]); - } - } - - this.generateVertexNormals(); - - this.boundingBox = new BoundingBox(); - var r = Math.max(this.topRadius, this.bottomRadius); - this.boundingBox.min.set(-r, -this.height/2, -r); - this.boundingBox.max.set(r, this.height/2, r); - } - }); - - return Cone; -}); -define('qtek/shader/source/util.essl',[],function () { return '// Use light attenuation formula in\n// http://blog.slindev.com/2011/01/10/natural-light-attenuation/\n@export buildin.util.calculate_attenuation\n\nuniform float attenuationFactor : 5.0;\n\nfloat lightAttenuation(float dist, float range)\n{\n float attenuation = 1.0;\n if( range > 0.0)\n {\n attenuation = dist*dist/(range*range);\n float att_s = attenuationFactor;\n attenuation = 1.0/(attenuation*att_s+1.0);\n att_s = 1.0/(att_s+1.0);\n attenuation = attenuation - att_s;\n attenuation /= 1.0 - att_s;\n }\n return clamp(attenuation, 0.0, 1.0);\n}\n\n@end\n\n//http://codeflow.org/entries/2012/aug/02/easy-wireframe-display-with-barycentric-coordinates/\n@export buildin.util.edge_factor\n\nfloat edgeFactor(float width)\n{\n vec3 d = fwidth(v_Barycentric);\n vec3 a3 = smoothstep(vec3(0.0), d * width, v_Barycentric);\n return min(min(a3.x, a3.y), a3.z);\n}\n\n@end\n\n// Pack depth\n// Float value can only be [0.0 - 1.0) ?\n@export buildin.util.encode_float\nvec4 encodeFloat( const in float depth )\n{\n\n const vec4 bitShifts = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );\n\n const vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );\n vec4 res = fract( depth * bitShifts );\n res -= res.xxyz * bit_mask;\n\n return res;\n}\n@end\n\n@export buildin.util.decode_float\nfloat decodeFloat(const in vec4 colour)\n{\n const vec4 bitShifts = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n return dot(colour, bitShifts);\n}\n@end\n\n// http://graphicrants.blogspot.com/2009/04/rgbm-color-encoding.html\n@export buildin.util.rgbm_decode\nvec3 RGBMDecode(vec4 rgbm, float range) {\n return range * rgbm.rgb * rgbm.a;\n}\n@end\n\n@export buildin.util.rgbm_encode\nvec4 RGBMEncode(vec3 color, float range) {\n vec4 rgbm;\n color *= 1.0 / range;\n rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 1e-6 ) ), 0.0, 1.0);\n rgbm.a = ceil(rgbm.a * 255.0) / 255.0;\n rgbm.rgb = color / rgbm.a;\n return rgbm;\n}\n@end\n\n\n@export buildin.chunk.skin_matrix\n\n// Weighted Sum Skinning Matrix\nmat4 skinMatrixWS;\nif (joint.x >= 0.0)\n{\n skinMatrixWS = skinMatrix[int(joint.x)] * weight.x;\n}\nif (joint.y >= 0.0)\n{\n skinMatrixWS += skinMatrix[int(joint.y)] * weight.y;\n}\nif (joint.z >= 0.0)\n{\n skinMatrixWS += skinMatrix[int(joint.z)] * weight.z;\n}\nif (joint.w >= 0.0)\n{\n skinMatrixWS += skinMatrix[int(joint.w)] * (1.0-weight.x-weight.y-weight.z);\n}\n@end\n';}); - -define('qtek/shader/source/deferred/gbuffer.essl',[],function () { return '@export buildin.deferred.gbuffer.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\n\nuniform vec2 uvRepeat;\nuniform vec2 uvOffset;\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 normal : NORMAL;\nattribute vec4 tangent : TANGENT;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\n\n#ifdef NORMALMAP_ENABLED\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\nvarying vec4 v_ProjPos;\n\nvoid main()\n{\n \n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n // Upper skinMatrix \n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n skinnedTangent = (skinMatrixWS * vec4(tangent.xyz, 0.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\n \n #ifdef NORMALMAP_ENABLED\n v_Tangent = normalize((worldInverseTranspose * vec4(skinnedTangent, 0.0)).xyz);\n v_Bitangent = normalize(cross(v_Normal, v_Tangent) * tangent.w);\n #endif\n\n v_ProjPos = gl_Position;\n}\n\n\n@end\n\n\n@export buildin.deferred.gbuffer.fragment\n\nuniform sampler2D diffuseMap;\nuniform float glossiness;\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\n\n#ifdef NORMALMAP_ENABLED\nuniform sampler2D normalMap;\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\nvarying vec4 v_ProjPos;\n\nvoid main()\n{\n vec3 N = v_Normal;\n #ifdef NORMALMAP_ENABLED\n N = texture2D(normalMap, v_Texcoord).xyz * 2.0 - 1.0;\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\n N = tbn * N;\n #endif\n\n // N.z can be recovered from sqrt(1 - dot(N.xy, N.xy));\n gl_FragColor.rg = (N.xy + 1.0) * 0.5;\n\n // Depth\n gl_FragColor.b = v_ProjPos.z / v_ProjPos.w;\n\n gl_FragColor.a = glossiness;\n #ifdef DIFFUSEMAP_ENABLED\n // Ouptut glossiness to alpha channel\n gl_FragColor.a *= texture2D(diffuseMap, v_Texcoord).a;\n #endif\n\n}\n@end';}); - -define('qtek/shader/source/deferred/chunk.essl',[],function () { return '@export buildin.deferred.chunk.light_head\nuniform sampler2D normalTex;\nuniform vec2 viewportSize;\n\nuniform mat4 viewProjectionInv;\n\nconst vec3 LUM = vec3(0.2125, 0.7154, 0.0721);\n@end\n\n@export buildin.deferred.chunk.gbuffer_read\n vec2 uv = gl_FragCoord.xy / viewportSize;\n\n vec4 tex = texture2D(normalTex, uv);\n // Is empty\n if (dot(tex.rgb, vec3(1.0)) == 0.0) {\n discard;\n }\n\n vec3 N;\n N.xy = tex.rg * 2.0 - 1.0;\n N.z = sqrt(1.0 - dot(N.xy, N.xy));\n\n // Depth value in depth texture is 0 - 1\n // float z = texture2D(depthTex, uv).r * 2.0 - 1.0;\n float z = tex.b;\n\n float glossiness = tex.a;\n\n vec2 xy = uv * 2.0 - 1.0;\n\n vec4 projectedPos = vec4(xy, z, 1.0);\n vec4 p4 = viewProjectionInv * projectedPos;\n\n vec3 position = p4.xyz / p4.w;\n@end\n\n@export buildin.deferred.chunk.light_equation\n\nfloat D_Phong(float g, float ndh) {\n // from black ops 2\n float a = pow(8192.0, g);\n return (a + 2.0) / 8.0 * pow(ndh, a);\n}\n\n@end';}); - -define('qtek/shader/source/deferred/lightvolume.essl',[],function () { return '@export buildin.deferred.light_volume.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\nvarying vec3 v_Position;\n\nvoid main()\n{\n gl_Position = worldViewProjection * vec4(position, 1.0);\n\n v_Position = position;\n}\n\n@end';}); - -define('qtek/shader/source/deferred/spot.essl',[],function () { return '@export buildin.deferred.spot_light\n\n@import buildin.deferred.chunk.light_head\n\n@import buildin.deferred.chunk.light_equation\n\n@import buildin.util.calculate_attenuation\n\nuniform vec3 lightPosition;\nuniform vec3 lightDirection;\nuniform vec3 lightColor;\nuniform float umbraAngleCosine;\nuniform float penumbraAngleCosine;\nuniform float lightRange;\nuniform float falloffFactor;\n\nuniform vec3 eyePosition;\n\nvoid main()\n{\n @import buildin.deferred.chunk.gbuffer_read\n\n vec3 L = lightPosition - position;\n vec3 V = normalize(eyePosition - position);\n\n float dist = length(L);\n L /= dist;\n\n float attenuation = lightAttenuation(dist, lightRange);\n float c = dot(-lightDirection, L);\n\n float falloff = clamp((c - umbraAngleCosine) / (penumbraAngleCosine - umbraAngleCosine), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n // Diffuse term\n gl_FragColor.rgb = lightColor * ndl * (1.0 - falloff) * attenuation;\n if (dot(gl_FragColor.rgb, vec3(1.0)) == 0.0) // Reduce blending\n {\n discard;\n }\n gl_FragColor.a = dot(LUM, gl_FragColor.rgb * D_Phong(glossiness, ndh));\n}\n@end\n';}); - -define('qtek/shader/source/deferred/directional.essl',[],function () { return '@export buildin.deferred.directional_light\n\n@import buildin.deferred.chunk.light_head\n\n@import buildin.deferred.chunk.light_equation\n\nuniform vec3 lightDirection;\nuniform vec3 lightColor;\n\nuniform vec3 eyePosition;\n\nvoid main()\n{\n @import buildin.deferred.chunk.gbuffer_read\n\n vec3 L = -normalize(lightDirection);\n vec3 V = normalize(eyePosition - position);\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n gl_FragColor.rgb = ndl * lightColor;\n gl_FragColor.a = dot(LUM, gl_FragColor.rgb * D_Phong(glossiness, ndh));\n}\n@end\n';}); - -define('qtek/shader/source/deferred/ambient.essl',[],function () { return '@export buildin.deferred.ambient_light\nuniform sampler2D normalTexture;\nuniform vec3 lightColor;\n\nvarying vec2 v_Texcoord;\n\nvoid main()\n{\n vec4 tex = texture2D(normalTexture, v_Texcoord);\n vec3 normal = tex.rgb * 2.0 - 1.0;\n\n gl_FragColor.rgb = lightColor * (clamp(normal.y * 0.7, 0.0, 1.0) + 0.3);\n gl_FragColor.a = 0.0;\n}\n@end';}); - -define('qtek/shader/source/deferred/point.essl',[],function () { return '@export buildin.deferred.point_light\n\n@import buildin.deferred.chunk.light_head\n\n@import buildin.util.calculate_attenuation\n\n@import buildin.deferred.chunk.light_equation\n\nuniform vec3 lightPosition;\nuniform vec3 lightColor;\nuniform float lightRange;\n\nuniform vec3 eyePosition;\n\nvarying vec3 v_Position;\n\nvoid main()\n{\n @import buildin.deferred.chunk.gbuffer_read\n\n vec3 L = lightPosition - position;\n vec3 V = normalize(eyePosition - position);\n\n float dist = length(L);\n L /= dist;\n\n vec3 H = normalize(L + V);\n\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n float attenuation = lightAttenuation(dist, lightRange);\n // Diffuse term\n gl_FragColor.rgb = lightColor * ndl * attenuation;\n if (dot(gl_FragColor.rgb, vec3(1.0)) == 0.0) // Reduce blending\n {\n discard;\n }\n // // Specular luminance\n gl_FragColor.a = dot(LUM, gl_FragColor.rgb * D_Phong(glossiness, ndh));\n}\n@end';}); - -define('qtek/shader/source/deferred/output.essl',[],function () { return '@export buildin.deferred.output.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world: WORLD;\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n\nuniform vec2 uvRepeat;\nuniform vec2 uvOffset;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\n\nvarying vec4 v_ProjPos;\n\nvarying vec3 v_WorldPos;\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n\n v_WorldPos = (world * vec4(skinnedPosition, 1.0)).xyz;\n\n v_ProjPos = gl_Position;\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n}\n@end\n\n@export buildin.deferred.output.fragment\n\nuniform sampler2D diffuseMap;\n\nuniform sampler2D lightAccumTex;\nuniform sampler2D normalTex;\n\nuniform vec3 color;\nuniform vec3 specularColor;\nuniform vec3 emission;\n\nuniform vec3 eyePosition;\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_WorldPos;\nvarying vec4 v_ProjPos;\n\nconst vec3 LUM = vec3(0.2125, 0.7154, 0.0721);\n\n// Fresnel\nvec3 F_Schlick(float ndv, vec3 spec) {\n return spec + (1.0 - spec) * pow(1.0 - ndv, 5.0);\n}\n\nvoid main()\n{\n vec2 uv = (v_ProjPos.xy / v_ProjPos.w + 1.0) * 0.5;\n\n vec3 V = normalize(eyePosition - v_WorldPos);\n\n vec3 albedo = color;\n #ifdef diffuseMap\n albedo *= texture2D(diffuseMap, v_Texcoord);\n #endif\n\n vec4 diffSpec = texture2D(lightAccumTex, uv);\n vec3 N;\n vec2 tex = texture2D(normalTex, uv).rg;\n N.xy = tex * 2.0 - 1.0;\n N.z = sqrt(1.0 - dot(N.xy, N.xy));\n\n vec3 diffTerm = diffSpec.rgb;\n // PENDING\n vec3 specTerm = diffTerm * diffSpec.a / (dot(LUM, diffTerm) + 0.1);\n vec3 fresnelTerm = F_Schlick(clamp(dot(N, V), 0.0, 1.0), specularColor);\n\n gl_FragColor.rgb = albedo * diffTerm + fresnelTerm * specTerm + emission;\n gl_FragColor.a = 1.0;\n}\n@end\n\n';}); - -define('qtek/shader/source/prez.essl',[],function () { return '// Shader for prez pass\n@export buildin.prez.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n \n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n \n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n}\n\n@end\n\n\n@export buildin.prez.fragment\n\nvoid main()\n{\n gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\n}\n\n@end';}); - -// Light-pre pass deferred rendering -// http://www.realtimerendering.com/blog/deferred-lighting-approaches/ -define('qtek/deferred/Renderer',['require','../core/Base','../Shader','./StandardMaterial','../Material','../FrameBuffer','../compositor/Pass','../Texture2D','../Texture','../math/BoundingBox','../Mesh','../geometry/Sphere','../geometry/Cone','../math/Matrix4','../math/Vector3','../Renderer','../shader/source/util.essl','../shader/source/deferred/gbuffer.essl','../shader/source/deferred/chunk.essl','../shader/source/deferred/lightvolume.essl','../shader/source/deferred/spot.essl','../shader/source/deferred/directional.essl','../shader/source/deferred/ambient.essl','../shader/source/deferred/point.essl','../shader/source/deferred/output.essl','../shader/source/prez.essl'],function (require) { - - + /** + * Subtracts vector b from vector a + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ + vec2.subtract = function(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + return out; + }; + + /** + * Alias for {@link vec2.subtract} + * @function + */ + vec2.sub = vec2.subtract; + + /** + * Multiplies two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ + vec2.multiply = function(out, a, b) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + return out; + }; - var Base = require('../core/Base'); - var Shader = require('../Shader'); - var StandardMaterial = require('./StandardMaterial'); - var Material = require('../Material'); - var FrameBuffer = require('../FrameBuffer'); - var FullQuadPass = require('../compositor/Pass'); - var Texture2D = require('../Texture2D'); - var Texture = require('../Texture'); - var boundingBox = require('../math/BoundingBox'); - var Mesh = require('../Mesh'); - var SphereGeo = require('../geometry/Sphere'); - var ConeGeo = require('../geometry/Cone'); - var Matrix4 = require('../math/Matrix4'); - var Vector3 = require('../math/Vector3'); - var ForwardRenderer = require('../Renderer'); + /** + * Alias for {@link vec2.multiply} + * @function + */ + vec2.mul = vec2.multiply; + + /** + * Divides two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ + vec2.divide = function(out, a, b) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + return out; + }; - Shader.import(require('../shader/source/util.essl')); - Shader.import(require('../shader/source/deferred/gbuffer.essl')); - Shader.import(require('../shader/source/deferred/chunk.essl')); + /** + * Alias for {@link vec2.divide} + * @function + */ + vec2.div = vec2.divide; + + /** + * Returns the minimum of two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ + vec2.min = function(out, a, b) { + out[0] = Math.min(a[0], b[0]); + out[1] = Math.min(a[1], b[1]); + return out; + }; - Shader.import(require('../shader/source/deferred/lightvolume.essl')); - // Light shaders - Shader.import(require('../shader/source/deferred/spot.essl')); - Shader.import(require('../shader/source/deferred/directional.essl')); - Shader.import(require('../shader/source/deferred/ambient.essl')); - Shader.import(require('../shader/source/deferred/point.essl')); + /** + * Returns the maximum of two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec2} out + */ + vec2.max = function(out, a, b) { + out[0] = Math.max(a[0], b[0]); + out[1] = Math.max(a[1], b[1]); + return out; + }; - Shader.import(require('../shader/source/deferred/output.essl')); + /** + * Scales a vec2 by a scalar number + * + * @param {vec2} out the receiving vector + * @param {vec2} a the vector to scale + * @param {Number} b amount to scale the vector by + * @returns {vec2} out + */ + vec2.scale = function(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + return out; + }; - Shader.import(require('../shader/source/prez.essl')); - - var errorShader = {}; - - var DeferredRenderer = Base.derive(function () { - - var gBufferShader = new Shader({ - vertex: Shader.source('buildin.deferred.gbuffer.vertex'), - fragment: Shader.source('buildin.deferred.gbuffer.fragment') - }); - - var gBufferDiffShader = gBufferShader.clone(); - gBufferDiffShader.enableTexture('diffuseMap'); - var gBufferNormShader = gBufferShader.clone(); - gBufferNormShader.enableTexture('normalMap'); - var gBufferDiffNormShader = gBufferDiffShader.clone(); - gBufferDiffNormShader.enableTexture('normalMap'); - - var outputShader = new Shader({ - vertex: Shader.source('buildin.deferred.output.vertex'), - fragment: Shader.source('buildin.deferred.output.fragment') - }); - var outputDiffShader = outputShader.clone(); - outputDiffShader.enableTexture('diffuseMap'); - - var fullQuadVertex = Shader.source('buildin.compositor.vertex'); - var lightVolumeVertex = Shader.source('buildin.deferred.light_volume.vertex'); - - var lightAccumulateBlendFunc = function (gl) { - gl.blendEquation(gl.FUNC_ADD); - gl.blendFunc(gl.ONE, gl.ONE); - } - - var createLightPassMat = function (shader) { - return new Material({ - shader: shader, - blend: lightAccumulateBlendFunc, - transparent: true, - depthMask: false - }) - } - - // Rotate and positioning to fit the spot light - // Which the cusp of cone pointing to the negative z - // and positioned on the origin - var coneGeo = new ConeGeo({ - bottomRadius: 1, - height: 2, - capSegments: 10 - }); - var mat = new Matrix4(); - mat.rotateX(Math.PI / 2); - mat.translate(new Vector3(0, 0, -1)); - - coneGeo.applyTransform(mat); - - return { - // GBuffer shaders - _gBufferShader: gBufferShader, - - _gBufferDiffShader: gBufferDiffShader, - - _gBufferDiffNormShader: gBufferDiffNormShader, - - _gBufferNormShader: gBufferNormShader, - - _outputShader: outputShader, - - _outputDiffShader: outputDiffShader, - - _gBufferFrameBuffer: new FrameBuffer(), - - _gBufferTex: new Texture2D({ - width: 0, - height: 0, - // FIXME Device not support float texture - // FIXME Half float seems has problem - type: Texture.FLOAT, - minFilter: Texture.NEAREST, - magFilter: Texture.NEAREST - }), - - // _depthTex: new Texture2D({ - // type: Texture.UNSIGNED_SHORT, - // format: Texture.DEPTH_COMPONENT - // }), - - _lightAccumFrameBuffer: new FrameBuffer(), - - _lightAccumTex: new Texture2D({ - // FIXME Device not support float texture - type: Texture.FLOAT, - minFilter: Texture.NEAREST, - magFilter: Texture.NEAREST - }), - - _fullQuadPass: new FullQuadPass(), - - _directionalLightMat: createLightPassMat(new Shader({ - vertex: fullQuadVertex, - fragment: Shader.source('buildin.deferred.directional_light') - })), - _ambientMat: createLightPassMat(new Shader({ - vertex: fullQuadVertex, - fragment: Shader.source('buildin.deferred.ambient_light') - })), - - _spotLightShader: new Shader({ - vertex: lightVolumeVertex, - fragment: Shader.source('buildin.deferred.spot_light') - }), - _pointLightShader: new Shader({ - vertex: lightVolumeVertex, - fragment: Shader.source('buildin.deferred.point_light') - }), - - _createLightPassMat: createLightPassMat, - - _lightSphereGeo: new SphereGeo({ - widthSegments: 10, - heightSegements: 10 - }), - - _lightConeGeo: coneGeo - } - }, { - render: function (renderer, scene, camera) { - - var gl = renderer.gl; - - scene.update(false, true); - - camera.update(true); - - var opaqueQueue = scene.opaqueQueue; - opaqueQueue.sort(ForwardRenderer.opaqueSortFunc); - - // Replace material - for (var i = 0; i < opaqueQueue.length; i++) { - this._replaceGBufferMaterial(opaqueQueue[i]); - } - - // Resize GBuffer - var width = renderer.getWidth(); - var height = renderer.getHeight(); - var gBufferFrameBuffer = this._gBufferFrameBuffer; - var gBufferTex = this._gBufferTex; - // var depthTex = this._depthTex; - var lightAccumTex = this._lightAccumTex; - if (width !== gBufferTex.width || height !== gBufferTex.height) { - gBufferTex.width = width; - gBufferTex.height = height; - // depthTex.width = width; - // depthTex.height = height; - lightAccumTex.width = width; - lightAccumTex.height = height; - gBufferTex.dirty(); - // depthTex.dirty(); - lightAccumTex.dirty(); - } - // Render normal and glossiness to GBuffer - gBufferFrameBuffer.attach(gl, gBufferTex); - // FIXME Device that not support depth texture - // gBufferFrameBuffer.attach(gl, depthTex, gl.DEPTH_ATTACHMENT); - gBufferFrameBuffer.bind(renderer); - gl.clearColor(0, 0, 0, 0); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - gl.disable(gl.BLEND); - renderer.renderQueue(opaqueQueue, camera); - gBufferFrameBuffer.unbind(renderer); - - // Accumulate light buffer - this._accumulateLightBuffer(renderer, scene, camera); - - // Final output rendering - var eyePosition = camera.getWorldPosition()._array; - for (var i = 0; i < opaqueQueue.length; i++) { - this._swapOutputMaterial(opaqueQueue[i], eyePosition); - } - - // Clear - if (renderer.clear) { - var cc = renderer.color; - gl.clearColor(cc[0], cc[1], cc[2], cc[3]); - gl.clear(renderer.clear); - } - - gl.disable(gl.BLEND); - renderer.renderQueue(opaqueQueue, camera); - - for (var i = 0; i < opaqueQueue.length; i++) { - // Swap material back - opaqueQueue[i].material = opaqueQueue[i].__standardMat; - } - }, - - _accumulateLightBuffer: function (renderer, scene, camera) { - var gl = renderer.gl; - var lightAccumTex = this._lightAccumTex; - var lightAccumFrameBuffer = this._lightAccumFrameBuffer; - var lightVolumeMeshList = this._lightVolumeMeshList; - var listLen = 0; - var eyePosition = camera.getWorldPosition()._array; - - lightAccumFrameBuffer.attach(gl, lightAccumTex); - lightAccumFrameBuffer.bind(renderer); - gl.clearColor(0, 0, 0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - gl.enable(gl.BLEND); - - var viewProjectionInv = new Matrix4(); - Matrix4.multiply(viewProjectionInv, camera.worldTransform, camera.invProjectionMatrix); - - var viewportSize = [lightAccumTex.width, lightAccumTex.height]; - var volumeMeshList = []; - - for (var i = 0; i < scene.lights.length; i++) { - var light = scene.lights[i]; - var uTpl = light.uniformTemplates; - - this._updateLightProxy(light); - - var volumeMesh = light.volumeMesh || light.__volumeMesh; - - if (volumeMesh) { - var material = volumeMesh.material; - // Volume mesh will affect the scene bounding box when rendering - // if castShadow is true - volumeMesh.castShadow = false; - - material.setUniform('eyePosition', eyePosition); - material.setUniform('viewportSize', viewportSize); - material.setUniform('viewProjectionInv', viewProjectionInv._array); - material.setUniform('normalTex', this._gBufferTex); - - switch (light.type) { - case 'POINT_LIGHT': - material.setUniform('lightColor', uTpl.pointLightColor.value(light)); - material.setUniform('lightRange', uTpl.pointLightRange.value(light)); - material.setUniform('lightPosition', uTpl.pointLightPosition.value(light)); - break; - case 'SPOT_LIGHT': - material.setUniform('lightPosition', uTpl.spotLightPosition.value(light)); - material.setUniform('lightColor', uTpl.spotLightColor.value(light)); - material.setUniform('lightRange', uTpl.spotLightRange.value(light)); - material.setUniform('lightDirection', uTpl.spotLightDirection.value(light)); - material.setUniform('umbraAngleCosine', uTpl.spotLightUmbraAngleCosine.value(light)); - material.setUniform('penumbraAngleCosine', uTpl.spotLightPenumbraAngleCosine.value(light)); - material.setUniform('falloffFactor', uTpl.spotLightFalloffFactor.value(light)); - break; - } - - volumeMeshList.push(volumeMesh); - } - else { - var pass = this._fullQuadPass; - // Full quad light - switch (light.type) { - case 'AMBIENT_LIGHT': - pass.material = this._ambientMat; - pass.material.set('lightColor', uTpl.ambientLightColor.value(light)); - break; - case 'DIRECTIONAL_LIGHT': - pass.material = this._directionalLightMat; - pass.material.set('lightColor', uTpl.directionalLightColor.value(light)); - pass.material.set('lightDirection', uTpl.directionalLightDirection.value(light)); - break; - } - pass.material.set('eyePosition', eyePosition); - pass.material.set('viewportSize', viewportSize); - pass.material.set('viewProjectionInv', viewProjectionInv._array); - pass.material.set('normalTex', this._gBufferTex); - - pass.renderQuad(renderer); - } - } - - this._renderVolumeMeshList(renderer, camera, volumeMeshList); - - lightAccumFrameBuffer.unbind(renderer); - }, - - // Update light volume mesh - // Light volume mesh is rendered in light accumulate pass instead of full quad. - // It will reduce pixels significantly when local light is relatively small. - // And we can use custom volume mesh to shape the light. - // - // See "Deferred Shading Optimizations" in GDC2011 - _updateLightProxy: function (light) { - var volumeMesh; - if (light.volumeMesh) { - volumeMesh = light.volumeMesh; - } - else { - switch (light.type) { - // Only local light (point and spot) needs volume mesh. - // Directional and ambient light renders in full quad - case 'POINT_LIGHT': - // Volume mesh created automatically - light.__volumeMesh = light.__volumeMesh || new Mesh({ - material: this._createLightPassMat(this._pointLightShader), - geometry: this._lightSphereGeo, - // Disable culling - // if light volume mesh intersect camera near plane - // We need mesh inside can still be rendered - culling: false - }); - volumeMesh = light.__volumeMesh; - var r = light.range; - volumeMesh.scale.set(r, r, r); - break; - case 'SPOT_LIGHT': - light.__volumeMesh = light.__volumeMesh || new Mesh({ - material: this._createLightPassMat(this._spotLightShader), - geometry: this._lightConeGeo, - culling: false - }); - volumeMesh = light.__volumeMesh; - var aspect = Math.tan(light.penumbraAngle * Math.PI / 180); - volumeMesh.scale.set( - aspect * light.range, aspect * light.range, light.range / 2 - ); - break; - } - } - if (volumeMesh) { - volumeMesh.update(); - // Apply light transform - Matrix4.multiply(volumeMesh.worldTransform, light.worldTransform, volumeMesh.worldTransform); - } - }, - - _renderVolumeMeshList: (function () { - var worldViewProjection = new Matrix4(); - var worldView = new Matrix4(); - var preZMaterial = new Material({ - shader: new Shader({ - vertex: Shader.source('buildin.prez.vertex'), - fragment: Shader.source('buildin.prez.fragment') - }) - }); - return function (renderer, camera, volumeMeshList) { - var gl = renderer.gl; - - gl.clear(gl.DEPTH_BUFFER_BIT); - - gl.enable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - gl.blendEquation(gl.FUNC_ADD); - gl.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.ONE); - gl.depthFunc(gl.LEQUAL); - for (var i = 0; i < volumeMeshList.length; i++) { - var volumeMesh = volumeMeshList[i]; - - // Frustum culling - Matrix4.multiply(worldView, camera.viewMatrix, volumeMesh.worldTransform); - if (renderer.isFrustumCulled( - volumeMesh, camera, worldView._array, camera.projectionMatrix._array - )) { - continue; - } - - // Use prez to avoid one pixel rendered twice - gl.colorMask(false, false, false, false); - gl.depthMask(true); - - gl.clear(gl.DEPTH_BUFFER_BIT); - - Matrix4.multiply(worldViewProjection, camera.projectionMatrix, worldView); - - var prezShader = preZMaterial.shader; - this._bindShader(renderer, prezShader); - - var semanticInfo = prezShader.matrixSemantics.WORLDVIEWPROJECTION; - prezShader.setUniform(gl, semanticInfo.type, semanticInfo.symbol, worldViewProjection._array); - volumeMesh.render(gl, preZMaterial); - - // Render light - gl.colorMask(true, true, true, true); - gl.depthMask(false); - var shader = volumeMesh.material.shader; - this._bindShader(renderer, shader); - - var semanticInfo = shader.matrixSemantics.WORLDVIEWPROJECTION; - shader.setUniform(gl, semanticInfo.type, semanticInfo.symbol, worldViewProjection._array); - volumeMesh.material.bind(gl); - volumeMesh.render(gl); - } - - gl.depthFunc(gl.LESS); - } - })(), - - _bindShader: function (renderer, shader) { - var errMsg = shader.bind(renderer.gl); - if (errMsg) { - - if (errorShader[shader.__GUID__]) { - return; - } - errorShader[shader.__GUID__] = true; - - if (renderer.throwError) { - throw new Error(errMsg); - } else { - renderer.trigger('error', errMsg); - } - } - }, - - _replaceGBufferMaterial: function (renderable) { - if (renderable.material instanceof StandardMaterial) { - var standardMat = renderable.material; - renderable.__standardMat = standardMat; - var isDiffuseAlphaGlossiness = standardMat.diffuseMap - && standardMat.diffuseAlphaUsage === 'glossiness'; - renderable.__gBufferMat = renderable.__gBufferMat || new Material(); - var gBufferMat = renderable.__gBufferMat; - - var shader; - if (standardMat.normalMap) { - if (isDiffuseAlphaGlossiness) { - // FIXME Toggle shader may be too frequently - if (gBufferMat.shader !== this._gBufferDiffNormShader) { - gBufferMat.attachShader(this._gBufferDiffNormShader, true); - } - gBufferMat.setUniform('diffuseMap', standardMat.diffuseMap); - } - else { - if (gBufferMat.shader !== this._gBufferNormShader) { - gBufferMat.attachShader(this._gBufferNormShader, true); - } - } - gBufferMat.setUniform('normalMap', standardMat.normalMap); - } - else { - if (isDiffuseAlphaGlossiness) { - if (gBufferMat.shader !== this._gBufferDiffShader) { - gBufferMat.attachShader(this._gBufferDiffShader, true); - } - gBufferMat.setUniform('diffuseMap', standardMat.diffuseMap); - } - else { - if (gBufferMat.shader !== this._gBufferShader) { - gBufferMat.attachShader(this._gBufferShader, true); - } - } - } - - gBufferMat.set('uvOffset', standardMat.uvOffset); - gBufferMat.setUniform('glossiness', standardMat.glossiness); - - renderable.material = gBufferMat; - } - else { - console.warn('Deferred renderer only support StandardMaterial'); - } - }, - - _swapOutputMaterial: function (renderable, eyePosition) { - if (renderable.__standardMat) { - var standardMat = renderable.__standardMat; - - renderable.__deferredOutputMat = renderable.__deferredOutputMat || new Material(); - var outputMat = renderable.__deferredOutputMat; - - if (standardMat.diffuseMap) { - if (outputMat.shader !== this._outputDiffShader) { - outputMat.attachShader(this._outputDiffShader, true); - } - - outputMat.setUniform('diffuseMap', standardMat.diffuseMap); - } - else { - if (outputMat.shader !== this._outputShader) { - outputMat.attachShader(this._outputShader, true); - } - } - - outputMat.set('eyePosition', eyePosition); - - outputMat.set('color', standardMat.color); - outputMat.set('emission', standardMat.emission); - outputMat.set('specularColor', standardMat.specularColor); - - outputMat.set('uvRepeat', standardMat.uvRepeat); - outputMat.set('uvOffset', standardMat.uvOffset); - outputMat.set('specularColor', standardMat.specularColor); - - outputMat.set('lightAccumTex', this._lightAccumTex); - outputMat.set('normalTex', this._gBufferTex); - - renderable.material = outputMat; - } - } - }); - - return DeferredRenderer; -}); -define('qtek/geometry/Cube',['require','../DynamicGeometry','./Plane','../math/Matrix4','../math/Vector3','../math/BoundingBox'],function(require) { - - - - var DynamicGeometry = require('../DynamicGeometry'); - var Plane = require('./Plane'); - var Matrix4 = require('../math/Matrix4'); - var Vector3 = require('../math/Vector3'); - var BoundingBox = require('../math/BoundingBox'); - - var planeMatrix = new Matrix4(); - - /** - * @constructor qtek.geometry.Cube - * @extends qtek.DynamicGeometry - * @param {Object} [opt] - * @param {number} [opt.widthSegments] - * @param {number} [opt.heightSegments] - * @param {number} [opt.depthSegments] - * @param {boolean} [opt.inside] - */ - var Cube = DynamicGeometry.derive( - /**@lends qtek.geometry.Cube# */ - { - /** - * @type {number} - */ - widthSegments: 1, - /** - * @type {number} - */ - heightSegments: 1, - /** - * @type {number} - */ - depthSegments: 1, - /** - * @type {boolean} - */ - inside: false - }, function() { - this.build(); - }, - /** @lends qtek.geometry.Cube.prototype */ - { - /** - * Build cube geometry - */ - build: function() { - - this.faces.length = 0; - this.attributes.position.value.length = 0; - this.attributes.texcoord0.value.length = 0; - this.attributes.normal.value.length = 0; - - var planes = { - 'px': createPlane('px', this.depthSegments, this.heightSegments), - 'nx': createPlane('nx', this.depthSegments, this.heightSegments), - 'py': createPlane('py', this.widthSegments, this.depthSegments), - 'ny': createPlane('ny', this.widthSegments, this.depthSegments), - 'pz': createPlane('pz', this.widthSegments, this.heightSegments), - 'nz': createPlane('nz', this.widthSegments, this.heightSegments), - }; - var cursor = 0; - var attrList = ['position', 'texcoord0', 'normal']; - for (var pos in planes) { - for (var k = 0; k < attrList.length; k++) { - var attrName = attrList[k]; - var attrArray = planes[pos].attributes[attrName].value; - for (var i = 0; i < attrArray.length; i++) { - var value = attrArray[i]; - if (this.inside && attrName === 'normal') { - value[0] = -value[0]; - value[1] = -value[1]; - value[2] = -value[2]; - } - this.attributes[attrName].value.push(value); - } - } - var plane = planes[pos]; - for (var i = 0; i < plane.faces.length; i++) { - var face = plane.faces[i]; - this.faces.push([face[0]+cursor, face[1]+cursor, face[2]+cursor]); - } - - cursor += planes[pos].getVertexNumber(); - } - - this.boundingBox = new BoundingBox(); - this.boundingBox.max.set(1, 1, 1); - this.boundingBox.min.set(-1, -1, -1); - } - }); - - function createPlane(pos, widthSegments, heightSegments) { - - planeMatrix.identity(); - - var plane = new Plane({ - widthSegments: widthSegments, - heightSegments: heightSegments - }); - - switch(pos) { - case 'px': - Matrix4.translate(planeMatrix, planeMatrix, Vector3.POSITIVE_X); - Matrix4.rotateY(planeMatrix, planeMatrix, Math.PI / 2); - break; - case 'nx': - Matrix4.translate(planeMatrix, planeMatrix, Vector3.NEGATIVE_X); - Matrix4.rotateY(planeMatrix, planeMatrix, -Math.PI / 2); - break; - case 'py': - Matrix4.translate(planeMatrix, planeMatrix, Vector3.POSITIVE_Y); - Matrix4.rotateX(planeMatrix, planeMatrix, -Math.PI / 2); - break; - case 'ny': - Matrix4.translate(planeMatrix, planeMatrix, Vector3.NEGATIVE_Y); - Matrix4.rotateX(planeMatrix, planeMatrix, Math.PI / 2); - break; - case 'pz': - Matrix4.translate(planeMatrix, planeMatrix, Vector3.POSITIVE_Z); - break; - case 'nz': - Matrix4.translate(planeMatrix, planeMatrix, Vector3.NEGATIVE_Z); - Matrix4.rotateY(planeMatrix, planeMatrix, Math.PI); - break; - } - plane.applyTransform(planeMatrix); - return plane; - } - - return Cube; -}); -define('qtek/geometry/Cylinder',['require','../DynamicGeometry','./Cone'],function(require) { - - - - var DynamicGeometry = require('../DynamicGeometry'); - var ConeGeometry = require('./Cone'); - - /** - * @constructor qtek.geometry.Cylinder - * @extends qtek.DynamicGeometry - * @param {Object} [opt] - * @param {number} [opt.radius] - * @param {number} [opt.height] - * @param {number} [opt.capSegments] - * @param {number} [opt.heightSegments] - */ - var Cylinder = DynamicGeometry.derive( - /** @lends qtek.geometry.Cylinder# */ - { - /** - * @type {number} - */ - radius: 1, - - /** - * @type {number} - */ - height: 2, - - /** - * @type {number} - */ - capSegments: 50, - - /** - * @type {number} - */ - heightSegments: 1 - }, function() { - this.build(); - }, - /** @lends qtek.geometry.Cylinder.prototype */ - { - /** - * Build cylinder geometry - */ - build: function() { - var cone = new ConeGeometry({ - topRadius: this.radius, - bottomRadius: this.radius, - capSegments: this.capSegments, - heightSegments: this.heightSegments, - height: this.height - }); - - this.attributes.position.value = cone.attributes.position.value; - this.attributes.normal.value = cone.attributes.normal.value; - this.attributes.texcoord0.value = cone.attributes.texcoord0.value; - this.faces = cone.faces; - - this.boundingBox = cone.boundingBox; - } - }); - - return Cylinder; -}); -define('qtek/light/Ambient',['require','../Light'],function(require) { - - - - var Light = require('../Light'); - - /** - * @constructor qtek.light.Ambient - * @extends qtek.Light - */ - var AmbientLight = Light.derive({ - castShadow: false - }, { - - type: 'AMBIENT_LIGHT', - - uniformTemplates: { - 'ambientLightColor': { - type: '3f', - value: function(instance) { - var color = instance.color, - intensity = instance.intensity; - return [color[0]*intensity, color[1]*intensity, color[2]*intensity]; - } - } - } - /** - * @method - * @name clone - * @return {qtek.light.Ambient} - * @memberOf qtek.light.Ambient.prototype - */ - }); - - return AmbientLight; -}); -define('qtek/light/Directional',['require','../Light','../math/Vector3'],function(require) { - - - - var Light = require('../Light'); - var Vector3 = require('../math/Vector3'); - - /** - * @constructor qtek.light.Directional - * @extends qtek.Light - * - * @example - * var light = new qtek.light.Directional({ - * intensity: 0.5, - * color: [1.0, 0.0, 0.0] - * }); - * light.position.set(10, 10, 10); - * light.lookAt(qtek.math.Vector3.ZERO); - * scene.add(light); - */ - var DirectionalLight = Light.derive( - /** @lends qtek.light.Directional# */ - { - /** - * @type {number} - */ - shadowBias: 0.0002, - /** - * @type {number} - */ - shadowSlopeScale: 2.0 - }, { - - type: 'DIRECTIONAL_LIGHT', - - uniformTemplates: { - 'directionalLightDirection': { - type: '3f', - value: (function() { - var z = new Vector3(); - return function(instance) { - return z.copy(instance.worldTransform.z).negate()._array; - }; - })() - }, - 'directionalLightColor': { - type: '3f', - value: function(instance) { - var color = instance.color; - var intensity = instance.intensity; - return [color[0] * intensity, color[1] * intensity, color[2] * intensity]; - } - } - }, - /** - * @return {qtek.light.Directional} - * @memberOf qtek.light.Directional.prototype - */ - clone: function() { - var light = Light.prototype.clone.call(this); - light.shadowBias = this.shadowBias; - light.shadowSlopeScale = this.shadowSlopeScale; - return light; - } - }); - - return DirectionalLight; -}); -define('qtek/light/Point',['require','../Light'],function(require) { - - - - var Light = require('../Light'); - - /** - * @constructor qtek.light.Point - * @extends qtek.Light - */ - var PointLight = Light.derive( - /** @lends qtek.light.Point# */ - { - /** - * @type {number} - */ - range: 100, - - /** - * @type {number} - */ - castShadow: false - }, { - - type: 'POINT_LIGHT', - - uniformTemplates: { - 'pointLightPosition': { - type: '3f', - value: function(instance) { - return instance.getWorldPosition()._array; - } - }, - 'pointLightRange': { - type: '1f', - value: function(instance) { - return instance.range; - } - }, - 'pointLightColor': { - type: '3f', - value: function(instance) { - var color = instance.color, - intensity = instance.intensity; - return [ color[0]*intensity, color[1]*intensity, color[2]*intensity ]; - } - } - }, - /** - * @return {qtek.light.Point} - * @memberOf qtek.light.Point.prototype - */ - clone: function() { - var light = Light.prototype.clone.call(this); - light.range = this.range; - return light; - } - }); - - return PointLight; -}); -define('qtek/light/Spot',['require','../Light','../math/Vector3'],function(require) { - - - - var Light = require('../Light'); - var Vector3 = require('../math/Vector3'); - - /** - * @constructor qtek.light.Spot - * @extends qtek.Light - */ - var SpotLight = Light.derive( - /**@lends qtek.light.Spot */ - { - /** - * @type {number} - */ - range: 20, - /** - * @type {number} - */ - umbraAngle: 30, - /** - * @type {number} - */ - penumbraAngle: 45, - /** - * @type {number} - */ - falloffFactor: 2.0, - /** - * @type {number} - */ - shadowBias: 0.0002, - /** - * @type {number} - */ - shadowSlopeScale: 2.0 - },{ - - type: 'SPOT_LIGHT', - - uniformTemplates: { - 'spotLightPosition': { - type: '3f', - value: function(instance) { - return instance.getWorldPosition()._array; - } - }, - 'spotLightRange': { - type: '1f', - value: function(instance) { - return instance.range; - } - }, - 'spotLightUmbraAngleCosine': { - type: '1f', - value: function(instance) { - return Math.cos(instance.umbraAngle * Math.PI / 180); - } - }, - 'spotLightPenumbraAngleCosine': { - type: '1f', - value: function(instance) { - return Math.cos(instance.penumbraAngle * Math.PI / 180); - } - }, - 'spotLightFalloffFactor': { - type: '1f', - value: function(instance) { - return instance.falloffFactor; - } - }, - 'spotLightDirection': { - type: '3f', - value: (function() { - var z = new Vector3(); - return function(instance) { - // Direction is target to eye - return z.copy(instance.worldTransform.z).negate()._array; - }; - })() - }, - 'spotLightColor': { - type: '3f', - value: function(instance) { - var color = instance.color; - var intensity = instance.intensity; - return [color[0] * intensity, color[1] * intensity, color[2] * intensity]; - } - } - }, - /** - * @return {qtek.light.Spot} - * @memberOf qtek.light.Spot.prototype - */ - clone: function() { - var light = Light.prototype.clone.call(this); - light.range = this.range; - light.umbraAngle = this.umbraAngle; - light.penumbraAngle = this.penumbraAngle; - light.falloffFactor = this.falloffFactor; - light.shadowBias = this.shadowBias; - light.shadowSlopeScale = this.shadowSlopeScale; - return light; - } - }); - - return SpotLight; -}); -define('qtek/loader/FX',['require','../core/Base','../core/request','../core/util','../compositor/Compositor','../compositor/Node','../Shader','../Texture','../Texture2D','../TextureCube'],function(require) { - - - - var Base = require('../core/Base'); - var request = require('../core/request'); - var util = require('../core/util'); - var Compositor = require('../compositor/Compositor'); - var CompoNode = require('../compositor/Node'); - var Shader = require('../Shader'); - var Texture = require('../Texture'); - var Texture2D = require('../Texture2D'); - var TextureCube = require('../TextureCube'); - - var shaderSourceReg = /#source\((.*?)\)/; - var urlReg = /#url\((.*?)\)/; - - /** - * @constructor qtek.loader.FX - * @extends qtek.core.Base - */ - var FXLoader = Base.derive( - /** @lends qtek.loader.FX# */ - { - /** - * @type {string} - */ - rootPath: '', - /** - * @type {string} - */ - textureRootPath: '', - /** - * @type {string} - */ - shaderRootPath: '' - }, - /** @lends qtek.loader.FX.prototype */ - { - /** - * @param {string} url - */ - load: function(url) { - var self = this; - - if (!this.rootPath) { - this.rootPath = url.slice(0, url.lastIndexOf('/')); - } - - request.get({ - url: url, - onprogress: function(percent, loaded, total) { - self.trigger('progress', percent, loaded, total); - }, - onerror: function(e) { - self.trigger('error', e); - }, - responseType: 'text', - onload: function(data) { - self.parse(JSON.parse(data)); - } - }); - }, - - /** - * @param {Object} json - * @return {qtek.compositor.Compositor} - */ - parse: function(json) { - var self = this; - var compositor = new Compositor(); - - var lib = { - textures: {}, - shaders: {}, - parameters: {} - }; - var afterLoad = function(shaderLib, textureLib) { - for (var i = 0; i < json.nodes.length; i++) { - var nodeInfo = json.nodes[i]; - var node = self._createNode(nodeInfo, lib); - if (node) { - compositor.addNode(node); - } - if (nodeInfo.output) { - compositor.addOutput(node); - } - } - - self.trigger('success', compositor); - }; - - for (var name in json.parameters) { - var paramInfo = json.parameters[name]; - lib.parameters[name] = this._convertParameter(paramInfo); - } - this._loadShaders(json, function(shaderLib) { - self._loadTextures(json, lib, function(textureLib) { - lib.textures = textureLib; - lib.shaders = shaderLib; - afterLoad(); - }); - }); - - return compositor; - }, - - _createNode: function(nodeInfo, lib) { - if (!nodeInfo.shader) { - return; - } - var type = nodeInfo.type || 'filter'; - var shaderSource; - var inputs; - var outputs; - - if (type === 'filter') { - var shaderExp = nodeInfo.shader.trim(); - var res = shaderSourceReg.exec(shaderExp); - if (res) { - shaderSource = Shader.source(res[1].trim()); - } else if (shaderExp.charAt(0) === '#') { - shaderSource = lib.shaders[shaderExp.substr(1)]; - } - if (!shaderSource) { - shaderSource = shaderExp; - } - if (!shaderSource) { - return; - } - } - - if (nodeInfo.inputs) { - inputs = {}; - for (var name in nodeInfo.inputs) { - inputs[name] = { - node: nodeInfo.inputs[name].node, - pin: nodeInfo.inputs[name].pin - }; - } - } - if (nodeInfo.outputs) { - outputs = {}; - for (var name in nodeInfo.outputs) { - var outputInfo = nodeInfo.outputs[name]; - outputs[name] = {}; - if (outputInfo.attachment !== undefined) { - outputs[name].attachment = outputInfo.attachment; - } - if (outputInfo.keepLastFrame !== undefined) { - outputs[name].keepLastFrame = outputInfo.keepLastFrame; - } - if (outputInfo.outputLastFrame !== undefined) { - outputs[name].outputLastFrame = outputInfo.outputLastFrame; - } - if (typeof(outputInfo.parameters) === 'string') { - var paramExp = outputInfo.parameters; - if (paramExp.charAt(0) === '#') { - outputs[name].parameters = lib.parameters[paramExp.substr(1)]; - } - } else if (outputInfo.parameters) { - outputs[name].parameters = this._convertParameter(outputInfo.parameters); - } - } - } - var node; - if (type === 'filter') { - node = new CompoNode({ - name: nodeInfo.name, - shader: shaderSource, - inputs: inputs, - outputs: outputs - }); - } - if (node) { - if (nodeInfo.parameters) { - for (var name in nodeInfo.parameters) { - var val = nodeInfo.parameters[name]; - if (typeof(val) === 'string') { - val = val.trim(); - if (val.charAt(0) === '#'){ - val = lib.textures[val.substr(1)]; - } else if (val.match(/%width$/)) { - node.on('beforerender', createWidthSetHandler(name, val)); - continue; - } else if (val.match(/%height$/)) { - node.on('beforerender', createHeightSetHandler(name, val)); - continue; - } - } else if (val instanceof Array) { - if ( - typeof(val[0]) === 'string' && typeof(val[1]) === 'string' - && (val[0].match(/%width$/)) - && (val[1].match(/%height$/)) - ) { - node.on('beforerender', createSizeSetHandler(name, val)); - continue; - } - } - node.setParameter(name, val); - } - } - if (nodeInfo.defines) { - for (var name in nodeInfo.defines) { - var val = nodeInfo.defines[name]; - node.pass.material.shader.define('fragment', name, val); - } - } - } - return node; - }, - - _convertParameter: function(paramInfo) { - var param = {}; - if (!paramInfo) { - return param; - } - ['type', 'minFilter', 'magFilter', 'wrapS', 'wrapT'] - .forEach(function(name) { - var val = paramInfo[name]; - if (val !== undefined) { - // Convert string to enum - if (typeof(val) === 'string') { - val = Texture[val]; - } - param[name] = val; - } - }); - ['width', 'height'] - .forEach(function(name) { - if (paramInfo[name] !== undefined) { - var val = paramInfo[name]; - if (typeof val === 'string') { - val = val.trim(); - if (val.match(/%width$/)) { - param[name] = percentToWidth.bind(null, parseFloat(val)); - } - else if (val.match(/%height$/)) { - param[name] = percentToHeight.bind(null, parseFloat(val)); - } - } else { - param[name] = val; - } - } - }); - if (paramInfo.useMipmap !== undefined) { - param.useMipmap = paramInfo.useMipmap; - } - return param; - }, - - _loadShaders: function(json, callback) { - if (!json.shaders) { - callback({}); - return; - } - var shaders = {}; - var loading = 0; - var cbd = false; - var shaderRootPath = this.shaderRootPath || this.rootPath; - util.each(json.shaders, function(shaderExp, name) { - var res = urlReg.exec(shaderExp); - if (res) { - var path = res[1]; - path = util.relative2absolute(path, shaderRootPath); - loading++; - request.get({ - url: path, - onload: function(shaderSource) { - shaders[name] = shaderSource; - Shader['import'](shaderSource); - loading--; - if (loading === 0) { - callback(shaders); - cbd = true; - } - } - }); - } else { - shaders[name] = shaderExp; - // Try import shader - Shader['import'](shaderExp); - } - }, this); - if (loading === 0 && !cbd) { - callback(shaders); - } - }, - - _loadTextures: function(json, lib, callback) { - if (!json.textures) { - callback({}); - return; - } - var textures = {}; - var loading = 0; - - var cbd = false; - var textureRootPath = this.textureRootPath || this.rootPath; - util.each(json.textures, function(textureInfo, name) { - var texture; - var path = textureInfo.path; - var parameters = this._convertParameter(textureInfo.parameters); - if (path instanceof Array && path.length === 6) { - path = path.map(function(item) { - return util.relative2absolute(item, textureRootPath); - }); - texture = new TextureCube(parameters); - } else if(typeof(path) === 'string') { - path = util.relative2absolute(path, textureRootPath); - texture = new Texture2D(parameters); - } else { - return; - } - - texture.load(path); - loading++; - texture.once('success', function() { - textures[name] = texture; - loading--; - if (loading === 0) { - callback(textures); - cbd = true; - } - }); - }, this); - - if (loading === 0 && !cbd) { - callback(textures); - } - } - }); - - function createWidthSetHandler(name, val) { - val = parseFloat(val); - return function (renderer) { - this.setParameter(name, percentToWidth(val, renderer)); - }; - } - function createHeightSetHandler(name, val) { - val = parseFloat(val); - return function (renderer) { - this.setParameter(name, percentToWidth(val, renderer)); - }; - } - - function createSizeSetHandler(name, val) { - val[0] = parseFloat(val[0]); - val[1] = parseFloat(val[1]); - return function (renderer) { - this.setParameter(name, [percentToWidth(val[0], renderer), percentToHeight(val[0], renderer)]); - }; - } - - function percentToWidth(percent, renderer) { - return percent / 100 * renderer.width; - } - - function percentToHeight(percent, renderer) { - return percent / 100 * renderer.height; - } - - return FXLoader; -}); -/** - * glTF Loader - * Specification https://github.com/KhronosGroup/glTF/blob/master/specification/README.md - * - * TODO https://github.com/KhronosGroup/glTF/issues/298 - */ -define('qtek/loader/GLTF',['require','../core/Base','../core/request','../core/util','../Scene','../Shader','../Material','../Mesh','../Node','../Texture','../Texture2D','../TextureCube','../shader/library','../Skeleton','../Joint','../camera/Perspective','../camera/Orthographic','../light/Point','../light/Spot','../light/Directional','../core/glenum','../math/Vector3','../math/Quaternion','../math/BoundingBox','../animation/SamplerClip','../animation/SkinningClip','../StaticGeometry','../dep/glmatrix'],function(require) { - - - - var Base = require('../core/Base'); - var request = require('../core/request'); - var util = require('../core/util'); - - var Scene = require('../Scene'); - var Shader = require('../Shader'); - var Material = require('../Material'); - var Mesh = require('../Mesh'); - var Node = require('../Node'); - var Texture = require('../Texture'); - var Texture2D = require('../Texture2D'); - var TextureCube = require('../TextureCube'); - var shaderLibrary = require('../shader/library'); - var Skeleton = require('../Skeleton'); - var Joint = require('../Joint'); - var PerspectiveCamera = require('../camera/Perspective'); - var OrthographicCamera = require('../camera/Orthographic'); - var PointLight = require('../light/Point'); - var SpotLight = require('../light/Spot'); - var DirectionalLight = require('../light/Directional'); - var glenum = require('../core/glenum'); - - var Vector3 = require('../math/Vector3'); - var Quaternion = require('../math/Quaternion'); - var BoundingBox = require('../math/BoundingBox'); - - var SamplerClip = require('../animation/SamplerClip'); - var SkinningClip = require('../animation/SkinningClip'); - - var StaticGeometry = require('../StaticGeometry'); - - var glMatrix = require('../dep/glmatrix'); - var quat = glMatrix.quat; - - var semanticAttributeMap = { - 'NORMAL': 'normal', - 'POSITION': 'position', - 'TEXCOORD_0': 'texcoord0', - 'WEIGHT': 'weight', - 'JOINT': 'joint', - 'COLOR': 'color' - }; - - /** - * @typedef {Object} qtek.loader.GLTF.IResult - * @property {qtek.Scene} scene - * @property {qtek.Node} rootNode - * @property {Object.} cameras - * @property {Object.} textures - * @property {Object.} materials - * @property {Object.} skeletons - * @property {Object.} meshes - * @property {qtek.animation.SkinningClip} clip - */ - - /** - * @constructor qtek.loader.GLTF - * @extends qtek.core.Base - */ - var GLTFLoader = Base.derive( - /** @lends qtek.loader.GLTF# */ - { - /** - * @type {qtek.Node} - */ - rootNode: null, - /** - * @type {string} - */ - rootPath: '', - - /** - * @type {string} - */ - textureRootPath: '', - - /** - * @type {string} - */ - bufferRootPath: '', - - /** - * @type {string} - */ - shaderName: 'buildin.standard', - - /** - * @type {boolean} - */ - includeCamera: true, - - /** - * @type {boolean} - */ - includeLight: true, - - /** - * @type {boolean} - */ - includeAnimation: true, - /** - * @type {boolean} - */ - includeMesh: true - }, - - /** @lends qtek.loader.GLTF.prototype */ - { - /** - * @param {string} url - */ - load: function(url) { - var self = this; - - if (!this.rootPath) { - this.rootPath = url.slice(0, url.lastIndexOf('/')); - } - - request.get({ - url: url, - onprogress: function(percent, loaded, total) { - self.trigger('progress', percent, loaded, total); - }, - onerror: function(e) { - self.trigger('error', e); - }, - responseType: 'text', - onload: function(data) { - self.parse(JSON.parse(data)); - } - }); - }, - - /** - * @param {Object} json - * @return {qtek.loader.GLTF.IResult} - */ - parse: function(json) { - var self = this; - var loading = 0; - - var lib = { - buffers: {}, - materials: {}, - textures: {}, - meshes: {}, - joints: {}, - skeletons: {}, - cameras: {}, - nodes: {} - }; - // Mount on the root node if given - var rootNode = this.rootNode || new Scene(); - // Load buffers - util.each(json.buffers, function(bufferInfo, name) { - loading++; - self._loadBuffer(bufferInfo.path, function(buffer) { - lib.buffers[name] = buffer; - loading--; - if (loading === 0) { - afterLoadBuffer(); - } - }, function() { - loading--; - if (loading === 0) { - afterLoadBuffer(); - } - }); - }); - - function afterLoadBuffer() { - if (self.includeMesh) { - self._parseTextures(json, lib); - self._parseMaterials(json, lib); - self._parseMeshes(json, lib); - } - self._parseNodes(json, lib); - - var sceneInfo = json.scenes[json.scene]; - for (var i = 0; i < sceneInfo.nodes.length; i++) { - var node = lib.nodes[sceneInfo.nodes[i]]; - node.update(); - rootNode.add(node); - } - - if (self.includeMesh) { - self._parseSkins(json, lib); - } - - if (self.includeAnimation) { - var clip = self._parseAnimations(json, lib); - if (clip) { - for (var name in lib.skeletons) { - lib.skeletons[name].addClip(clip); - } - } - } - - self.trigger('success', { - scene: self.rootNode ? null : rootNode, - rootNode: self.rootNode ? rootNode : null, - cameras: lib.cameras, - textures: lib.textures, - materials: lib.materials, - skeletons: lib.skeletons, - meshes: lib.meshes, - clip: clip - }); - } - - return { - scene: self.rootNode ? null : rootNode, - rootNode: self.rootNode ? rootNode : null, - cameras: lib.cameras, - textures: lib.textures, - materials: lib.materials, - skeletons: lib.skeletons, - meshes: lib.meshes, - clip: null - }; - }, - - _loadBuffer: function(path, onsuccess, onerror) { - var root = this.bufferRootPath || this.rootPath; - if (root) { - path = root + '/' + path; - } - request.get({ - url: path, - responseType: 'arraybuffer', - onload: function(buffer) { - onsuccess && onsuccess(buffer); - }, - onerror: function(buffer) { - onerror && onerror(buffer); - } - }); - }, - - // https://github.com/KhronosGroup/glTF/issues/100 - // https://github.com/KhronosGroup/glTF/issues/193 - _parseSkins: function(json, lib) { - - // Create skeletons and joints - var haveInvBindMatrices = false; - for (var name in json.skins) { - var skinInfo = json.skins[name]; - var skeleton = new Skeleton({ - name: name - }); - for (var i = 0; i < skinInfo.joints.length; i++) { - var jointId = skinInfo.joints[i]; - var joint = new Joint({ - name: jointId, - index: skeleton.joints.length - }); - skeleton.joints.push(joint); - } - if (skinInfo.inverseBindMatrices) { - haveInvBindMatrices = true; - var IBMInfo = skinInfo.inverseBindMatrices; - var bufferViewName = IBMInfo.bufferView; - var bufferViewInfo = json.bufferViews[bufferViewName]; - var buffer = lib.buffers[bufferViewInfo.buffer]; - - var offset = IBMInfo.byteOffset + bufferViewInfo.byteOffset; - var size = IBMInfo.count * 16; - - var array = new Float32Array(buffer, offset, size); - - skeleton._invBindPoseMatricesArray = array; - skeleton._skinMatricesArray = new Float32Array(array.length); - } - lib.skeletons[name] = skeleton; - } - - var bindNodeToJoint = function(jointsMap, nodeName, parentIndex, rootNode) { - var node = lib.nodes[nodeName]; - var nodeInfo = json.nodes[nodeName]; - var joint = jointsMap[nodeInfo.jointId]; - if (joint) { - // throw new Error('Joint bind to ' + nodeInfo.name + ' doesn\'t exist in skin'); - joint.node = node; - joint.parentIndex = parentIndex; - joint.rootNode = rootNode; - parentIndex = joint.index; - } - else { - // Some root node may be a simple transform joint, without deformation data. - // Which is, no vertex is attached to the joint - // PENDING - joint = new Joint({ - node: node, - rootNode: rootNode, - parentIndex: parentIndex - }); - } - - for (var i = 0; i < nodeInfo.children.length; i++) { - bindNodeToJoint(jointsMap, nodeInfo.children[i], parentIndex, rootNode); - } - - return joint; - }; - - var getJointIndex = function(joint) { - return joint.index; - }; - - var instanceSkins = {}; - - for (var name in json.nodes) { - - var nodeInfo = json.nodes[name]; - - if (nodeInfo.instanceSkin) { - var skinName = nodeInfo.instanceSkin.skin; - var skeleton = lib.skeletons[skinName]; - instanceSkins[skinName] = skeleton; - - var node = lib.nodes[name]; - var jointIndices = skeleton.joints.map(getJointIndex); - if (node instanceof Mesh) { - node.skeleton = skeleton; - node.joints = jointIndices; - var material = node.material; - material.shader = material.shader.clone(); - material.shader.define('vertex', 'SKINNING'); - material.shader.define('vertex', 'JOINT_NUMBER', jointIndices.length); - } else { - // Mesh have multiple primitives - for (var i = 0; i < node._children.length; i++) { - var child = node._children[i]; - if (child.skeleton) { - child.skeleton = skeleton; - child.joints = jointIndices; - var material = child.material; - material.shader = material.shader.clone(); - material.shader.define('vertex', 'SKINNING'); - material.shader.define('vertex', 'JOINT_NUMBER', jointIndices.length); - } - } - } - - var jointsMap = {}; - for (var i = 0; i < skeleton.joints.length; i++) { - var joint = skeleton.joints[i]; - jointsMap[joint.name] = joint; - } - // Build up hierarchy from root nodes - var rootNodes = nodeInfo.instanceSkin.skeletons; - for (i = 0; i < rootNodes.length; i++) { - var rootNode = lib.nodes[rootNodes[i]]; - var rootJoint = bindNodeToJoint(jointsMap, rootNodes[i], -1, rootNode); - // Root joint may not in the skeleton - if (rootJoint) { - skeleton.roots.push(rootJoint); - } - } - } - } - - for (var name in instanceSkins) { - var skeleton = instanceSkins[name]; - if (haveInvBindMatrices) { - skeleton.updateMatricesSubArrays(); - } else { - skeleton.updateJointMatrices(); - } - skeleton.update(); - } - }, - - _parseTextures: function(json, lib) { - var root = this.textureRootPath || this.rootPath; - util.each(json.textures, function(textureInfo, name){ - var samplerInfo = json.samplers[textureInfo.sampler]; - var parameters = {}; - ['wrapS', 'wrapT', 'magFilter', 'minFilter'] - .forEach(function(name) { - var value = samplerInfo[name]; - if (value !== undefined) { - if (typeof(value) === 'string') { - // DEPRECATED, sampler parameter now use gl enum instead of string - value = glenum[value]; - } - parameters[name] = value; - } - }); - - var target = textureInfo.target; - var format = textureInfo.format; - if (typeof(target) === 'string') { - // DEPRECATED - target = glenum[target]; - format = glenum[format]; - } - parameters.format = format; - - if (target === glenum.TEXTURE_2D) { - var texture = new Texture2D(parameters); - var imageInfo = json.images[textureInfo.source]; - texture.load(util.relative2absolute(imageInfo.path, root)); - lib.textures[name] = texture; - } else if(target === glenum.TEXTURE_CUBE_MAP) { - // TODO - } - }, this); - }, - - // Only phong material is support yet - // TODO support custom material - _parseMaterials: function(json, lib) { - var techniques = {}; - // Parse techniques - for (var name in json.techniques) { - var techniqueInfo = json.techniques[name]; - // Default phong shader - // var shader = new Shader({ - // vertex: Shader.source('buildin.phong.vertex'), - // fragment: Shader.source('buildin.phong.fragment') - // }); - techniques[name] = { - // shader: shader, - pass: techniqueInfo.passes[techniqueInfo.pass] - }; - } - for (var name in json.materials) { - var materialInfo = json.materials[name]; - - var instanceTechniqueInfo = materialInfo.instanceTechnique; - var technique = techniques[instanceTechniqueInfo.technique]; - var pass = technique.pass; - var uniforms = {}; - - uniforms = instanceTechniqueInfo.values; - for (var symbol in uniforms) { - var value = uniforms[symbol]; - // TODO: texture judgement should be more robust - if (typeof(value) === 'string' && lib.textures[value]) { - uniforms[symbol] = lib.textures[value]; - } - } - var enabledTextures = []; - if (uniforms['diffuse'] instanceof Texture2D) { - enabledTextures.push('diffuseMap'); - } - if (uniforms['normalMap'] instanceof Texture2D) { - enabledTextures.push('normalMap'); - } - var material = new Material({ - name: materialInfo.name, - shader: shaderLibrary.get(this.shaderName, enabledTextures) - }); - if (pass.states.depthMask !== undefined) { - material.depthMask = pass.states.depthMask; - } - if (pass.states.depthTestEnable !== undefined) { - material.depthTest = pass.states.depthTestEnable; - } - material.cullFace = pass.states.cullFaceEnable || false; - if (pass.states.blendEnable) { - material.transparent = true; - // TODO blend Func and blend Equation - } - - if (uniforms['diffuse']) { - // Color - if (uniforms['diffuse'] instanceof Array) { - material.set('color', uniforms['diffuse'].slice(0, 3)); - } else { // Texture - material.set('diffuseMap', uniforms['diffuse']); - } - } - if (uniforms['normalMap'] !== undefined) { - material.set('normalMap', uniforms['normalMap']); - } - if (uniforms['emission'] !== undefined) { - material.set('emission', uniforms['emission'].slice(0, 3)); - } - if (uniforms['shininess'] !== undefined) { - material.set('glossiness', Math.log(uniforms['shininess']) / Math.log(8192)); - material.set('shininess', uniforms['shininess']); - } else { - material.set('glossiness', 0.5); - material.set('shininess', 0.5); - } - if (uniforms['specular'] !== undefined) { - material.set('specularColor', uniforms['specular'].slice(0, 3)); - } - if (uniforms['transparency'] !== undefined) { - material.set('alpha', uniforms['transparency']); - } - - lib.materials[name] = material; - } - }, - - _parseMeshes: function(json, lib) { - var self = this; - - var meshKeys = Object.keys(json.meshes); - for (var nn = 0; nn < meshKeys.length; nn++) { - var name = meshKeys[nn]; - var meshInfo = json.meshes[name]; - - lib.meshes[name] = []; - // Geometry - for (var pp = 0; pp < meshInfo.primitives.length; pp++) { - var primitiveInfo = meshInfo.primitives[pp]; - var geometry = new StaticGeometry({ - boundingBox: new BoundingBox() - }); - // Parse attributes - var semantics = Object.keys(primitiveInfo.attributes); - for (var ss = 0; ss < semantics.length; ss++) { - var semantic = semantics[ss]; - var accessorName = primitiveInfo.attributes[semantic]; - var attributeInfo = json.accessors[accessorName]; - var attributeName = semanticAttributeMap[semantic]; - if (!attributeName) { - continue; - } - var attributeType = attributeInfo.type; - var bufferViewInfo = json.bufferViews[attributeInfo.bufferView]; - var buffer = lib.buffers[bufferViewInfo.buffer]; - var byteOffset = bufferViewInfo.byteOffset + attributeInfo.byteOffset; - - var size; - var ArrayCtor; - var type; - switch(attributeType) { - case 0x8B50: // FLOAT_VEC2 - size = 2; - type = 'float'; - ArrayCtor = Float32Array; - break; - case 0x8B51: // FLOAT_VEC3 - size = 3; - type = 'float'; - ArrayCtor = Float32Array; - break; - case 0x8B52: // FLOAT_VEC4 - size = 4; - type = 'float'; - ArrayCtor = Float32Array; - break; - case 0x1406: // FLOAT - size = 1; - type = 'float'; - ArrayCtor = Float32Array; - break; - default: - console.warn('Attribute type '+attributeInfo.type+' not support yet'); - break; - } - var attributeArray = new ArrayCtor(buffer, byteOffset, attributeInfo.count * size); - if (semantic === 'WEIGHT' && size === 4) { - // Weight data in QTEK has only 3 component, the last component can be evaluated since it is normalized - var weightArray = new ArrayCtor(attributeInfo.count * 3); - for (var i = 0; i < attributeInfo.count; i++) { - weightArray[i * 3] = attributeArray[i * 4]; - weightArray[i * 3 + 1] = attributeArray[i * 4 + 1]; - weightArray[i * 3 + 2] = attributeArray[i * 4 + 2]; - } - geometry.attributes[attributeName].value = weightArray; - } else { - geometry.attributes[attributeName].value = attributeArray; - } - if (semantic === 'POSITION') { - // Bounding Box - var min = attributeInfo.min; - var max = attributeInfo.max; - if (min) { - geometry.boundingBox.min.set(min[0], min[1], min[2]); - } - if (max) { - geometry.boundingBox.max.set(max[0], max[1], max[2]); - } - } - } - - // Parse indices - var indicesInfo = json.accessors[primitiveInfo.indices]; - - var bufferViewInfo = json.bufferViews[indicesInfo.bufferView]; - var buffer = lib.buffers[bufferViewInfo.buffer]; - var byteOffset = bufferViewInfo.byteOffset + indicesInfo.byteOffset; - - // index uint - if (indicesInfo.type === 0x1405) { // UNSIGNED_INT - geometry.faces = new Uint32Array(buffer, byteOffset, indicesInfo.count); - } - else { // UNSIGNED_SHORT, 0x1403 - geometry.faces = new Uint16Array(buffer, byteOffset, indicesInfo.count); - } - - var material = lib.materials[primitiveInfo.material]; - //Collada export from blender may not have default material - if (!material) { - material = new Material({ - shader: shaderLibrary.get(self.shaderName) - }); - } - var mesh = new Mesh({ - geometry: geometry, - material: material - }); - if (material.shader.isTextureEnabled('normalMap')) { - if (!mesh.geometry.attributes.tangent.value) { - mesh.geometry.generateTangents(); - } - } - - if (meshInfo.name) { - if (meshInfo.primitives.length > 1) { - mesh.name = [meshInfo.name, pp].join('-'); - } - else { - // PENDING name or meshInfo.name ? - mesh.name = meshInfo.name; - } - } - - lib.meshes[name].push(mesh); - } - } - }, - - _parseNodes: function(json, lib) { - - for (var name in json.nodes) { - var nodeInfo = json.nodes[name]; - var node; - if (nodeInfo.camera && this.includeCamera) { - var cameraInfo = json.cameras[nodeInfo.camera]; - - if (cameraInfo.projection === 'perspective') { - node = new PerspectiveCamera({ - name: nodeInfo.name, - aspect: cameraInfo.aspect_ratio, - fov: cameraInfo.xfov, - far: cameraInfo.zfar, - near: cameraInfo.znear - }); - } else { - // TODO - node = new OrthographicCamera(); - console.warn('TODO:Orthographic camera'); - } - node.setName(nodeInfo.name); - lib.cameras[nodeInfo.name] = node; - } - else if (nodeInfo.lights && this.includeLight) { - var lights = []; - for (var i = 0; i < nodeInfo.lights.length; i++) { - var lightInfo = json.lights[nodeInfo.lights[i]]; - var light = this._parseLight(lightInfo); - if (light) { - lights.push(light); - } - } - if (lights.length == 1) { - // Replace the node with light - node = lights[0]; - node.setName(nodeInfo.name); - } else { - node = new Node(); - node.setName(nodeInfo.name); - for (var i = 0; i < lights.length; i++) { - node.add(lights[i]); - } - } - } - else if ((nodeInfo.meshes || nodeInfo.instanceSkin) && this.includeMesh) { - // TODO one node have multiple meshes ? - var meshKey; - if (nodeInfo.meshes) { - meshKey = nodeInfo.meshes[0]; - } else { - meshKey = nodeInfo.instanceSkin.sources[0]; - } - if (meshKey) { - var primitives = lib.meshes[meshKey]; - if (primitives) { - if (primitives.length === 1) { - // Replace the node with mesh directly - node = primitives[0]; - node.setName(nodeInfo.name); - } else { - node = new Node(); - node.setName(nodeInfo.name); - for (var j = 0; j < primitives.length; j++) { - if (nodeInfo.instanceSkin) { - primitives[j].skeleton = nodeInfo.instanceSkin.skin; - } - node.add(primitives[j]); - } - } - } - } - } else { - node = new Node(); - node.setName(nodeInfo.name); - } - if (nodeInfo.matrix) { - for (var i = 0; i < 16; i++) { - node.localTransform._array[i] = nodeInfo.matrix[i]; - } - node.decomposeLocalTransform(); - } else { - if (nodeInfo.translation) { - node.position.setArray(nodeInfo.translation); - } - if (nodeInfo.rotation) { - // glTF use axis angle in rotation - // https://github.com/KhronosGroup/glTF/issues/144 - quat.setAxisAngle(node.rotation._array, nodeInfo.rotation.slice(0, 3), nodeInfo.rotation[3]); - node.rotation._dirty = true; - } - if (nodeInfo.scale) { - node.scale.setArray(nodeInfo.scale); - } - } - - lib.nodes[name] = node; - } - - // Build hierarchy - for (var name in json.nodes) { - var nodeInfo = json.nodes[name]; - var node = lib.nodes[name]; - if (nodeInfo.children) { - for (var i = 0; i < nodeInfo.children.length; i++) { - var childName = nodeInfo.children[i]; - var child = lib.nodes[childName]; - node.add(child); - } - } - } - }, - - _parseLight: function(lightInfo) { - // TODO Light parameters - switch(lightInfo.type) { - case 'point': - var light = new PointLight({ - name: lightInfo.id, - color: lightInfo.point.color, - }); - break; - case 'spot': - var light = new SpotLight({ - name: lightInfo.id, - color: lightInfo.spot.color - }); - break; - case 'directional': - var light = new DirectionalLight({ - name: lightInfo.id, - color: lightInfo.directional.color - }); - break; - default: - console.warn('Light ' + lightInfo.type + ' not support yet'); - } - - return light; - }, - - _parseAnimations: function(json, lib) { - // TODO Only support nodes animation now - var clip = new SkinningClip(); - // Default loop the skinning animation - clip.setLoop(true); - var haveAnimation = false; - - var jointClips = {}; - - var quatTmp = quat.create(); - - for (var animName in json.animations) { - haveAnimation = true; - var animationInfo = json.animations[animName]; - var parameters = {}; - - for (var paramName in animationInfo.parameters) { - var accessorName = animationInfo.parameters[paramName]; - var accessorInfo = json.accessors[accessorName]; - - var bufferViewInfo = json.bufferViews[accessorInfo.bufferView]; - var buffer = lib.buffers[bufferViewInfo.buffer]; - var byteOffset = bufferViewInfo.byteOffset + accessorInfo.byteOffset; - switch(accessorInfo.type) { - case 0x8B50: // FLOAT_VEC2 - var size = 2; - break; - case 0x8B51: // FLOAT_VEC3 - var size = 3; - break; - case 0x8B52: // FLOAT_VEC4 - var size = 4; - break; - case 0x1406: // FLOAT - var size = 1; - break; - } - parameters[paramName] = new Float32Array(buffer, byteOffset, size * accessorInfo.count); - } - - if (!parameters.TIME || !animationInfo.channels.length) { - continue; - } - - // Use the first channels target - var targetId = animationInfo.channels[0].target.id; - var targetNode = lib.nodes[targetId]; - - // glTF use axis angle in rotation, convert to quaternion - // https://github.com/KhronosGroup/glTF/issues/144 - var rotationArr = parameters.rotation; - if (rotationArr) { - for (var i = 0; i < parameters.TIME.length; i++) { - parameters.TIME[i] *= 1000; - var offset = i * 4; - if (rotationArr) { - quatTmp[0] = rotationArr[offset]; - quatTmp[1] = rotationArr[offset + 1]; - quatTmp[2] = rotationArr[offset + 2]; - quat.setAxisAngle(quatTmp, quatTmp, rotationArr[offset + 3]); - parameters.rotation[offset] = quatTmp[0]; - parameters.rotation[offset + 1] = quatTmp[1]; - parameters.rotation[offset + 2] = quatTmp[2]; - parameters.rotation[offset + 3] = quatTmp[3]; - } - } - } - - // TODO - // if (jointClips[targetId]) { - // continue; - // } - jointClips[targetId] = new SamplerClip({ - name: targetNode.name - }); - var jointClip = jointClips[targetId]; - jointClip.channels.time = parameters.TIME; - jointClip.channels.rotation = parameters.rotation || null; - jointClip.channels.position = parameters.translation || null; - jointClip.channels.scale = parameters.scale || null; - jointClip.life = parameters.TIME[parameters.TIME.length - 1]; - } - - for (var targetId in jointClips) { - clip.addJointClip(jointClips[targetId]); - } - - if (haveAnimation) { - return clip; - } else { - return null; - } - } - }); - - return GLTFLoader; -}); -/** - * Load three.js JSON Format model - * - * Format specification https://github.com/mrdoob/three.js/wiki/JSON-Model-format-3.1 - */ -define('qtek/loader/ThreeModel',['require','../core/Base','../core/request','../core/util','../Shader','../Material','../DynamicGeometry','../Mesh','../Node','../Texture2D','../TextureCube','../shader/library','../Skeleton','../Joint','../math/Vector3','../math/Quaternion','../core/glenum','../animation/SkinningClip','../dep/glmatrix'],function(require) { - - var Base = require('../core/Base'); - - var request = require('../core/request'); - var util = require('../core/util'); - var Shader = require('../Shader'); - var Material = require('../Material'); - var DynamicGeometry = require('../DynamicGeometry'); - var Mesh = require('../Mesh'); - var Node = require('../Node'); - var Texture2D = require('../Texture2D'); - var TextureCube = require('../TextureCube'); - var shaderLibrary = require('../shader/library'); - var Skeleton = require('../Skeleton'); - var Joint = require('../Joint'); - var Vector3 = require('../math/Vector3'); - var Quaternion = require('../math/Quaternion'); - var glenum = require('../core/glenum'); - var SkinningClip = require('../animation/SkinningClip'); - - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - var quat = glMatrix.quat; - - /** - * @constructor qtek.loader.ThreeModel - * @extends qtek.core.Base - */ - var Loader = Base.derive( - /** @lends qtek.loader.ThreeModel# */ - { - /** - * @type {string} - */ - rootPath: '', - /** - * @type {string} - */ - textureRootPath: '' - }, { - /** - * @param {string} url - */ - load: function(url) { - var self = this; - - if (!this.rootPath) { - this.rootPath = url.slice(0, url.lastIndexOf('/')); - } - - request.get({ - url: url, - onprogress: function(percent, loaded, total) { - self.trigger('progress', percent, loaded, total); - }, - onerror: function(e) { - self.trigger('error', e); - }, - responseType: 'text', - onload: function(data) { - self.parse(JSON.parse(data)); - } - }); - }, - - /** - * @param {Object} json - * @return {Array.} - */ - parse: function(data) { - - var geometryList = this._parseGeometry(data); - - var dSkinIndices = data.skinIndices; - var dSkinWeights = data.skinWeights; - var skinned = dSkinIndices && dSkinIndices.length - && dSkinWeights && dSkinWeights.length; - - var jointNumber; - var skeleton; - if (skinned) { - skeleton = this._parseSkeleton(data); - jointNumber = skeleton.joints.length; - } else { - jointNumber = 0; - } - - var meshList = []; - for (var i = 0; i < data.materials.length; i++) { - var geometry = geometryList[i]; - if ( - geometry && geometry.faces.length && geometry.attributes.position.value.length - ) { - geometry.updateBoundingBox(); - var material = this._parseMaterial(data.materials[i], jointNumber); - var mesh = new Mesh({ - geometry: geometryList[i], - material: material - }) ; - if (skinned) { - mesh.skeleton = skeleton; - for (var i = 0; i < skeleton.joints.length; i++) { - // Use all the joints of skeleton - mesh.joints[i] = i; - } - } - meshList.push(mesh); - } - } - - this.trigger('success', meshList); - - return meshList; - }, - - _parseGeometry: function(data) { - - var geometryList = []; - var cursorList = []; - - for (var i = 0; i < data.materials.length; i++) { - geometryList[i] = null; - cursorList[i] = 0; - } - geometryList[0] = new DynamicGeometry(); - - var dFaces = data.faces; - var dVertices = data.vertices; - var dNormals = data.normals; - var dColors = data.colors; - var dSkinIndices = data.skinIndices; - var dSkinWeights = data.skinWeights; - var dUvs = data.uvs; - - var skinned = dSkinIndices && dSkinIndices.length - && dSkinWeights && dSkinWeights.length; - - var geometry = geometryList[0]; - var attributes = geometry.attributes; - var positions = attributes.position.value; - var normals = attributes.normal.value; - var texcoords = [ - attributes.texcoord0.value, - attributes.texcoord1.value - ]; - var colors = attributes.color.value; - var jointIndices = attributes.joint.value; - var jointWeights = attributes.weight.value; - var faces = geometry.faces; - - var nUvLayers = 0; - if (dUvs[0] && dUvs[0].length) { - nUvLayers++; - } - if (dUvs[1] && dUvs[1].length) { - nUvLayers++; - } - - var offset = 0; - var len = dFaces.length; - - // Cache the reorganized index - var newIndexMap = []; - var geoIndexMap = []; - for (var i = 0; i < dVertices.length; i++) { - newIndexMap[i] = -1; - geoIndexMap[i] = -1; - } - - var currentGeometryIndex = 0; - var isNew = []; - function getNewIndex(oi, faceIndex) { - if ( newIndexMap[oi] >= 0) { - // Switch to the geometry of existed index - currentGeometryIndex = geoIndexMap[oi]; - geometry = geometryList[currentGeometryIndex]; - attributes = geometry.attributes; - positions = attributes.position.value; - normals = attributes.normal.value; - texcoords = [attributes.texcoord0.value, - attributes.texcoord1.value]; - colors = attributes.color.value; - jointWeights = attributes.weight.value; - jointIndices = attributes.joint.value; - - isNew[faceIndex] = false; - return newIndexMap[oi]; - }else{ - - positions.push([dVertices[oi * 3], dVertices[oi * 3 + 1], dVertices[oi * 3 + 2]]); - //Skin data - if (skinned) { - jointWeights.push([dSkinWeights[oi * 2], dSkinWeights[oi * 2 + 1], 0]); - jointIndices.push([dSkinIndices[oi * 2], dSkinIndices[oi * 2 + 1], -1, -1]); - } - - newIndexMap[oi] = cursorList[materialIndex]; - geoIndexMap[oi] = materialIndex; - - isNew[faceIndex] = true; - return cursorList[materialIndex]++; - } - } - // Put the vertex data of one face here - // Incase the program create amount of tmp arrays and cause - // GC bottleneck - var faceUvs = []; - var faceNormals = []; - var faceColors = []; - for (var i =0; i < 4; i++) { - faceUvs[i] = [0, 0]; - faceNormals[i] = [0, 0, 0]; - faceColors[i] = [0, 0, 0]; - } - var materialIndex = 0; - - while (offset < len) { - var type = dFaces[offset++]; - var isQuad = isBitSet(type, 0); - var hasMaterial = isBitSet(type, 1); - var hasFaceUv = isBitSet(type, 2); - var hasFaceVertexUv = isBitSet(type, 3); - var hasFaceNormal = isBitSet(type, 4); - var hasFaceVertexNormal = isBitSet(type, 5); - var hasFaceColor = isBitSet(type, 6); - var hasFaceVertexColor = isBitSet(type, 7); - - var nVertices = isQuad ? 4 : 3; - - if (hasMaterial) { - materialIndex = dFaces[offset+ (isQuad ? 4 : 3)]; - if (!geometryList[materialIndex]) { - geometryList[materialIndex] = new DynamicGeometry(); - } - geometry = geometryList[materialIndex]; - attributes = geometry.attributes; - positions = attributes.position.value; - normals = attributes.normal.value; - texcoords = [ - attributes.texcoord0.value, - attributes.texcoord1.value - ]; - colors = attributes.color.value; - jointWeights = attributes.weight.value; - jointIndices = attributes.joint.value; - faces = geometry.faces; - } - var i1o, i2o, i3o, i4o; - var i1, i2, i3, i4, i5, i6; - if (isQuad) { - // Split into two triangle faces, 1-2-4 and 2-3-4 - i1o = dFaces[offset++]; - i2o = dFaces[offset++]; - i3o = dFaces[offset++]; - i4o = dFaces[offset++]; - // Face1 - i1 = getNewIndex(i1o, 0); - i2 = getNewIndex(i2o, 1); - i3 = getNewIndex(i4o, 2); - // Face2 - i4 = getNewIndex(i2o, 3); - i5 = getNewIndex(i3o, 4); - i6 = getNewIndex(i4o, 5); - faces.push([i1, i2, i3], [i4, i5, i6]); - } else { - i1 = dFaces[offset++]; - i2 = dFaces[offset++]; - i3 = dFaces[offset++]; - i1 = getNewIndex(i1, 0); - i2 = getNewIndex(i2, 1); - i3 = getNewIndex(i3, 2); - faces.push([i1, i2, i3]); - } - if (hasMaterial) { - offset++; - } - if (hasFaceUv) { - for (var i = 0; i < nUvLayers; i++) { - var uvLayer = dUvs[i]; - var uvIndex = faces[offset++]; - var u = uvLayer[uvIndex*2]; - var v = uvLayer[uvIndex*2+1]; - if (isQuad) { - // Random write of array seems not slow - // http://jsperf.com/random-vs-sequence-array-set - isNew[0] && (texcoords[i][i1] = [u, v]); - isNew[1] && (texcoords[i][i2] = [u, v]); - isNew[2] && (texcoords[i][i3] = [u, v]); - isNew[3] && (texcoords[i][i4] = [u, v]); - isNew[4] && (texcoords[i][i5] = [u, v]); - isNew[5] && (texcoords[i][i6] = [u, v]); - } else { - isNew[0] && (texcoords[i][i1] = [u, v]); - isNew[1] && (texcoords[i][i2] = [u, v]); - isNew[2] && (texcoords[i][i3] = [u, v]); - } - } - } - if (hasFaceVertexUv) { - for (var i = 0; i < nUvLayers; i++) { - var uvLayer = dUvs[i]; - for (var j = 0; j < nVertices; j++) { - var uvIndex = dFaces[offset++]; - faceUvs[j][0] = uvLayer[uvIndex*2]; - faceUvs[j][1] = uvLayer[uvIndex*2+1]; - } - if (isQuad) { - // Use array slice to clone array is incredibly faster than - // Construct from Float32Array - // http://jsperf.com/typedarray-v-s-array-clone/2 - isNew[0] && (texcoords[i][i1] = faceUvs[0].slice()); - isNew[1] && (texcoords[i][i2] = faceUvs[1].slice()); - isNew[2] && (texcoords[i][i3] = faceUvs[3].slice()); - isNew[3] && (texcoords[i][i4] = faceUvs[1].slice()); - isNew[4] && (texcoords[i][i5] = faceUvs[2].slice()); - isNew[5] && (texcoords[i][i6] = faceUvs[3].slice()); - } else { - isNew[0] && (texcoords[i][i1] = faceUvs[0].slice()); - isNew[1] && (texcoords[i][i2] = faceUvs[1].slice()); - isNew[2] && (texcoords[i][i3] = faceUvs[2].slice()); - } - } - } - if (hasFaceNormal) { - var normalIndex = dFaces[offset++]*3; - var x = dNormals[normalIndex++]; - var y = dNormals[normalIndex++]; - var z = dNormals[normalIndex]; - if (isQuad) { - isNew[0] && (normals[i1] = [x, y, z]); - isNew[1] && (normals[i2] = [x, y, z]); - isNew[2] && (normals[i3] = [x, y, z]); - isNew[3] && (normals[i4] = [x, y, z]); - isNew[4] && (normals[i5] = [x, y, z]); - isNew[5] && (normals[i6] = [x, y, z]); - }else{ - isNew[0] && (normals[i1] = [x, y, z]); - isNew[1] && (normals[i2] = [x, y, z]); - isNew[2] && (normals[i3] = [x, y, z]); - } - } - if (hasFaceVertexNormal) { - for (var i = 0; i < nVertices; i++) { - var normalIndex = dFaces[offset++]*3; - faceNormals[i][0] = dNormals[normalIndex++]; - faceNormals[i][1] = dNormals[normalIndex++]; - faceNormals[i][2] = dNormals[normalIndex]; - } - if (isQuad) { - isNew[0] && (normals[i1] = faceNormals[0].slice()); - isNew[1] && (normals[i2] = faceNormals[1].slice()); - isNew[2] && (normals[i3] = faceNormals[3].slice()); - isNew[3] && (normals[i4] = faceNormals[1].slice()); - isNew[4] && (normals[i5] = faceNormals[2].slice()); - isNew[5] && (normals[i6] = faceNormals[3].slice()); - } else { - isNew[0] && (normals[i1] = faceNormals[0].slice()); - isNew[1] && (normals[i2] = faceNormals[1].slice()); - isNew[2] && (normals[i3] = faceNormals[2].slice()); - } - } - if (hasFaceColor) { - var colorIndex = dFaces[offset++]; - var color = hex2rgb(dColors[colorIndex]); - if (isQuad) { - // Does't clone the color here - isNew[0] && (colors[i1] = color); - isNew[1] && (colors[i2] = color); - isNew[2] && (colors[i3] = color); - isNew[3] && (colors[i4] = color); - isNew[4] && (colors[i5] = color); - isNew[5] && (colors[i6] = color); - } else { - isNew[0] && (colors[i1] = color); - isNew[1] && (colors[i2] = color); - isNew[2] && (colors[i3] = color); - } - } - if (hasFaceVertexColor) { - for (var i = 0; i < nVertices; i++) { - var colorIndex = dFaces[offset++]; - faceColors[i] = hex2rgb(dColors[colorIndex]); - } - if (isQuad) { - isNew[0] && (colors[i1] = faceColors[0].slice()); - isNew[1] && (colors[i2] = faceColors[1].slice()); - isNew[2] && (colors[i3] = faceColors[3].slice()); - isNew[3] && (colors[i4] = faceColors[1].slice()); - isNew[4] && (colors[i5] = faceColors[2].slice()); - isNew[5] && (colors[i6] = faceColors[3].slice()); - } else { - isNew[0] && (colors[i1] = faceColors[0].slice()); - isNew[1] && (colors[i2] = faceColors[1].slice()); - isNew[2] && (colors[i3] = faceColors[2].slice()); - } - } - } - - return geometryList; - }, - - _parseSkeleton: function(data) { - var joints = []; - var dBones = data.bones; - for ( var i = 0; i < dBones.length; i++) { - var dBone = dBones[i]; - var joint = new Joint({ - index: i, - parentIndex: dBone.parent, - name: dBone.name - }); - joint.node = new Node({ - name: dBone.name, - position: new Vector3(dBone.pos[0], dBone.pos[1], dBone.pos[2]), - rotation: new Quaternion(dBone.rotq[0], dBone.rotq[1], dBone.rotq[2], dBone.rotq[3]), - scale: new Vector3(dBone.scl[0], dBone.scl[1], dBone.scl[2]) - }); - joints.push(joint); - } - - var skeleton = new Skeleton({ - joints: joints - }); - skeleton.updateHierarchy(); - skeleton.updateJointMatrices(); - skeleton.update(); - - if (data.animation) { - var dFrames = data.animation.hierarchy; - - var jointClips = []; - // Parse Animations - for (var i = 0; i < dFrames.length; i++) { - var channel = dFrames[i]; - var jointPose = jointClips[i] = { - keyFrames: [] - }; - jointPose.name = joints[i].name; - for (var j = 0; j < channel.keys.length; j++) { - var key = channel.keys[j]; - jointPose.keyFrames[j] = {}; - var kf = jointPose.keyFrames[j]; - kf.time = parseFloat(key.time) * 1000; - if (key.pos) { - kf.position = vec3.fromValues(key.pos[0], key.pos[1], key.pos[2]); - } - if (key.rot) { - kf.rotation = quat.fromValues(key.rot[0], key.rot[1], key.rot[2], key.rot[3]); - } - if (key.scl) { - kf.scale = vec3.fromValues(key.scl[0], key.scl[1], key.scl[2]); - } - } - } - - var skinningClip = new SkinningClip({ - jointClips: jointClips - }); - - skeleton.addClip(skinningClip); - } - - return skeleton; - }, - - _parseMaterial: function(mConfig, jointNumber) { - var shaderName = 'buildin.lambert'; - var shading = mConfig.shading && mConfig.shading.toLowerCase(); - if (shading === 'phong' || shading === 'lambert') { - shaderName = 'buildin.' + shading; - } - var enabledTextures = []; - if (mConfig.mapDiffuse) { - enabledTextures.push('diffuseMap'); - } - if (mConfig.mapNormal || mConfig.mapBump) { - enabledTextures.push('normalMap'); - } - var shader; - if (jointNumber === 0) { - shader = shaderLibrary.get(shaderName, enabledTextures); - } else { - // Shader for skinned mesh - shader = new Shader({ - vertex: Shader.source(shaderName+'.vertex'), - fragment: Shader.source(shaderName+'.fragment') - }); - for (var i = 0; i < enabledTextures; i++) { - shader.enableTexture(enabledTextures[i]); - } - shader.define('vertex', 'SKINNING'); - shader.define('vertex', 'JOINT_NUMBER', jointNumber); - } - - var material = new Material({ - shader: shader - }); - if (mConfig.colorDiffuse) { - material.set('color', mConfig.colorDiffuse ); - } else if (mConfig.DbgColor) { - material.set('color', hex2rgb(mConfig.DbgColor)); - } - if (mConfig.colorSpecular) { - material.set('specular', mConfig.colorSpecular ); - } - if (mConfig.transparent !== undefined && mConfig.transparent) { - material.transparent = true; - } - if (mConfig.depthTest !== undefined) { - material.depthTest = mConfig.depthTest; - } - if (mConfig.depthWrite !== undefined) { - material.depthMask = mConfig.depthWrite; - } - - if (mConfig.transparency && mConfig.transparency < 1) { - material.set('opacity', mConfig.transparency); - } - if (mConfig.specularCoef) { - material.set('shininess', mConfig.specularCoef); - } - - // Textures - if (mConfig.mapDiffuse) { - material.set('diffuseMap', this._loadTexture(mConfig.mapDiffuse, mConfig.mapDiffuseWrap) ); - } - if (mConfig.mapBump) { - material.set('normalMap', this._loadTexture(mConfig.mapBump, mConfig.mapBumpWrap) ); - } - if (mConfig.mapNormal) { - material.set('normalMap', this._loadTexture(mConfig.mapNormal, mConfig.mapBumpWrap) ); - } - - return material; - }, - - _loadTexture: function(path, wrap) { - var img = new Image(); - var texture = new Texture2D(); - texture.image = img; - - if (wrap && wrap.length) { - texture.wrapS = glenum[wrap[0].toUpperCase()]; - texture.wrapT = glenum[wrap[1].toUpperCase()]; - } - img.onload = function() { - texture.dirty(); - }; - var root = this.textureRootPath || this.rootPath; - img.src = util.relative2absolute(path, root); - - return texture; - } - }); - - - function isBitSet(value, position) { - return value & ( 1 << position ); - } - - - function hex2rgb(hex) { - var r = (hex >> 16) & 0xff; - var g = (hex >> 8) & 0xff; - var b = hex & 0xff; - return [r / 255, g / 255, b / 255]; - } - - return Loader; -}); -define('qtek/math/Matrix2',['require','../dep/glmatrix'],function(require) { - - - - var glMatrix = require('../dep/glmatrix'); - var mat2 = glMatrix.mat2; - - function makeProperty(n) { - return { - configurable: false, - set: function(value) { - this._array[n] = value; - this._dirty = true; - }, - get: function() { - return this._array[n]; - } - }; - } - - /** - * @constructor - * @alias qtek.math.Matrix2 - */ - var Matrix2 = function() { - - /** - * Storage of Matrix2 - * @type {Float32Array} - */ - this._array = mat2.create(); - - /** - * @type {boolean} - */ - this._dirty = true; - }; - - Matrix2.prototype = { - - constructor: Matrix2, - - /** - * Clone a new Matrix2 - * @return {qtek.math.Matrix2} - */ - clone: function() { - return (new Matrix2()).copy(this); - }, - - /** - * Copy from b - * @param {qtek.math.Matrix2} b - * @return {qtek.math.Matrix2} - */ - copy: function(b) { - mat2.copy(this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Calculate the adjugate of self, in-place - * @return {qtek.math.Matrix2} - */ - adjoint: function() { - mat2.adjoint(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Calculate matrix determinant - * @return {number} - */ - determinant: function() { - return mat2.determinant(this._array); - }, - - /** - * Set to a identity matrix - * @return {qtek.math.Matrix2} - */ - identity: function() { - mat2.identity(this._array); - this._dirty = true; - return this; - }, - - /** - * Invert self - * @return {qtek.math.Matrix2} - */ - invert: function() { - mat2.invert(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Alias for mutiply - * @param {qtek.math.Matrix2} b - * @return {qtek.math.Matrix2} - */ - mul: function(b) { - mat2.mul(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Alias for multiplyLeft - * @param {qtek.math.Matrix2} a - * @return {qtek.math.Matrix2} - */ - mulLeft: function(a) { - mat2.mul(this._array, a._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Multiply self and b - * @param {qtek.math.Matrix2} b - * @return {qtek.math.Matrix2} - */ - multiply: function(b) { - mat2.multiply(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Multiply a and self, a is on the left - * @param {qtek.math.Matrix2} a - * @return {qtek.math.Matrix2} - */ - multiplyLeft: function(a) { - mat2.multiply(this._array, a._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Rotate self by a given radian - * @param {number} rad - * @return {qtek.math.Matrix2} - */ - rotate: function(rad) { - mat2.rotate(this._array, this._array, rad); - this._dirty = true; - return this; - }, - - /** - * Scale self by s - * @param {qtek.math.Vector2} s - * @return {qtek.math.Matrix2} - */ - scale: function(v) { - mat2.scale(this._array, this._array, v._array); - this._dirty = true; - return this; - }, - /** - * Transpose self, in-place. - * @return {qtek.math.Matrix2} - */ - transpose: function() { - mat2.transpose(this._array, this._array); - this._dirty = true; - return this; - }, - toString: function() { - return '[' + Array.prototype.join.call(this._array, ',') + ']'; - } - }; - - /** - * @param {Matrix2} out - * @param {Matrix2} a - * @return {Matrix2} - */ - Matrix2.adjoint = function(out, a) { - mat2.adjoint(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix2} out - * @param {qtek.math.Matrix2} a - * @return {qtek.math.Matrix2} - */ - Matrix2.copy = function(out, a) { - mat2.copy(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix2} a - * @return {number} - */ - Matrix2.determinant = function(a) { - return mat2.determinant(a._array); - }; - - /** - * @param {qtek.math.Matrix2} out - * @return {qtek.math.Matrix2} - */ - Matrix2.identity = function(out) { - mat2.identity(out._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix2} out - * @param {qtek.math.Matrix2} a - * @return {qtek.math.Matrix2} - */ - Matrix2.invert = function(out, a) { - mat2.invert(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix2} out - * @param {qtek.math.Matrix2} a - * @param {qtek.math.Matrix2} b - * @return {qtek.math.Matrix2} - */ - Matrix2.mul = function(out, a, b) { - mat2.mul(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @method - * @param {qtek.math.Matrix2} out - * @param {qtek.math.Matrix2} a - * @param {qtek.math.Matrix2} b - * @return {qtek.math.Matrix2} - */ - Matrix2.multiply = Matrix2.mul; - - /** - * @param {qtek.math.Matrix2} out - * @param {qtek.math.Matrix2} a - * @param {number} rad - * @return {qtek.math.Matrix2} - */ - Matrix2.rotate = function(out, a, rad) { - mat2.rotate(out._array, a._array, rad); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix2} out - * @param {qtek.math.Matrix2} a - * @param {qtek.math.Vector2} v - * @return {qtek.math.Matrix2} - */ - Matrix2.scale = function(out, a, v) { - mat2.scale(out._array, a._array, v._array); - out._dirty = true; - return out; - }; - /** - * @param {Matrix2} out - * @param {Matrix2} a - * @return {Matrix2} - */ - Matrix2.transpose = function(out, a) { - mat2.transpose(out._array, a._array); - out._dirty = true; - return out; - }; - - return Matrix2; -}); -define('qtek/math/Matrix2d',['require','../dep/glmatrix'],function(require) { - - - - var glMatrix = require('../dep/glmatrix'); - var mat2d = glMatrix.mat2d; - - function makeProperty(n) { - return { - configurable: false, - set: function(value) { - this._array[n] = value; - this._dirty = true; - }, - get: function() { - return this._array[n]; - } - }; - } - - /** - * @constructor - * @alias qtek.math.Matrix2d - */ - var Matrix2d = function() { - /** - * Storage of Matrix2d - * @type {Float32Array} - */ - this._array = mat2d.create(); - - /** - * @type {boolean} - */ - this._dirty = true; - }; - - Matrix2d.prototype = { - - constructor: Matrix2d, - - /** - * Clone a new Matrix2d - * @return {qtek.math.Matrix2d} - */ - clone: function() { - return (new Matrix2d()).copy(this); - }, - - /** - * Copy from b - * @param {qtek.math.Matrix2d} b - * @return {qtek.math.Matrix2d} - */ - copy: function(b) { - mat2d.copy(this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Calculate matrix determinant - * @return {number} - */ - determinant: function() { - return mat2d.determinant(this._array); - }, - - /** - * Set to a identity matrix - * @return {qtek.math.Matrix2d} - */ - identity: function() { - mat2d.identity(this._array); - this._dirty = true; - return this; - }, - - /** - * Invert self - * @return {qtek.math.Matrix2d} - */ - invert: function() { - mat2d.invert(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Alias for mutiply - * @param {qtek.math.Matrix2d} b - * @return {qtek.math.Matrix2d} - */ - mul: function(b) { - mat2d.mul(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Alias for multiplyLeft - * @param {qtek.math.Matrix2d} a - * @return {qtek.math.Matrix2d} - */ - mulLeft: function(b) { - mat2d.mul(this._array, b._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Multiply self and b - * @param {qtek.math.Matrix2d} b - * @return {qtek.math.Matrix2d} - */ - multiply: function(b) { - mat2d.multiply(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Multiply a and self, a is on the left - * @param {qtek.math.Matrix2d} a - * @return {qtek.math.Matrix2d} - */ - multiplyLeft: function(b) { - mat2d.multiply(this._array, b._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Rotate self by a given radian - * @param {number} rad - * @return {qtek.math.Matrix2d} - */ - rotate: function(rad) { - mat2d.rotate(this._array, this._array, rad); - this._dirty = true; - return this; - }, - - /** - * Scale self by s - * @param {qtek.math.Vector2} s - * @return {qtek.math.Matrix2d} - */ - scale: function(s) { - mat2d.scale(this._array, this._array, s._array); - this._dirty = true; - return this; - }, - - /** - * Translate self by v - * @param {qtek.math.Vector2} v - * @return {qtek.math.Matrix2d} - */ - translate: function(v) { - mat2d.translate(this._array, this._array, v._array); - this._dirty = true; - return this; - }, - toString: function() { - return '[' + Array.prototype.join.call(this._array, ',') + ']'; - } - }; - - /** - * @param {qtek.math.Matrix2d} out - * @param {qtek.math.Matrix2d} a - * @return {qtek.math.Matrix2d} - */ - Matrix2d.copy = function(out, a) { - mat2d.copy(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix2d} a - * @return {number} - */ - Matrix2d.determinant = function(a) { - return mat2d.determinant(a._array); - }; - - /** - * @param {qtek.math.Matrix2d} out - * @return {qtek.math.Matrix2d} - */ - Matrix2d.identity = function(out) { - mat2d.identity(out._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix2d} out - * @param {qtek.math.Matrix2d} a - * @return {qtek.math.Matrix2d} - */ - Matrix2d.invert = function(out, a) { - mat2d.invert(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix2d} out - * @param {qtek.math.Matrix2d} a - * @param {qtek.math.Matrix2d} b - * @return {qtek.math.Matrix2d} - */ - Matrix2d.mul = function(out, a, b) { - mat2d.mul(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @method - * @param {qtek.math.Matrix2d} out - * @param {qtek.math.Matrix2d} a - * @param {qtek.math.Matrix2d} b - * @return {qtek.math.Matrix2d} - */ - Matrix2d.multiply = Matrix2d.mul; - - /** - * @param {qtek.math.Matrix2d} out - * @param {qtek.math.Matrix2d} a - * @param {number} rad - * @return {qtek.math.Matrix2d} - */ - Matrix2d.rotate = function(out, a, rad) { - mat2d.rotate(out._array, a._array, rad); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix2d} out - * @param {qtek.math.Matrix2d} a - * @param {qtek.math.Vector2} v - * @return {qtek.math.Matrix2d} - */ - Matrix2d.scale = function(out, a, v) { - mat2d.scale(out._array, a._array, v._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix2d} out - * @param {qtek.math.Matrix2d} a - * @param {qtek.math.Vector2} v - * @return {qtek.math.Matrix2d} - */ - Matrix2d.translate = function(out, a, v) { - mat2d.translate(out._array, a._array, v._array); - out._dirty = true; - return out; - }; - - return Matrix2d; -}); -define('qtek/math/Matrix3',['require','../dep/glmatrix'],function(require) { - - - - var glMatrix = require('../dep/glmatrix'); - var mat3 = glMatrix.mat3; - - function makeProperty(n) { - return { - configurable: false, - set: function(value) { - this._array[n] = value; - this._dirty = true; - }, - get: function() { - return this._array[n]; - } - }; - } - - /** - * @constructor - * @alias qtek.math.Matrix3 - */ - var Matrix3 = function() { - - /** - * Storage of Matrix3 - * @type {Float32Array} - */ - this._array = mat3.create(); - - /** - * @type {boolean} - */ - this._dirty = true; - }; - - Matrix3.prototype = { - - constructor: Matrix3, - - /** - * Calculate the adjugate of self, in-place - * @return {qtek.math.Matrix3} - */ - adjoint: function() { - mat3.adjoint(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Clone a new Matrix3 - * @return {qtek.math.Matrix3} - */ - clone: function() { - return (new Matrix3()).copy(this); - }, - - /** - * Copy from b - * @param {qtek.math.Matrix3} b - * @return {qtek.math.Matrix3} - */ - copy: function(b) { - mat3.copy(this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Calculate matrix determinant - * @return {number} - */ - determinant: function() { - return mat3.determinant(this._array); - }, - - /** - * Copy the values from Matrix2d a - * @param {qtek.math.Matrix2d} a - * @return {qtek.math.Matrix3} - */ - fromMat2d: function(a) { - mat3.fromMat2d(this._array, a._array); - this._dirty = true; - return this; - }, - - /** - * Copies the upper-left 3x3 values of Matrix4 - * @param {qtek.math.Matrix4} a - * @return {qtek.math.Matrix3} - */ - fromMat4: function(a) { - mat3.fromMat4(this._array, a._array); - this._dirty = true; - return this; - }, - - /** - * Calculates a rotation matrix from the given quaternion - * @param {qtek.math.Quaternion} q - * @return {qtek.math.Matrix3} - */ - fromQuat: function(q) { - mat3.fromQuat(this._array, q._array); - this._dirty = true; - return this; - }, - - /** - * Set to a identity matrix - * @return {qtek.math.Matrix3} - */ - identity: function() { - mat3.identity(this._array); - this._dirty = true; - return this; - }, - - /** - * Invert self - * @return {qtek.math.Matrix3} - */ - invert: function() { - mat3.invert(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Alias for mutiply - * @param {qtek.math.Matrix3} b - * @return {qtek.math.Matrix3} - */ - mul: function(b) { - mat3.mul(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Alias for multiplyLeft - * @param {qtek.math.Matrix3} a - * @return {qtek.math.Matrix3} - */ - mulLeft: function(a) { - mat3.mul(this._array, a._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Multiply self and b - * @param {qtek.math.Matrix3} b - * @return {qtek.math.Matrix3} - */ - multiply: function(b) { - mat3.multiply(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Multiply a and self, a is on the left - * @param {qtek.math.Matrix3} a - * @return {qtek.math.Matrix3} - */ - multiplyLeft: function(a) { - mat3.multiply(this._array, a._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Rotate self by a given radian - * @param {number} rad - * @return {qtek.math.Matrix3} - */ - rotate: function(rad) { - mat3.rotate(this._array, this._array, rad); - this._dirty = true; - return this; - }, - - /** - * Scale self by s - * @param {qtek.math.Vector2} s - * @return {qtek.math.Matrix3} - */ - scale: function(v) { - mat3.scale(this._array, this._array, v._array); - this._dirty = true; - return this; - }, - - /** - * Translate self by v - * @param {qtek.math.Vector2} v - * @return {qtek.math.Matrix3} - */ - translate: function(v) { - mat3.translate(this._array, this._array, v._array); - this._dirty = true; - return this; - }, - /** - * Calculates a 3x3 normal matrix (transpose inverse) from the 4x4 matrix - * @param {qtek.math.Matrix4} a - */ - normalFromMat4: function(a) { - mat3.normalFromMat4(this._array, a._array); - this._dirty = true; - return this; - }, - - /** - * Transpose self, in-place. - * @return {qtek.math.Matrix2} - */ - transpose: function() { - mat3.transpose(this._array, this._array); - this._dirty = true; - return this; - }, - toString: function() { - return '[' + Array.prototype.join.call(this._array, ',') + ']'; - } - }; - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix3} a - * @return {qtek.math.Matrix3} - */ - Matrix3.adjoint = function(out, a) { - mat3.adjoint(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix3} a - * @return {qtek.math.Matrix3} - */ - Matrix3.copy = function(out, a) { - mat3.copy(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix3} a - * @return {number} - */ - Matrix3.determinant = function(a) { - return mat3.determinant(a._array); - }; - - /** - * @param {qtek.math.Matrix3} out - * @return {qtek.math.Matrix3} - */ - Matrix3.identity = function(out) { - mat3.identity(out._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix3} a - * @return {qtek.math.Matrix3} - */ - Matrix3.invert = function(out, a) { - mat3.invert(out._array, a._array); - return out; - }; - - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix3} a - * @param {qtek.math.Matrix3} b - * @return {qtek.math.Matrix3} - */ - Matrix3.mul = function(out, a, b) { - mat3.mul(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @method - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix3} a - * @param {qtek.math.Matrix3} b - * @return {qtek.math.Matrix3} - */ - Matrix3.multiply = Matrix3.mul; - - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix2d} a - * @return {qtek.math.Matrix3} - */ - Matrix3.fromMat2d = function(out, a) { - mat3.fromMat2d(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix4} a - * @return {qtek.math.Matrix3} - */ - Matrix3.fromMat4 = function(out, a) { - mat3.fromMat4(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Quaternion} a - * @return {qtek.math.Matrix3} - */ - Matrix3.fromQuat = function(out, q) { - mat3.fromQuat(out._array, q._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix4} a - * @return {qtek.math.Matrix3} - */ - Matrix3.normalFromMat4 = function(out, a) { - mat3.normalFromMat4(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix3} a - * @param {number} rad - * @return {qtek.math.Matrix3} - */ - Matrix3.rotate = function(out, a, rad) { - mat3.rotate(out._array, a._array, rad); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix3} a - * @param {qtek.math.Vector2} v - * @return {qtek.math.Matrix3} - */ - Matrix3.scale = function(out, a, v) { - mat3.scale(out._array, a._array, v._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix3} a - * @return {qtek.math.Matrix3} - */ - Matrix3.transpose = function(out, a) { - mat3.transpose(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Matrix3} out - * @param {qtek.math.Matrix3} a - * @param {qtek.math.Vector2} v - * @return {qtek.math.Matrix3} - */ - Matrix3.translate = function(out, a, v) { - mat3.translate(out._array, a._array, v._array); - out._dirty = true; - return out; - }; - - return Matrix3; -}); -define('qtek/math/Value',['require','./Vector3','./Vector2'],function(require) { - - - - var Vector3 = require('./Vector3'); - var Vector2 = require('./Vector2'); - - /** - * Random or constant 1d, 2d, 3d vector generator - * @constructor - * @alias qtek.math.Value - */ - var Value = function() {}; - - /** - * @method - * @param {number|qtek.math.Vector2|qtek.math.Vector3} [out] - * @return {number|qtek.math.Vector2|qtek.math.Vector3} - */ - Value.prototype.get = function(out) {}; - - // Constant - var ConstantValue = function(val) { - this.get = function() { - return val; - }; - }; - ConstantValue.prototype = new Value(); - ConstantValue.prototype.constructor = ConstantValue; - - // Vector - var VectorValue = function(val) { - var Constructor = val.constructor; - this.get = function(out) { - if (!out) { - out = new Constructor(); - } - out.copy(val); - return out; - }; - }; - VectorValue.prototype = new Value(); - VectorValue.prototype.constructor = VectorValue; - //Random 1D - var Random1D = function(min, max) { - var range = max - min; - this.get = function() { - return Math.random() * range + min; - }; - }; - Random1D.prototype = new Value(); - Random1D.prototype.constructor = Random1D; - - // Random2D - var Random2D = function(min, max) { - var rangeX = max.x - min.x; - var rangeY = max.y - min.y; - - this.get = function(out) { - if (!out) { - out = new Vector2(); - } - Vector2.set( - out, - rangeX * Math.random() + min._array[0], - rangeY * Math.random() + min._array[1] - ); - - return out; - }; - }; - Random2D.prototype = new Value(); - Random2D.prototype.constructor = Random2D; - - var Random3D = function(min, max) { - var rangeX = max.x - min.x; - var rangeY = max.y - min.y; - var rangeZ = max.z - min.z; - - this.get = function(out) { - if (!out) { - out = new Vector3(); - } - Vector3.set( - out, - rangeX * Math.random() + min._array[0], - rangeY * Math.random() + min._array[1], - rangeZ * Math.random() + min._array[2] - ); - - return out; - }; - }; - Random3D.prototype = new Value(); - Random3D.prototype.constructor = Random3D; - - // Factory methods - - /** - * Create a constant 1d value generator - * @param {number} constant - * @return {qtek.math.Value} - */ - Value.constant = function(constant) { - return new ConstantValue(constant); - }; - - /** - * Create a constant vector value(2d or 3d) generator - * @param {qtek.math.Vector2|qtek.math.Vector3} vector - * @return {qtek.math.Value} - */ - Value.vector = function(vector) { - return new VectorValue(vector); - }; - - /** - * Create a random 1d value generator - * @param {number} min - * @param {number} max - * @return {qtek.math.Value} - */ - Value.random1D = function(min, max) { - return new Random1D(min, max); - }; - - /** - * Create a random 2d value generator - * @param {qtek.math.Vector2} min - * @param {qtek.math.Vector2} max - * @return {qtek.math.Value} - */ - Value.random2D = function(min, max) { - return new Random2D(min, max); - }; - - /** - * Create a random 3d value generator - * @param {qtek.math.Vector3} min - * @param {qtek.math.Vector3} max - * @return {qtek.math.Value} - */ - Value.random3D = function(min, max) { - return new Random3D(min, max); - }; - - return Value; -}); -define('qtek/math/Vector4',['require','../dep/glmatrix'], function(require) { - - - - var glMatrix = require('../dep/glmatrix'); - var vec4 = glMatrix.vec4; - - /** - * @constructor - * @alias qtek.math.Vector4 - * @param {number} x - * @param {number} y - * @param {number} z - * @param {number} w - */ - var Vector4 = function(x, y, z, w) { - - x = x || 0; - y = y || 0; - z = z || 0; - w = w || 0; - - /** - * Storage of Vector4, read and write of x, y, z, w will change the values in _array - * All methods also operate on the _array instead of x, y, z, w components - * @type {Float32Array} - */ - this._array = vec4.fromValues(x, y, z, w); - - /** - * Dirty flag is used by the Node to determine - * if the matrix is updated to latest - * @type {boolean} - */ - this._dirty = true; - }; - - Vector4.prototype = { - - constructor: Vector4, - - /** - * Add b to self - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - add: function(b) { - vec4.add( this._array, this._array, b._array ); - this._dirty = true; - return this; - }, - - /** - * Set x, y and z components - * @param {number} x - * @param {number} y - * @param {number} z - * @param {number} w - * @return {qtek.math.Vector4} - */ - set: function(x, y, z, w) { - this._array[0] = x; - this._array[1] = y; - this._array[2] = z; - this._array[3] = w; - this._dirty = true; - return this; - }, - - /** - * Set x, y, z and w components from array - * @param {Float32Array|number[]} arr - * @return {qtek.math.Vector4} - */ - setArray: function(arr) { - this._array[0] = arr[0]; - this._array[1] = arr[1]; - this._array[2] = arr[2]; - this._array[3] = arr[3]; - - this._dirty = true; - return this; - }, - - /** - * Clone a new Vector4 - * @return {qtek.math.Vector4} - */ - clone: function() { - return new Vector4( this.x, this.y, this.z, this.w); - }, - - /** - * Copy from b - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - copy: function(b) { - vec4.copy( this._array, b._array ); - this._dirty = true; - return this; - }, - - /** - * Alias for distance - * @param {qtek.math.Vector4} b - * @return {number} - */ - dist: function(b) { - return vec4.dist(this._array, b._array); - }, - - /** - * Distance between self and b - * @param {qtek.math.Vector4} b - * @return {number} - */ - distance: function(b) { - return vec4.distance(this._array, b._array); - }, - - /** - * Alias for divide - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - div: function(b) { - vec4.div(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Divide self by b - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - divide: function(b) { - vec4.divide(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Dot product of self and b - * @param {qtek.math.Vector4} b - * @return {number} - */ - dot: function(b) { - return vec4.dot(this._array, b._array); - }, - - /** - * Alias of length - * @return {number} - */ - len: function() { - return vec4.len(this._array); - }, - - /** - * Calculate the length - * @return {number} - */ - length: function() { - return vec4.length(this._array); - }, - /** - * Linear interpolation between a and b - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @param {number} t - * @return {qtek.math.Vector4} - */ - lerp: function(a, b, t) { - vec4.lerp(this._array, a._array, b._array, t); - this._dirty = true; - return this; - }, - - /** - * Minimum of self and b - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - min: function(b) { - vec4.min(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Maximum of self and b - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - max: function(b) { - vec4.max(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Alias for multiply - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - mul: function(b) { - vec4.mul(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Mutiply self and b - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - multiply: function(b) { - vec4.multiply(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Negate self - * @return {qtek.math.Vector4} - */ - negate: function() { - vec4.negate(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Normalize self - * @return {qtek.math.Vector4} - */ - normalize: function() { - vec4.normalize(this._array, this._array); - this._dirty = true; - return this; - }, - - /** - * Generate random x, y, z, w components with a given scale - * @param {number} scale - * @return {qtek.math.Vector4} - */ - random: function(scale) { - vec4.random(this._array, scale); - this._dirty = true; - return this; - }, - - /** - * Scale self - * @param {number} scale - * @return {qtek.math.Vector4} - */ - scale: function(s) { - vec4.scale(this._array, this._array, s); - this._dirty = true; - return this; - }, - /** - * Scale b and add to self - * @param {qtek.math.Vector4} b - * @param {number} scale - * @return {qtek.math.Vector4} - */ - scaleAndAdd: function(b, s) { - vec4.scaleAndAdd(this._array, this._array, b._array, s); - this._dirty = true; - return this; - }, - - /** - * Alias for squaredDistance - * @param {qtek.math.Vector4} b - * @return {number} - */ - sqrDist: function(b) { - return vec4.sqrDist(this._array, b._array); - }, - - /** - * Squared distance between self and b - * @param {qtek.math.Vector4} b - * @return {number} - */ - squaredDistance: function(b) { - return vec4.squaredDistance(this._array, b._array); - }, - - /** - * Alias for squaredLength - * @return {number} - */ - sqrLen: function() { - return vec4.sqrLen(this._array); - }, - - /** - * Squared length of self - * @return {number} - */ - squaredLength: function() { - return vec4.squaredLength(this._array); - }, - - /** - * Alias for subtract - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - sub: function(b) { - vec4.sub(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Subtract b from self - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - subtract: function(b) { - vec4.subtract(this._array, this._array, b._array); - this._dirty = true; - return this; - }, - - /** - * Transform self with a Matrix4 m - * @param {qtek.math.Matrix4} m - * @return {qtek.math.Vector4} - */ - transformMat4: function(m) { - vec4.transformMat4(this._array, this._array, m._array); - this._dirty = true; - return this; - }, - - /** - * Transform self with a Quaternion q - * @param {qtek.math.Quaternion} q - * @return {qtek.math.Vector4} - */ - transformQuat: function(q) { - vec4.transformQuat(this._array, this._array, q._array); - this._dirty = true; - return this; - }, - - toString: function() { - return '[' + Array.prototype.join.call(this._array, ',') + ']'; - } - }; - - // Getter and Setter - if (Object.defineProperty) { - - var proto = Vector4.prototype; - /** - * @name x - * @type {number} - * @memberOf qtek.math.Vector4 - * @instance - */ - Object.defineProperty(proto, 'x', { - get: function () { - return this._array[0]; - }, - set: function (value) { - this._array[0] = value; - this._dirty = true; - } - }); - - /** - * @name y - * @type {number} - * @memberOf qtek.math.Vector4 - * @instance - */ - Object.defineProperty(proto, 'y', { - get: function () { - return this._array[1]; - }, - set: function (value) { - this._array[1] = value; - this._dirty = true; - } - }); - - /** - * @name z - * @type {number} - * @memberOf qtek.math.Vector4 - * @instance - */ - Object.defineProperty(proto, 'z', { - get: function () { - return this._array[2]; - }, - set: function (value) { - this._array[2] = value; - this._dirty = true; - } - }); - - /** - * @name w - * @type {number} - * @memberOf qtek.math.Vector4 - * @instance - */ - Object.defineProperty(proto, 'w', { - get: function () { - return this._array[3]; - }, - set: function (value) { - this._array[3] = value; - this._dirty = true; - } - }); - } - - // Supply methods that are not in place - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - Vector4.add = function(out, a, b) { - vec4.add(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector4} out - * @param {number} x - * @param {number} y - * @param {number} z - * @return {qtek.math.Vector4} - */ - Vector4.set = function(out, x, y, z, w) { - vec4.set(out._array, x, y, z, w); - out._dirty = true; - }; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - Vector4.copy = function(out, b) { - vec4.copy(out._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {number} - */ - Vector4.dist = function(a, b) { - return vec4.distance(a._array, b._array); - }; - - /** - * @method - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {number} - */ - Vector4.distance = Vector4.dist; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - Vector4.div = function(out, a, b) { - vec4.divide(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @method - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - Vector4.divide = Vector4.div; - - /** - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {number} - */ - Vector4.dot = function(a, b) { - return vec4.dot(a._array, b._array); - }; - - /** - * @param {qtek.math.Vector4} a - * @return {number} - */ - Vector4.len = function(b) { - return vec4.length(b._array); - }; - - // Vector4.length = Vector4.len; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @param {number} t - * @return {qtek.math.Vector4} - */ - Vector4.lerp = function(out, a, b, t) { - vec4.lerp(out._array, a._array, b._array, t); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - Vector4.min = function(out, a, b) { - vec4.min(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - Vector4.max = function(out, a, b) { - vec4.max(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - Vector4.mul = function(out, a, b) { - vec4.multiply(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - - /** - * @method - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - Vector4.multiply = Vector4.mul; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @return {qtek.math.Vector4} - */ - Vector4.negate = function(out, a) { - vec4.negate(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @return {qtek.math.Vector4} - */ - Vector4.normalize = function(out, a) { - vec4.normalize(out._array, a._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector4} out - * @param {number} scale - * @return {qtek.math.Vector4} - */ - Vector4.random = function(out, scale) { - vec4.random(out._array, scale); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {number} scale - * @return {qtek.math.Vector4} - */ - Vector4.scale = function(out, a, scale) { - vec4.scale(out._array, a._array, scale); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @param {number} scale - * @return {qtek.math.Vector4} - */ - Vector4.scaleAndAdd = function(out, a, b, scale) { - vec4.scaleAndAdd(out._array, a._array, b._array, scale); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {number} - */ - Vector4.sqrDist = function(a, b) { - return vec4.sqrDist(a._array, b._array); - }; - - /** - * @method - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {number} - */ - Vector4.squaredDistance = Vector4.sqrDist; - - /** - * @param {qtek.math.Vector4} a - * @return {number} - */ - Vector4.sqrLen = function(a) { - return vec4.sqrLen(a._array); - }; - /** - * @method - * @param {qtek.math.Vector4} a - * @return {number} - */ - Vector4.squaredLength = Vector4.sqrLen; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - Vector4.sub = function(out, a, b) { - vec4.subtract(out._array, a._array, b._array); - out._dirty = true; - return out; - }; - /** - * @method - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Vector4} b - * @return {qtek.math.Vector4} - */ - Vector4.subtract = Vector4.sub; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Matrix4} m - * @return {qtek.math.Vector4} - */ - Vector4.transformMat4 = function(out, a, m) { - vec4.transformMat4(out._array, a._array, m._array); - out._dirty = true; - return out; - }; - - /** - * @param {qtek.math.Vector4} out - * @param {qtek.math.Vector4} a - * @param {qtek.math.Quaternion} q - * @return {qtek.math.Vector4} - */ - Vector4.transformQuat = function(out, a, q) { - vec4.transformQuat(out._array, a._array, q._array); - out._dirty = true; - return out; - }; - - return Vector4; -}); -define('qtek/particleSystem/Particle',['require','../math/Vector3','../dep/glmatrix'],function(require) { - - var Vector3 = require('../math/Vector3'); - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - - /** - * @constructor - * @alias qtek.particleSystem.Particle - */ - var Particle = function() { - /** - * @type {qtek.math.Vector3} - */ - this.position = new Vector3(); - - /** - * Use euler angle to represent particle rotation - * @type {qtek.math.Vector3} - */ - this.rotation = new Vector3(); - - /** - * @type {?qtek.math.Vector3} - */ - this.velocity = null; - - /** - * @type {?qtek.math.Vector3} - */ - this.angularVelocity = null; - - /** - * @type {number} - */ - this.life = 1; - - /** - * @type {number} - */ - this.age = 0; - - /** - * @type {number} - */ - this.spriteSize = 1; - - /** - * @type {number} - */ - this.weight = 1; - - /** - * @type {qtek.particleSystem.Emitter} - */ - this.emitter = null; - }; - - /** - * Update particle position - * @param {number} deltaTime - */ - Particle.prototype.update = function(deltaTime) { - if (this.velocity) { - vec3.scaleAndAdd(this.position._array, this.position._array, this.velocity._array, deltaTime); - } - if (this.angularVelocity) { - vec3.scaleAndAdd(this.rotation._array, this.rotation._array, this.angularVelocity._array, deltaTime); - } - }; - - return Particle; -}); -define('qtek/particleSystem/Emitter',['require','../core/Base','../math/Vector3','./Particle','../math/Value','../dep/glmatrix'],function(require) { - - var Base = require('../core/Base'); - var Vector3 = require('../math/Vector3'); - var Particle = require('./Particle'); - var Value = require('../math/Value'); - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - - /** - * @constructor qtek.particleSystem.Emitter - * @extends qtek.core.Base - */ - var Emitter = Base.derive( - /** @lends qtek.particleSystem.Emitter# */ - { - /** - * Maximum number of particles created by this emitter - * @type {number} - */ - max: 1000, - /** - * Number of particles created by this emitter each shot - * @type {number} - */ - amount: 20, - - // Init status for each particle - /** - * Particle life generator - * @type {?qtek.math.Value.} - */ - life: null, - /** - * Particle position generator - * @type {?qtek.math.Value.} - */ - position: null, - /** - * Particle rotation generator - * @type {?qtek.math.Value.} - */ - rotation: null, - /** - * Particle velocity generator - * @type {?qtek.math.Value.} - */ - velocity: null, - /** - * Particle angular velocity generator - * @type {?qtek.math.Value.} - */ - angularVelocity: null, - /** - * Particle sprite size generator - * @type {?qtek.math.Value.} - */ - spriteSize: null, - /** - * Particle weight generator - * @type {?qtek.math.Value.} - */ - weight: null, - - _particlePool: null - - }, function() { - - this._particlePool = []; - - // TODO Reduce heap memory - for (var i = 0; i < this.max; i++) { - var particle = new Particle(); - particle.emitter = this; - this._particlePool.push(particle); - - if (this.velocity) { - particle.velocity = new Vector3(); - } - if (this.angularVelocity) { - particle.angularVelocity = new Vector3(); - } - } - - }, - /** @lends qtek.particleSystem.Emitter.prototype */ - { - /** - * Emitter number of particles and push them to a given particle list. Emmit number is defined by amount property - * @param {Array.} out - */ - emit: function(out) { - var amount = Math.min(this._particlePool.length, this.amount); - - var particle; - for (var i = 0; i < amount; i++) { - particle = this._particlePool.pop(); - // Initialize particle status - if (this.position) { - this.position.get(particle.position); - } - if (this.rotation) { - this.rotation.get(particle.rotation); - } - if (this.velocity) { - this.velocity.get(particle.velocity); - } - if (this.angularVelocity) { - this.angularVelocity.get(particle.angularVelocity); - } - if (this.life) { - particle.life = this.life.get(); - } - if (this.spriteSize) { - particle.spriteSize = this.spriteSize.get(); - } - if (this.weight) { - particle.weight = this.weight.get(); - } - particle.age = 0; - - out.push(particle); - } - }, - /** - * Kill a dead particle and put it back in the pool - * @param {qtek.particleSystem.Particle} particle - */ - kill: function(particle) { - this._particlePool.push(particle); - } - }); - - /** - * Create a constant 1d value generator. Alias for {@link qtek.math.Value.constant} - * @method qtek.particleSystem.Emitter.constant - */ - Emitter.constant = Value.constant; - - /** - * Create a constant vector value(2d or 3d) generator. Alias for {@link qtek.math.Value.vector} - * @method qtek.particleSystem.Emitter.vector - */ - Emitter.vector = Value.vector; - - /** - * Create a random 1d value generator. Alias for {@link qtek.math.Value.random1D} - * @method qtek.particleSystem.Emitter.random1D - */ - Emitter.random1D = Value.random1D; - - /** - * Create a random 2d value generator. Alias for {@link qtek.math.Value.random2D} - * @method qtek.particleSystem.Emitter.random2D - */ - Emitter.random2D = Value.random2D; - - /** - * Create a random 3d value generator. Alias for {@link qtek.math.Value.random3D} - * @method qtek.particleSystem.Emitter.random3D - */ - Emitter.random3D = Value.random3D; - - return Emitter; -}); -define('qtek/particleSystem/Field',['require','../core/Base'],function(require) { - - var Base = require('../core/Base'); - /** - * @constructor qtek.particleSystem.Field - * @extends qtek.core.Base - */ - var Field = Base.derive({}, { - /** - * Apply a field to the particle and update the particle velocity - * @param {qtek.math.Vector3} velocity - * @param {qtek.math.Vector3} position - * @param {number} weight - * @param {number} deltaTime - * @memberOf qtek.particleSystem.Field.prototype - */ - applyTo: function(velocity, position, weight, deltaTime) {} - }); - - return Field; -}); -define('qtek/particleSystem/ForceField',['require','./Field','../math/Vector3','../dep/glmatrix'],function(require) { - - var Field = require('./Field'); - var Vector3 = require('../math/Vector3'); - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - - /** - * @constructor qtek.particleSystem.ForceField - * @extends qtek.particleSystem.Field - */ - var ForceField = Field.derive(function() { - return { - force: new Vector3() - }; - }, { - applyTo: function(velocity, position, weight, deltaTime) { - if (weight > 0) { - vec3.scaleAndAdd(velocity._array, velocity._array, this.force._array, deltaTime / weight); - } - } - }); - - return ForceField; -}); -define('qtek/particleSystem/particle.essl',[],function () { return '@export buildin.particle.vertex\n\nuniform mat4 worldView : WORLDVIEW;\nuniform mat4 projection : PROJECTION;\n\nattribute vec3 position : POSITION;\nattribute vec3 normal : NORMAL;\n\n#ifdef UV_ANIMATION\nattribute vec2 texcoord0 : TEXCOORD_0;\nattribute vec2 texcoord1 : TEXCOORD_1;\n\nvarying vec2 v_Uv0;\nvarying vec2 v_Uv1;\n#endif\n\nvarying float v_Age;\n\nvoid main() {\n v_Age = normal.x;\n float rotation = normal.y;\n\n vec4 worldViewPosition = worldView * vec4(position, 1.0);\n gl_Position = projection * worldViewPosition;\n float w = gl_Position.w;\n // TODO\n gl_PointSize = normal.z * projection[0].x / w;\n\n #ifdef UV_ANIMATION\n v_Uv0 = texcoord0;\n v_Uv1 = texcoord1;\n #endif\n}\n\n@end\n\n@export buildin.particle.fragment\n\nuniform sampler2D sprite;\nuniform sampler2D gradient;\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\n\nvarying float v_Age;\n\n#ifdef UV_ANIMATION\nvarying vec2 v_Uv0;\nvarying vec2 v_Uv1;\n#endif\n\nvoid main() {\n vec4 color = vec4(color, alpha);\n #ifdef SPRITE_ENABLED\n #ifdef UV_ANIMATION\n color *= texture2D(sprite, mix(v_Uv0, v_Uv1, gl_PointCoord));\n #else\n color *= texture2D(sprite, gl_PointCoord);\n #endif\n #endif\n #ifdef GRADIENT_ENABLED\n color *= texture2D(gradient, vec2(v_Age, 0.5));\n #endif\n gl_FragColor = color;\n}\n\n@end';}); - -define('qtek/particleSystem/ParticleRenderable',['require','../Renderable','../math/Vector3','../core/glenum','../StaticGeometry','../Material','../Shader','../dep/glmatrix','./particle.essl'],function(require) { - - - - var Renderable = require('../Renderable'); - var Vector3 = require('../math/Vector3'); - var glenum = require('../core/glenum'); - - var StaticGeometry = require('../StaticGeometry'); - var Material = require('../Material'); - var Shader = require('../Shader'); - - var glMatrix = require('../dep/glmatrix'); - var vec3 = glMatrix.vec3; - - Shader['import'](require('./particle.essl')); - - var particleShader = new Shader({ - vertex: Shader.source('buildin.particle.vertex'), - fragment: Shader.source('buildin.particle.fragment') - }); - particleShader.enableTexture('sprite'); - - /** - * @constructor qtek.particleSystem.ParticleRenderable - * @extends qtek.Renderable - * - * @example - * var particleRenderable = new qtek.particleSystem.ParticleRenderable({ - * spriteAnimationTileX: 4, - * spriteAnimationTileY: 4, - * spriteAnimationRepeat: 1 - * }); - * scene.add(particleRenderable); - * // Enable uv animation in the shader - * particleRenderable.material.shader.define('both', 'UV_ANIMATION'); - * var Emitter = qtek.particleSystem.Emitter; - * var Vector3 = qtek.math.Vector3; - * var emitter = new Emitter({ - * max: 2000, - * amount: 100, - * life: Emitter.random1D(10, 20), - * position: Emitter.vector(new Vector3()), - * velocity: Emitter.random3D(new Vector3(-10, 0, -10), new Vector3(10, 0, 10)); - * }); - * particleRenderable.addEmitter(emitter); - * var gravityField = new qtek.particleSystem.ForceField(); - * gravityField.force.y = -10; - * particleRenderable.addField(gravityField); - * ... - * animation.on('frame', function(frameTime) { - * particleRenderable.updateParticles(frameTime); - * renderer.render(scene, camera); - * }); - */ - var ParticleRenderable = Renderable.derive( - /** @lends qtek.particleSystem.ParticleRenderable# */ - { - /** - * @type {boolean} - */ - loop: true, - /** - * @type {boolean} - */ - oneshot: false, - /** - * Duration of particle system in milliseconds - * @type {number} - */ - duration: 1, - - // UV Animation - /** - * @type {number} - */ - spriteAnimationTileX: 1, - /** - * @type {number} - */ - spriteAnimationTileY: 1, - /** - * @type {number} - */ - spriteAnimationRepeat: 0, - - mode: Renderable.POINTS, - - _elapsedTime: 0, - - _emitting: true - - }, function(){ - - this.geometry = new StaticGeometry({ - dynamic: true - }); - - if (!this.material) { - this.material = new Material({ - shader: particleShader, - transparent: true, - depthMask: false - }); - } - - this._particles = []; - this._fields = []; - this._emitters = []; - }, - /** @lends qtek.particleSystem.ParticleRenderable.prototype */ - { - - culling: false, - - frustumCulling: false, - - castShadow: false, - receiveShadow: false, - - /** - * Add emitter - * @param {qtek.particleSystem.Emitter} emitter - */ - addEmitter: function(emitter) { - this._emitters.push(emitter); - }, - - /** - * Remove emitter - * @param {qtek.particleSystem.Emitter} emitter - */ - removeEmitter: function(emitter) { - this._emitters.splice(this._emitters.indexOf(emitter), 1); - }, - - /** - * Add field - * @param {qtek.particleSystem.Field} field - */ - addField: function(field) { - this._fields.push(field); - }, - - /** - * Remove field - * @param {qtek.particleSystem.Field} field - */ - removeField: function(field) { - this._fields.splice(this._fields.indexOf(field), 1); - }, - - /** - * Reset the particle system. - */ - reset: function() { - // Put all the particles back - for (var i = 0; i < this._particles.length; i++) { - var p = this._particles[i]; - p.emitter.kill(p); - } - this._particles.length = 0; - this._elapsedTime = 0; - this._emitting = true; - }, - - /** - * @param {number} deltaTime - */ - updateParticles: function(deltaTime) { - - // MS => Seconds - deltaTime /= 1000; - this._elapsedTime += deltaTime; - - var particles = this._particles; - - if (this._emitting) { - for (var i = 0; i < this._emitters.length; i++) { - this._emitters[i].emit(particles); - } - if (this.oneshot) { - this._emitting = false; - } - } - - // Aging - var len = particles.length; - for (var i = 0; i < len;) { - var p = particles[i]; - p.age += deltaTime; - if (p.age >= p.life) { - p.emitter.kill(p); - particles[i] = particles[len-1]; - particles.pop(); - len--; - } else { - i++; - } - } - - for (var i = 0; i < len; i++) { - // Update - var p = particles[i]; - if (this._fields.length > 0) { - for (var j = 0; j < this._fields.length; j++) { - this._fields[j].applyTo(p.velocity, p.position, p.weight, deltaTime); - } - } - p.update(deltaTime); - } - }, - - _updateVertices: function() { - var geometry = this.geometry; - // If has uv animation - var animTileX = this.spriteAnimationTileX; - var animTileY = this.spriteAnimationTileY; - var animRepeat = this.spriteAnimationRepeat; - var nUvAnimFrame = animTileY * animTileX * animRepeat; - var hasUvAnimation = nUvAnimFrame > 1; - var positions = geometry.attributes.position.value; - // Put particle status in normal - var normals = geometry.attributes.normal.value; - var uvs = geometry.attributes.texcoord0.value; - var uvs2 = geometry.attributes.texcoord1.value; - - var len = this._particles.length; - if (!positions || positions.length !== len * 3) { - // TODO Optimize - positions = geometry.attributes.position.value = new Float32Array(len * 3); - normals = geometry.attributes.normal.value = new Float32Array(len * 3); - if (hasUvAnimation) { - uvs = geometry.attributes.texcoord0.value = new Float32Array(len * 2); - uvs2 = geometry.attributes.texcoord1.value = new Float32Array(len * 2); - } - } - - var invAnimTileX = 1 / animTileX; - for (var i = 0; i < len; i++) { - var particle = this._particles[i]; - var offset = i * 3; - for (var j = 0; j < 3; j++) { - positions[offset + j] = particle.position._array[j]; - normals[offset] = particle.age / particle.life; - // normals[offset + 1] = particle.rotation; - normals[offset + 1] = 0; - normals[offset + 2] = particle.spriteSize; - } - var offset2 = i * 2; - if (hasUvAnimation) { - // TODO - var p = particle.age / particle.life; - var stage = Math.round(p * (nUvAnimFrame - 1)) * animRepeat; - var v = Math.floor(stage * invAnimTileX); - var u = stage - v * animTileX; - uvs[offset2] = u / animTileX; - uvs[offset2 + 1] = 1 - v / animTileY; - uvs2[offset2] = (u + 1) / animTileX; - uvs2[offset2 + 1] = 1 - (v + 1) / animTileY; - } - } - - geometry.dirty(); - }, - - render: function(_gl) { - this._updateVertices(); - return Renderable.prototype.render.call(this, _gl); - }, - - /** - * @return {boolean} - */ - isFinished: function() { - return this._elapsedTime > this.duration && !this.loop; - }, - - /** - * @param {WebGLRenderingContext} _gl - */ - dispose: function(_gl) { - // Put all the particles back - for (var i = 0; i < this._particles.length; i++) { - var p = this._particles[i]; - p.emitter.kill(p); - } - this.geometry.dispose(_gl); - // TODO Dispose texture, shader ? - }, - - /** - * @return {qtek.particleSystem.ParticleRenderable} - */ - clone: function() { - var particleSystem = new ParticleRenderable({ - material: this.material - }); - particleSystem.loop = this.loop; - particleSystem.duration = this.duration; - particleSystem.oneshot = this.oneshot; - particleSystem.spriteAnimationRepeat = this.spriteAnimationRepeat; - particleSystem.spriteAnimationTileY = this.spriteAnimationTileY; - particleSystem.spriteAnimationTileX = this.spriteAnimationTileX; - - particleSystem.position.copy(this.position); - particleSystem.rotation.copy(this.rotation); - particleSystem.scale.copy(this.scale); - - for (var i = 0; i < this._children.length; i++) { - particleSystem.add(this._children[i].clone()); - } - return particleSystem; - } - }); - - return ParticleRenderable; -}); -define('qtek/picking/color.essl',[],function () { return '@export buildin.picking.color.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvoid main(){\n\n vec3 skinnedPosition = position;\n\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n}\n\n@end\n\n@end\n@export buildin.picking.color.fragment\n\nuniform vec4 color : [1.0, 1.0, 1.0, 1.0];\n\nvoid main(){\n gl_FragColor = color;\n}\n\n@end';}); - -define('qtek/picking/PixelPicking',['require','../core/Base','../FrameBuffer','../Texture2D','../Shader','../Material','./color.essl'],function (require) { - - var Base = require('../core/Base'); - var FrameBuffer = require('../FrameBuffer'); - var Texture2D = require('../Texture2D'); - var Shader = require('../Shader'); - var Material = require('../Material'); - - Shader.import(require('./color.essl')); - - /** - * Pixel picking is gpu based picking, which is fast and accurate. - * But not like ray picking, it can't get the intersection point and triangle. - * @constructor qtek.picking.PixelPicking - * @extends qtek.core.Base - */ - var PixelPicking = Base.derive(function() { - return /** @lends qtek.picking.PixelPicking# */ { - /** - * Target renderer - * @type {qtek.Renderer} - */ - renderer: null, - /** - * Downsample ratio of hidden frame buffer - * @type {number} - */ - downSampleRatio: 1, - - width: 100, - height: 100, - - lookupOffset: 1, - - _frameBuffer: null, - _texture: null, - _shader: null, - - _idMaterials: [], - _lookupTable: [], - - _meshMaterials: [], - - _idOffset: 0 - }; - }, function() { - if (this.renderer) { - this.width = this.renderer.width; - this.height = this.renderer.height; - } - this._init(); - }, /** @lends qtek.picking.PixelPicking.prototype */ { - _init: function() { - this._texture = new Texture2D({ - width: this.width * this.downSampleRatio, - height: this.height * this.downSampleRatio - }); - this._frameBuffer = new FrameBuffer(); - - this._shader = new Shader({ - vertex: Shader.source('buildin.picking.color.vertex'), - fragment: Shader.source('buildin.picking.color.fragment') - }); - }, - /** - * Set picking presision - * @param {number} ratio - */ - setPrecision: function(ratio) { - this._texture.width = this.width * ratio; - this._texture.height = this.height * ratio; - this.downSampleRatio = ratio; - }, - resize: function(width, height) { - this._texture.width = width * this.downSampleRatio; - this._texture.height = height * this.downSampleRatio; - this.width = width; - this.height = height; - this._texture.dirty(); - }, - /** - * Update the picking framebuffer - * @param {number} ratio - */ - update: function(scene, camera) { - var renderer = this.renderer; - if (renderer.width !== this.width || renderer.height !== this.height) { - this.resize(renderer.width, renderer.height); - } - - this._frameBuffer.attach(renderer.gl, this._texture); - this._frameBuffer.bind(renderer); - this._idOffset = this.lookupOffset; - this._setMaterial(scene); - renderer.render(scene, camera); - this._restoreMaterial(); - this._frameBuffer.unbind(renderer); - }, - - _setMaterial: function(root) { - for (var i =0; i < root._children.length; i++) { - var child = root._children[i]; - if (child.geometry && child.material && child.material.shader) { - var id = this._idOffset++; - var idx = id - this.lookupOffset; - var material = this._idMaterials[idx]; - if (!material) { - material = new Material({ - shader: this._shader - }); - var color = packID(id); - color[0] /= 255; - color[1] /= 255; - color[2] /= 255; - color[3] = 1.0; - material.set('color', color); - this._idMaterials[idx] = material; - } - this._meshMaterials[idx] = child.material; - this._lookupTable[idx] = child; - child.material = material; - } - if (child._children.length) { - this._setMaterial(child); - } - } - }, - - /** - * Pick the object - * @param {number} x Mouse position x - * @param {number} y Mouse position y - * @return {qtek.Node} - */ - pick: function(x, y) { - var renderer = this.renderer; - - var ratio = this.downSampleRatio; - x = Math.ceil(ratio * x); - y = Math.ceil(ratio * (this.height - y)); - - this._frameBuffer.bind(renderer); - var pixel = new Uint8Array(4); - var _gl = renderer.gl; - // TODO out of bounds ? - // preserveDrawingBuffer ? - _gl.readPixels(x, y, 1, 1, _gl.RGBA, _gl.UNSIGNED_BYTE, pixel); - this._frameBuffer.unbind(renderer); - // Skip interpolated pixel because of anti alias - if (pixel[3] === 255) { - var id = unpackID(pixel[0], pixel[1], pixel[2]); - if (id) { - var el = this._lookupTable[id - this.lookupOffset]; - return el; - } - } - }, - - _restoreMaterial: function() { - for (var i = 0; i < this._lookupTable.length; i++) { - this._lookupTable[i].material = this._meshMaterials[i]; - } - }, - - dispose: function(_gl) { - this._frameBuffer.dispose(_gl); - this._shader.dispose(_gl); - } - }); - - function packID(id){ - var r = id >> 16; - var g = (id - (r << 8)) >> 8; - var b = id - (r << 16) - (g<<8); - return [r, g, b]; - } - - function unpackID(r, g, b){ - return (r << 16) + (g<<8) + b; - } - - return PixelPicking; -}); -define('qtek/picking/RayPicking',['require','../core/Base','../math/Ray','../math/Vector2','../math/Vector3','../math/Matrix4','../Renderable','../StaticGeometry','../core/glenum'],function(require) { - - var Base = require('../core/Base'); - var Ray = require('../math/Ray'); - var Vector2 = require('../math/Vector2'); - var Vector3 = require('../math/Vector3'); - var Matrix4 = require('../math/Matrix4'); - var Renderable = require('../Renderable'); - var StaticGeometry = require('../StaticGeometry'); - var glenum = require('../core/glenum'); - - /** - * @constructor qtek.picking.RayPicking - * @extends qtek.core.Base - */ - var RayPicking = Base.derive( - /** @lends qtek.picking.RayPicking# */ - { - /** - * Target scene - * @type {qtek.Scene} - */ - scene: null, - /** - * Target camera - * @type {qtek.Camera} - */ - camera: null, - /** - * Target renderer - * @type {qtek.Renderer} - */ - renderer: null - }, function() { - this._ray = new Ray(); - this._ndc = new Vector2(); - }, - /** @lends qtek.picking.RayPicking.prototype */ - { - - /** - * Pick the nearest intersection object in the scene - * @param {number} x Mouse position x - * @param {number} y Mouse position y - * @return {qtek.picking.RayPicking~Intersection} - */ - pick: function(x, y) { - var out = this.pickAll(x, y); - return out[0] || null; - }, - - /** - * Pick all intersection objects, wich will be sorted from near to far - * @param {number} x Mouse position x - * @param {number} y Mouse position y - * @return {Array.} - */ - pickAll: function(x, y) { - this.renderer.screenToNdc(x, y, this._ndc); - this.camera.castRay(this._ndc, this._ray); - - var output = []; - - this._intersectNode(this.scene, output); - - output.sort(this._intersectionCompareFunc); - - return output; - }, - - _intersectNode: function(node, out) { - if ((node instanceof Renderable) && node.isRenderable()) { - if (!node.ignorePicking && node.geometry.isUseFace()) { - this._intersectRenderable(node, out); - } - } - for (var i = 0; i < node._children.length; i++) { - this._intersectNode(node._children[i], out); - } - }, - - _intersectRenderable: (function() { - - var v1 = new Vector3(); - var v2 = new Vector3(); - var v3 = new Vector3(); - var ray = new Ray(); - var worldInverse = new Matrix4(); - - return function(renderable, out) { - - ray.copy(this._ray); - Matrix4.invert(worldInverse, renderable.worldTransform); - - ray.applyTransform(worldInverse); - - var geometry = renderable.geometry; - if (geometry.boundingBox) { - if (!ray.intersectBoundingBox(geometry.boundingBox)) { - return; - } - } - // Use user defined ray picking algorithm - if (geometry.pickByRay) { - var intersection = geometry.pickByRay(ray); - if (intersection) { - out.push(intersection); - } - return; - } - - var isStatic = geometry instanceof StaticGeometry; - var cullBack = (renderable.cullFace === glenum.BACK && renderable.frontFace === glenum.CCW) - || (renderable.cullFace === glenum.FRONT && renderable.frontFace === glenum.CW); - - var point; - if (isStatic) { - var faces = geometry.faces; - var positions = geometry.attributes.position.value; - for (var i = 0; i < faces.length;) { - var i1 = faces[i++] * 3; - var i2 = faces[i++] * 3; - var i3 = faces[i++] * 3; - - v1._array[0] = positions[i1]; - v1._array[1] = positions[i1 + 1]; - v1._array[2] = positions[i1 + 2]; - - v2._array[0] = positions[i2]; - v2._array[1] = positions[i2 + 1]; - v2._array[2] = positions[i2 + 2]; - - v3._array[0] = positions[i3]; - v3._array[1] = positions[i3 + 1]; - v3._array[2] = positions[i3 + 2]; - - if (cullBack) { - point = ray.intersectTriangle(v1, v2, v3, renderable.culling); - } else { - point = ray.intersectTriangle(v1, v3, v2, renderable.culling); - } - if (point) { - var pointW = new Vector3(); - Vector3.transformMat4(pointW, point, renderable.worldTransform); - out.push(new RayPicking.Intersection( - point, pointW, renderable, [i1, i2, i3], - Vector3.dist(pointW, this._ray.origin) - )); - } - } - } else { - var faces = geometry.faces; - var positions = geometry.attributes.position.value; - for (var i = 0; i < faces.length; i++) { - var face = faces[i]; - var i1 = face[0]; - var i2 = face[1]; - var i3 = face[2]; - - v1.setArray(positions[i1]); - v2.setArray(positions[i2]); - v3.setArray(positions[i3]); - - if (cullBack) { - point = ray.intersectTriangle(v1, v2, v3, renderable.culling); - } else { - point = ray.intersectTriangle(v1, v3, v2, renderable.culling); - } - if (point) { - var pointW = new Vector3(); - Vector3.transformMat4(pointW, point, renderable.worldTransform); - out.push(new RayPicking.Intersection( - point, pointW, renderable, [i1, i2, i3], - Vector3.dist(pointW, this._ray.origin) - )); - } - } - } - }; - })(), - - _intersectionCompareFunc: function(a, b) { - return a.distance - b.distance; - } - }); - - /** - * @constructor qtek.picking.RayPicking~Intersection - * @param {qtek.math.Vector3} point - * @param {qtek.math.Vector3} pointWorld - * @param {qtek.Node} target - * @param {Array.} face - * @param {number} distance - */ - RayPicking.Intersection = function(point, pointWorld, target, face, distance) { - /** - * Intersection point in local transform coordinates - * @type {qtek.math.Vector3} - */ - this.point = point; - /** - * Intersection point in world transform coordinates - * @type {qtek.math.Vector3} - */ - this.pointWorld = pointWorld; - /** - * Intersection scene node - * @type {qtek.Node} - */ - this.target = target; - /** - * Intersection triangle, which is an array of vertex index - * @type {Array.} - */ - this.face = face; - /** - * Distance from intersection point to ray origin - * @type {number} - */ - this.distance = distance; - }; - - return RayPicking; -}); -define('qtek/plugin/FirstPersonControl',['require','../core/Base','../math/Vector3','../math/Matrix4','../math/Quaternion'],function(require) { - - var Base = require('../core/Base'); - var Vector3 = require('../math/Vector3'); - var Matrix4 = require('../math/Matrix4'); - var Quaternion = require('../math/Quaternion'); - - /** - * @constructor qtek.plugin.FirstPersonControl - * @example - * var control = new qtek.plugin.FirstPersonControl({ - * target: camera, - * domElement: renderer.canvas - * }); - * ... - * animation.on('frame', function(frameTime) { - * control.update(frameTime); - * renderer.render(scene, camera); - * }); - */ - var FirstPersonControl = Base.derive(function() { - return /** @lends qtek.plugin.FirstPersonControl# */ { - /** - * Scene node to control, mostly it is a camera - * @type {qtek.Node} - */ - target: null, - - /** - * Target dom to bind with mouse events - * @type {HTMLElement} - */ - domElement: null, - - /** - * Mouse move sensitivity - * @type {number} - */ - sensitivity: 1, - - /** - * Target move speed - * @type {number} - */ - speed: 0.4, - - /** - * Up axis - * @type {qtek.math.Vector3} - */ - up: new Vector3(0, 1, 0), - - /** - * If lock vertical movement - * @type {boolean} - */ - verticalMoveLock: false, - - _moveForward: false, - _moveBackward: false, - _moveLeft: false, - _moveRight: false, - - _offsetPitch: 0, - _offsetRoll: 0 - }; - }, function() { - this._lockChange = this._lockChange.bind(this); - this._keyDown = this._keyDown.bind(this); - this._keyUp = this._keyUp.bind(this); - this._mouseMove = this._mouseMove.bind(this); - - if (this.domElement) { - this.enable(); - } - }, - /** @lends qtek.plugin.FirstPersonControl.prototype */ - { - /** - * Enable control - */ - enable: function() { - // Use pointer lock - // http://www.html5rocks.com/en/tutorials/pointerlock/intro/ - var el = this.domElement; - - //Must request pointer lock after click event, can't not do it directly - //Why ? ? - el.addEventListener('click', this._requestPointerLock); - - document.addEventListener('pointerlockchange', this._lockChange); - document.addEventListener('mozpointerlockchange', this._lockChange); - document.addEventListener('webkitpointerlockchange', this._lockChange); - - document.addEventListener('keydown', this._keyDown); - document.addEventListener('keyup', this._keyUp); - }, - - /** - * Disable control - */ - disable: function() { - - this.target.off('beforeupdate', this._beforeUpdateCamera); - - var el = this.domElement; - - el.exitPointerLock = el.exitPointerLock - || el.mozExitPointerLock - || el.webkitExitPointerLock; - - if (el.exitPointerLock) { - el.exitPointerLock(); - } - - this.domElement.removeEventListener('click', this._requestPointerLock); - - document.removeEventListener('pointerlockchange', this._lockChange); - document.removeEventListener('mozpointerlockchange', this._lockChange); - document.removeEventListener('webkitpointerlockchange', this._lockChange); - - document.removeEventListener('keydown', this._keyDown); - document.removeEventListener('keyup', this._keyUp); - }, - - _requestPointerLock: function() { - var el = this; - el.requestPointerLock = el.requestPointerLock - || el.mozRequestPointerLock - || el.webkitRequestPointerLock; - - el.requestPointerLock(); - }, - - /** - * Control update. Should be invoked every frame - * @param {number} frameTime Frame time - */ - update: function(frameTime) { - var target = this.target; - - var position = this.target.position; - var xAxis = target.localTransform.x.normalize(); - var zAxis = target.localTransform.z.normalize(); - - if (this.verticalMoveLock) { - zAxis.y = 0; - zAxis.normalize(); - } - - var speed = this.speed * frameTime / 20; - - if (this._moveForward) { - // Opposite direction of z - position.scaleAndAdd(zAxis, -speed); - } - if (this._moveBackward) { - position.scaleAndAdd(zAxis, speed); - } - if (this._moveLeft) { - position.scaleAndAdd(xAxis, -speed / 2); - } - if (this._moveRight) { - position.scaleAndAdd(xAxis, speed / 2); - } - - target.rotateAround(target.position, this.up, -this._offsetPitch * frameTime * Math.PI / 360); - var xAxis = target.localTransform.right; - target.rotateAround(target.position, xAxis, -this._offsetRoll * frameTime * Math.PI / 360); - - this._offsetRoll = this._offsetPitch = 0; - }, - - _lockChange: function() { - if ( - document.pointerLockElement === this.domElement - || document.mozPointerLockElement === this.domElement - || document.webkitPointerLockElement === this.domElement - ) { - document.addEventListener('mousemove', this._mouseMove, false); - } else { - document.removeEventListener('mousemove', this._mouseMove); - } - }, - - _mouseMove: function(e) { - var dx = e.movementX || e.mozMovementX || e.webkitMovementX || 0; - var dy = e.movementY || e.mozMovementY || e.webkitMovementY || 0; - - this._offsetPitch += dx * this.sensitivity / 200; - this._offsetRoll += dy * this.sensitivity / 200; - }, - - _keyDown: function(e) { - switch(e.keyCode) { - case 87: //w - case 37: //up arrow - this._moveForward = true; - break; - case 83: //s - case 40: //down arrow - this._moveBackward = true; - break; - case 65: //a - case 37: //left arrow - this._moveLeft = true; - break; - case 68: //d - case 39: //right arrow - this._moveRight = true; - break; - } - }, - - _keyUp: function(e) { - this._moveForward = false; - this._moveBackward = false; - this._moveLeft = false; - this._moveRight = false; - } - }); - - return FirstPersonControl; -}); -define('qtek/plugin/InfinitePlane',['require','../Mesh','../DynamicGeometry','../math/Plane','../math/Vector3','../math/Matrix4','../math/Ray','../camera/Perspective','../dep/glmatrix'],function(require) { - - var Mesh = require('../Mesh'); - var DynamicGeometry = require('../DynamicGeometry'); - var Plane = require('../math/Plane'); - var Vector3 = require('../math/Vector3'); - var Matrix4 = require('../math/Matrix4'); - var Ray = require('../math/Ray'); - - var PerspectiveCamera = require('../camera/Perspective'); - - var glMatrix = require('../dep/glmatrix'); - var mat4 = glMatrix.mat4; - var vec3 = glMatrix.vec3; - var vec4 = glMatrix.vec4; - - var uvs = [[0, 0], [0, 1], [1, 1], [1, 0]]; - var tris = [0, 1, 2, 2, 3, 0]; - - var InfinitePlane = Mesh.derive({ - - camera: null, - - plane: null, - - gridSize: 1, - - maxGrid: 0, - - // TODO - frustumCulling: false - - }, function() { - if (!this.geometry) { - this.geometry = new DynamicGeometry(); - } - if (!this.plane) { - this.plane = new Plane(); - } - }, { - - updateGeometry: function() { - - var coords = this._unProjectGrid(); - if (!coords) { - return; - } - var positions = this.geometry.attributes.position.value; - var normals = this.geometry.attributes.normal.value; - var texcoords = this.geometry.attributes.texcoord0.value; - var faces = this.geometry.faces; - var nVertices = 0; - var normal = vec3.clone(this.plane.normal._array); - - // if (this.gridSize > 0) { - // TODO - - // } else { - for (var i = 0; i < 6; i++) { - var idx = tris[i]; - positions[nVertices] = coords[idx]._array; - normals[nVertices] = normal; - texcoords[nVertices] = uvs[idx]; - nVertices++; - } - faces[0] = [0, 1, 2]; - faces[1] = [3, 4, 5]; - this.geometry.dirty(); - // } - }, - - // http://fileadmin.cs.lth.se/graphics/theses/projects/projgrid/ - _unProjectGrid: (function() { - - var planeViewSpace = new Plane(); - var lines = [ - 0, 1, 0, 2, 1, 3, 2, 3, - 4, 5, 4, 6, 5, 7, 6, 7, - 0, 4, 1, 5, 2, 6, 3, 7 - ]; - - var start = new Vector3(); - var end = new Vector3(); - - var points = []; - - // 1----2 - // | | - // 0----3 - var coords = []; - for (var i = 0; i < 4; i++) { - coords[i] = new Vector3(0, 0); - } - - var ray = new Ray(); - - return function() { - planeViewSpace.copy(this.plane); - planeViewSpace.applyTransform(this.camera.viewMatrix); - - var frustumVertices = this.camera.frustum.vertices; - - var nPoints = 0; - // Intersect with lines of frustum - for (var i = 0; i < 12; i++) { - start._array = frustumVertices[lines[i * 2]]; - end._array = frustumVertices[lines[i * 2 + 1]]; - - var point = planeViewSpace.intersectLine(start, end, points[nPoints]); - if (point) { - if (!points[nPoints]) { - points[nPoints] = point; - } - nPoints++; - } - } - if (nPoints === 0) { - return; - } - for (var i = 0; i < nPoints; i++) { - points[i].applyProjection(this.camera.projectionMatrix); - } - var minX = points[0]._array[0]; - var minY = points[0]._array[1]; - var maxX = points[0]._array[0]; - var maxY = points[0]._array[1]; - for (var i = 1; i < nPoints; i++) { - maxX = Math.max(maxX, points[i]._array[0]); - maxY = Math.max(maxY, points[i]._array[1]); - minX = Math.min(minX, points[i]._array[0]); - minY = Math.min(minY, points[i]._array[1]); - } - if (minX == maxX || minY == maxY) { - return; - } - coords[0]._array[0] = minX; - coords[0]._array[1] = minY; - coords[1]._array[0] = minX; - coords[1]._array[1] = maxY; - coords[2]._array[0] = maxX; - coords[2]._array[1] = maxY; - coords[3]._array[0] = maxX; - coords[3]._array[1] = minY; - - for (var i = 0; i < 4; i++) { - this.camera.castRay(coords[i], ray); - ray.intersectPlane(this.plane, coords[i]); - } - - return coords; - }; - })() - }); - - return InfinitePlane; -}); -define('qtek/plugin/OrbitControl',['require','../core/Base','../math/Vector3','../math/Matrix4'],function(require) { - - var Base = require('../core/Base'); - var Vector3 = require('../math/Vector3'); - var Matrix4 = require('../math/Matrix4'); - - /** - * @constructor qtek.plugin.OrbitControl - * - * @example - * - * var control = new qtek.plugin.OrbitControl({ - * target: camera, - * domElement: renderer.canvas - * }); - * // Rotate around car - * control.origin.copy(car.position); - * ... - * animation.on('frame', function(frameTime) { - * control.update(frameTime); - * renderer.render(scene, camera); - * }); - */ - var OrbitControl = Base.derive(function() { - return /** @lends qtek.plugin.OrbitControl# */ { - /** - * Scene node to control, mostly it is a camera - * @type {qtek.Node} - */ - target: null, - - /** - * Target dom to bind with mouse events - * @type {HTMLElement} - */ - domElement: null, - - /** - * Mouse move sensitivity - * @type {number} - */ - sensitivity: 1, - - /** - * Origin to rotate around - * @type {qtek.math.Vector3} - */ - origin: new Vector3(), - - /** - * Up axis - * @type {qtek.math.Vector3} - */ - up: new Vector3(0, 1, 0), - - /** - * Minimum distance from origin to target when zooming in - * @type {number} - */ - minDistance: 0, - /** - * Maximum distance from origin to target when zooming out - * @type {number} - */ - maxDistance: Infinity, - - /** - * Minimum polar angle when rotate up, it is 0 when the direction origin point to target is same with up axis - * @type {number} - */ - minPolarAngle: 0, // [0, Math.PI/2] - - /** - * Maximum polar angle when rotate down. It is PI when the direction origin point to target is opposite to up axis - * @type {number} - */ - maxPolarAngle: Math.PI, // [Math.PI/2, Math.PI] - - // Rotate around origin - _offsetPitch: 0, - _offsetRoll: 0, - - // Pan the origin - _panX: 0, - _panY: 0, - - // Offset of mouse move - _offsetX: 0, - _offsetY: 0, - - // Zoom with mouse wheel - _forward: 0, - - _op: -1 //0: ROTATE, 1: PAN - }; - }, function() { - this._mouseDown = this._mouseDown.bind(this); - this._mouseUp = this._mouseUp.bind(this); - this._mouseMove = this._mouseMove.bind(this); - this._mouseOut = this._mouseOut.bind(this); - this._mouseWheel = this._mouseWheel.bind(this); - - if (this.domElement) { - this.enable(); - } - }, - /** @lends qtek.plugin.OrbitControl.prototype */ - { - /** - * Enable control - */ - enable: function() { - var domElement = this.domElement; - domElement.addEventListener('mousedown', this._mouseDown); - domElement.addEventListener('mousewheel', this._mouseWheel); - domElement.addEventListener('DOMMouseScroll', this._mouseWheel); - - domElement.addEventListener('touchstart', this._mouseDown); - - }, - - /** - * Disable control - */ - disable: function() { - this.domElement.removeEventListener('mousedown', this._mouseDown); - this.domElement.removeEventListener('mousewheel', this._mouseWheel); - this.domElement.removeEventListener('DOMMouseScroll', this._mouseWheel); - - this.domElement.removeEventListener('touchstart', this._mouseDown); - - this._mouseUp(); - }, - - _mouseWheel: function(e) { - e.preventDefault(); - var delta = e.wheelDelta // Webkit - || -e.detail; // Firefox - - this._forward += delta * this.sensitivity; - }, - - _mouseDown: function(e) { - document.addEventListener('mousemove', this._mouseMove); - document.addEventListener('mouseup', this._mouseUp); - document.addEventListener('mouseout', this._mouseOut); - - document.addEventListener('touchend', this._mouseUp); - document.addEventListener('touchmove', this._mouseMove); - - this._offsetX = e.pageX; - this._offsetY = e.pageY; - - // Rotate - if (e.button === 0) { - this._op = 0; - } else if (e.button === 1) { - this._op = 1; - } - }, - - _mouseMove: function(e) { - var dx = e.pageX - this._offsetX; - var dy = e.pageY - this._offsetY; - - if (this._op === 0) { - this._offsetPitch += dx * this.sensitivity / 100; - this._offsetRoll += dy * this.sensitivity / 100; - } else if (this._op === 1) { - var len = this.origin.distance(this.target.position); - var divider; - if (this.target.fov) { - divider = Math.sin(this.target.fov * Math.PI / 360) / 200; - } else { - divider = 1 / 200; - } - this._panX += dx * this.sensitivity * len * divider; - this._panY += dy * this.sensitivity * len * divider; - } - - this._offsetX = e.pageX; - this._offsetY = e.pageY; - }, - - _mouseUp: function() { - - document.removeEventListener('mousemove', this._mouseMove); - document.removeEventListener('mouseup', this._mouseUp); - document.removeEventListener('mouseout', this._mouseOut); - - document.removeEventListener('touchend', this._mouseUp); - document.removeEventListener('touchmove', this._mouseMove); - - this._op = -1; - }, - - _mouseOut: function() { - this._mouseUp(); - }, - - /** - * Control update. Should be invoked every frame - * @param {number} frameTime Frame time - */ - update: function(frameTime) { - var target = this.target; - var zAxis = target.localTransform.z.normalize(); - var yAxis = target.localTransform.y.normalize(); - if (this._op === 0 && this._offsetPitch !== 0 && this._offsetRoll !== 0) { - // Rotate - target.rotateAround(this.origin, this.up, -this._offsetPitch); - var xAxis = target.localTransform.x; - target.rotateAround(this.origin, xAxis, -this._offsetRoll); - - var zAxis = target.worldTransform.z.normalize(); - var phi = Math.acos(this.up.dot(zAxis)); - // Rotate back a bit - if (this._offsetRoll > 0 && phi <= this.minPolarAngle) { - target.rotateAround(this.origin, xAxis, -phi + this.minPolarAngle); - } - else if (this._offsetRoll < 0 && phi >= this.maxPolarAngle) { - target.rotateAround(this.origin, xAxis, -phi + this.maxPolarAngle); - } - this._offsetRoll = this._offsetPitch = 0; - } else if (this._op === 1) { - // Pan - var xAxis = target.localTransform.x.normalize().scale(-this._panX); - var yAxis = target.localTransform.y.normalize().scale(this._panY); - target.position.add(xAxis).add(yAxis); - this.origin.add(xAxis).add(yAxis); - this._panX = this._panY = 0; - } - if (this._forward !== 0) { - // Zoom - var distance = target.position.distance(this.origin); - var nextDistance = distance + this._forward * distance / 5000; - if (nextDistance < this.maxDistance && nextDistance > this.minDistance) { - target.position.scaleAndAdd(zAxis, this._forward * distance / 5000); - } - this._forward = 0; - } - - } - }); - - return OrbitControl; -}); -define('qtek/plugin/Skybox',['require','../Mesh','../geometry/Cube','../Shader','../Material'],function(require) { - - var Mesh = require('../Mesh'); - var CubeGeometry = require('../geometry/Cube'); - var Shader = require('../Shader'); - var Material = require('../Material'); - - var skyboxShader; - - /** - * @constructor qtek.plugin.Skybox - * - * @example - * var skyTex = new qtek.TextureCube(); - * skyTex.load({ - * 'px': 'assets/textures/sky/px.jpg', - * 'nx': 'assets/textures/sky/nx.jpg' - * 'py': 'assets/textures/sky/py.jpg' - * 'ny': 'assets/textures/sky/ny.jpg' - * 'pz': 'assets/textures/sky/pz.jpg' - * 'nz': 'assets/textures/sky/nz.jpg' - * }); - * var skybox = new qtek.plugin.Skybox({ - * scene: scene - * }); - * skybox.material.set('environmentMap', skyTex); - */ - var Skybox = Mesh.derive(function() { - - if (!skyboxShader) { - skyboxShader = new Shader({ - vertex: Shader.source('buildin.skybox.vertex'), - fragment: Shader.source('buildin.skybox.fragment') - }); - } - var material = new Material({ - shader: skyboxShader, - depthMask: false - }); - - return { - /** - * @type {qtek.Scene} - * @memberOf qtek.plugin.Skybox.prototype - */ - scene: null, - - geometry: new CubeGeometry(), - material: material, - culling: false - }; - }, function() { - var scene = this.scene; - if (scene) { - this.attachScene(scene); - } - }, { - /** - * Attach the skybox to the scene - * @param {qtek.Scene} scene - * @memberOf qtek.plugin.Skybox.prototype - */ - attachScene: function(scene) { - if (this.scene) { - this.detachScene(); - } - this.scene = scene; - scene.on('beforerender', this._beforeRenderScene, this); - }, - /** - * Detach from scene - * @memberOf qtek.plugin.Skybox.prototype - */ - detachScene: function() { - if (this.scene) { - this.scene.off('beforerender', this._beforeRenderScene, this); - } - this.scene = null; - }, - - dispose: function() { - this.detachScene(); - }, - - _beforeRenderScene: function(renderer, scene, camera) { - this.position.copy(camera.getWorldPosition()); - this.update(); - renderer.renderQueue([this], camera); - } - }); - - return Skybox; -}); -define('qtek/plugin/Skydome',['require','../Mesh','../geometry/Sphere','../Shader','../Material','../shader/library'],function(require) { - - var Mesh = require('../Mesh'); - var SphereGeometry = require('../geometry/Sphere'); - var Shader = require('../Shader'); - var Material = require('../Material'); - var shaderLibrary = require('../shader/library'); - - var skydomeShader; - - /** - * @constructor qtek.plugin.Skydome - * - * @example - * var skyTex = new qtek.Texture2D(); - * skyTex.load('assets/textures/sky.jpg'); - * var skydome = new qtek.plugin.Skydome({ - * scene: scene - * }); - * skydome.material.set('diffuseMap', skyTex); - */ - var Skydome = Mesh.derive(function() { - - if (!skydomeShader) { - skydomeShader = new Shader({ - vertex: Shader.source('buildin.basic.vertex'), - fragment: Shader.source('buildin.basic.fragment') - }); - skydomeShader.enableTexture('diffuseMap'); - } - - var material = new Material({ - shader: skydomeShader, - depthMask: false - }); - - return { - /** - * @type {qtek.Scene} - * @memberOf qtek.plugin.Skydome# - */ - scene: null, - - geometry: new SphereGeometry({ - widthSegments: 30, - heightSegments: 30, - // thetaLength: Math.PI / 2 - }), - material: material, - culling: false - }; - }, function() { - var scene = this.scene; - if (scene) { - this.attachScene(scene); - } - }, { - /** - * Attach the skybox to the scene - * @param {qtek.Scene} scene - * @memberOf qtek.plugin.Skydome.prototype - */ - attachScene: function(scene) { - if (this.scene) { - this.detachScene(); - } - this.scene = scene; - scene.on('beforerender', this._beforeRenderScene, this); - }, - /** - * Detach from scene - * @memberOf qtek.plugin.Skydome.prototype - */ - detachScene: function() { - if (this.scene) { - this.scene.off('beforerender', this._beforeRenderScene, this); - } - this.scene = null; - }, - - _beforeRenderScene: function(renderer, scene, camera) { - this.position.copy(camera.getWorldPosition()); - this.update(); - renderer.renderQueue([this], camera); - }, - - dispose: function() { - this.detachScene(); - } - }); - - return Skydome; -}); -define('qtek/prePass/EnvironmentMap',['require','../core/Base','../math/Vector3','../camera/Perspective','../core/glenum','../FrameBuffer','../TextureCube'],function (require) { - - var Base = require('../core/Base'); - var Vector3 = require('../math/Vector3'); - var PerspectiveCamera = require('../camera/Perspective'); - var glenum = require('../core/glenum'); - var FrameBuffer = require('../FrameBuffer'); - var TextureCube = require('../TextureCube'); - - var targets = ['px', 'nx', 'py', 'ny', 'pz', 'nz']; - - /** - * Pass rendering scene to a environment cube map - * - * @constructor qtek.prePass.EnvironmentMap - * @extends qtek.core.Base - * @example - * // Example of car reflection - * var envMap = new qtek.TextureCube({ - * width: 256, - * height: 256 - * }); - * var envPass = new qtek.prePass.EnvironmentMap({ - * position: car.position, - * texture: envMap - * }); - * var carBody = car.getChildByName('body'); - * carBody.material.shader.enableTexture('environmentMap'); - * carBody.material.set('environmentMap', envMap); - * ... - * animation.on('frame', function(frameTime) { - * envPass.render(renderer, scene); - * renderer.render(scene, camera); - * }); - */ - var EnvironmentMapPass = Base.derive(function() { - var ret = { - /** - * Camera position - * @type {qtek.math.Vector3} - * @memberOf qtek.prePass.EnvironmentMap# - */ - position: new Vector3(), - /** - * Camera far plane - * @type {number} - * @memberOf qtek.prePass.EnvironmentMap# - */ - far: 1000, - /** - * Camera near plane - * @type {number} - * @memberOf qtek.prePass.EnvironmentMap# - */ - near: 0.1, - /** - * Environment cube map - * @type {qtek.TextureCube} - * @memberOf qtek.prePass.EnvironmentMap# - */ - texture: null - - // frameBuffer: new FrameBuffer() - }; - ret._cameras = { - px: new PerspectiveCamera({fov: 90}), - nx: new PerspectiveCamera({fov: 90}), - py: new PerspectiveCamera({fov: 90}), - ny: new PerspectiveCamera({fov: 90}), - pz: new PerspectiveCamera({fov: 90}), - nz: new PerspectiveCamera({fov: 90}) - }; - ret._cameras.px.lookAt(Vector3.POSITIVE_X, Vector3.NEGATIVE_Y); - ret._cameras.nx.lookAt(Vector3.NEGATIVE_X, Vector3.NEGATIVE_Y); - ret._cameras.py.lookAt(Vector3.POSITIVE_Y, Vector3.POSITIVE_Z); - ret._cameras.ny.lookAt(Vector3.NEGATIVE_Y, Vector3.NEGATIVE_Z); - ret._cameras.pz.lookAt(Vector3.POSITIVE_Z, Vector3.NEGATIVE_Y); - ret._cameras.nz.lookAt(Vector3.NEGATIVE_Z, Vector3.NEGATIVE_Y); - - // FIXME In windows, use one framebuffer only renders one side of cubemap - ret._frameBuffers = { - px: new FrameBuffer(), - nx: new FrameBuffer(), - py: new FrameBuffer(), - ny: new FrameBuffer(), - pz: new FrameBuffer(), - nz: new FrameBuffer() - }; - - return ret; - }, { - /** - * @param {qtek.Renderer} renderer - * @param {qtek.Scene} scene - * @param {boolean} [notUpdateScene=false] - */ - render: function(renderer, scene, notUpdateScene) { - var _gl = renderer.gl; - if (!notUpdateScene) { - scene.update(true); - } - // Tweak fov - // http://the-witness.net/news/2012/02/seamless-cube-map-filtering/ - var n = this.texture.width; - var fov = 2 * Math.atan(n / (n - 0.5)) / Math.PI * 180; - for (var i = 0; i < 6; i++) { - var target = targets[i]; - var camera = this._cameras[target]; - Vector3.copy(camera.position, this.position); - camera.far = this.far; - camera.near = this.near; - camera.fov = fov; - - this._frameBuffers[target].attach( - _gl, this.texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i - ); - this._frameBuffers[target].bind(renderer); - renderer.render(scene, camera, true); - this._frameBuffers[target].unbind(renderer); - } - }, - /** - * @param {qtek.Renderer} renderer - */ - dispose: function(renderer) { - // this.frameBuffer.dispose(renderer.gl); - for (var i = 0; i < 6; i++) { - var target = targets[i]; - this._frameBuffers[target].dispose(renderer.gl); - } - } - }); - - return EnvironmentMapPass; -}); -define('qtek/prePass/Reflection',['require','../core/Base','../math/Vector4'],function(require) { + /** + * Adds two vec2's after scaling the second operand by a scalar value + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @param {Number} scale the amount to scale b by before adding + * @returns {vec2} out + */ + vec2.scaleAndAdd = function(out, a, b, scale) { + out[0] = a[0] + (b[0] * scale); + out[1] = a[1] + (b[1] * scale); + return out; + }; - var Base = require('../core/Base'); - var Vector4 = require('../math/Vector4'); + /** + * Calculates the euclidian distance between two vec2's + * + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {Number} distance between a and b + */ + vec2.distance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1]; + return Math.sqrt(x*x + y*y); + }; - var ReflectionPass = Base.derive(function() { - console.warn('TODO'); - }, { - render : function(renderer, scene, camera) { + /** + * Alias for {@link vec2.distance} + * @function + */ + vec2.dist = vec2.distance; + + /** + * Calculates the squared euclidian distance between two vec2's + * + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {Number} squared distance between a and b + */ + vec2.squaredDistance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1]; + return x*x + y*y; + }; - } - }); + /** + * Alias for {@link vec2.squaredDistance} + * @function + */ + vec2.sqrDist = vec2.squaredDistance; + + /** + * Calculates the length of a vec2 + * + * @param {vec2} a vector to calculate length of + * @returns {Number} length of a + */ + vec2.length = function (a) { + var x = a[0], + y = a[1]; + return Math.sqrt(x*x + y*y); + }; - return ReflectionPass; -}); -define('qtek/prePass/ShadowMap',['require','../core/Base','../core/glenum','../math/Vector3','../math/BoundingBox','../math/Frustum','../math/Matrix4','../Renderer','../Shader','../Light','../Mesh','../light/Spot','../light/Directional','../light/Point','../shader/library','../Material','../FrameBuffer','../Texture2D','../TextureCube','../camera/Perspective','../camera/Orthographic','../compositor/Pass','../compositor/TexturePool','../dep/glmatrix'],function(require) { - - var Base = require('../core/Base'); - var glenum = require('../core/glenum'); - var Vector3 = require('../math/Vector3'); - var BoundingBox = require('../math/BoundingBox'); - var Frustum = require('../math/Frustum'); - var Matrix4 = require('../math/Matrix4'); - var Renderer = require('../Renderer'); - var Shader = require('../Shader'); - var Light = require('../Light'); - var Mesh = require('../Mesh'); - var SpotLight = require('../light/Spot'); - var DirectionalLight = require('../light/Directional'); - var PointLight = require('../light/Point'); - var shaderLibrary = require('../shader/library'); - var Material = require('../Material'); - var FrameBuffer = require('../FrameBuffer'); - var Texture2D = require('../Texture2D'); - var TextureCube = require('../TextureCube'); - var PerspectiveCamera = require('../camera/Perspective'); - var OrthoCamera = require('../camera/Orthographic'); - - var Pass = require('../compositor/Pass'); - var TexturePool = require('../compositor/TexturePool'); - - var glMatrix = require('../dep/glmatrix'); - var mat4 = glMatrix.mat4; - var vec3 = glMatrix.vec3; - - var targets = ['px', 'nx', 'py', 'ny', 'pz', 'nz']; - - /** - * Pass rendering shadow map. - * - * @constructor qtek.prePass.ShadowMap - * @extends qtek.core.Base - * @example - * var shadowMapPass = new qtek.prePass.ShadowMap({ - * softShadow: qtek.prePass.ShadowMap.VSM - * }); - * ... - * animation.on('frame', function(frameTime) { - * shadowMapPass.render(renderer, scene, camera); - * renderer.render(scene, camera); - * }); - */ - var ShadowMapPass = Base.derive(function() { - return /** @lends qtek.prePass.ShadowMap# */ { - /** - * Soft shadow technique. - * Can be {@link qtek.prePass.ShadowMap.PCF} or {@link qtek.prePass.ShadowMap.VSM} - * @type {number} - */ - softShadow: ShadowMapPass.PCF, - - /** - * Soft shadow blur size - * @type {number} - */ - shadowBlur: 1.0, - - /** - * Shadow cascade. - * Use PSSM technique when it is larger than 1 and have a unique directional light in scene. - * @type {number} - */ - shadowCascade: 1, - - /** - * Available when shadowCascade is larger than 1 and have a unique directional light in scene. - * @type {number} - */ - cascadeSplitLogFactor: 0.2, - - lightFrustumBias: 10, - - _frameBuffer: new FrameBuffer(), - - _textures: {}, - _shadowMapNumber: { - 'POINT_LIGHT': 0, - 'DIRECTIONAL_LIGHT': 0, - 'SPOT_LIGHT': 0 - }, - - _meshMaterials: {}, - _depthMaterials: {}, - _depthShaders: {}, - _distanceMaterials: {}, - - _opaqueCasters: [], - _receivers: [], - _lightsCastShadow: [], - - _lightCameras: {}, - - _texturePool: new TexturePool() - }; - }, function() { - // Gaussian filter pass for VSM - this._gaussianPassH = new Pass({ - fragment: Shader.source('buildin.compositor.gaussian_blur_h') - }); - this._gaussianPassV = new Pass({ - fragment: Shader.source('buildin.compositor.gaussian_blur_v') - }); - this._gaussianPassH.setUniform('blurSize', this.shadowBlur); - this._gaussianPassV.setUniform('blurSize', this.shadowBlur); - - this._outputDepthPass = new Pass({ - fragment: Shader.source('buildin.sm.debug_depth') - }); - }, { - /** - * Render scene to shadow textures - * @param {qtek.Renderer} renderer - * @param {qtek.Scene} scene - * @param {qtek.Camera} sceneCamera - * @memberOf qtek.prePass.ShadowMap.prototype - */ - render: function(renderer, scene, sceneCamera) { - this.trigger('beforerender', this, renderer, scene, sceneCamera); - this._renderShadowPass(renderer, scene, sceneCamera); - this.trigger('afterrender', this, renderer, scene, sceneCamera); - }, - - /** - * Debug rendering of shadow textures - * @param {qtek.Renderer} renderer - * @param {number} size - * @memberOf qtek.prePass.ShadowMap.prototype - */ - renderDebug: function(renderer, size) { - var prevClear = renderer.clear; - renderer.clear = glenum.DEPTH_BUFFER_BIT; - var viewport = renderer.viewport; - var x = 0, y = 0; - var width = size || viewport.width / 4; - var height = width; - if (this.softShadow === ShadowMapPass.VSM) { - this._outputDepthPass.material.shader.define('fragment', 'USE_VSM'); - } else { - this._outputDepthPass.material.shader.unDefine('fragment', 'USE_VSM'); - } - for (var name in this._textures) { - renderer.setViewport(x, y, width, height); - this._outputDepthPass.setUniform('depthMap', this._textures[name]); - this._outputDepthPass.render(renderer); - x += width; - } - renderer.setViewport(viewport); - renderer.clear = prevClear; - }, - - _bindDepthMaterial: function(casters, bias, slopeScale) { - for (var i = 0; i < casters.length; i++) { - var mesh = casters[i]; - var isShadowTransparent = mesh.material.shadowTransparentMap instanceof Texture2D; - var transparentMap = mesh.material.shadowTransparentMap; - var nJoints = mesh.joints && mesh.joints.length; - var matHashKey; - var shaderHashKey; - if (isShadowTransparent) { - matHashKey = nJoints + '-' + transparentMap.__GUID__; - shaderHashKey = nJoints + 's'; - } else { - matHashKey = nJoints; - shaderHashKey = nJoints; - } - var depthMaterial = this._depthMaterials[matHashKey]; - var depthShader = this._depthShaders[shaderHashKey]; - - if (mesh.material !== depthMaterial) { // Not binded yet - if (!depthShader) { - depthShader = new Shader({ - vertex: Shader.source('buildin.sm.depth.vertex'), - fragment: Shader.source('buildin.sm.depth.fragment') - }); - if (nJoints > 0) { - depthShader.define('vertex', 'SKINNING'); - depthShader.define('vertex', 'JOINT_NUMBER', nJoints); - } - if (isShadowTransparent) { - depthShader.define('both', 'SHADOW_TRANSPARENT'); - } - this._depthShaders[shaderHashKey] = depthShader; - } - if (!depthMaterial) { - // Skinned mesh - depthMaterial = new Material({ - shader: depthShader - }); - this._depthMaterials[matHashKey] = depthMaterial; - } - - this._meshMaterials[mesh.__GUID__] = mesh.material; - mesh.material = depthMaterial; - - if (this.softShadow === ShadowMapPass.VSM) { - depthShader.define('fragment', 'USE_VSM'); - } else { - depthShader.unDefine('fragment', 'USE_VSM'); - } - - depthMaterial.setUniform('bias', bias); - depthMaterial.setUniform('slopeScale', slopeScale); - if (isShadowTransparent) { - depthMaterial.set('shadowTransparentMap', transparentMap); - } - } - } - }, - - _bindDistanceMaterial: function(casters, light) { - for (var i = 0; i < casters.length; i++) { - var mesh = casters[i]; - var nJoints = mesh.joints && mesh.joints.length; - var distanceMaterial = this._distanceMaterials[nJoints]; - if (mesh.material !== distanceMaterial) { - if (!distanceMaterial) { - // Skinned mesh - distanceMaterial = new Material({ - shader: new Shader({ - vertex: Shader.source('buildin.sm.distance.vertex'), - fragment: Shader.source('buildin.sm.distance.fragment') - }) - }); - if (nJoints > 0) { - distanceMaterial.shader.define('vertex', 'SKINNING'); - distanceMaterial.shader.define('vertex', 'JOINT_NUMBER', nJoints); - } - this._distanceMaterials[nJoints] = distanceMaterial; - } - - this._meshMaterials[mesh.__GUID__] = mesh.material; - mesh.material = distanceMaterial; - - if (this.softShadow === ShadowMapPass.VSM) { - distanceMaterial.shader.define('fragment', 'USE_VSM'); - } else { - distanceMaterial.shader.unDefine('fragment', 'USE_VSM'); - } - distanceMaterial.set('lightPosition', light.position._array); - distanceMaterial.set('range', light.range * 5); - } - } - }, - - _restoreMaterial: function(casters) { - for (var i = 0; i < casters.length; i++) { - var mesh = casters[i]; - mesh.material = this._meshMaterials[mesh.__GUID__]; - } - }, - - _updateCaster: function(mesh) { - if (mesh.castShadow) { - this._opaqueCasters.push(mesh); - } - if (mesh.receiveShadow) { - this._receivers.push(mesh); - mesh.material.__shadowUniformUpdated = false; - mesh.material.shader.__shadowDefineUpdated = false; - mesh.material.set('shadowEnabled', 1); - } else { - mesh.material.set('shadowEnabled', 0); - } - if (this.softShadow === ShadowMapPass.VSM) { - mesh.material.shader.define('fragment', 'USE_VSM'); - } else { - mesh.material.shader.unDefine('fragment', 'USE_VSM'); - } - }, - - _update: function(scene) { - for (var i = 0; i < scene.opaqueQueue.length; i++) { - this._updateCaster(scene.opaqueQueue[i]); - } - for (var i = 0; i < scene.transparentQueue.length; i++) { - // TODO Transparent object receive shadow will be very slow - // in stealth demo, still not find the reason - this._updateCaster(scene.transparentQueue[i]); - } - for (var i = 0; i < scene.lights.length; i++) { - var light = scene.lights[i]; - if (light.castShadow) { - this._lightsCastShadow.push(light); - } - } - }, - - _renderShadowPass: function(renderer, scene, sceneCamera) { - // reset - for (var name in this._shadowMapNumber) { - this._shadowMapNumber[name] = 0; - } - this._lightsCastShadow.length = 0; - this._opaqueCasters.length = 0; - this._receivers.length = 0; - - var _gl = renderer.gl; - - scene.update(); - - this._update(scene); - - if (!this._lightsCastShadow.length) { - return; - } - - _gl.enable(_gl.DEPTH_TEST); - _gl.depthMask(true); - _gl.disable(_gl.BLEND); - - // Clear with high-z, so the part not rendered will not been shadowed - // TODO - _gl.clearColor(1.0, 1.0, 1.0, 1.0); - - // Shadow uniforms - var spotLightShadowMaps = []; - var spotLightMatrices = []; - var directionalLightShadowMaps = []; - var directionalLightMatrices = []; - var shadowCascadeClips = []; - var pointLightShadowMaps = []; - var pointLightRanges = []; - - // Create textures for shadow map - for (var i = 0; i < this._lightsCastShadow.length; i++) { - var light = this._lightsCastShadow[i]; - if (light instanceof DirectionalLight) { - this._renderDirectionalLightShadow( - renderer, - light, - scene, - sceneCamera, - this._opaqueCasters, - shadowCascadeClips, - directionalLightMatrices, - directionalLightShadowMaps - ); - } else if (light instanceof SpotLight) { - this._renderSpotLightShadow( - renderer, - light, - this._opaqueCasters, - spotLightMatrices, - spotLightShadowMaps - ); - } else if (light instanceof PointLight) { - this._renderPointLightShadow( - renderer, - light, - this._opaqueCasters, - pointLightRanges, - pointLightShadowMaps - ); - } - - this._shadowMapNumber[light.type]++; - } - this._restoreMaterial(this._opaqueCasters); - - if (this.shadowCascade > 1 && this._shadowMapNumber.DIRECTIONAL_LIGHT > 1) { - console.warn('There is only one directional light can cast shadow when using cascaded shadow map'); - } - - var shadowCascadeClipsNear = shadowCascadeClips.slice(); - var shadowCascadeClipsFar = shadowCascadeClips.slice(); - shadowCascadeClipsNear.pop(); - shadowCascadeClipsFar.shift(); - - // Iterate from far to near - shadowCascadeClipsNear.reverse(); - shadowCascadeClipsFar.reverse(); - directionalLightShadowMaps.reverse(); - directionalLightMatrices.reverse(); - - for (var i = 0; i < this._receivers.length; i++) { - var mesh = this._receivers[i]; - var material = mesh.material; - if (material.__shadowUniformUpdated) { - continue; - } - var shader = material.shader; - - if (!shader.__shadowDefineUpdated) { - var shaderNeedsUpdate = false; - for (var lightType in this._shadowMapNumber) { - var number = this._shadowMapNumber[lightType]; - var key = lightType + '_SHADOWMAP_NUMBER'; - - if (shader.fragmentDefines[key] !== number && number > 0) { - shader.fragmentDefines[key] = number; - shaderNeedsUpdate = true; - } - } - if (shaderNeedsUpdate) { - shader.dirty(); - } - if (this.shadowCascade > 1) { - shader.define('fragment', 'SHADOW_CASCADE', this.shadowCascade); - } else { - shader.unDefine('fragment', 'SHADOW_CASCADE'); - } - shader.__shadowDefineUpdated = true; - } - - if (spotLightShadowMaps.length > 0) { - material.setUniform('spotLightShadowMaps', spotLightShadowMaps); - material.setUniform('spotLightMatrices', spotLightMatrices); - } - if (directionalLightShadowMaps.length > 0) { - material.setUniform('directionalLightShadowMaps', directionalLightShadowMaps); - if (this.shadowCascade > 1) { - material.setUniform('shadowCascadeClipsNear', shadowCascadeClipsNear); - material.setUniform('shadowCascadeClipsFar', shadowCascadeClipsFar); - } - material.setUniform('directionalLightMatrices', directionalLightMatrices); - } - if (pointLightShadowMaps.length > 0) { - material.setUniform('pointLightShadowMaps', pointLightShadowMaps); - material.setUniform('pointLightRanges', pointLightRanges); - } - material.__shadowUniformUpdated = true; - } - }, - - _renderDirectionalLightShadow: (function() { - - var splitFrustum = new Frustum(); - var splitProjMatrix = new Matrix4(); - var cropBBox = new BoundingBox(); - var cropMatrix = new Matrix4(); - var lightViewProjMatrix = new Matrix4(); - var lightProjMatrix = new Matrix4(); - - var prevDepth = 0; - var deltaDepth = 0; - return function(renderer, light, scene, sceneCamera, casters, shadowCascadeClips, directionalLightMatrices, directionalLightShadowMaps) { - - var shadowBias = light.shadowBias; - this._bindDepthMaterial(casters, shadowBias, light.shadowSlopeScale); - - casters.sort(Renderer.opaqueSortFunc); - - // Adjust scene camera - var originalFar = sceneCamera.far; - - // Considering moving speed since the bounding box is from last frame - // verlet integration ? - var depth = -sceneCamera.sceneBoundingBoxLastFrame.min.z; - deltaDepth = Math.max(depth - prevDepth, 0); - prevDepth = depth; - depth += deltaDepth; - // TODO: add a bias - if (depth > sceneCamera.near) { - sceneCamera.far = Math.min(sceneCamera.far, depth); - } - sceneCamera.updateProjectionMatrix(); - sceneCamera.frustum.setFromProjection(sceneCamera.projectionMatrix); - var lightCamera = this._getDirectionalLightCamera(light, scene, sceneCamera); - - var lvpMat4Arr = lightViewProjMatrix._array; - mat4.copy(lvpMat4Arr, lightCamera.worldTransform._array); - mat4.invert(lvpMat4Arr, lvpMat4Arr); - mat4.multiply(lvpMat4Arr, lightCamera.projectionMatrix._array, lvpMat4Arr); - mat4.multiply(lvpMat4Arr, lvpMat4Arr, sceneCamera.worldTransform._array); - - lightProjMatrix.copy(lightCamera.projectionMatrix); - - var clipPlanes = []; - var near = sceneCamera.near; - var far = sceneCamera.far; - var rad = sceneCamera.fov / 180 * Math.PI; - var aspect = sceneCamera.aspect; - - var scaleZ = (near + originalFar) / (near - originalFar); - var offsetZ = 2 * near * originalFar / (near - originalFar); - for (var i = 0; i <= this.shadowCascade; i++) { - var clog = near * Math.pow(far / near, i / this.shadowCascade); - var cuni = near + (far - near) * i / this.shadowCascade; - var c = clog * this.cascadeSplitLogFactor + cuni * (1 - this.cascadeSplitLogFactor); - clipPlanes.push(c); - shadowCascadeClips.push(-(-c * scaleZ + offsetZ) / -c); - } - for (var i = 0; i < this.shadowCascade; i++) { - var texture = this._getTexture(light.__GUID__ + '_' + i, light); - - // Get the splitted frustum - var nearPlane = clipPlanes[i]; - var farPlane = clipPlanes[i+1]; - mat4.perspective(splitProjMatrix._array, rad, aspect, nearPlane, farPlane); - splitFrustum.setFromProjection(splitProjMatrix); - splitFrustum.getTransformedBoundingBox(cropBBox, lightViewProjMatrix); - var _min = cropBBox.min._array; - var _max = cropBBox.max._array; - cropMatrix.ortho(_min[0], _max[0], _min[1], _max[1], 1, -1); - lightCamera.projectionMatrix.multiplyLeft(cropMatrix); - - var _gl = renderer.gl; - - this._frameBuffer.attach(_gl, texture); - this._frameBuffer.bind(renderer); - - _gl.clear(_gl.COLOR_BUFFER_BIT | _gl.DEPTH_BUFFER_BIT); - - // Set bias seperately for each cascade - // TODO Simply divide 1.5 ? - for (var key in this._depthMaterials) { - this._depthMaterials[key].set('shadowBias', shadowBias); - } - - renderer.renderQueue(casters, lightCamera); - - this._frameBuffer.unbind(renderer); - - // Filter for VSM - if (this.softShadow === ShadowMapPass.VSM) { - this._gaussianFilter(renderer, texture, texture.width); - } - - var matrix = new Matrix4(); - matrix.copy(lightCamera.worldTransform) - .invert() - .multiplyLeft(lightCamera.projectionMatrix); - - directionalLightShadowMaps.push(texture); - directionalLightMatrices.push(matrix._array); - - lightCamera.projectionMatrix.copy(lightProjMatrix); - } - - // set back - sceneCamera.far = originalFar; - }; - })(), - - _renderSpotLightShadow: function(renderer, light, casters, spotLightMatrices, spotLightShadowMaps) { - - this._bindDepthMaterial(casters, light.shadowBias, light.shadowSlopeScale); - casters.sort(Renderer.opaqueSortFunc); - - var texture = this._getTexture(light.__GUID__, light); - var camera = this._getSpotLightCamera(light); - var _gl = renderer.gl; - - this._frameBuffer.attach(_gl, texture); - this._frameBuffer.bind(renderer); - - _gl.clear(_gl.COLOR_BUFFER_BIT | _gl.DEPTH_BUFFER_BIT); - - renderer.renderQueue(casters, camera); - - this._frameBuffer.unbind(renderer); - - // Filter for VSM - if (this.softShadow === ShadowMapPass.VSM) { - this._gaussianFilter(renderer, texture, texture.width); - } - - var matrix = new Matrix4(); - matrix.copy(camera.worldTransform) - .invert() - .multiplyLeft(camera.projectionMatrix); - - spotLightShadowMaps.push(texture); - spotLightMatrices.push(matrix._array); - }, - - _renderPointLightShadow: function(renderer, light, casters, pointLightRanges, pointLightShadowMaps) { - var texture = this._getTexture(light.__GUID__, light); - var _gl = renderer.gl; - pointLightShadowMaps.push(texture); - pointLightRanges.push(light.range * 5); - - this._bindDistanceMaterial(casters, light); - for (var i = 0; i < 6; i++) { - var target = targets[i]; - var camera = this._getPointLightCamera(light, target); - - this._frameBuffer.attach(renderer.gl, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i); - this._frameBuffer.bind(renderer); - - _gl.clear(_gl.COLOR_BUFFER_BIT | _gl.DEPTH_BUFFER_BIT); - - renderer.renderQueue(casters, camera); - - this._frameBuffer.unbind(renderer); - } - }, - - _gaussianFilter: function(renderer, texture, size) { - var parameter = { - width: size, - height: size, - type: glenum.FLOAT - }; - var _gl = renderer.gl; - var tmpTexture = this._texturePool.get(parameter); - - this._frameBuffer.attach(_gl, tmpTexture); - this._frameBuffer.bind(renderer); - this._gaussianPassH.setUniform('texture', texture); - this._gaussianPassH.setUniform('textureWidth', size); - this._gaussianPassH.render(renderer); - this._frameBuffer.unbind(renderer); - - this._frameBuffer.attach(_gl, texture); - this._frameBuffer.bind(renderer); - this._gaussianPassV.setUniform('texture', tmpTexture); - this._gaussianPassV.setUniform('textureHeight', size); - this._gaussianPassV.render(renderer); - this._frameBuffer.unbind(renderer); - - this._texturePool.put(tmpTexture); - }, - - _getTexture: function(key, light) { - var texture = this._textures[key]; - var resolution = light.shadowResolution || 512; - if (!texture) { - if (light instanceof PointLight) { - texture = new TextureCube(); - } else { - texture = new Texture2D(); - } - texture.width = resolution; - texture.height = resolution; - if (this.softShadow === ShadowMapPass.VSM) { - texture.type = glenum.FLOAT; - texture.anisotropic = 4; - } else { - texture.minFilter = glenum.LINEAR; - texture.magFilter = glenum.LINEAR; - texture.useMipmap = false; - } - this._textures[key] = texture; - } - - return texture; - }, - - _getPointLightCamera: function(light, target) { - if (!this._lightCameras.point) { - this._lightCameras.point = { - px: new PerspectiveCamera(), - nx: new PerspectiveCamera(), - py: new PerspectiveCamera(), - ny: new PerspectiveCamera(), - pz: new PerspectiveCamera(), - nz: new PerspectiveCamera() - }; - } - var camera = this._lightCameras.point[target]; - - camera.far = light.range; - camera.fov = 90; - camera.position.set(0, 0, 0); - switch (target) { - case 'px': - camera.lookAt(Vector3.POSITIVE_X, Vector3.NEGATIVE_Y); - break; - case 'nx': - camera.lookAt(Vector3.NEGATIVE_X, Vector3.NEGATIVE_Y); - break; - case 'py': - camera.lookAt(Vector3.POSITIVE_Y, Vector3.POSITIVE_Z); - break; - case 'ny': - camera.lookAt(Vector3.NEGATIVE_Y, Vector3.NEGATIVE_Z); - break; - case 'pz': - camera.lookAt(Vector3.POSITIVE_Z, Vector3.NEGATIVE_Y); - break; - case 'nz': - camera.lookAt(Vector3.NEGATIVE_Z, Vector3.NEGATIVE_Y); - break; - } - camera.position.copy(light.position); - camera.update(); - - return camera; - }, - - _getDirectionalLightCamera: (function() { - var lightViewMatrix = new Matrix4(); - var lightViewBBox = new BoundingBox(); - // Camera of directional light will be adjusted - // to contain the view frustum and scene bounding box as tightly as possible - return function(light, scene, sceneCamera) { - if (!this._lightCameras.directional) { - this._lightCameras.directional = new OrthoCamera(); - } - var camera = this._lightCameras.directional; - - // Move to the center of frustum(in world space) - camera.position - .copy(sceneCamera.frustum.boundingBox.min) - .add(sceneCamera.frustum.boundingBox.max) - .scale(0.5) - .transformMat4(sceneCamera.worldTransform); - camera.rotation.copy(light.rotation); - camera.scale.copy(light.scale); - camera.updateLocalTransform(); - camera.updateWorldTransform(); - - // Transform to light view space - lightViewMatrix - .copy(camera.worldTransform) - .invert() - .multiply(sceneCamera.worldTransform); - - sceneCamera.frustum.getTransformedBoundingBox(lightViewBBox, lightViewMatrix); - var min = lightViewBBox.min._array; - var max = lightViewBBox.max._array; - - // Move camera to adjust the near to 0 - // TODO: some scene object cast shadow in view will also be culled - // add a bias? - camera.position.scaleAndAdd(camera.worldTransform.z, max[2] + this.lightFrustumBias); - camera.near = 0; - camera.far = -min[2] + max[2] + this.lightFrustumBias; - camera.left = min[0] - this.lightFrustumBias; - camera.right = max[0] + this.lightFrustumBias; - camera.top = max[1] + this.lightFrustumBias; - camera.bottom = min[1] - this.lightFrustumBias; - camera.update(true); - - return camera; - }; - })(), - - _getSpotLightCamera: function(light) { - if (!this._lightCameras.spot) { - this._lightCameras.spot = new PerspectiveCamera(); - } - var camera = this._lightCameras.spot; - // Update properties - camera.fov = light.penumbraAngle * 2; - camera.far = light.range; - camera.worldTransform.copy(light.worldTransform); - camera.updateProjectionMatrix(); - mat4.invert(camera.viewMatrix._array, camera.worldTransform._array); - - return camera; - }, - - /** - * @param {qtek.Renderer} renderer - * @memberOf qtek.prePass.ShadowMap.prototype - */ - dispose: function(renderer) { - var _gl = renderer.gl; - for (var guid in this._depthMaterials) { - var mat = this._depthMaterials[guid]; - mat.dispose(_gl); - } - for (var guid in this._distanceMaterials) { - var mat = this._distanceMaterials[guid]; - mat.dispose(_gl); - } - - if (this._frameBuffer) { - this._frameBuffer.dispose(_gl); - } - - for (var name in this._textures) { - this._textures[name].dispose(_gl); - } - - this._texturePool.clear(renderer.gl); - - this._depthMaterials = {}; - this._distanceMaterials = {}; - this._textures = {}; - this._lightCameras = {}; - this._shadowMapNumber = { - 'POINT_LIGHT': 0, - 'DIRECTIONAL_LIGHT': 0, - 'SPOT_LIGHT': 0 - }; - this._meshMaterials = {}; - - for (var i = 0; i < this._receivers.length; i++) { - var mesh = this._receivers[i]; - // Mesh may be disposed - if (mesh.material && mesh.material.shader) { - var material = mesh.material; - var shader = material.shader; - shader.unDefine('fragment', 'POINT_LIGHT_SHADOW_NUMBER'); - shader.unDefine('fragment', 'DIRECTIONAL_LIGHT_SHADOW_NUMBER'); - shader.unDefine('fragment', 'AMBIENT_LIGHT_SHADOW_NUMBER'); - material.set('shadowEnabled', 0); - } - } - - this._opaqueCasters = []; - this._receivers = []; - this._lightsCastShadow = []; - } - }); - - /** - * @name qtek.prePass.ShadowMap.VSM - * @type {number} - */ - ShadowMapPass.VSM = 1; - - /** - * @name qtek.prePass.ShadowMap.PCF - * @type {number} - */ - ShadowMapPass.PCF = 2; - - return ShadowMapPass; -}); -define('qtek/shader/source/basic.essl',[],function () { return '@export buildin.basic.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\n\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 position : POSITION;\n\nattribute vec3 barycentric;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Barycentric;\n\nvoid main()\n{\n vec3 skinnedPosition = position;\n\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n \n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_Barycentric = barycentric;\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n}\n\n@end\n\n\n\n\n@export buildin.basic.fragment\n\nvarying vec2 v_Texcoord;\nuniform sampler2D diffuseMap;\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform vec3 emission : [0.0, 0.0, 0.0];\nuniform float alpha : 1.0;\n\n// Uniforms for wireframe\nuniform float lineWidth : 0.0;\nuniform vec3 lineColor : [0.0, 0.0, 0.0];\nvarying vec3 v_Barycentric;\n\n#extension GL_OES_standard_derivatives : enable\n@import buildin.util.edge_factor\n\nvoid main()\n{\n\n #ifdef RENDER_TEXCOORD\n gl_FragColor = vec4(v_Texcoord, 1.0, 1.0);\n return;\n #endif\n\n gl_FragColor = vec4(color, alpha);\n \n #ifdef DIFFUSEMAP_ENABLED\n vec4 tex = texture2D( diffuseMap, v_Texcoord );\n\n #ifdef SRGB_DECODE\n tex.rgb = pow(tex.rgb, vec3(2.2));\n #endif\n \n #if defined(DIFFUSEMAP_ALPHA_ALPHA)\n gl_FragColor.a = tex.a;\n #endif\n\n gl_FragColor.rgb *= tex.rgb;\n #endif\n\n gl_FragColor.rgb += emission;\n if( lineWidth > 0.01)\n {\n gl_FragColor.rgb = gl_FragColor.rgb * mix(lineColor, vec3(1.0), edgeFactor(lineWidth));\n }\n}\n\n@end';}); + /** + * Alias for {@link vec2.length} + * @function + */ + vec2.len = vec2.length; + + /** + * Calculates the squared length of a vec2 + * + * @param {vec2} a vector to calculate squared length of + * @returns {Number} squared length of a + */ + vec2.squaredLength = function (a) { + var x = a[0], + y = a[1]; + return x*x + y*y; + }; -define('qtek/shader/source/lambert.essl',[],function () { return '/**\n * http://en.wikipedia.org/wiki/Lambertian_reflectance\n */\n\n@export buildin.lambert.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\n\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 normal : NORMAL;\n\nattribute vec3 barycentric;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n // Upper 3x3 of skinMatrix is orthogonal\n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4( skinnedPosition, 1.0 );\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_Normal = normalize( ( worldInverseTranspose * vec4(skinnedNormal, 0.0) ).xyz );\n v_WorldPosition = ( world * vec4( skinnedPosition, 1.0) ).xyz;\n\n v_Barycentric = barycentric;\n}\n\n@end\n\n\n@export buildin.lambert.fragment\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\n\nuniform sampler2D diffuseMap;\nuniform sampler2D alphaMap;\n\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform vec3 emission : [0.0, 0.0, 0.0];\nuniform float alpha : 1.0;\n\n// Uniforms for wireframe\nuniform float lineWidth : 0.0;\nuniform vec3 lineColor : [0.0, 0.0, 0.0];\nvarying vec3 v_Barycentric;\n\n#ifdef AMBIENT_LIGHT_NUMBER\n@import buildin.header.ambient_light\n#endif\n#ifdef POINT_LIGHT_NUMBER\n@import buildin.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_NUMBER\n@import buildin.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_NUMBER\n@import buildin.header.spot_light\n#endif\n\n#extension GL_OES_standard_derivatives : enable\n// Import util functions and uniforms needed\n@import buildin.util.calculate_attenuation\n\n@import buildin.util.edge_factor\n\n@import buildin.plugin.compute_shadow_map\n\nvoid main()\n{\n #ifdef RENDER_NORMAL\n gl_FragColor = vec4(v_Normal, 1.0);\n return;\n #endif\n #ifdef RENDER_TEXCOORD\n gl_FragColor = vec4(v_Texcoord, 1.0, 1.0);\n return;\n #endif\n\n gl_FragColor = vec4(color, alpha);\n\n #ifdef DIFFUSEMAP_ENABLED\n vec4 tex = texture2D( diffuseMap, v_Texcoord );\n #ifdef SRGB_DECODE\n tex.rgb = pow(tex.rgb, vec3(2.2));\n #endif\n gl_FragColor.rgb *= tex.rgb;\n #ifdef DIFFUSEMAP_ALPHA_ALPHA\n gl_FragColor.a *= tex.a;\n #endif\n #endif\n\n vec3 diffuseColor = vec3(0.0, 0.0, 0.0);\n \n #ifdef AMBIENT_LIGHT_NUMBER\n for(int i = 0; i < AMBIENT_LIGHT_NUMBER; i++)\n {\n diffuseColor += ambientLightColor[i];\n }\n #endif\n // Compute point light color\n #ifdef POINT_LIGHT_NUMBER\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[POINT_LIGHT_NUMBER];\n if( shadowEnabled )\n {\n computeShadowOfPointLights( v_WorldPosition, shadowContribs );\n }\n #endif\n for(int i = 0; i < POINT_LIGHT_NUMBER; i++)\n {\n\n vec3 lightPosition = pointLightPosition[i];\n vec3 lightColor = pointLightColor[i];\n float range = pointLightRange[i];\n\n vec3 lightDirection = lightPosition - v_WorldPosition;\n\n // Calculate point light attenuation\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range);\n\n // Normalize vectors\n lightDirection /= dist;\n\n float ndl = dot( v_Normal, lightDirection );\n\n float shadowContrib = 1.0;\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n diffuseColor += lightColor * clamp(ndl, 0.0, 1.0) * attenuation * shadowContrib;\n }\n #endif\n #ifdef DIRECTIONAL_LIGHT_NUMBER\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[DIRECTIONAL_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfDirectionalLights( v_WorldPosition, shadowContribs );\n }\n #endif\n for(int i = 0; i < DIRECTIONAL_LIGHT_NUMBER; i++)\n {\n vec3 lightDirection = -directionalLightDirection[i];\n vec3 lightColor = directionalLightColor[i];\n \n float ndl = dot( v_Normal, normalize( lightDirection ) );\n\n float shadowContrib = 1.0;\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n diffuseColor += lightColor * clamp(ndl, 0.0, 1.0) * shadowContrib;\n }\n #endif\n \n #ifdef SPOT_LIGHT_NUMBER\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[SPOT_LIGHT_NUMBER];\n if( shadowEnabled )\n {\n computeShadowOfSpotLights( v_WorldPosition, shadowContribs );\n }\n #endif\n for(int i = 0; i < SPOT_LIGHT_NUMBER; i++)\n {\n vec3 lightPosition = -spotLightPosition[i];\n vec3 spotLightDirection = -normalize( spotLightDirection[i] );\n vec3 lightColor = spotLightColor[i];\n float range = spotLightRange[i];\n float a = spotLightUmbraAngleCosine[i];\n float b = spotLightPenumbraAngleCosine[i];\n float falloffFactor = spotLightFalloffFactor[i];\n\n vec3 lightDirection = lightPosition - v_WorldPosition;\n // Calculate attenuation\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range); \n\n // Normalize light direction\n lightDirection /= dist;\n // Calculate spot light fall off\n float c = dot(spotLightDirection, lightDirection);\n\n float falloff;\n falloff = clamp((c - a) /( b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n\n float ndl = dot(v_Normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n diffuseColor += lightColor * ndl * attenuation * (1.0-falloff) * shadowContrib;\n\n }\n #endif\n\n gl_FragColor.rgb *= diffuseColor;\n gl_FragColor.rgb += emission;\n if(lineWidth > 0.01)\n {\n gl_FragColor.rgb = gl_FragColor.rgb * mix(lineColor, vec3(1.0), edgeFactor(lineWidth));\n }\n}\n\n@end';}); + /** + * Alias for {@link vec2.squaredLength} + * @function + */ + vec2.sqrLen = vec2.squaredLength; + + /** + * Negates the components of a vec2 + * + * @param {vec2} out the receiving vector + * @param {vec2} a vector to negate + * @returns {vec2} out + */ + vec2.negate = function(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + return out; + }; -define('qtek/shader/source/phong.essl',[],function () { return '\n// http://en.wikipedia.org/wiki/Blinn%E2%80%93Phong_shading_model\n\n@export buildin.phong.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\n\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 normal : NORMAL;\nattribute vec4 tangent : TANGENT;\n\n#ifdef VERTEX_COLOR\nattribute vec4 color : COLOR;\n#endif\n\nattribute vec3 barycentric;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\n\n#ifdef NORMALMAP_ENABLED\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\n#ifdef VERTEX_COLOR\nvarying vec4 v_Color;\n#endif\n\nvoid main()\n{\n \n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n // Upper 3x3 of skinMatrix is orthogonal\n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n skinnedTangent = (skinMatrixWS * vec4(tangent.xyz, 0.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n v_Barycentric = barycentric;\n\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\n \n #ifdef NORMALMAP_ENABLED\n v_Tangent = normalize((worldInverseTranspose * vec4(skinnedTangent, 0.0)).xyz);\n v_Bitangent = normalize(cross(v_Normal, v_Tangent) * tangent.w);\n #endif\n\n #ifdef VERTEX_COLOR\n v_Color = color;\n #endif\n}\n\n@end\n\n\n@export buildin.phong.fragment\n\nuniform mat4 viewInverse : VIEWINVERSE;\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\n\n#ifdef NORMALMAP_ENABLED\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\nuniform sampler2D diffuseMap;\nuniform sampler2D normalMap;\nuniform sampler2D specularMap;\nuniform samplerCube environmentMap;\n\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\n\nuniform float shininess : 30;\n\nuniform vec3 specularColor : [1.0, 1.0, 1.0];\nuniform vec3 emission : [0.0, 0.0, 0.0];\n\nuniform float reflectivity : 0.5;\n\n// Uniforms for wireframe\nuniform float lineWidth : 0.0;\nuniform vec3 lineColor : [0.0, 0.0, 0.0];\nvarying vec3 v_Barycentric;\n\n#ifdef AMBIENT_LIGHT_NUMBER\n@import buildin.header.ambient_light\n#endif\n#ifdef POINT_LIGHT_NUMBER\n@import buildin.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_NUMBER\n@import buildin.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_NUMBER\n@import buildin.header.spot_light\n#endif\n\n#extension GL_OES_standard_derivatives : enable\n// Import util functions and uniforms needed\n@import buildin.util.calculate_attenuation\n\n@import buildin.util.edge_factor\n\n@import buildin.plugin.compute_shadow_map\n\nvoid main()\n{\n #ifdef RENDER_TEXCOORD\n gl_FragColor = vec4(v_Texcoord, 1.0, 1.0);\n return;\n #endif\n\n vec4 finalColor = vec4(color, alpha);\n\n vec3 eyePos = viewInverse[3].xyz;\n vec3 viewDirection = normalize(eyePos - v_WorldPosition);\n\n #ifdef DIFFUSEMAP_ENABLED\n vec4 tex = texture2D(diffuseMap, v_Texcoord);\n #ifdef SRGB_DECODE\n tex.rgb = pow(tex.rgb, vec3(2.2));\n #endif\n finalColor.rgb *= tex.rgb;\n #ifdef DIFFUSEMAP_ALPHA_ALPHA\n finalColor.a *= tex.a;\n #endif\n #endif\n\n vec3 spec = specularColor;\n #ifdef SPECULARMAP_ENABLED\n spec *= texture2D(specularMap, v_Texcoord).rgb;\n #endif\n\n vec3 normal = v_Normal;\n #ifdef NORMALMAP_ENABLED\n normal = texture2D(normalMap, v_Texcoord).xyz * 2.0 - 1.0;\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\n normal = normalize(tbn * normal);\n #endif\n\n #ifdef RENDER_NORMAL\n gl_FragColor = vec4(normal, 1.0);\n return;\n #endif\n\n // Diffuse part of all lights\n vec3 diffuseTerm = vec3(0.0, 0.0, 0.0);\n // Specular part of all lights\n vec3 specularTerm = vec3(0.0, 0.0, 0.0);\n \n #ifdef AMBIENT_LIGHT_NUMBER\n for(int i = 0; i < AMBIENT_LIGHT_NUMBER; i++)\n {\n diffuseTerm += ambientLightColor[i];\n }\n #endif\n #ifdef POINT_LIGHT_NUMBER\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[POINT_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfPointLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < POINT_LIGHT_NUMBER; i++)\n {\n vec3 lightPosition = pointLightPosition[i];\n vec3 lightColor = pointLightColor[i];\n float range = pointLightRange[i];\n\n vec3 lightDirection = lightPosition - v_WorldPosition;\n\n // Calculate point light attenuation\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range); \n\n // Normalize vectors\n lightDirection /= dist;\n vec3 halfVector = normalize(lightDirection + viewDirection);\n\n float ndh = dot(normal, halfVector);\n ndh = clamp(ndh, 0.0, 1.0);\n\n float ndl = dot(normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lightColor * ndl * attenuation * shadowContrib;\n\n diffuseTerm += li;\n if (shininess > 0.0)\n {\n specularTerm += li * pow(ndh, shininess);\n }\n\n }\n #endif\n\n #ifdef DIRECTIONAL_LIGHT_NUMBER\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[DIRECTIONAL_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfDirectionalLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < DIRECTIONAL_LIGHT_NUMBER; i++)\n {\n\n vec3 lightDirection = -normalize(directionalLightDirection[i]);\n vec3 lightColor = directionalLightColor[i];\n\n vec3 halfVector = normalize(lightDirection + viewDirection);\n\n float ndh = dot(normal, halfVector);\n ndh = clamp(ndh, 0.0, 1.0);\n\n float ndl = dot(normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lightColor * ndl * shadowContrib;\n\n diffuseTerm += li;\n if (shininess > 0.0)\n {\n specularTerm += li * pow(ndh, shininess);\n }\n }\n #endif\n\n #ifdef SPOT_LIGHT_NUMBER\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[SPOT_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfSpotLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < SPOT_LIGHT_NUMBER; i++)\n {\n vec3 lightPosition = spotLightPosition[i];\n vec3 spotLightDirection = -normalize(spotLightDirection[i]);\n vec3 lightColor = spotLightColor[i];\n float range = spotLightRange[i];\n float a = spotLightUmbraAngleCosine[i];\n float b = spotLightPenumbraAngleCosine[i];\n float falloffFactor = spotLightFalloffFactor[i];\n\n vec3 lightDirection = lightPosition - v_WorldPosition;\n // Calculate attenuation\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range); \n\n // Normalize light direction\n lightDirection /= dist;\n // Calculate spot light fall off\n float c = dot(spotLightDirection, lightDirection);\n\n float falloff;\n // Fomular from real-time-rendering\n falloff = clamp((c - a) /( b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n\n vec3 halfVector = normalize(lightDirection + viewDirection);\n\n float ndh = dot(normal, halfVector);\n ndh = clamp(ndh, 0.0, 1.0);\n\n float ndl = dot(normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n if (shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lightColor * ndl * attenuation * (1.0-falloff) * shadowContrib;\n\n diffuseTerm += li;\n if (shininess > 0.0)\n {\n specularTerm += li * pow(ndh, shininess);\n }\n }\n #endif\n\n finalColor.rgb *= diffuseTerm;\n finalColor.rgb += specularTerm * spec;\n finalColor.rgb += emission;\n\n #ifdef ENVIRONMENTMAP_ENABLED\n vec3 envTex = textureCube(environmentMap, reflect(-viewDirection, normal)).xyz;\n finalColor.rgb = finalColor.rgb + envTex * reflectivity;\n #endif\n\n if(lineWidth > 0.01)\n {\n finalColor.rgb = finalColor.rgb * mix(lineColor, vec3(1.0), edgeFactor(lineWidth));\n }\n\n #ifdef GAMMA_ENCODE\n finalColor.rgb = pow(finalColor.rgb, vec3(1 / 2.2));\n #endif\n\n gl_FragColor = finalColor;\n}\n\n@end';}); + /** + * Returns the inverse of the components of a vec2 + * + * @param {vec2} out the receiving vector + * @param {vec2} a vector to invert + * @returns {vec2} out + */ + vec2.inverse = function(out, a) { + out[0] = 1.0 / a[0]; + out[1] = 1.0 / a[1]; + return out; + }; -define('qtek/shader/source/standard.essl',[],function () { return '\n// http://blog.selfshadow.com/publications/s2013-shading-course/\n\n@export buildin.standard.vertex\n\n@import buildin.phong.vertex\n\n@end\n\n\n@export buildin.standard.fragment\n\n#define PI 3.14159265358979\n\nuniform mat4 viewInverse : VIEWINVERSE;\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\n\n#ifdef NORMALMAP_ENABLED\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\nuniform sampler2D diffuseMap;\nuniform sampler2D normalMap;\nuniform sampler2D specularMap;\nuniform samplerCube environmentMap;\n\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\n\nuniform float glossiness : 0.5;\n\nuniform vec3 specularColor : [0.1, 0.1, 0.1];\nuniform vec3 emission : [0.0, 0.0, 0.0];\n\n// Uniforms for wireframe\nuniform float lineWidth : 0.0;\nuniform vec3 lineColor : [0.0, 0.0, 0.0];\nvarying vec3 v_Barycentric;\n\n#ifdef AMBIENT_LIGHT_NUMBER\n@import buildin.header.ambient_light\n#endif\n#ifdef POINT_LIGHT_NUMBER\n@import buildin.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_NUMBER\n@import buildin.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_NUMBER\n@import buildin.header.spot_light\n#endif\n\n#extension GL_OES_standard_derivatives : enable\n\n// Import util functions and uniforms needed\n@import buildin.util.calculate_attenuation\n\n@import buildin.util.edge_factor\n\n@import buildin.plugin.compute_shadow_map\n\n\nfloat G_Smith(float glossiness, float ndv, float ndl)\n{\n // float k = (roughness+1.0) * (roughness+1.0) * 0.125;\n float roughness = 1.0 - glossiness;\n float k = roughness * roughness / 2.0;\n float G1V = ndv / (ndv * (1.0 - k) + k);\n float G1L = ndl / (ndl * (1.0 - k) + k);\n return G1L * G1V;\n}\n// Fresnel\nvec3 F_Schlick(float ndv, vec3 spec) {\n return spec + (1.0 - spec) * pow(1.0 - ndv, 5.0);\n}\n\nfloat D_Phong(float g, float ndh) {\n // from black ops 2\n float a = pow(8192.0, g);\n return (a + 2.0) / 8.0 * pow(ndh, a);\n}\n\nfloat D_GGX(float g, float ndh) {\n float r = 1.0 - g;\n float a = r * r;\n float tmp = ndh * ndh * (a - 1.0) + 1.0;\n return a / (PI * tmp * tmp);\n}\n\nvoid main()\n{\n #ifdef RENDER_TEXCOORD\n gl_FragColor = vec4(v_Texcoord, 1.0, 1.0);\n return;\n #endif\n\n vec4 finalColor = vec4(color, alpha);\n\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = normalize(eyePos - v_WorldPosition);\n float g = glossiness;\n\n #ifdef DIFFUSEMAP_ENABLED\n vec4 tex = texture2D(diffuseMap, v_Texcoord);\n #ifdef SRGB_DECODE\n tex.rgb = pow(tex.rgb, vec3(2.2));\n #endif\n finalColor.rgb *= tex.rgb;\n #ifdef DIFFUSEMAP_ALPHA_ALPHA\n finalColor.a *= tex.a;\n #endif\n #ifdef DIFFUSEMAP_ALPHA_GLOSS\n g *= tex.a;\n #endif\n #endif\n\n vec3 spec = specularColor;\n #ifdef SPECULARMAP_ENABLED\n spec *= texture2D(specularMap, v_Texcoord).rgb;\n #endif\n\n vec3 N = v_Normal;\n #ifdef NORMALMAP_ENABLED\n N = texture2D(normalMap, v_Texcoord).xyz * 2.0 - 1.0;\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\n N = normalize(tbn * N);\n #endif\n\n #ifdef RENDER_NORMAL\n gl_FragColor = vec4(N, 1.0);\n return;\n #endif\n\n #ifdef RENDER_GLOSSINESS\n gl_FragColor = vec4(vec3(g), 1.0);\n return;\n #endif\n\n // Diffuse part of all lights\n vec3 diffuseTerm = vec3(0.0, 0.0, 0.0);\n // Specular part of all lights\n vec3 specularTerm = vec3(0.0, 0.0, 0.0);\n \n vec3 fresnelTerm = F_Schlick(clamp(dot(N, V), 0.0, 1.0), spec);\n \n #ifdef AMBIENT_LIGHT_NUMBER\n for(int i = 0; i < AMBIENT_LIGHT_NUMBER; i++)\n {\n // Hemisphere ambient lighting from cryengine\n diffuseTerm += ambientLightColor[i] * (clamp(N.y * 0.7, 0.0, 1.0) + 0.3);\n // diffuseTerm += ambientLightColor[i];\n }\n #endif\n #ifdef POINT_LIGHT_NUMBER\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[POINT_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfPointLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < POINT_LIGHT_NUMBER; i++)\n {\n\n vec3 lightPosition = pointLightPosition[i];\n vec3 lc = pointLightColor[i];\n float range = pointLightRange[i];\n\n vec3 L = lightPosition - v_WorldPosition;\n\n // Calculate point light attenuation\n float dist = length(L);\n float attenuation = lightAttenuation(dist, range);\n L /= dist;\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lc * ndl * attenuation * shadowContrib;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }\n #endif\n\n #ifdef DIRECTIONAL_LIGHT_NUMBER\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[DIRECTIONAL_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfDirectionalLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < DIRECTIONAL_LIGHT_NUMBER; i++)\n {\n\n vec3 L = -normalize(directionalLightDirection[i]);\n vec3 lc = directionalLightColor[i];\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lc * ndl * shadowContrib;\n\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }\n #endif\n\n #ifdef SPOT_LIGHT_NUMBER\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[SPOT_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfSpotLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < SPOT_LIGHT_NUMBER; i++)\n {\n vec3 lightPosition = spotLightPosition[i];\n vec3 spotLightDirection = -normalize(spotLightDirection[i]);\n vec3 lc = spotLightColor[i];\n float range = spotLightRange[i];\n float a = spotLightUmbraAngleCosine[i];\n float b = spotLightPenumbraAngleCosine[i];\n float falloffFactor = spotLightFalloffFactor[i];\n\n vec3 L = lightPosition - v_WorldPosition;\n // Calculate attenuation\n float dist = length(L);\n float attenuation = lightAttenuation(dist, range); \n\n // Normalize light direction\n L /= dist;\n // Calculate spot light fall off\n float c = dot(spotLightDirection, L);\n\n float falloff;\n // Fomular from real-time-rendering\n falloff = clamp((c - a) /( b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n if (shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lc * attenuation * (1.0-falloff) * shadowContrib * ndl;\n\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }\n #endif\n\n finalColor.rgb *= diffuseTerm;\n finalColor.rgb += specularTerm;\n finalColor.rgb += emission;\n\n #ifdef ENVIRONMENTMAP_ENABLED\n vec3 envTex = textureCube(environmentMap, reflect(-V, N)).xyz;;\n finalColor.rgb = finalColor.rgb + envTex * g * fresnelTerm;\n #endif\n\n if(lineWidth > 0.)\n {\n finalColor.rgb = finalColor.rgb * mix(lineColor, vec3(1.0), edgeFactor(lineWidth));\n }\n\n #ifdef GAMMA_ENCODE\n finalColor.rgb = pow(finalColor.rgb, vec3(1 / 2.2));\n #endif\n gl_FragColor = finalColor;\n}\n\n@end\n\n\n@export buildin.physical.vertex\n\n@import buildin.standard.vertex\n\n@end\n\n@export buildin.physical.fragment\n\n@import buildin.standard.fragment\n\n@end';}); + /** + * Normalize a vec2 + * + * @param {vec2} out the receiving vector + * @param {vec2} a vector to normalize + * @returns {vec2} out + */ + vec2.normalize = function(out, a) { + var x = a[0], + y = a[1]; + var len = x*x + y*y; + if (len > 0) { + //TODO: evaluate use of glm_invsqrt here? + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + } + return out; + }; -define('qtek/shader/source/wireframe.essl',[],function () { return '@export buildin.wireframe.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world : WORLD;\n\nattribute vec3 position : POSITION;\nattribute vec3 barycentric;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec3 v_Barycentric;\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n #ifdef SKINNING\n\n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0 );\n\n v_Barycentric = barycentric;\n}\n\n@end\n\n\n@export buildin.wireframe.fragment\n\nuniform vec3 color : [0.0, 0.0, 0.0];\n\nuniform float alpha : 1.0;\nuniform float lineWidth : 1.0;\n\nvarying vec3 v_Barycentric;\n\n#extension GL_OES_standard_derivatives : enable\n\n@import buildin.util.edge_factor\n\nvoid main()\n{\n\n gl_FragColor.rgb = color;\n gl_FragColor.a = ( 1.0-edgeFactor(lineWidth) ) * alpha;\n}\n\n@end';}); + /** + * Calculates the dot product of two vec2's + * + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {Number} dot product of a and b + */ + vec2.dot = function (a, b) { + return a[0] * b[0] + a[1] * b[1]; + }; -define('qtek/shader/source/skybox.essl',[],function () { return '@export buildin.skybox.vertex\n\nuniform mat4 world : WORLD;\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\nvarying vec3 v_WorldPosition;\n\nvoid main()\n{\n v_WorldPosition = (world * vec4(position, 1.0)).xyz;\n gl_Position = worldViewProjection * vec4(position, 1.0);\n}\n\n@end\n\n@export buildin.skybox.fragment\n\nuniform mat4 viewInverse : VIEWINVERSE;\nuniform samplerCube environmentMap;\n\nvarying vec3 v_WorldPosition;\n\nvoid main()\n{\n vec3 eyePos = viewInverse[3].xyz;\n vec3 viewDirection = normalize(v_WorldPosition - eyePos);\n\n vec3 tex = textureCube(environmentMap, viewDirection).xyz;\n\n #ifdef SRGB_DECODE\n tex.rgb = pow(tex.rgb, vec3(2.2));\n #endif\n \n gl_FragColor = vec4(tex, 1.0);\n}\n@end';}); + /** + * Computes the cross product of two vec2's + * Note that the cross product must by definition produce a 3D vector + * + * @param {vec3} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec3} out + */ + vec2.cross = function(out, a, b) { + var z = a[0] * b[1] - a[1] * b[0]; + out[0] = out[1] = 0; + out[2] = z; + return out; + }; -define('qtek/shader/source/shadowmap.essl',[],function () { return '\n@export buildin.sm.depth.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\n#ifdef SHADOW_TRANSPARENT \nattribute vec2 texcoord : TEXCOORD_0;\n#endif\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec4 v_ViewPosition;\n\n#ifdef SHADOW_TRANSPARENT\nvarying vec2 v_Texcoord;\n#endif\n\nvoid main(){\n \n vec3 skinnedPosition = position;\n \n #ifdef SKINNING\n\n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n\n v_ViewPosition = worldViewProjection * vec4(skinnedPosition, 1.0);\n gl_Position = v_ViewPosition;\n\n #ifdef SHADOW_TRANSPARENT\n v_Texcoord = texcoord;\n #endif\n}\n@end\n\n@export buildin.sm.depth.fragment\n\nvarying vec4 v_ViewPosition;\n\n#ifdef SHADOW_TRANSPARENT\nvarying vec2 v_Texcoord;\n#endif\n\nuniform float bias : 0.001;\nuniform float slopeScale : 1.0;\n\n#ifdef SHADOW_TRANSPARENT\nuniform sampler2D transparentMap;\n#endif\n\n#extension GL_OES_standard_derivatives : enable\n\n@import buildin.util.encode_float\n\nvoid main(){\n // Whats the difference between gl_FragCoord.z and this v_ViewPosition\n // gl_FragCoord consider the polygon offset ?\n float depth = v_ViewPosition.z / v_ViewPosition.w;\n // float depth = gl_FragCoord.z / gl_FragCoord.w;\n\n #ifdef USE_VSM\n depth = depth * 0.5 + 0.5;\n float moment1 = depth;\n float moment2 = depth * depth;\n\n // Adjusting moments using partial derivative\n float dx = dFdx(depth);\n float dy = dFdy(depth);\n moment2 += 0.25*(dx*dx+dy*dy);\n\n gl_FragColor = vec4(moment1, moment2, 0.0, 1.0);\n #else\n // Add slope scaled bias using partial derivative\n float dx = dFdx(depth);\n float dy = dFdy(depth);\n depth += sqrt(dx*dx + dy*dy) * slopeScale + bias;\n\n #ifdef SHADOW_TRANSPARENT\n if (texture2D(transparentMap, v_Texcoord).a <= 0.1) {\n // Hi-Z\n gl_FragColor = encodeFloat(0.9999);\n return;\n }\n #endif\n\n gl_FragColor = encodeFloat(depth * 0.5 + 0.5);\n #endif\n}\n@end\n\n@export buildin.sm.debug_depth\n\nuniform sampler2D depthMap;\nvarying vec2 v_Texcoord;\n\n@import buildin.util.decode_float\n\nvoid main() {\n vec4 tex = texture2D(depthMap, v_Texcoord);\n #ifdef USE_VSM\n gl_FragColor = vec4(tex.rgb, 1.0);\n #else\n float depth = decodeFloat(tex);\n gl_FragColor = vec4(depth, depth, depth, 1.0);\n #endif\n}\n\n@end\n\n\n@export buildin.sm.distance.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world : WORLD;\n\nattribute vec3 position : POSITION;\n\n#ifdef SKINNING\nattribute vec3 boneWeight;\nattribute vec4 boneIndex;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec3 v_WorldPosition;\n\nvoid main (){\n\n vec3 skinnedPosition = position;\n #ifdef SKINNING\n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition , 1.0);\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n}\n\n@end\n\n@export buildin.sm.distance.fragment\n\nuniform vec3 lightPosition;\nuniform float range : 100;\n\nvarying vec3 v_WorldPosition;\n\n@import buildin.util.encode_float\n\nvoid main(){\n float dist = distance(lightPosition, v_WorldPosition);\n #ifdef USE_VSM\n gl_FragColor = vec4(dist, dist * dist, 0.0, 0.0);\n #else\n dist = dist / range;\n gl_FragColor = encodeFloat(dist);\n #endif\n}\n@end\n\n@export buildin.plugin.compute_shadow_map\n\n#if defined(SPOT_LIGHT_SHADOWMAP_NUMBER) || defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER) || defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n\n#ifdef SPOT_LIGHT_SHADOWMAP_NUMBER\nuniform sampler2D spotLightShadowMaps[SPOT_LIGHT_SHADOWMAP_NUMBER];\nuniform mat4 spotLightMatrices[SPOT_LIGHT_SHADOWMAP_NUMBER];\n#endif\n\n#ifdef DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER\n#if defined(SHADOW_CASCADE)\nuniform sampler2D directionalLightShadowMaps[SHADOW_CASCADE];\nuniform mat4 directionalLightMatrices[SHADOW_CASCADE];\nuniform float shadowCascadeClipsNear[SHADOW_CASCADE];\nuniform float shadowCascadeClipsFar[SHADOW_CASCADE];\n#else\nuniform sampler2D directionalLightShadowMaps[DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER];\nuniform mat4 directionalLightMatrices[DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER];\n#endif\n#endif\n\n#ifdef POINT_LIGHT_SHADOWMAP_NUMBER\nuniform samplerCube pointLightShadowMaps[POINT_LIGHT_SHADOWMAP_NUMBER];\nuniform float pointLightRanges[POINT_LIGHT_SHADOWMAP_NUMBER];\n#endif\n\nuniform bool shadowEnabled : true;\n\n@import buildin.util.decode_float\n\n#if defined(DIRECTIONAL_LIGHT_NUMBER) || defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n\nfloat tapShadowMap(sampler2D map, vec2 uv, float z){\n vec4 tex = texture2D(map, uv);\n return decodeFloat(tex) * 2.0 - 1.0 < z ? 0.0 : 1.0;\n}\n\nfloat pcf(sampler2D map, vec2 uv, float z){\n\n float shadowContrib = tapShadowMap(map, uv, z);\n float offset = 1.0 / 2048.0;\n shadowContrib += tapShadowMap(map, uv+vec2(offset, 0.0), z);\n shadowContrib += tapShadowMap(map, uv+vec2(offset, offset), z);\n shadowContrib += tapShadowMap(map, uv+vec2(-offset, offset), z);\n shadowContrib += tapShadowMap(map, uv+vec2(0.0, offset), z);\n shadowContrib += tapShadowMap(map, uv+vec2(-offset, 0.0), z);\n shadowContrib += tapShadowMap(map, uv+vec2(-offset, -offset), z);\n shadowContrib += tapShadowMap(map, uv+vec2(offset, -offset), z);\n shadowContrib += tapShadowMap(map, uv+vec2(0.0, -offset), z);\n\n return shadowContrib / 9.0;\n}\nfloat chebyshevUpperBound(vec2 moments, float z){\n float p = 0.0;\n z = z * 0.5 + 0.5;\n if (z <= moments.x) {\n p = 1.0;\n }\n float variance = moments.y - moments.x * moments.x;\n // http://fabiensanglard.net/shadowmappingVSM/\n variance = max(variance, 0.0000001);\n // Compute probabilistic upper bound. \n float mD = moments.x - z;\n float pMax = variance / (variance + mD * mD);\n // Now reduce light-bleeding by removing the [0, x] tail and linearly rescaling (x, 1]\n // TODO : bleedBias parameter ?\n pMax = clamp((pMax-0.4)/(1.0-0.4), 0.0, 1.0);\n return max(p, pMax);\n}\nfloat computeShadowContrib(sampler2D map, mat4 lightVPM, vec3 position){\n \n vec4 posInLightSpace = lightVPM * vec4(v_WorldPosition, 1.0);\n posInLightSpace.xyz /= posInLightSpace.w;\n float z = posInLightSpace.z;\n // In frustum\n if(all(greaterThan(posInLightSpace.xyz, vec3(-0.99, -0.99, -1.0))) &&\n all(lessThan(posInLightSpace.xyz, vec3(0.99, 0.99, 1.0)))){\n // To texture uv\n vec2 uv = (posInLightSpace.xy+1.0) / 2.0;\n\n #ifdef USE_VSM\n vec2 moments = texture2D(map, uv).xy;\n return chebyshevUpperBound(moments, z);\n #else\n return pcf(map, uv, z);\n #endif\n }\n return 1.0;\n}\n\n#endif\n\n#ifdef POINT_LIGHT_SHADOWMAP_NUMBER\n\nfloat computeShadowOfCube(samplerCube map, vec3 direction, float range){\n vec4 shadowTex = textureCube(map, direction);\n float dist = length(direction);\n\n #ifdef USE_VSM\n vec2 moments = shadowTex.xy;\n float variance = moments.y - moments.x * moments.x;\n float mD = moments.x - dist;\n float p = variance / (variance + mD * mD);\n if(moments.x + 0.001 < dist){\n return clamp(p, 0.0, 1.0);\n }else{\n return 1.0;\n }\n #else\n if((decodeFloat(shadowTex) + 0.0002) * range < dist){\n return 0.0;\n }else{\n return 1.0;\n }\n #endif\n}\n#endif\n\n#if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n\nvoid computeShadowOfSpotLights(vec3 position, inout float shadowContribs[SPOT_LIGHT_NUMBER] ){\n for(int i = 0; i < SPOT_LIGHT_SHADOWMAP_NUMBER; i++){\n float shadowContrib = computeShadowContrib(spotLightShadowMaps[i], spotLightMatrices[i], position);\n shadowContribs[i] = shadowContrib;\n }\n // set default fallof of rest lights\n for(int i = SPOT_LIGHT_SHADOWMAP_NUMBER; i < SPOT_LIGHT_NUMBER; i++){\n shadowContribs[i] = 1.0;\n }\n}\n\n#endif\n\n\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n\n#ifdef SHADOW_CASCADE\n\nvoid computeShadowOfDirectionalLights(vec3 position, inout float shadowContribs[DIRECTIONAL_LIGHT_NUMBER]){\n // http://www.opengl.org/wiki/Compute_eye_space_from_window_space\n float depth = (2.0 * gl_FragCoord.z - gl_DepthRange.near - gl_DepthRange.far)\n / (gl_DepthRange.far - gl_DepthRange.near);\n\n // Pixels not in light box are lighted\n // TODO\n shadowContribs[0] = 1.0;\n\n for (int i = 0; i < SHADOW_CASCADE; i++) {\n if (\n depth >= shadowCascadeClipsNear[i] &&\n depth <= shadowCascadeClipsFar[i]\n ) {\n float shadowContrib = computeShadowContrib(directionalLightShadowMaps[i], directionalLightMatrices[i], position);\n // TODO Will get a sampler needs to be be uniform error in native gl\n shadowContribs[0] = shadowContrib;\n }\n }\n // set default fallof of rest lights\n for(int i = DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER; i < DIRECTIONAL_LIGHT_NUMBER; i++){\n shadowContribs[i] = 1.0;\n }\n}\n\n#else\n\nvoid computeShadowOfDirectionalLights(vec3 position, inout float shadowContribs[DIRECTIONAL_LIGHT_NUMBER]){\n for(int i = 0; i < DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER; i++){\n float shadowContrib = computeShadowContrib(directionalLightShadowMaps[i], directionalLightMatrices[i], position);\n shadowContribs[i] = shadowContrib;\n }\n // set default fallof of rest lights\n for(int i = DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER; i < DIRECTIONAL_LIGHT_NUMBER; i++){\n shadowContribs[i] = 1.0;\n }\n}\n#endif\n\n#endif\n\n\n#if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n\nvoid computeShadowOfPointLights(vec3 position, inout float shadowContribs[POINT_LIGHT_NUMBER] ){\n for(int i = 0; i < POINT_LIGHT_SHADOWMAP_NUMBER; i++){\n vec3 lightPosition = pointLightPosition[i];\n vec3 direction = position - lightPosition;\n shadowContribs[i] = computeShadowOfCube(pointLightShadowMaps[i], direction, pointLightRanges[i]);\n }\n for(int i = POINT_LIGHT_SHADOWMAP_NUMBER; i < POINT_LIGHT_NUMBER; i++){\n shadowContribs[i] = 1.0;\n }\n}\n\n#endif\n\n#endif\n\n@end';}); + /** + * Performs a linear interpolation between two vec2's + * + * @param {vec2} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {vec2} out + */ + vec2.lerp = function (out, a, b, t) { + var ax = a[0], + ay = a[1]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + return out; + }; -define('qtek/shader/source/compositor/coloradjust.essl',[],function () { return '@export buildin.compositor.coloradjust\n\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float brightness : 0.0;\nuniform float contrast : 1.0;\nuniform float exposure : 0.0;\nuniform float gamma : 1.0;\nuniform float saturation : 1.0;\n\n// Values from "Graphics Shaders: Theory and Practice" by Bailey and Cunningham\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord);\n\n // brightness\n vec3 color = clamp(tex.rgb + vec3(brightness), 0.0, 1.0);\n // contrast\n color = clamp( (color-vec3(0.5))*contrast+vec3(0.5), 0.0, 1.0);\n // exposure\n color = clamp( color * pow(2.0, exposure), 0.0, 1.0);\n // gamma\n color = clamp( pow(color, vec3(gamma)), 0.0, 1.0);\n // saturation\n float luminance = dot( color, w );\n color = mix(vec3(luminance), color, saturation);\n \n gl_FragColor = vec4(color, tex.a);\n}\n\n@end\n\n// Seperate shader for float texture\n@export buildin.compositor.brightness\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float brightness : 0.0;\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord);\n vec3 color = tex.rgb + vec3(brightness);\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export buildin.compositor.contrast\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float contrast : 1.0;\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord);\n vec3 color = (tex.rgb-vec3(0.5))*contrast+vec3(0.5);\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export buildin.compositor.exposure\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float exposure : 0.0;\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n vec3 color = tex.rgb * pow(2.0, exposure);\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export buildin.compositor.gamma\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float gamma : 1.0;\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n vec3 color = pow(tex.rgb, vec3(gamma));\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export buildin.compositor.saturation\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float saturation : 1.0;\n\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n vec3 color = tex.rgb;\n float luminance = dot(color, w);\n color = mix(vec3(luminance), color, saturation);\n gl_FragColor = vec4(color, tex.a);\n}\n@end';}); + /** + * Generates a random vector with the given scale + * + * @param {vec2} out the receiving vector + * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned + * @returns {vec2} out + */ + vec2.random = function (out, scale) { + scale = scale || 1.0; + var r = GLMAT_RANDOM() * 2.0 * Math.PI; + out[0] = Math.cos(r) * scale; + out[1] = Math.sin(r) * scale; + return out; + }; -define('qtek/shader/source/compositor/blur.essl',[],function () { return '@export buildin.compositor.gaussian_blur_h\n\nuniform sampler2D texture; // the texture with the scene you want to blur\nvarying vec2 v_Texcoord;\n \nuniform float blurSize : 2.0; \nuniform float textureWidth : 512.0;\n\nvoid main(void)\n{\n vec4 sum = vec4(0.0);\n float blurOffset = blurSize / textureWidth;\n // blur in y (vertical)\n // take nine samples, with the distance blurSize between them\n sum += texture2D(texture, vec2(max(v_Texcoord.x - 4.0*blurOffset, 0.0), v_Texcoord.y)) * 0.05;\n sum += texture2D(texture, vec2(max(v_Texcoord.x - 3.0*blurOffset, 0.0), v_Texcoord.y)) * 0.09;\n sum += texture2D(texture, vec2(max(v_Texcoord.x - 2.0*blurOffset, 0.0), v_Texcoord.y)) * 0.12;\n sum += texture2D(texture, vec2(max(v_Texcoord.x - blurOffset, 0.0), v_Texcoord.y)) * 0.15;\n sum += texture2D(texture, vec2(v_Texcoord.x, v_Texcoord.y)) * 0.18;\n sum += texture2D(texture, vec2(min(v_Texcoord.x + blurOffset, 1.0), v_Texcoord.y)) * 0.15;\n sum += texture2D(texture, vec2(min(v_Texcoord.x + 2.0*blurOffset, 1.0), v_Texcoord.y)) * 0.12;\n sum += texture2D(texture, vec2(min(v_Texcoord.x + 3.0*blurOffset, 1.0), v_Texcoord.y)) * 0.09;\n sum += texture2D(texture, vec2(min(v_Texcoord.x + 4.0*blurOffset, 1.0), v_Texcoord.y)) * 0.05;\n \n gl_FragColor = sum;\n}\n\n@end\n\n@export buildin.compositor.gaussian_blur_v\n\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\n \nuniform float blurSize : 2.0;\nuniform float textureHeight : 512.0;\n \nvoid main(void)\n{\n vec4 sum = vec4(0.0);\n float blurOffset = blurSize / textureHeight;\n // blur in y (vertical)\n // take nine samples, with the distance blurSize between them\n sum += texture2D(texture, vec2(v_Texcoord.x, max(v_Texcoord.y - 4.0*blurOffset, 0.0))) * 0.05;\n sum += texture2D(texture, vec2(v_Texcoord.x, max(v_Texcoord.y - 3.0*blurOffset, 0.0))) * 0.09;\n sum += texture2D(texture, vec2(v_Texcoord.x, max(v_Texcoord.y - 2.0*blurOffset, 0.0))) * 0.12;\n sum += texture2D(texture, vec2(v_Texcoord.x, max(v_Texcoord.y - blurOffset, 0.0))) * 0.15;\n sum += texture2D(texture, vec2(v_Texcoord.x, v_Texcoord.y)) * 0.18;\n sum += texture2D(texture, vec2(v_Texcoord.x, min(v_Texcoord.y + blurOffset, 1.0))) * 0.15;\n sum += texture2D(texture, vec2(v_Texcoord.x, min(v_Texcoord.y + 2.0*blurOffset, 1.0))) * 0.12;\n sum += texture2D(texture, vec2(v_Texcoord.x, min(v_Texcoord.y + 3.0*blurOffset, 1.0))) * 0.09;\n sum += texture2D(texture, vec2(v_Texcoord.x, min(v_Texcoord.y + 4.0*blurOffset, 1.0))) * 0.05;\n \n gl_FragColor = sum;\n}\n\n@end\n\n@export buildin.compositor.box_blur\n\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 3.0;\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n\n vec4 tex = texture2D(texture, v_Texcoord);\n vec2 offset = blurSize / textureSize;\n\n tex += texture2D(texture, v_Texcoord + vec2(offset.x, 0.0) );\n tex += texture2D(texture, v_Texcoord + vec2(offset.x, offset.y) );\n tex += texture2D(texture, v_Texcoord + vec2(-offset.x, offset.y) );\n tex += texture2D(texture, v_Texcoord + vec2(0.0, offset.y) );\n tex += texture2D(texture, v_Texcoord + vec2(-offset.x, 0.0) );\n tex += texture2D(texture, v_Texcoord + vec2(-offset.x, -offset.y) );\n tex += texture2D(texture, v_Texcoord + vec2(offset.x, -offset.y) );\n tex += texture2D(texture, v_Texcoord + vec2(0.0, -offset.y) );\n\n tex /= 9.0;\n\n gl_FragColor = tex;\n}\n\n@end\n\n// http://www.slideshare.net/DICEStudio/five-rendering-ideas-from-battlefield-3-need-for-speed-the-run\n@export buildin.compositor.hexagonal_blur_mrt_1\n\n// MRT in chrome\n// https://www.khronos.org/registry/webgl/sdk/tests/conformance/extensions/webgl-draw-buffers.html\n#extension GL_EXT_draw_buffers : require\n\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 2.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n vec2 offset = blurSize / textureSize;\n\n vec4 color = vec4(0.0);\n // Top\n for(int i = 0; i < 10; i++){\n color += 1.0/10.0 * texture2D(texture, v_Texcoord + vec2(0.0, offset.y * float(i)) );\n }\n gl_FragData[0] = color;\n vec4 color2 = vec4(0.0);\n // Down left\n for(int i = 0; i < 10; i++){\n color2 += 1.0/10.0 * texture2D(texture, v_Texcoord - vec2(offset.x * float(i), offset.y * float(i)) );\n }\n gl_FragData[1] = (color + color2) / 2.0;\n}\n\n@end\n\n@export buildin.compositor.hexagonal_blur_mrt_2\n\nuniform sampler2D texture0;\nuniform sampler2D texture1;\n\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 2.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n vec2 offset = blurSize / textureSize;\n\n vec4 color1 = vec4(0.0);\n // Down left\n for(int i = 0; i < 10; i++){\n color1 += 1.0/10.0 * texture2D(texture0, v_Texcoord - vec2(offset.x * float(i), offset.y * float(i)) );\n }\n vec4 color2 = vec4(0.0);\n // Down right\n for(int i = 0; i < 10; i++){\n color2 += 1.0/10.0 * texture2D(texture1, v_Texcoord + vec2(offset.x * float(i), -offset.y * float(i)) );\n }\n\n gl_FragColor = (color1 + color2) / 2.0;\n}\n\n@end\n\n\n@export buildin.compositor.hexagonal_blur_1\n\n#define KERNEL_SIZE 10\n\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 1.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n vec2 offset = blurSize / textureSize;\n\n vec4 color = vec4(0.0);\n float fKernelSize = float(KERNEL_SIZE);\n // Top\n for(int i = 0; i < KERNEL_SIZE; i++){\n color += 1.0 / fKernelSize * texture2D(texture, v_Texcoord + vec2(0.0, offset.y * float(i)) );\n }\n gl_FragColor = color;\n}\n\n@end\n\n@export buildin.compositor.hexagonal_blur_2\n\n#define KERNEL_SIZE 10\n\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 1.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n vec2 offset = blurSize / textureSize;\n offset.y /= 2.0;\n\n vec4 color = vec4(0.0);\n float fKernelSize = float(KERNEL_SIZE);\n // Down left\n for(int i = 0; i < KERNEL_SIZE; i++){\n color += 1.0/fKernelSize * texture2D(texture, v_Texcoord - vec2(offset.x * float(i), offset.y * float(i)) );\n }\n gl_FragColor = color;\n}\n@end\n\n@export buildin.compositor.hexagonal_blur_3\n\n#define KERNEL_SIZE 10\n\nuniform sampler2D texture1;\nuniform sampler2D texture2;\n\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 1.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n vec2 offset = blurSize / textureSize;\n offset.y /= 2.0;\n\n vec4 color1 = vec4(0.0);\n float fKernelSize = float(KERNEL_SIZE);\n // Down left\n for(int i = 0; i < KERNEL_SIZE; i++){\n color1 += 1.0/fKernelSize * texture2D(texture1, v_Texcoord - vec2(offset.x * float(i), offset.y * float(i)) );\n }\n vec4 color2 = vec4(0.0);\n // Down right\n for(int i = 0; i < KERNEL_SIZE; i++){\n color2 += 1.0/fKernelSize * texture2D(texture1, v_Texcoord + vec2(offset.x * float(i), -offset.y * float(i)) );\n }\n\n vec4 color3 = vec4(0.0);\n // Down right\n for(int i = 0; i < KERNEL_SIZE; i++){\n color3 += 1.0/fKernelSize * texture2D(texture2, v_Texcoord + vec2(offset.x * float(i), -offset.y * float(i)) );\n }\n\n gl_FragColor = (color1 + color2 + color3) / 3.0;\n}\n\n@end';}); + /** + * Transforms the vec2 with a mat2 + * + * @param {vec2} out the receiving vector + * @param {vec2} a the vector to transform + * @param {mat2} m matrix to transform with + * @returns {vec2} out + */ + vec2.transformMat2 = function(out, a, m) { + var x = a[0], + y = a[1]; + out[0] = m[0] * x + m[2] * y; + out[1] = m[1] * x + m[3] * y; + return out; + }; -define('qtek/shader/source/compositor/lum.essl',[],function () { return '\n@export buildin.compositor.lum\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\n\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord );\n float luminance = dot(tex.rgb, w);\n\n gl_FragColor = vec4(vec3(luminance), 1.0);\n}\n\n@end';}); + /** + * Transforms the vec2 with a mat2d + * + * @param {vec2} out the receiving vector + * @param {vec2} a the vector to transform + * @param {mat2d} m matrix to transform with + * @returns {vec2} out + */ + vec2.transformMat2d = function(out, a, m) { + var x = a[0], + y = a[1]; + out[0] = m[0] * x + m[2] * y + m[4]; + out[1] = m[1] * x + m[3] * y + m[5]; + return out; + }; -define('qtek/shader/source/compositor/lut.essl',[],function () { return '\n// https://github.com/BradLarson/GPUImage?source=c\n@export buildin.compositor.lut\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\nuniform sampler2D lookup;\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n\n float blueColor = tex.b * 63.0;\n \n vec2 quad1;\n quad1.y = floor(floor(blueColor) / 8.0);\n quad1.x = floor(blueColor) - (quad1.y * 8.0);\n \n vec2 quad2;\n quad2.y = floor(ceil(blueColor) / 8.0);\n quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n \n vec2 texPos1;\n texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.r);\n texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.g);\n \n vec2 texPos2;\n texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.r);\n texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.g);\n \n vec4 newColor1 = texture2D(lookup, texPos1);\n vec4 newColor2 = texture2D(lookup, texPos2);\n \n vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n gl_FragColor = vec4(newColor.rgb, tex.w);\n}\n\n@end';}); + /** + * Transforms the vec2 with a mat3 + * 3rd vector component is implicitly '1' + * + * @param {vec2} out the receiving vector + * @param {vec2} a the vector to transform + * @param {mat3} m matrix to transform with + * @returns {vec2} out + */ + vec2.transformMat3 = function(out, a, m) { + var x = a[0], + y = a[1]; + out[0] = m[0] * x + m[3] * y + m[6]; + out[1] = m[1] * x + m[4] * y + m[7]; + return out; + }; -define('qtek/shader/source/compositor/output.essl',[],function () { return '@export buildin.compositor.output\n\n#define OUTPUT_ALPHA;\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n\n gl_FragColor.rgb = tex.rgb;\n\n #ifdef OUTPUT_ALPHA\n gl_FragColor.a = tex.a;\n #else\n gl_FragColor.a = 1.0;\n #endif\n\n}\n\n@end';}); + /** + * Transforms the vec2 with a mat4 + * 3rd vector component is implicitly '0' + * 4th vector component is implicitly '1' + * + * @param {vec2} out the receiving vector + * @param {vec2} a the vector to transform + * @param {mat4} m matrix to transform with + * @returns {vec2} out + */ + vec2.transformMat4 = function(out, a, m) { + var x = a[0], + y = a[1]; + out[0] = m[0] * x + m[4] * y + m[12]; + out[1] = m[1] * x + m[5] * y + m[13]; + return out; + }; -define('qtek/shader/source/compositor/hdr.essl',[],function () { return '// HDR Pipeline\n@export buildin.compositor.hdr.bright\n\nuniform sampler2D texture;\nuniform float threshold : 1;\nuniform float scale : 1.0;\n\nvarying vec2 v_Texcoord;\n\nconst vec3 lumWeight = vec3(0.2125, 0.7154, 0.0721);\n\n@import buildin.util.rgbm_decode\n@import buildin.util.rgbm_encode\n\nvoid main()\n{\n #ifdef TEXTURE_ENABLED\n #ifdef RGBM_DECODE\n vec3 tex = RGBMDecode(texture2D(texture, v_Texcoord));\n #else\n vec3 tex = texture2D(texture, v_Texcoord).rgb;\n #endif\n #else\n vec3 tex = vec3(0.0);\n #endif\n\n float lum = dot(tex, lumWeight);\n if (lum > threshold)\n {\n gl_FragColor.rgb = tex * scale;\n }\n else\n {\n gl_FragColor.rgb = vec3(0.0);\n }\n gl_FragColor.a = 1.0;\n\n #ifdef RGBM_ENCODE\n gl_FragColor.rgba = RGBMEncode(gl_FragColor.rgb);\n #endif\n}\n@end\n\n@export buildin.compositor.hdr.log_lum\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\n\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n float luminance = dot(tex.rgb, w);\n luminance = log(luminance + 0.001);\n\n gl_FragColor = vec4(vec3(luminance), 1.0);\n}\n\n@end\n\n@export buildin.compositor.hdr.lum_adaption\nvarying vec2 v_Texcoord;\n\nuniform sampler2D adaptedLum;\nuniform sampler2D currentLum;\n\nuniform float frameTime : 0.02;\n\nvoid main()\n{\n float fAdaptedLum = texture2D(adaptedLum, vec2(0.5, 0.5)).r;\n float fCurrentLum = exp(texture2D(currentLum, vec2(0.5, 0.5)).r);\n\n fAdaptedLum += (fCurrentLum - fAdaptedLum) * (1.0 - pow(0.98, 30.0 * frameTime));\n gl_FragColor.rgb = vec3(fAdaptedLum);\n gl_FragColor.a = 1.0;\n}\n@end\n\n// Tone mapping with gamma correction\n// http://filmicgames.com/archives/75\n@export buildin.compositor.hdr.tonemapping\n\nuniform sampler2D texture;\nuniform sampler2D bloom;\nuniform sampler2D lensflare;\nuniform sampler2D lum;\n\nuniform float exposure : 1.0;\n\nvarying vec2 v_Texcoord;\n\nconst float A = 0.22; // Shoulder Strength\nconst float B = 0.30; // Linear Strength\nconst float C = 0.10; // Linear Angle\nconst float D = 0.20; // Toe Strength\nconst float E = 0.01; // Toe Numerator\nconst float F = 0.30; // Toe Denominator\nconst vec3 whiteScale = vec3(11.2);\n\nvec3 uncharted2ToneMap(vec3 x)\n{\n return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;\n}\n\nvec3 filmicToneMap(vec3 color)\n{\n vec3 x = max(vec3(0.0), color - 0.004);\n return (x*(6.2*x+0.5))/(x*(6.2*x+1.7)+0.06);\n}\n\nfloat eyeAdaption(float fLum)\n{\n return mix(0.2, fLum, 0.5);\n}\n\nvoid main()\n{\n vec3 tex = vec3(0.0);\n float a = 1.0;\n #ifdef TEXTURE_ENABLED\n vec4 res = texture2D(texture, v_Texcoord);\n a = res.a;\n tex = res.rgb;\n #endif\n\n #ifdef BLOOM_ENABLED\n tex += texture2D(bloom, v_Texcoord).rgb * 0.25;\n #endif\n\n #ifdef LENSFLARE_ENABLED\n tex += texture2D(lensflare, v_Texcoord).rgb;\n #endif\n\n // Adjust exposure\n // From KlayGE\n #ifdef LUM_ENABLED\n float fLum = texture2D(lum, vec2(0.5, 0.5)).r;\n float adaptedLumDest = 3.0 / (max(0.1, 1.0 + 10.0*eyeAdaption(fLum)));\n float exposureBias = adaptedLumDest * exposure;\n #else\n float exposureBias = exposure;\n #endif\n tex *= exposureBias;\n\n // Do tone mapping\n vec3 color = uncharted2ToneMap(tex) / uncharted2ToneMap(whiteScale);\n color = pow(color, vec3(1.0/2.2));\n // vec3 color = filmicToneMap(tex);\n\n #ifdef RGBM_ENCODE\n gl_FragColor.rgba = RGBMEncode(color);\n #else\n gl_FragColor = vec4(color, a);\n #endif\n}\n\n@end';}); + /** + * Perform some operation over an array of vec2s. + * + * @param {Array} a the array of vectors to iterate over + * @param {Number} stride Number of elements between the start of each vec2. If 0 assumes tightly packed + * @param {Number} offset Number of elements to skip at the beginning of the array + * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array + * @param {Function} fn Function to call for each vector in the array + * @param {Object} [arg] additional argument to pass to fn + * @returns {Array} a + * @function + */ + vec2.forEach = (function() { + var vec = vec2.create(); + + return function(a, stride, offset, count, fn, arg) { + var i, l; + if(!stride) { + stride = 2; + } + + if(!offset) { + offset = 0; + } + + if(count) { + l = Math.min((count * stride) + offset, a.length); + } else { + l = a.length; + } + + for(i = offset; i < l; i += stride) { + vec[0] = a[i]; vec[1] = a[i+1]; + fn(vec, vec, arg); + a[i] = vec[0]; a[i+1] = vec[1]; + } + + return a; + }; + })(); + + /** + * Returns a string representation of a vector + * + * @param {vec2} vec vector to represent as a string + * @returns {String} string representation of the vector + */ + vec2.str = function (a) { + return 'vec2(' + a[0] + ', ' + a[1] + ')'; + }; -define('qtek/shader/source/compositor/lensflare.essl',[],function () { return '// john-chapman-graphics.blogspot.co.uk/2013/02/pseudo-lens-flare.html\n@export buildin.compositor.lensflare\n\n#define SAMPLE_NUMBER 8\n\nuniform sampler2D texture;\nuniform sampler2D lensColor;\n\nuniform vec2 textureSize : [512, 512];\n\nuniform float dispersal : 0.3;\nuniform float haloWidth : 0.4;\nuniform float distortion : 1.0;\n\nvarying vec2 v_Texcoord;\n\nvec4 textureDistorted(\n in vec2 texcoord,\n in vec2 direction,\n in vec3 distortion\n) {\n return vec4(\n texture2D(texture, texcoord + direction * distortion.r).r,\n texture2D(texture, texcoord + direction * distortion.g).g,\n texture2D(texture, texcoord + direction * distortion.b).b,\n 1.0\n );\n}\n\nvoid main()\n{\n vec2 texcoord = -v_Texcoord + vec2(1.0); // Flip texcoords\n vec2 textureOffset = 1.0 / textureSize;\n\n vec2 ghostVec = (vec2(0.5) - texcoord) * dispersal;\n vec2 haloVec = normalize(ghostVec) * haloWidth;\n\n vec3 distortion = vec3(-textureOffset.x * distortion, 0.0, textureOffset.x * distortion);\n //Sample ghost\n vec4 result = vec4(0.0);\n for (int i = 0; i < SAMPLE_NUMBER; i++)\n {\n vec2 offset = fract(texcoord + ghostVec * float(i));\n\n float weight = length(vec2(0.5) - offset) / length(vec2(0.5));\n weight = pow(1.0 - weight, 10.0);\n\n result += textureDistorted(offset, normalize(ghostVec), distortion) * weight;\n }\n\n result *= texture2D(lensColor, vec2(length(vec2(0.5) - texcoord)) / length(vec2(0.5)));\n //Sample halo\n float weight = length(vec2(0.5) - fract(texcoord + haloVec)) / length(vec2(0.5));\n weight = pow(1.0 - weight, 10.0);\n vec2 offset = fract(texcoord + haloVec);\n result += textureDistorted(offset, normalize(ghostVec), distortion) * weight;\n\n gl_FragColor = result;\n}\n@end';}); + if(typeof(exports) !== 'undefined') { + exports.vec2 = vec2; + } + ; + /* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + /** + * @class 3 Dimensional Vector + * @name vec3 + */ + + var vec3 = {}; + + /** + * Creates a new, empty vec3 + * + * @returns {vec3} a new 3D vector + */ + vec3.create = function() { + var out = new GLMAT_ARRAY_TYPE(3); + out[0] = 0; + out[1] = 0; + out[2] = 0; + return out; + }; -define('qtek/shader/source/compositor/blend.essl',[],function () { return '@export buildin.compositor.blend\n// Blend at most 4 textures\n#ifdef TEXTURE1_ENABLED\nuniform sampler2D texture1;\nuniform float weight1 : 1.0;\n#endif\n#ifdef TEXTURE2_ENABLED\nuniform sampler2D texture2;\nuniform float weight2 : 1.0;\n#endif\n#ifdef TEXTURE3_ENABLED\nuniform sampler2D texture3;\nuniform float weight3 : 1.0;\n#endif\n#ifdef TEXTURE4_ENABLED\nuniform sampler2D texture4;\nuniform float weight4 : 1.0;\n#endif\n\nvarying vec2 v_Texcoord;\n\nvoid main()\n{\n vec3 tex = vec3(0.0);\n #ifdef TEXTURE1_ENABLED\n tex += texture2D(texture1, v_Texcoord).rgb * weight1;\n #endif\n #ifdef TEXTURE2_ENABLED\n tex += texture2D(texture2, v_Texcoord).rgb * weight2;\n #endif\n #ifdef TEXTURE3_ENABLED\n tex += texture2D(texture3, v_Texcoord).rgb * weight3;\n #endif\n #ifdef TEXTURE4_ENABLED\n tex += texture2D(texture4, v_Texcoord).rgb * weight4;\n #endif\n\n gl_FragColor = vec4(tex, 1.0);\n}\n@end';}); + /** + * Creates a new vec3 initialized with values from an existing vector + * + * @param {vec3} a vector to clone + * @returns {vec3} a new 3D vector + */ + vec3.clone = function(a) { + var out = new GLMAT_ARRAY_TYPE(3); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + return out; + }; -define('qtek/shader/source/compositor/fxaa.essl',[],function () { return '// https://github.com/mitsuhiko/webgl-meincraft/blob/master/assets/shaders/fxaa.glsl\n@export buildin.compositor.fxaa\n\nuniform sampler2D texture;\nuniform vec2 viewportSize : [512, 512];\n\nvarying vec2 v_Texcoord;\n\n#define FXAA_REDUCE_MIN (1.0/128.0)\n#define FXAA_REDUCE_MUL (1.0/8.0)\n#define FXAA_SPAN_MAX 8.0\n\nvoid main()\n{\n vec2 resolution = 1.0 / viewportSize;\n vec3 rgbNW = texture2D( texture, ( gl_FragCoord.xy + vec2( -1.0, -1.0 ) ) * resolution ).xyz;\n vec3 rgbNE = texture2D( texture, ( gl_FragCoord.xy + vec2( 1.0, -1.0 ) ) * resolution ).xyz;\n vec3 rgbSW = texture2D( texture, ( gl_FragCoord.xy + vec2( -1.0, 1.0 ) ) * resolution ).xyz;\n vec3 rgbSE = texture2D( texture, ( gl_FragCoord.xy + vec2( 1.0, 1.0 ) ) * resolution ).xyz;\n vec4 rgbaM = texture2D( texture, gl_FragCoord.xy * resolution );\n vec3 rgbM = rgbaM.xyz;\n float opacity = rgbaM.w;\n\n vec3 luma = vec3( 0.299, 0.587, 0.114 );\n\n float lumaNW = dot( rgbNW, luma );\n float lumaNE = dot( rgbNE, luma );\n float lumaSW = dot( rgbSW, luma );\n float lumaSE = dot( rgbSE, luma );\n float lumaM = dot( rgbM, luma );\n float lumaMin = min( lumaM, min( min( lumaNW, lumaNE ), min( lumaSW, lumaSE ) ) );\n float lumaMax = max( lumaM, max( max( lumaNW, lumaNE) , max( lumaSW, lumaSE ) ) );\n\n vec2 dir;\n dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\n float dirReduce = max( ( lumaNW + lumaNE + lumaSW + lumaSE ) * ( 0.25 * FXAA_REDUCE_MUL ), FXAA_REDUCE_MIN );\n\n float rcpDirMin = 1.0 / ( min( abs( dir.x ), abs( dir.y ) ) + dirReduce );\n dir = min( vec2( FXAA_SPAN_MAX, FXAA_SPAN_MAX),\n max( vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),\n dir * rcpDirMin)) * resolution;\n\n vec3 rgbA = texture2D( texture, gl_FragCoord.xy * resolution + dir * ( 1.0 / 3.0 - 0.5 ) ).xyz;\n rgbA += texture2D( texture, gl_FragCoord.xy * resolution + dir * ( 2.0 / 3.0 - 0.5 ) ).xyz;\n rgbA *= 0.5;\n\n vec3 rgbB = texture2D( texture, gl_FragCoord.xy * resolution + dir * -0.5 ).xyz;\n rgbB += texture2D( texture, gl_FragCoord.xy * resolution + dir * 0.5 ).xyz;\n rgbB *= 0.25;\n rgbB += rgbA * 0.5;\n\n float lumaB = dot( rgbB, luma );\n\n if ( ( lumaB < lumaMin ) || ( lumaB > lumaMax ) )\n {\n\n gl_FragColor = vec4( rgbA, opacity );\n\n } else {\n\n gl_FragColor = vec4( rgbB, opacity );\n\n }\n}\n\n@end';}); + /** + * Creates a new vec3 initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @returns {vec3} a new 3D vector + */ + vec3.fromValues = function(x, y, z) { + var out = new GLMAT_ARRAY_TYPE(3); + out[0] = x; + out[1] = y; + out[2] = z; + return out; + }; -define('qtek/shader/buildin',['require','./library','../Shader','./source/basic.essl','./source/lambert.essl','./source/phong.essl','./source/standard.essl','./source/wireframe.essl','./source/skybox.essl','./source/util.essl','./source/prez.essl','./source/shadowmap.essl','./source/compositor/coloradjust.essl','./source/compositor/blur.essl','./source/compositor/lum.essl','./source/compositor/lut.essl','./source/compositor/output.essl','./source/compositor/hdr.essl','./source/compositor/lensflare.essl','./source/compositor/blend.essl','./source/compositor/fxaa.essl'],function (require) { + /** + * Copy the values from one vec3 to another + * + * @param {vec3} out the receiving vector + * @param {vec3} a the source vector + * @returns {vec3} out + */ + vec3.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + return out; + }; - var library = require('./library'); - var Shader = require('../Shader'); + /** + * Set the components of a vec3 to the given values + * + * @param {vec3} out the receiving vector + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @returns {vec3} out + */ + vec3.set = function(out, x, y, z) { + out[0] = x; + out[1] = y; + out[2] = z; + return out; + }; - // Some build in shaders - Shader['import'](require('./source/basic.essl')); - Shader['import'](require('./source/lambert.essl')); - Shader['import'](require('./source/phong.essl')); - Shader['import'](require('./source/standard.essl')); - Shader['import'](require('./source/wireframe.essl')); - Shader['import'](require('./source/skybox.essl')); - Shader['import'](require('./source/util.essl')); - Shader['import'](require('./source/prez.essl')); + /** + * Adds two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ + vec3.add = function(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + return out; + }; - Shader['import'](require('./source/shadowmap.essl')); + /** + * Subtracts vector b from vector a + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ + vec3.subtract = function(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + out[2] = a[2] - b[2]; + return out; + }; - library.template('buildin.basic', Shader.source('buildin.basic.vertex'), Shader.source('buildin.basic.fragment')); - library.template('buildin.lambert', Shader.source('buildin.lambert.vertex'), Shader.source('buildin.lambert.fragment')); - library.template('buildin.phong', Shader.source('buildin.phong.vertex'), Shader.source('buildin.phong.fragment')); - library.template('buildin.wireframe', Shader.source('buildin.wireframe.vertex'), Shader.source('buildin.wireframe.fragment')); - library.template('buildin.skybox', Shader.source('buildin.skybox.vertex'), Shader.source('buildin.skybox.fragment')); - library.template('buildin.prez', Shader.source('buildin.prez.vertex'), Shader.source('buildin.prez.fragment')); - library.template('buildin.standard', Shader.source('buildin.standard.vertex'), Shader.source('buildin.standard.fragment')); - // Compatible with previous - library.template('buildin.physical', Shader.source('buildin.physical.vertex'), Shader.source('buildin.physical.fragment')); + /** + * Alias for {@link vec3.subtract} + * @function + */ + vec3.sub = vec3.subtract; + + /** + * Multiplies two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ + vec3.multiply = function(out, a, b) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + out[2] = a[2] * b[2]; + return out; + }; - // Some build in shaders - Shader['import'](require('./source/compositor/coloradjust.essl')); - Shader['import'](require('./source/compositor/blur.essl')); - Shader['import'](require('./source/compositor/lum.essl')); - Shader['import'](require('./source/compositor/lut.essl')); - Shader['import'](require('./source/compositor/output.essl')); - Shader['import'](require('./source/compositor/hdr.essl')); - Shader['import'](require('./source/compositor/lensflare.essl')); - Shader['import'](require('./source/compositor/blend.essl')); - Shader['import'](require('./source/compositor/fxaa.essl')); + /** + * Alias for {@link vec3.multiply} + * @function + */ + vec3.mul = vec3.multiply; + + /** + * Divides two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ + vec3.divide = function(out, a, b) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + out[2] = a[2] / b[2]; + return out; + }; -}); -define('qtek/util/dds',['require','../Texture','../Texture2D','../TextureCube'],function(require) { - - - - var Texture = require('../Texture'); - var Texture2D = require('../Texture2D'); - var TextureCube = require('../TextureCube'); - - // http://msdn.microsoft.com/en-us/library/windows/desktop/bb943991(v=vs.85).aspx - // https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js - var DDS_MAGIC = 0x20534444; - - var DDSD_CAPS = 0x1; - var DDSD_HEIGHT = 0x2; - var DDSD_WIDTH = 0x4; - var DDSD_PITCH = 0x8; - var DDSD_PIXELFORMAT = 0x1000; - var DDSD_MIPMAPCOUNT = 0x20000; - var DDSD_LINEARSIZE = 0x80000; - var DDSD_DEPTH = 0x800000; - - var DDSCAPS_COMPLEX = 0x8; - var DDSCAPS_MIPMAP = 0x400000; - var DDSCAPS_TEXTURE = 0x1000; - - var DDSCAPS2_CUBEMAP = 0x200; - var DDSCAPS2_CUBEMAP_POSITIVEX = 0x400; - var DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800; - var DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000; - var DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000; - var DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000; - var DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000; - var DDSCAPS2_VOLUME = 0x200000; - - var DDPF_ALPHAPIXELS = 0x1; - var DDPF_ALPHA = 0x2; - var DDPF_FOURCC = 0x4; - var DDPF_RGB = 0x40; - var DDPF_YUV = 0x200; - var DDPF_LUMINANCE = 0x20000; - - function fourCCToInt32(value) { - return value.charCodeAt(0) + - (value.charCodeAt(1) << 8) + - (value.charCodeAt(2) << 16) + - (value.charCodeAt(3) << 24); - } - - function int32ToFourCC(value) { - return String.fromCharCode( - value & 0xff, - (value >> 8) & 0xff, - (value >> 16) & 0xff, - (value >> 24) & 0xff - ); - } - - var headerLengthInt = 31; // The header length in 32 bit ints - - var FOURCC_DXT1 = fourCCToInt32('DXT1'); - var FOURCC_DXT3 = fourCCToInt32('DXT3'); - var FOURCC_DXT5 = fourCCToInt32('DXT5'); - // Offsets into the header array - var off_magic = 0; - - var off_size = 1; - var off_flags = 2; - var off_height = 3; - var off_width = 4; - - var off_mipmapCount = 7; - - var off_pfFlags = 20; - var off_pfFourCC = 21; - - var off_caps = 27; - var off_caps2 = 28; - var off_caps3 = 29; - var off_caps4 = 30; - - var ret = { - parse: function(arrayBuffer, out) { - var header = new Int32Array(arrayBuffer, 0, headerLengthInt); - if (header[off_magic] !== DDS_MAGIC) { - return null; - } - if (!header(off_pfFlags) & DDPF_FOURCC) { - return null; - } - - var fourCC = header(off_pfFourCC); - var width = header[off_width]; - var height = header[off_height]; - var isCubeMap = header[off_caps2] & DDSCAPS2_CUBEMAP; - var hasMipmap = header[off_flags] & DDSD_MIPMAPCOUNT; - var blockBytes, internalFormat; - switch(fourCC) { - case FOURCC_DXT1: - blockBytes = 8; - internalFormat = Texture.COMPRESSED_RGB_S3TC_DXT1_EXT; - break; - case FOURCC_DXT3: - blockBytes = 16; - internalFormat = Texture.COMPRESSED_RGBA_S3TC_DXT3_EXT; - break; - case FOURCC_DXT5: - blockBytes = 16; - internalFormat = Texture.COMPRESSED_RGBA_S3TC_DXT5_EXT; - break; - default: - return null; - } - var dataOffset = header[off_size] + 4; - // TODO: Suppose all face are existed - var faceNumber = isCubeMap ? 6 : 1; - var mipmapCount = 1; - if (hasMipmap) { - mipmapCount = Math.max(1, header[off_mipmapCount]); - } - - var textures = []; - for (var f = 0; f < faceNumber; f++) { - var _width = width; - var _height = height; - textures[f] = new Texture2D({ - width : _width, - height : _height, - format : internalFormat - }); - var mipmaps = []; - for (var i = 0; i < mipmapCount; i++) { - var dataLength = Math.max(4, _width) / 4 * Math.max(4, _height) / 4 * blockBytes; - var byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength); - - dataOffset += dataLength; - _width *= 0.5; - _height *= 0.5; - mipmaps[i] = byteArray; - } - textures[f].pixels = mipmaps[0]; - if (hasMipmap) { - textures[f].mipmaps = mipmaps; - } - } - // TODO - // return isCubeMap ? textures : textures[0]; - if (out) { - out.width = textures[0].width; - out.height = textures[0].height; - out.format = textures[0].format; - out.pixels = textures[0].pixels; - out.mipmaps = textures[0].mipmaps; - } else { - return textures[0]; - } - } - }; - - return ret; -}); -define('qtek/util/hdr',['require','../Texture','../Texture2D'],function(require) { - - - - var Texture = require('../Texture'); - var Texture2D = require('../Texture2D'); - var toChar = String.fromCharCode; - - var MINELEN = 8; - var MAXELEN = 0x7fff; - function rgbe2float(rgbe, buffer, offset, exposure) { - if (rgbe[3] > 0) { - var f = Math.pow(2.0, rgbe[3] - 128 - 8 + exposure); - buffer[offset + 0] = rgbe[0] * f; - buffer[offset + 1] = rgbe[1] * f; - buffer[offset + 2] = rgbe[2] * f; - } else { - buffer[offset + 0] = 0; - buffer[offset + 1] = 0; - buffer[offset + 2] = 0; - } - buffer[offset + 3] = 1.0; - return buffer; - } - - function uint82string(array, offset, size) { - var str = ''; - for (var i = offset; i < size; i++) { - str += toChar(array[i]); - } - return str; - } - - function copyrgbe(s, t) { - t[0] = s[0]; - t[1] = s[1]; - t[2] = s[2]; - t[3] = s[3]; - } - - // TODO : check - function oldReadColors(scan, buffer, offset, xmax) { - var rshift = 0, x = 0, len = xmax; - while (len > 0) { - scan[x][0] = buffer[offset++]; - scan[x][1] = buffer[offset++]; - scan[x][2] = buffer[offset++]; - scan[x][3] = buffer[offset++]; - if (scan[x][0] === 1 && scan[x][1] === 1 && scan[x][2] === 1) { - // exp is count of repeated pixels - for (var i = (scan[x][3] << rshift) >>> 0; i > 0; i--) { - copyrgbe(scan[x-1], scan[x]); - x++; - len--; - } - rshift += 8; - } else { - x++; - len--; - rshift = 0; - } - } - return offset; - } - - function readColors(scan, buffer, offset, xmax) { - if ((xmax < MINELEN) | (xmax > MAXELEN)) { - return oldReadColors(scan, buffer, offset, xmax); - } - var i = buffer[offset++]; - if (i != 2) { - return oldReadColors(scan, buffer, offset - 1, xmax); - } - scan[0][1] = buffer[offset++]; - scan[0][2] = buffer[offset++]; - - i = buffer[offset++]; - if ((((scan[0][2] << 8) >>> 0) | i) >>> 0 !== xmax) { - return null; - } - for (var i = 0; i < 4; i++) { - for (var x = 0; x < xmax;) { - var code = buffer[offset++]; - if (code > 128) { - code = (code & 127) >>> 0; - var val = buffer[offset++]; - while (code--) { - scan[x++][i] = val; - } - } else { - while (code--) { - scan[x++][i] = buffer[offset++]; - } - } - } - } - return offset; - } - - - var ret = { - // http://www.graphics.cornell.edu/~bjw/rgbe.html - // Blender source - // http://radsite.lbl.gov/radiance/refer/Notes/picture_format.html - parseRGBE: function(arrayBuffer, texture, exposure) { - if (exposure === undefined) { - exposure = 0; - } - var data = new Uint8Array(arrayBuffer); - var size = data.length; - if (uint82string(data, 0, 2) !== '#?') { - return; - } - // find empty line, next line is resolution info - for (var i = 2; i < size; i++) { - if (toChar(data[i]) === '\n' && toChar(data[i+1]) === '\n') { - break; - } - } - if (i >= size) { // not found - return; - } - // find resolution info line - i += 2; - var str = ''; - for (; i < size; i++) { - var _char = toChar(data[i]); - if (_char === '\n') { - break; - } - str += _char; - } - // -Y M +X N - var tmp = str.split(' '); - var height = parseInt(tmp[1]); - var width = parseInt(tmp[3]); - if (!width || !height) { - return; - } - - // read and decode actual data - var offset = i+1; - var scanline = []; - // memzero - for (var x = 0; x < width; x++) { - scanline[x] = []; - for (var j = 0; j < 4; j++) { - scanline[x][j] = 0; - } - } - var pixels = new Float32Array(width * height * 4); - var offset2 = 0; - for (var y = 0; y < height; y++) { - var offset = readColors(scanline, data, offset, width); - if (!offset) { - return null; - } - for (var x = 0; x < width; x++) { - rgbe2float(scanline[x], pixels, offset2, exposure); - offset2 += 4; - } - } - - if (!texture) { - texture = new Texture2D(); - } - texture.width = width; - texture.height = height; - texture.pixels = pixels; - texture.type = Texture.FLOAT; - return texture; - }, - - parseRGBEFromPNG: function(png) { - - } - }; - - return ret; -}); -define('qtek/util/mesh',['require','../Geometry','../DynamicGeometry','../StaticGeometry','../Mesh','../Node','../Material','../Shader','../math/BoundingBox','../dep/glmatrix'],function(require) { - - - - var Geometry = require('../Geometry'); - var DynamicGeometry = require('../DynamicGeometry'); - var StaticGeometry = require('../StaticGeometry'); - var Mesh = require('../Mesh'); - var Node = require('../Node'); - var Material = require('../Material'); - var Shader = require('../Shader'); - var BoundingBox = require('../math/BoundingBox'); - var glMatrix = require('../dep/glmatrix'); - var mat4 = glMatrix.mat4; - var vec3 = glMatrix.vec3; - - var arraySlice = Array.prototype.slice; - - /** - * @namespace qtek.util.mesh - */ - var meshUtil = { - /** - * Merge multiple meshes to one. - * Note that these meshes must have the same material - * - * @param {Array.} meshes - * @param {boolean} applyWorldTransform - * @return qtek.Mesh - * @memberOf qtek.util.mesh - */ - merge: function(meshes, applyWorldTransform) { - - if (! meshes.length) { - return; - } - - var templateMesh = meshes[0]; - var templateGeo = templateMesh.geometry; - var material = templateMesh.material; - var isStatic = templateGeo instanceof StaticGeometry; - - var geometry = isStatic ? new StaticGeometry() : new DynamicGeometry(); - geometry.boundingBox = new BoundingBox(); - var faces = geometry.faces; - - var attributeNames = templateGeo.getEnabledAttributes(); - // TODO - if (!isStatic) { - attributeNames = Object.keys(attributeNames); - } - - for (var i = 0; i < attributeNames.length; i++) { - var name = attributeNames[i]; - var attr = templateGeo.attributes[name]; - // Extend custom attributes - if (! geometry.attributes[name]) { - geometry.attributes[name] = attr.clone(false); - } - } - - var inverseTransposeMatrix = mat4.create(); - // Initialize the array data and merge bounding box - if (isStatic) { - var nVertex = 0; - var nFace = 0; - for (var k = 0; k < meshes.length; k++) { - var currentGeo = meshes[k].geometry; - if (currentGeo.boundingBox) { - currentGeo.boundingBox.applyTransform(applyWorldTransform ? meshes[k].worldTransform : meshes[k].localTransform); - geometry.boundingBox.union(currentGeo.boundingBox); - } - nVertex += currentGeo.getVertexNumber(); - nFace += currentGeo.getFaceNumber(); - } - for (var n = 0; n < attributeNames.length; n++) { - var name = attributeNames[n]; - var attrib = geometry.attributes[name]; - attrib.init(nVertex); - } - if (nVertex >= 0xffff) { - geometry.faces = new Uint32Array(nFace * 3); - } else { - geometry.faces = new Uint16Array(nFace * 3); - } - } - - var vertexOffset = 0; - var faceOffset = 0; - var useFaces = templateGeo.isUseFace(); - - for (var mm = 0; mm < meshes.length; mm++) { - var mesh = meshes[mm]; - var currentGeo = mesh.geometry; - - var nVertex = currentGeo.getVertexNumber(); - - var matrix = applyWorldTransform ? mesh.worldTransform._array : mesh.localTransform._array; - mat4.invert(inverseTransposeMatrix, matrix); - mat4.transpose(inverseTransposeMatrix, inverseTransposeMatrix); - - for (var nn = 0; nn < attributeNames.length; nn++) { - var name = attributeNames[nn]; - var currentAttr = currentGeo.attributes[name]; - var targetAttr = geometry.attributes[name]; - // Skip the unused attributes; - if (!currentAttr.value.length) { - continue; - } - if (isStatic) { - var len = currentAttr.value.length; - var size = currentAttr.size; - var offset = vertexOffset * size; - var count = len / size; - for (var i = 0; i < len; i++) { - targetAttr.value[offset + i] = currentAttr.value[i]; - } - // Transform position, normal and tangent - if (name === 'position') { - vec3.forEach(targetAttr.value, size, offset, count, vec3.transformMat4, matrix); - } else if (name === 'normal' || name === 'tangent') { - vec3.forEach(targetAttr.value, size, offset, count, vec3.transformMat4, inverseTransposeMatrix); - } - } else { - for (var i = 0; i < nVertex; i++) { - // Transform position, normal and tangent - if (name === 'position') { - var newValue = vec3.create(); - vec3.transformMat4(newValue, currentAttr.value[i], matrix); - targetAttr.value.push(newValue); - } - else if (name === 'normal' || name === 'tangent') { - var newValue = vec3.create(); - vec3.transformMat4(newValue, currentAttr.value[i], inverseTransposeMatrix); - targetAttr.value.push(newValue); - } else { - targetAttr.value.push(currentAttr.value[i]); - } - } - } - } - - if (useFaces) { - var len = currentGeo.faces.length; - if (isStatic) { - for (var i = 0; i < len; i++) { - geometry.faces[i + faceOffset] = currentGeo.faces[i] + vertexOffset; - } - faceOffset += len; - } else { - for (var i = 0; i < len; i++) { - var newFace = []; - var face = currentGeo.faces[i]; - newFace[0] = face[0] + vertexOffset; - newFace[1] = face[1] + vertexOffset; - newFace[2] = face[2] + vertexOffset; - - faces.push(newFace); - } - } - } - - vertexOffset += nVertex; - } - - return new Mesh({ - material: material, - geometry: geometry - }); - }, - - /** - * Split mesh into sub meshes, each mesh will have maxJointNumber joints. - * @param {qtek.Mesh} mesh - * @param {number} maxJointNumber - * @param {boolean} inPlace - * @return {qtek.Node} - * - * @memberOf qtek.util.mesh - */ - splitByJoints: function(mesh, maxJointNumber, inPlace) { - var geometry = mesh.geometry; - var skeleton = mesh.skeleton; - var material = mesh.material; - var shader = material.shader; - var joints = mesh.joints; - if (!geometry || !skeleton || !joints.length) { - return; - } - if (joints.length < maxJointNumber) { - return mesh; - } - var isStatic = geometry instanceof StaticGeometry; - - var shaders = {}; - - var faces = geometry.faces; - - var faceLen = geometry.getFaceNumber(); - var rest = faceLen; - var isFaceAdded = []; - var jointValues = geometry.attributes.joint.value; - for (var i = 0; i < faceLen; i++) { - isFaceAdded[i] = false; - } - var addedJointIdxPerFace = []; - - var buckets = []; - - var getJointByIndex = function(idx) { - return joints[idx]; - }; - while(rest > 0) { - var bucketFaces = []; - var bucketJointReverseMap = []; - var bucketJoints = []; - var subJointNumber = 0; - for (var i = 0; i < joints.length; i++) { - bucketJointReverseMap[i] = -1; - } - for (var f = 0; f < faceLen; f++) { - if (isFaceAdded[f]) { - continue; - } - var canAddToBucket = true; - var addedNumber = 0; - for (var i = 0; i < 3; i++) { - - var idx = isStatic ? faces[f * 3 + i] : faces[f][i]; - - for (var j = 0; j < 4; j++) { - var jointIdx; - if (isStatic) { - jointIdx = jointValues[idx * 4 + j]; - } else { - jointIdx = jointValues[idx][j]; - } - if (jointIdx >= 0) { - if (bucketJointReverseMap[jointIdx] === -1) { - if (subJointNumber < maxJointNumber) { - bucketJointReverseMap[jointIdx] = subJointNumber; - bucketJoints[subJointNumber++] = jointIdx; - addedJointIdxPerFace[addedNumber++] = jointIdx; - } else { - canAddToBucket = false; - } - } - } - } - } - if (!canAddToBucket) { - // Reverse operation - for (var i = 0; i < addedNumber; i++) { - bucketJointReverseMap[addedJointIdxPerFace[i]] = -1; - bucketJoints.pop(); - subJointNumber--; - } - } else { - if (isStatic) { - bucketFaces.push(faces.subarray(f * 3, (f + 1) * 3)); - } else { - bucketFaces.push(faces[f]); - } - isFaceAdded[f] = true; - rest--; - } - } - buckets.push({ - faces : bucketFaces, - joints : bucketJoints.map(getJointByIndex), - jointReverseMap : bucketJointReverseMap - }); - } - - var root = new Node({ - name : mesh.name - }); - var attribNames = geometry.getEnabledAttributes(); - // TODO - if (!isStatic) { - attribNames = Object.keys(attribNames); - } - - attribNames.splice(attribNames.indexOf('joint'), 1); - // Map from old vertex index to new vertex index - var newIndices = []; - for (var b = 0; b < buckets.length; b++) { - var bucket = buckets[b]; - var jointReverseMap = bucket.jointReverseMap; - var subJointNumber = bucket.joints.length; - var subShader = shaders[subJointNumber]; - if (!subShader) { - subShader = shader.clone(); - subShader.define('vertex', 'JOINT_NUMBER', subJointNumber); - shaders[subJointNumber] = subShader; - } - var subMat = new Material({ - name : [material.name, b].join('-'), - shader : subShader, - transparent : material.transparent, - depthTest : material.depthTest, - depthMask : material.depthMask, - blend : material.blend - }); - for (var name in material.uniforms) { - var uniform = material.uniforms[name]; - subMat.set(name, uniform.value); - } - var subGeo = isStatic ? new StaticGeometry() : new DynamicGeometry(); - - var subMesh = new Mesh({ - name : [mesh.name, i].join('-'), - material : subMat, - geometry : subGeo, - skeleton : skeleton, - joints : bucket.joints.slice() - }); - var nVertex = 0; - var nVertex2 = geometry.getVertexNumber(); - for (var i = 0; i < nVertex2; i++) { - newIndices[i] = -1; - } - // Count sub geo number - for (var f = 0; f < bucket.faces.length; f++) { - var face = bucket.faces[f]; - for (var i = 0; i < 3; i++) { - var idx = face[i]; - if (newIndices[idx] === -1) { - newIndices[idx] = nVertex; - nVertex++; - } - } - } - if (isStatic) { - for (var a = 0; a < attribNames.length; a++) { - var attribName = attribNames[a]; - var subAttrib = subGeo.attributes[attribName]; - subAttrib.init(nVertex); - } - subGeo.attributes.joint.value = new Float32Array(nVertex * 4); - - if (nVertex > 0xffff) { - subGeo.faces = new Uint32Array(bucket.faces.length * 3); - } else { - subGeo.faces = new Uint16Array(bucket.faces.length * 3); - } - } - - var faceOffset = 0; - nVertex = 0; - for (var i = 0; i < nVertex2; i++) { - newIndices[i] = -1; - } - - for (var f = 0; f < bucket.faces.length; f++) { - var newFace; - if (!isStatic) { - newFace = []; - } - var face = bucket.faces[f]; - for (var i = 0; i < 3; i++) { - - var idx = face[i]; - - if (newIndices[idx] === -1) { - newIndices[idx] = nVertex; - for (var a = 0; a < attribNames.length; a++) { - var attribName = attribNames[a]; - var attrib = geometry.attributes[attribName]; - var subAttrib = subGeo.attributes[attribName]; - var size = attrib.size; - - if (isStatic) { - for (var j = 0; j < size; j++) { - subAttrib.value[nVertex * size + j] = attrib.value[idx * size + j]; - } - } else { - if (attrib.size === 1) { - subAttrib.value[nVertex] = attrib.value[idx]; - } else { - subAttrib.value[nVertex] = arraySlice.call(attrib.value[idx]); - } - } - } - if (isStatic) { - for (var j = 0; j < 4; j++) { - var jointIdx = geometry.attributes.joint.value[idx * 4 + j]; - var offset = nVertex * 4 + j; - if (jointIdx >= 0) { - subGeo.attributes.joint.value[offset] = jointReverseMap[jointIdx]; - } else { - subGeo.attributes.joint.value[offset] = -1; - } - } - } else { - var newJoints = subGeo.attributes.joint.value[nVertex] = [-1, -1, -1, -1]; - // joints - for (var j = 0; j < 4; j++) { - var jointIdx = geometry.attributes.joint.value[idx][j]; - if (jointIdx >= 0) { - newJoints[j] = jointReverseMap[jointIdx]; - } - } - } - nVertex++; - } - if (isStatic) { - subGeo.faces[faceOffset++] = newIndices[idx]; - } else { - newFace.push(newIndices[idx]); - } - } - if (!isStatic) { - subGeo.faces.push(newFace); - } - } - - root.add(subMesh); - } - var children = mesh.children(); - for (var i = 0; i < children.length; i++) { - root.add(children[i]); - } - root.position.copy(mesh.position); - root.rotation.copy(mesh.rotation); - root.scale.copy(mesh.scale); - - if (inPlace) { - if (mesh.getParent()) { - var parent = mesh.getParent(); - parent.remove(mesh); - parent.add(root); - } - } - return root; - } - }; - - return meshUtil; -}); -define('qtek/util/texture',['require','../Texture','../Texture2D','../TextureCube','../core/request','../prePass/EnvironmentMap','../plugin/Skydome','../Scene','./dds','./hdr'],function(require) { - - - - var Texture = require('../Texture'); - var Texture2D = require('../Texture2D'); - var TextureCube = require('../TextureCube'); - var request = require('../core/request'); - var EnvironmentMapPass = require('../prePass/EnvironmentMap'); - var Skydome = require('../plugin/Skydome'); - var Scene = require('../Scene'); - - var dds = require('./dds'); - var hdr = require('./hdr'); - - /** - * @namespace qtek.util.texture - */ - var textureUtil = { - /** - * @param {string|object} path - * @param {object} [option] - * @param {Function} [onsuccess] - * @param {Function} [onerror] - * @return {qtek.Texture} - * - * @memberOf qtek.util.texture - */ - loadTexture: function(path, option, onsuccess, onerror) { - var texture; - if (typeof(option) === 'function') { - onsuccess = option; - onerror = onsuccess; - option = {}; - } else { - option = option || {}; - } - if (typeof(path) === 'string') { - if (path.match(/.hdr$/)) { - texture = new Texture2D({ - width: 0, - height: 0 - }); - textureUtil._fetchTexture( - path, - function (data) { - hdr.parseRGBE(data, texture, option.exposure); - texture.dirty(); - onsuccess && onsuccess(texture); - }, - onerror - ); - return texture; - } else if (path.match(/.dds$/)) { - texture = new Texture2D({ - width: 0, - height: 0 - }); - textureUtil._fetchTexture( - path, - function (data) { - dds.parse(data, texture); - texture.dirty(); - onsuccess && onsuccess(texture); - }, - onerror - ); - } else { - texture = new Texture2D(); - texture.load(path); - texture.success(onsuccess); - texture.error(onerror); - } - } else if (typeof(path) == 'object' && typeof(path.px) !== 'undefined') { - var texture = new TextureCube(); - texture.load(path); - texture.success(onsuccess); - texture.error(onerror); - } - return texture; - }, - - /** - * Load a panorama texture and render it to a cube map - * @param {string} path - * @param {qtek.TextureCube} cubeMap - * @param {qtek.Renderer} renderer - * @param {object} [option] - * @param {Function} [onsuccess] - * @param {Function} [onerror] - * - * @memberOf qtek.util.texture - */ - loadPanorama: function(path, cubeMap, renderer, option, onsuccess, onerror) { - var self = this; - - if (typeof(option) === 'function') { - onsuccess = option; - onerror = onsuccess; - option = {}; - } else { - option = option || {}; - } - - textureUtil.loadTexture(path, option, function(texture) { - // PENDING - texture.flipY = false; - self.panoramaToCubeMap(texture, cubeMap, renderer); - texture.dispose(renderer.gl); - onsuccess && onsuccess(cubeMap); - }, onerror); - }, - - /** - * Render a panorama texture to a cube map - * @param {qtek.Texture2D} panoramaMap - * @param {qtek.TextureCube} cubeMap - * @param {qtek.Renderer} renderer - * - * @memberOf qtek.util.texture - */ - panoramaToCubeMap: function(panoramaMap, cubeMap, renderer) { - var environmentMapPass = new EnvironmentMapPass(); - var skydome = new Skydome({ - scene: new Scene() - }); - skydome.material.set('diffuseMap', panoramaMap); - environmentMapPass.texture = cubeMap; - environmentMapPass.render(renderer, skydome.scene); - environmentMapPass.texture = null; - environmentMapPass.dispose(renderer); - return cubeMap; - }, - - _fetchTexture: function(path, onsuccess, onerror) { - request.get({ - url: path, - responseType: 'arraybuffer', - onload: onsuccess, - onerror: onerror - }); - }, - - /** - * Create a chessboard texture - * @param {number} [size] - * @param {number} [unitSize] - * @param {string} [color1] - * @param {string} [color2] - * @return {qtek.Texture2D} - * - * @memberOf qtek.util.texture - */ - createChessboard: function(size, unitSize, color1, color2) { - size = size || 512; - unitSize = unitSize || 64; - color1 = color1 || 'black'; - color2 = color2 || 'white'; - - var repeat = Math.ceil(size / unitSize); - - var canvas = document.createElement('canvas'); - canvas.width = size; - canvas.height = size; - var ctx = canvas.getContext('2d'); - ctx.fillStyle = color2; - ctx.fillRect(0, 0, size, size); - - ctx.fillStyle = color1; - for (var i = 0; i < repeat; i++) { - for (var j = 0; j < repeat; j++) { - var isFill = j % 2 ? (i % 2) : (i % 2 - 1); - if (isFill) { - ctx.fillRect(i * unitSize, j * unitSize, unitSize, unitSize); - } - } - } - - var texture = new Texture2D({ - image: canvas, - anisotropic: 8 - }); - - return texture; - }, - - /** - * Create a blank pure color 1x1 texture - * @param {string} color - * @return {qtek.Texture2D} - * - * @memberOf qtek.util.texture - */ - createBlank: function(color) { - var canvas = document.createElement('canvas'); - canvas.width = 1; - canvas.height = 1; - var ctx = canvas.getContext('2d'); - ctx.fillStyle = color; - ctx.fillRect(0, 0, 1, 1); - - var texture = new Texture2D({ - image: canvas - }); - - return texture; - } - }; - - return textureUtil; -}); -/** @namespace qtek */ -/** @namespace qtek.math */ -/** @namespace qtek.animation */ -/** @namespace qtek.async */ -/** @namespace qtek.camera */ -/** @namespace qtek.compositor */ -/** @namespace qtek.core */ -/** @namespace qtek.geometry */ -/** @namespace qtek.helper */ -/** @namespace qtek.light */ -/** @namespace qtek.loader */ -/** @namespace qtek.particleSystem */ -/** @namespace qtek.plugin */ -/** @namespace qtek.prePass */ -/** @namespace qtek.shader */ -/** @namespace qtek.texture */ -/** @namespace qtek.util */ -define('qtek/qtek',['require','qtek/Camera','qtek/DynamicGeometry','qtek/FrameBuffer','qtek/Geometry','qtek/Joint','qtek/Light','qtek/Material','qtek/Mesh','qtek/Node','qtek/Renderable','qtek/Renderer','qtek/Scene','qtek/Shader','qtek/Skeleton','qtek/StaticGeometry','qtek/Texture','qtek/Texture2D','qtek/TextureCube','qtek/animation/Animation','qtek/animation/Blend1DClip','qtek/animation/Blend2DClip','qtek/animation/Clip','qtek/animation/SamplerClip','qtek/animation/SkinningClip','qtek/animation/TransformClip','qtek/animation/easing','qtek/async/Task','qtek/async/TaskGroup','qtek/camera/Orthographic','qtek/camera/Perspective','qtek/compositor/Compositor','qtek/compositor/Graph','qtek/compositor/Node','qtek/compositor/Pass','qtek/compositor/SceneNode','qtek/compositor/TextureNode','qtek/compositor/TexturePool','qtek/core/Base','qtek/core/Cache','qtek/core/Event','qtek/core/LRU','qtek/core/LinkedList','qtek/core/glenum','qtek/core/glinfo','qtek/core/mixin/derive','qtek/core/mixin/notifier','qtek/core/request','qtek/core/util','qtek/deferred/Renderer','qtek/deferred/StandardMaterial','qtek/dep/glmatrix','qtek/geometry/Cone','qtek/geometry/Cube','qtek/geometry/Cylinder','qtek/geometry/Plane','qtek/geometry/Sphere','qtek/light/Ambient','qtek/light/Directional','qtek/light/Point','qtek/light/Spot','qtek/loader/FX','qtek/loader/GLTF','qtek/loader/ThreeModel','qtek/math/BoundingBox','qtek/math/Frustum','qtek/math/Matrix2','qtek/math/Matrix2d','qtek/math/Matrix3','qtek/math/Matrix4','qtek/math/Plane','qtek/math/Quaternion','qtek/math/Ray','qtek/math/Value','qtek/math/Vector2','qtek/math/Vector3','qtek/math/Vector4','qtek/particleSystem/Emitter','qtek/particleSystem/Field','qtek/particleSystem/ForceField','qtek/particleSystem/Particle','qtek/particleSystem/ParticleRenderable','qtek/picking/PixelPicking','qtek/picking/RayPicking','qtek/plugin/FirstPersonControl','qtek/plugin/InfinitePlane','qtek/plugin/OrbitControl','qtek/plugin/Skybox','qtek/plugin/Skydome','qtek/prePass/EnvironmentMap','qtek/prePass/Reflection','qtek/prePass/ShadowMap','qtek/shader/buildin','qtek/shader/library','qtek/util/dds','qtek/util/delaunay','qtek/util/hdr','qtek/util/mesh','qtek/util/texture'],function(require) { - - var exportsObject = { - "Camera": require('qtek/Camera'), - "DynamicGeometry": require('qtek/DynamicGeometry'), - "FrameBuffer": require('qtek/FrameBuffer'), - "Geometry": require('qtek/Geometry'), - "Joint": require('qtek/Joint'), - "Light": require('qtek/Light'), - "Material": require('qtek/Material'), - "Mesh": require('qtek/Mesh'), - "Node": require('qtek/Node'), - "Renderable": require('qtek/Renderable'), - "Renderer": require('qtek/Renderer'), - "Scene": require('qtek/Scene'), - "Shader": require('qtek/Shader'), - "Skeleton": require('qtek/Skeleton'), - "StaticGeometry": require('qtek/StaticGeometry'), - "Texture": require('qtek/Texture'), - "Texture2D": require('qtek/Texture2D'), - "TextureCube": require('qtek/TextureCube'), - "animation": { - "Animation": require('qtek/animation/Animation'), - "Blend1DClip": require('qtek/animation/Blend1DClip'), - "Blend2DClip": require('qtek/animation/Blend2DClip'), - "Clip": require('qtek/animation/Clip'), - "SamplerClip": require('qtek/animation/SamplerClip'), - "SkinningClip": require('qtek/animation/SkinningClip'), - "TransformClip": require('qtek/animation/TransformClip'), - "easing": require('qtek/animation/easing') - }, - "async": { - "Task": require('qtek/async/Task'), - "TaskGroup": require('qtek/async/TaskGroup') - }, - "camera": { - "Orthographic": require('qtek/camera/Orthographic'), - "Perspective": require('qtek/camera/Perspective') - }, - "compositor": { - "Compositor": require('qtek/compositor/Compositor'), - "Graph": require('qtek/compositor/Graph'), - "Node": require('qtek/compositor/Node'), - "Pass": require('qtek/compositor/Pass'), - "SceneNode": require('qtek/compositor/SceneNode'), - "TextureNode": require('qtek/compositor/TextureNode'), - "TexturePool": require('qtek/compositor/TexturePool') - }, - "core": { - "Base": require('qtek/core/Base'), - "Cache": require('qtek/core/Cache'), - "Event": require('qtek/core/Event'), - "LRU": require('qtek/core/LRU'), - "LinkedList": require('qtek/core/LinkedList'), - "glenum": require('qtek/core/glenum'), - "glinfo": require('qtek/core/glinfo'), - "mixin": { - "derive": require('qtek/core/mixin/derive'), - "notifier": require('qtek/core/mixin/notifier') - }, - "request": require('qtek/core/request'), - "util": require('qtek/core/util') - }, - "deferred": { - "Renderer": require('qtek/deferred/Renderer'), - "StandardMaterial": require('qtek/deferred/StandardMaterial') - }, - "dep": { - "glmatrix": require('qtek/dep/glmatrix') - }, - "geometry": { - "Cone": require('qtek/geometry/Cone'), - "Cube": require('qtek/geometry/Cube'), - "Cylinder": require('qtek/geometry/Cylinder'), - "Plane": require('qtek/geometry/Plane'), - "Sphere": require('qtek/geometry/Sphere') - }, - "light": { - "Ambient": require('qtek/light/Ambient'), - "Directional": require('qtek/light/Directional'), - "Point": require('qtek/light/Point'), - "Spot": require('qtek/light/Spot') - }, - "loader": { - "FX": require('qtek/loader/FX'), - "GLTF": require('qtek/loader/GLTF'), - "ThreeModel": require('qtek/loader/ThreeModel') - }, - "math": { - "BoundingBox": require('qtek/math/BoundingBox'), - "Frustum": require('qtek/math/Frustum'), - "Matrix2": require('qtek/math/Matrix2'), - "Matrix2d": require('qtek/math/Matrix2d'), - "Matrix3": require('qtek/math/Matrix3'), - "Matrix4": require('qtek/math/Matrix4'), - "Plane": require('qtek/math/Plane'), - "Quaternion": require('qtek/math/Quaternion'), - "Ray": require('qtek/math/Ray'), - "Value": require('qtek/math/Value'), - "Vector2": require('qtek/math/Vector2'), - "Vector3": require('qtek/math/Vector3'), - "Vector4": require('qtek/math/Vector4') - }, - "particleSystem": { - "Emitter": require('qtek/particleSystem/Emitter'), - "Field": require('qtek/particleSystem/Field'), - "ForceField": require('qtek/particleSystem/ForceField'), - "Particle": require('qtek/particleSystem/Particle'), - "ParticleRenderable": require('qtek/particleSystem/ParticleRenderable') - }, - "picking": { - "PixelPicking": require('qtek/picking/PixelPicking'), - "RayPicking": require('qtek/picking/RayPicking') - }, - "plugin": { - "FirstPersonControl": require('qtek/plugin/FirstPersonControl'), - "InfinitePlane": require('qtek/plugin/InfinitePlane'), - "OrbitControl": require('qtek/plugin/OrbitControl'), - "Skybox": require('qtek/plugin/Skybox'), - "Skydome": require('qtek/plugin/Skydome') - }, - "prePass": { - "EnvironmentMap": require('qtek/prePass/EnvironmentMap'), - "Reflection": require('qtek/prePass/Reflection'), - "ShadowMap": require('qtek/prePass/ShadowMap') - }, - "shader": { - "buildin": require('qtek/shader/buildin'), - "library": require('qtek/shader/library') - }, - "util": { - "dds": require('qtek/util/dds'), - "delaunay": require('qtek/util/delaunay'), - "hdr": require('qtek/util/hdr'), - "mesh": require('qtek/util/mesh'), - "texture": require('qtek/util/texture') + /** + * Alias for {@link vec3.divide} + * @function + */ + vec3.div = vec3.divide; + + /** + * Returns the minimum of two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ + vec3.min = function(out, a, b) { + out[0] = Math.min(a[0], b[0]); + out[1] = Math.min(a[1], b[1]); + out[2] = Math.min(a[2], b[2]); + return out; + }; + + /** + * Returns the maximum of two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ + vec3.max = function(out, a, b) { + out[0] = Math.max(a[0], b[0]); + out[1] = Math.max(a[1], b[1]); + out[2] = Math.max(a[2], b[2]); + return out; + }; + + /** + * Scales a vec3 by a scalar number + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to scale + * @param {Number} b amount to scale the vector by + * @returns {vec3} out + */ + vec3.scale = function(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + return out; + }; + + /** + * Adds two vec3's after scaling the second operand by a scalar value + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @param {Number} scale the amount to scale b by before adding + * @returns {vec3} out + */ + vec3.scaleAndAdd = function(out, a, b, scale) { + out[0] = a[0] + (b[0] * scale); + out[1] = a[1] + (b[1] * scale); + out[2] = a[2] + (b[2] * scale); + return out; + }; + + /** + * Calculates the euclidian distance between two vec3's + * + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {Number} distance between a and b + */ + vec3.distance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2]; + return Math.sqrt(x*x + y*y + z*z); + }; + + /** + * Alias for {@link vec3.distance} + * @function + */ + vec3.dist = vec3.distance; + + /** + * Calculates the squared euclidian distance between two vec3's + * + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {Number} squared distance between a and b + */ + vec3.squaredDistance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2]; + return x*x + y*y + z*z; + }; + + /** + * Alias for {@link vec3.squaredDistance} + * @function + */ + vec3.sqrDist = vec3.squaredDistance; + + /** + * Calculates the length of a vec3 + * + * @param {vec3} a vector to calculate length of + * @returns {Number} length of a + */ + vec3.length = function (a) { + var x = a[0], + y = a[1], + z = a[2]; + return Math.sqrt(x*x + y*y + z*z); + }; + + /** + * Alias for {@link vec3.length} + * @function + */ + vec3.len = vec3.length; + + /** + * Calculates the squared length of a vec3 + * + * @param {vec3} a vector to calculate squared length of + * @returns {Number} squared length of a + */ + vec3.squaredLength = function (a) { + var x = a[0], + y = a[1], + z = a[2]; + return x*x + y*y + z*z; + }; + + /** + * Alias for {@link vec3.squaredLength} + * @function + */ + vec3.sqrLen = vec3.squaredLength; + + /** + * Negates the components of a vec3 + * + * @param {vec3} out the receiving vector + * @param {vec3} a vector to negate + * @returns {vec3} out + */ + vec3.negate = function(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + return out; + }; + + /** + * Returns the inverse of the components of a vec3 + * + * @param {vec3} out the receiving vector + * @param {vec3} a vector to invert + * @returns {vec3} out + */ + vec3.inverse = function(out, a) { + out[0] = 1.0 / a[0]; + out[1] = 1.0 / a[1]; + out[2] = 1.0 / a[2]; + return out; + }; + + /** + * Normalize a vec3 + * + * @param {vec3} out the receiving vector + * @param {vec3} a vector to normalize + * @returns {vec3} out + */ + vec3.normalize = function(out, a) { + var x = a[0], + y = a[1], + z = a[2]; + var len = x*x + y*y + z*z; + if (len > 0) { + //TODO: evaluate use of glm_invsqrt here? + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + out[2] = a[2] * len; + } + return out; + }; + + /** + * Calculates the dot product of two vec3's + * + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {Number} dot product of a and b + */ + vec3.dot = function (a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + }; + + /** + * Computes the cross product of two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @returns {vec3} out + */ + vec3.cross = function(out, a, b) { + var ax = a[0], ay = a[1], az = a[2], + bx = b[0], by = b[1], bz = b[2]; + + out[0] = ay * bz - az * by; + out[1] = az * bx - ax * bz; + out[2] = ax * by - ay * bx; + return out; + }; + + /** + * Performs a linear interpolation between two vec3's + * + * @param {vec3} out the receiving vector + * @param {vec3} a the first operand + * @param {vec3} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {vec3} out + */ + vec3.lerp = function (out, a, b, t) { + var ax = a[0], + ay = a[1], + az = a[2]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + out[2] = az + t * (b[2] - az); + return out; + }; + + /** + * Generates a random vector with the given scale + * + * @param {vec3} out the receiving vector + * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned + * @returns {vec3} out + */ + vec3.random = function (out, scale) { + scale = scale || 1.0; + + var r = GLMAT_RANDOM() * 2.0 * Math.PI; + var z = (GLMAT_RANDOM() * 2.0) - 1.0; + var zScale = Math.sqrt(1.0-z*z) * scale; + + out[0] = Math.cos(r) * zScale; + out[1] = Math.sin(r) * zScale; + out[2] = z * scale; + return out; + }; + + /** + * Transforms the vec3 with a mat4. + * 4th vector component is implicitly '1' + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {mat4} m matrix to transform with + * @returns {vec3} out + */ + vec3.transformMat4 = function(out, a, m) { + var x = a[0], y = a[1], z = a[2], + w = m[3] * x + m[7] * y + m[11] * z + m[15]; + w = w || 1.0; + out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w; + out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w; + out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w; + return out; + }; + + /** + * Transforms the vec3 with a mat3. + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {mat4} m the 3x3 matrix to transform with + * @returns {vec3} out + */ + vec3.transformMat3 = function(out, a, m) { + var x = a[0], y = a[1], z = a[2]; + out[0] = x * m[0] + y * m[3] + z * m[6]; + out[1] = x * m[1] + y * m[4] + z * m[7]; + out[2] = x * m[2] + y * m[5] + z * m[8]; + return out; + }; + + /** + * Transforms the vec3 with a quat + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {quat} q quaternion to transform with + * @returns {vec3} out + */ + vec3.transformQuat = function(out, a, q) { + // benchmarks: http://jsperf.com/quaternion-transform-vec3-implementations + + var x = a[0], y = a[1], z = a[2], + qx = q[0], qy = q[1], qz = q[2], qw = q[3], + + // calculate quat * vec + ix = qw * x + qy * z - qz * y, + iy = qw * y + qz * x - qx * z, + iz = qw * z + qx * y - qy * x, + iw = -qx * x - qy * y - qz * z; + + // calculate result * inverse quat + out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; + out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; + out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; + return out; + }; + + /** + * Rotate a 3D vector around the x-axis + * @param {vec3} out The receiving vec3 + * @param {vec3} a The vec3 point to rotate + * @param {vec3} b The origin of the rotation + * @param {Number} c The angle of rotation + * @returns {vec3} out + */ + vec3.rotateX = function(out, a, b, c){ + var 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 + out[0] = r[0] + b[0]; + out[1] = r[1] + b[1]; + out[2] = r[2] + b[2]; + + return out; + }; + + /** + * Rotate a 3D vector around the y-axis + * @param {vec3} out The receiving vec3 + * @param {vec3} a The vec3 point to rotate + * @param {vec3} b The origin of the rotation + * @param {Number} c The angle of rotation + * @returns {vec3} out + */ + vec3.rotateY = function(out, a, b, c){ + var 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 + out[0] = r[0] + b[0]; + out[1] = r[1] + b[1]; + out[2] = r[2] + b[2]; + + return out; + }; + + /** + * Rotate a 3D vector around the z-axis + * @param {vec3} out The receiving vec3 + * @param {vec3} a The vec3 point to rotate + * @param {vec3} b The origin of the rotation + * @param {Number} c The angle of rotation + * @returns {vec3} out + */ + vec3.rotateZ = function(out, a, b, c){ + var 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 + out[0] = r[0] + b[0]; + out[1] = r[1] + b[1]; + out[2] = r[2] + b[2]; + + return out; + }; + + /** + * Perform some operation over an array of vec3s. + * + * @param {Array} a the array of vectors to iterate over + * @param {Number} stride Number of elements between the start of each vec3. If 0 assumes tightly packed + * @param {Number} offset Number of elements to skip at the beginning of the array + * @param {Number} count Number of vec3s to iterate over. If 0 iterates over entire array + * @param {Function} fn Function to call for each vector in the array + * @param {Object} [arg] additional argument to pass to fn + * @returns {Array} a + * @function + */ + vec3.forEach = (function() { + var vec = vec3.create(); + + return function(a, stride, offset, count, fn, arg) { + var i, l; + if(!stride) { + stride = 3; + } + + if(!offset) { + offset = 0; + } + + if(count) { + l = Math.min((count * stride) + offset, a.length); + } else { + l = a.length; + } + + for(i = offset; i < l; i += stride) { + vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; + fn(vec, vec, arg); + a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; + } + + return a; + }; + })(); + + /** + * Get the angle between two 3D vectors + * @param {vec3} a The first operand + * @param {vec3} b The second operand + * @returns {Number} The angle in radians + */ + vec3.angle = function(a, b) { + + var tempA = vec3.fromValues(a[0], a[1], a[2]); + var tempB = vec3.fromValues(b[0], b[1], b[2]); + + vec3.normalize(tempA, tempA); + vec3.normalize(tempB, tempB); + + var cosine = vec3.dot(tempA, tempB); + + if(cosine > 1.0){ + return 0; + } else { + return Math.acos(cosine); + } + }; + + /** + * Returns a string representation of a vector + * + * @param {vec3} vec vector to represent as a string + * @returns {String} string representation of the vector + */ + vec3.str = function (a) { + return 'vec3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ')'; + }; + + if(typeof(exports) !== 'undefined') { + exports.vec3 = vec3; } -}; + ; + /* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + /** + * @class 4 Dimensional Vector + * @name vec4 + */ + + var vec4 = {}; + + /** + * Creates a new, empty vec4 + * + * @returns {vec4} a new 4D vector + */ + vec4.create = function() { + var out = new GLMAT_ARRAY_TYPE(4); + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 0; + return out; + }; + + /** + * Creates a new vec4 initialized with values from an existing vector + * + * @param {vec4} a vector to clone + * @returns {vec4} a new 4D vector + */ + vec4.clone = function(a) { + var out = new GLMAT_ARRAY_TYPE(4); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; + }; - exportsObject.version = '0.2.1'; + /** + * Creates a new vec4 initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {vec4} a new 4D vector + */ + vec4.fromValues = function(x, y, z, w) { + var out = new GLMAT_ARRAY_TYPE(4); + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = w; + return out; + }; - return exportsObject; -}); -define('qtek', ['qtek/qtek'], function (main) { return main; }); + /** + * Copy the values from one vec4 to another + * + * @param {vec4} out the receiving vector + * @param {vec4} a the source vector + * @returns {vec4} out + */ + vec4.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; + }; + + /** + * Set the components of a vec4 to the given values + * + * @param {vec4} out the receiving vector + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {vec4} out + */ + vec4.set = function(out, x, y, z, w) { + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = w; + return out; + }; + + /** + * Adds two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ + vec4.add = function(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + out[3] = a[3] + b[3]; + return out; + }; + + /** + * Subtracts vector b from vector a + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ + vec4.subtract = function(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + out[2] = a[2] - b[2]; + out[3] = a[3] - b[3]; + return out; + }; + + /** + * Alias for {@link vec4.subtract} + * @function + */ + vec4.sub = vec4.subtract; + + /** + * Multiplies two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ + vec4.multiply = function(out, a, b) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + out[2] = a[2] * b[2]; + out[3] = a[3] * b[3]; + return out; + }; -var qtek = require("qtek"); + /** + * Alias for {@link vec4.multiply} + * @function + */ + vec4.mul = vec4.multiply; + + /** + * Divides two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ + vec4.divide = function(out, a, b) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + out[2] = a[2] / b[2]; + out[3] = a[3] / b[3]; + return out; + }; + + /** + * Alias for {@link vec4.divide} + * @function + */ + vec4.div = vec4.divide; + + /** + * Returns the minimum of two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ + vec4.min = function(out, a, b) { + out[0] = Math.min(a[0], b[0]); + out[1] = Math.min(a[1], b[1]); + out[2] = Math.min(a[2], b[2]); + out[3] = Math.min(a[3], b[3]); + return out; + }; + + /** + * Returns the maximum of two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {vec4} out + */ + vec4.max = function(out, a, b) { + out[0] = Math.max(a[0], b[0]); + out[1] = Math.max(a[1], b[1]); + out[2] = Math.max(a[2], b[2]); + out[3] = Math.max(a[3], b[3]); + return out; + }; + + /** + * Scales a vec4 by a scalar number + * + * @param {vec4} out the receiving vector + * @param {vec4} a the vector to scale + * @param {Number} b amount to scale the vector by + * @returns {vec4} out + */ + vec4.scale = function(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + out[3] = a[3] * b; + return out; + }; + + /** + * Adds two vec4's after scaling the second operand by a scalar value + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @param {Number} scale the amount to scale b by before adding + * @returns {vec4} out + */ + vec4.scaleAndAdd = function(out, a, b, scale) { + out[0] = a[0] + (b[0] * scale); + out[1] = a[1] + (b[1] * scale); + out[2] = a[2] + (b[2] * scale); + out[3] = a[3] + (b[3] * scale); + return out; + }; + + /** + * Calculates the euclidian distance between two vec4's + * + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {Number} distance between a and b + */ + vec4.distance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2], + w = b[3] - a[3]; + return Math.sqrt(x*x + y*y + z*z + w*w); + }; + + /** + * Alias for {@link vec4.distance} + * @function + */ + vec4.dist = vec4.distance; + + /** + * Calculates the squared euclidian distance between two vec4's + * + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {Number} squared distance between a and b + */ + vec4.squaredDistance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1], + z = b[2] - a[2], + w = b[3] - a[3]; + return x*x + y*y + z*z + w*w; + }; + + /** + * Alias for {@link vec4.squaredDistance} + * @function + */ + vec4.sqrDist = vec4.squaredDistance; + + /** + * Calculates the length of a vec4 + * + * @param {vec4} a vector to calculate length of + * @returns {Number} length of a + */ + vec4.length = function (a) { + var x = a[0], + y = a[1], + z = a[2], + w = a[3]; + return Math.sqrt(x*x + y*y + z*z + w*w); + }; + + /** + * Alias for {@link vec4.length} + * @function + */ + vec4.len = vec4.length; + + /** + * Calculates the squared length of a vec4 + * + * @param {vec4} a vector to calculate squared length of + * @returns {Number} squared length of a + */ + vec4.squaredLength = function (a) { + var x = a[0], + y = a[1], + z = a[2], + w = a[3]; + return x*x + y*y + z*z + w*w; + }; + + /** + * Alias for {@link vec4.squaredLength} + * @function + */ + vec4.sqrLen = vec4.squaredLength; + + /** + * Negates the components of a vec4 + * + * @param {vec4} out the receiving vector + * @param {vec4} a vector to negate + * @returns {vec4} out + */ + vec4.negate = function(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + out[3] = -a[3]; + return out; + }; + + /** + * Returns the inverse of the components of a vec4 + * + * @param {vec4} out the receiving vector + * @param {vec4} a vector to invert + * @returns {vec4} out + */ + vec4.inverse = function(out, a) { + out[0] = 1.0 / a[0]; + out[1] = 1.0 / a[1]; + out[2] = 1.0 / a[2]; + out[3] = 1.0 / a[3]; + return out; + }; + + /** + * Normalize a vec4 + * + * @param {vec4} out the receiving vector + * @param {vec4} a vector to normalize + * @returns {vec4} out + */ + vec4.normalize = function(out, a) { + var x = a[0], + y = a[1], + z = a[2], + w = a[3]; + var len = x*x + y*y + z*z + w*w; + if (len > 0) { + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + out[2] = a[2] * len; + out[3] = a[3] * len; + } + return out; + }; + + /** + * Calculates the dot product of two vec4's + * + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @returns {Number} dot product of a and b + */ + vec4.dot = function (a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; + }; + + /** + * Performs a linear interpolation between two vec4's + * + * @param {vec4} out the receiving vector + * @param {vec4} a the first operand + * @param {vec4} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {vec4} out + */ + vec4.lerp = function (out, a, b, t) { + var ax = a[0], + ay = a[1], + az = a[2], + aw = a[3]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + out[2] = az + t * (b[2] - az); + out[3] = aw + t * (b[3] - aw); + return out; + }; + + /** + * Generates a random vector with the given scale + * + * @param {vec4} out the receiving vector + * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned + * @returns {vec4} out + */ + vec4.random = function (out, scale) { + scale = scale || 1.0; + + //TODO: This is a pretty awful way of doing this. Find something better. + out[0] = GLMAT_RANDOM(); + out[1] = GLMAT_RANDOM(); + out[2] = GLMAT_RANDOM(); + out[3] = GLMAT_RANDOM(); + vec4.normalize(out, out); + vec4.scale(out, out, scale); + return out; + }; + + /** + * Transforms the vec4 with a mat4. + * + * @param {vec4} out the receiving vector + * @param {vec4} a the vector to transform + * @param {mat4} m matrix to transform with + * @returns {vec4} out + */ + vec4.transformMat4 = function(out, a, m) { + var x = a[0], y = a[1], z = a[2], w = a[3]; + out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w; + out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w; + out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w; + out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w; + return out; + }; -for(var name in qtek){ - _exports[name] = qtek[name]; -} + /** + * Transforms the vec4 with a quat + * + * @param {vec4} out the receiving vector + * @param {vec4} a the vector to transform + * @param {quat} q quaternion to transform with + * @returns {vec4} out + */ + vec4.transformQuat = function(out, a, q) { + var x = a[0], y = a[1], z = a[2], + qx = q[0], qy = q[1], qz = q[2], qw = q[3], + + // calculate quat * vec + ix = qw * x + qy * z - qz * y, + iy = qw * y + qz * x - qx * z, + iz = qw * z + qx * y - qy * x, + iw = -qx * x - qy * y - qz * z; + + // calculate result * inverse quat + out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; + out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; + out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; + return out; + }; -}) \ No newline at end of file + /** + * Perform some operation over an array of vec4s. + * + * @param {Array} a the array of vectors to iterate over + * @param {Number} stride Number of elements between the start of each vec4. If 0 assumes tightly packed + * @param {Number} offset Number of elements to skip at the beginning of the array + * @param {Number} count Number of vec4s to iterate over. If 0 iterates over entire array + * @param {Function} fn Function to call for each vector in the array + * @param {Object} [arg] additional argument to pass to fn + * @returns {Array} a + * @function + */ + vec4.forEach = (function() { + var vec = vec4.create(); + + return function(a, stride, offset, count, fn, arg) { + var i, l; + if(!stride) { + stride = 4; + } + + if(!offset) { + offset = 0; + } + + if(count) { + l = Math.min((count * stride) + offset, a.length); + } else { + l = a.length; + } + + for(i = offset; i < l; i += stride) { + vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; vec[3] = a[i+3]; + fn(vec, vec, arg); + a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; a[i+3] = vec[3]; + } + + return a; + }; + })(); + + /** + * Returns a string representation of a vector + * + * @param {vec4} vec vector to represent as a string + * @returns {String} string representation of the vector + */ + vec4.str = function (a) { + return 'vec4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; + }; + + if(typeof(exports) !== 'undefined') { + exports.vec4 = vec4; + } + ; + /* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + /** + * @class 2x2 Matrix + * @name mat2 + */ + + var mat2 = {}; + + /** + * Creates a new identity mat2 + * + * @returns {mat2} a new 2x2 matrix + */ + mat2.create = function() { + var out = new GLMAT_ARRAY_TYPE(4); + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; + }; + + /** + * Creates a new mat2 initialized with values from an existing matrix + * + * @param {mat2} a matrix to clone + * @returns {mat2} a new 2x2 matrix + */ + mat2.clone = function(a) { + var out = new GLMAT_ARRAY_TYPE(4); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; + }; + + /** + * Copy the values from one mat2 to another + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the source matrix + * @returns {mat2} out + */ + mat2.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; + }; + + /** + * Set a mat2 to the identity matrix + * + * @param {mat2} out the receiving matrix + * @returns {mat2} out + */ + mat2.identity = function(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; + }; + + /** + * Transpose the values of a mat2 + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the source matrix + * @returns {mat2} out + */ + mat2.transpose = function(out, a) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (out === a) { + var a1 = a[1]; + out[1] = a[2]; + out[2] = a1; + } else { + out[0] = a[0]; + out[1] = a[2]; + out[2] = a[1]; + out[3] = a[3]; + } + + return out; + }; + + /** + * Inverts a mat2 + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the source matrix + * @returns {mat2} out + */ + mat2.invert = function(out, a) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], + + // Calculate the determinant + det = a0 * a3 - a2 * a1; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = a3 * det; + out[1] = -a1 * det; + out[2] = -a2 * det; + out[3] = a0 * det; + + return out; + }; + + /** + * Calculates the adjugate of a mat2 + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the source matrix + * @returns {mat2} out + */ + mat2.adjoint = function(out, a) { + // Caching this value is nessecary if out == a + var a0 = a[0]; + out[0] = a[3]; + out[1] = -a[1]; + out[2] = -a[2]; + out[3] = a0; + + return out; + }; + + /** + * Calculates the determinant of a mat2 + * + * @param {mat2} a the source matrix + * @returns {Number} determinant of a + */ + mat2.determinant = function (a) { + return a[0] * a[3] - a[2] * a[1]; + }; + + /** + * Multiplies two mat2's + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the first operand + * @param {mat2} b the second operand + * @returns {mat2} out + */ + mat2.multiply = function (out, a, b) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3]; + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = a0 * b0 + a2 * b1; + out[1] = a1 * b0 + a3 * b1; + out[2] = a0 * b2 + a2 * b3; + out[3] = a1 * b2 + a3 * b3; + return out; + }; + + /** + * Alias for {@link mat2.multiply} + * @function + */ + mat2.mul = mat2.multiply; + + /** + * Rotates a mat2 by the given angle + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat2} out + */ + mat2.rotate = function (out, a, rad) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], + s = Math.sin(rad), + c = Math.cos(rad); + out[0] = a0 * c + a2 * s; + out[1] = a1 * c + a3 * s; + out[2] = a0 * -s + a2 * c; + out[3] = a1 * -s + a3 * c; + return out; + }; + + /** + * Scales the mat2 by the dimensions in the given vec2 + * + * @param {mat2} out the receiving matrix + * @param {mat2} a the matrix to rotate + * @param {vec2} v the vec2 to scale the matrix by + * @returns {mat2} out + **/ + mat2.scale = function(out, a, v) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], + v0 = v[0], v1 = v[1]; + out[0] = a0 * v0; + out[1] = a1 * v0; + out[2] = a2 * v1; + out[3] = a3 * v1; + return out; + }; + + /** + * Returns a string representation of a mat2 + * + * @param {mat2} mat matrix to represent as a string + * @returns {String} string representation of the matrix + */ + mat2.str = function (a) { + return 'mat2(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; + }; + + /** + * Returns Frobenius norm of a mat2 + * + * @param {mat2} a the matrix to calculate Frobenius norm of + * @returns {Number} Frobenius norm + */ + mat2.frob = function (a) { + return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2))) + }; + + /** + * Returns L, D and U matrices (Lower triangular, Diagonal and Upper triangular) by factorizing the input matrix + * @param {mat2} L the lower triangular matrix + * @param {mat2} D the diagonal matrix + * @param {mat2} U the upper triangular matrix + * @param {mat2} a the input matrix to factorize + */ + + mat2.LDU = function (L, D, U, a) { + L[2] = a[2]/a[0]; + U[0] = a[0]; + U[1] = a[1]; + U[3] = a[3] - L[2] * U[1]; + return [L, D, U]; + }; + + if(typeof(exports) !== 'undefined') { + exports.mat2 = mat2; + } + ; + /* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + /** + * @class 2x3 Matrix + * @name mat2d + * + * @description + * A mat2d contains six elements defined as: + *
+	 * [a, c, tx,
+	 *  b, d, ty]
+	 * 
+ * This is a short form for the 3x3 matrix: + *
+	 * [a, c, tx,
+	 *  b, d, ty,
+	 *  0, 0, 1]
+	 * 
+ * The last row is ignored so the array is shorter and operations are faster. + */ + + var mat2d = {}; + + /** + * Creates a new identity mat2d + * + * @returns {mat2d} a new 2x3 matrix + */ + mat2d.create = function() { + var out = new GLMAT_ARRAY_TYPE(6); + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 1; + out[4] = 0; + out[5] = 0; + return out; + }; + + /** + * Creates a new mat2d initialized with values from an existing matrix + * + * @param {mat2d} a matrix to clone + * @returns {mat2d} a new 2x3 matrix + */ + mat2d.clone = function(a) { + var out = new GLMAT_ARRAY_TYPE(6); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + return out; + }; + + /** + * Copy the values from one mat2d to another + * + * @param {mat2d} out the receiving matrix + * @param {mat2d} a the source matrix + * @returns {mat2d} out + */ + mat2d.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + return out; + }; + + /** + * Set a mat2d to the identity matrix + * + * @param {mat2d} out the receiving matrix + * @returns {mat2d} out + */ + mat2d.identity = function(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 1; + out[4] = 0; + out[5] = 0; + return out; + }; + + /** + * Inverts a mat2d + * + * @param {mat2d} out the receiving matrix + * @param {mat2d} a the source matrix + * @returns {mat2d} out + */ + mat2d.invert = function(out, a) { + var aa = a[0], ab = a[1], ac = a[2], ad = a[3], + atx = a[4], aty = a[5]; + + var det = aa * ad - ab * ac; + if(!det){ + return null; + } + det = 1.0 / det; + + out[0] = ad * det; + out[1] = -ab * det; + out[2] = -ac * det; + out[3] = aa * det; + out[4] = (ac * aty - ad * atx) * det; + out[5] = (ab * atx - aa * aty) * det; + return out; + }; + + /** + * Calculates the determinant of a mat2d + * + * @param {mat2d} a the source matrix + * @returns {Number} determinant of a + */ + mat2d.determinant = function (a) { + return a[0] * a[3] - a[1] * a[2]; + }; + + /** + * Multiplies two mat2d's + * + * @param {mat2d} out the receiving matrix + * @param {mat2d} a the first operand + * @param {mat2d} b the second operand + * @returns {mat2d} out + */ + mat2d.multiply = function (out, a, b) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5], + b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5]; + out[0] = a0 * b0 + a2 * b1; + out[1] = a1 * b0 + a3 * b1; + out[2] = a0 * b2 + a2 * b3; + out[3] = a1 * b2 + a3 * b3; + out[4] = a0 * b4 + a2 * b5 + a4; + out[5] = a1 * b4 + a3 * b5 + a5; + return out; + }; + + /** + * Alias for {@link mat2d.multiply} + * @function + */ + mat2d.mul = mat2d.multiply; + + + /** + * Rotates a mat2d by the given angle + * + * @param {mat2d} out the receiving matrix + * @param {mat2d} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat2d} out + */ + mat2d.rotate = function (out, a, rad) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5], + s = Math.sin(rad), + c = Math.cos(rad); + out[0] = a0 * c + a2 * s; + out[1] = a1 * c + a3 * s; + out[2] = a0 * -s + a2 * c; + out[3] = a1 * -s + a3 * c; + out[4] = a4; + out[5] = a5; + return out; + }; + + /** + * Scales the mat2d by the dimensions in the given vec2 + * + * @param {mat2d} out the receiving matrix + * @param {mat2d} a the matrix to translate + * @param {vec2} v the vec2 to scale the matrix by + * @returns {mat2d} out + **/ + mat2d.scale = function(out, a, v) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5], + v0 = v[0], v1 = v[1]; + out[0] = a0 * v0; + out[1] = a1 * v0; + out[2] = a2 * v1; + out[3] = a3 * v1; + out[4] = a4; + out[5] = a5; + return out; + }; + + /** + * Translates the mat2d by the dimensions in the given vec2 + * + * @param {mat2d} out the receiving matrix + * @param {mat2d} a the matrix to translate + * @param {vec2} v the vec2 to translate the matrix by + * @returns {mat2d} out + **/ + mat2d.translate = function(out, a, v) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5], + v0 = v[0], v1 = v[1]; + out[0] = a0; + out[1] = a1; + out[2] = a2; + out[3] = a3; + out[4] = a0 * v0 + a2 * v1 + a4; + out[5] = a1 * v0 + a3 * v1 + a5; + return out; + }; + + /** + * Returns a string representation of a mat2d + * + * @param {mat2d} a matrix to represent as a string + * @returns {String} string representation of the matrix + */ + mat2d.str = function (a) { + return 'mat2d(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + + a[3] + ', ' + a[4] + ', ' + a[5] + ')'; + }; + + /** + * Returns Frobenius norm of a mat2d + * + * @param {mat2d} a the matrix to calculate Frobenius norm of + * @returns {Number} Frobenius norm + */ + mat2d.frob = function (a) { + return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2) + Math.pow(a[4], 2) + Math.pow(a[5], 2) + 1)) + }; + + if(typeof(exports) !== 'undefined') { + exports.mat2d = mat2d; + } + ; + /* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + /** + * @class 3x3 Matrix + * @name mat3 + */ + + var mat3 = {}; + + /** + * Creates a new identity mat3 + * + * @returns {mat3} a new 3x3 matrix + */ + mat3.create = function() { + var out = new GLMAT_ARRAY_TYPE(9); + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 1; + out[5] = 0; + out[6] = 0; + out[7] = 0; + out[8] = 1; + return out; + }; + + /** + * Copies the upper-left 3x3 values into the given mat3. + * + * @param {mat3} out the receiving 3x3 matrix + * @param {mat4} a the source 4x4 matrix + * @returns {mat3} out + */ + mat3.fromMat4 = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[4]; + out[4] = a[5]; + out[5] = a[6]; + out[6] = a[8]; + out[7] = a[9]; + out[8] = a[10]; + return out; + }; + + /** + * Creates a new mat3 initialized with values from an existing matrix + * + * @param {mat3} a matrix to clone + * @returns {mat3} a new 3x3 matrix + */ + mat3.clone = function(a) { + var out = new GLMAT_ARRAY_TYPE(9); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + return out; + }; + + /** + * Copy the values from one mat3 to another + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ + mat3.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + return out; + }; + + /** + * Set a mat3 to the identity matrix + * + * @param {mat3} out the receiving matrix + * @returns {mat3} out + */ + mat3.identity = function(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 1; + out[5] = 0; + out[6] = 0; + out[7] = 0; + out[8] = 1; + return out; + }; + + /** + * Transpose the values of a mat3 + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ + mat3.transpose = function(out, a) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (out === a) { + var a01 = a[1], a02 = a[2], a12 = a[5]; + out[1] = a[3]; + out[2] = a[6]; + out[3] = a01; + out[5] = a[7]; + out[6] = a02; + out[7] = a12; + } else { + out[0] = a[0]; + out[1] = a[3]; + out[2] = a[6]; + out[3] = a[1]; + out[4] = a[4]; + out[5] = a[7]; + out[6] = a[2]; + out[7] = a[5]; + out[8] = a[8]; + } + + return out; + }; + + /** + * Inverts a mat3 + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ + mat3.invert = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8], + + b01 = a22 * a11 - a12 * a21, + b11 = -a22 * a10 + a12 * a20, + b21 = a21 * a10 - a11 * a20, + + // Calculate the determinant + det = a00 * b01 + a01 * b11 + a02 * b21; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = b01 * det; + out[1] = (-a22 * a01 + a02 * a21) * det; + out[2] = (a12 * a01 - a02 * a11) * det; + out[3] = b11 * det; + out[4] = (a22 * a00 - a02 * a20) * det; + out[5] = (-a12 * a00 + a02 * a10) * det; + out[6] = b21 * det; + out[7] = (-a21 * a00 + a01 * a20) * det; + out[8] = (a11 * a00 - a01 * a10) * det; + return out; + }; + + /** + * Calculates the adjugate of a mat3 + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the source matrix + * @returns {mat3} out + */ + mat3.adjoint = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8]; + + out[0] = (a11 * a22 - a12 * a21); + out[1] = (a02 * a21 - a01 * a22); + out[2] = (a01 * a12 - a02 * a11); + out[3] = (a12 * a20 - a10 * a22); + out[4] = (a00 * a22 - a02 * a20); + out[5] = (a02 * a10 - a00 * a12); + out[6] = (a10 * a21 - a11 * a20); + out[7] = (a01 * a20 - a00 * a21); + out[8] = (a00 * a11 - a01 * a10); + return out; + }; + + /** + * Calculates the determinant of a mat3 + * + * @param {mat3} a the source matrix + * @returns {Number} determinant of a + */ + mat3.determinant = function (a) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8]; + + return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20); + }; + + /** + * Multiplies two mat3's + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the first operand + * @param {mat3} b the second operand + * @returns {mat3} out + */ + mat3.multiply = function (out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8], + + b00 = b[0], b01 = b[1], b02 = b[2], + b10 = b[3], b11 = b[4], b12 = b[5], + b20 = b[6], b21 = b[7], b22 = b[8]; + + out[0] = b00 * a00 + b01 * a10 + b02 * a20; + out[1] = b00 * a01 + b01 * a11 + b02 * a21; + out[2] = b00 * a02 + b01 * a12 + b02 * a22; + + out[3] = b10 * a00 + b11 * a10 + b12 * a20; + out[4] = b10 * a01 + b11 * a11 + b12 * a21; + out[5] = b10 * a02 + b11 * a12 + b12 * a22; + + out[6] = b20 * a00 + b21 * a10 + b22 * a20; + out[7] = b20 * a01 + b21 * a11 + b22 * a21; + out[8] = b20 * a02 + b21 * a12 + b22 * a22; + return out; + }; + + /** + * Alias for {@link mat3.multiply} + * @function + */ + mat3.mul = mat3.multiply; + + /** + * Translate a mat3 by the given vector + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the matrix to translate + * @param {vec2} v vector to translate by + * @returns {mat3} out + */ + mat3.translate = function(out, a, v) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8], + x = v[0], y = v[1]; + + out[0] = a00; + out[1] = a01; + out[2] = a02; + + out[3] = a10; + out[4] = a11; + out[5] = a12; + + out[6] = x * a00 + y * a10 + a20; + out[7] = x * a01 + y * a11 + a21; + out[8] = x * a02 + y * a12 + a22; + return out; + }; + + /** + * Rotates a mat3 by the given angle + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat3} out + */ + mat3.rotate = function (out, a, rad) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[3], a11 = a[4], a12 = a[5], + a20 = a[6], a21 = a[7], a22 = a[8], + + s = Math.sin(rad), + c = Math.cos(rad); + + out[0] = c * a00 + s * a10; + out[1] = c * a01 + s * a11; + out[2] = c * a02 + s * a12; + + out[3] = c * a10 - s * a00; + out[4] = c * a11 - s * a01; + out[5] = c * a12 - s * a02; + + out[6] = a20; + out[7] = a21; + out[8] = a22; + return out; + }; + + /** + * Scales the mat3 by the dimensions in the given vec2 + * + * @param {mat3} out the receiving matrix + * @param {mat3} a the matrix to rotate + * @param {vec2} v the vec2 to scale the matrix by + * @returns {mat3} out + **/ + mat3.scale = function(out, a, v) { + var x = v[0], y = v[1]; + + out[0] = x * a[0]; + out[1] = x * a[1]; + out[2] = x * a[2]; + + out[3] = y * a[3]; + out[4] = y * a[4]; + out[5] = y * a[5]; + + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + return out; + }; + + /** + * Copies the values from a mat2d into a mat3 + * + * @param {mat3} out the receiving matrix + * @param {mat2d} a the matrix to copy + * @returns {mat3} out + **/ + mat3.fromMat2d = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = 0; + + out[3] = a[2]; + out[4] = a[3]; + out[5] = 0; + + out[6] = a[4]; + out[7] = a[5]; + out[8] = 1; + return out; + }; + + /** + * Calculates a 3x3 matrix from the given quaternion + * + * @param {mat3} out mat3 receiving operation result + * @param {quat} q Quaternion to create matrix from + * + * @returns {mat3} out + */ + mat3.fromQuat = function (out, q) { + var x = q[0], y = q[1], z = q[2], w = q[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + + xx = x * x2, + yx = y * x2, + yy = y * y2, + zx = z * x2, + zy = z * y2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + wz = w * z2; + + out[0] = 1 - yy - zz; + out[3] = yx - wz; + out[6] = zx + wy; + + out[1] = yx + wz; + out[4] = 1 - xx - zz; + out[7] = zy - wx; + + out[2] = zx - wy; + out[5] = zy + wx; + out[8] = 1 - xx - yy; + + return out; + }; + + /** + * Calculates a 3x3 normal matrix (transpose inverse) from the 4x4 matrix + * + * @param {mat3} out mat3 receiving operation result + * @param {mat4} a Mat4 to derive the normal matrix from + * + * @returns {mat3} out + */ + mat3.normalFromMat4 = function (out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + // Calculate the determinant + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[2] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + + out[3] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[4] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[5] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + + out[6] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[7] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[8] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + + return out; + }; + + /** + * Returns a string representation of a mat3 + * + * @param {mat3} mat matrix to represent as a string + * @returns {String} string representation of the matrix + */ + mat3.str = function (a) { + return 'mat3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + + a[3] + ', ' + a[4] + ', ' + a[5] + ', ' + + a[6] + ', ' + a[7] + ', ' + a[8] + ')'; + }; + + /** + * Returns Frobenius norm of a mat3 + * + * @param {mat3} a the matrix to calculate Frobenius norm of + * @returns {Number} Frobenius norm + */ + mat3.frob = function (a) { + return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2) + Math.pow(a[4], 2) + Math.pow(a[5], 2) + Math.pow(a[6], 2) + Math.pow(a[7], 2) + Math.pow(a[8], 2))) + }; + + + if(typeof(exports) !== 'undefined') { + exports.mat3 = mat3; + } + ; + /* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + /** + * @class 4x4 Matrix + * @name mat4 + */ + + var mat4 = {}; + + /** + * Creates a new identity mat4 + * + * @returns {mat4} a new 4x4 matrix + */ + mat4.create = function() { + var out = new GLMAT_ARRAY_TYPE(16); + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; + }; + + /** + * Creates a new mat4 initialized with values from an existing matrix + * + * @param {mat4} a matrix to clone + * @returns {mat4} a new 4x4 matrix + */ + mat4.clone = function(a) { + var out = new GLMAT_ARRAY_TYPE(16); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; + }; + + /** + * Copy the values from one mat4 to another + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ + mat4.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; + }; + + /** + * Set a mat4 to the identity matrix + * + * @param {mat4} out the receiving matrix + * @returns {mat4} out + */ + mat4.identity = function(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; + }; + + /** + * Transpose the values of a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ + mat4.transpose = function(out, a) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (out === a) { + var a01 = a[1], a02 = a[2], a03 = a[3], + a12 = a[6], a13 = a[7], + a23 = a[11]; + + out[1] = a[4]; + out[2] = a[8]; + out[3] = a[12]; + out[4] = a01; + out[6] = a[9]; + out[7] = a[13]; + out[8] = a02; + out[9] = a12; + out[11] = a[14]; + out[12] = a03; + out[13] = a13; + out[14] = a23; + } else { + out[0] = a[0]; + out[1] = a[4]; + out[2] = a[8]; + out[3] = a[12]; + out[4] = a[1]; + out[5] = a[5]; + out[6] = a[9]; + out[7] = a[13]; + out[8] = a[2]; + out[9] = a[6]; + out[10] = a[10]; + out[11] = a[14]; + out[12] = a[3]; + out[13] = a[7]; + out[14] = a[11]; + out[15] = a[15]; + } + + return out; + }; + + /** + * Inverts a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ + mat4.invert = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + // Calculate the determinant + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + + return out; + }; + + /** + * Calculates the adjugate of a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ + mat4.adjoint = function(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + out[0] = (a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22)); + out[1] = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22)); + out[2] = (a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12)); + out[3] = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12)); + out[4] = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22)); + out[5] = (a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22)); + out[6] = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12)); + out[7] = (a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12)); + out[8] = (a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21)); + out[9] = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21)); + out[10] = (a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11)); + out[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11)); + out[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21)); + out[13] = (a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21)); + out[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11)); + out[15] = (a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11)); + return out; + }; + + /** + * Calculates the determinant of a mat4 + * + * @param {mat4} a the source matrix + * @returns {Number} determinant of a + */ + mat4.determinant = function (a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32; + + // Calculate the determinant + return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + }; + + /** + * Multiplies two mat4's + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the first operand + * @param {mat4} b the second operand + * @returns {mat4} out + */ + mat4.multiply = function (out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + // Cache only the current line of the second matrix + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; + out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; + out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; + out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + return out; + }; + + /** + * Multiplies two affine mat4's + * Add by https://github.com/pissang + * @param {mat4} out the receiving matrix + * @param {mat4} a the first operand + * @param {mat4} b the second operand + * @returns {mat4} out + */ + mat4.multiplyAffine = function (out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], + a10 = a[4], a11 = a[5], a12 = a[6], + a20 = a[8], a21 = a[9], a22 = a[10], + a30 = a[12], a31 = a[13], a32 = a[14]; + + // Cache only the current line of the second matrix + var b0 = b[0], b1 = b[1], b2 = b[2]; + out[0] = b0*a00 + b1*a10 + b2*a20; + out[1] = b0*a01 + b1*a11 + b2*a21; + out[2] = b0*a02 + b1*a12 + b2*a22; + // out[3] = 0; + + b0 = b[4]; b1 = b[5]; b2 = b[6]; + out[4] = b0*a00 + b1*a10 + b2*a20; + out[5] = b0*a01 + b1*a11 + b2*a21; + out[6] = b0*a02 + b1*a12 + b2*a22; + // out[7] = 0; + + b0 = b[8]; b1 = b[9]; b2 = b[10]; + out[8] = b0*a00 + b1*a10 + b2*a20; + out[9] = b0*a01 + b1*a11 + b2*a21; + out[10] = b0*a02 + b1*a12 + b2*a22; + // out[11] = 0; + + b0 = b[12]; b1 = b[13]; b2 = b[14]; + out[12] = b0*a00 + b1*a10 + b2*a20 + a30; + out[13] = b0*a01 + b1*a11 + b2*a21 + a31; + out[14] = b0*a02 + b1*a12 + b2*a22 + a32; + // out[15] = 1; + return out; + }; + + /** + * Alias for {@link mat4.multiply} + * @function + */ + mat4.mul = mat4.multiply; + + /** + * Alias for {@link mat4.multiplyAffine} + * @function + */ + mat4.mulAffine = mat4.multiplyAffine; + /** + * Translate a mat4 by the given vector + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to translate + * @param {vec3} v vector to translate by + * @returns {mat4} out + */ + mat4.translate = function (out, a, v) { + var x = v[0], y = v[1], z = v[2], + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23; + + if (a === out) { + out[12] = a[0] * x + a[4] * y + a[8] * z + a[12]; + out[13] = a[1] * x + a[5] * y + a[9] * z + a[13]; + out[14] = a[2] * x + a[6] * y + a[10] * z + a[14]; + out[15] = a[3] * x + a[7] * y + a[11] * z + a[15]; + } else { + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03; + out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13; + out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23; + + out[12] = a00 * x + a10 * y + a20 * z + a[12]; + out[13] = a01 * x + a11 * y + a21 * z + a[13]; + out[14] = a02 * x + a12 * y + a22 * z + a[14]; + out[15] = a03 * x + a13 * y + a23 * z + a[15]; + } + + return out; + }; + + /** + * Scales the mat4 by the dimensions in the given vec3 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to scale + * @param {vec3} v the vec3 to scale the matrix by + * @returns {mat4} out + **/ + mat4.scale = function(out, a, v) { + var x = v[0], y = v[1], z = v[2]; + + out[0] = a[0] * x; + out[1] = a[1] * x; + out[2] = a[2] * x; + out[3] = a[3] * x; + out[4] = a[4] * y; + out[5] = a[5] * y; + out[6] = a[6] * y; + out[7] = a[7] * y; + out[8] = a[8] * z; + out[9] = a[9] * z; + out[10] = a[10] * z; + out[11] = a[11] * z; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; + }; + + /** + * Rotates a mat4 by the given angle + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @param {vec3} axis the axis to rotate around + * @returns {mat4} out + */ + mat4.rotate = function (out, a, rad, axis) { + var x = axis[0], y = axis[1], z = axis[2], + len = Math.sqrt(x * x + y * y + z * z), + s, c, t, + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23, + b00, b01, b02, + b10, b11, b12, + b20, b21, b22; + + if (Math.abs(len) < GLMAT_EPSILON) { return null; } + + len = 1 / len; + x *= len; + y *= len; + z *= len; + + s = Math.sin(rad); + c = Math.cos(rad); + t = 1 - c; + + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + // Construct the elements of the rotation matrix + b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; + b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; + b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; + + // Perform rotation-specific matrix multiplication + out[0] = a00 * b00 + a10 * b01 + a20 * b02; + out[1] = a01 * b00 + a11 * b01 + a21 * b02; + out[2] = a02 * b00 + a12 * b01 + a22 * b02; + out[3] = a03 * b00 + a13 * b01 + a23 * b02; + out[4] = a00 * b10 + a10 * b11 + a20 * b12; + out[5] = a01 * b10 + a11 * b11 + a21 * b12; + out[6] = a02 * b10 + a12 * b11 + a22 * b12; + out[7] = a03 * b10 + a13 * b11 + a23 * b12; + out[8] = a00 * b20 + a10 * b21 + a20 * b22; + out[9] = a01 * b20 + a11 * b21 + a21 * b22; + out[10] = a02 * b20 + a12 * b21 + a22 * b22; + out[11] = a03 * b20 + a13 * b21 + a23 * b22; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + return out; + }; + + /** + * Rotates a matrix by the given angle around the X axis + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ + mat4.rotateX = function (out, a, rad) { + var s = Math.sin(rad), + c = Math.cos(rad), + a10 = a[4], + a11 = a[5], + a12 = a[6], + a13 = a[7], + a20 = a[8], + a21 = a[9], + a22 = a[10], + a23 = a[11]; + + if (a !== out) { // If the source and destination differ, copy the unchanged rows + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + // Perform axis-specific matrix multiplication + out[4] = a10 * c + a20 * s; + out[5] = a11 * c + a21 * s; + out[6] = a12 * c + a22 * s; + out[7] = a13 * c + a23 * s; + out[8] = a20 * c - a10 * s; + out[9] = a21 * c - a11 * s; + out[10] = a22 * c - a12 * s; + out[11] = a23 * c - a13 * s; + return out; + }; + + /** + * Rotates a matrix by the given angle around the Y axis + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ + mat4.rotateY = function (out, a, rad) { + var s = Math.sin(rad), + c = Math.cos(rad), + a00 = a[0], + a01 = a[1], + a02 = a[2], + a03 = a[3], + a20 = a[8], + a21 = a[9], + a22 = a[10], + a23 = a[11]; + + if (a !== out) { // If the source and destination differ, copy the unchanged rows + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + // Perform axis-specific matrix multiplication + out[0] = a00 * c - a20 * s; + out[1] = a01 * c - a21 * s; + out[2] = a02 * c - a22 * s; + out[3] = a03 * c - a23 * s; + out[8] = a00 * s + a20 * c; + out[9] = a01 * s + a21 * c; + out[10] = a02 * s + a22 * c; + out[11] = a03 * s + a23 * c; + return out; + }; + + /** + * Rotates a matrix by the given angle around the Z axis + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @returns {mat4} out + */ + mat4.rotateZ = function (out, a, rad) { + var s = Math.sin(rad), + c = Math.cos(rad), + a00 = a[0], + a01 = a[1], + a02 = a[2], + a03 = a[3], + a10 = a[4], + a11 = a[5], + a12 = a[6], + a13 = a[7]; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + // Perform axis-specific matrix multiplication + out[0] = a00 * c + a10 * s; + out[1] = a01 * c + a11 * s; + out[2] = a02 * c + a12 * s; + out[3] = a03 * c + a13 * s; + out[4] = a10 * c - a00 * s; + out[5] = a11 * c - a01 * s; + out[6] = a12 * c - a02 * s; + out[7] = a13 * c - a03 * s; + return out; + }; + + /** + * Creates a matrix from a quaternion rotation and vector translation + * This is equivalent to (but much faster than): + * + * mat4.identity(dest); + * mat4.translate(dest, vec); + * var quatMat = mat4.create(); + * quat4.toMat4(quat, quatMat); + * mat4.multiply(dest, quatMat); + * + * @param {mat4} out mat4 receiving operation result + * @param {quat4} q Rotation quaternion + * @param {vec3} v Translation vector + * @returns {mat4} out + */ + mat4.fromRotationTranslation = function (out, q, v) { + // Quaternion math + var x = q[0], y = q[1], z = q[2], w = q[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + + xx = x * x2, + xy = x * y2, + xz = x * z2, + yy = y * y2, + yz = y * z2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + wz = w * z2; + + out[0] = 1 - (yy + zz); + out[1] = xy + wz; + out[2] = xz - wy; + out[3] = 0; + out[4] = xy - wz; + out[5] = 1 - (xx + zz); + out[6] = yz + wx; + out[7] = 0; + out[8] = xz + wy; + out[9] = yz - wx; + out[10] = 1 - (xx + yy); + out[11] = 0; + out[12] = v[0]; + out[13] = v[1]; + out[14] = v[2]; + out[15] = 1; + + return out; + }; + + mat4.fromQuat = function (out, q) { + var x = q[0], y = q[1], z = q[2], w = q[3], + x2 = x + x, + y2 = y + y, + z2 = z + z, + + xx = x * x2, + yx = y * x2, + yy = y * y2, + zx = z * x2, + zy = z * y2, + zz = z * z2, + wx = w * x2, + wy = w * y2, + wz = w * z2; + + out[0] = 1 - yy - zz; + out[1] = yx + wz; + out[2] = zx - wy; + out[3] = 0; + + out[4] = yx - wz; + out[5] = 1 - xx - zz; + out[6] = zy + wx; + out[7] = 0; + + out[8] = zx + wy; + out[9] = zy - wx; + out[10] = 1 - xx - yy; + out[11] = 0; + + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + + return out; + }; + + /** + * Generates a frustum matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {Number} left Left bound of the frustum + * @param {Number} right Right bound of the frustum + * @param {Number} bottom Bottom bound of the frustum + * @param {Number} top Top bound of the frustum + * @param {Number} near Near bound of the frustum + * @param {Number} far Far bound of the frustum + * @returns {mat4} out + */ + mat4.frustum = function (out, left, right, bottom, top, near, far) { + var rl = 1 / (right - left), + tb = 1 / (top - bottom), + nf = 1 / (near - far); + out[0] = (near * 2) * rl; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = (near * 2) * tb; + out[6] = 0; + out[7] = 0; + out[8] = (right + left) * rl; + out[9] = (top + bottom) * tb; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = (far * near * 2) * nf; + out[15] = 0; + return out; + }; + + /** + * Generates a perspective projection matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} fovy Vertical field of view in radians + * @param {number} aspect Aspect ratio. typically viewport width/height + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ + mat4.perspective = function (out, fovy, aspect, near, far) { + var f = 1.0 / Math.tan(fovy / 2), + nf = 1 / (near - far); + out[0] = f / aspect; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = f; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = (2 * far * near) * nf; + out[15] = 0; + return out; + }; + + /** + * Generates a orthogonal projection matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} left Left bound of the frustum + * @param {number} right Right bound of the frustum + * @param {number} bottom Bottom bound of the frustum + * @param {number} top Top bound of the frustum + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ + mat4.ortho = function (out, left, right, bottom, top, near, far) { + var lr = 1 / (left - right), + bt = 1 / (bottom - top), + nf = 1 / (near - far); + out[0] = -2 * lr; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = -2 * bt; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 2 * nf; + out[11] = 0; + out[12] = (left + right) * lr; + out[13] = (top + bottom) * bt; + out[14] = (far + near) * nf; + out[15] = 1; + return out; + }; + + /** + * Generates a look-at matrix with the given eye position, focal point, and up axis + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {vec3} eye Position of the viewer + * @param {vec3} center Point the viewer is looking at + * @param {vec3} up vec3 pointing up + * @returns {mat4} out + */ + mat4.lookAt = function (out, eye, center, up) { + var x0, x1, x2, y0, y1, y2, z0, z1, z2, len, + eyex = eye[0], + eyey = eye[1], + eyez = eye[2], + upx = up[0], + upy = up[1], + upz = up[2], + centerx = center[0], + centery = center[1], + centerz = center[2]; + + if (Math.abs(eyex - centerx) < GLMAT_EPSILON && + Math.abs(eyey - centery) < GLMAT_EPSILON && + Math.abs(eyez - centerz) < GLMAT_EPSILON) { + return mat4.identity(out); + } + + z0 = eyex - centerx; + z1 = eyey - centery; + z2 = eyez - centerz; + + len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); + z0 *= len; + z1 *= len; + z2 *= len; + + 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; + } + + 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; + } + + out[0] = x0; + out[1] = y0; + out[2] = z0; + out[3] = 0; + out[4] = x1; + out[5] = y1; + out[6] = z1; + out[7] = 0; + out[8] = x2; + out[9] = y2; + out[10] = z2; + out[11] = 0; + out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); + out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); + out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); + out[15] = 1; + + return out; + }; + + /** + * Returns a string representation of a mat4 + * + * @param {mat4} mat matrix to represent as a string + * @returns {String} string representation of the matrix + */ + mat4.str = function (a) { + return 'mat4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ', ' + + a[4] + ', ' + a[5] + ', ' + a[6] + ', ' + a[7] + ', ' + + a[8] + ', ' + a[9] + ', ' + a[10] + ', ' + a[11] + ', ' + + a[12] + ', ' + a[13] + ', ' + a[14] + ', ' + a[15] + ')'; + }; + + /** + * Returns Frobenius norm of a mat4 + * + * @param {mat4} a the matrix to calculate Frobenius norm of + * @returns {Number} Frobenius norm + */ + mat4.frob = function (a) { + return(Math.sqrt(Math.pow(a[0], 2) + Math.pow(a[1], 2) + Math.pow(a[2], 2) + Math.pow(a[3], 2) + Math.pow(a[4], 2) + Math.pow(a[5], 2) + Math.pow(a[6], 2) + Math.pow(a[7], 2) + Math.pow(a[8], 2) + Math.pow(a[9], 2) + Math.pow(a[10], 2) + Math.pow(a[11], 2) + Math.pow(a[12], 2) + Math.pow(a[13], 2) + Math.pow(a[14], 2) + Math.pow(a[15], 2) )) + }; + + + if(typeof(exports) !== 'undefined') { + exports.mat4 = mat4; + } + ; + /* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + /** + * @class Quaternion + * @name quat + */ + + var quat = {}; + + /** + * Creates a new identity quat + * + * @returns {quat} a new quaternion + */ + quat.create = function() { + var out = new GLMAT_ARRAY_TYPE(4); + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; + }; + + /** + * Sets a quaternion to represent the shortest rotation from one + * vector to another. + * + * Both vectors are assumed to be unit length. + * + * @param {quat} out the receiving quaternion. + * @param {vec3} a the initial vector + * @param {vec3} b the destination vector + * @returns {quat} out + */ + quat.rotationTo = (function() { + var tmpvec3 = vec3.create(); + var xUnitVec3 = vec3.fromValues(1,0,0); + var yUnitVec3 = vec3.fromValues(0,1,0); + + return function(out, a, b) { + var dot = vec3.dot(a, b); + if (dot < -0.999999) { + vec3.cross(tmpvec3, xUnitVec3, a); + if (vec3.length(tmpvec3) < 0.000001) + vec3.cross(tmpvec3, yUnitVec3, a); + vec3.normalize(tmpvec3, tmpvec3); + quat.setAxisAngle(out, tmpvec3, Math.PI); + return out; + } else if (dot > 0.999999) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; + } else { + vec3.cross(tmpvec3, a, b); + out[0] = tmpvec3[0]; + out[1] = tmpvec3[1]; + out[2] = tmpvec3[2]; + out[3] = 1 + dot; + return quat.normalize(out, out); + } + }; + })(); + + /** + * Sets the specified quaternion with values corresponding to the given + * axes. Each axis is a vec3 and is expected to be unit length and + * perpendicular to all other specified axes. + * + * @param {vec3} view the vector representing the viewing direction + * @param {vec3} right the vector representing the local "right" direction + * @param {vec3} up the vector representing the local "up" direction + * @returns {quat} out + */ + quat.setAxes = (function() { + var matr = mat3.create(); + + return function(out, view, right, up) { + matr[0] = right[0]; + matr[3] = right[1]; + matr[6] = right[2]; + + matr[1] = up[0]; + matr[4] = up[1]; + matr[7] = up[2]; + + matr[2] = -view[0]; + matr[5] = -view[1]; + matr[8] = -view[2]; + + return quat.normalize(out, quat.fromMat3(out, matr)); + }; + })(); + + /** + * Creates a new quat initialized with values from an existing quaternion + * + * @param {quat} a quaternion to clone + * @returns {quat} a new quaternion + * @function + */ + quat.clone = vec4.clone; + + /** + * Creates a new quat initialized with the given values + * + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {quat} a new quaternion + * @function + */ + quat.fromValues = vec4.fromValues; + + /** + * Copy the values from one quat to another + * + * @param {quat} out the receiving quaternion + * @param {quat} a the source quaternion + * @returns {quat} out + * @function + */ + quat.copy = vec4.copy; + + /** + * Set the components of a quat to the given values + * + * @param {quat} out the receiving quaternion + * @param {Number} x X component + * @param {Number} y Y component + * @param {Number} z Z component + * @param {Number} w W component + * @returns {quat} out + * @function + */ + quat.set = vec4.set; + + /** + * Set a quat to the identity quaternion + * + * @param {quat} out the receiving quaternion + * @returns {quat} out + */ + quat.identity = function(out) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; + }; + + /** + * Sets a quat from the given angle and rotation axis, + * then returns it. + * + * @param {quat} out the receiving quaternion + * @param {vec3} axis the axis around which to rotate + * @param {Number} rad the angle in radians + * @returns {quat} out + **/ + quat.setAxisAngle = function(out, axis, rad) { + rad = rad * 0.5; + var s = Math.sin(rad); + out[0] = s * axis[0]; + out[1] = s * axis[1]; + out[2] = s * axis[2]; + out[3] = Math.cos(rad); + return out; + }; + + /** + * Adds two quat's + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @returns {quat} out + * @function + */ + quat.add = vec4.add; + + /** + * Multiplies two quat's + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @returns {quat} out + */ + quat.multiply = function(out, a, b) { + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bx = b[0], by = b[1], bz = b[2], bw = b[3]; + + out[0] = ax * bw + aw * bx + ay * bz - az * by; + out[1] = ay * bw + aw * by + az * bx - ax * bz; + out[2] = az * bw + aw * bz + ax * by - ay * bx; + out[3] = aw * bw - ax * bx - ay * by - az * bz; + return out; + }; + + /** + * Alias for {@link quat.multiply} + * @function + */ + quat.mul = quat.multiply; + + /** + * Scales a quat by a scalar number + * + * @param {quat} out the receiving vector + * @param {quat} a the vector to scale + * @param {Number} b amount to scale the vector by + * @returns {quat} out + * @function + */ + quat.scale = vec4.scale; + + /** + * Rotates a quaternion by the given angle about the X axis + * + * @param {quat} out quat receiving operation result + * @param {quat} a quat to rotate + * @param {number} rad angle (in radians) to rotate + * @returns {quat} out + */ + quat.rotateX = function (out, a, rad) { + rad *= 0.5; + + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bx = Math.sin(rad), bw = Math.cos(rad); + + out[0] = ax * bw + aw * bx; + out[1] = ay * bw + az * bx; + out[2] = az * bw - ay * bx; + out[3] = aw * bw - ax * bx; + return out; + }; + + /** + * Rotates a quaternion by the given angle about the Y axis + * + * @param {quat} out quat receiving operation result + * @param {quat} a quat to rotate + * @param {number} rad angle (in radians) to rotate + * @returns {quat} out + */ + quat.rotateY = function (out, a, rad) { + rad *= 0.5; + + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + by = Math.sin(rad), bw = Math.cos(rad); + + out[0] = ax * bw - az * by; + out[1] = ay * bw + aw * by; + out[2] = az * bw + ax * by; + out[3] = aw * bw - ay * by; + return out; + }; + + /** + * Rotates a quaternion by the given angle about the Z axis + * + * @param {quat} out quat receiving operation result + * @param {quat} a quat to rotate + * @param {number} rad angle (in radians) to rotate + * @returns {quat} out + */ + quat.rotateZ = function (out, a, rad) { + rad *= 0.5; + + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bz = Math.sin(rad), bw = Math.cos(rad); + + out[0] = ax * bw + ay * bz; + out[1] = ay * bw - ax * bz; + out[2] = az * bw + aw * bz; + out[3] = aw * bw - az * bz; + return out; + }; + + /** + * Calculates the W component of a quat from the X, Y, and Z components. + * Assumes that quaternion is 1 unit in length. + * Any existing W component will be ignored. + * + * @param {quat} out the receiving quaternion + * @param {quat} a quat to calculate W component of + * @returns {quat} out + */ + quat.calculateW = function (out, a) { + var x = a[0], y = a[1], z = a[2]; + + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z)); + return out; + }; + + /** + * Calculates the dot product of two quat's + * + * @param {quat} a the first operand + * @param {quat} b the second operand + * @returns {Number} dot product of a and b + * @function + */ + quat.dot = vec4.dot; + + /** + * Performs a linear interpolation between two quat's + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {quat} out + * @function + */ + quat.lerp = vec4.lerp; + + /** + * Performs a spherical linear interpolation between two quat + * + * @param {quat} out the receiving quaternion + * @param {quat} a the first operand + * @param {quat} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {quat} out + */ + quat.slerp = function (out, a, b, t) { + // benchmarks: + // http://jsperf.com/quaternion-slerp-implementations + + var ax = a[0], ay = a[1], az = a[2], aw = a[3], + bx = b[0], by = b[1], bz = b[2], bw = b[3]; + + var omega, cosom, sinom, scale0, scale1; + + // calc cosine + cosom = ax * bx + ay * by + az * bz + aw * bw; + // adjust signs (if necessary) + if ( cosom < 0.0 ) { + cosom = -cosom; + bx = - bx; + by = - by; + bz = - bz; + bw = - bw; + } + // calculate coefficients + if ( (1.0 - cosom) > 0.000001 ) { + // standard case (slerp) + omega = Math.acos(cosom); + sinom = Math.sin(omega); + scale0 = Math.sin((1.0 - t) * omega) / sinom; + scale1 = Math.sin(t * omega) / sinom; + } else { + // "from" and "to" quaternions are very close + // ... so we can do a linear interpolation + scale0 = 1.0 - t; + scale1 = t; + } + // calculate final values + out[0] = scale0 * ax + scale1 * bx; + out[1] = scale0 * ay + scale1 * by; + out[2] = scale0 * az + scale1 * bz; + out[3] = scale0 * aw + scale1 * bw; + + return out; + }; + + /** + * Calculates the inverse of a quat + * + * @param {quat} out the receiving quaternion + * @param {quat} a quat to calculate inverse of + * @returns {quat} out + */ + quat.invert = function(out, a) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], + dot = a0*a0 + a1*a1 + a2*a2 + a3*a3, + invDot = dot ? 1.0/dot : 0; + + // TODO: Would be faster to return [0,0,0,0] immediately if dot == 0 + + out[0] = -a0*invDot; + out[1] = -a1*invDot; + out[2] = -a2*invDot; + out[3] = a3*invDot; + return out; + }; + + /** + * Calculates the conjugate of a quat + * If the quaternion is normalized, this function is faster than quat.inverse and produces the same result. + * + * @param {quat} out the receiving quaternion + * @param {quat} a quat to calculate conjugate of + * @returns {quat} out + */ + quat.conjugate = function (out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + out[3] = a[3]; + return out; + }; + + /** + * Calculates the length of a quat + * + * @param {quat} a vector to calculate length of + * @returns {Number} length of a + * @function + */ + quat.length = vec4.length; + + /** + * Alias for {@link quat.length} + * @function + */ + quat.len = quat.length; + + /** + * Calculates the squared length of a quat + * + * @param {quat} a vector to calculate squared length of + * @returns {Number} squared length of a + * @function + */ + quat.squaredLength = vec4.squaredLength; + + /** + * Alias for {@link quat.squaredLength} + * @function + */ + quat.sqrLen = quat.squaredLength; + + /** + * Normalize a quat + * + * @param {quat} out the receiving quaternion + * @param {quat} a quaternion to normalize + * @returns {quat} out + * @function + */ + quat.normalize = vec4.normalize; + + /** + * Creates a quaternion from the given 3x3 rotation matrix. + * + * NOTE: The resultant quaternion is not normalized, so you should be sure + * to renormalize the quaternion yourself where necessary. + * + * @param {quat} out the receiving quaternion + * @param {mat3} m rotation matrix + * @returns {quat} out + * @function + */ + quat.fromMat3 = function(out, m) { + // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes + // article "Quaternion Calculus and Fast Animation". + var fTrace = m[0] + m[4] + m[8]; + var fRoot; + + if ( fTrace > 0.0 ) { + // |w| > 1/2, may as well choose w > 1/2 + fRoot = Math.sqrt(fTrace + 1.0); // 2w + out[3] = 0.5 * fRoot; + fRoot = 0.5/fRoot; // 1/(4w) + out[0] = (m[5]-m[7])*fRoot; + out[1] = (m[6]-m[2])*fRoot; + out[2] = (m[1]-m[3])*fRoot; + } else { + // |w| <= 1/2 + var i = 0; + if ( m[4] > m[0] ) + i = 1; + if ( m[8] > m[i*3+i] ) + i = 2; + var j = (i+1)%3; + var k = (i+2)%3; + + fRoot = Math.sqrt(m[i*3+i]-m[j*3+j]-m[k*3+k] + 1.0); + out[i] = 0.5 * fRoot; + fRoot = 0.5 / fRoot; + out[3] = (m[j*3+k] - m[k*3+j]) * fRoot; + out[j] = (m[j*3+i] + m[i*3+j]) * fRoot; + out[k] = (m[k*3+i] + m[i*3+k]) * fRoot; + } + + return out; + }; + + /** + * Returns a string representation of a quatenion + * + * @param {quat} vec vector to represent as a string + * @returns {String} string representation of the vector + */ + quat.str = function (a) { + return 'quat(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')'; + }; + + if(typeof(exports) !== 'undefined') { + exports.quat = quat; + } + ; + + + + + + + + + + + + + + })(shim.exports); + })(this); + +/***/ }, +/* 15 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + // Sampler clip is especially for the animation sampler in glTF + // Use Typed Array can reduce a lot of heap memory + + + var Clip = __webpack_require__(8); + var TransformClip = __webpack_require__(16); + + var glMatrix = __webpack_require__(14); + var quat = glMatrix.quat; + var vec3 = glMatrix.vec3; + + // lerp function with offset in large array + function vec3lerp(out, a, b, t, oa, ob) { + var ax = a[oa]; + var ay = a[oa + 1]; + var az = a[oa + 2]; + out[0] = ax + t * (b[ob] - ax); + out[1] = ay + t * (b[ob + 1] - ay); + out[2] = az + t * (b[ob + 2] - az); + + return out; + } + + function quatSlerp(out, a, b, t, oa, ob) { + // benchmarks: + // http://jsperf.com/quaternion-slerp-implementations + + var ax = a[0 + oa], ay = a[1 + oa], az = a[2 + oa], aw = a[3 + oa], + bx = b[0 + ob], by = b[1 + ob], bz = b[2 + ob], bw = b[3 + ob]; + + var omega, cosom, sinom, scale0, scale1; + + // calc cosine + cosom = ax * bx + ay * by + az * bz + aw * bw; + // adjust signs (if necessary) + if (cosom < 0.0) { + cosom = -cosom; + bx = - bx; + by = - by; + bz = - bz; + bw = - bw; + } + // calculate coefficients + if ((1.0 - cosom) > 0.000001) { + // standard case (slerp) + omega = Math.acos(cosom); + sinom = Math.sin(omega); + scale0 = Math.sin((1.0 - t) * omega) / sinom; + scale1 = Math.sin(t * omega) / sinom; + } + else { + // 'from' and 'to' quaternions are very close + // ... so we can do a linear interpolation + scale0 = 1.0 - t; + scale1 = t; + } + // calculate final values + out[0] = scale0 * ax + scale1 * bx; + out[1] = scale0 * ay + scale1 * by; + out[2] = scale0 * az + scale1 * bz; + out[3] = scale0 * aw + scale1 * bw; + + return out; + } + + /** + * @constructor + * @alias qtek.animation.SamplerClip + * @extends qtek.animation.Clip + * + * @param {Object} [opts] + * @param {string} [opts.name] + * @param {Object} [opts.target] + * @param {number} [opts.life] + * @param {number} [opts.delay] + * @param {number} [opts.gap] + * @param {number} [opts.playbackRatio] + * @param {boolean|number} [opts.loop] If loop is a number, it indicate the loop count of animation + * @param {string|Function} [opts.easing] + * @param {Function} [opts.onframe] + * @param {Function} [opts.onfinish] + * @param {Function} [opts.onrestart] + */ + var SamplerClip = function(opts) { + + opts = opts || {}; + + Clip.call(this, opts); + + /** + * @type {Float32Array} + */ + this.position = vec3.create(); + /** + * Rotation is represented by a quaternion + * @type {Float32Array} + */ + this.rotation = quat.create(); + /** + * @type {Float32Array} + */ + this.scale = vec3.fromValues(1, 1, 1); + + this.channels = { + time: null, + position: null, + rotation: null, + scale: null + }; + + this._cacheKey = 0; + this._cacheTime = 0; + }; + + SamplerClip.prototype = Object.create(Clip.prototype); + + SamplerClip.prototype.constructor = SamplerClip; + + SamplerClip.prototype.step = function (time) { + + var ret = Clip.prototype.step.call(this, time); + + if (ret !== 'finish') { + this.setTime(this._elapsedTime); + } + + return ret; + }; + + SamplerClip.prototype.setTime = function (time) { + if (!this.channels.time) { + return; + } + var channels = this.channels; + var len = channels.time.length; + var key = -1; + if (time < this._cacheTime) { + var s = Math.min(len - 2, this._cacheKey); + for (var i = s; i >= 0; i--) { + if (channels.time[i - 1] <= time && channels.time[i] > time) { + key = i - 1; + break; + } + } + } else { + for (var i = this._cacheKey; i < len-1; i++) { + if (channels.time[i] <= time && channels.time[i + 1] > time) { + key = i; + break; + } + } + } + + if (key > -1) { + this._cacheKey = key; + this._cacheTime = time; + var start = key; + var end = key + 1; + var startTime = channels.time[start]; + var endTime = channels.time[end]; + var percent = (time - startTime) / (endTime - startTime); + + if (channels.rotation) { + quatSlerp(this.rotation, channels.rotation, channels.rotation, percent, start * 4, end * 4); + } + if (channels.position) { + vec3lerp(this.position, channels.position, channels.position, percent, start * 3, end * 3); + } + if (channels.scale) { + vec3lerp(this.scale, channels.scale, channels.scale, percent, start * 3, end * 3); + } + } + // Loop handling + if (key == len - 2) { + this._cacheKey = 0; + this._cacheTime = 0; + } + }; + + /** + * @param {number} startTime + * @param {number} endTime + * @return {qtek.animation.SamplerClip} + */ + SamplerClip.prototype.getSubClip = function(startTime, endTime) { + + var subClip = new SamplerClip({ + name: this.name + }); + var minTime = this.channels.time[0]; + startTime = Math.min(Math.max(startTime, minTime), this.life); + endTime = Math.min(Math.max(endTime, minTime), this.life); + + var rangeStart = this._findRange(startTime); + var rangeEnd = this._findRange(endTime); + + var count = rangeEnd[0] - rangeStart[0] + 1; + if (rangeStart[1] === 0 && rangeEnd[1] === 0) { + count -= 1; + } + if (this.channels.rotation) { + subClip.channels.rotation = new Float32Array(count * 4); + } + if (this.channels.position) { + subClip.channels.position = new Float32Array(count * 3); + } + if (this.channels.scale) { + subClip.channels.scale = new Float32Array(count * 3); + } + if (this.channels.time) { + subClip.channels.time = new Float32Array(count); + } + // Clip at the start + this.setTime(startTime); + for (var i = 0; i < 3; i++) { + subClip.channels.rotation[i] = this.rotation[i]; + subClip.channels.position[i] = this.position[i]; + subClip.channels.scale[i] = this.scale[i]; + } + subClip.channels.time[0] = 0; + subClip.channels.rotation[3] = this.rotation[3]; + + for (var i = 1; i < count-1; i++) { + var i2; + for (var j = 0; j < 3; j++) { + i2 = rangeStart[0] + i; + subClip.channels.rotation[i * 4 + j] = this.channels.rotation[i2 * 4 + j]; + subClip.channels.position[i * 3 + j] = this.channels.position[i2 * 3 + j]; + subClip.channels.scale[i * 3 + j] = this.channels.scale[i2 * 3 + j]; + } + subClip.channels.time[i] = this.channels.time[i2] - startTime; + subClip.channels.rotation[i * 4 + 3] = this.channels.rotation[i2 * 4 + 3]; + } + // Clip at the end + this.setTime(endTime); + for (var i = 0; i < 3; i++) { + subClip.channels.rotation[(count - 1) * 4 + i] = this.rotation[i]; + subClip.channels.position[(count - 1) * 3 + i] = this.position[i]; + subClip.channels.scale[(count - 1) * 3 + i] = this.scale[i]; + } + subClip.channels.time[(count - 1)] = endTime - startTime; + subClip.channels.rotation[(count - 1) * 4 + 3] = this.rotation[3]; + + // TODO set back ? + subClip.life = endTime - startTime; + return subClip; + }; + + SamplerClip.prototype._findRange = function(time) { + var channels = this.channels; + var len = channels.time.length; + var start = -1; + for (var i = 0; i < len - 1; i++) { + if (channels.time[i] <= time && channels.time[i+1] > time) { + start = i; + } + } + var percent = 0; + if (start >= 0) { + var startTime = channels.time[start]; + var endTime = channels.time[start+1]; + var percent = (time-startTime) / (endTime-startTime); + } + // Percent [0, 1) + return [start, percent]; + }; + + /** + * 1D blending between two clips + * @method + * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 + * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 + * @param {number} w + */ + SamplerClip.prototype.blend1D = TransformClip.prototype.blend1D; + /** + * 2D blending between three clips + * @method + * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 + * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 + * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c3 + * @param {number} f + * @param {number} g + */ + SamplerClip.prototype.blend2D = TransformClip.prototype.blend2D; + /** + * Additive blending between two clips + * @method + * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 + * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 + */ + SamplerClip.prototype.additiveBlend = TransformClip.prototype.additiveBlend; + /** + * Subtractive blending between two clips + * @method + * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 + * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 + */ + SamplerClip.prototype.subtractiveBlend = TransformClip.prototype.subtractiveBlend; + + /** + * Clone a new SamplerClip + * @return {qtek.animation.SamplerClip} + */ + SamplerClip.prototype.clone = function () { + var clip = Clip.prototype.clone.call(this); + clip.channels = { + time: this.channels.time || null, + position: this.channels.position || null, + rotation: this.channels.rotation || null, + scale: this.channels.scale || null + }; + vec3.copy(clip.position, this.position); + quat.copy(clip.rotation, this.rotation); + vec3.copy(clip.scale, this.scale); + + return clip; + + }; + + module.exports = SamplerClip; + + +/***/ }, +/* 16 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Clip = __webpack_require__(8); + + var glMatrix = __webpack_require__(14); + var quat = glMatrix.quat; + var vec3 = glMatrix.vec3; + + function keyframeSort(a, b) { + return a.time - b.time; + } + + /** + * @constructor + * @alias qtek.animation.TransformClip + * @extends qtek.animation.Clip + * + * @param {Object} [opts] + * @param {string} [opts.name] + * @param {Object} [opts.target] + * @param {number} [opts.life] + * @param {number} [opts.delay] + * @param {number} [opts.gap] + * @param {number} [opts.playbackRatio] + * @param {boolean|number} [opts.loop] If loop is a number, it indicate the loop count of animation + * @param {string|Function} [opts.easing] + * @param {Function} [opts.onframe] + * @param {Function} [opts.onfinish] + * @param {Function} [opts.onrestart] + * @param {object[]} [opts.keyFrames] + */ + var TransformClip = function(opts) { + + opts = opts || {}; + + Clip.call(this, opts); + + //[{ + // time: //ms + // position: // optional + // rotation: // optional + // scale: // optional + //}] + this.keyFrames = []; + if (opts.keyFrames) { + this.addKeyFrames(opts.keyFrames); + } + + /** + * @type {Float32Array} + */ + this.position = vec3.create(); + /** + * Rotation is represented by a quaternion + * @type {Float32Array} + */ + this.rotation = quat.create(); + /** + * @type {Float32Array} + */ + this.scale = vec3.fromValues(1, 1, 1); + + this._cacheKey = 0; + this._cacheTime = 0; + }; + + TransformClip.prototype = Object.create(Clip.prototype); + + TransformClip.prototype.constructor = TransformClip; + + TransformClip.prototype.step = function(time) { + + var ret = Clip.prototype.step.call(this, time); + + if (ret !== 'finish') { + this.setTime(this._elapsedTime); + } + + return ret; + }; + + TransformClip.prototype.setTime = function(time) { + this._interpolateField(time, 'position'); + this._interpolateField(time, 'rotation'); + this._interpolateField(time, 'scale'); + }; + /** + * Add a key frame + * @param {Object} kf + */ + TransformClip.prototype.addKeyFrame = function(kf) { + for (var i = 0; i < this.keyFrames.length - 1; i++) { + var prevFrame = this.keyFrames[i]; + var nextFrame = this.keyFrames[i+1]; + if (prevFrame.time <= kf.time && nextFrame.time >= kf.time) { + this.keyFrames.splice(i, 0, kf); + return i; + } + } + + this.life = kf.time; + this.keyFrames.push(kf); + }; + + /** + * Add keyframes + * @param {object[]} kfs + */ + TransformClip.prototype.addKeyFrames = function(kfs) { + for (var i = 0; i < kfs.length; i++) { + this.keyFrames.push(kfs[i]); + } + + this.keyFrames.sort(keyframeSort); + + this.life = this.keyFrames[this.keyFrames.length - 1].time; + }; + + TransformClip.prototype._interpolateField = function(time, fieldName) { + var kfs = this.keyFrames; + var len = kfs.length; + var start; + var end; + + if (!kfs.length) { + return; + } + if (time < kfs[0].time || time > kfs[kfs.length-1].time) { + return; + } + if (time < this._cacheTime) { + var s = this._cacheKey >= len-1 ? len-1 : this._cacheKey+1; + for (var i = s; i >= 0; i--) { + if (kfs[i].time <= time && kfs[i][fieldName]) { + start = kfs[i]; + this._cacheKey = i; + this._cacheTime = time; + } else if (kfs[i][fieldName]) { + end = kfs[i]; + break; + } + } + } else { + for (var i = this._cacheKey; i < len; i++) { + if (kfs[i].time <= time && kfs[i][fieldName]) { + start = kfs[i]; + this._cacheKey = i; + this._cacheTime = time; + } else if (kfs[i][fieldName]) { + end = kfs[i]; + break; + } + } + } + + if (start && end) { + var percent = (time-start.time) / (end.time-start.time); + percent = Math.max(Math.min(percent, 1), 0); + if (fieldName === 'rotation') { + quat.slerp(this[fieldName], start[fieldName], end[fieldName], percent); + } else { + vec3.lerp(this[fieldName], start[fieldName], end[fieldName], percent); + } + } else { + this._cacheKey = 0; + this._cacheTime = 0; + } + }; + /** + * 1D blending between two clips + * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 + * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 + * @param {number} w + */ + TransformClip.prototype.blend1D = function(c1, c2, w) { + vec3.lerp(this.position, c1.position, c2.position, w); + vec3.lerp(this.scale, c1.scale, c2.scale, w); + quat.slerp(this.rotation, c1.rotation, c2.rotation, w); + }; + + /** + * 2D blending between three clips + * @method + * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 + * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 + * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c3 + * @param {number} f + * @param {number} g + */ + TransformClip.prototype.blend2D = (function() { + var q1 = quat.create(); + var q2 = quat.create(); + return function(c1, c2, c3, f, g) { + var a = 1 - f - g; + + this.position[0] = c1.position[0] * a + c2.position[0] * f + c3.position[0] * g; + this.position[1] = c1.position[1] * a + c2.position[1] * f + c3.position[1] * g; + this.position[2] = c1.position[2] * a + c2.position[2] * f + c3.position[2] * g; + + this.scale[0] = c1.scale[0] * a + c2.scale[0] * f + c3.scale[0] * g; + this.scale[1] = c1.scale[1] * a + c2.scale[1] * f + c3.scale[1] * g; + this.scale[2] = c1.scale[2] * a + c2.scale[2] * f + c3.scale[2] * g; + + // http://msdn.microsoft.com/en-us/library/windows/desktop/bb205403(v=vs.85).aspx + // http://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.quaternion.xmquaternionbarycentric(v=vs.85).aspx + var s = f + g; + if (s === 0) { + quat.copy(this.rotation, c1.rotation); + } else { + quat.slerp(q1, c1.rotation, c2.rotation, s); + quat.slerp(q2, c1.rotation, c3.rotation, s); + quat.slerp(this.rotation, q1, q2, g / s); + } + }; + })(); + + /** + * Additive blending between two clips + * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 + * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 + */ + TransformClip.prototype.additiveBlend = function(c1, c2) { + vec3.add(this.position, c1.position, c2.position); + vec3.add(this.scale, c1.scale, c2.scale); + quat.multiply(this.rotation, c2.rotation, c1.rotation); + }; + + /** + * Subtractive blending between two clips + * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c1 + * @param {qtek.animation.SamplerClip|qtek.animation.TransformClip} c2 + */ + TransformClip.prototype.subtractiveBlend = function(c1, c2) { + vec3.sub(this.position, c1.position, c2.position); + vec3.sub(this.scale, c1.scale, c2.scale); + quat.invert(this.rotation, c2.rotation); + quat.multiply(this.rotation, this.rotation, c1.rotation); + }; + + /** + * @param {number} startTime + * @param {number} endTime + * @param {boolean} isLoop + */ + TransformClip.prototype.getSubClip = function(startTime, endTime) { + // TODO + console.warn('TODO'); + }; + + /** + * Clone a new TransformClip + * @return {qtek.animation.TransformClip} + */ + TransformClip.prototype.clone = function () { + var clip = Clip.prototype.clone.call(this); + clip.keyFrames = this.keyFrames; + + vec3.copy(clip.position, this.position); + quat.copy(clip.rotation, this.rotation); + vec3.copy(clip.scale, this.scale); + + return clip; + }; + + + module.exports = TransformClip; + + +/***/ }, +/* 17 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Clip = __webpack_require__(8); + + var TransformClip = __webpack_require__(16); + + var glMatrix = __webpack_require__(14); + var quat = glMatrix.quat; + var vec3 = glMatrix.vec3; + + /** + * @constructor + * @alias qtek.animation.SkinningClip + * + * @extends qtek.animation.Clip + * @param {Object} [opts] + * @param {string} [opts.name] + * @param {Object} [opts.target] + * @param {number} [opts.life] + * @param {number} [opts.delay] + * @param {number} [opts.gap] + * @param {number} [opts.playbackRatio] + * @param {boolean|number} [opts.loop] If loop is a number, it indicate the loop count of animation + * @param {string|function} [opts.easing] + * @param {function} [opts.onframe] + * @param {function} [opts.onfinish] + * @param {function} [opts.onrestart] + */ + var SkinningClip = function(opts) { + + opts = opts || {}; + + Clip.call(this, opts); + + this.jointClips = []; + + this.life = 0; + if (opts.jointClips && opts.jointClips.length > 0) { + for (var j = 0; j < opts.jointClips.length; j++) { + var jointPoseCfg = opts.jointClips[j]; + var jointClip = new TransformClip({ + keyFrames: jointPoseCfg.keyFrames + }); + jointClip.name = jointPoseCfg.name || ''; + this.jointClips[j] = jointClip; + + this.life = Math.max(jointClip.life, this.life); + } + } + }; + + SkinningClip.prototype = Object.create(Clip.prototype); + + SkinningClip.prototype.constructor = SkinningClip; + + SkinningClip.prototype.step = function(time) { + + var ret = Clip.prototype.step.call(this, time); + + if (ret !== 'finish') { + this.setTime(this._elapsedTime); + } + + return ret; + }; + + SkinningClip.prototype.setTime = function(time) { + for (var i = 0; i < this.jointClips.length; i++) { + this.jointClips[i].setTime(time); + } + }; + + /** + * @param {qtek.animation.TransformClip|qtek.animation.SamplerClip} jointClip + */ + SkinningClip.prototype.addJointClip = function(jointClip) { + this.jointClips.push(jointClip); + this.life = Math.max(jointClip.life, this.life); + }; + + /** + * @param {qtek.animation.TransformClip|qtek.animation.SamplerClip} jointClip + */ + SkinningClip.prototype.removeJointClip = function(jointClip) { + this.jointClips.splice(this.jointClips.indexOf(jointClip), 1); + }; + + /** + * @param {number} startTime + * @param {number} endTime + * @param {boolean} isLoop + * @return {qtek.animation.SkinningClip} + */ + SkinningClip.prototype.getSubClip = function(startTime, endTime, isLoop) { + var subClip = new SkinningClip({ + name: this.name + }); + + for (var i = 0; i < this.jointClips.length; i++) { + var subJointClip = this.jointClips[i].getSubClip(startTime, endTime); + subClip.addJointClip(subJointClip); + } + + if (isLoop !== undefined) { + subClip.setLoop(isLoop); + } + + return subClip; + }; + + /** + * 1d blending between two skinning clips + * @param {qtek.animation.SkinningClip} clip1 + * @param {qtek.animation.SkinningClip} clip2 + * @param {number} w + */ + SkinningClip.prototype.blend1D = function(clip1, clip2, w) { + for (var i = 0; i < this.jointClips.length; i++) { + var c1 = clip1.jointClips[i]; + var c2 = clip2.jointClips[i]; + var tClip = this.jointClips[i]; + + tClip.blend1D(c1, c2, w); + } + }; + + /** + * Additive blending between two skinning clips + * @param {qtek.animation.SkinningClip} clip1 + * @param {qtek.animation.SkinningClip} clip2 + */ + SkinningClip.prototype.additiveBlend = function(clip1, clip2) { + for (var i = 0; i < this.jointClips.length; i++) { + var c1 = clip1.jointClips[i]; + var c2 = clip2.jointClips[i]; + var tClip = this.jointClips[i]; + + tClip.additiveBlend(c1, c2); + } + }; + + /** + * Subtractive blending between two skinning clips + * @param {qtek.animation.SkinningClip} clip1 + * @param {qtek.animation.SkinningClip} clip2 + */ + SkinningClip.prototype.subtractiveBlend = function(clip1, clip2) { + for (var i = 0; i < this.jointClips.length; i++) { + var c1 = clip1.jointClips[i]; + var c2 = clip2.jointClips[i]; + var tClip = this.jointClips[i]; + + tClip.subtractiveBlend(c1, c2); + } + }; + + /** + * 2D blending between three skinning clips + * @param {qtek.animation.SkinningClip} clip1 + * @param {qtek.animation.SkinningClip} clip2 + * @param {qtek.animation.SkinningClip} clip3 + * @param {number} f + * @param {number} g + */ + SkinningClip.prototype.blend2D = function(clip1, clip2, clip3, f, g) { + for (var i = 0; i < this.jointClips.length; i++) { + var c1 = clip1.jointClips[i]; + var c2 = clip2.jointClips[i]; + var c3 = clip3.jointClips[i]; + var tClip = this.jointClips[i]; + + tClip.blend2D(c1, c2, c3, f, g); + } + }; + + /** + * Copy SRT of all joints clips from another SkinningClip + * @param {qtek.animation.SkinningClip} clip + */ + SkinningClip.prototype.copy = function(clip) { + for (var i = 0; i < this.jointClips.length; i++) { + var sClip = clip.jointClips[i]; + var tClip = this.jointClips[i]; + + vec3.copy(tClip.position, sClip.position); + vec3.copy(tClip.scale, sClip.scale); + quat.copy(tClip.rotation, sClip.rotation); + } + }; + + SkinningClip.prototype.clone = function () { + var clip = Clip.prototype.clone.call(this); + for (var i = 0; i < this.jointClips.length; i++) { + clip.addJointClip(this.jointClips[i].clone()); + } + return clip; + }; + + module.exports = SkinningClip; + + +/***/ }, +/* 18 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var notifier = __webpack_require__(5); + var request = __webpack_require__(19); + var util = __webpack_require__(6); + + /** + * @constructor + * @alias qtek.async.Task + * @mixes qtek.core.mixin.notifier + */ + var Task = function() { + this._fullfilled = false; + this._rejected = false; + }; + /** + * Task successed + * @param {} data + */ + Task.prototype.resolve = function(data) { + this._fullfilled = true; + this._rejected = false; + this.trigger('success', data); + }; + /** + * Task failed + * @param {} err + */ + Task.prototype.reject = function(err) { + this._rejected = true; + this._fullfilled = false; + this.trigger('error', err); + }; + /** + * If task successed + * @return {boolean} + */ + Task.prototype.isFullfilled = function() { + return this._fullfilled; + }; + /** + * If task failed + * @return {boolean} + */ + Task.prototype.isRejected = function() { + return this._rejected; + }; + /** + * If task finished, either successed or failed + * @return {boolean} + */ + Task.prototype.isSettled = function() { + return this._fullfilled || this._rejected; + }; + + util.extend(Task.prototype, notifier); + + function makeRequestTask(url, responseType) { + var task = new Task(); + request.get({ + url: url, + responseType: responseType, + onload: function(res) { + task.resolve(res); + }, + onerror: function(error) { + task.reject(error); + } + }); + return task; + } + /** + * Make a request task + * @param {string|object|object[]|string[]} url + * @param {string} [responseType] + * @example + * var task = Task.makeRequestTask('./a.json'); + * var task = Task.makeRequestTask({ + * url: 'b.bin', + * responseType: 'arraybuffer' + * }); + * var tasks = Task.makeRequestTask(['./a.json', './b.json']); + * var tasks = Task.makeRequestTask([ + * {url: 'a.json'}, + * {url: 'b.bin', responseType: 'arraybuffer'} + * ]); + * @return {qtek.async.Task|qtek.async.Task[]} + */ + Task.makeRequestTask = function(url, responseType) { + if (typeof url === 'string') { + return makeRequestTask(url, responseType); + } else if (url.url) { // Configure object + var obj = url; + return makeRequestTask(obj.url, obj.responseType); + } else if (url instanceof Array) { // Url list + var urlList = url; + var tasks = []; + urlList.forEach(function(obj) { + var url, responseType; + if (typeof obj === 'string') { + url = obj; + } else if (Object(obj) === obj) { + url = obj.url; + responseType = obj.responseType; + } + tasks.push(makeRequestTask(url, responseType)); + }); + return tasks; + } + }; + /** + * @return {qtek.async.Task} + */ + Task.makeTask = function() { + return new Task(); + }; + + util.extend(Task.prototype, notifier); + + module.exports = Task; + + +/***/ }, +/* 19 */ +/***/ function(module, exports) { + + 'use strict'; + + + function get(options) { + + var xhr = new XMLHttpRequest(); + + xhr.open('get', options.url); + // With response type set browser can get and put binary data + // https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest/Sending_and_Receiving_Binary_Data + // Default is text, and it can be set + // arraybuffer, blob, document, json, text + xhr.responseType = options.responseType || 'text'; + + if (options.onprogress) { + //https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest/Using_XMLHttpRequest + xhr.onprogress = function(e) { + if (e.lengthComputable) { + var percent = e.loaded / e.total; + options.onprogress(percent, e.loaded, e.total); + } else { + options.onprogress(null); + } + }; + } + xhr.onload = function(e) { + options.onload && options.onload(xhr.response); + }; + if (options.onerror) { + xhr.onerror = options.onerror; + } + xhr.send(null); + } + + module.exports = { + get : get + }; + + +/***/ }, +/* 20 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var util = __webpack_require__(6); + var Task = __webpack_require__(18); + + /** + * @constructor + * @alias qtek.async.TaskGroup + * @extends qtek.async.Task + */ + var TaskGroup = function () { + + Task.apply(this, arguments); + + this._tasks = []; + + this._fulfilledNumber = 0; + + this._rejectedNumber = 0; + }; + + var Ctor = function (){}; + Ctor.prototype = Task.prototype; + TaskGroup.prototype = new Ctor(); + + TaskGroup.prototype.constructor = TaskGroup; + + /** + * Wait for all given tasks successed, task can also be any notifier object which will trigger success and error events. Like {@link qtek.Texture2D}, {@link qtek.TextureCube}, {@link qtek.loader.GLTF}. + * @param {Array.} tasks + * @chainable + * @example + * // Load texture list + * var list = ['a.jpg', 'b.jpg', 'c.jpg'] + * var textures = list.map(function (src) { + * var texture = new qtek.Texture2D(); + * texture.load(src); + * return texture; + * }); + * var taskGroup = new qtek.async.TaskGroup(); + * taskGroup.all(textures).success(function () { + * // Do some thing after all textures loaded + * }); + */ + TaskGroup.prototype.all = function (tasks) { + var count = 0; + var self = this; + var data = []; + this._tasks = tasks; + this._fulfilledNumber = 0; + this._rejectedNumber = 0; + + util.each(tasks, function (task, idx) { + if (!task || !task.once) { + return; + } + count++; + task.once('success', function (res) { + count--; + + self._fulfilledNumber++; + // TODO + // Some tasks like texture, loader are not inherited from task + // We need to set the states here + task._fulfilled = true; + task._rejected = false; + + data[idx] = res; + if (count === 0) { + self.resolve(data); + } + }); + task.once('error', function () { + + self._rejectedNumber ++; + + task._fulfilled = false; + task._rejected = true; + + self.reject(task); + }); + }); + if (count === 0) { + setTimeout(function () { + self.resolve(data); + }); + return this; + } + return this; + }; + /** + * Wait for all given tasks finished, either successed or failed + * @param {Array.} tasks + * @return {qtek.async.TaskGroup} + */ + TaskGroup.prototype.allSettled = function (tasks) { + var count = 0; + var self = this; + var data = []; + if (tasks.length === 0) { + setTimeout(function () { + self.trigger('success', data); + }); + return this; + } + this._tasks = tasks; + + util.each(tasks, function (task, idx) { + if (!task || !task.once) { + return; + } + count++; + task.once('success', function (res) { + count--; + + self._fulfilledNumber++; + + task._fulfilled = true; + task._rejected = false; + + data[idx] = res; + if (count === 0) { + self.resolve(data); + } + }); + task.once('error', function (err) { + count--; + + self._rejectedNumber++; + + task._fulfilled = false; + task._rejected = true; + + // TODO + data[idx] = null; + if (count === 0) { + self.resolve(data); + } + }); + }); + return this; + }; + /** + * Get successed sub tasks number, recursive can be true if sub task is also a TaskGroup. + * @param {boolean} [recursive] + * @return {number} + */ + TaskGroup.prototype.getFulfilledNumber = function (recursive) { + if (recursive) { + var nFulfilled = 0; + for (var i = 0; i < this._tasks.length; i++) { + var task = this._tasks[i]; + if (task instanceof TaskGroup) { + nFulfilled += task.getFulfilledNumber(recursive); + } else if(task._fulfilled) { + nFulfilled += 1; + } + } + return nFulfilled; + } else { + return this._fulfilledNumber; + } + }; + + /** + * Get failed sub tasks number, recursive can be true if sub task is also a TaskGroup. + * @param {boolean} [recursive] + * @return {number} + */ + TaskGroup.prototype.getRejectedNumber = function (recursive) { + if (recursive) { + var nRejected = 0; + for (var i = 0; i < this._tasks.length; i++) { + var task = this._tasks[i]; + if (task instanceof TaskGroup) { + nRejected += task.getRejectedNumber(recursive); + } else if(task._rejected) { + nRejected += 1; + } + } + return nRejected; + } else { + return this._rejectedNumber; + } + }; + + /** + * Get finished sub tasks number, recursive can be true if sub task is also a TaskGroup. + * @param {boolean} [recursive] + * @return {number} + */ + TaskGroup.prototype.getSettledNumber = function (recursive) { + + if (recursive) { + var nSettled = 0; + for (var i = 0; i < this._tasks.length; i++) { + var task = this._tasks[i]; + if (task instanceof TaskGroup) { + nSettled += task.getSettledNumber(recursive); + } else if(task._rejected || task._fulfilled) { + nSettled += 1; + } + } + return nSettled; + } else { + return this._fulfilledNumber + this._rejectedNumber; + } + }; + + /** + * Get all sub tasks number, recursive can be true if sub task is also a TaskGroup. + * @param {boolean} [recursive] + * @return {number} + */ + TaskGroup.prototype.getTaskNumber = function (recursive) { + if (recursive) { + var nTask = 0; + for (var i = 0; i < this._tasks.length; i++) { + var task = this._tasks[i]; + if (task instanceof TaskGroup) { + nTask += task.getTaskNumber(recursive); + } else { + nTask += 1; + } + } + return nTask; + } else { + return this._tasks.length; + } + }; + + module.exports = TaskGroup; + + +/***/ }, +/* 21 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Node = __webpack_require__(22); + var Matrix4 = __webpack_require__(25); + var Frustum = __webpack_require__(27); + var Ray = __webpack_require__(29); + + var glMatrix = __webpack_require__(14); + var vec3 = glMatrix.vec3; + var vec4 = glMatrix.vec4; + + /** + * @constructor qtek.Camera + * @extends qtek.Node + */ + var Camera = Node.extend(function () { + return /** @lends qtek.Camera# */ { + /** + * Camera projection matrix + * @type {qtek.math.Matrix4} + */ + projectionMatrix: new Matrix4(), + + /** + * Inverse of camera projection matrix + * @type {qtek.math.Matrix4} + */ + invProjectionMatrix: new Matrix4(), + + /** + * View matrix, equal to inverse of camera's world matrix + * @type {qtek.math.Matrix4} + */ + viewMatrix: new Matrix4(), + + /** + * Camera frustum in view space + * @type {qtek.math.Frustum} + */ + frustum: new Frustum() + }; + }, function () { + this.update(true); + }, + /** @lends qtek.Camera.prototype */ + { + + update: function (force) { + Node.prototype.update.call(this, force); + Matrix4.invert(this.viewMatrix, this.worldTransform); + + this.updateProjectionMatrix(); + Matrix4.invert(this.invProjectionMatrix, this.projectionMatrix); + + this.frustum.setFromProjection(this.projectionMatrix); + }, + + /** + * Set camera view matrix + */ + setViewMatrix: function (viewMatrix) { + Matrix4.invert(this.worldTransform, viewMatrix); + this.decomposeWorldTransform(); + }, + + /** + * Decompose camera projection matrix + */ + decomposeProjectionMatrix: function () {}, + + /** + * Set camera projection matrix + */ + setProjectionMatrix: function (projectionMatrix) { + Matrix4.copy(this.projectionMatrix, projectionMatrix); + Matrix4.invert(this.invProjectionMatrix, projectionMatrix); + this.decomposeProjectionMatrix(); + }, + /** + * Update projection matrix, called after update + */ + updateProjectionMatrix: function () {}, + + /** + * Cast a picking ray from camera near plane to far plane + * @method + * @param {qtek.math.Vector2} ndc + * @param {qtek.math.Ray} [out] + * @return {qtek.math.Ray} + */ + castRay: (function () { + var v4 = vec4.create(); + return function (ndc, out) { + var ray = out !== undefined ? out : new Ray(); + var x = ndc._array[0]; + var y = ndc._array[1]; + vec4.set(v4, x, y, -1, 1); + vec4.transformMat4(v4, v4, this.invProjectionMatrix._array); + vec4.transformMat4(v4, v4, this.worldTransform._array); + vec3.scale(ray.origin._array, v4, 1 / v4[3]); + + vec4.set(v4, x, y, 1, 1); + vec4.transformMat4(v4, v4, this.invProjectionMatrix._array); + vec4.transformMat4(v4, v4, this.worldTransform._array); + vec3.scale(v4, v4, 1 / v4[3]); + vec3.sub(ray.direction._array, v4, ray.origin._array); + + vec3.normalize(ray.direction._array, ray.direction._array); + ray.direction._dirty = true; + ray.origin._dirty = true; + + return ray; + }; + })() + + /** + * @method + * @name clone + * @return {qtek.Camera} + * @memberOf qtek.Camera.prototype + */ + }); + + module.exports = Camera; + + +/***/ }, +/* 22 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Base = __webpack_require__(3); + var Vector3 = __webpack_require__(23); + var Quaternion = __webpack_require__(24); + var Matrix4 = __webpack_require__(25); + var glMatrix = __webpack_require__(14); + var BoundingBox = __webpack_require__(26); + var mat4 = glMatrix.mat4; + + var nameId = 0; + + /** + * @constructor qtek.Node + * @extends qtek.core.Base + */ + var Node = Base.extend( + /** @lends qtek.Node# */ + { + /** + * Scene node name + * @type {string} + */ + name: '', + + /** + * Position relative to its parent node. aka translation. + * @type {qtek.math.Vector3} + */ + position: null, + + /** + * Rotation relative to its parent node. Represented by a quaternion + * @type {qtek.math.Quaternion} + */ + rotation: null, + + /** + * Scale relative to its parent node + * @type {qtek.math.Vector3} + */ + scale: null, + + /** + * Affine transform matrix relative to its root scene. + * @type {qtek.math.Matrix4} + */ + worldTransform: null, + + /** + * Affine transform matrix relative to its parent node. + * Composited with position, rotation and scale. + * @type {qtek.math.Matrix4} + */ + localTransform: null, + + /** + * If the local transform is update from SRT(scale, rotation, translation, which is position here) each frame + * @type {boolean} + */ + autoUpdateLocalTransform: true, + + /** + * Parent of current scene node + * @type {?qtek.Node} + * @private + */ + _parent: null, + /** + * The root scene mounted. Null if it is a isolated node + * @type {?qtek.Scene} + * @private + */ + _scene: null, + + _needsUpdateWorldTransform: true, + + _inIterating: false, + + // Depth for transparent queue sorting + __depth: 0 + + }, function () { + + if (!this.name) { + this.name = (this.type || 'NODE') + '_' + (nameId++); + } + + if (!this.position) { + this.position = new Vector3(); + } + if (!this.rotation) { + this.rotation = new Quaternion(); + } + if (!this.scale) { + this.scale = new Vector3(1, 1, 1); + } + + this.worldTransform = new Matrix4(); + this.localTransform = new Matrix4(); + + this._children = []; + + }, + /**@lends qtek.Node.prototype. */ + { + + /** + * If node and its chilren invisible + * @type {boolean} + * @memberOf qtek.Node + * @instance + */ + invisible: false, + + /** + * Return true if it is a renderable scene node, like Mesh and ParticleSystem + * @return {boolean} + */ + isRenderable: function () { + return false; + }, + + /** + * Set the name of the scene node + * @param {string} name + */ + setName: function (name) { + var scene = this._scene; + if (scene) { + var nodeRepository = scene._nodeRepository; + delete nodeRepository[this.name]; + nodeRepository[name] = this; + } + this.name = name; + }, + + /** + * Add a child node + * @param {qtek.Node} node + */ + add: function (node) { + if (this._inIterating) { + console.warn('Add operation can cause unpredictable error when in iterating'); + } + var originalParent = node._parent; + if (originalParent === this) { + return; + } + if (originalParent) { + originalParent.remove(node); + } + node._parent = this; + this._children.push(node); + + var scene = this._scene; + if (scene && scene !== node.scene) { + node.traverse(this._addSelfToScene, this); + } + // Mark children needs update transform + // In case child are remove and added again after parent moved + node._needsUpdateWorldTransform = true; + }, + + /** + * Remove the given child scene node + * @param {qtek.Node} node + */ + remove: function (node) { + if (this._inIterating) { + console.warn('Remove operation can cause unpredictable error when in iterating'); + } + var children = this._children; + var idx = children.indexOf(node); + if (idx < 0) { + return; + } + + children.splice(idx, 1); + node._parent = null; + + if (this._scene) { + node.traverse(this._removeSelfFromScene, this); + } + }, + + /** + * Remove all children + */ + removeAll: function () { + var children = this._children; + + for (var idx = 0; idx < children.length; idx++) { + children[idx]._parent = null; + + if (this._scene) { + children[idx].traverse(this._removeSelfFromScene, this); + } + } + + this._children = []; + }, + + /** + * Get the scene mounted + * @return {qtek.Scene} + */ + getScene: function () { + return this._scene; + }, + + /** + * Get parent node + * @return {qtek.Scene} + */ + getParent: function () { + return this._parent; + }, + + _removeSelfFromScene: function (descendant) { + descendant._scene.removeFromScene(descendant); + descendant._scene = null; + }, + + _addSelfToScene: function (descendant) { + this._scene.addToScene(descendant); + descendant._scene = this._scene; + }, + + /** + * Return true if it is ancestor of the given scene node + * @param {qtek.Node} node + */ + isAncestor: function (node) { + var parent = node._parent; + while(parent) { + if (parent === this) { + return true; + } + parent = parent._parent; + } + return false; + }, + + /** + * Get a new created array of all its children nodes + * @return {qtek.Node[]} + */ + children: function () { + return this._children.slice(); + }, + + childAt: function (idx) { + return this._children[idx]; + }, + + /** + * Get first child with the given name + * @param {string} name + * @return {qtek.Node} + */ + getChildByName: function (name) { + var children = this._children; + for (var i = 0; i < children.length; i++) { + if (children[i].name === name) { + return children[i]; + } + } + }, + + /** + * Get first descendant have the given name + * @param {string} name + * @return {qtek.Node} + */ + getDescendantByName: function (name) { + var children = this._children; + for (var i = 0; i < children.length; i++) { + var child = children[i]; + if (child.name === name) { + return child; + } else { + var res = child.getDescendantByName(name); + if (res) { + return res; + } + } + } + }, + + /** + * Query descendant node by path + * @param {string} path + * @return {qtek.Node} + */ + queryNode: function (path) { + if (!path) { + return; + } + // TODO Name have slash ? + var pathArr = path.split('/'); + var current = this; + for (var i = 0; i < pathArr.length; i++) { + var name = pathArr[i]; + // Skip empty + if (!name) { + continue; + } + var found = false; + var children = current._children; + for (var j = 0; j < children.length; j++) { + var child = children[j]; + if (child.name === name) { + current = child; + found = true; + break; + } + } + // Early return if not found + if (!found) { + return; + } + } + + return current; + }, + + /** + * Get query path, relative to rootNode(default is scene) + * @return {string} + */ + getPath: function (rootNode) { + if (!this._parent) { + return '/'; + } + + var current = this._parent; + var path = this.name; + while (current._parent) { + path = current.name + '/' + path; + if (current._parent == rootNode) { + break; + } + current = current._parent; + } + if (!current._parent && rootNode) { + return null; + } + return path; + }, + + /** + * Depth first traverse all its descendant scene nodes and + * @param {Function} callback + * @param {Node} [context] + * @param {Function} [ctor] + */ + traverse: function (callback, context, ctor) { + + this._inIterating = true; + + if (ctor == null || this.constructor === ctor) { + callback.call(context, this); + } + var _children = this._children; + for(var i = 0, len = _children.length; i < len; i++) { + _children[i].traverse(callback, context, ctor); + } + + this._inIterating = false; + }, + + eachChild: function (callback, context, ctor) { + this._inIterating = true; + + var _children = this._children; + var noCtor = ctor == null; + for(var i = 0, len = _children.length; i < len; i++) { + var child = _children[i]; + if (noCtor || child.constructor === ctor) { + callback.call(context, child, i); + } + } + + this._inIterating = false; + }, + + /** + * Set the local transform and decompose to SRT + * @param {qtek.math.Matrix4} matrix + */ + setLocalTransform: function (matrix) { + mat4.copy(this.localTransform._array, matrix._array); + this.decomposeLocalTransform(); + }, + + /** + * Decompose the local transform to SRT + */ + decomposeLocalTransform: function (keepScale) { + var scale = !keepScale ? this.scale: null; + this.localTransform.decomposeMatrix(scale, this.rotation, this.position); + }, + + /** + * Set the world transform and decompose to SRT + * @param {qtek.math.Matrix4} matrix + */ + setWorldTransform: function (matrix) { + mat4.copy(this.worldTransform._array, matrix._array); + this.decomposeWorldTransform(); + }, + + /** + * Decompose the world transform to SRT + * @method + */ + decomposeWorldTransform: (function () { + + var tmp = mat4.create(); + + return function (keepScale) { + var localTransform = this.localTransform; + var worldTransform = this.worldTransform; + // Assume world transform is updated + if (this._parent) { + mat4.invert(tmp, this._parent.worldTransform._array); + mat4.multiply(localTransform._array, tmp, worldTransform._array); + } else { + mat4.copy(localTransform._array, worldTransform._array); + } + var scale = !keepScale ? this.scale: null; + localTransform.decomposeMatrix(scale, this.rotation, this.position); + }; + })(), + + transformNeedsUpdate: function () { + return this.position._dirty + || this.rotation._dirty + || this.scale._dirty; + }, + + /** + * Update local transform from SRT + * Notice that local transform will not be updated if _dirty mark of position, rotation, scale is all false + */ + updateLocalTransform: function () { + var position = this.position; + var rotation = this.rotation; + var scale = this.scale; + + if (this.transformNeedsUpdate()) { + var m = this.localTransform._array; + + // Transform order, scale->rotation->position + mat4.fromRotationTranslation(m, rotation._array, position._array); + + mat4.scale(m, m, scale._array); + + rotation._dirty = false; + scale._dirty = false; + position._dirty = false; + + this._needsUpdateWorldTransform = true; + } + }, + + /** + * Update world transform, assume its parent world transform have been updated + */ + updateWorldTransform: function () { + var localTransform = this.localTransform._array; + var worldTransform = this.worldTransform._array; + if (this._parent) { + mat4.multiplyAffine( + worldTransform, + this._parent.worldTransform._array, + localTransform + ); + } + else { + mat4.copy(worldTransform, localTransform); + } + }, + + /** + * Update local transform and world transform recursively + * @param {boolean} forceUpdateWorld + */ + update: function (forceUpdateWorld) { + if (this.autoUpdateLocalTransform) { + this.updateLocalTransform(); + } + else { + // Transform is manually setted + forceUpdateWorld = true; + } + + if (forceUpdateWorld || this._needsUpdateWorldTransform) { + this.updateWorldTransform(); + forceUpdateWorld = true; + this._needsUpdateWorldTransform = false; + } + + var children = this._children; + for(var i = 0, len = children.length; i < len; i++) { + children[i].update(forceUpdateWorld); + } + }, + + /** + * Get bounding box of node + * @param {Function} [filter] + * @param {qtek.math.BoundingBox} [out] + * @return {qtek.math.BoundingBox} + */ + getBoundingBox: (function () { + + function defaultFilter (el) { + return !el.invisible; + } + return function (filter, out) { + out = out || new BoundingBox(); + filter = filter || defaultFilter; + + var children = this._children; + if (children.length === 0) { + out.max.set(-Infinity, -Infinity, -Infinity); + out.min.set(Infinity, Infinity, Infinity); + } + + var tmpBBox = new BoundingBox(); + for (var i = 0; i < children.length; i++) { + var child = children[i]; + if (!filter(child)) { + continue; + } + child.getBoundingBox(filter, tmpBBox); + child.updateLocalTransform(); + if (tmpBBox.isFinite()) { + tmpBBox.applyTransform(child.localTransform); + } + if (i === 0) { + out.copy(tmpBBox); + } + else { + out.union(tmpBBox); + } + + } + + return out; + }; + })(), + + /** + * Get world position, extracted from world transform + * @param {qtek.math.Vector3} [out] + * @return {qtek.math.Vector3} + */ + getWorldPosition: function (out) { + // TODO If update when get worldTransform + if (this.transformNeedsUpdate()) { + // Find the root node which transform needs update; + var rootNodeDirty = this; + while (rootNodeDirty && rootNodeDirty.getParent() + && rootNodeDirty.getParent().transformNeedsUpdate() + ) { + rootNodeDirty = rootNodeDirty.getParent(); + } + rootNodeDirty.update(); + } + var m = this.worldTransform._array; + if (out) { + var arr = out._array; + arr[0] = m[12]; + arr[1] = m[13]; + arr[2] = m[14]; + return out; + } + else { + return new Vector3(m[12], m[13], m[14]); + } + }, + + // TODO Set world transform + + /** + * Clone a new node + * @return {Node} + */ + clone: function () { + var node = new this.constructor(); + var children = this._children; + + node.setName(this.name); + node.position.copy(this.position); + node.rotation.copy(this.rotation); + node.scale.copy(this.scale); + + for (var i = 0; i < children.length; i++) { + node.add(children[i].clone()); + } + return node; + }, + + /** + * Rotate the node around a axis by angle degrees, axis passes through point + * @param {qtek.math.Vector3} point Center point + * @param {qtek.math.Vector3} axis Center axis + * @param {number} angle Rotation angle + * @see http://docs.unity3d.com/Documentation/ScriptReference/Transform.RotateAround.html + * @method + */ + rotateAround: (function () { + var v = new Vector3(); + var RTMatrix = new Matrix4(); + + // TODO improve performance + return function (point, axis, angle) { + + v.copy(this.position).subtract(point); + + var localTransform = this.localTransform; + localTransform.identity(); + // parent node + localTransform.translate(point); + localTransform.rotate(angle, axis); + + RTMatrix.fromRotationTranslation(this.rotation, v); + localTransform.multiply(RTMatrix); + localTransform.scale(this.scale); + + this.decomposeLocalTransform(); + this._needsUpdateWorldTransform = true; + }; + })(), + + /** + * @param {qtek.math.Vector3} target + * @param {qtek.math.Vector3} [up] + * @see http://www.opengl.org/sdk/docs/man2/xhtml/gluLookAt.xml + * @method + */ + // TODO world space ? + lookAt: (function () { + var m = new Matrix4(); + return function (target, up) { + m.lookAt(this.position, target, up || this.localTransform.y).invert(); + this.setLocalTransform(m); + }; + })() + }); + + module.exports = Node; + + +/***/ }, +/* 23 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var glMatrix = __webpack_require__(14); + var vec3 = glMatrix.vec3; + + /** + * @constructor + * @alias qtek.math.Vector3 + * @param {number} x + * @param {number} y + * @param {number} z + */ + var Vector3 = function(x, y, z) { + + x = x || 0; + y = y || 0; + z = z || 0; + + /** + * Storage of Vector3, read and write of x, y, z will change the values in _array + * All methods also operate on the _array instead of x, y, z components + * @name _array + * @type {Float32Array} + */ + this._array = vec3.fromValues(x, y, z); + + /** + * Dirty flag is used by the Node to determine + * if the matrix is updated to latest + * @name _dirty + * @type {boolean} + */ + this._dirty = true; + }; + + Vector3.prototype = { + + constructor : Vector3, + + /** + * Add b to self + * @param {qtek.math.Vector3} b + * @return {qtek.math.Vector3} + */ + add: function (b) { + vec3.add(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Set x, y and z components + * @param {number} x + * @param {number} y + * @param {number} z + * @return {qtek.math.Vector3} + */ + set: function (x, y, z) { + this._array[0] = x; + this._array[1] = y; + this._array[2] = z; + this._dirty = true; + return this; + }, + + /** + * Set x, y and z components from array + * @param {Float32Array|number[]} arr + * @return {qtek.math.Vector3} + */ + setArray: function (arr) { + this._array[0] = arr[0]; + this._array[1] = arr[1]; + this._array[2] = arr[2]; + + this._dirty = true; + return this; + }, + + /** + * Clone a new Vector3 + * @return {qtek.math.Vector3} + */ + clone: function () { + return new Vector3(this.x, this.y, this.z); + }, + + /** + * Copy from b + * @param {qtek.math.Vector3} b + * @return {qtek.math.Vector3} + */ + copy: function (b) { + vec3.copy(this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Cross product of self and b, written to a Vector3 out + * @param {qtek.math.Vector3} a + * @param {qtek.math.Vector3} b + * @return {qtek.math.Vector3} + */ + cross: function (a, b) { + vec3.cross(this._array, a._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Alias for distance + * @param {qtek.math.Vector3} b + * @return {number} + */ + dist: function (b) { + return vec3.dist(this._array, b._array); + }, + + /** + * Distance between self and b + * @param {qtek.math.Vector3} b + * @return {number} + */ + distance: function (b) { + return vec3.distance(this._array, b._array); + }, + + /** + * Alias for divide + * @param {qtek.math.Vector3} b + * @return {qtek.math.Vector3} + */ + div: function (b) { + vec3.div(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Divide self by b + * @param {qtek.math.Vector3} b + * @return {qtek.math.Vector3} + */ + divide: function (b) { + vec3.divide(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Dot product of self and b + * @param {qtek.math.Vector3} b + * @return {number} + */ + dot: function (b) { + return vec3.dot(this._array, b._array); + }, + + /** + * Alias of length + * @return {number} + */ + len: function () { + return vec3.len(this._array); + }, + + /** + * Calculate the length + * @return {number} + */ + length: function () { + return vec3.length(this._array); + }, + /** + * Linear interpolation between a and b + * @param {qtek.math.Vector3} a + * @param {qtek.math.Vector3} b + * @param {number} t + * @return {qtek.math.Vector3} + */ + lerp: function (a, b, t) { + vec3.lerp(this._array, a._array, b._array, t); + this._dirty = true; + return this; + }, + + /** + * Minimum of self and b + * @param {qtek.math.Vector3} b + * @return {qtek.math.Vector3} + */ + min: function (b) { + vec3.min(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Maximum of self and b + * @param {qtek.math.Vector3} b + * @return {qtek.math.Vector3} + */ + max: function (b) { + vec3.max(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Alias for multiply + * @param {qtek.math.Vector3} b + * @return {qtek.math.Vector3} + */ + mul: function (b) { + vec3.mul(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Mutiply self and b + * @param {qtek.math.Vector3} b + * @return {qtek.math.Vector3} + */ + multiply: function (b) { + vec3.multiply(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Negate self + * @return {qtek.math.Vector3} + */ + negate: function () { + vec3.negate(this._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Normalize self + * @return {qtek.math.Vector3} + */ + normalize: function () { + vec3.normalize(this._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Generate random x, y, z components with a given scale + * @param {number} scale + * @return {qtek.math.Vector3} + */ + random: function (scale) { + vec3.random(this._array, scale); + this._dirty = true; + return this; + }, + + /** + * Scale self + * @param {number} scale + * @return {qtek.math.Vector3} + */ + scale: function (s) { + vec3.scale(this._array, this._array, s); + this._dirty = true; + return this; + }, + + /** + * Scale b and add to self + * @param {qtek.math.Vector3} b + * @param {number} scale + * @return {qtek.math.Vector3} + */ + scaleAndAdd: function (b, s) { + vec3.scaleAndAdd(this._array, this._array, b._array, s); + this._dirty = true; + return this; + }, + + /** + * Alias for squaredDistance + * @param {qtek.math.Vector3} b + * @return {number} + */ + sqrDist: function (b) { + return vec3.sqrDist(this._array, b._array); + }, + + /** + * Squared distance between self and b + * @param {qtek.math.Vector3} b + * @return {number} + */ + squaredDistance: function (b) { + return vec3.squaredDistance(this._array, b._array); + }, + + /** + * Alias for squaredLength + * @return {number} + */ + sqrLen: function () { + return vec3.sqrLen(this._array); + }, + + /** + * Squared length of self + * @return {number} + */ + squaredLength: function () { + return vec3.squaredLength(this._array); + }, + + /** + * Alias for subtract + * @param {qtek.math.Vector3} b + * @return {qtek.math.Vector3} + */ + sub: function (b) { + vec3.sub(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Subtract b from self + * @param {qtek.math.Vector3} b + * @return {qtek.math.Vector3} + */ + subtract: function (b) { + vec3.subtract(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Transform self with a Matrix3 m + * @param {qtek.math.Matrix3} m + * @return {qtek.math.Vector3} + */ + transformMat3: function (m) { + vec3.transformMat3(this._array, this._array, m._array); + this._dirty = true; + return this; + }, + + /** + * Transform self with a Matrix4 m + * @param {qtek.math.Matrix4} m + * @return {qtek.math.Vector3} + */ + transformMat4: function (m) { + vec3.transformMat4(this._array, this._array, m._array); + this._dirty = true; + return this; + }, + /** + * Transform self with a Quaternion q + * @param {qtek.math.Quaternion} q + * @return {qtek.math.Vector3} + */ + transformQuat: function (q) { + vec3.transformQuat(this._array, this._array, q._array); + this._dirty = true; + return this; + }, + + /** + * Trasnform self into projection space with m + * @param {qtek.math.Matrix4} m + * @return {qtek.math.Vector3} + */ + applyProjection: function (m) { + var v = this._array; + m = m._array; + + // Perspective projection + if (m[15] === 0) { + var w = -1 / v[2]; + v[0] = m[0] * v[0] * w; + v[1] = m[5] * v[1] * w; + v[2] = (m[10] * v[2] + m[14]) * w; + } + else { + v[0] = m[0] * v[0] + m[12]; + v[1] = m[5] * v[1] + m[13]; + v[2] = m[10] * v[2] + m[14]; + } + this._dirty = true; + + return this; + }, + + eulerFromQuat: function(q, order) { + Vector3.eulerFromQuat(this, q, order); + }, + + eulerFromMat3: function (m, order) { + Vector3.eulerFromMat3(this, m, order); + }, + + toString: function() { + return '[' + Array.prototype.join.call(this._array, ',') + ']'; + }, + + toArray: function () { + return Array.prototype.slice.call(this._array); + } + }; + + var defineProperty = Object.defineProperty; + // Getter and Setter + if (defineProperty) { + + var proto = Vector3.prototype; + /** + * @name x + * @type {number} + * @memberOf qtek.math.Vector3 + * @instance + */ + defineProperty(proto, 'x', { + get: function () { + return this._array[0]; + }, + set: function (value) { + this._array[0] = value; + this._dirty = true; + } + }); + + /** + * @name y + * @type {number} + * @memberOf qtek.math.Vector3 + * @instance + */ + defineProperty(proto, 'y', { + get: function () { + return this._array[1]; + }, + set: function (value) { + this._array[1] = value; + this._dirty = true; + } + }); + + /** + * @name z + * @type {number} + * @memberOf qtek.math.Vector3 + * @instance + */ + defineProperty(proto, 'z', { + get: function () { + return this._array[2]; + }, + set: function (value) { + this._array[2] = value; + this._dirty = true; + } + }); + } + + + // Supply methods that are not in place + + /** + * @param {qtek.math.Vector3} out + * @param {qtek.math.Vector3} a + * @param {qtek.math.Vector3} b + * @return {qtek.math.Vector3} + */ + Vector3.add = function(out, a, b) { + vec3.add(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Vector3} out + * @param {number} x + * @param {number} y + * @param {number} z + * @return {qtek.math.Vector3} + */ + Vector3.set = function(out, x, y, z) { + vec3.set(out._array, x, y, z); + out._dirty = true; + }; + + /** + * @param {qtek.math.Vector3} out + * @param {qtek.math.Vector3} b + * @return {qtek.math.Vector3} + */ + Vector3.copy = function(out, b) { + vec3.copy(out._array, b._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Vector3} out + * @param {qtek.math.Vector3} a + * @param {qtek.math.Vector3} b + * @return {qtek.math.Vector3} + */ + Vector3.cross = function(out, a, b) { + vec3.cross(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Vector3} a + * @param {qtek.math.Vector3} b + * @return {number} + */ + Vector3.dist = function(a, b) { + return vec3.distance(a._array, b._array); + }; + + /** + * @method + * @param {qtek.math.Vector3} a + * @param {qtek.math.Vector3} b + * @return {number} + */ + Vector3.distance = Vector3.dist; + + /** + * @param {qtek.math.Vector3} out + * @param {qtek.math.Vector3} a + * @param {qtek.math.Vector3} b + * @return {qtek.math.Vector3} + */ + Vector3.div = function(out, a, b) { + vec3.divide(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + + /** + * @method + * @param {qtek.math.Vector3} out + * @param {qtek.math.Vector3} a + * @param {qtek.math.Vector3} b + * @return {qtek.math.Vector3} + */ + Vector3.divide = Vector3.div; + + /** + * @param {qtek.math.Vector3} a + * @param {qtek.math.Vector3} b + * @return {number} + */ + Vector3.dot = function(a, b) { + return vec3.dot(a._array, b._array); + }; + + /** + * @param {qtek.math.Vector3} a + * @return {number} + */ + Vector3.len = function(b) { + return vec3.length(b._array); + }; + + // Vector3.length = Vector3.len; + + /** + * @param {qtek.math.Vector3} out + * @param {qtek.math.Vector3} a + * @param {qtek.math.Vector3} b + * @param {number} t + * @return {qtek.math.Vector3} + */ + Vector3.lerp = function(out, a, b, t) { + vec3.lerp(out._array, a._array, b._array, t); + out._dirty = true; + return out; + }; + /** + * @param {qtek.math.Vector3} out + * @param {qtek.math.Vector3} a + * @param {qtek.math.Vector3} b + * @return {qtek.math.Vector3} + */ + Vector3.min = function(out, a, b) { + vec3.min(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Vector3} out + * @param {qtek.math.Vector3} a + * @param {qtek.math.Vector3} b + * @return {qtek.math.Vector3} + */ + Vector3.max = function(out, a, b) { + vec3.max(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + /** + * @param {qtek.math.Vector3} out + * @param {qtek.math.Vector3} a + * @param {qtek.math.Vector3} b + * @return {qtek.math.Vector3} + */ + Vector3.mul = function(out, a, b) { + vec3.multiply(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + /** + * @method + * @param {qtek.math.Vector3} out + * @param {qtek.math.Vector3} a + * @param {qtek.math.Vector3} b + * @return {qtek.math.Vector3} + */ + Vector3.multiply = Vector3.mul; + /** + * @param {qtek.math.Vector3} out + * @param {qtek.math.Vector3} a + * @return {qtek.math.Vector3} + */ + Vector3.negate = function(out, a) { + vec3.negate(out._array, a._array); + out._dirty = true; + return out; + }; + /** + * @param {qtek.math.Vector3} out + * @param {qtek.math.Vector3} a + * @return {qtek.math.Vector3} + */ + Vector3.normalize = function(out, a) { + vec3.normalize(out._array, a._array); + out._dirty = true; + return out; + }; + /** + * @param {qtek.math.Vector3} out + * @param {number} scale + * @return {qtek.math.Vector3} + */ + Vector3.random = function(out, scale) { + vec3.random(out._array, scale); + out._dirty = true; + return out; + }; + /** + * @param {qtek.math.Vector3} out + * @param {qtek.math.Vector3} a + * @param {number} scale + * @return {qtek.math.Vector3} + */ + Vector3.scale = function(out, a, scale) { + vec3.scale(out._array, a._array, scale); + out._dirty = true; + return out; + }; + /** + * @param {qtek.math.Vector3} out + * @param {qtek.math.Vector3} a + * @param {qtek.math.Vector3} b + * @param {number} scale + * @return {qtek.math.Vector3} + */ + Vector3.scaleAndAdd = function(out, a, b, scale) { + vec3.scaleAndAdd(out._array, a._array, b._array, scale); + out._dirty = true; + return out; + }; + /** + * @param {qtek.math.Vector3} a + * @param {qtek.math.Vector3} b + * @return {number} + */ + Vector3.sqrDist = function(a, b) { + return vec3.sqrDist(a._array, b._array); + }; + /** + * @method + * @param {qtek.math.Vector3} a + * @param {qtek.math.Vector3} b + * @return {number} + */ + Vector3.squaredDistance = Vector3.sqrDist; + /** + * @param {qtek.math.Vector3} a + * @return {number} + */ + Vector3.sqrLen = function(a) { + return vec3.sqrLen(a._array); + }; + /** + * @method + * @param {qtek.math.Vector3} a + * @return {number} + */ + Vector3.squaredLength = Vector3.sqrLen; + + /** + * @param {qtek.math.Vector3} out + * @param {qtek.math.Vector3} a + * @param {qtek.math.Vector3} b + * @return {qtek.math.Vector3} + */ + Vector3.sub = function(out, a, b) { + vec3.subtract(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + /** + * @method + * @param {qtek.math.Vector3} out + * @param {qtek.math.Vector3} a + * @param {qtek.math.Vector3} b + * @return {qtek.math.Vector3} + */ + Vector3.subtract = Vector3.sub; + + /** + * @param {qtek.math.Vector3} out + * @param {qtek.math.Vector3} a + * @param {Matrix3} m + * @return {qtek.math.Vector3} + */ + Vector3.transformMat3 = function(out, a, m) { + vec3.transformMat3(out._array, a._array, m._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Vector3} out + * @param {qtek.math.Vector3} a + * @param {qtek.math.Matrix4} m + * @return {qtek.math.Vector3} + */ + Vector3.transformMat4 = function(out, a, m) { + vec3.transformMat4(out._array, a._array, m._array); + out._dirty = true; + return out; + }; + /** + * @param {qtek.math.Vector3} out + * @param {qtek.math.Vector3} a + * @param {qtek.math.Quaternion} q + * @return {qtek.math.Vector3} + */ + Vector3.transformQuat = function(out, a, q) { + vec3.transformQuat(out._array, a._array, q._array); + out._dirty = true; + return out; + }; + + function clamp(val, min, max) { + return val < min ? min : (val > max ? max : val); + } + var atan2 = Math.atan2; + var asin = Math.asin; + var abs = Math.abs; + /** + * Convert quaternion to euler angle + * Quaternion must be normalized + * From three.js + */ + Vector3.eulerFromQuat = function (out, q, order) { + out._dirty = true; + q = q._array; + + var target = out._array; + var x = q[0], y = q[1], z = q[2], w = q[3]; + var x2 = x * x; + var y2 = y * y; + var z2 = z * z; + var w2 = w * w; + + var order = (order || 'XYZ').toUpperCase(); + + switch (order) { + case 'XYZ': + target[0] = atan2(2 * (x * w - y * z), (w2 - x2 - y2 + z2)); + target[1] = asin(clamp(2 * (x * z + y * w), - 1, 1)); + target[2] = atan2(2 * (z * w - x * y), (w2 + x2 - y2 - z2)); + break; + case 'YXZ': + target[0] = asin(clamp(2 * (x * w - y * z), - 1, 1)); + target[1] = atan2(2 * (x * z + y * w), (w2 - x2 - y2 + z2)); + target[2] = atan2(2 * (x * y + z * w), (w2 - x2 + y2 - z2)); + break; + case 'ZXY': + target[0] = asin(clamp(2 * (x * w + y * z), - 1, 1)); + target[1] = atan2(2 * (y * w - z * x), (w2 - x2 - y2 + z2)); + target[2] = atan2(2 * (z * w - x * y), (w2 - x2 + y2 - z2)); + break; + case 'ZYX': + target[0] = atan2(2 * (x * w + z * y), (w2 - x2 - y2 + z2)); + target[1] = asin(clamp(2 * (y * w - x * z), - 1, 1)); + target[2] = atan2(2 * (x * y + z * w), (w2 + x2 - y2 - z2)); + break; + case 'YZX': + target[0] = atan2(2 * (x * w - z * y), (w2 - x2 + y2 - z2)); + target[1] = atan2(2 * (y * w - x * z), (w2 + x2 - y2 - z2)); + target[2] = asin(clamp(2 * (x * y + z * w), - 1, 1)); + break; + case 'XZY': + target[0] = atan2(2 * (x * w + y * z), (w2 - x2 + y2 - z2)); + target[1] = atan2(2 * (x * z + y * w), (w2 + x2 - y2 - z2)); + target[2] = asin(clamp(2 * (z * w - x * y), - 1, 1)); + break; + default: + console.warn('Unkown order: ' + order); + } + return out; + }; + + /** + * Convert rotation matrix to euler angle + * from three.js + */ + Vector3.eulerFromMat3 = function (out, m, order) { + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + var te = m._array; + var m11 = te[0], m12 = te[3], m13 = te[6]; + var m21 = te[1], m22 = te[4], m23 = te[7]; + var m31 = te[2], m32 = te[5], m33 = te[8]; + var target = out._array; + + var order = (order || 'XYZ').toUpperCase(); + + switch (order) { + case 'XYZ': + target[1] = asin(clamp(m13, -1, 1)); + if (abs(m13) < 0.99999) { + target[0] = atan2(-m23, m33); + target[2] = atan2(-m12, m11); + } + else { + target[0] = atan2(m32, m22); + target[2] = 0; + } + break; + case 'YXZ': + target[0] = asin(-clamp(m23, -1, 1)); + if (abs(m23) < 0.99999) { + target[1] = atan2(m13, m33); + target[2] = atan2(m21, m22); + } + else { + target[1] = atan2(-m31, m11); + target[2] = 0; + } + break; + case 'ZXY': + target[0] = asin(clamp(m32, -1, 1)); + if (abs(m32) < 0.99999) { + target[1] = atan2(-m31, m33); + target[2] = atan2(-m12, m22); + } + else { + target[1] = 0; + target[2] = atan2(m21, m11); + } + break; + case 'ZYX': + target[1] = asin(-clamp(m31, -1, 1)); + if (abs(m31) < 0.99999) { + target[0] = atan2(m32, m33); + target[2] = atan2(m21, m11); + } + else { + target[0] = 0; + target[2] = atan2(-m12, m22); + } + break; + case 'YZX': + target[2] = asin(clamp(m21, -1, 1)); + if (abs(m21) < 0.99999) { + target[0] = atan2(-m23, m22); + target[1] = atan2(-m31, m11); + } + else { + target[0] = 0; + target[1] = atan2(m13, m33); + } + break; + case 'XZY': + target[2] = asin(-clamp(m12, -1, 1)); + if (abs(m12) < 0.99999) { + target[0] = atan2(m32, m22); + target[1] = atan2(m13, m11); + } + else { + target[0] = atan2(-m23, m33); + target[1] = 0; + } + break; + default: + console.warn('Unkown order: ' + order); + } + out._dirty = true; + + return out; + }; + + /** + * @type {qtek.math.Vector3} + */ + Vector3.POSITIVE_X = new Vector3(1, 0, 0); + /** + * @type {qtek.math.Vector3} + */ + Vector3.NEGATIVE_X = new Vector3(-1, 0, 0); + /** + * @type {qtek.math.Vector3} + */ + Vector3.POSITIVE_Y = new Vector3(0, 1, 0); + /** + * @type {qtek.math.Vector3} + */ + Vector3.NEGATIVE_Y = new Vector3(0, -1, 0); + /** + * @type {qtek.math.Vector3} + */ + Vector3.POSITIVE_Z = new Vector3(0, 0, 1); + /** + * @type {qtek.math.Vector3} + */ + Vector3.NEGATIVE_Z = new Vector3(0, 0, -1); + /** + * @type {qtek.math.Vector3} + */ + Vector3.UP = new Vector3(0, 1, 0); + /** + * @type {qtek.math.Vector3} + */ + Vector3.ZERO = new Vector3(0, 0, 0); + + module.exports = Vector3; + + +/***/ }, +/* 24 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var glMatrix = __webpack_require__(14); + var quat = glMatrix.quat; + + /** + * @constructor + * @alias qtek.math.Quaternion + * @param {number} x + * @param {number} y + * @param {number} z + * @param {number} w + */ + var Quaternion = function (x, y, z, w) { + + x = x || 0; + y = y || 0; + z = z || 0; + w = w === undefined ? 1 : w; + + /** + * Storage of Quaternion, read and write of x, y, z, w will change the values in _array + * All methods also operate on the _array instead of x, y, z, w components + * @name _array + * @type {Float32Array} + */ + this._array = quat.fromValues(x, y, z, w); + + /** + * Dirty flag is used by the Node to determine + * if the matrix is updated to latest + * @name _dirty + * @type {boolean} + */ + this._dirty = true; + }; + + Quaternion.prototype = { + + constructor: Quaternion, + + /** + * Add b to self + * @param {qtek.math.Quaternion} b + * @return {qtek.math.Quaternion} + */ + add: function (b) { + quat.add(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Calculate the w component from x, y, z component + * @return {qtek.math.Quaternion} + */ + calculateW: function () { + quat.calculateW(this._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Set x, y and z components + * @param {number} x + * @param {number} y + * @param {number} z + * @param {number} w + * @return {qtek.math.Quaternion} + */ + set: function (x, y, z, w) { + this._array[0] = x; + this._array[1] = y; + this._array[2] = z; + this._array[3] = w; + this._dirty = true; + return this; + }, + + /** + * Set x, y, z and w components from array + * @param {Float32Array|number[]} arr + * @return {qtek.math.Quaternion} + */ + setArray: function (arr) { + this._array[0] = arr[0]; + this._array[1] = arr[1]; + this._array[2] = arr[2]; + this._array[3] = arr[3]; + + this._dirty = true; + return this; + }, + + /** + * Clone a new Quaternion + * @return {qtek.math.Quaternion} + */ + clone: function () { + return new Quaternion(this.x, this.y, this.z, this.w); + }, + + /** + * Calculates the conjugate of self If the quaternion is normalized, + * this function is faster than invert and produces the same result. + * + * @return {qtek.math.Quaternion} + */ + conjugate: function () { + quat.conjugate(this._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Copy from b + * @param {qtek.math.Quaternion} b + * @return {qtek.math.Quaternion} + */ + copy: function (b) { + quat.copy(this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Dot product of self and b + * @param {qtek.math.Quaternion} b + * @return {number} + */ + dot: function (b) { + return quat.dot(this._array, b._array); + }, + + /** + * Set from the given 3x3 rotation matrix + * @param {qtek.math.Matrix3} m + * @return {qtek.math.Quaternion} + */ + fromMat3: function (m) { + quat.fromMat3(this._array, m._array); + this._dirty = true; + return this; + }, + + /** + * Set from the given 4x4 rotation matrix + * The 4th column and 4th row will be droped + * @param {qtek.math.Matrix4} m + * @return {qtek.math.Quaternion} + */ + fromMat4: (function () { + var mat3 = glMatrix.mat3; + var m3 = mat3.create(); + return function (m) { + mat3.fromMat4(m3, m._array); + // TODO Not like mat4, mat3 in glmatrix seems to be row-based + mat3.transpose(m3, m3); + quat.fromMat3(this._array, m3); + this._dirty = true; + return this; + }; + })(), + + /** + * Set to identity quaternion + * @return {qtek.math.Quaternion} + */ + identity: function () { + quat.identity(this._array); + this._dirty = true; + return this; + }, + /** + * Invert self + * @return {qtek.math.Quaternion} + */ + invert: function () { + quat.invert(this._array, this._array); + this._dirty = true; + return this; + }, + /** + * Alias of length + * @return {number} + */ + len: function () { + return quat.len(this._array); + }, + + /** + * Calculate the length + * @return {number} + */ + length: function () { + return quat.length(this._array); + }, + + /** + * Linear interpolation between a and b + * @param {qtek.math.Quaternion} a + * @param {qtek.math.Quaternion} b + * @param {number} t + * @return {qtek.math.Quaternion} + */ + lerp: function (a, b, t) { + quat.lerp(this._array, a._array, b._array, t); + this._dirty = true; + return this; + }, + + /** + * Alias for multiply + * @param {qtek.math.Quaternion} b + * @return {qtek.math.Quaternion} + */ + mul: function (b) { + quat.mul(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Alias for multiplyLeft + * @param {qtek.math.Quaternion} a + * @return {qtek.math.Quaternion} + */ + mulLeft: function (a) { + quat.multiply(this._array, a._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Mutiply self and b + * @param {qtek.math.Quaternion} b + * @return {qtek.math.Quaternion} + */ + multiply: function (b) { + quat.multiply(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Mutiply a and self + * Quaternion mutiply is not commutative, so the result of mutiplyLeft is different with multiply. + * @param {qtek.math.Quaternion} a + * @return {qtek.math.Quaternion} + */ + multiplyLeft: function (a) { + quat.multiply(this._array, a._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Normalize self + * @return {qtek.math.Quaternion} + */ + normalize: function () { + quat.normalize(this._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Rotate self by a given radian about X axis + * @param {number} rad + * @return {qtek.math.Quaternion} + */ + rotateX: function (rad) { + quat.rotateX(this._array, this._array, rad); + this._dirty = true; + return this; + }, + + /** + * Rotate self by a given radian about Y axis + * @param {number} rad + * @return {qtek.math.Quaternion} + */ + rotateY: function (rad) { + quat.rotateY(this._array, this._array, rad); + this._dirty = true; + return this; + }, + + /** + * Rotate self by a given radian about Z axis + * @param {number} rad + * @return {qtek.math.Quaternion} + */ + rotateZ: function (rad) { + quat.rotateZ(this._array, this._array, rad); + this._dirty = true; + return this; + }, + + /** + * Sets self to represent the shortest rotation from Vector3 a to Vector3 b. + * a and b needs to be normalized + * @param {qtek.math.Vector3} a + * @param {qtek.math.Vector3} b + * @return {qtek.math.Quaternion} + */ + rotationTo: function (a, b) { + quat.rotationTo(this._array, a._array, b._array); + this._dirty = true; + return this; + }, + /** + * Sets self with values corresponding to the given axes + * @param {qtek.math.Vector3} view + * @param {qtek.math.Vector3} right + * @param {qtek.math.Vector3} up + * @return {qtek.math.Quaternion} + */ + setAxes: function (view, right, up) { + quat.setAxes(this._array, view._array, right._array, up._array); + this._dirty = true; + return this; + }, + + /** + * Sets self with a rotation axis and rotation angle + * @param {qtek.math.Vector3} axis + * @param {number} rad + * @return {qtek.math.Quaternion} + */ + setAxisAngle: function (axis, rad) { + quat.setAxisAngle(this._array, axis._array, rad); + this._dirty = true; + return this; + }, + /** + * Perform spherical linear interpolation between a and b + * @param {qtek.math.Quaternion} a + * @param {qtek.math.Quaternion} b + * @param {number} t + * @return {qtek.math.Quaternion} + */ + slerp: function (a, b, t) { + quat.slerp(this._array, a._array, b._array, t); + this._dirty = true; + return this; + }, + + /** + * Alias for squaredLength + * @return {number} + */ + sqrLen: function () { + return quat.sqrLen(this._array); + }, + + /** + * Squared length of self + * @return {number} + */ + squaredLength: function () { + return quat.squaredLength(this._array); + }, + + /** + * Set from euler + * @param {qtek.math.Vector3} v + * @param {String} order + */ + fromEuler: function (v, order) { + return Quaternion.fromEuler(this, v, order); + }, + + toString: function () { + return '[' + Array.prototype.join.call(this._array, ',') + ']'; + }, + + toArray: function () { + return Array.prototype.slice.call(this._array); + } + }; + + var defineProperty = Object.defineProperty; + // Getter and Setter + if (defineProperty) { + + var proto = Quaternion.prototype; + /** + * @name x + * @type {number} + * @memberOf qtek.math.Quaternion + * @instance + */ + defineProperty(proto, 'x', { + get: function () { + return this._array[0]; + }, + set: function (value) { + this._array[0] = value; + this._dirty = true; + } + }); + + /** + * @name y + * @type {number} + * @memberOf qtek.math.Quaternion + * @instance + */ + defineProperty(proto, 'y', { + get: function () { + return this._array[1]; + }, + set: function (value) { + this._array[1] = value; + this._dirty = true; + } + }); + + /** + * @name z + * @type {number} + * @memberOf qtek.math.Quaternion + * @instance + */ + defineProperty(proto, 'z', { + get: function () { + return this._array[2]; + }, + set: function (value) { + this._array[2] = value; + this._dirty = true; + } + }); + + /** + * @name w + * @type {number} + * @memberOf qtek.math.Quaternion + * @instance + */ + defineProperty(proto, 'w', { + get: function () { + return this._array[3]; + }, + set: function (value) { + this._array[3] = value; + this._dirty = true; + } + }); + } + + // Supply methods that are not in place + + /** + * @param {qtek.math.Quaternion} out + * @param {qtek.math.Quaternion} a + * @param {qtek.math.Quaternion} b + * @return {qtek.math.Quaternion} + */ + Quaternion.add = function (out, a, b) { + quat.add(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Quaternion} out + * @param {number} x + * @param {number} y + * @param {number} z + * @param {number} w + * @return {qtek.math.Quaternion} + */ + Quaternion.set = function (out, x, y, z, w) { + quat.set(out._array, x, y, z, w); + out._dirty = true; + }; + + /** + * @param {qtek.math.Quaternion} out + * @param {qtek.math.Quaternion} b + * @return {qtek.math.Quaternion} + */ + Quaternion.copy = function (out, b) { + quat.copy(out._array, b._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Quaternion} out + * @param {qtek.math.Quaternion} a + * @return {qtek.math.Quaternion} + */ + Quaternion.calculateW = function (out, a) { + quat.calculateW(out._array, a._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Quaternion} out + * @param {qtek.math.Quaternion} a + * @return {qtek.math.Quaternion} + */ + Quaternion.conjugate = function (out, a) { + quat.conjugate(out._array, a._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Quaternion} out + * @return {qtek.math.Quaternion} + */ + Quaternion.identity = function (out) { + quat.identity(out._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Quaternion} out + * @param {qtek.math.Quaternion} a + * @return {qtek.math.Quaternion} + */ + Quaternion.invert = function (out, a) { + quat.invert(out._array, a._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Quaternion} a + * @param {qtek.math.Quaternion} b + * @return {number} + */ + Quaternion.dot = function (a, b) { + return quat.dot(a._array, b._array); + }; + + /** + * @param {qtek.math.Quaternion} a + * @return {number} + */ + Quaternion.len = function (a) { + return quat.length(a._array); + }; + + // Quaternion.length = Quaternion.len; + + /** + * @param {qtek.math.Quaternion} out + * @param {qtek.math.Quaternion} a + * @param {qtek.math.Quaternion} b + * @param {number} t + * @return {qtek.math.Quaternion} + */ + Quaternion.lerp = function (out, a, b, t) { + quat.lerp(out._array, a._array, b._array, t); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Quaternion} out + * @param {qtek.math.Quaternion} a + * @param {qtek.math.Quaternion} b + * @param {number} t + * @return {qtek.math.Quaternion} + */ + Quaternion.slerp = function (out, a, b, t) { + quat.slerp(out._array, a._array, b._array, t); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Quaternion} out + * @param {qtek.math.Quaternion} a + * @param {qtek.math.Quaternion} b + * @return {qtek.math.Quaternion} + */ + Quaternion.mul = function (out, a, b) { + quat.multiply(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + + /** + * @method + * @param {qtek.math.Quaternion} out + * @param {qtek.math.Quaternion} a + * @param {qtek.math.Quaternion} b + * @return {qtek.math.Quaternion} + */ + Quaternion.multiply = Quaternion.mul; + + /** + * @param {qtek.math.Quaternion} out + * @param {qtek.math.Quaternion} a + * @param {number} rad + * @return {qtek.math.Quaternion} + */ + Quaternion.rotateX = function (out, a, rad) { + quat.rotateX(out._array, a._array, rad); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Quaternion} out + * @param {qtek.math.Quaternion} a + * @param {number} rad + * @return {qtek.math.Quaternion} + */ + Quaternion.rotateY = function (out, a, rad) { + quat.rotateY(out._array, a._array, rad); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Quaternion} out + * @param {qtek.math.Quaternion} a + * @param {number} rad + * @return {qtek.math.Quaternion} + */ + Quaternion.rotateZ = function (out, a, rad) { + quat.rotateZ(out._array, a._array, rad); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Quaternion} out + * @param {qtek.math.Vector3} axis + * @param {number} rad + * @return {qtek.math.Quaternion} + */ + Quaternion.setAxisAngle = function (out, axis, rad) { + quat.setAxisAngle(out._array, axis._array, rad); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Quaternion} out + * @param {qtek.math.Quaternion} a + * @return {qtek.math.Quaternion} + */ + Quaternion.normalize = function (out, a) { + quat.normalize(out._array, a._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Quaternion} a + * @return {number} + */ + Quaternion.sqrLen = function (a) { + return quat.sqrLen(a._array); + }; + + /** + * @method + * @param {qtek.math.Quaternion} a + * @return {number} + */ + Quaternion.squaredLength = Quaternion.sqrLen; + + /** + * @param {qtek.math.Quaternion} out + * @param {qtek.math.Matrix3} m + * @return {qtek.math.Quaternion} + */ + Quaternion.fromMat3 = function (out, m) { + quat.fromMat3(out._array, m._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Quaternion} out + * @param {qtek.math.Vector3} view + * @param {qtek.math.Vector3} right + * @param {qtek.math.Vector3} up + * @return {qtek.math.Quaternion} + */ + Quaternion.setAxes = function (out, view, right, up) { + quat.setAxes(out._array, view._array, right._array, up._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Quaternion} out + * @param {qtek.math.Vector3} a + * @param {qtek.math.Vector3} b + * @return {qtek.math.Quaternion} + */ + Quaternion.rotationTo = function (out, a, b) { + quat.rotationTo(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + + /** + * Set quaternion from euler + * @param {qtek.math.Quaternion} out + * @param {qtek.math.Vector3} v + * @param {String} order + */ + Quaternion.fromEuler = function (out, v, order) { + + out._dirty = true; + + v = v._array; + var target = out._array; + var c1 = Math.cos(v[0] / 2); + var c2 = Math.cos(v[1] / 2); + var c3 = Math.cos(v[2] / 2); + var s1 = Math.sin(v[0] / 2); + var s2 = Math.sin(v[1] / 2); + var s3 = Math.sin(v[2] / 2); + + var order = (order || 'XYZ').toUpperCase(); + + // http://www.mathworks.com/matlabcentral/fileexchange/ + // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ + // content/SpinCalc.m + + switch (order) { + case 'XYZ': + target[0] = s1 * c2 * c3 + c1 * s2 * s3; + target[1] = c1 * s2 * c3 - s1 * c2 * s3; + target[2] = c1 * c2 * s3 + s1 * s2 * c3; + target[3] = c1 * c2 * c3 - s1 * s2 * s3; + break; + case 'YXZ': + target[0] = s1 * c2 * c3 + c1 * s2 * s3; + target[1] = c1 * s2 * c3 - s1 * c2 * s3; + target[2] = c1 * c2 * s3 - s1 * s2 * c3; + target[3] = c1 * c2 * c3 + s1 * s2 * s3; + break; + case 'ZXY': + target[0] = s1 * c2 * c3 - c1 * s2 * s3; + target[1] = c1 * s2 * c3 + s1 * c2 * s3; + target[2] = c1 * c2 * s3 + s1 * s2 * c3; + target[3] = c1 * c2 * c3 - s1 * s2 * s3; + break; + case 'ZYX': + target[0] = s1 * c2 * c3 - c1 * s2 * s3; + target[1] = c1 * s2 * c3 + s1 * c2 * s3; + target[2] = c1 * c2 * s3 - s1 * s2 * c3; + target[3] = c1 * c2 * c3 + s1 * s2 * s3; + break; + case 'YZX': + target[0] = s1 * c2 * c3 + c1 * s2 * s3; + target[1] = c1 * s2 * c3 + s1 * c2 * s3; + target[2] = c1 * c2 * s3 - s1 * s2 * c3; + target[3] = c1 * c2 * c3 - s1 * s2 * s3; + break; + case 'XZY': + target[0] = s1 * c2 * c3 - c1 * s2 * s3; + target[1] = c1 * s2 * c3 - s1 * c2 * s3; + target[2] = c1 * c2 * s3 + s1 * s2 * c3; + target[3] = c1 * c2 * c3 + s1 * s2 * s3; + break; + } + }; + + module.exports = Quaternion; + + +/***/ }, +/* 25 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var glMatrix = __webpack_require__(14); + var Vector3 = __webpack_require__(23); + var mat4 = glMatrix.mat4; + var vec3 = glMatrix.vec3; + var mat3 = glMatrix.mat3; + var quat = glMatrix.quat; + + /** + * @constructor + * @alias qtek.math.Matrix4 + */ + var Matrix4 = function() { + + this._axisX = new Vector3(); + this._axisY = new Vector3(); + this._axisZ = new Vector3(); + + /** + * Storage of Matrix4 + * @name _array + * @type {Float32Array} + */ + this._array = mat4.create(); + + /** + * @name _dirty + * @type {boolean} + */ + this._dirty = true; + }; + + Matrix4.prototype = { + + constructor: Matrix4, + + /** + * Set components from array + * @param {Float32Array|number[]} arr + */ + setArray: function (arr) { + for (var i = 0; i < this._array.length; i++) { + this._array[i] = arr[i]; + } + this._dirty = true; + return this; + }, + /** + * Calculate the adjugate of self, in-place + * @return {qtek.math.Matrix4} + */ + adjoint: function() { + mat4.adjoint(this._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Clone a new Matrix4 + * @return {qtek.math.Matrix4} + */ + clone: function() { + return (new Matrix4()).copy(this); + }, + + /** + * Copy from b + * @param {qtek.math.Matrix4} b + * @return {qtek.math.Matrix4} + */ + copy: function(a) { + mat4.copy(this._array, a._array); + this._dirty = true; + return this; + }, + + /** + * Calculate matrix determinant + * @return {number} + */ + determinant: function() { + return mat4.determinant(this._array); + }, + + /** + * Set upper 3x3 part from quaternion + * @param {qtek.math.Quaternion} q + * @return {qtek.math.Matrix4} + */ + fromQuat: function(q) { + mat4.fromQuat(this._array, q._array); + this._dirty = true; + return this; + }, + + /** + * Set from a quaternion rotation and a vector translation + * @param {qtek.math.Quaternion} q + * @param {qtek.math.Vector3} v + * @return {qtek.math.Matrix4} + */ + fromRotationTranslation: function(q, v) { + mat4.fromRotationTranslation(this._array, q._array, v._array); + this._dirty = true; + return this; + }, + + /** + * Set from Matrix2d, it is used when converting a 2d shape to 3d space. + * In 3d space it is equivalent to ranslate on xy plane and rotate about z axis + * @param {qtek.math.Matrix2d} m2d + * @return {qtek.math.Matrix4} + */ + fromMat2d: function(m2d) { + Matrix4.fromMat2d(this, m2d); + return this; + }, + + /** + * Set from frustum bounds + * @param {number} left + * @param {number} right + * @param {number} bottom + * @param {number} top + * @param {number} near + * @param {number} far + * @return {qtek.math.Matrix4} + */ + frustum: function (left, right, bottom, top, near, far) { + mat4.frustum(this._array, left, right, bottom, top, near, far); + this._dirty = true; + return this; + }, + + /** + * Set to a identity matrix + * @return {qtek.math.Matrix4} + */ + identity: function() { + mat4.identity(this._array); + this._dirty = true; + return this; + }, + + /** + * Invert self + * @return {qtek.math.Matrix4} + */ + invert: function() { + mat4.invert(this._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Set as a matrix with the given eye position, focal point, and up axis + * @param {qtek.math.Vector3} eye + * @param {qtek.math.Vector3} center + * @param {qtek.math.Vector3} up + * @return {qtek.math.Matrix4} + */ + lookAt: function(eye, center, up) { + mat4.lookAt(this._array, eye._array, center._array, up._array); + this._dirty = true; + return this; + }, + + /** + * Alias for mutiply + * @param {qtek.math.Matrix4} b + * @return {qtek.math.Matrix4} + */ + mul: function(b) { + mat4.mul(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Alias for multiplyLeft + * @param {qtek.math.Matrix4} a + * @return {qtek.math.Matrix4} + */ + mulLeft: function(a) { + mat4.mul(this._array, a._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Multiply self and b + * @param {qtek.math.Matrix4} b + * @return {qtek.math.Matrix4} + */ + multiply: function(b) { + mat4.multiply(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Multiply a and self, a is on the left + * @param {qtek.math.Matrix3} a + * @return {qtek.math.Matrix3} + */ + multiplyLeft: function(a) { + mat4.multiply(this._array, a._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Set as a orthographic projection matrix + * @param {number} left + * @param {number} right + * @param {number} bottom + * @param {number} top + * @param {number} near + * @param {number} far + * @return {qtek.math.Matrix4} + */ + ortho: function(left, right, bottom, top, near, far) { + mat4.ortho(this._array, left, right, bottom, top, near, far); + this._dirty = true; + return this; + }, + /** + * Set as a perspective projection matrix + * @param {number} fovy + * @param {number} aspect + * @param {number} near + * @param {number} far + * @return {qtek.math.Matrix4} + */ + perspective: function(fovy, aspect, near, far) { + mat4.perspective(this._array, fovy, aspect, near, far); + this._dirty = true; + return this; + }, + + /** + * Rotate self by rad about axis. + * Equal to right-multiply a rotaion matrix + * @param {number} rad + * @param {qtek.math.Vector3} axis + * @return {qtek.math.Matrix4} + */ + rotate: function(rad, axis) { + mat4.rotate(this._array, this._array, rad, axis._array); + this._dirty = true; + return this; + }, + + /** + * Rotate self by a given radian about X axis. + * Equal to right-multiply a rotaion matrix + * @param {number} rad + * @return {qtek.math.Matrix4} + */ + rotateX: function(rad) { + mat4.rotateX(this._array, this._array, rad); + this._dirty = true; + return this; + }, + + /** + * Rotate self by a given radian about Y axis. + * Equal to right-multiply a rotaion matrix + * @param {number} rad + * @return {qtek.math.Matrix4} + */ + rotateY: function(rad) { + mat4.rotateY(this._array, this._array, rad); + this._dirty = true; + return this; + }, + + /** + * Rotate self by a given radian about Z axis. + * Equal to right-multiply a rotaion matrix + * @param {number} rad + * @return {qtek.math.Matrix4} + */ + rotateZ: function(rad) { + mat4.rotateZ(this._array, this._array, rad); + this._dirty = true; + return this; + }, + + /** + * Scale self by s + * Equal to right-multiply a scale matrix + * @param {qtek.math.Vector3} s + * @return {qtek.math.Matrix4} + */ + scale: function(v) { + mat4.scale(this._array, this._array, v._array); + this._dirty = true; + return this; + }, + + /** + * Translate self by v. + * Equal to right-multiply a translate matrix + * @param {qtek.math.Vector3} v + * @return {qtek.math.Matrix4} + */ + translate: function(v) { + mat4.translate(this._array, this._array, v._array); + this._dirty = true; + return this; + }, + + /** + * Transpose self, in-place. + * @return {qtek.math.Matrix2} + */ + transpose: function() { + mat4.transpose(this._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Decompose a matrix to SRT + * @param {qtek.math.Vector3} [scale] + * @param {qtek.math.Quaternion} rotation + * @param {qtek.math.Vector} position + * @see http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.matrix.decompose.aspx + */ + decomposeMatrix: (function() { + + var x = vec3.create(); + var y = vec3.create(); + var z = vec3.create(); + + var m3 = mat3.create(); + + return function(scale, rotation, position) { + + var el = this._array; + vec3.set(x, el[0], el[1], el[2]); + vec3.set(y, el[4], el[5], el[6]); + vec3.set(z, el[8], el[9], el[10]); + + var sx = vec3.length(x); + var sy = vec3.length(y); + var sz = vec3.length(z); + if (scale) { + scale.x = sx; + scale.y = sy; + scale.z = sz; + scale._dirty = true; + } + + position.set(el[12], el[13], el[14]); + + mat3.fromMat4(m3, el); + // Not like mat4, mat3 in glmatrix seems to be row-based + // Seems fixed in gl-matrix 2.2.2 + // https://github.com/toji/gl-matrix/issues/114 + // mat3.transpose(m3, m3); + + m3[0] /= sx; + m3[1] /= sx; + m3[2] /= sx; + + m3[3] /= sy; + m3[4] /= sy; + m3[5] /= sy; + + m3[6] /= sz; + m3[7] /= sz; + m3[8] /= sz; + + quat.fromMat3(rotation._array, m3); + quat.normalize(rotation._array, rotation._array); + + rotation._dirty = true; + position._dirty = true; + }; + })(), + + toString: function() { + return '[' + Array.prototype.join.call(this._array, ',') + ']'; + }, + + toArray: function () { + return Array.prototype.slice.call(this._array); + } + }; + + var defineProperty = Object.defineProperty; + + if (defineProperty) { + var proto = Matrix4.prototype; + /** + * Z Axis of local transform + * @name z + * @type {qtek.math.Vector3} + * @memberOf qtek.math.Matrix4 + * @instance + */ + defineProperty(proto, 'z', { + get: function () { + var el = this._array; + this._axisZ.set(el[8], el[9], el[10]); + return this._axisZ; + }, + set: function (v) { + // TODO Here has a problem + // If only set an item of vector will not work + var el = this._array; + v = v._array; + el[8] = v[0]; + el[9] = v[1]; + el[10] = v[2]; + + this._dirty = true; + } + }); + + /** + * Y Axis of local transform + * @name y + * @type {qtek.math.Vector3} + * @memberOf qtek.math.Matrix4 + * @instance + */ + defineProperty(proto, 'y', { + get: function () { + var el = this._array; + this._axisY.set(el[4], el[5], el[6]); + return this._axisY; + }, + set: function (v) { + var el = this._array; + v = v._array; + el[4] = v[0]; + el[5] = v[1]; + el[6] = v[2]; + + this._dirty = true; + } + }); + + /** + * X Axis of local transform + * @name x + * @type {qtek.math.Vector3} + * @memberOf qtek.math.Matrix4 + * @instance + */ + defineProperty(proto, 'x', { + get: function () { + var el = this._array; + this._axisX.set(el[0], el[1], el[2]); + return this._axisX; + }, + set: function (v) { + var el = this._array; + v = v._array; + el[0] = v[0]; + el[1] = v[1]; + el[2] = v[2]; + + this._dirty = true; + } + }) + } + + /** + * @param {qtek.math.Matrix4} out + * @param {qtek.math.Matrix4} a + * @return {qtek.math.Matrix4} + */ + Matrix4.adjoint = function(out, a) { + mat4.adjoint(out._array, a._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix4} out + * @param {qtek.math.Matrix4} a + * @return {qtek.math.Matrix4} + */ + Matrix4.copy = function(out, a) { + mat4.copy(out._array, a._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix4} a + * @return {number} + */ + Matrix4.determinant = function(a) { + return mat4.determinant(a._array); + }; + + /** + * @param {qtek.math.Matrix4} out + * @return {qtek.math.Matrix4} + */ + Matrix4.identity = function(out) { + mat4.identity(out._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix4} out + * @param {number} left + * @param {number} right + * @param {number} bottom + * @param {number} top + * @param {number} near + * @param {number} far + * @return {qtek.math.Matrix4} + */ + Matrix4.ortho = function(out, left, right, bottom, top, near, far) { + mat4.ortho(out._array, left, right, bottom, top, near, far); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix4} out + * @param {number} fovy + * @param {number} aspect + * @param {number} near + * @param {number} far + * @return {qtek.math.Matrix4} + */ + Matrix4.perspective = function(out, fovy, aspect, near, far) { + mat4.perspective(out._array, fovy, aspect, near, far); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix4} out + * @param {qtek.math.Vector3} eye + * @param {qtek.math.Vector3} center + * @param {qtek.math.Vector3} up + * @return {qtek.math.Matrix4} + */ + Matrix4.lookAt = function(out, eye, center, up) { + mat4.lookAt(out._array, eye._array, center._array, up._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix4} out + * @param {qtek.math.Matrix4} a + * @return {qtek.math.Matrix4} + */ + Matrix4.invert = function(out, a) { + mat4.invert(out._array, a._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix4} out + * @param {qtek.math.Matrix4} a + * @param {qtek.math.Matrix4} b + * @return {qtek.math.Matrix4} + */ + Matrix4.mul = function(out, a, b) { + mat4.mul(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + + /** + * @method + * @param {qtek.math.Matrix4} out + * @param {qtek.math.Matrix4} a + * @param {qtek.math.Matrix4} b + * @return {qtek.math.Matrix4} + */ + Matrix4.multiply = Matrix4.mul; + + /** + * @param {qtek.math.Matrix4} out + * @param {qtek.math.Quaternion} q + * @return {qtek.math.Matrix4} + */ + Matrix4.fromQuat = function(out, q) { + mat4.fromQuat(out._array, q._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix4} out + * @param {qtek.math.Quaternion} q + * @param {qtek.math.Vector3} v + * @return {qtek.math.Matrix4} + */ + Matrix4.fromRotationTranslation = function(out, q, v) { + mat4.fromRotationTranslation(out._array, q._array, v._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix4} m4 + * @param {qtek.math.Matrix2d} m2d + * @return {qtek.math.Matrix4} + */ + Matrix4.fromMat2d = function(m4, m2d) { + m4._dirty = true; + var m2d = m2d._array; + var m4 = m4._array; + + m4[0] = m2d[0]; + m4[4] = m2d[2]; + m4[12] = m2d[4]; + + m4[1] = m2d[1]; + m4[5] = m2d[3]; + m4[13] = m2d[5]; + + return m4; + }; + + /** + * @param {qtek.math.Matrix4} out + * @param {qtek.math.Matrix4} a + * @param {number} rad + * @param {qtek.math.Vector3} axis + * @return {qtek.math.Matrix4} + */ + Matrix4.rotate = function(out, a, rad, axis) { + mat4.rotate(out._array, a._array, rad, axis._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix4} out + * @param {qtek.math.Matrix4} a + * @param {number} rad + * @return {qtek.math.Matrix4} + */ + Matrix4.rotateX = function(out, a, rad) { + mat4.rotateX(out._array, a._array, rad); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix4} out + * @param {qtek.math.Matrix4} a + * @param {number} rad + * @return {qtek.math.Matrix4} + */ + Matrix4.rotateY = function(out, a, rad) { + mat4.rotateY(out._array, a._array, rad); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix4} out + * @param {qtek.math.Matrix4} a + * @param {number} rad + * @return {qtek.math.Matrix4} + */ + Matrix4.rotateZ = function(out, a, rad) { + mat4.rotateZ(out._array, a._array, rad); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix4} out + * @param {qtek.math.Matrix4} a + * @param {qtek.math.Vector3} v + * @return {qtek.math.Matrix4} + */ + Matrix4.scale = function(out, a, v) { + mat4.scale(out._array, a._array, v._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix4} out + * @param {qtek.math.Matrix4} a + * @return {qtek.math.Matrix4} + */ + Matrix4.transpose = function(out, a) { + mat4.transpose(out._array, a._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix4} out + * @param {qtek.math.Matrix4} a + * @param {qtek.math.Vector3} v + * @return {qtek.math.Matrix4} + */ + Matrix4.translate = function(out, a, v) { + mat4.translate(out._array, a._array, v._array); + out._dirty = true; + return out; + }; + + module.exports = Matrix4; + + +/***/ }, +/* 26 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Vector3 = __webpack_require__(23); + var glMatrix = __webpack_require__(14); + var vec3 = glMatrix.vec3; + + var vec3Copy = vec3.copy; + var vec3Set = vec3.set; + + /** + * Axis aligned bounding box + * @constructor + * @alias qtek.math.BoundingBox + * @param {qtek.math.Vector3} [min] + * @param {qtek.math.Vector3} [max] + */ + var BoundingBox = function (min, max) { + + /** + * Minimum coords of bounding box + * @type {qtek.math.Vector3} + */ + this.min = min || new Vector3(Infinity, Infinity, Infinity); + + /** + * Maximum coords of bounding box + * @type {qtek.math.Vector3} + */ + this.max = max || new Vector3(-Infinity, -Infinity, -Infinity); + }; + + BoundingBox.prototype = { + + constructor: BoundingBox, + /** + * Update min and max coords from a vertices array + * @param {array} vertices + */ + updateFromVertices: function (vertices) { + if (vertices.length > 0) { + var min = this.min; + var max = this.max; + var minArr = min._array; + var maxArr = max._array; + vec3Copy(minArr, vertices[0]); + vec3Copy(maxArr, vertices[0]); + for (var i = 1; i < vertices.length; i++) { + var vertex = vertices[i]; + + if (vertex[0] < minArr[0]) { minArr[0] = vertex[0]; } + if (vertex[1] < minArr[1]) { minArr[1] = vertex[1]; } + if (vertex[2] < minArr[2]) { minArr[2] = vertex[2]; } + + if (vertex[0] > maxArr[0]) { maxArr[0] = vertex[0]; } + if (vertex[1] > maxArr[1]) { maxArr[1] = vertex[1]; } + if (vertex[2] > maxArr[2]) { maxArr[2] = vertex[2]; } + } + min._dirty = true; + max._dirty = true; + } + }, + + /** + * Union operation with another bounding box + * @param {qtek.math.BoundingBox} bbox + */ + union: function (bbox) { + var min = this.min; + var max = this.max; + vec3.min(min._array, min._array, bbox.min._array); + vec3.max(max._array, max._array, bbox.max._array); + min._dirty = true; + max._dirty = true; + return this; + }, + + /** + * Intersection operation with another bounding box + * @param {qtek.math.BoundingBox} bbox + */ + intersection: function (bbox) { + var min = this.min; + var max = this.max; + vec3.max(min._array, min._array, bbox.min._array); + vec3.min(max._array, max._array, bbox.max._array); + min._dirty = true; + max._dirty = true; + return this; + }, + + /** + * If intersect with another bounding box + * @param {qtek.math.BoundingBox} bbox + * @return {boolean} + */ + intersectBoundingBox: function (bbox) { + var _min = this.min._array; + var _max = this.max._array; + + var _min2 = bbox.min._array; + var _max2 = bbox.max._array; + + return ! (_min[0] > _max2[0] || _min[1] > _max2[1] || _min[2] > _max2[2] + || _max[0] < _min2[0] || _max[1] < _min2[1] || _max[2] < _min2[2]); + }, + + /** + * If contain another bounding box entirely + * @param {qtek.math.BoundingBox} bbox + * @return {boolean} + */ + containBoundingBox: function (bbox) { + + var _min = this.min._array; + var _max = this.max._array; + + var _min2 = bbox.min._array; + var _max2 = bbox.max._array; + + return _min[0] <= _min2[0] && _min[1] <= _min2[1] && _min[2] <= _min2[2] + && _max[0] >= _max2[0] && _max[1] >= _max2[1] && _max[2] >= _max2[2]; + }, + + /** + * If contain point entirely + * @param {qtek.math.Vector3} point + * @return {boolean} + */ + containPoint: function (p) { + var _min = this.min._array; + var _max = this.max._array; + + var _p = p._array; + + return _min[0] <= _p[0] && _min[1] <= _p[1] && _min[2] <= _p[2] + && _max[0] >= _p[0] && _max[1] >= _p[1] && _max[2] >= _p[2]; + }, + + /** + * If bounding box is finite + */ + isFinite: function () { + var _min = this.min._array; + var _max = this.max._array; + return isFinite(_min[0]) && isFinite(_min[1]) && isFinite(_min[2]) + && isFinite(_max[0]) && isFinite(_max[1]) && isFinite(_max[2]); + }, + + /** + * Apply an affine transform matrix to the bounding box + * @param {qtek.math.Matrix4} matrix + */ + applyTransform: (function () { + // http://dev.theomader.com/transform-bounding-boxes/ + var xa = vec3.create(); + var xb = vec3.create(); + var ya = vec3.create(); + var yb = vec3.create(); + var za = vec3.create(); + var zb = vec3.create(); + + return function (matrix) { + var min = this.min._array; + var max = this.max._array; + + var m = matrix._array; + + xa[0] = m[0] * min[0]; xa[1] = m[1] * min[0]; xa[2] = m[2] * min[0]; + xb[0] = m[0] * max[0]; xb[1] = m[1] * max[0]; xb[2] = m[2] * max[0]; + + ya[0] = m[4] * min[1]; ya[1] = m[5] * min[1]; ya[2] = m[6] * min[1]; + yb[0] = m[4] * max[1]; yb[1] = m[5] * max[1]; yb[2] = m[6] * max[1]; + + za[0] = m[8] * min[2]; za[1] = m[9] * min[2]; za[2] = m[10] * min[2]; + zb[0] = m[8] * max[2]; zb[1] = m[9] * max[2]; zb[2] = m[10] * max[2]; + + min[0] = Math.min(xa[0], xb[0]) + Math.min(ya[0], yb[0]) + Math.min(za[0], zb[0]) + m[12]; + min[1] = Math.min(xa[1], xb[1]) + Math.min(ya[1], yb[1]) + Math.min(za[1], zb[1]) + m[13]; + min[2] = Math.min(xa[2], xb[2]) + Math.min(ya[2], yb[2]) + Math.min(za[2], zb[2]) + m[14]; + + max[0] = Math.max(xa[0], xb[0]) + Math.max(ya[0], yb[0]) + Math.max(za[0], zb[0]) + m[12]; + max[1] = Math.max(xa[1], xb[1]) + Math.max(ya[1], yb[1]) + Math.max(za[1], zb[1]) + m[13]; + max[2] = Math.max(xa[2], xb[2]) + Math.max(ya[2], yb[2]) + Math.max(za[2], zb[2]) + m[14]; + + this.min._dirty = true; + this.max._dirty = true; + + return this; + }; + })(), + + /** + * Apply a projection matrix to the bounding box + * @param {qtek.math.Matrix4} matrix + */ + applyProjection: function (matrix) { + var min = this.min._array; + var max = this.max._array; + + var m = matrix._array; + // min in min z + var v10 = min[0]; + var v11 = min[1]; + var v12 = min[2]; + // max in min z + var v20 = max[0]; + var v21 = max[1]; + var v22 = min[2]; + // max in max z + var v30 = max[0]; + var v31 = max[1]; + var v32 = max[2]; + + if (m[15] === 1) { // Orthographic projection + min[0] = m[0] * v10 + m[12]; + min[1] = m[5] * v11 + m[13]; + max[2] = m[10] * v12 + m[14]; + + max[0] = m[0] * v30 + m[12]; + max[1] = m[5] * v31 + m[13]; + min[2] = m[10] * v32 + m[14]; + } + else { + var w = -1 / v12; + min[0] = m[0] * v10 * w; + min[1] = m[5] * v11 * w; + max[2] = (m[10] * v12 + m[14]) * w; + + w = -1 / v22; + max[0] = m[0] * v20 * w; + max[1] = m[5] * v21 * w; + + w = -1 / v32; + min[2] = (m[10] * v32 + m[14]) * w; + } + this.min._dirty = true; + this.max._dirty = true; + + return this; + }, + + updateVertices: function () { + var vertices = this.vertices; + if (!vertices) { + // Cube vertices + var vertices = []; + for (var i = 0; i < 8; i++) { + vertices[i] = vec3.fromValues(0, 0, 0); + } + + /** + * Eight coords of bounding box + * @type {Float32Array[]} + */ + this.vertices = vertices; + } + var min = this.min._array; + var max = this.max._array; + //--- min z + // min x + vec3Set(vertices[0], min[0], min[1], min[2]); + vec3Set(vertices[1], min[0], max[1], min[2]); + // max x + vec3Set(vertices[2], max[0], min[1], min[2]); + vec3Set(vertices[3], max[0], max[1], min[2]); + + //-- max z + vec3Set(vertices[4], min[0], min[1], max[2]); + vec3Set(vertices[5], min[0], max[1], max[2]); + vec3Set(vertices[6], max[0], min[1], max[2]); + vec3Set(vertices[7], max[0], max[1], max[2]); + + return this; + }, + /** + * Copy values from another bounding box + * @param {qtek.math.BoundingBox} bbox + */ + copy: function (bbox) { + var min = this.min; + var max = this.max; + vec3Copy(min._array, bbox.min._array); + vec3Copy(max._array, bbox.max._array); + min._dirty = true; + max._dirty = true; + return this; + }, + + /** + * Clone a new bounding box + * @return {qtek.math.BoundingBox} + */ + clone: function () { + var boundingBox = new BoundingBox(); + boundingBox.copy(this); + return boundingBox; + } + }; + + module.exports = BoundingBox; + + +/***/ }, +/* 27 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Vector3 = __webpack_require__(23); + var BoundingBox = __webpack_require__(26); + var Plane = __webpack_require__(28); + + var glMatrix = __webpack_require__(14); + var vec3 = glMatrix.vec3; + + var vec3Set = vec3.set; + var vec3Copy = vec3.copy; + var vec3TranformMat4 = vec3.transformMat4; + var mathMin = Math.min; + var mathMax = Math.max; + /** + * @constructor + * @alias qtek.math.Frustum + */ + var Frustum = function() { + + /** + * Eight planes to enclose the frustum + * @type {qtek.math.Plane[]} + */ + this.planes = []; + + for (var i = 0; i < 6; i++) { + this.planes.push(new Plane()); + } + + /** + * Bounding box of frustum + * @type {qtek.math.BoundingBox} + */ + this.boundingBox = new BoundingBox(); + + /** + * Eight vertices of frustum + * @type {Float32Array[]} + */ + this.vertices = []; + for (var i = 0; i < 8; i++) { + this.vertices[i] = vec3.fromValues(0, 0, 0); + } + }; + + Frustum.prototype = { + + // http://web.archive.org/web/20120531231005/http://crazyjoke.free.fr/doc/3D/plane%20extraction.pdf + /** + * Set frustum from a projection matrix + * @param {qtek.math.Matrix4} projectionMatrix + */ + setFromProjection: function(projectionMatrix) { + + var planes = this.planes; + var m = projectionMatrix._array; + var m0 = m[0], m1 = m[1], m2 = m[2], m3 = m[3]; + var m4 = m[4], m5 = m[5], m6 = m[6], m7 = m[7]; + var m8 = m[8], m9 = m[9], m10 = m[10], m11 = m[11]; + var m12 = m[12], m13 = m[13], m14 = m[14], m15 = m[15]; + + // Update planes + vec3Set(planes[0].normal._array, m3 - m0, m7 - m4, m11 - m8); + planes[0].distance = -(m15 - m12); + planes[0].normalize(); + + vec3Set(planes[1].normal._array, m3 + m0, m7 + m4, m11 + m8); + planes[1].distance = -(m15 + m12); + planes[1].normalize(); + + vec3Set(planes[2].normal._array, m3 + m1, m7 + m5, m11 + m9); + planes[2].distance = -(m15 + m13); + planes[2].normalize(); + + vec3Set(planes[3].normal._array, m3 - m1, m7 - m5, m11 - m9); + planes[3].distance = -(m15 - m13); + planes[3].normalize(); + + vec3Set(planes[4].normal._array, m3 - m2, m7 - m6, m11 - m10); + planes[4].distance = -(m15 - m14); + planes[4].normalize(); + + vec3Set(planes[5].normal._array, m3 + m2, m7 + m6, m11 + m10); + planes[5].distance = -(m15 + m14); + planes[5].normalize(); + + // Perspective projection + var boundingBox = this.boundingBox; + if (m15 === 0) { + var aspect = m5 / m0; + var zNear = -m14 / (m10 - 1); + var zFar = -m14 / (m10 + 1); + var farY = -zFar / m5; + var nearY = -zNear / m5; + // Update bounding box + boundingBox.min.set(-farY * aspect, -farY, zFar); + boundingBox.max.set(farY * aspect, farY, zNear); + // update vertices + var vertices = this.vertices; + //--- min z + // min x + vec3Set(vertices[0], -farY * aspect, -farY, zFar); + vec3Set(vertices[1], -farY * aspect, farY, zFar); + // max x + vec3Set(vertices[2], farY * aspect, -farY, zFar); + vec3Set(vertices[3], farY * aspect, farY, zFar); + //-- max z + vec3Set(vertices[4], -nearY * aspect, -nearY, zNear); + vec3Set(vertices[5], -nearY * aspect, nearY, zNear); + vec3Set(vertices[6], nearY * aspect, -nearY, zNear); + vec3Set(vertices[7], nearY * aspect, nearY, zNear); + } + else { // Orthographic projection + var left = (-1 - m12) / m0; + var right = (1 - m12) / m0; + var top = (1 - m13) / m5; + var bottom = (-1 - m13) / m5; + var near = (-1 - m14) / m10; + var far = (1 - m14) / m10; + + boundingBox.min.set(left, bottom, far); + boundingBox.max.set(right, top, near); + + var min = boundingBox.min._array; + var max = boundingBox.max._array; + var vertices = this.vertices; + //--- min z + // min x + vec3Set(vertices[0], min[0], min[1], min[2]); + vec3Set(vertices[1], min[0], max[1], min[2]); + // max x + vec3Set(vertices[2], max[0], min[1], min[2]); + vec3Set(vertices[3], max[0], max[1], min[2]); + //-- max z + vec3Set(vertices[4], min[0], min[1], max[2]); + vec3Set(vertices[5], min[0], max[1], max[2]); + vec3Set(vertices[6], max[0], min[1], max[2]); + vec3Set(vertices[7], max[0], max[1], max[2]); + } + }, + + /** + * Apply a affine transform matrix and set to the given bounding box + * @method + * @param {qtek.math.BoundingBox} + * @param {qtek.math.Matrix4} + * @return {qtek.math.BoundingBox} + */ + getTransformedBoundingBox: (function() { + + var tmpVec3 = vec3.create(); + + return function(bbox, matrix) { + var vertices = this.vertices; + + var m4 = matrix._array; + var min = bbox.min; + var max = bbox.max; + var minArr = min._array; + var maxArr = max._array; + var v = vertices[0]; + vec3TranformMat4(tmpVec3, v, m4); + vec3Copy(minArr, tmpVec3); + vec3Copy(maxArr, tmpVec3); + + for (var i = 1; i < 8; i++) { + v = vertices[i]; + vec3TranformMat4(tmpVec3, v, m4); + + minArr[0] = mathMin(tmpVec3[0], minArr[0]); + minArr[1] = mathMin(tmpVec3[1], minArr[1]); + minArr[2] = mathMin(tmpVec3[2], minArr[2]); + + maxArr[0] = mathMax(tmpVec3[0], maxArr[0]); + maxArr[1] = mathMax(tmpVec3[1], maxArr[1]); + maxArr[2] = mathMax(tmpVec3[2], maxArr[2]); + } + + min._dirty = true; + max._dirty = true; + + return bbox; + }; + }) () + }; + module.exports = Frustum; + + +/***/ }, +/* 28 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Vector3 = __webpack_require__(23); + var glMatrix = __webpack_require__(14); + var vec3 = glMatrix.vec3; + var mat4 = glMatrix.mat4; + var vec4 = glMatrix.vec4; + + /** + * @constructor + * @alias qtek.math.Plane + * @param {qtek.math.Vector3} [normal] + * @param {number} [distance] + */ + var Plane = function(normal, distance) { + /** + * Normal of the plane + * @type {qtek.math.Vector3} + */ + this.normal = normal || new Vector3(0, 1, 0); + + /** + * Constant of the plane equation, used as distance to the origin + * @type {number} + */ + this.distance = distance || 0; + }; + + Plane.prototype = { + + constructor: Plane, + + /** + * Distance from given point to plane + * @param {qtek.math.Vector3} point + * @return {number} + */ + distanceToPoint: function(point) { + return vec3.dot(point._array, this.normal._array) - this.distance; + }, + + /** + * Calculate the projection on the plane of point + * @param {qtek.math.Vector3} point + * @param {qtek.math.Vector3} out + * @return {qtek.math.Vector3} + */ + projectPoint: function(point, out) { + if (!out) { + out = new Vector3(); + } + var d = this.distanceToPoint(point); + vec3.scaleAndAdd(out._array, point._array, this.normal._array, -d); + out._dirty = true; + return out; + }, + + /** + * Normalize the plane's normal and calculate distance + */ + normalize: function() { + var invLen = 1 / vec3.len(this.normal._array); + vec3.scale(this.normal._array, invLen); + this.distance *= invLen; + }, + + /** + * If the plane intersect a frustum + * @param {qtek.math.Frustum} Frustum + * @return {boolean} + */ + intersectFrustum: function(frustum) { + // Check if all coords of frustum is on plane all under plane + var coords = frustum.vertices; + var normal = this.normal._array; + var onPlane = vec3.dot(coords[0]._array, normal) > this.distance; + for (var i = 1; i < 8; i++) { + if ((vec3.dot(coords[i]._array, normal) > this.distance) != onPlane) { + return true; + } + } + }, + + /** + * Calculate the intersection point between plane and a given line + * @method + * @param {qtek.math.Vector3} start start point of line + * @param {qtek.math.Vector3} end end point of line + * @param {qtek.math.Vector3} [out] + * @return {qtek.math.Vector3} + */ + intersectLine: (function() { + var rd = vec3.create(); + return function(start, end, out) { + var d0 = this.distanceToPoint(start); + var d1 = this.distanceToPoint(end); + if ((d0 > 0 && d1 > 0) || (d0 < 0 && d1 < 0)) { + return null; + } + // Ray intersection + var pn = this.normal._array; + var d = this.distance; + var ro = start._array; + // direction + vec3.sub(rd, end._array, start._array); + vec3.normalize(rd, rd); + + var divider = vec3.dot(pn, rd); + // ray is parallel to the plane + if (divider === 0) { + return null; + } + if (!out) { + out = new Vector3(); + } + var t = (vec3.dot(pn, ro) - d) / divider; + vec3.scaleAndAdd(out._array, ro, rd, -t); + out._dirty = true; + return out; + }; + })(), + + /** + * Apply an affine transform matrix to plane + * @method + * @return {qtek.math.Matrix4} + */ + applyTransform: (function() { + var inverseTranspose = mat4.create(); + var normalv4 = vec4.create(); + var pointv4 = vec4.create(); + pointv4[3] = 1; + return function(m4) { + m4 = m4._array; + // Transform point on plane + vec3.scale(pointv4, this.normal._array, this.distance); + vec4.transformMat4(pointv4, pointv4, m4); + this.distance = vec3.dot(pointv4, this.normal._array); + // Transform plane normal + mat4.invert(inverseTranspose, m4); + mat4.transpose(inverseTranspose, inverseTranspose); + normalv4[3] = 0; + vec3.copy(normalv4, this.normal._array); + vec4.transformMat4(normalv4, normalv4, inverseTranspose); + vec3.copy(this.normal._array, normalv4); + }; + })(), + + /** + * Copy from another plane + * @param {qtek.math.Vector3} plane + */ + copy: function(plane) { + vec3.copy(this.normal._array, plane.normal._array); + this.normal._dirty = true; + this.distance = plane.distance; + }, + + /** + * Clone a new plane + * @return {qtek.math.Plane} + */ + clone: function() { + var plane = new Plane(); + plane.copy(this); + return plane; + } + }; + + module.exports = Plane; + + +/***/ }, +/* 29 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Vector3 = __webpack_require__(23); + var glMatrix = __webpack_require__(14); + var vec3 = glMatrix.vec3; + + var EPSILON = 1e-5; + + /** + * @constructor + * @alias qtek.math.Ray + * @param {qtek.math.Vector3} [origin] + * @param {qtek.math.Vector3} [direction] + */ + var Ray = function(origin, direction) { + /** + * @type {qtek.math.Vector3} + */ + this.origin = origin || new Vector3(); + /** + * @type {qtek.math.Vector3} + */ + this.direction = direction || new Vector3(); + }; + + Ray.prototype = { + + constructor: Ray, + + // http://www.siggraph.org/education/materials/HyperGraph/raytrace/rayplane_intersection.htm + /** + * Calculate intersection point between ray and a give plane + * @param {qtek.math.Plane} plane + * @param {qtek.math.Vector3} [out] + * @return {qtek.math.Vector3} + */ + intersectPlane: function(plane, out) { + var pn = plane.normal._array; + var d = plane.distance; + var ro = this.origin._array; + var rd = this.direction._array; + + var divider = vec3.dot(pn, rd); + // ray is parallel to the plane + if (divider === 0) { + return null; + } + if (!out) { + out = new Vector3(); + } + var t = (vec3.dot(pn, ro) - d) / divider; + vec3.scaleAndAdd(out._array, ro, rd, -t); + out._dirty = true; + return out; + }, + + /** + * Mirror the ray against plane + * @param {qtek.math.Plane} plane + */ + mirrorAgainstPlane: function(plane) { + // Distance to plane + var d = vec3.dot(plane.normal._array, this.direction._array); + vec3.scaleAndAdd(this.direction._array, this.direction._array, plane.normal._array, -d * 2); + this.direction._dirty = true; + }, + + distanceToPoint: (function () { + var v = vec3.create(); + return function (point) { + vec3.sub(v, point, this.origin._array); + // Distance from projection point to origin + var b = vec3.dot(v, this.direction._array); + if (b < 0) { + return vec3.distance(this.origin._array, point); + } + // Squared distance from center to origin + var c2 = vec3.lenSquared(v); + // Squared distance from center to projection point + return Math.sqrt(c2 - b * b); + }; + })(), + + /** + * Calculate intersection point between ray and sphere + * @param {qtek.math.Vector3} center + * @param {number} radius + * @param {qtek.math.Vector3} out + * @return {qtek.math.Vector3} + */ + intersectSphere: (function () { + var v = vec3.create(); + return function (center, radius, out) { + var origin = this.origin._array; + var direction = this.direction._array; + center = center._array; + vec3.sub(v, center, origin); + // Distance from projection point to origin + var b = vec3.dot(v, direction); + // Squared distance from center to origin + var c2 = vec3.squaredLength(v); + // Squared distance from center to projection point + var d2 = c2 - b * b; + + var r2 = radius * radius; + // No intersection + if (d2 > r2) { + return; + } + + var a = Math.sqrt(r2 - d2); + // First intersect point + var t0 = b - a; + // Second intersect point + var t1 = b + a; + + if (!out) { + out = new Vector3(); + } + if (t0 < 0) { + if (t1 < 0) { + return null; + } + else { + vec3.scaleAndAdd(out._array, origin, direction, t1); + return out; + } + } + else { + vec3.scaleAndAdd(out._array, origin, direction, t0); + return out; + } + }; + })(), + + // http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/ + /** + * Calculate intersection point between ray and bounding box + * @param {qtek.math.BoundingBox} bbox + * @param {qtek.math.Vector3} + * @return {qtek.math.Vector3} + */ + intersectBoundingBox: function(bbox, out) { + var dir = this.direction._array; + var origin = this.origin._array; + var min = bbox.min._array; + var max = bbox.max._array; + + var invdirx = 1 / dir[0]; + var invdiry = 1 / dir[1]; + var invdirz = 1 / dir[2]; + + var tmin, tmax, tymin, tymax, tzmin, tzmax; + if (invdirx >= 0) { + tmin = (min[0] - origin[0]) * invdirx; + tmax = (max[0] - origin[0]) * invdirx; + } + else { + tmax = (min[0] - origin[0]) * invdirx; + tmin = (max[0] - origin[0]) * invdirx; + } + if (invdiry >= 0) { + tymin = (min[1] - origin[1]) * invdiry; + tymax = (max[1] - origin[1]) * invdiry; + } + else { + tymax = (min[1] - origin[1]) * invdiry; + tymin = (max[1] - origin[1]) * invdiry; + } + + if ((tmin > tymax) || (tymin > tmax)) { + return null; + } + + if (tymin > tmin || tmin !== tmin) { + tmin = tymin; + } + if (tymax < tmax || tmax !== tmax) { + tmax = tymax; + } + + if (invdirz >= 0) { + tzmin = (min[2] - origin[2]) * invdirz; + tzmax = (max[2] - origin[2]) * invdirz; + } + else { + tzmax = (min[2] - origin[2]) * invdirz; + tzmin = (max[2] - origin[2]) * invdirz; + } + + if ((tmin > tzmax) || (tzmin > tmax)) { + return null; + } + + if (tzmin > tmin || tmin !== tmin) { + tmin = tzmin; + } + if (tzmax < tmax || tmax !== tmax) { + tmax = tzmax; + } + if (tmax < 0) { + return null; + } + + var t = tmin >= 0 ? tmin : tmax; + + if (!out) { + out = new Vector3(); + } + vec3.scaleAndAdd(out._array, origin, dir, t); + return out; + }, + + // http://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm + /** + * Calculate intersection point between ray and three triangle vertices + * @param {qtek.math.Vector3} a + * @param {qtek.math.Vector3} b + * @param {qtek.math.Vector3} c + * @param {boolean} singleSided, CW triangle will be ignored + * @param {qtek.math.Vector3} [out] + * @param {qtek.math.Vector3} [barycenteric] barycentric coords + * @return {qtek.math.Vector3} + */ + intersectTriangle: (function() { + + var eBA = vec3.create(); + var eCA = vec3.create(); + var AO = vec3.create(); + var vCross = vec3.create(); + + return function (a, b, c, singleSided, out, barycenteric) { + var dir = this.direction._array; + var origin = this.origin._array; + a = a._array; + b = b._array; + c = c._array; + + vec3.sub(eBA, b, a); + vec3.sub(eCA, c, a); + + vec3.cross(vCross, eCA, dir); + + var det = vec3.dot(eBA, vCross); + + if (singleSided) { + if (det > -EPSILON) { + return null; + } + } + else { + if (det > -EPSILON && det < EPSILON) { + return null; + } + } + + vec3.sub(AO, origin, a); + var u = vec3.dot(vCross, AO) / det; + if (u < 0 || u > 1) { + return null; + } + + vec3.cross(vCross, eBA, AO); + var v = vec3.dot(dir, vCross) / det; + + if (v < 0 || v > 1 || (u + v > 1)) { + return null; + } + + vec3.cross(vCross, eBA, eCA); + var t = -vec3.dot(AO, vCross) / det; + + if (t < 0) { + return null; + } + + if (!out) { + out = new Vector3(); + } + if (barycenteric) { + Vector3.set(barycenteric, (1 - u - v), u, v); + } + vec3.scaleAndAdd(out._array, origin, dir, t); + + return out; + }; + })(), + + /** + * Apply an affine transform matrix to the ray + * @return {qtek.math.Matrix4} matrix + */ + applyTransform: function(matrix) { + Vector3.add(this.direction, this.direction, this.origin); + Vector3.transformMat4(this.origin, this.origin, matrix); + Vector3.transformMat4(this.direction, this.direction, matrix); + + Vector3.sub(this.direction, this.direction, this.origin); + Vector3.normalize(this.direction, this.direction); + }, + + /** + * Copy values from another ray + * @param {qtek.math.Ray} ray + */ + copy: function(ray) { + Vector3.copy(this.origin, ray.origin); + Vector3.copy(this.direction, ray.direction); + }, + + /** + * Clone a new ray + * @return {qtek.math.Ray} + */ + clone: function() { + var ray = new Ray(); + ray.copy(this); + return ray; + } + }; + + module.exports = Ray; + + +/***/ }, +/* 30 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Camera = __webpack_require__(21); + /** + * @constructor qtek.camera.Orthographic + * @extends qtek.Camera + */ + var Orthographic = Camera.extend( + /** @lends qtek.camera.Orthographic# */ + { + /** + * @type {number} + */ + left: -1, + /** + * @type {number} + */ + right: 1, + /** + * @type {number} + */ + near: -1, + /** + * @type {number} + */ + far: 1, + /** + * @type {number} + */ + top: 1, + /** + * @type {number} + */ + bottom: -1 + }, + /** @lends qtek.camera.Orthographic.prototype */ + { + + updateProjectionMatrix: function() { + this.projectionMatrix.ortho(this.left, this.right, this.bottom, this.top, this.near, this.far); + }, + + decomposeProjectionMatrix: function () { + var m = this.projectionMatrix._array; + this.left = (-1 - m[12]) / m[0]; + this.right = (1 - m[12]) / m[0]; + this.top = (1 - m[13]) / m[5]; + this.bottom = (-1 - m[13]) / m[5]; + this.near = -(-1 - m[14]) / m[10]; + this.far = -(1 - m[14]) / m[10]; + }, + /** + * @return {qtek.camera.Orthographic} + */ + clone: function() { + var camera = Camera.prototype.clone.call(this); + camera.left = this.left; + camera.right = this.right; + camera.near = this.near; + camera.far = this.far; + camera.top = this.top; + camera.bottom = this.bottom; + + return camera; + } + }); + + module.exports = Orthographic; + + +/***/ }, +/* 31 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Camera = __webpack_require__(21); + + /** + * @constructor qtek.camera.Perspective + * @extends qtek.Camera + */ + var Perspective = Camera.extend( + /** @lends qtek.camera.Perspective# */ + { + /** + * Vertical field of view in radians + * @type {number} + */ + fov: 50, + /** + * Aspect ratio, typically viewport width / height + * @type {number} + */ + aspect: 1, + /** + * Near bound of the frustum + * @type {number} + */ + near: 0.1, + /** + * Far bound of the frustum + * @type {number} + */ + far: 2000 + }, + /** @lends qtek.camera.Perspective.prototype */ + { + + updateProjectionMatrix: function() { + var rad = this.fov / 180 * Math.PI; + this.projectionMatrix.perspective(rad, this.aspect, this.near, this.far); + }, + decomposeProjectionMatrix: function () { + var m = this.projectionMatrix._array; + var rad = Math.atan(1 / m[5]) * 2; + this.fov = rad / Math.PI * 180; + this.aspect = m[5] / m[0]; + this.near = m[14] / (m[10] - 1); + this.far = m[14] / (m[10] + 1); + }, + /** + * @return {qtek.camera.Perspective} + */ + clone: function() { + var camera = Camera.prototype.clone.call(this); + camera.fov = this.fov; + camera.aspect = this.aspect; + camera.near = this.near; + camera.far = this.far; + + return camera; + } + }); + + module.exports = Perspective; + + +/***/ }, +/* 32 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Base = __webpack_require__(3); + + var CanvasMaterial = Base.extend({ + + color: [1, 1, 1, 1], + + opacity: 1, + + pointSize: 0, + + /** + * Point shape. Can be 'rectangle' and 'circle' + * @type {string} + */ + pointShape: 'rectangle' + }); + + module.exports = CanvasMaterial; + + +/***/ }, +/* 33 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Base = __webpack_require__(3); + var glMatrix = __webpack_require__(14); + var glenum = __webpack_require__(34); + var mat4 = glMatrix.mat4; + var vec3 = glMatrix.vec3; + var vec4 = glMatrix.vec4; + + var vec3Set = vec3.set; + var vec3Create = vec3.create; + + var vec4Create = vec4.create; + + var round = Math.round; + + var PRIMITIVE_TRIANGLE = 1; + var PRIMITIVE_LINE = 2; + var PRIMITIVE_POINT = 3; + + function PrimitivePool(constructor) { + this.ctor = constructor; + + this._data = []; + + this._size = 0; + } + + PrimitivePool.prototype = { + pick: function () { + var data = this._data; + var size = this._size; + var obj = data[size]; + if (! obj) { + // Constructor must have no parameters + obj = new this.ctor(); + data[size] = obj; + } + this._size++; + return obj; + }, + + reset: function () { + this._size = 0; + }, + + shrink: function () { + this._data.length = this._size; + }, + + clear: function () { + this._data = []; + this._size = 0; + } + } + + function Triangle() { + this.vertices = [vec4Create(), vec4Create(), vec4Create()]; + this.color = vec4Create(); + + this.depth = 0; + } + + Triangle.prototype.type = PRIMITIVE_TRIANGLE; + + function Point() { + // Here use an array to make it more convinient to proccessing in _setPrimitive method + this.vertices = [vec4Create()]; + + this.color = vec4Create(); + + this.depth = 0; + } + + Point.prototype.type = PRIMITIVE_POINT; + + function Line() { + this.vertices = [vec4Create(), vec4Create()]; + this.color = vec4Create(); + + this.depth = 0; + + this.lineWidth = 1; + } + + Line.prototype.type = PRIMITIVE_LINE; + + function depthSortFunc(x, y) { + // Sort from far to near, which in depth of projection space is from larger to smaller + return y.depth - x.depth; + } + + function vec3ToColorStr(v3) { + return 'rgb(' + round(v3[0] * 255) + ',' + round(v3[1] * 255) + ',' + round(v3[2] * 255) + ')'; + } + + function vec4ToColorStr(v4) { + return 'rgba(' + round(v4[0] * 255) + ',' + round(v4[1] * 255) + ',' + round(v4[2] * 255) + ',' + v4[3] + ')'; + } + + var CanvasRenderer = Base.extend({ + + canvas: null, + + _width: 100, + + _height: 100, + + devicePixelRatio: window.devicePixelRatio || 1.0, + + color: [0.0, 0.0, 0.0, 0.0], + + clear: true, + + ctx: null, + + // Cached primitive list, including triangle, line, point + _primitives: [], + + // Triangle pool + _triangles: new PrimitivePool(Triangle), + + // Line pool + _lines: new PrimitivePool(Line), + + // Point pool + _points: new PrimitivePool(Point) + }, function () { + if (! this.canvas) { + this.canvas = document.createElement('canvas'); + } + var canvas = this.canvas; + + try { + this.ctx = canvas.getContext('2d'); + var ctx = this.ctx; + if (! ctx) { + throw new Error(); + } + } + catch (e) { + throw 'Error creating WebGL Context ' + e; + } + + this.resize(); + }, { + + resize: function (width, height) { + var dpr = this.devicePixelRatio; + var canvas = this.canvas; + if (width != null) { + canvas.style.width = width + 'px'; + canvas.style.height = height + 'px'; + canvas.width = width * dpr; + canvas.height = height * dpr; + + this._width = width; + this._height = height; + } + else { + this._width = canvas.width / dpr; + this._height = canvas.height / dpr; + } + }, + + getWidth: function () { + return this._width; + }, + + getHeight: function () { + return this._height; + }, + + getViewportAspect: function () { + return this._width / this._height; + }, + + render: function (scene, camera) { + + if (this.clear) { + var color = this.color; + var ctx = this.ctx; + var dpr = this.devicePixelRatio; + var w = this._width * dpr; + var h = this._height * dpr; + if (color && color[3] === 0) { + ctx.clearRect(0, 0, w, h); + } + else { + // Has transparency + if (color[3] < 1) { + ctx.clearRect(0, 0, w, h); + } + ctx.fillStyle = color.length === 4 ? vec4ToColorStr(color) : vec3ToColorStr(color); + ctx.fillRect(0, 0, w, h); + } + } + + scene.update(); + camera.update(); + + var opaqueQueue = scene.opaqueQueue; + var transparentQueue = scene.transparentQueue; + var sceneMaterial = scene.material; + + var queue = opaqueQueue.concat(transparentQueue); + + this.renderQueue(queue, camera); + }, + + renderQueue: function (queue, camera) { + var viewProj = mat4.create(); + mat4.multiply(viewProj, camera.projectionMatrix._array, camera.viewMatrix._array); + var worldViewProjMat = mat4.create(); + var posViewSpace = vec3.create(); + + var primitives = this._primitives; + var trianglesPool = this._triangles; + var linesPool = this._lines; + var pointsPool = this._points; + + trianglesPool.reset(); + linesPool.reset(); + pointsPool.reset(); + + var nPrimitive = 0; + + var indices = [0, 0, 0]; + var matColor = []; + for (var i = 0; i < queue.length; i++) { + var renderable = queue[i]; + + mat4.multiply(worldViewProjMat, viewProj, renderable.worldTransform._array); + + var geometry = renderable.geometry; + var material = renderable.material; + var attributes = geometry.attributes; + + // alpha is default 1 + if (material.color.length == 3) { + vec3.copy(matColor, material.color); + matColor[3] = 1; + } + else { + vec4.copy(matColor, material.color); + } + + var nVertex = geometry.vertexCount; + // Only support TRIANGLES, LINES, POINTS draw modes + switch (renderable.mode) { + case glenum.TRIANGLES: + if (geometry.isUseIndices()) { + var nFace = geometry.triangleCount; + for (var j = 0; j < nFace; j++) { + geometry.getFace(j, indices); + + var triangle = trianglesPool.pick(); + triangle.material = material; + + var clipped = this._setPrimitive(triangle, indices, 3, attributes, worldViewProjMat, matColor); + + if (! clipped) { + primitives[nPrimitive++] = triangle; + } + } + } + else { + for (var j = 0; j < nVertex;) { + indices[0] = j++; + indices[1] = j++; + indices[2] = j++; + + var triangle = trianglesPool.pick(); + triangle.material = material; + + var clipped = this._setPrimitive(triangle, indices, 3, attributes, worldViewProjMat, matColor); + + if (! clipped) { + primitives[nPrimitive++] = triangle; + } + } + } + break; + case glenum.LINES: + // LINES mode can't use face + for (var j = 0; j < nVertex;) { + indices[0] = j++; + indices[1] = j++; + var line = linesPool.pick(); + line.material = material; + line.lineWidth = renderable.lineWidth; + + var clipped = this._setPrimitive(line, indices, 2, attributes, worldViewProjMat, matColor); + + if (! clipped) { + primitives[nPrimitive++] = line; + } + } + break; + case glenum.POINTS: + for (var j = 0; j < nVertex; j++) { + indices[0] = j; + var point = pointsPool.pick(); + point.material = material; + + var clipped = this._setPrimitive(point, indices, 1, attributes, worldViewProjMat, matColor); + + if (! clipped) { + primitives[nPrimitive++] = point; + } + } + // POINTS mode can't use face + break; + } + } + + trianglesPool.shrink(); + linesPool.shrink(); + pointsPool.shrink(); + + primitives.length = nPrimitive; + + primitives.sort(depthSortFunc); + this._drawPrimitives(primitives); + }, + + _setPrimitive: (function () { + var vertexColor = vec4Create(); + return function (primitive, indices, size, attributes, worldViewProjMat, matColor) { + var colorAttrib = attributes.color; + var useVertexColor = colorAttrib.value && colorAttrib.value.length > 0; + var priColor = primitive.color; + + primitive.depth = 0; + if (useVertexColor) { + vec4.set(priColor, 0, 0, 0, 0); + } + + var clipped = true; + + var percent = 1 / size; + for (var i = 0; i < size; i++) { + var coord = primitive.vertices[i]; + attributes.position.get(indices[i], coord); + coord[3] = 1; + vec4.transformMat4(coord, coord, worldViewProjMat); + if (useVertexColor) { + colorAttrib.get(indices[i], vertexColor); + // Average vertex color + // Each primitive only call fill or stroke once + // So color must be the same + vec4.scaleAndAdd(priColor, priColor, vertexColor, percent); + } + + // Clipping + var x = coord[0]; + var y = coord[1]; + var z = coord[2]; + var w = coord[3]; + + // TODO Point clipping + if (x > -w && x < w && y > -w && y < w && z > -w && z < w) { + clipped = false; + } + + var invW = 1 / w; + coord[0] = x * invW; + coord[1] = y * invW; + coord[2] = z * invW; + // Use primitive average depth; + primitive.depth += coord[2]; + } + + if (! clipped) { + primitive.depth /= size; + + if (useVertexColor) { + vec4.mul(priColor, priColor, matColor); + } + else { + vec4.copy(priColor, matColor); + } + } + + return clipped; + } + })(), + + _drawPrimitives: function (primitives) { + var ctx = this.ctx; + ctx.save(); + + var prevMaterial; + + var dpr = this.devicePixelRatio; + var width = this._width * dpr; + var height = this._height * dpr; + var halfWidth = width / 2; + var halfHeight = height / 2; + + var prevLineWidth; + var prevStrokeColor; + + for (var i = 0; i < primitives.length; i++) { + var primitive = primitives[i]; + var vertices = primitive.vertices; + + var primitiveType = primitive.type; + var material = primitive.material; + if (material !== prevMaterial) { + // Set material + ctx.globalAlpha = material.opacity; + prevMaterial = material; + } + + var colorStr = vec4ToColorStr(primitive.color); + switch (primitiveType) { + case PRIMITIVE_TRIANGLE: + var v0 = vertices[0]; + var v1 = vertices[1]; + var v2 = vertices[2]; + ctx.fillStyle = colorStr; + ctx.beginPath(); + ctx.moveTo((v0[0] + 1) * halfWidth, (-v0[1] + 1) * halfHeight); + ctx.lineTo((v1[0] + 1) * halfWidth, (-v1[1] + 1) * halfHeight); + ctx.lineTo((v2[0] + 1) * halfWidth, (-v2[1] + 1) * halfHeight); + ctx.closePath(); + ctx.fill(); + break; + case PRIMITIVE_LINE: + var v0 = vertices[0]; + var v1 = vertices[1]; + var lineWidth = primitive.lineWidth; + if (prevStrokeColor !== colorStr) { + prevStrokeColor = ctx.strokeStyle = colorStr; + } + if (lineWidth !== prevLineWidth) { + ctx.lineWidth = prevLineWidth = lineWidth; + } + ctx.beginPath(); + ctx.moveTo((v0[0] + 1) * halfWidth, (-v0[1] + 1) * halfHeight); + ctx.lineTo((v1[0] + 1) * halfWidth, (-v1[1] + 1) * halfHeight); + ctx.stroke(); + break; + case PRIMITIVE_POINT: + var pointSize = material.pointSize; + var pointShape = material.pointShape; + var halfSize = pointSize / 2; + if (pointSize > 0) { + var v0 = vertices[0]; + var cx = (v0[0] + 1) * halfWidth; + var cy = (-v0[1] + 1) * halfHeight; + + ctx.fillStyle = colorStr; + if (pointShape === 'rectangle') { + ctx.fillRect(cx - halfSize, cy - halfSize, pointSize, pointSize); + } + else if (pointShape === 'circle') { + ctx.beginPath(); + ctx.arc(cx, cy, halfSize, 0, Math.PI * 2); + ctx.fill(); + } + } + break; + } + } + + ctx.restore(); + }, + + dispose: function () { + this._triangles.clear(); + this._lines.clear(); + this._points.clear(); + this._primitives = []; + + this.ctx = null; + this.canvas = null; + } + }); + + module.exports = CanvasRenderer; + + +/***/ }, +/* 34 */ +/***/ function(module, exports) { + + /** + * @namespace qtek.core.glenum + * @see http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14 + */ + + + module.exports = { + /* ClearBufferMask */ + DEPTH_BUFFER_BIT : 0x00000100, + STENCIL_BUFFER_BIT : 0x00000400, + COLOR_BUFFER_BIT : 0x00004000, + + /* BeginMode */ + POINTS : 0x0000, + LINES : 0x0001, + LINE_LOOP : 0x0002, + LINE_STRIP : 0x0003, + TRIANGLES : 0x0004, + TRIANGLE_STRIP : 0x0005, + TRIANGLE_FAN : 0x0006, + + /* AlphaFunction (not supported in ES20) */ + /* NEVER */ + /* LESS */ + /* EQUAL */ + /* LEQUAL */ + /* GREATER */ + /* NOTEQUAL */ + /* GEQUAL */ + /* ALWAYS */ + + /* BlendingFactorDest */ + ZERO : 0, + ONE : 1, + SRC_COLOR : 0x0300, + ONE_MINUS_SRC_COLOR : 0x0301, + SRC_ALPHA : 0x0302, + ONE_MINUS_SRC_ALPHA : 0x0303, + DST_ALPHA : 0x0304, + ONE_MINUS_DST_ALPHA : 0x0305, + + /* BlendingFactorSrc */ + /* ZERO */ + /* ONE */ + DST_COLOR : 0x0306, + ONE_MINUS_DST_COLOR : 0x0307, + SRC_ALPHA_SATURATE : 0x0308, + /* SRC_ALPHA */ + /* ONE_MINUS_SRC_ALPHA */ + /* DST_ALPHA */ + /* ONE_MINUS_DST_ALPHA */ + + /* BlendEquationSeparate */ + FUNC_ADD : 0x8006, + BLEND_EQUATION : 0x8009, + BLEND_EQUATION_RGB : 0x8009, /* same as BLEND_EQUATION */ + BLEND_EQUATION_ALPHA : 0x883D, + + /* BlendSubtract */ + FUNC_SUBTRACT : 0x800A, + FUNC_REVERSE_SUBTRACT : 0x800B, + + /* Separate Blend Functions */ + BLEND_DST_RGB : 0x80C8, + BLEND_SRC_RGB : 0x80C9, + BLEND_DST_ALPHA : 0x80CA, + BLEND_SRC_ALPHA : 0x80CB, + CONSTANT_COLOR : 0x8001, + ONE_MINUS_CONSTANT_COLOR : 0x8002, + CONSTANT_ALPHA : 0x8003, + ONE_MINUS_CONSTANT_ALPHA : 0x8004, + BLEND_COLOR : 0x8005, + + /* Buffer Objects */ + ARRAY_BUFFER : 0x8892, + ELEMENT_ARRAY_BUFFER : 0x8893, + ARRAY_BUFFER_BINDING : 0x8894, + ELEMENT_ARRAY_BUFFER_BINDING : 0x8895, + + STREAM_DRAW : 0x88E0, + STATIC_DRAW : 0x88E4, + DYNAMIC_DRAW : 0x88E8, + + BUFFER_SIZE : 0x8764, + BUFFER_USAGE : 0x8765, + + CURRENT_VERTEX_ATTRIB : 0x8626, + + /* CullFaceMode */ + FRONT : 0x0404, + BACK : 0x0405, + FRONT_AND_BACK : 0x0408, + + /* DepthFunction */ + /* NEVER */ + /* LESS */ + /* EQUAL */ + /* LEQUAL */ + /* GREATER */ + /* NOTEQUAL */ + /* GEQUAL */ + /* ALWAYS */ + + /* EnableCap */ + /* TEXTURE_2D */ + CULL_FACE : 0x0B44, + BLEND : 0x0BE2, + DITHER : 0x0BD0, + STENCIL_TEST : 0x0B90, + DEPTH_TEST : 0x0B71, + SCISSOR_TEST : 0x0C11, + POLYGON_OFFSET_FILL : 0x8037, + SAMPLE_ALPHA_TO_COVERAGE : 0x809E, + SAMPLE_COVERAGE : 0x80A0, + + /* ErrorCode */ + NO_ERROR : 0, + INVALID_ENUM : 0x0500, + INVALID_VALUE : 0x0501, + INVALID_OPERATION : 0x0502, + OUT_OF_MEMORY : 0x0505, + + /* FrontFaceDirection */ + CW : 0x0900, + CCW : 0x0901, + + /* GetPName */ + LINE_WIDTH : 0x0B21, + ALIASED_POINT_SIZE_RANGE : 0x846D, + ALIASED_LINE_WIDTH_RANGE : 0x846E, + CULL_FACE_MODE : 0x0B45, + FRONT_FACE : 0x0B46, + DEPTH_RANGE : 0x0B70, + DEPTH_WRITEMASK : 0x0B72, + DEPTH_CLEAR_VALUE : 0x0B73, + DEPTH_FUNC : 0x0B74, + STENCIL_CLEAR_VALUE : 0x0B91, + STENCIL_FUNC : 0x0B92, + STENCIL_FAIL : 0x0B94, + STENCIL_PASS_DEPTH_FAIL : 0x0B95, + STENCIL_PASS_DEPTH_PASS : 0x0B96, + STENCIL_REF : 0x0B97, + STENCIL_VALUE_MASK : 0x0B93, + STENCIL_WRITEMASK : 0x0B98, + STENCIL_BACK_FUNC : 0x8800, + STENCIL_BACK_FAIL : 0x8801, + STENCIL_BACK_PASS_DEPTH_FAIL : 0x8802, + STENCIL_BACK_PASS_DEPTH_PASS : 0x8803, + STENCIL_BACK_REF : 0x8CA3, + STENCIL_BACK_VALUE_MASK : 0x8CA4, + STENCIL_BACK_WRITEMASK : 0x8CA5, + VIEWPORT : 0x0BA2, + SCISSOR_BOX : 0x0C10, + /* SCISSOR_TEST */ + COLOR_CLEAR_VALUE : 0x0C22, + COLOR_WRITEMASK : 0x0C23, + UNPACK_ALIGNMENT : 0x0CF5, + PACK_ALIGNMENT : 0x0D05, + MAX_TEXTURE_SIZE : 0x0D33, + MAX_VIEWPORT_DIMS : 0x0D3A, + SUBPIXEL_BITS : 0x0D50, + RED_BITS : 0x0D52, + GREEN_BITS : 0x0D53, + BLUE_BITS : 0x0D54, + ALPHA_BITS : 0x0D55, + DEPTH_BITS : 0x0D56, + STENCIL_BITS : 0x0D57, + POLYGON_OFFSET_UNITS : 0x2A00, + /* POLYGON_OFFSET_FILL */ + POLYGON_OFFSET_FACTOR : 0x8038, + TEXTURE_BINDING_2D : 0x8069, + SAMPLE_BUFFERS : 0x80A8, + SAMPLES : 0x80A9, + SAMPLE_COVERAGE_VALUE : 0x80AA, + SAMPLE_COVERAGE_INVERT : 0x80AB, + + /* GetTextureParameter */ + /* TEXTURE_MAG_FILTER */ + /* TEXTURE_MIN_FILTER */ + /* TEXTURE_WRAP_S */ + /* TEXTURE_WRAP_T */ + + COMPRESSED_TEXTURE_FORMATS : 0x86A3, + + /* HintMode */ + DONT_CARE : 0x1100, + FASTEST : 0x1101, + NICEST : 0x1102, + + /* HintTarget */ + GENERATE_MIPMAP_HINT : 0x8192, + + /* DataType */ + BYTE : 0x1400, + UNSIGNED_BYTE : 0x1401, + SHORT : 0x1402, + UNSIGNED_SHORT : 0x1403, + INT : 0x1404, + UNSIGNED_INT : 0x1405, + FLOAT : 0x1406, + + /* PixelFormat */ + DEPTH_COMPONENT : 0x1902, + ALPHA : 0x1906, + RGB : 0x1907, + RGBA : 0x1908, + LUMINANCE : 0x1909, + LUMINANCE_ALPHA : 0x190A, + + /* PixelType */ + /* UNSIGNED_BYTE */ + UNSIGNED_SHORT_4_4_4_4 : 0x8033, + UNSIGNED_SHORT_5_5_5_1 : 0x8034, + UNSIGNED_SHORT_5_6_5 : 0x8363, + + /* Shaders */ + FRAGMENT_SHADER : 0x8B30, + VERTEX_SHADER : 0x8B31, + MAX_VERTEX_ATTRIBS : 0x8869, + MAX_VERTEX_UNIFORM_VECTORS : 0x8DFB, + MAX_VARYING_VECTORS : 0x8DFC, + MAX_COMBINED_TEXTURE_IMAGE_UNITS : 0x8B4D, + MAX_VERTEX_TEXTURE_IMAGE_UNITS : 0x8B4C, + MAX_TEXTURE_IMAGE_UNITS : 0x8872, + MAX_FRAGMENT_UNIFORM_VECTORS : 0x8DFD, + SHADER_TYPE : 0x8B4F, + DELETE_STATUS : 0x8B80, + LINK_STATUS : 0x8B82, + VALIDATE_STATUS : 0x8B83, + ATTACHED_SHADERS : 0x8B85, + ACTIVE_UNIFORMS : 0x8B86, + ACTIVE_ATTRIBUTES : 0x8B89, + SHADING_LANGUAGE_VERSION : 0x8B8C, + CURRENT_PROGRAM : 0x8B8D, + + /* StencilFunction */ + NEVER : 0x0200, + LESS : 0x0201, + EQUAL : 0x0202, + LEQUAL : 0x0203, + GREATER : 0x0204, + NOTEQUAL : 0x0205, + GEQUAL : 0x0206, + ALWAYS : 0x0207, + + /* StencilOp */ + /* ZERO */ + KEEP : 0x1E00, + REPLACE : 0x1E01, + INCR : 0x1E02, + DECR : 0x1E03, + INVERT : 0x150A, + INCR_WRAP : 0x8507, + DECR_WRAP : 0x8508, + + /* StringName */ + VENDOR : 0x1F00, + RENDERER : 0x1F01, + VERSION : 0x1F02, + + /* TextureMagFilter */ + NEAREST : 0x2600, + LINEAR : 0x2601, + + /* TextureMinFilter */ + /* NEAREST */ + /* LINEAR */ + NEAREST_MIPMAP_NEAREST : 0x2700, + LINEAR_MIPMAP_NEAREST : 0x2701, + NEAREST_MIPMAP_LINEAR : 0x2702, + LINEAR_MIPMAP_LINEAR : 0x2703, + + /* TextureParameterName */ + TEXTURE_MAG_FILTER : 0x2800, + TEXTURE_MIN_FILTER : 0x2801, + TEXTURE_WRAP_S : 0x2802, + TEXTURE_WRAP_T : 0x2803, + + /* TextureTarget */ + TEXTURE_2D : 0x0DE1, + TEXTURE : 0x1702, + + TEXTURE_CUBE_MAP : 0x8513, + TEXTURE_BINDING_CUBE_MAP : 0x8514, + TEXTURE_CUBE_MAP_POSITIVE_X : 0x8515, + TEXTURE_CUBE_MAP_NEGATIVE_X : 0x8516, + TEXTURE_CUBE_MAP_POSITIVE_Y : 0x8517, + TEXTURE_CUBE_MAP_NEGATIVE_Y : 0x8518, + TEXTURE_CUBE_MAP_POSITIVE_Z : 0x8519, + TEXTURE_CUBE_MAP_NEGATIVE_Z : 0x851A, + MAX_CUBE_MAP_TEXTURE_SIZE : 0x851C, + + /* TextureUnit */ + TEXTURE0 : 0x84C0, + TEXTURE1 : 0x84C1, + TEXTURE2 : 0x84C2, + TEXTURE3 : 0x84C3, + TEXTURE4 : 0x84C4, + TEXTURE5 : 0x84C5, + TEXTURE6 : 0x84C6, + TEXTURE7 : 0x84C7, + TEXTURE8 : 0x84C8, + TEXTURE9 : 0x84C9, + TEXTURE10 : 0x84CA, + TEXTURE11 : 0x84CB, + TEXTURE12 : 0x84CC, + TEXTURE13 : 0x84CD, + TEXTURE14 : 0x84CE, + TEXTURE15 : 0x84CF, + TEXTURE16 : 0x84D0, + TEXTURE17 : 0x84D1, + TEXTURE18 : 0x84D2, + TEXTURE19 : 0x84D3, + TEXTURE20 : 0x84D4, + TEXTURE21 : 0x84D5, + TEXTURE22 : 0x84D6, + TEXTURE23 : 0x84D7, + TEXTURE24 : 0x84D8, + TEXTURE25 : 0x84D9, + TEXTURE26 : 0x84DA, + TEXTURE27 : 0x84DB, + TEXTURE28 : 0x84DC, + TEXTURE29 : 0x84DD, + TEXTURE30 : 0x84DE, + TEXTURE31 : 0x84DF, + ACTIVE_TEXTURE : 0x84E0, + + /* TextureWrapMode */ + REPEAT : 0x2901, + CLAMP_TO_EDGE : 0x812F, + MIRRORED_REPEAT : 0x8370, + + /* Uniform Types */ + FLOAT_VEC2 : 0x8B50, + FLOAT_VEC3 : 0x8B51, + FLOAT_VEC4 : 0x8B52, + INT_VEC2 : 0x8B53, + INT_VEC3 : 0x8B54, + INT_VEC4 : 0x8B55, + BOOL : 0x8B56, + BOOL_VEC2 : 0x8B57, + BOOL_VEC3 : 0x8B58, + BOOL_VEC4 : 0x8B59, + FLOAT_MAT2 : 0x8B5A, + FLOAT_MAT3 : 0x8B5B, + FLOAT_MAT4 : 0x8B5C, + SAMPLER_2D : 0x8B5E, + SAMPLER_CUBE : 0x8B60, + + /* Vertex Arrays */ + VERTEX_ATTRIB_ARRAY_ENABLED : 0x8622, + VERTEX_ATTRIB_ARRAY_SIZE : 0x8623, + VERTEX_ATTRIB_ARRAY_STRIDE : 0x8624, + VERTEX_ATTRIB_ARRAY_TYPE : 0x8625, + VERTEX_ATTRIB_ARRAY_NORMALIZED : 0x886A, + VERTEX_ATTRIB_ARRAY_POINTER : 0x8645, + VERTEX_ATTRIB_ARRAY_BUFFER_BINDING : 0x889F, + + /* Shader Source */ + COMPILE_STATUS : 0x8B81, + + /* Shader Precision-Specified Types */ + LOW_FLOAT : 0x8DF0, + MEDIUM_FLOAT : 0x8DF1, + HIGH_FLOAT : 0x8DF2, + LOW_INT : 0x8DF3, + MEDIUM_INT : 0x8DF4, + HIGH_INT : 0x8DF5, + + /* Framebuffer Object. */ + FRAMEBUFFER : 0x8D40, + RENDERBUFFER : 0x8D41, + + RGBA4 : 0x8056, + RGB5_A1 : 0x8057, + RGB565 : 0x8D62, + DEPTH_COMPONENT16 : 0x81A5, + STENCIL_INDEX : 0x1901, + STENCIL_INDEX8 : 0x8D48, + DEPTH_STENCIL : 0x84F9, + + RENDERBUFFER_WIDTH : 0x8D42, + RENDERBUFFER_HEIGHT : 0x8D43, + RENDERBUFFER_INTERNAL_FORMAT : 0x8D44, + RENDERBUFFER_RED_SIZE : 0x8D50, + RENDERBUFFER_GREEN_SIZE : 0x8D51, + RENDERBUFFER_BLUE_SIZE : 0x8D52, + RENDERBUFFER_ALPHA_SIZE : 0x8D53, + RENDERBUFFER_DEPTH_SIZE : 0x8D54, + RENDERBUFFER_STENCIL_SIZE : 0x8D55, + + FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE : 0x8CD0, + FRAMEBUFFER_ATTACHMENT_OBJECT_NAME : 0x8CD1, + FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL : 0x8CD2, + FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE : 0x8CD3, + + COLOR_ATTACHMENT0 : 0x8CE0, + DEPTH_ATTACHMENT : 0x8D00, + STENCIL_ATTACHMENT : 0x8D20, + DEPTH_STENCIL_ATTACHMENT : 0x821A, + + NONE : 0, + + FRAMEBUFFER_COMPLETE : 0x8CD5, + FRAMEBUFFER_INCOMPLETE_ATTACHMENT : 0x8CD6, + FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT : 0x8CD7, + FRAMEBUFFER_INCOMPLETE_DIMENSIONS : 0x8CD9, + FRAMEBUFFER_UNSUPPORTED : 0x8CDD, + + FRAMEBUFFER_BINDING : 0x8CA6, + RENDERBUFFER_BINDING : 0x8CA7, + MAX_RENDERBUFFER_SIZE : 0x84E8, + + INVALID_FRAMEBUFFER_OPERATION : 0x0506, + + /* WebGL-specific enums */ + UNPACK_FLIP_Y_WEBGL : 0x9240, + UNPACK_PREMULTIPLY_ALPHA_WEBGL : 0x9241, + CONTEXT_LOST_WEBGL : 0x9242, + UNPACK_COLORSPACE_CONVERSION_WEBGL : 0x9243, + BROWSER_DEFAULT_WEBGL : 0x9244, + }; + + +/***/ }, +/* 35 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Graph = __webpack_require__(36); + var TexturePool = __webpack_require__(38); + var FrameBuffer = __webpack_require__(44); + + /** + * Compositor provide graph based post processing + * + * @constructor qtek.compositor.Compositor + * @extends qtek.compositor.Graph + * + */ + var Compositor = Graph.extend(function() { + return { + // Output node + _outputs: [], + + _texturePool: new TexturePool(), + + _frameBuffer: new FrameBuffer({ + depthBuffer: false + }) + }; + }, + /** @lends qtek.compositor.Compositor.prototype */ + { + addNode: function(node) { + Graph.prototype.addNode.call(this, node); + node._compositor = this; + }, + /** + * @param {qtek.Renderer} renderer + */ + render: function(renderer, frameBuffer) { + if (this._dirty) { + this.update(); + this._dirty = false; + + this._outputs.length = 0; + for (var i = 0; i < this.nodes.length; i++) { + if (!this.nodes[i].outputs) { + this._outputs.push(this.nodes[i]); + } + } + } + + for (var i = 0; i < this.nodes.length; i++) { + // Update the reference number of each output texture + this.nodes[i].beforeFrame(); + } + + for (var i = 0; i < this._outputs.length; i++) { + this._outputs[i].updateReference(); + } + + for (var i = 0; i < this._outputs.length; i++) { + this._outputs[i].render(renderer, frameBuffer); + } + + for (var i = 0; i < this.nodes.length; i++) { + // Clear up + this.nodes[i].afterFrame(); + } + }, + + allocateTexture: function (parameters) { + return this._texturePool.get(parameters); + }, + + releaseTexture: function (parameters) { + this._texturePool.put(parameters); + }, + + getFrameBuffer: function () { + return this._frameBuffer; + }, + + /** + * Dispose compositor + * @param {WebGLRenderingContext|qtek.Renderer} renderer + */ + dispose: function (renderer) { + this._texturePool.clear(renderer.gl || renderer); + } + }); + + module.exports = Compositor; + + +/***/ }, +/* 36 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Base = __webpack_require__(3); + var GraphNode = __webpack_require__(37); + + /** + * @constructor qtek.compositor.Graph + * @extends qtek.core.Base + */ + var Graph = Base.extend(function () { + return /** @lends qtek.compositor.Graph# */ { + /** + * @type {Array.} + */ + nodes: [] + }; + }, + /** @lends qtek.compositor.Graph.prototype */ + { + + /** + * Mark to update + */ + dirty: function () { + this._dirty = true; + }, + /** + * @param {qtek.compositor.Node} node + */ + addNode: function (node) { + + if (this.nodes.indexOf(node) >= 0) { + return; + } + + this.nodes.push(node); + + this._dirty = true; + }, + /** + * @param {qtek.compositor.Node|string} node + */ + removeNode: function (node) { + if (typeof node === 'string') { + node = this.getNodeByName(node); + } + var idx = this.nodes.indexOf(node); + if (idx >= 0) { + this.nodes.splice(idx, 1); + this._dirty = true; + } + }, + /** + * @param {string} name + * @return {qtek.compositor.Node} + */ + getNodeByName: function (name) { + for (var i = 0; i < this.nodes.length; i++) { + if (this.nodes[i].name === name) { + return this.nodes[i]; + } + } + }, + /** + * Update links of graph + */ + update: function () { + for (var i = 0; i < this.nodes.length; i++) { + this.nodes[i].clear(); + } + // Traverse all the nodes and build the graph + for (var i = 0; i < this.nodes.length; i++) { + var node = this.nodes[i]; + + if (!node.inputs) { + continue; + } + for (var inputName in node.inputs) { + if (!node.inputs[inputName]) { + continue; + } + if (node.pass && !node.pass.material.isUniformEnabled(inputName)) { + console.warn('Pin ' + node.name + '.' + inputName + ' not used.'); + continue; + } + var fromPinInfo = node.inputs[inputName]; + + var fromPin = this.findPin(fromPinInfo); + if (fromPin) { + node.link(inputName, fromPin.node, fromPin.pin); + } + else { + if (typeof fromPinInfo === 'string') { + console.warn('Node ' + fromPinInfo + ' not exist'); + } + else { + console.warn('Pin of ' + fromPinInfo.node + '.' + fromPinInfo.pin + ' not exist'); + } + } + } + } + }, + + findPin: function (input) { + var node; + // Try to take input as a directly a node + if (typeof input === 'string' || input instanceof GraphNode) { + input = { + node: input + }; + } + + if (typeof input.node === 'string') { + for (var i = 0; i < this.nodes.length; i++) { + var tmp = this.nodes[i]; + if (tmp.name === input.node) { + node = tmp; + } + } + } + else { + node = input.node; + } + if (node) { + var inputPin = input.pin; + if (!inputPin) { + // Use first pin defaultly + if (node.outputs) { + inputPin = Object.keys(node.outputs)[0]; + } + } + if (node.outputs[inputPin]) { + return { + node: node, + pin: inputPin + }; + } + } + } + }); + + module.exports = Graph; + + +/***/ }, +/* 37 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Base = __webpack_require__(3); + + // PENDING + // Use topological sort ? + + /** + * Node of graph based post processing. + * + * @constructor qtek.compositor.Node + * @extends qtek.core.Base + * + */ + var Node = Base.extend(function () { + return /** @lends qtek.compositor.Node# */ { + /** + * @type {string} + */ + name: '', + + /** + * Input links, will be updated by the graph + * @example: + * inputName: { + * node: someNode, + * pin: 'xxxx' + * } + * @type {Object} + */ + inputLinks: {}, + + /** + * Output links, will be updated by the graph + * @example: + * outputName: { + * node: someNode, + * pin: 'xxxx' + * } + * @type {Object} + */ + outputLinks: {}, + + // Save the output texture of previous frame + // Will be used when there exist a circular reference + _prevOutputTextures: {}, + _outputTextures: {}, + + // Example: { name: 2 } + _outputReferences: {}, + + _rendering: false, + // If rendered in this frame + _rendered: false, + + _compositor: null + }; + }, + /** @lends qtek.compositor.Node.prototype */ + { + + // TODO Remove parameter function callback + updateParameter: function (outputName, renderer) { + var outputInfo = this.outputs[outputName]; + var parameters = outputInfo.parameters; + var parametersCopy = outputInfo._parametersCopy; + if (!parametersCopy) { + parametersCopy = outputInfo._parametersCopy = {}; + } + if (parameters) { + for (var key in parameters) { + if (key !== 'width' && key !== 'height') { + parametersCopy[key] = parameters[key]; + } + } + } + var width, height; + if (parameters.width instanceof Function) { + width = parameters.width.call(this, renderer); + } + else { + width = parameters.width; + } + if (parameters.height instanceof Function) { + height = parameters.height.call(this, renderer); + } + else { + height = parameters.height; + } + if ( + parametersCopy.width !== width + || parametersCopy.height !== height + ) { + if (this._outputTextures[outputName]) { + this._outputTextures[outputName].dispose(renderer.gl); + } + } + parametersCopy.width = width; + parametersCopy.height = height; + + return parametersCopy; + }, + + /** + * Set parameter + * @param {string} name + * @param {} value + */ + setParameter: function (name, value) {}, + /** + * Get parameter value + * @param {string} name + * @return {} + */ + getParameter: function (name) {}, + /** + * Set parameters + * @param {Object} obj + */ + setParameters: function (obj) { + for (var name in obj) { + this.setParameter(name, obj[name]); + } + }, + + render: function () {}, + + getOutput: function (renderer /*optional*/, name) { + if (name == null) { + // Return the output texture without rendering + name = renderer; + return this._outputTextures[name]; + } + var outputInfo = this.outputs[name]; + if (!outputInfo) { + return ; + } + + // Already been rendered in this frame + if (this._rendered) { + // Force return texture in last frame + if (outputInfo.outputLastFrame) { + return this._prevOutputTextures[name]; + } + else { + return this._outputTextures[name]; + } + } + else if ( + // TODO + this._rendering // Solve Circular Reference + ) { + if (!this._prevOutputTextures[name]) { + // Create a blank texture at first pass + this._prevOutputTextures[name] = this._compositor.allocateTexture(outputInfo.parameters || {}); + } + return this._prevOutputTextures[name]; + } + + this.render(renderer); + + return this._outputTextures[name]; + }, + + removeReference: function (outputName) { + this._outputReferences[outputName]--; + if (this._outputReferences[outputName] === 0) { + var outputInfo = this.outputs[outputName]; + if (outputInfo.keepLastFrame) { + if (this._prevOutputTextures[outputName]) { + this._compositor.releaseTexture(this._prevOutputTextures[outputName]); + } + this._prevOutputTextures[outputName] = this._outputTextures[outputName]; + } + else { + // Output of this node have alreay been used by all other nodes + // Put the texture back to the pool. + this._compositor.releaseTexture(this._outputTextures[outputName]); + } + } + }, + + link: function (inputPinName, fromNode, fromPinName) { + + // The relationship from output pin to input pin is one-on-multiple + this.inputLinks[inputPinName] = { + node: fromNode, + pin: fromPinName + }; + if (!fromNode.outputLinks[fromPinName]) { + fromNode.outputLinks[fromPinName] = []; + } + fromNode.outputLinks[fromPinName].push({ + node: this, + pin: inputPinName + }); + + // Enabled the pin texture in shader + var shader = this.pass.material.shader; + shader.enableTexture(inputPinName); + }, + + clear: function () { + this.inputLinks = {}; + this.outputLinks = {}; + }, + + updateReference: function (outputName) { + if (!this._rendering) { + this._rendering = true; + for (var inputName in this.inputLinks) { + var link = this.inputLinks[inputName]; + link.node.updateReference(link.pin); + } + this._rendering = false; + } + if (outputName) { + this._outputReferences[outputName] ++; + } + }, + + beforeFrame: function () { + this._rendered = false; + + for (var name in this.outputLinks) { + this._outputReferences[name] = 0; + } + }, + + afterFrame: function () { + // Put back all the textures to pool + for (var name in this.outputLinks) { + if (this._outputReferences[name] > 0) { + var outputInfo = this.outputs[name]; + if (outputInfo.keepLastFrame) { + if (this._prevOutputTextures[name]) { + this._compositor.releaseTexture(this._prevOutputTextures[name]); + } + this._prevOutputTextures[name] = this._outputTextures[name]; + } + else { + this._compositor.releaseTexture(this._outputTextures[name]); + } + } + } + } + }); + + module.exports = Node; + + +/***/ }, +/* 38 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Texture2D = __webpack_require__(39); + var glenum = __webpack_require__(34); + var util = __webpack_require__(6); + + var TexturePool = function () { + + this._pool = {}; + + this._allocatedTextures = []; + }; + + TexturePool.prototype = { + + constructor: TexturePool, + + get: function (parameters) { + var key = generateKey(parameters); + if (!this._pool.hasOwnProperty(key)) { + this._pool[key] = []; + } + var list = this._pool[key]; + if (!list.length) { + var texture = new Texture2D(parameters); + this._allocatedTextures.push(texture); + return texture; + } + return list.pop(); + }, + + put: function (texture) { + var key = generateKey(texture); + if (!this._pool.hasOwnProperty(key)) { + this._pool[key] = []; + } + var list = this._pool[key]; + list.push(texture); + }, + + clear: function (gl) { + for (var i = 0; i < this._allocatedTextures.length; i++) { + this._allocatedTextures[i].dispose(gl); + } + this._pool = {}; + this._allocatedTextures = []; + } + }; + + var defaultParams = { + width: 512, + height: 512, + type: glenum.UNSIGNED_BYTE, + format: glenum.RGBA, + wrapS: glenum.CLAMP_TO_EDGE, + wrapT: glenum.CLAMP_TO_EDGE, + minFilter: glenum.LINEAR_MIPMAP_LINEAR, + magFilter: glenum.LINEAR, + useMipmap: true, + anisotropic: 1, + flipY: true, + unpackAlignment: 4, + premultiplyAlpha: false + }; + + var defaultParamPropList = Object.keys(defaultParams); + + function generateKey(parameters) { + util.defaultsWithPropList(parameters, defaultParams, defaultParamPropList); + fallBack(parameters); + + var key = ''; + for (var i = 0; i < defaultParamPropList.length; i++) { + var name = defaultParamPropList[i]; + var chunk = parameters[name].toString(); + key += chunk; + } + return key; + } + + function fallBack(target) { + + var IPOT = isPowerOfTwo(target.width, target.height); + + if (target.format === glenum.DEPTH_COMPONENT) { + target.useMipmap = false; + } + + if (!IPOT || !target.useMipmap) { + if (target.minFilter == glenum.NEAREST_MIPMAP_NEAREST || + target.minFilter == glenum.NEAREST_MIPMAP_LINEAR) { + target.minFilter = glenum.NEAREST; + } else if ( + target.minFilter == glenum.LINEAR_MIPMAP_LINEAR || + target.minFilter == glenum.LINEAR_MIPMAP_NEAREST + ) { + target.minFilter = glenum.LINEAR; + } + + target.wrapS = glenum.CLAMP_TO_EDGE; + target.wrapT = glenum.CLAMP_TO_EDGE; + } + } + + function isPowerOfTwo(width, height) { + return (width & (width-1)) === 0 && + (height & (height-1)) === 0; + } + + module.exports = TexturePool; + + +/***/ }, +/* 39 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Texture = __webpack_require__(40); + var glinfo = __webpack_require__(42); + var glenum = __webpack_require__(34); + var mathUtil = __webpack_require__(43); + var isPowerOfTwo = mathUtil.isPowerOfTwo; + + /** + * @constructor qtek.Texture2D + * @extends qtek.Texture + * + * @example + * ... + * var mat = new qtek.Material({ + * shader: qtek.shader.library.get('qtek.phong', 'diffuseMap') + * }); + * var diffuseMap = new qtek.Texture2D(); + * diffuseMap.load('assets/textures/diffuse.jpg'); + * mat.set('diffuseMap', diffuseMap); + * ... + * diffuseMap.success(function() { + * // Wait for the diffuse texture loaded + * animation.on('frame', function(frameTime) { + * renderer.render(scene, camera); + * }); + * }); + */ + var Texture2D = Texture.extend(function() { + return /** @lends qtek.Texture2D# */ { + /** + * @type {HTMLImageElement|HTMLCanvasElemnet} + */ + image: null, + /** + * @type {Uint8Array|Float32Array} + */ + pixels: null, + /** + * @type {Array.} + * @example + * [{ + * image: mipmap0, + * pixels: null + * }, { + * image: mipmap1, + * pixels: null + * }, ....] + */ + mipmaps: [] + }; + }, { + update: function(_gl) { + + _gl.bindTexture(_gl.TEXTURE_2D, this._cache.get('webgl_texture')); + + this.beforeUpdate( _gl); + + var glFormat = this.format; + var glType = this.type; + + _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, this.wrapS); + _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, this.wrapT); + + _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, this.magFilter); + _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, this.minFilter); + + var anisotropicExt = glinfo.getExtension(_gl, 'EXT_texture_filter_anisotropic'); + if (anisotropicExt && this.anisotropic > 1) { + _gl.texParameterf(_gl.TEXTURE_2D, anisotropicExt.TEXTURE_MAX_ANISOTROPY_EXT, this.anisotropic); + } + + // Fallback to float type if browser don't have half float extension + if (glType === 36193) { + var halfFloatExt = glinfo.getExtension(_gl, 'OES_texture_half_float'); + if (!halfFloatExt) { + glType = glenum.FLOAT; + } + } + + if (this.mipmaps.length) { + var width = this.width; + var height = this.height; + for (var i = 0; i < this.mipmaps.length; i++) { + var mipmap = this.mipmaps[i]; + this._updateTextureData(_gl, mipmap, i, width, height, glFormat, glType); + width /= 2; + height /= 2; + } + } + else { + this._updateTextureData(_gl, this, 0, this.width, this.height, glFormat, glType); + + if (this.useMipmap && !this.NPOT) { + _gl.generateMipmap(_gl.TEXTURE_2D); + } + } + + _gl.bindTexture(_gl.TEXTURE_2D, null); + }, + + _updateTextureData: function (_gl, data, level, width, height, glFormat, glType) { + if (data.image) { + _gl.texImage2D(_gl.TEXTURE_2D, level, glFormat, glFormat, glType, data.image); + } + else { + // Can be used as a blank texture when writing render to texture(RTT) + if ( + glFormat <= Texture.COMPRESSED_RGBA_S3TC_DXT5_EXT + && glFormat >= Texture.COMPRESSED_RGB_S3TC_DXT1_EXT + ) { + _gl.compressedTexImage2D(_gl.TEXTURE_2D, level, glFormat, width, height, 0, data.pixels); + } + else { + // Is a render target if pixels is null + _gl.texImage2D(_gl.TEXTURE_2D, level, glFormat, width, height, 0, glFormat, glType, data.pixels); + } + } + }, + + /** + * @param {WebGLRenderingContext} _gl + * @memberOf qtek.Texture2D.prototype + */ + generateMipmap: function(_gl) { + if (this.useMipmap && !this.NPOT) { + _gl.bindTexture(_gl.TEXTURE_2D, this._cache.get('webgl_texture')); + _gl.generateMipmap(_gl.TEXTURE_2D); + } + }, + + isPowerOfTwo: function() { + var width; + var height; + if (this.image) { + width = this.image.width; + height = this.image.height; + } + else { + width = this.width; + height = this.height; + } + return isPowerOfTwo(width) && isPowerOfTwo(height); + }, + + isRenderable: function() { + if (this.image) { + return this.image.nodeName === 'CANVAS' + || this.image.nodeName === 'VIDEO' + || this.image.complete; + } + else { + return !!(this.width && this.height); + } + }, + + bind: function(_gl) { + _gl.bindTexture(_gl.TEXTURE_2D, this.getWebGLTexture(_gl)); + }, + + unbind: function(_gl) { + _gl.bindTexture(_gl.TEXTURE_2D, null); + }, + + load: function (src) { + var image = new Image(); + var self = this; + image.onload = function() { + self.dirty(); + self.trigger('success', self); + image.onload = null; + }; + image.onerror = function() { + self.trigger('error', self); + image.onerror = null; + }; + + image.src = src; + this.image = image; + + return this; + } + }); + + module.exports = Texture2D; + + +/***/ }, +/* 40 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * Base class for all textures like compressed texture, texture2d, texturecube + * TODO mapping + */ + + + var Base = __webpack_require__(3); + var glenum = __webpack_require__(34); + var Cache = __webpack_require__(41); + + /** + * @constructor qtek.Texture + * @extends qtek.core.Base + */ + var Texture = Base.extend( + /** @lends qtek.Texture# */ + { + /** + * Texture width, only needed when the texture is used as a render target + * @type {number} + */ + width: 512, + /** + * Texture height, only needed when the texture is used as a render target + * @type {number} + */ + height: 512, + /** + * Texel data type + * @type {number} + */ + type: glenum.UNSIGNED_BYTE, + /** + * Format of texel data + * @type {number} + */ + format: glenum.RGBA, + /** + * @type {number} + */ + wrapS: glenum.CLAMP_TO_EDGE, + /** + * @type {number} + */ + wrapT: glenum.CLAMP_TO_EDGE, + /** + * @type {number} + */ + minFilter: glenum.LINEAR_MIPMAP_LINEAR, + /** + * @type {number} + */ + magFilter: glenum.LINEAR, + /** + * @type {boolean} + */ + useMipmap: true, + + /** + * Anisotropic filtering, enabled if value is larger than 1 + * @see http://blog.tojicode.com/2012/03/anisotropic-filtering-in-webgl.html + * @type {number} + */ + anisotropic: 1, + // pixelStorei parameters, not available when texture is used as render target + // http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml + /** + * @type {boolean} + */ + flipY: true, + /** + * @type {number} + */ + unpackAlignment: 4, + /** + * @type {boolean} + */ + premultiplyAlpha: false, + + /** + * Dynamic option for texture like video + * @type {boolean} + */ + dynamic: false, + + NPOT: false + }, function () { + this._cache = new Cache(); + }, + /** @lends qtek.Texture.prototype */ + { + + getWebGLTexture: function (_gl) { + var cache = this._cache; + cache.use(_gl.__GLID__); + + if (cache.miss('webgl_texture')) { + // In a new gl context, create new texture and set dirty true + cache.put('webgl_texture', _gl.createTexture()); + } + if (this.dynamic) { + this.update(_gl); + } + else if (cache.isDirty()) { + this.update(_gl); + cache.fresh(); + } + + return cache.get('webgl_texture'); + }, + + bind: function () {}, + unbind: function () {}, + + /** + * Mark texture is dirty and update in the next frame + */ + dirty: function () { + this._cache.dirtyAll(); + }, + + update: function (_gl) {}, + + // Update the common parameters of texture + beforeUpdate: function (_gl) { + _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, this.flipY); + _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this.premultiplyAlpha); + _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, this.unpackAlignment); + + this.fallBack(); + }, + + fallBack: function () { + // Use of none-power of two texture + // http://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences + + var isPowerOfTwo = this.isPowerOfTwo(); + + if (this.format === glenum.DEPTH_COMPONENT) { + this.useMipmap = false; + } + + if (!isPowerOfTwo || !this.useMipmap) { + // none-power of two flag + this.NPOT = true; + // Save the original value for restore + this._minFilterOriginal = this.minFilter; + this._magFilterOriginal = this.magFilter; + this._wrapSOriginal = this.wrapS; + this._wrapTOriginal = this.wrapT; + + if (this.minFilter == glenum.NEAREST_MIPMAP_NEAREST || + this.minFilter == glenum.NEAREST_MIPMAP_LINEAR) { + this.minFilter = glenum.NEAREST; + } else if ( + this.minFilter == glenum.LINEAR_MIPMAP_LINEAR || + this.minFilter == glenum.LINEAR_MIPMAP_NEAREST + ) { + this.minFilter = glenum.LINEAR; + } + + this.wrapS = glenum.CLAMP_TO_EDGE; + this.wrapT = glenum.CLAMP_TO_EDGE; + } + else { + this.NPOT = false; + if (this._minFilterOriginal) { + this.minFilter = this._minFilterOriginal; + } + if (this._magFilterOriginal) { + this.magFilter = this._magFilterOriginal; + } + if (this._wrapSOriginal) { + this.wrapS = this._wrapSOriginal; + } + if (this._wrapTOriginal) { + this.wrapT = this._wrapTOriginal; + } + } + + }, + + nextHighestPowerOfTwo: function (x) { + --x; + for (var i = 1; i < 32; i <<= 1) { + x = x | x >> i; + } + return x + 1; + }, + /** + * @param {WebGLRenderingContext} _gl + */ + dispose: function (_gl) { + + var cache = this._cache; + + cache.use(_gl.__GLID__); + + var webglTexture = cache.get('webgl_texture'); + if (webglTexture){ + _gl.deleteTexture(webglTexture); + } + cache.deleteContext(_gl.__GLID__); + + }, + /** + * Test if image of texture is valid and loaded. + * @return {boolean} + */ + isRenderable: function () {}, + + isPowerOfTwo: function () {} + }); + + /* DataType */ + Texture.BYTE = glenum.BYTE; + Texture.UNSIGNED_BYTE = glenum.UNSIGNED_BYTE; + Texture.SHORT = glenum.SHORT; + Texture.UNSIGNED_SHORT = glenum.UNSIGNED_SHORT; + Texture.INT = glenum.INT; + Texture.UNSIGNED_INT = glenum.UNSIGNED_INT; + Texture.FLOAT = glenum.FLOAT; + Texture.HALF_FLOAT = 0x8D61; + + // ext.UNSIGNED_INT_24_8_WEBGL for WEBGL_depth_texture extension + Texture.UNSIGNED_INT_24_8_WEBGL = 34042; + + /* PixelFormat */ + Texture.DEPTH_COMPONENT = glenum.DEPTH_COMPONENT; + Texture.DEPTH_STENCIL = glenum.DEPTH_STENCIL; + Texture.ALPHA = glenum.ALPHA; + Texture.RGB = glenum.RGB; + Texture.RGBA = glenum.RGBA; + Texture.LUMINANCE = glenum.LUMINANCE; + Texture.LUMINANCE_ALPHA = glenum.LUMINANCE_ALPHA; + + /* Compressed Texture */ + Texture.COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0; + Texture.COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1; + Texture.COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2; + Texture.COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3; + + /* TextureMagFilter */ + Texture.NEAREST = glenum.NEAREST; + Texture.LINEAR = glenum.LINEAR; + + /* TextureMinFilter */ + /* NEAREST */ + /* LINEAR */ + Texture.NEAREST_MIPMAP_NEAREST = glenum.NEAREST_MIPMAP_NEAREST; + Texture.LINEAR_MIPMAP_NEAREST = glenum.LINEAR_MIPMAP_NEAREST; + Texture.NEAREST_MIPMAP_LINEAR = glenum.NEAREST_MIPMAP_LINEAR; + Texture.LINEAR_MIPMAP_LINEAR = glenum.LINEAR_MIPMAP_LINEAR; + + /* TextureParameterName */ + // Texture.TEXTURE_MAG_FILTER = glenum.TEXTURE_MAG_FILTER; + // Texture.TEXTURE_MIN_FILTER = glenum.TEXTURE_MIN_FILTER; + + /* TextureWrapMode */ + Texture.REPEAT = glenum.REPEAT; + Texture.CLAMP_TO_EDGE = glenum.CLAMP_TO_EDGE; + Texture.MIRRORED_REPEAT = glenum.MIRRORED_REPEAT; + + + module.exports = Texture; + + +/***/ }, +/* 41 */ +/***/ function(module, exports) { + + 'use strict'; + + + var DIRTY_PREFIX = '__dt__'; + + var Cache = function () { + + this._contextId = 0; + + this._caches = []; + + this._context = {}; + }; + + Cache.prototype = { + + use: function (contextId, documentSchema) { + var caches = this._caches; + if (!caches[contextId]) { + caches[contextId] = {}; + + if (documentSchema) { + caches[contextId] = documentSchema(); + } + } + this._contextId = contextId; + + this._context = caches[contextId]; + }, + + put: function (key, value) { + this._context[key] = value; + }, + + get: function (key) { + return this._context[key]; + }, + + dirty: function (field) { + field = field || ''; + var key = DIRTY_PREFIX + field; + this.put(key, true); + }, + + dirtyAll: function (field) { + field = field || ''; + var key = DIRTY_PREFIX + field; + var caches = this._caches; + for (var i = 0; i < caches.length; i++) { + if (caches[i]) { + caches[i][key] = true; + } + } + }, + + fresh: function (field) { + field = field || ''; + var key = DIRTY_PREFIX + field; + this.put(key, false); + }, + + freshAll: function (field) { + field = field || ''; + var key = DIRTY_PREFIX + field; + var caches = this._caches; + for (var i = 0; i < caches.length; i++) { + if (caches[i]) { + caches[i][key] = false; + } + } + }, + + isDirty: function (field) { + field = field || ''; + var key = DIRTY_PREFIX + field; + var context = this._context; + return !context.hasOwnProperty(key) + || context[key] === true; + }, + + deleteContext: function (contextId) { + delete this._caches[contextId]; + this._context = {}; + }, + + delete: function (key) { + delete this._context[key]; + }, + + clearAll: function () { + this._caches = {}; + }, + + getContext: function () { + return this._context; + }, + + eachContext : function (cb, context) { + var keys = Object.keys(this._caches); + keys.forEach(function (key) { + cb && cb.call(context, key); + }); + }, + + miss: function (key) { + return ! this._context.hasOwnProperty(key); + } + }; + + Cache.prototype.constructor = Cache; + + module.exports = Cache; + + + +/***/ }, +/* 42 */ +/***/ function(module, exports) { + + 'use strict'; + /** + * @namespace qtek.core.glinfo + * @see http://www.khronos.org/registry/webgl/extensions/ + */ + + + var EXTENSION_LIST = [ + 'OES_texture_float', + 'OES_texture_half_float', + 'OES_texture_float_linear', + 'OES_texture_half_float_linear', + 'OES_standard_derivatives', + 'OES_vertex_array_object', + 'OES_element_index_uint', + 'WEBGL_compressed_texture_s3tc', + 'WEBGL_depth_texture', + 'EXT_texture_filter_anisotropic', + 'EXT_shader_texture_lod', + 'WEBGL_draw_buffers', + 'EXT_frag_depth' + ]; + + var PARAMETER_NAMES = [ + 'MAX_TEXTURE_SIZE', + 'MAX_CUBE_MAP_TEXTURE_SIZE' + ]; + + var extensions = {}; + var parameters = {}; + + var glinfo = { + /** + * Initialize all extensions and parameters in context + * @param {WebGLRenderingContext} _gl + * @memberOf qtek.core.glinfo + */ + initialize: function (_gl) { + var glid = _gl.__GLID__; + if (extensions[glid]) { + return; + } + extensions[glid] = {}; + parameters[glid] = {}; + // Get webgl extension + for (var i = 0; i < EXTENSION_LIST.length; i++) { + var extName = EXTENSION_LIST[i]; + + this._createExtension(_gl, extName); + } + // Get parameters + for (var i = 0; i < PARAMETER_NAMES.length; i++) { + var name = PARAMETER_NAMES[i]; + parameters[glid][name] = _gl.getParameter(_gl[name]); + } + }, + + /** + * Get extension + * @param {WebGLRenderingContext} _gl + * @param {string} name - Extension name, vendorless + * @return {WebGLExtension} + * @memberOf qtek.core.glinfo + */ + getExtension: function (_gl, name) { + var glid = _gl.__GLID__; + if (extensions[glid]) { + if (typeof(extensions[glid][name]) == 'undefined') { + this._createExtension(_gl, name); + } + return extensions[glid][name]; + } + }, + + /** + * Get parameter + * @param {WebGLRenderingContext} _gl + * @param {string} name Parameter name + * @return {*} + */ + getParameter: function (_gl, name) { + var glid = _gl.__GLID__; + if (parameters[glid]) { + return parameters[glid][name]; + } + }, + + /** + * Dispose context + * @param {WebGLRenderingContext} _gl + * @memberOf qtek.core.glinfo + */ + dispose: function (_gl) { + delete extensions[_gl.__GLID__]; + delete parameters[_gl.__GLID__]; + }, + + _createExtension: function (_gl, name) { + var ext = _gl.getExtension(name); + if (!ext) { + ext = _gl.getExtension('MOZ_' + name); + } + if (!ext) { + ext = _gl.getExtension('WEBKIT_' + name); + } + + extensions[_gl.__GLID__][name] = ext; + } + }; + + module.exports = glinfo; + + +/***/ }, +/* 43 */ +/***/ function(module, exports) { + + + + var mathUtil = {}; + + mathUtil.isPowerOfTwo = function (value) { + return (value & (value - 1)) === 0; + }; + + mathUtil.nextPowerOfTwo = function (value) { + value --; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value ++; + + return value; + }; + + mathUtil.nearestPowerOfTwo = function (value) { + return Math.pow( 2, Math.round( Math.log( value ) / Math.LN2 ) ); + }; + + module.exports = mathUtil; + + +/***/ }, +/* 44 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Base = __webpack_require__(3); + var Texture = __webpack_require__(40); + var TextureCube = __webpack_require__(45); + var glinfo = __webpack_require__(42); + var glenum = __webpack_require__(34); + var Cache = __webpack_require__(41); + + var KEY_FRAMEBUFFER = 'framebuffer'; + var KEY_RENDERBUFFER = 'renderbuffer'; + var KEY_RENDERBUFFER_WIDTH = KEY_RENDERBUFFER + '_width'; + var KEY_RENDERBUFFER_HEIGHT = KEY_RENDERBUFFER + '_height'; + var KEY_RENDERBUFFER_ATTACHED = KEY_RENDERBUFFER + '_attached'; + var KEY_DEPTHTEXTURE_ATTACHED = 'depthtexture_attached'; + + var GL_FRAMEBUFFER = glenum.FRAMEBUFFER; + var GL_RENDERBUFFER = glenum.RENDERBUFFER; + var GL_DEPTH_ATTACHMENT = glenum.DEPTH_ATTACHMENT; + var GL_COLOR_ATTACHMENT0 = glenum.COLOR_ATTACHMENT0; + /** + * @constructor qtek.FrameBuffer + * @extends qtek.core.Base + */ + var FrameBuffer = Base.extend( + /** @lends qtek.FrameBuffer# */ + { + /** + * If use depth buffer + * @type {boolean} + */ + depthBuffer: true, + + /** + * @type {Object} + */ + viewport: null, + + _width: 0, + _height: 0, + + _textures: null, + + _boundRenderer: null, + }, function () { + // Use cache + this._cache = new Cache(); + + this._textures = {}; + }, + + /**@lends qtek.FrameBuffer.prototype. */ + { + /** + * Get attached texture width + * {number} + */ + // FIXME Can't use before #bind + getTextureWidth: function () { + return this._width; + }, + + /** + * Get attached texture height + * {number} + */ + getTextureHeight: function () { + return this._height; + }, + + /** + * Bind the framebuffer to given renderer before rendering + * @param {qtek.Renderer} renderer + */ + bind: function (renderer) { + + if (renderer.__currentFrameBuffer) { + // Already bound + if (renderer.__currentFrameBuffer === this) { + return; + } + + console.warn('Renderer already bound with another framebuffer. Unbind it first'); + } + renderer.__currentFrameBuffer = this; + + var _gl = renderer.gl; + + _gl.bindFramebuffer(GL_FRAMEBUFFER, this._getFrameBufferGL(_gl)); + this._boundRenderer = renderer; + var cache = this._cache; + + cache.put('viewport', renderer.viewport); + + var hasTextureAttached = false; + var width; + var height; + for (var attachment in this._textures) { + hasTextureAttached = true; + var obj = this._textures[attachment]; + if (obj) { + // TODO Do width, height checking, make sure size are same + width = obj.texture.width; + height = obj.texture.height; + // Attach textures + this._doAttach(_gl, obj.texture, attachment, obj.target); + } + } + + this._width = width; + this._height = height; + + if (!hasTextureAttached && this.depthBuffer) { + console.error('Must attach texture before bind, or renderbuffer may have incorrect width and height.') + } + + if (this.viewport) { + renderer.setViewport(this.viewport); + } + else { + renderer.setViewport(0, 0, width, height, 1); + } + + var attachedTextures = cache.get('attached_textures'); + if (attachedTextures) { + for (var attachment in attachedTextures) { + if (!this._textures[attachment]) { + var target = attachedTextures[attachment]; + this._doDetach(_gl, attachment, target); + } + } + } + if (!cache.get(KEY_DEPTHTEXTURE_ATTACHED) && this.depthBuffer) { + // Create a new render buffer + if (cache.miss(KEY_RENDERBUFFER)) { + cache.put(KEY_RENDERBUFFER, _gl.createRenderbuffer()); + } + var renderbuffer = cache.get(KEY_RENDERBUFFER); + + if (width !== cache.get(KEY_RENDERBUFFER_WIDTH) + || height !== cache.get(KEY_RENDERBUFFER_HEIGHT)) { + _gl.bindRenderbuffer(GL_RENDERBUFFER, renderbuffer); + _gl.renderbufferStorage(GL_RENDERBUFFER, _gl.DEPTH_COMPONENT16, width, height); + cache.put(KEY_RENDERBUFFER_WIDTH, width); + cache.put(KEY_RENDERBUFFER_HEIGHT, height); + _gl.bindRenderbuffer(GL_RENDERBUFFER, null); + } + if (!cache.get(KEY_RENDERBUFFER_ATTACHED)) { + _gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuffer); + cache.put(KEY_RENDERBUFFER_ATTACHED, true); + } + } + }, + + /** + * Unbind the frame buffer after rendering + * @param {qtek.Renderer} renderer + */ + unbind: function (renderer) { + // Remove status record on renderer + renderer.__currentFrameBuffer = null; + + var _gl = renderer.gl; + + _gl.bindFramebuffer(GL_FRAMEBUFFER, null); + this._boundRenderer = null; + + this._cache.use(_gl.__GLID__); + var viewport = this._cache.get('viewport'); + // Reset viewport; + if (viewport) { + renderer.setViewport(viewport); + } + + this.updateMipmap(_gl); + }, + + // Because the data of texture is changed over time, + // Here update the mipmaps of texture each time after rendered; + updateMipmap: function (_gl) { + for (var attachment in this._textures) { + var obj = this._textures[attachment]; + if (obj) { + var texture = obj.texture; + // FIXME some texture format can't generate mipmap + if (!texture.NPOT && texture.useMipmap + && texture.minFilter === Texture.LINEAR_MIPMAP_LINEAR) { + var target = texture instanceof TextureCube ? glenum.TEXTURE_CUBE_MAP : glenum.TEXTURE_2D; + _gl.bindTexture(target, texture.getWebGLTexture(_gl)); + _gl.generateMipmap(target); + _gl.bindTexture(target, null); + } + } + } + }, + + /** + * 0x8CD5, 36053, FRAMEBUFFER_COMPLETE + * 0x8CD6, 36054, FRAMEBUFFER_INCOMPLETE_ATTACHMENT + * 0x8CD7, 36055, FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT + * 0x8CD9, 36057, FRAMEBUFFER_INCOMPLETE_DIMENSIONS + * 0x8CDD, 36061, FRAMEBUFFER_UNSUPPORTED + */ + checkStatus: function (_gl) { + return _gl.checkFramebufferStatus(GL_FRAMEBUFFER); + }, + + _getFrameBufferGL: function (_gl) { + var cache = this._cache; + cache.use(_gl.__GLID__); + + if (cache.miss(KEY_FRAMEBUFFER)) { + cache.put(KEY_FRAMEBUFFER, _gl.createFramebuffer()); + } + + return cache.get(KEY_FRAMEBUFFER); + }, + + /** + * Attach a texture(RTT) to the framebuffer + * @param {qtek.Texture} texture + * @param {number} [attachment=gl.COLOR_ATTACHMENT0] + * @param {number} [target=gl.TEXTURE_2D] + */ + attach: function (texture, attachment, target) { + + if (!texture.width) { + throw new Error('The texture attached to color buffer is not a valid.'); + } + // TODO width and height check + + // If the depth_texture extension is enabled, developers + // Can attach a depth texture to the depth buffer + // http://blog.tojicode.com/2012/07/using-webgldepthtexture.html + attachment = attachment || GL_COLOR_ATTACHMENT0; + target = target || glenum.TEXTURE_2D; + + var boundRenderer = this._boundRenderer; + var _gl = boundRenderer && boundRenderer.gl; + var attachedTextures; + + if (_gl) { + var cache = this._cache; + cache.use(_gl.__GLID__); + attachedTextures = cache.get('attached_textures'); + } + + // Check if texture attached + var previous = this._textures[attachment]; + if (previous && previous.target === target + && previous.texture === texture + && (attachedTextures && attachedTextures[attachment] != null) + ) { + return; + } + + var canAttach = true; + if (_gl) { + canAttach = this._doAttach(_gl, texture, attachment, target); + // Set viewport again incase attached to different size textures. + if (!this.viewport) { + boundRenderer.setViewport(0, 0, texture.width, texture.height, 1); + } + } + + if (canAttach) { + this._textures[attachment] = this._textures[attachment] || {}; + this._textures[attachment].texture = texture; + this._textures[attachment].target = target; + } + }, + + _doAttach: function (_gl, texture, attachment, target) { + + // Make sure texture is always updated + // Because texture width or height may be changed and in this we can't be notified + // FIXME awkward; + var webglTexture = texture.getWebGLTexture(_gl); + // Assume cache has been used. + var attachedTextures = this._cache.get('attached_textures'); + if (attachedTextures && attachedTextures[attachment]) { + var obj = attachedTextures[attachment]; + // Check if texture and target not changed + if (obj.texture === texture && obj.target === target) { + return; + } + } + attachment = +attachment; + + var canAttach = true; + if (attachment === GL_DEPTH_ATTACHMENT || attachment === glenum.DEPTH_STENCIL_ATTACHMENT) { + var extension = glinfo.getExtension(_gl, 'WEBGL_depth_texture'); + + if (!extension) { + console.error('Depth texture is not supported by the browser'); + canAttach = false; + } + if (texture.format !== glenum.DEPTH_COMPONENT + && texture.format !== glenum.DEPTH_STENCIL + ) { + console.error('The texture attached to depth buffer is not a valid.'); + canAttach = false; + } + + // Dispose render buffer created previous + if (canAttach) { + var renderbuffer = this._cache.get(KEY_RENDERBUFFER); + if (renderbuffer) { + _gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, null); + _gl.deleteRenderbuffer(renderbuffer); + this._cache.put(KEY_RENDERBUFFER, false); + } + + this._cache.put(KEY_RENDERBUFFER_ATTACHED, false); + this._cache.put(KEY_DEPTHTEXTURE_ATTACHED, true); + } + } + + // Mipmap level can only be 0 + _gl.framebufferTexture2D(GL_FRAMEBUFFER, attachment, target, webglTexture, 0); + + if (!attachedTextures) { + attachedTextures = {}; + this._cache.put('attached_textures', attachedTextures); + } + attachedTextures[attachment] = attachedTextures[attachment] || {}; + attachedTextures[attachment].texture = texture; + attachedTextures[attachment].target = target; + + return canAttach; + }, + + _doDetach: function (_gl, attachment, target) { + // Detach a texture from framebuffer + // https://github.com/KhronosGroup/WebGL/blob/master/conformance-suites/1.0.0/conformance/framebuffer-test.html#L145 + _gl.framebufferTexture2D(GL_FRAMEBUFFER, attachment, target, null, 0); + + // Assume cache has been used. + var attachedTextures = this._cache.get('attached_textures'); + if (attachedTextures && attachedTextures[attachment]) { + attachedTextures[attachment] = null; + } + + if (attachment === GL_DEPTH_ATTACHMENT || attachment === glenum.DEPTH_STENCIL_ATTACHMENT) { + this._cache.put(KEY_DEPTHTEXTURE_ATTACHED, false); + } + }, + + /** + * Detach a texture + * @param {number} [attachment=gl.COLOR_ATTACHMENT0] + * @param {number} [target=gl.TEXTURE_2D] + */ + detach: function (attachment, target) { + // TODO depth extension check ? + this._textures[attachment] = null; + if (this._boundRenderer) { + var gl = this._boundRenderer.gl; + var cache = this._cache; + cache.use(gl.__GLID__); + this._doDetach(gl, attachment, target); + } + }, + /** + * Dispose + * @param {WebGLRenderingContext} _gl + */ + dispose: function (_gl) { + + var cache = this._cache; + + cache.use(_gl.__GLID__); + + var renderBuffer = cache.get(KEY_RENDERBUFFER); + if (renderBuffer) { + _gl.deleteRenderbuffer(renderBuffer); + } + var frameBuffer = cache.get(KEY_FRAMEBUFFER); + if (frameBuffer) { + _gl.deleteFramebuffer(frameBuffer); + } + cache.deleteContext(_gl.__GLID__); + + // Clear cache for reusing + this._textures = {}; + + } + }); + + FrameBuffer.DEPTH_ATTACHMENT = GL_DEPTH_ATTACHMENT; + FrameBuffer.COLOR_ATTACHMENT0 = GL_COLOR_ATTACHMENT0; + FrameBuffer.STENCIL_ATTACHMENT = glenum.STENCIL_ATTACHMENT; + FrameBuffer.DEPTH_STENCIL_ATTACHMENT = glenum.DEPTH_STENCIL_ATTACHMENT; + + module.exports = FrameBuffer; + + +/***/ }, +/* 45 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Texture = __webpack_require__(40); + var glinfo = __webpack_require__(42); + var glenum = __webpack_require__(34); + var util = __webpack_require__(6); + var mathUtil = __webpack_require__(43); + var isPowerOfTwo = mathUtil.isPowerOfTwo; + + var targetList = ['px', 'nx', 'py', 'ny', 'pz', 'nz']; + + /** + * @constructor qtek.TextureCube + * @extends qtek.Texture + * + * @example + * ... + * var mat = new qtek.Material({ + * shader: qtek.shader.library.get('qtek.phong', 'environmentMap') + * }); + * var envMap = new qtek.TextureCube(); + * envMap.load({ + * 'px': 'assets/textures/sky/px.jpg', + * 'nx': 'assets/textures/sky/nx.jpg' + * 'py': 'assets/textures/sky/py.jpg' + * 'ny': 'assets/textures/sky/ny.jpg' + * 'pz': 'assets/textures/sky/pz.jpg' + * 'nz': 'assets/textures/sky/nz.jpg' + * }); + * mat.set('environmentMap', envMap); + * ... + * envMap.success(function() { + * // Wait for the sky texture loaded + * animation.on('frame', function(frameTime) { + * renderer.render(scene, camera); + * }); + * }); + */ + var TextureCube = Texture.extend(function() { + return /** @lends qtek.TextureCube# */{ + /** + * @type {Object} + * @property {HTMLImageElement|HTMLCanvasElemnet} px + * @property {HTMLImageElement|HTMLCanvasElemnet} nx + * @property {HTMLImageElement|HTMLCanvasElemnet} py + * @property {HTMLImageElement|HTMLCanvasElemnet} ny + * @property {HTMLImageElement|HTMLCanvasElemnet} pz + * @property {HTMLImageElement|HTMLCanvasElemnet} nz + */ + image: { + px: null, + nx: null, + py: null, + ny: null, + pz: null, + nz: null + }, + /** + * @type {Object} + * @property {Uint8Array} px + * @property {Uint8Array} nx + * @property {Uint8Array} py + * @property {Uint8Array} ny + * @property {Uint8Array} pz + * @property {Uint8Array} nz + */ + pixels: { + px: null, + nx: null, + py: null, + ny: null, + pz: null, + nz: null + }, + + /** + * @type {Array.} + */ + mipmaps: [] + }; + }, { + update: function(_gl) { + + _gl.bindTexture(_gl.TEXTURE_CUBE_MAP, this._cache.get('webgl_texture')); + + this.beforeUpdate(_gl); + + var glFormat = this.format; + var glType = this.type; + + _gl.texParameteri(_gl.TEXTURE_CUBE_MAP, _gl.TEXTURE_WRAP_S, this.wrapS); + _gl.texParameteri(_gl.TEXTURE_CUBE_MAP, _gl.TEXTURE_WRAP_T, this.wrapT); + + _gl.texParameteri(_gl.TEXTURE_CUBE_MAP, _gl.TEXTURE_MAG_FILTER, this.magFilter); + _gl.texParameteri(_gl.TEXTURE_CUBE_MAP, _gl.TEXTURE_MIN_FILTER, this.minFilter); + + var anisotropicExt = glinfo.getExtension(_gl, 'EXT_texture_filter_anisotropic'); + if (anisotropicExt && this.anisotropic > 1) { + _gl.texParameterf(_gl.TEXTURE_CUBE_MAP, anisotropicExt.TEXTURE_MAX_ANISOTROPY_EXT, this.anisotropic); + } + + // Fallback to float type if browser don't have half float extension + if (glType === 36193) { + var halfFloatExt = glinfo.getExtension(_gl, 'OES_texture_half_float'); + if (!halfFloatExt) { + glType = glenum.FLOAT; + } + } + + if (this.mipmaps.length) { + var width = this.width; + var height = this.height; + for (var i = 0; i < this.mipmaps.length; i++) { + var mipmap = this.mipmaps[i]; + this._updateTextureData(_gl, mipmap, i, width, height, glFormat, glType); + width /= 2; + height /= 2; + } + } + else { + this._updateTextureData(_gl, this, 0, this.width, this.height, glFormat, glType); + + if (!this.NPOT && this.useMipmap) { + _gl.generateMipmap(_gl.TEXTURE_CUBE_MAP); + } + } + + _gl.bindTexture(_gl.TEXTURE_CUBE_MAP, null); + }, + + _updateTextureData: function (_gl, data, level, width, height, glFormat, glType) { + for (var i = 0; i < 6; i++) { + var target = targetList[i]; + var img = data.image && data.image[target]; + if (img) { + _gl.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, level, glFormat, glFormat, glType, img); + } + else { + _gl.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, level, glFormat, width, height, 0, glFormat, glType, data.pixels && data.pixels[target]); + } + } + }, + + /** + * @param {WebGLRenderingContext} _gl + * @memberOf qtek.TextureCube.prototype + */ + generateMipmap: function(_gl) { + if (this.useMipmap && !this.NPOT) { + _gl.bindTexture(_gl.TEXTURE_CUBE_MAP, this._cache.get('webgl_texture')); + _gl.generateMipmap(_gl.TEXTURE_CUBE_MAP); + } + }, + + bind: function(_gl) { + + _gl.bindTexture(_gl.TEXTURE_CUBE_MAP, this.getWebGLTexture(_gl)); + }, + + unbind: function(_gl) { + _gl.bindTexture(_gl.TEXTURE_CUBE_MAP, null); + }, + + // Overwrite the isPowerOfTwo method + isPowerOfTwo: function() { + if (this.image.px) { + return isPowerOfTwo(this.image.px.width) + && isPowerOfTwo(this.image.px.height); + } + else { + return isPowerOfTwo(this.width) + && isPowerOfTwo(this.height); + } + }, + + isRenderable: function() { + if (this.image.px) { + return isImageRenderable(this.image.px) + && isImageRenderable(this.image.nx) + && isImageRenderable(this.image.py) + && isImageRenderable(this.image.ny) + && isImageRenderable(this.image.pz) + && isImageRenderable(this.image.nz); + } + else { + return !!(this.width && this.height); + } + }, + + load: function(imageList) { + var loading = 0; + var self = this; + util.each(imageList, function(src, target){ + var image = new Image(); + image.onload = function() { + loading --; + if (loading === 0){ + self.dirty(); + self.trigger('success', self); + } + image.onload = null; + }; + image.onerror = function() { + loading --; + image.onerror = null; + }; + + loading++; + image.src = src; + self.image[target] = image; + }); + + return this; + } + }); + + function isImageRenderable(image) { + return image.nodeName === 'CANVAS' || + image.nodeName === 'VIDEO' || + image.complete; + } + + module.exports = TextureCube; + + +/***/ }, +/* 46 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Pass = __webpack_require__(47); + var Node = __webpack_require__(37); + + // TODO curlnoise demo wrong + + // PENDING + // Use topological sort ? + + /** + * Filter node + * + * @constructor qtek.compositor.FilterNode + * @extends qtek.compositor.Node + * + * @example + var node = new qtek.compositor.Node({ + name: 'fxaa', + shader: qtek.Shader.source('qtek.compositor.fxaa'), + inputs: { + texture: { + node: 'scene', + pin: 'color' + } + }, + // Multiple outputs is preserved for MRT support in WebGL2.0 + outputs: { + color: { + attachment: qtek.FrameBuffer.COLOR_ATTACHMENT0 + parameters: { + format: qtek.Texture.RGBA, + width: 512, + height: 512 + }, + // Node will keep the RTT rendered in last frame + keepLastFrame: true, + // Force the node output the RTT rendered in last frame + outputLastFrame: true + } + } + }); + * + */ + var FilterNode = Node.extend(function () { + return /** @lends qtek.compositor.Node# */ { + /** + * @type {string} + */ + name: '', + + /** + * @type {Object} + */ + inputs: {}, + + /** + * @type {Object} + */ + outputs: null, + + /** + * @type {string} + */ + shader: '', + + /** + * Input links, will be updated by the graph + * @example: + * inputName: { + * node: someNode, + * pin: 'xxxx' + * } + * @type {Object} + */ + inputLinks: {}, + + /** + * Output links, will be updated by the graph + * @example: + * outputName: { + * node: someNode, + * pin: 'xxxx' + * } + * @type {Object} + */ + outputLinks: {}, + + /** + * @type {qtek.compositor.Pass} + */ + pass: null, + + // Save the output texture of previous frame + // Will be used when there exist a circular reference + _prevOutputTextures: {}, + _outputTextures: {}, + + // Example: { name: 2 } + _outputReferences: {}, + + _rendering: false, + // If rendered in this frame + _rendered: false, + + _compositor: null + }; + }, function () { + + var pass = new Pass({ + fragment: this.shader + }); + this.pass = pass; + }, + /** @lends qtek.compositor.Node.prototype */ + { + /** + * @param {qtek.Renderer} renderer + */ + render: function (renderer, frameBuffer) { + this.trigger('beforerender', renderer); + + this._rendering = true; + + var _gl = renderer.gl; + + for (var inputName in this.inputLinks) { + var link = this.inputLinks[inputName]; + var inputTexture = link.node.getOutput(renderer, link.pin); + this.pass.setUniform(inputName, inputTexture); + } + // Output + if (!this.outputs) { + this.pass.outputs = null; + + this._compositor.getFrameBuffer().unbind(renderer); + + this.pass.render(renderer, frameBuffer); + } + else { + this.pass.outputs = {}; + + var attachedTextures = {}; + for (var name in this.outputs) { + var parameters = this.updateParameter(name, renderer); + if (isNaN(parameters.width)) { + this.updateParameter(name, renderer); + } + var outputInfo = this.outputs[name]; + var texture = this._compositor.allocateTexture(parameters); + this._outputTextures[name] = texture; + var attachment = outputInfo.attachment || _gl.COLOR_ATTACHMENT0; + if (typeof(attachment) == 'string') { + attachment = _gl[attachment]; + } + attachedTextures[attachment] = texture; + } + this._compositor.getFrameBuffer().bind(renderer); + + for (var attachment in attachedTextures) { + // FIXME attachment changes in different nodes + this._compositor.getFrameBuffer().attach( + attachedTextures[attachment], attachment + ); + } + + this.pass.render(renderer); + + // Because the data of texture is changed over time, + // Here update the mipmaps of texture each time after rendered; + this._compositor.getFrameBuffer().updateMipmap(renderer.gl); + } + + for (var inputName in this.inputLinks) { + var link = this.inputLinks[inputName]; + link.node.removeReference(link.pin); + } + + this._rendering = false; + this._rendered = true; + + this.trigger('afterrender', renderer); + }, + + // TODO Remove parameter function callback + updateParameter: function (outputName, renderer) { + var outputInfo = this.outputs[outputName]; + var parameters = outputInfo.parameters; + var parametersCopy = outputInfo._parametersCopy; + if (!parametersCopy) { + parametersCopy = outputInfo._parametersCopy = {}; + } + if (parameters) { + for (var key in parameters) { + if (key !== 'width' && key !== 'height') { + parametersCopy[key] = parameters[key]; + } + } + } + var width, height; + if (parameters.width instanceof Function) { + width = parameters.width.call(this, renderer); + } + else { + width = parameters.width; + } + if (parameters.height instanceof Function) { + height = parameters.height.call(this, renderer); + } + else { + height = parameters.height; + } + if ( + parametersCopy.width !== width + || parametersCopy.height !== height + ) { + if (this._outputTextures[outputName]) { + this._outputTextures[outputName].dispose(renderer.gl); + } + } + parametersCopy.width = width; + parametersCopy.height = height; + + return parametersCopy; + }, + + /** + * Set parameter + * @param {string} name + * @param {} value + */ + setParameter: function (name, value) { + this.pass.setUniform(name, value); + }, + /** + * Get parameter value + * @param {string} name + * @return {} + */ + getParameter: function (name) { + return this.pass.getUniform(name); + }, + /** + * Set parameters + * @param {Object} obj + */ + setParameters: function (obj) { + for (var name in obj) { + this.setParameter(name, obj[name]); + } + }, + /** + * Set shader code + * @param {string} shaderStr + */ + setShader: function (shaderStr) { + var material = this.pass.material; + material.shader.setFragment(shaderStr); + material.attachShader(material.shader, true); + }, + /** + * Proxy of pass.material.shader.define('fragment', xxx); + * @param {string} symbol + * @param {number} [val] + */ + shaderDefine: function (symbol, val) { + this.pass.material.shader.define('fragment', symbol, val); + }, + + /** + * Proxy of pass.material.shader.unDefine('fragment', xxx) + * @param {string} symbol + */ + shaderUnDefine: function (symbol) { + this.pass.material.shader.unDefine('fragment', symbol); + }, + + removeReference: function (outputName) { + this._outputReferences[outputName]--; + if (this._outputReferences[outputName] === 0) { + var outputInfo = this.outputs[outputName]; + if (outputInfo.keepLastFrame) { + if (this._prevOutputTextures[outputName]) { + this._compositor.releaseTexture(this._prevOutputTextures[outputName]); + } + this._prevOutputTextures[outputName] = this._outputTextures[outputName]; + } + else { + // Output of this node have alreay been used by all other nodes + // Put the texture back to the pool. + this._compositor.releaseTexture(this._outputTextures[outputName]); + } + } + }, + + link: function (inputPinName, fromNode, fromPinName) { + + // The relationship from output pin to input pin is one-on-multiple + this.inputLinks[inputPinName] = { + node: fromNode, + pin: fromPinName + }; + if (!fromNode.outputLinks[fromPinName]) { + fromNode.outputLinks[fromPinName] = []; + } + fromNode.outputLinks[ fromPinName ].push({ + node: this, + pin: inputPinName + }); + + // Enabled the pin texture in shader + var shader = this.pass.material.shader; + shader.enableTexture(inputPinName); + }, + + clear: function () { + Node.prototype.clear.call(this); + + var shader = this.pass.material.shader; + // Default disable all texture + shader.disableTexturesAll(); + }, + + updateReference: function (outputName) { + if (!this._rendering) { + this._rendering = true; + for (var inputName in this.inputLinks) { + var link = this.inputLinks[inputName]; + link.node.updateReference(link.pin); + } + this._rendering = false; + } + if (outputName) { + this._outputReferences[outputName] ++; + } + }, + + beforeFrame: function () { + this._rendered = false; + + for (var name in this.outputLinks) { + this._outputReferences[name] = 0; + } + }, + + afterFrame: function () { + // Put back all the textures to pool + for (var name in this.outputLinks) { + if (this._outputReferences[name] > 0) { + var outputInfo = this.outputs[name]; + if (outputInfo.keepLastFrame) { + if (this._prevOutputTextures[name]) { + this._compositor.releaseTexture(this._prevOutputTextures[name]); + } + this._prevOutputTextures[name] = this._outputTextures[name]; + } + else { + this._compositor.releaseTexture(this._outputTextures[name]); + } + } + } + } + }); + + module.exports = FilterNode; + + +/***/ }, +/* 47 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Base = __webpack_require__(3); + var OrthoCamera = __webpack_require__(30); + var Plane = __webpack_require__(48); + var Shader = __webpack_require__(52); + var Material = __webpack_require__(53); + var Mesh = __webpack_require__(54); + var glinfo = __webpack_require__(42); + var glenum = __webpack_require__(34); + + Shader['import'](__webpack_require__(56)); + + var planeGeo = new Plane(); + var mesh = new Mesh({ + geometry: planeGeo, + frustumCulling: false + }); + var camera = new OrthoCamera(); + + /** + * @constructor qtek.compositor.Pass + * @extends qtek.core.Base + */ + var Pass = Base.extend(function () { + return /** @lends qtek.compositor.Pass# */ { + /** + * Fragment shader string + * @type {string} + */ + // PENDING shader or fragment ? + fragment : '', + + /** + * @type {Object} + */ + outputs : null, + + /** + * @type {qtek.Material} + */ + material : null, + + /** + * @type {Boolean} + */ + blendWithPrevious: false, + + /** + * @type {Boolean} + */ + clearColor: false, + + /** + * @type {Boolean} + */ + clearDepth: true + }; + }, function() { + + var shader = new Shader({ + vertex : Shader.source('qtek.compositor.vertex'), + fragment : this.fragment + }); + var material = new Material({ + shader : shader + }); + shader.enableTexturesAll(); + + this.material = material; + + }, + /** @lends qtek.compositor.Pass.prototype */ + { + /** + * @param {string} name + * @param {} value + */ + setUniform : function(name, value) { + var uniform = this.material.uniforms[name]; + if (uniform) { + uniform.value = value; + } + }, + /** + * @param {string} name + * @return {} + */ + getUniform : function(name) { + var uniform = this.material.uniforms[name]; + if (uniform) { + return uniform.value; + } + }, + /** + * @param {qtek.Texture} texture + * @param {number} attachment + */ + attachOutput : function(texture, attachment) { + if (!this.outputs) { + this.outputs = {}; + } + attachment = attachment || glenum.COLOR_ATTACHMENT0; + this.outputs[attachment] = texture; + }, + /** + * @param {qtek.Texture} texture + */ + detachOutput : function(texture) { + for (var attachment in this.outputs) { + if (this.outputs[attachment] === texture) { + this.outputs[attachment] = null; + } + } + }, + + bind : function(renderer, frameBuffer) { + + if (this.outputs) { + for (var attachment in this.outputs) { + var texture = this.outputs[attachment]; + if (texture) { + frameBuffer.attach(texture, attachment); + } + } + } + + if (frameBuffer) { + frameBuffer.bind(renderer); + } + }, + + unbind : function(renderer, frameBuffer) { + frameBuffer.unbind(renderer); + }, + /** + * @param {qtek.Renderer} renderer + * @param {qtek.FrameBuffer} [frameBuffer] + */ + render : function(renderer, frameBuffer) { + + var _gl = renderer.gl; + + if (frameBuffer) { + this.bind(renderer, frameBuffer); + // MRT Support in chrome + // https://www.khronos.org/registry/webgl/sdk/tests/conformance/extensions/ext-draw-buffers.html + var ext = glinfo.getExtension(_gl, 'EXT_draw_buffers'); + if (ext && this.outputs) { + var bufs = []; + for (var attachment in this.outputs) { + attachment = +attachment; + if (attachment >= _gl.COLOR_ATTACHMENT0 && attachment <= _gl.COLOR_ATTACHMENT0 + 8) { + bufs.push(attachment); + } + } + ext.drawBuffersEXT(bufs); + } + } + + this.trigger('beforerender', this, renderer); + + // FIXME Don't clear in each pass in default, let the color overwrite the buffer + // FIXME pixels may be discard + var clearBit = this.clearDepth ? _gl.DEPTH_BUFFER_BIT : 0; + _gl.depthMask(true); + if (this.clearColor) { + clearBit = clearBit | _gl.COLOR_BUFFER_BIT; + _gl.colorMask(true, true, true, true); + var cc = this.clearColor; + if (cc instanceof Array) { + _gl.clearColor(cc[0], cc[1], cc[2], cc[3]); + } + } + _gl.clear(clearBit); + + if (this.blendWithPrevious) { + // Blend with previous rendered scene in the final output + // FIXME Configure blend. + // FIXME It will cause screen blink? + _gl.enable(_gl.BLEND); + this.material.transparent = true; + } + else { + _gl.disable(_gl.BLEND); + this.material.transparent = false; + } + + this.renderQuad(renderer); + + this.trigger('afterrender', this, renderer); + + if (frameBuffer) { + this.unbind(renderer, frameBuffer); + } + }, + + /** + * Simply do quad rendering + */ + renderQuad: function (renderer) { + mesh.material = this.material; + renderer.renderQueue([mesh], camera); + }, + + /** + * @param {WebGLRenderingContext} _gl + */ + dispose: function (gl) { + this.material.dispose(gl); + } + }); + + module.exports = Pass; + + +/***/ }, +/* 48 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var StaticGeometry = __webpack_require__(49); + var BoundingBox = __webpack_require__(26); + + /** + * @constructor qtek.geometry.Plane + * @extends qtek.StaticGeometry + * @param {Object} [opt] + * @param {number} [opt.widthSegments] + * @param {number} [opt.heightSegments] + */ + var Plane = StaticGeometry.extend( + /** @lends qtek.geometry.Plane# */ + { + /** + * @type {number} + */ + widthSegments: 1, + /** + * @type {number} + */ + heightSegments: 1 + }, function() { + this.build(); + }, + /** @lends qtek.geometry.Plane.prototype */ + { + /** + * Build plane geometry + */ + build: function() { + var heightSegments = this.heightSegments; + var widthSegments = this.widthSegments; + var attributes = this.attributes; + var positions = []; + var texcoords = []; + var normals = []; + var faces = []; + + for (var y = 0; y <= heightSegments; y++) { + var t = y / heightSegments; + for (var x = 0; x <= widthSegments; x++) { + var s = x / widthSegments; + + positions.push([2 * s - 1, 2 * t - 1, 0]); + if (texcoords) { + texcoords.push([s, t]); + } + if (normals) { + normals.push([0, 0, 1]); + } + if (x < widthSegments && y < heightSegments) { + var i = x + y * (widthSegments + 1); + faces.push([i, i + 1, i + widthSegments + 1]); + faces.push([i + widthSegments + 1, i + 1, i + widthSegments + 2]); + } + } + } + + attributes.position.fromArray(positions); + attributes.texcoord0.fromArray(texcoords); + attributes.normal.fromArray(normals); + + this.initIndicesFromArray(faces); + + this.boundingBox = new BoundingBox(); + this.boundingBox.min.set(-1, -1, 0); + this.boundingBox.max.set(1, 1, 0); + } + }); + + module.exports = Plane; + + +/***/ }, +/* 49 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * StaticGeometry can not be changed once they've been setup + */ + + + var Geometry = __webpack_require__(50); + var BoundingBox = __webpack_require__(26); + var glMatrix = __webpack_require__(14); + var vendor = __webpack_require__(51); + var glenum = __webpack_require__(34); + var mat4 = glMatrix.mat4; + var vec3 = glMatrix.vec3; + + var StaticAttribute = Geometry.StaticAttribute; + var vec3Create = vec3.create; + var vec3Add = vec3.add; + var vec3Set = vec3.set; + + function makeAttrKey(attrName) { + return 'attr_' + attrName; + } + /** + * @constructor qtek.StaticGeometry + * @extends qtek.Geometry + */ + var StaticGeometry = Geometry.extend(function () { + return /** @lends qtek.StaticGeometry# */ { + attributes: { + position: new StaticAttribute('position', 'float', 3, 'POSITION'), + texcoord0: new StaticAttribute('texcoord0', 'float', 2, 'TEXCOORD_0'), + texcoord1: new StaticAttribute('texcoord1', 'float', 2, 'TEXCOORD_1'), + normal: new StaticAttribute('normal', 'float', 3, 'NORMAL'), + tangent: new StaticAttribute('tangent', 'float', 4, 'TANGENT'), + color: new StaticAttribute('color', 'float', 4, 'COLOR'), + // Skinning attributes + // Each vertex can be bind to 4 bones, because the + // sum of weights is 1, so the weights is stored in vec3 and the last + // can be calculated by 1-w.x-w.y-w.z + weight: new StaticAttribute('weight', 'float', 3, 'WEIGHT'), + joint: new StaticAttribute('joint', 'float', 4, 'JOINT'), + // For wireframe display + // http://codeflow.org/entries/2012/aug/02/easy-wireframe-display-with-barycentric-coordinates/ + barycentric: new StaticAttribute('barycentric', 'float', 3, null), + }, + + hint: glenum.STATIC_DRAW, + + /** + * @type {Uint16Array|Uint32Array} + */ + indices: null, + + _normalType: 'vertex', + + _enabledAttributes: null + }; + }, + /** @lends qtek.StaticGeometry.prototype */ + { + updateBoundingBox: function () { + var bbox = this.boundingBox; + if (!bbox) { + bbox = this.boundingBox = new BoundingBox(); + } + var posArr = this.attributes.position.value; + if (posArr && posArr.length) { + var min = bbox.min; + var max = bbox.max; + var minArr = min._array; + var maxArr = max._array; + vec3.set(minArr, posArr[0], posArr[1], posArr[2]); + vec3.set(maxArr, posArr[0], posArr[1], posArr[2]); + for (var i = 3; i < posArr.length;) { + var x = posArr[i++]; + var y = posArr[i++]; + var z = posArr[i++]; + if (x < minArr[0]) { minArr[0] = x; } + if (y < minArr[1]) { minArr[1] = y; } + if (z < minArr[2]) { minArr[2] = z; } + + if (x > maxArr[0]) { maxArr[0] = x; } + if (y > maxArr[1]) { maxArr[1] = y; } + if (z > maxArr[2]) { maxArr[2] = z; } + } + min._dirty = true; + max._dirty = true; + } + }, + + dirty: function () { + var enabledAttributes = this.getEnabledAttributes(); + for (var i = 0; i < enabledAttributes.length; i++) { + this.dirtyAttribute(enabledAttributes[i]); + } + this.dirtyIndices(); + this._enabledAttributes = null; + }, + + dirtyIndices: function () { + this._cache.dirtyAll('indices'); + }, + + dirtyAttribute: function (attrName) { + this._cache.dirtyAll(makeAttrKey(attrName)); + this._cache.dirtyAll('attributes'); + }, + + getTriangleIndices: function (idx, out) { + if (idx < this.triangleCount && idx >= 0) { + if (!out) { + out = vec3Create(); + } + var indices = this.indices; + out[0] = indices[idx * 3]; + out[1] = indices[idx * 3 + 1]; + out[2] = indices[idx * 3 + 2]; + return out; + } + }, + + setTriangleIndices: function (idx, arr) { + var indices = this.indices; + indices[idx * 3] = arr[0]; + indices[idx * 3 + 1] = arr[1]; + indices[idx * 3 + 2] = arr[2]; + }, + + isUseIndices: function () { + return this.indices; + }, + + initIndicesFromArray: function (array) { + var value; + var ArrayConstructor = this.vertexCount > 0xffff + ? vendor.Uint32Array : vendor.Uint16Array; + // Convert 2d array to flat + if (array[0] && (array[0].length)) { + var n = 0; + var size = 3; + + value = new ArrayConstructor(array.length * size); + for (var i = 0; i < array.length; i++) { + for (var j = 0; j < size; j++) { + value[n++] = array[i][j]; + } + } + } + else { + value = new ArrayConstructor(array); + } + + this.indices = value; + }, + + createAttribute: function (name, type, size, semantic) { + var attrib = new StaticAttribute(name, type, size, semantic); + if (this.attributes[name]) { + this.removeAttribute(name); + } + this.attributes[name] = attrib; + this._attributeList.push(name); + return attrib; + }, + + removeAttribute: function (name) { + var attributeList = this._attributeList; + var idx = attributeList.indexOf(name); + if (idx >= 0) { + attributeList.splice(idx, 1); + delete this.attributes[name]; + return true; + } + return false; + }, + + /** + * Get enabled attributes name list + * Attribute which has the same vertex number with position is treated as a enabled attribute + * @return {string[]} + */ + getEnabledAttributes: function () { + var enabledAttributes = this._enabledAttributes; + var attributeList = this._attributeList; + // Cache + if (enabledAttributes) { + return enabledAttributes; + } + + var result = []; + var nVertex = this.vertexCount; + + for (var i = 0; i < attributeList.length; i++) { + var name = attributeList[i]; + var attrib = this.attributes[name]; + if (attrib.value) { + if (attrib.value.length === nVertex * attrib.size) { + result.push(name); + } + } + } + + this._enabledAttributes = result; + + return result; + }, + + getBufferChunks: function (_gl) { + var cache = this._cache; + cache.use(_gl.__GLID__); + var isAttributesDirty = cache.isDirty('attributes'); + var isIndicesDirty = cache.isDirty('indices'); + if (isAttributesDirty || isIndicesDirty) { + this._updateBuffer(_gl, isAttributesDirty, isIndicesDirty); + var enabledAttributes = this.getEnabledAttributes(); + for (var i = 0; i < enabledAttributes.length; i++) { + cache.fresh(makeAttrKey(enabledAttributes[i])); + } + cache.fresh('attributes'); + cache.fresh('indices'); + } + return cache.get('chunks'); + }, + + _updateBuffer: function (_gl, isAttributesDirty, isIndicesDirty) { + var cache = this._cache; + var chunks = cache.get('chunks'); + var firstUpdate = false; + if (!chunks) { + chunks = []; + // Intialize + chunks[0] = { + attributeBuffers: [], + indicesBuffer: null + }; + cache.put('chunks', chunks); + firstUpdate = true; + } + + var chunk = chunks[0]; + var attributeBuffers = chunk.attributeBuffers; + var indicesBuffer = chunk.indicesBuffer; + + if (isAttributesDirty || firstUpdate) { + var attributeList = this.getEnabledAttributes(); + + var attributeBufferMap = {}; + if (!firstUpdate) { + for (var i = 0; i < attributeBuffers.length; i++) { + attributeBufferMap[attributeBuffers[i].name] = attributeBuffers[i]; + } + } + // FIXME If some attributes removed + for (var k = 0; k < attributeList.length; k++) { + var name = attributeList[k]; + var attribute = this.attributes[name]; + + var bufferInfo; + + if (!firstUpdate) { + bufferInfo = attributeBufferMap[name]; + } + var buffer; + if (bufferInfo) { + buffer = bufferInfo.buffer; + } + else { + buffer = _gl.createBuffer(); + } + if (cache.isDirty(makeAttrKey(name))) { + // Only update when they are dirty. + // TODO: Use BufferSubData? + _gl.bindBuffer(_gl.ARRAY_BUFFER, buffer); + _gl.bufferData(_gl.ARRAY_BUFFER, attribute.value, this.hint); + } + + attributeBuffers[k] = new Geometry.AttributeBuffer(name, attribute.type, buffer, attribute.size, attribute.semantic); + } + // Remove unused attributes buffers. + // PENDING + for (var i = k; i < attributeBuffers.length; i++) { + _gl.deleteBuffer(attributeBuffers[i].buffer); + } + attributeBuffers.length = k; + + } + + if (this.isUseIndices() && (isIndicesDirty || firstUpdate)) { + if (!indicesBuffer) { + indicesBuffer = new Geometry.IndicesBuffer(_gl.createBuffer()); + chunk.indicesBuffer = indicesBuffer; + } + indicesBuffer.count = this.indices.length; + _gl.bindBuffer(_gl.ELEMENT_ARRAY_BUFFER, indicesBuffer.buffer); + _gl.bufferData(_gl.ELEMENT_ARRAY_BUFFER, this.indices, this.hint); + } + }, + + generateVertexNormals: function () { + if (!this.vertexCount) { + return; + } + + var indices = this.indices; + var attributes = this.attributes; + var positions = attributes.position.value; + var normals = attributes.normal.value; + + if (!normals || normals.length !== positions.length) { + normals = attributes.normal.value = new vendor.Float32Array(positions.length); + } + else { + // Reset + for (var i = 0; i < normals.length; i++) { + normals[i] = 0; + } + } + + var p1 = vec3Create(); + var p2 = vec3Create(); + var p3 = vec3Create(); + + var v21 = vec3Create(); + var v32 = vec3Create(); + + var n = vec3Create(); + + // TODO if no indices + for (var f = 0; f < indices.length;) { + var i1 = indices[f++]; + var i2 = indices[f++]; + var i3 = indices[f++]; + + vec3Set(p1, positions[i1*3], positions[i1*3+1], positions[i1*3+2]); + vec3Set(p2, positions[i2*3], positions[i2*3+1], positions[i2*3+2]); + vec3Set(p3, positions[i3*3], positions[i3*3+1], positions[i3*3+2]); + + vec3.sub(v21, p1, p2); + vec3.sub(v32, p2, p3); + vec3.cross(n, v21, v32); + // Already be weighted by the triangle area + for (var i = 0; i < 3; i++) { + normals[i1*3+i] = normals[i1*3+i] + n[i]; + normals[i2*3+i] = normals[i2*3+i] + n[i]; + normals[i3*3+i] = normals[i3*3+i] + n[i]; + } + } + + for (var i = 0; i < normals.length;) { + vec3Set(n, normals[i], normals[i+1], normals[i+2]); + vec3.normalize(n, n); + normals[i++] = n[0]; + normals[i++] = n[1]; + normals[i++] = n[2]; + } + this.dirty(); + }, + + generateFaceNormals: function () { + if (!this.vertexCount) { + return; + } + + if (!this.isUniqueVertex()) { + this.generateUniqueVertex(); + } + + var indices = this.indices; + var attributes = this.attributes; + var positions = attributes.position.value; + var normals = attributes.normal.value; + + var p1 = vec3Create(); + var p2 = vec3Create(); + var p3 = vec3Create(); + + var v21 = vec3Create(); + var v32 = vec3Create(); + var n = vec3Create(); + + if (!normals) { + normals = attributes.normal.value = new Float32Array(positions.length); + } + for (var f = 0; f < indices.length;) { + var i1 = indices[f++]; + var i2 = indices[f++]; + var i3 = indices[f++]; + + vec3Set(p1, positions[i1*3], positions[i1*3+1], positions[i1*3+2]); + vec3Set(p2, positions[i2*3], positions[i2*3+1], positions[i2*3+2]); + vec3Set(p3, positions[i3*3], positions[i3*3+1], positions[i3*3+2]); + + vec3.sub(v21, p1, p2); + vec3.sub(v32, p2, p3); + vec3.cross(n, v21, v32); + + vec3.normalize(n, n); + + for (var i = 0; i < 3; i++) { + normals[i1*3 + i] = n[i]; + normals[i2*3 + i] = n[i]; + normals[i3*3 + i] = n[i]; + } + } + this.dirty(); + }, + + generateTangents: function () { + if (!this.vertexCount) { + return; + } + + var nVertex = this.vertexCount; + var attributes = this.attributes; + if (!attributes.tangent.value) { + attributes.tangent.value = new Float32Array(nVertex * 4); + } + var texcoords = attributes.texcoord0.value; + var positions = attributes.position.value; + var tangents = attributes.tangent.value; + var normals = attributes.normal.value; + + var tan1 = []; + var tan2 = []; + for (var i = 0; i < nVertex; i++) { + tan1[i] = [0.0, 0.0, 0.0]; + tan2[i] = [0.0, 0.0, 0.0]; + } + + var sdir = [0.0, 0.0, 0.0]; + var tdir = [0.0, 0.0, 0.0]; + var indices = this.indices; + for (var i = 0; i < indices.length;) { + var i1 = indices[i++], + i2 = indices[i++], + i3 = indices[i++], + + st1s = texcoords[i1 * 2], + st2s = texcoords[i2 * 2], + st3s = texcoords[i3 * 2], + st1t = texcoords[i1 * 2 + 1], + st2t = texcoords[i2 * 2 + 1], + st3t = texcoords[i3 * 2 + 1], + + p1x = positions[i1 * 3], + p2x = positions[i2 * 3], + p3x = positions[i3 * 3], + p1y = positions[i1 * 3 + 1], + p2y = positions[i2 * 3 + 1], + p3y = positions[i3 * 3 + 1], + p1z = positions[i1 * 3 + 2], + p2z = positions[i2 * 3 + 2], + p3z = positions[i3 * 3 + 2]; + + var x1 = p2x - p1x, + x2 = p3x - p1x, + y1 = p2y - p1y, + y2 = p3y - p1y, + z1 = p2z - p1z, + z2 = p3z - p1z; + + var s1 = st2s - st1s, + s2 = st3s - st1s, + t1 = st2t - st1t, + t2 = st3t - st1t; + + var r = 1.0 / (s1 * t2 - t1 * s2); + sdir[0] = (t2 * x1 - t1 * x2) * r; + sdir[1] = (t2 * y1 - t1 * y2) * r; + sdir[2] = (t2 * z1 - t1 * z2) * r; + + tdir[0] = (s1 * x2 - s2 * x1) * r; + tdir[1] = (s1 * y2 - s2 * y1) * r; + tdir[2] = (s1 * z2 - s2 * z1) * r; + + vec3Add(tan1[i1], tan1[i1], sdir); + vec3Add(tan1[i2], tan1[i2], sdir); + vec3Add(tan1[i3], tan1[i3], sdir); + vec3Add(tan2[i1], tan2[i1], tdir); + vec3Add(tan2[i2], tan2[i2], tdir); + vec3Add(tan2[i3], tan2[i3], tdir); + } + var tmp = vec3Create(); + var nCrossT = vec3Create(); + var n = vec3Create(); + for (var i = 0; i < nVertex; i++) { + n[0] = normals[i * 3]; + n[1] = normals[i * 3 + 1]; + n[2] = normals[i * 3 + 2]; + var t = tan1[i]; + + // Gram-Schmidt orthogonalize + vec3.scale(tmp, n, vec3.dot(n, t)); + vec3.sub(tmp, t, tmp); + vec3.normalize(tmp, tmp); + // Calculate handedness. + vec3.cross(nCrossT, n, t); + tangents[i * 4] = tmp[0]; + tangents[i * 4 + 1] = tmp[1]; + tangents[i * 4 + 2] = tmp[2]; + tangents[i * 4 + 3] = vec3.dot(nCrossT, tan2[i]) < 0.0 ? -1.0 : 1.0; + } + this.dirty(); + }, + + isUniqueVertex: function () { + if (this.isUseIndices()) { + return this.vertexCount === this.indices.length; + } + else { + return true; + } + }, + + generateUniqueVertex: function () { + if (!this.vertexCount) { + return; + } + + var vertexUseCount = []; + + for (var i = 0, len = this.vertexCount; i < len; i++) { + vertexUseCount[i] = 0; + } + if (this.indices.length > 0xffff) { + this.indices = new vendor.Uint32Array(this.indices); + } + + var cursor = 0; + var attributes = this.attributes; + var indices = this.indices; + + // Cursor not use vertexNumber in case vertex array length is larger than face used. + for (var i = 0; i < indices.length; i++) { + cursor = Math.max(cursor, indices[i] + 1); + } + + var attributeNameList = this.getEnabledAttributes(); + + for (var a = 0; a < attributeNameList.length; a++) { + var name = attributeNameList[a]; + var valueArr = attributes[name].value; + attributes[name].init(this.indices.length); + var expandedArray = attributes[name].value; + for (var i = 0; i < valueArr.length; i++) { + expandedArray[i] = valueArr[i]; + } + } + + for (var i = 0; i < indices.length; i++) { + var ii = indices[i]; + if (vertexUseCount[ii] > 0) { + for (var a = 0; a < attributeNameList.length; a++) { + var name = attributeNameList[a]; + var array = attributes[name].value; + var size = attributes[name].size; + + for (var k = 0; k < size; k++) { + array[cursor * size + k] = array[ii * size + k]; + } + } + indices[i] = cursor; + cursor++; + } + vertexUseCount[ii]++; + } + + this.dirty(); + }, + + generateBarycentric: function () { + if (!this.vertexCount) { + return; + } + + if (!this.isUniqueVertex()) { + this.generateUniqueVertex(); + } + + var attributes = this.attributes; + var array = attributes.barycentric.value; + var indices = this.indices; + // Already existed; + if (array && array.length === indices.length * 3) { + return; + } + array = attributes.barycentric.value = new Float32Array(indices.length * 3); + for (var i = 0; i < indices.length;) { + for (var j = 0; j < 3; j++) { + var ii = indices[i++]; + array[ii * 3 + j] = 1; + } + } + this.dirty(); + }, + + applyTransform: function (matrix) { + + var attributes = this.attributes; + var positions = attributes.position.value; + var normals = attributes.normal.value; + var tangents = attributes.tangent.value; + + matrix = matrix._array; + // Normal Matrix + var inverseTransposeMatrix = mat4.create(); + mat4.invert(inverseTransposeMatrix, matrix); + mat4.transpose(inverseTransposeMatrix, inverseTransposeMatrix); + + var vec3TransformMat4 = vec3.transformMat4; + var vec3ForEach = vec3.forEach; + vec3ForEach(positions, 3, 0, null, vec3TransformMat4, matrix); + if (normals) { + vec3ForEach(normals, 3, 0, null, vec3TransformMat4, inverseTransposeMatrix); + } + if (tangents) { + vec3ForEach(tangents, 4, 0, null, vec3TransformMat4, inverseTransposeMatrix); + } + + if (this.boundingBox) { + this.updateBoundingBox(); + } + }, + + dispose: function (_gl) { + + var cache = this._cache; + + cache.use(_gl.__GLID__); + var chunks = cache.get('chunks'); + if (chunks) { + for (var c = 0; c < chunks.length; c++) { + var chunk = chunks[c]; + + for (var k = 0; k < chunk.attributeBuffers.length; k++) { + var attribs = chunk.attributeBuffers[k]; + _gl.deleteBuffer(attribs.buffer); + } + } + } + cache.deleteContext(_gl.__GLID__); + } + }); + + if (Object.defineProperty) { + Object.defineProperty(StaticGeometry.prototype, 'vertexCount', { + + enumerable: false, + + get: function () { + var mainAttribute = this.attributes[this.mainAttribute]; + if (!mainAttribute || !mainAttribute.value) { + return 0; + } + return mainAttribute.value.length / mainAttribute.size; + } + }); + Object.defineProperty(StaticGeometry.prototype, 'triangleCount', { + + enumerable: false, + + get: function () { + var indices = this.indices; + if (!indices) { + return 0; + } + else { + return indices.length / 3; + } + } + }); + } + + StaticGeometry.Attribute = Geometry.StaticAttribute; + + module.exports = StaticGeometry; + + +/***/ }, +/* 50 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Base = __webpack_require__(3); + var glenum = __webpack_require__(34); + var Cache = __webpack_require__(41); + var vendor = __webpack_require__(51); + var glmatrix = __webpack_require__(14); + var vec2 = glmatrix.vec2; + var vec3 = glmatrix.vec3; + var vec4 = glmatrix.vec4; + + var vec4Copy = vec4.copy; + var vec3Copy = vec3.copy; + var vec2Copy = vec2.copy; + + function getArrayCtorByType (type) { + var ArrayConstructor; + switch(type) { + case 'byte': + ArrayConstructor = vendor.Int8Array; + break; + case 'ubyte': + ArrayConstructor = vendor.Uint8Array; + break; + case 'short': + ArrayConstructor = vendor.Int16Array; + break; + case 'ushort': + ArrayConstructor = vendor.Uint16Array; + break; + default: + ArrayConstructor = vendor.Float32Array; + break; + } + return ArrayConstructor; + } + + + function Attribute(name, type, size, semantic) { + this.name = name; + this.type = type; + this.size = size; + if (semantic) { + this.semantic = semantic; + } + } + Attribute.prototype.clone = function(copyValue) { + var ret = new this.constructor(this.name, this.type, this.size, this.semantic); + // FIXME + if (copyValue) { + console.warn('todo'); + } + return ret; + }; + + + /** + * Attribute for static geometry + */ + function StaticAttribute (name, type, size, semantic) { + Attribute.call(this, name, type, size, semantic); + this.value = null; + + // Init getter setter + switch (size) { + case 1: + this.get = function (idx) { + return this.value[idx]; + }; + this.set = function (idx, value) { + this.value[idx] = value; + }; + // Copy from source to target + this.copy = function (target, source) { + this.value[target] = this.value[target]; + }; + break; + case 2: + this.get = function (idx, out) { + var arr = this.value; + out[0] = arr[idx * 2]; + out[1] = arr[idx * 2 + 1]; + return out; + }; + this.set = function (idx, val) { + var arr = this.value; + arr[idx * 2] = val[0]; + arr[idx * 2 + 1] = val[1]; + }; + this.copy = function (target, source) { + var arr = this.value; + source *= 2; + target *= 2; + arr[target] = arr[source]; + arr[target + 1] = arr[source + 1]; + }; + break; + case 3: + this.get = function (idx, out) { + var idx3 = idx * 3; + var arr = this.value; + out[0] = arr[idx3++]; + out[1] = arr[idx3++]; + out[2] = arr[idx3++]; + return out; + }; + this.set = function (idx, val) { + var idx3 = idx * 3; + var arr = this.value; + arr[idx3++] = val[0]; + arr[idx3++] = val[1]; + arr[idx3++] = val[2]; + }; + this.copy = function (target, source) { + var arr = this.value; + source *= 3; + target *= 3; + arr[target] = arr[source]; + arr[target + 1] = arr[source + 1]; + arr[target + 2] = arr[source + 2]; + }; + break; + case 4: + this.get = function (idx, out) { + var arr = this.value; + var idx4 = idx * 4; + out[0] = arr[idx4++]; + out[1] = arr[idx4++]; + out[2] = arr[idx4++]; + out[3] = arr[idx4++]; + return out; + }; + this.set = function (idx, val) { + var arr = this.value; + var idx4 = idx * 4; + arr[idx4++] = val[0]; + arr[idx4++] = val[1]; + arr[idx4++] = val[2]; + arr[idx4++] = val[3]; + }; + this.copy = function (target, source) { + var arr = this.value; + source *= 4; + target *= 4; + // copyWithin is extremely slow + arr[target] = arr[source]; + arr[target + 1] = arr[source + 1]; + arr[target + 2] = arr[source + 2]; + arr[target + 3] = arr[source + 3]; + }; + } + } + + StaticAttribute.prototype.constructor = new Attribute(); + + StaticAttribute.prototype.init = function (nVertex) { + if (!this.value || this.value.length != nVertex * this.size) { + var ArrayConstructor = getArrayCtorByType(this.type); + this.value = new ArrayConstructor(nVertex * this.size); + } + }; + + StaticAttribute.prototype.fromArray = function (array) { + var ArrayConstructor = getArrayCtorByType(this.type); + var value; + // Convert 2d array to flat + if (array[0] && (array[0].length)) { + var n = 0; + var size = this.size; + value = new ArrayConstructor(array.length * size); + for (var i = 0; i < array.length; i++) { + for (var j = 0; j < size; j++) { + value[n++] = array[i][j]; + } + } + } + else { + value = new ArrayConstructor(array); + } + this.value = value; + }; + + function AttributeBuffer(name, type, buffer, size, semantic) { + this.name = name; + this.type = type; + this.buffer = buffer; + this.size = size; + this.semantic = semantic; + + // To be set in mesh + // symbol in the shader + this.symbol = ''; + + // Needs remove flag + this.needsRemove = false; + } + + function IndicesBuffer(buffer) { + this.buffer = buffer; + this.count = 0; + } + + function notImplementedWarn() { + console.warn('Geometry doesn\'t implement this method, use StaticGeometry instead'); + } + + /** + * @constructor qtek.Geometry + * @extends qtek.core.Base + */ + var Geometry = Base.extend( + /** @lends qtek.Geometry# */ + { + /** + * @type {qtek.math.BoundingBox} + */ + boundingBox : null, + + /** + * Vertex attributes + * @type {Object} + */ + attributes : {}, + + indices : null, + + /** + * Is vertices data dynamically updated + * @type {boolean} + */ + dynamic: false, + + }, function() { + // Use cache + this._cache = new Cache(); + + this._attributeList = Object.keys(this.attributes); + }, + /** @lends qtek.Geometry.prototype */ + { + /** + * User defined ray picking algorithm instead of default + * triangle ray intersection + * @type {Function} + */ + pickByRay: null, + + /** + * Main attribute will be used to count vertex number + * @type {string} + */ + mainAttribute: 'position', + /** + * Mark attributes in geometry is dirty + * @method + */ + dirty: notImplementedWarn, + /** + * Create a new attribute + * @method + * @param {string} name + * @param {string} type + * @param {number} size + * @param {string} [semantic] + */ + createAttribute: notImplementedWarn, + /** + * Remove attribute + * @method + * @param {string} name + */ + removeAttribute: notImplementedWarn, + + /** + * @method + * @param {number} idx + * @param {Array.} out + * @return {Array.} + */ + getTriangleIndices: notImplementedWarn, + + /** + * @method + * @param {number} idx + * @param {Array.} face + */ + setTriangleIndices: notImplementedWarn, + /** + * @method + * @return {boolean} + */ + isUseIndices: notImplementedWarn, + + getEnabledAttributes: notImplementedWarn, + getBufferChunks: notImplementedWarn, + + /** + * @method + */ + generateVertexNormals: notImplementedWarn, + /** + * @method + */ + generateFaceNormals: notImplementedWarn, + /** + * @method + * @return {boolean} + */ + isUniqueVertex: notImplementedWarn, + /** + * @method + */ + generateUniqueVertex: notImplementedWarn, + /** + * @method + */ + generateTangents: notImplementedWarn, + /** + * @method + */ + generateBarycentric: notImplementedWarn, + /** + * @method + * @param {qtek.math.Matrix4} matrix + */ + applyTransform: notImplementedWarn, + /** + * @method + * @param {WebGLRenderingContext} [gl] + */ + dispose: notImplementedWarn + }); + + Geometry.STATIC_DRAW = glenum.STATIC_DRAW; + Geometry.DYNAMIC_DRAW = glenum.DYNAMIC_DRAW; + Geometry.STREAM_DRAW = glenum.STREAM_DRAW; + + Geometry.AttributeBuffer = AttributeBuffer; + Geometry.IndicesBuffer = IndicesBuffer; + Geometry.Attribute = Attribute; + Geometry.StaticAttribute = StaticAttribute; + + module.exports = Geometry; + + +/***/ }, +/* 51 */ +/***/ function(module, exports) { + + 'use strict'; + + + var supportWebGL = true; + try { + var canvas = document.createElement('canvas'); + var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); + if (!gl) { + throw new Error(); + } + } catch (e) { + supportWebGL = false; + } + + var vendor = {}; + + /** + * If support WebGL + * @return {boolean} + */ + vendor.supportWebGL = function () { + return supportWebGL; + }; + + + vendor.Int8Array = typeof Int8Array == 'undefined' ? Array : Int8Array; + + vendor.Uint8Array = typeof Uint8Array == 'undefined' ? Array : Uint8Array; + + vendor.Uint16Array = typeof Uint16Array == 'undefined' ? Array : Uint16Array; + + vendor.Uint32Array = typeof Uint32Array == 'undefined' ? Array : Uint32Array; + + vendor.Int16Array = typeof Int16Array == 'undefined' ? Array : Int16Array; + + vendor.Float32Array = typeof Float32Array == 'undefined' ? Array : Float32Array; + + vendor.Float64Array = typeof Float64Array == 'undefined' ? Array : Float64Array; + + module.exports = vendor; + + +/***/ }, +/* 52 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * Mainly do the parse and compile of shader string + * Support shader code chunk import and export + * Support shader semantics + * http://www.nvidia.com/object/using_sas.html + * https://github.com/KhronosGroup/collada2json/issues/45 + * + * TODO: Use etpl or other string template engine + */ + + + var Base = __webpack_require__(3); + var util = __webpack_require__(6); + var Cache = __webpack_require__(41); + var vendor = __webpack_require__(51); + var glMatrix = __webpack_require__(14); + var glInfo = __webpack_require__(42); + var mat2 = glMatrix.mat2; + var mat3 = glMatrix.mat3; + var mat4 = glMatrix.mat4; + + var uniformRegex = /uniform\s+(bool|float|int|vec2|vec3|vec4|ivec2|ivec3|ivec4|mat2|mat3|mat4|sampler2D|samplerCube)\s+([\w\,]+)?(\[.*?\])?\s*(:\s*([\S\s]+?))?;/g; + var attributeRegex = /attribute\s+(float|int|vec2|vec3|vec4)\s+(\w*)\s*(:\s*(\w+))?;/g; + var defineRegex = /#define\s+(\w+)?(\s+[\w-.]+)?\s*;?\s*\n/g; + var loopRegex = /for\s*?\(int\s*?_idx_\s*\=\s*([\w-]+)\;\s*_idx_\s*<\s*([\w-]+);\s*_idx_\s*\+\+\s*\)\s*\{\{([\s\S]+?)(?=\}\})\}\}/g; + + var uniformTypeMap = { + 'bool': '1i', + 'int': '1i', + 'sampler2D': 't', + 'samplerCube': 't', + 'float': '1f', + 'vec2': '2f', + 'vec3': '3f', + 'vec4': '4f', + 'ivec2': '2i', + 'ivec3': '3i', + 'ivec4': '4i', + 'mat2': 'm2', + 'mat3': 'm3', + 'mat4': 'm4' + }; + + var uniformValueConstructor = { + 'bool': function () {return true;}, + 'int': function () {return 0;}, + 'float': function () {return 0;}, + 'sampler2D': function () {return null;}, + 'samplerCube': function () {return null;}, + + 'vec2': function () {return [0, 0];}, + 'vec3': function () {return [0, 0, 0];}, + 'vec4': function () {return [0, 0, 0, 0];}, + + 'ivec2': function () {return [0, 0];}, + 'ivec3': function () {return [0, 0, 0];}, + 'ivec4': function () {return [0, 0, 0, 0];}, + + 'mat2': function () {return mat2.create();}, + 'mat3': function () {return mat3.create();}, + 'mat4': function () {return mat4.create();}, + + 'array': function () {return [];} + }; + + var attribSemantics = [ + 'POSITION', + 'NORMAL', + 'BINORMAL', + 'TANGENT', + 'TEXCOORD', + 'TEXCOORD_0', + 'TEXCOORD_1', + 'COLOR', + // Skinning + // https://github.com/KhronosGroup/glTF/blob/master/specification/README.md#semantics + 'JOINT', + 'WEIGHT' + ]; + var uniformSemantics = [ + 'SKIN_MATRIX', + // Information about viewport + 'VIEWPORT_SIZE', + 'VIEWPORT', + // Window size for window relative coordinate + // https://www.opengl.org/sdk/docs/man/html/gl_FragCoord.xhtml + 'WINDOW_SIZE', + // Infomation about camera + 'NEAR', + 'FAR' + ]; + var matrixSemantics = [ + 'WORLD', + 'VIEW', + 'PROJECTION', + 'WORLDVIEW', + 'VIEWPROJECTION', + 'WORLDVIEWPROJECTION', + 'WORLDINVERSE', + 'VIEWINVERSE', + 'PROJECTIONINVERSE', + 'WORLDVIEWINVERSE', + 'VIEWPROJECTIONINVERSE', + 'WORLDVIEWPROJECTIONINVERSE', + 'WORLDTRANSPOSE', + 'VIEWTRANSPOSE', + 'PROJECTIONTRANSPOSE', + 'WORLDVIEWTRANSPOSE', + 'VIEWPROJECTIONTRANSPOSE', + 'WORLDVIEWPROJECTIONTRANSPOSE', + 'WORLDINVERSETRANSPOSE', + 'VIEWINVERSETRANSPOSE', + 'PROJECTIONINVERSETRANSPOSE', + 'WORLDVIEWINVERSETRANSPOSE', + 'VIEWPROJECTIONINVERSETRANSPOSE', + 'WORLDVIEWPROJECTIONINVERSETRANSPOSE' + ]; + + // Enable attribute operation is global to all programs + // Here saved the list of all enabled attribute index + // http://www.mjbshaw.com/2013/03/webgl-fixing-invalidoperation.html + var enabledAttributeList = {}; + + var SHADER_STATE_TO_ENABLE = 1; + var SHADER_STATE_KEEP_ENABLE = 2; + var SHADER_STATE_PENDING = 3; + + /** + * @constructor qtek.Shader + * @extends qtek.core.Base + * + * @example + * // Create a phong shader + * var shader = new qtek.Shader({ + * vertex: qtek.Shader.source('qtek.phong.vertex'), + * fragment: qtek.Shader.source('qtek.phong.fragment') + * }); + * // Enable diffuse texture + * shader.enableTexture('diffuseMap'); + * // Use alpha channel in diffuse texture + * shader.define('fragment', 'DIFFUSEMAP_ALPHA_ALPHA'); + */ + var Shader = Base.extend(function () { + return /** @lends qtek.Shader# */ { + /** + * Vertex shader code + * @type {string} + */ + vertex: '', + + /** + * Fragment shader code + * @type {string} + */ + fragment: '', + + + // FIXME mediump is toooooo low for depth on mobile + precision: 'highp', + + // Properties follow will be generated by the program + attribSemantics: {}, + matrixSemantics: {}, + uniformSemantics: {}, + matrixSemanticKeys: [], + + uniformTemplates: {}, + attributeTemplates: {}, + + /** + * Custom defined values in the vertex shader + * @type {Object} + */ + vertexDefines: {}, + /** + * Custom defined values in the vertex shader + * @type {Object} + */ + fragmentDefines: {}, + + /** + * Enabled extensions + * @type {Array.} + */ + extensions: [ + 'OES_standard_derivatives', + 'EXT_shader_texture_lod' + ], + + /** + * Used light group. default is all zero + */ + lightGroup: 0, + + // Defines the each type light number in the scene + // AMBIENT_LIGHT + // AMBIENT_SH_LIGHT + // AMBIENT_CUBEMAP_LIGHT + // POINT_LIGHT + // SPOT_LIGHT + // AREA_LIGHT + lightNumber: {}, + + _textureSlot: 0, + + _attacheMaterialNumber: 0, + + _uniformList: [], + // { + // enabled: true + // shaderType: "vertex", + // } + _textureStatus: {}, + + _vertexProcessed: '', + _fragmentProcessed: '', + + _currentLocationsMap: {} + }; + }, function () { + + this._cache = new Cache(); + + // All context use same code + this._codeDirty = true; + + this._updateShaderString(); + }, + /** @lends qtek.Shader.prototype */ + { + isEqual: function (otherShader) { + if (!otherShader) { + return false; + } + if (this === otherShader) { + if (this._codeDirty) { + // Still needs update and rebind. + return false; + } + return true; + } + if (otherShader._codeDirty) { + otherShader._updateShaderString(); + } + if (this._codeDirty) { + this._updateShaderString(); + } + return !(otherShader._vertexProcessed !== this._vertexProcessed + || otherShader._fragmentProcessed !== this._fragmentProcessed); + }, + /** + * Set vertex shader code + * @param {string} str + */ + setVertex: function (str) { + this.vertex = str; + this._updateShaderString(); + this.dirty(); + }, + + /** + * Set fragment shader code + * @param {string} str + */ + setFragment: function (str) { + this.fragment = str; + this._updateShaderString(); + this.dirty(); + }, + + /** + * Bind shader program + * Return true or error msg if error happened + * @param {WebGLRenderingContext} _gl + */ + bind: function (_gl) { + var cache = this._cache; + cache.use(_gl.__GLID__, getCacheSchema); + + this._currentLocationsMap = cache.get('locations'); + + // Reset slot + this._textureSlot = 0; + + if (this._codeDirty) { + // PENDING + // var availableExts = []; + // var extensions = this.extensions; + // for (var i = 0; i < extensions.length; i++) { + // if (glInfo.getExtension(_gl, extensions[i])) { + // availableExts.push(extensions[i]); + // } + // } + this._updateShaderString(); + } + + if (cache.isDirty('program')) { + var errMsg = this._buildProgram(_gl, this._vertexProcessed, this._fragmentProcessed); + cache.fresh('program'); + + if (errMsg) { + return errMsg; + } + } + + _gl.useProgram(cache.get('program')); + }, + + /** + * Mark dirty and update program in next frame + */ + dirty: function () { + var cache = this._cache; + this._codeDirty = true; + cache.dirtyAll('program'); + for (var i = 0; i < cache._caches.length; i++) { + if (cache._caches[i]) { + var context = cache._caches[i]; + context['locations'] = {}; + context['attriblocations'] = {}; + } + } + }, + + _updateShaderString: function (extensions) { + + if (this.vertex !== this._vertexPrev || + this.fragment !== this._fragmentPrev + ) { + + this._parseImport(); + + this.attribSemantics = {}; + this.matrixSemantics = {}; + this._textureStatus = {}; + + this._parseUniforms(); + this._parseAttributes(); + this._parseDefines(); + + this._vertexPrev = this.vertex; + this._fragmentPrev = this.fragment; + } + + this._addDefineExtensionAndPrecision(extensions); + + this._vertexProcessed = this._unrollLoop(this._vertexProcessed, this.vertexDefines); + this._fragmentProcessed = this._unrollLoop(this._fragmentProcessed, this.fragmentDefines); + + this._codeDirty = false; + }, + + /** + * Add a #define micro in shader code + * @param {string} shaderType Can be vertex, fragment or both + * @param {string} symbol + * @param {number} [val] + */ + define: function (shaderType, symbol, val) { + var vertexDefines = this.vertexDefines; + var fragmentDefines = this.fragmentDefines; + if (shaderType !== 'vertex' && shaderType !== 'fragment' && shaderType !== 'both' + && arguments.length < 3 + ) { + // shaderType default to be 'both' + val = symbol; + symbol = shaderType; + shaderType = 'both'; + } + val = val != null ? val : null; + if (shaderType === 'vertex' || shaderType === 'both') { + if (vertexDefines[symbol] !== val) { + vertexDefines[symbol] = val; + // Mark as dirty + this.dirty(); + } + } + if (shaderType === 'fragment' || shaderType === 'both') { + if (fragmentDefines[symbol] !== val) { + fragmentDefines[symbol] = val; + if (shaderType !== 'both') { + this.dirty(); + } + } + } + }, + + /** + * @param {string} shaderType Can be vertex, fragment or both + * @param {string} symbol + */ + unDefine: function (shaderType, symbol) { + if (shaderType !== 'vertex' && shaderType !== 'fragment' && shaderType !== 'both' + && arguments.length < 2 + ) { + // shaderType default to be 'both' + symbol = shaderType; + shaderType = 'both'; + } + if (shaderType === 'vertex' || shaderType === 'both') { + if (this.isDefined('vertex', symbol)) { + delete this.vertexDefines[symbol]; + // Mark as dirty + this.dirty(); + } + } + if (shaderType === 'fragment' || shaderType === 'both') { + if (this.isDefined('fragment', symbol)) { + delete this.fragmentDefines[symbol]; + if (shaderType !== 'both') { + this.dirty(); + } + } + } + }, + + /** + * @param {string} shaderType Can be vertex, fragment or both + * @param {string} symbol + */ + isDefined: function (shaderType, symbol) { + switch(shaderType) { + case 'vertex': + return this.vertexDefines[symbol] !== undefined; + case 'fragment': + return this.fragmentDefines[symbol] !== undefined; + } + }, + /** + * @param {string} shaderType Can be vertex, fragment or both + * @param {string} symbol + */ + getDefine: function (shaderType, symbol) { + switch(shaderType) { + case 'vertex': + return this.vertexDefines[symbol]; + case 'fragment': + return this.fragmentDefines[symbol]; + } + }, + /** + * Enable a texture, actually it will add a #define micro in the shader code + * For example, if texture symbol is diffuseMap, it will add a line `#define DIFFUSEMAP_ENABLED` in the shader code + * @param {string} symbol + */ + enableTexture: function (symbol) { + if (symbol instanceof Array) { + for (var i = 0; i < symbol.length; i++) { + this.enableTexture(symbol[i]); + } + return; + } + + var status = this._textureStatus[symbol]; + if (status) { + var isEnabled = status.enabled; + if (!isEnabled) { + status.enabled = true; + this.dirty(); + } + } + }, + /** + * Enable all textures used in the shader + */ + enableTexturesAll: function () { + var textureStatus = this._textureStatus; + for (var symbol in textureStatus) { + textureStatus[symbol].enabled = true; + } + + this.dirty(); + }, + /** + * Disable a texture, it remove a #define micro in the shader + * @param {string} symbol + */ + disableTexture: function (symbol) { + if (symbol instanceof Array) { + for (var i = 0; i < symbol.length; i++) { + this.disableTexture(symbol[i]); + } + return; + } + + var status = this._textureStatus[symbol]; + if (status) { + var isDisabled = ! status.enabled; + if (!isDisabled) { + status.enabled = false; + this.dirty(); + } + } + }, + /** + * Disable all textures used in the shader + */ + disableTexturesAll: function () { + var textureStatus = this._textureStatus; + for (var symbol in textureStatus) { + textureStatus[symbol].enabled = false; + } + + this.dirty(); + }, + /** + * @param {string} symbol + * @return {boolean} + */ + isTextureEnabled: function (symbol) { + var textureStatus = this._textureStatus; + return textureStatus[symbol] + && textureStatus[symbol].enabled; + }, + + getEnabledTextures: function () { + var enabledTextures = []; + var textureStatus = this._textureStatus; + for (var symbol in textureStatus) { + if (textureStatus[symbol].enabled) { + enabledTextures.push(symbol); + } + } + return enabledTextures; + }, + + hasUniform: function (symbol) { + var location = this._currentLocationsMap[symbol]; + return location !== null && location !== undefined; + }, + + currentTextureSlot: function () { + return this._textureSlot; + }, + + resetTextureSlot: function (slot) { + this._textureSlot = slot || 0; + }, + + useCurrentTextureSlot: function (_gl, texture) { + var textureSlot = this._textureSlot; + + this.useTextureSlot(_gl, texture, textureSlot); + + this._textureSlot++; + + return textureSlot; + }, + + useTextureSlot: function (_gl, texture, slot) { + if (texture) { + _gl.activeTexture(_gl.TEXTURE0 + slot); + // Maybe texture is not loaded yet; + if (texture.isRenderable()) { + texture.bind(_gl); + } + else { + // Bind texture to null + texture.unbind(_gl); + } + } + }, + + setUniform: function (_gl, type, symbol, value) { + var locationMap = this._currentLocationsMap; + var location = locationMap[symbol]; + // Uniform is not existed in the shader + if (location === null || location === undefined) { + return false; + } + switch (type) { + case 'm4': + // The matrix must be created by glmatrix and can pass it directly. + _gl.uniformMatrix4fv(location, false, value); + break; + case '2i': + _gl.uniform2i(location, value[0], value[1]); + break; + case '2f': + _gl.uniform2f(location, value[0], value[1]); + break; + case '3i': + _gl.uniform3i(location, value[0], value[1], value[2]); + break; + case '3f': + _gl.uniform3f(location, value[0], value[1], value[2]); + break; + case '4i': + _gl.uniform4i(location, value[0], value[1], value[2], value[3]); + break; + case '4f': + _gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + case '1i': + _gl.uniform1i(location, value); + break; + case '1f': + _gl.uniform1f(location, value); + break; + case '1fv': + _gl.uniform1fv(location, value); + break; + case '1iv': + _gl.uniform1iv(location, value); + break; + case '2iv': + _gl.uniform2iv(location, value); + break; + case '2fv': + _gl.uniform2fv(location, value); + break; + case '3iv': + _gl.uniform3iv(location, value); + break; + case '3fv': + _gl.uniform3fv(location, value); + break; + case '4iv': + _gl.uniform4iv(location, value); + break; + case '4fv': + _gl.uniform4fv(location, value); + break; + case 'm2': + case 'm2v': + _gl.uniformMatrix2fv(location, false, value); + break; + case 'm3': + case 'm3v': + _gl.uniformMatrix3fv(location, false, value); + break; + case 'm4v': + // Raw value + if (value instanceof Array) { + var array = new vendor.Float32Array(value.length * 16); + var cursor = 0; + for (var i = 0; i < value.length; i++) { + var item = value[i]; + for (var j = 0; j < 16; j++) { + array[cursor++] = item[j]; + } + } + _gl.uniformMatrix4fv(location, false, array); + } + else if (value instanceof vendor.Float32Array) { // ArrayBufferView + _gl.uniformMatrix4fv(location, false, value); + } + break; + } + return true; + }, + + setUniformOfSemantic: function (_gl, semantic, val) { + var semanticInfo = this.uniformSemantics[semantic]; + if (semanticInfo) { + return this.setUniform(_gl, semanticInfo.type, semanticInfo.symbol, val); + } + return false; + }, + + // Enable the attributes passed in and disable the rest + // Example Usage: + // enableAttributes(_gl, ["position", "texcoords"]) + enableAttributes: function (_gl, attribList, vao) { + + var program = this._cache.get('program'); + + var locationMap = this._cache.get('attriblocations'); + + var enabledAttributeListInContext; + if (vao) { + enabledAttributeListInContext = vao.__enabledAttributeList; + } + else { + enabledAttributeListInContext = enabledAttributeList[_gl.__GLID__]; + } + if (! enabledAttributeListInContext) { + // In vertex array object context + // PENDING Each vao object needs to enable attributes again? + if (vao) { + enabledAttributeListInContext + = vao.__enabledAttributeList + = []; + } + else { + enabledAttributeListInContext + = enabledAttributeList[_gl.__GLID__] + = []; + } + } + var locationList = []; + for (var i = 0; i < attribList.length; i++) { + var symbol = attribList[i]; + if (!this.attributeTemplates[symbol]) { + locationList[i] = -1; + continue; + } + var location = locationMap[symbol]; + if (location === undefined) { + location = _gl.getAttribLocation(program, symbol); + // Attrib location is a number from 0 to ... + if (location === -1) { + locationList[i] = -1; + continue; + } + locationMap[symbol] = location; + } + locationList[i] = location; + + if (!enabledAttributeListInContext[location]) { + enabledAttributeListInContext[location] = SHADER_STATE_TO_ENABLE; + } + else { + enabledAttributeListInContext[location] = SHADER_STATE_KEEP_ENABLE; + } + } + + for (var i = 0; i < enabledAttributeListInContext.length; i++) { + switch(enabledAttributeListInContext[i]){ + case SHADER_STATE_TO_ENABLE: + _gl.enableVertexAttribArray(i); + enabledAttributeListInContext[i] = SHADER_STATE_PENDING; + break; + case SHADER_STATE_KEEP_ENABLE: + enabledAttributeListInContext[i] = SHADER_STATE_PENDING; + break; + // Expired + case SHADER_STATE_PENDING: + _gl.disableVertexAttribArray(i); + enabledAttributeListInContext[i] = 0; + break; + } + } + + return locationList; + }, + + _parseImport: function () { + + this._vertexProcessedWithoutDefine = Shader.parseImport(this.vertex); + this._fragmentProcessedWithoutDefine = Shader.parseImport(this.fragment); + + }, + + _addDefineExtensionAndPrecision: function (extensions) { + + extensions = extensions || this.extensions; + // Extension declaration must before all non-preprocessor codes + // TODO vertex ? extension enum ? + var extensionStr = []; + for (var i = 0; i < extensions.length; i++) { + extensionStr.push('#extension GL_' + extensions[i] + ' : enable'); + } + + // Add defines + // VERTEX + var defineStr = this._getDefineStr(this.vertexDefines); + this._vertexProcessed = defineStr + '\n' + this._vertexProcessedWithoutDefine; + + // FRAGMENT + var defineStr = this._getDefineStr(this.fragmentDefines); + var code = defineStr + '\n' + this._fragmentProcessedWithoutDefine; + + // Add precision + this._fragmentProcessed = extensionStr.join('\n') + '\n' + + ['precision', this.precision, 'float'].join(' ') + ';\n' + + ['precision', this.precision, 'int'].join(' ') + ';\n' + // depth texture may have precision problem on iOS device. + + ['precision', this.precision, 'sampler2D'].join(' ') + ';\n' + + code; + }, + + _getDefineStr: function (defines) { + + var lightNumber = this.lightNumber; + var textureStatus = this._textureStatus; + var defineStr = []; + for (var lightType in lightNumber) { + var count = lightNumber[lightType]; + if (count > 0) { + defineStr.push('#define ' + lightType.toUpperCase() + '_COUNT ' + count); + } + } + for (var symbol in textureStatus) { + var status = textureStatus[symbol]; + if (status.enabled) { + defineStr.push('#define ' + symbol.toUpperCase() + '_ENABLED'); + } + } + // Custom Defines + for (var symbol in defines) { + var value = defines[symbol]; + if (value === null) { + defineStr.push('#define ' + symbol); + } + else{ + defineStr.push('#define ' + symbol + ' ' + value.toString()); + } + } + return defineStr.join('\n'); + }, + + _unrollLoop: function (shaderStr, defines) { + // Loop unroll from three.js, https://github.com/mrdoob/three.js/blob/master/src/renderers/webgl/WebGLProgram.js#L175 + // In some case like shadowMap in loop use 'i' to index value much slower. + + // Loop use _idx_ and increased with _idx_++ will be unrolled + // Use {{ }} to match the pair so the if statement will not be affected + // Write like following + // for (int _idx_ = 0; _idx_ < 4; _idx_++) {{ + // vec3 color = texture2D(textures[_idx_], uv).rgb; + // }} + function replace(match, start, end, snippet) { + var unroll = ''; + // Try to treat as define + if (isNaN(start)) { + if (start in defines) { + start = defines[start]; + } + else { + start = lightNumberDefines[start]; + } + } + if (isNaN(end)) { + if (end in defines) { + end = defines[end]; + } + else { + end = lightNumberDefines[end]; + } + } + // TODO Error checking + + for (var idx = parseInt(start); idx < parseInt(end); idx++) { + // PENDING Add scope? + unroll += '{' + + snippet + .replace(/float\s*\(\s*_idx_\s*\)/g, idx.toFixed(1)) + .replace(/_idx_/g, idx) + + '\n' + '}'; + } + + return unroll; + } + + var lightNumberDefines = {}; + for (var lightType in this.lightNumber) { + lightNumberDefines[lightType + '_COUNT'] = this.lightNumber[lightType]; + } + return shaderStr.replace(loopRegex, replace); + }, + + _parseUniforms: function () { + var uniforms = {}; + var self = this; + var shaderType = 'vertex'; + this._uniformList = []; + + this._vertexProcessedWithoutDefine = this._vertexProcessedWithoutDefine.replace(uniformRegex, _uniformParser); + shaderType = 'fragment'; + this._fragmentProcessedWithoutDefine = this._fragmentProcessedWithoutDefine.replace(uniformRegex, _uniformParser); + + self.matrixSemanticKeys = Object.keys(this.matrixSemantics); + + function _uniformParser(str, type, symbol, isArray, semanticWrapper, semantic) { + if (type && symbol) { + var uniformType = uniformTypeMap[type]; + var isConfigurable = true; + var defaultValueFunc; + if (uniformType) { + self._uniformList.push(symbol); + if (type === 'sampler2D' || type === 'samplerCube') { + // Texture is default disabled + self._textureStatus[symbol] = { + enabled: false, + shaderType: shaderType + }; + } + if (isArray) { + uniformType += 'v'; + } + if (semantic) { + // This case is only for SKIN_MATRIX + // TODO + if (attribSemantics.indexOf(semantic) >= 0) { + self.attribSemantics[semantic] = { + symbol: symbol, + type: uniformType + }; + isConfigurable = false; + } + else if (matrixSemantics.indexOf(semantic) >= 0) { + var isTranspose = false; + var semanticNoTranspose = semantic; + if (semantic.match(/TRANSPOSE$/)) { + isTranspose = true; + semanticNoTranspose = semantic.slice(0, -9); + } + self.matrixSemantics[semantic] = { + symbol: symbol, + type: uniformType, + isTranspose: isTranspose, + semanticNoTranspose: semanticNoTranspose + }; + isConfigurable = false; + } + else if (uniformSemantics.indexOf(semantic) >= 0) { + self.uniformSemantics[semantic] = { + symbol: symbol, + type: uniformType + }; + isConfigurable = false; + } + else { + // The uniform is not configurable, which means it will not appear + // in the material uniform properties + if (semantic === 'unconfigurable') { + isConfigurable = false; + } + else { + // Uniform have a defalut value, like + // uniform vec3 color: [1, 1, 1]; + defaultValueFunc = self._parseDefaultValue(type, semantic); + if (!defaultValueFunc) { + throw new Error('Unkown semantic "' + semantic + '"'); + } + else { + semantic = ''; + } + } + } + } + + if (isConfigurable) { + uniforms[symbol] = { + type: uniformType, + value: isArray ? uniformValueConstructor['array'] : (defaultValueFunc || uniformValueConstructor[type]), + semantic: semantic || null + }; + } + } + return ['uniform', type, symbol, isArray].join(' ') + ';\n'; + } + } + + this.uniformTemplates = uniforms; + }, + + _parseDefaultValue: function (type, str) { + var arrayRegex = /\[\s*(.*)\s*\]/; + if (type === 'vec2' || type === 'vec3' || type === 'vec4') { + var arrayStr = arrayRegex.exec(str)[1]; + if (arrayStr) { + var arr = arrayStr.split(/\s*,\s*/); + return function () { + return new vendor.Float32Array(arr); + }; + } + else { + // Invalid value + return; + } + } + else if (type === 'bool') { + return function () { + return str.toLowerCase() === 'true' ? true : false; + }; + } + else if (type === 'float') { + return function () { + return parseFloat(str); + }; + } + else if (type === 'int') { + return function () { + return parseInt(str); + }; + } + }, + + // Create a new uniform instance for material + createUniforms: function () { + var uniforms = {}; + + for (var symbol in this.uniformTemplates){ + var uniformTpl = this.uniformTemplates[symbol]; + uniforms[symbol] = { + type: uniformTpl.type, + value: uniformTpl.value() + }; + } + + return uniforms; + }, + + // Attached to material + attached: function () { + this._attacheMaterialNumber++; + }, + + // Detached to material + detached: function () { + this._attacheMaterialNumber--; + }, + + isAttachedToAny: function () { + return this._attacheMaterialNumber !== 0; + }, + + _parseAttributes: function () { + var attributes = {}; + var self = this; + this._vertexProcessedWithoutDefine = this._vertexProcessedWithoutDefine.replace( + attributeRegex, _attributeParser + ); + + function _attributeParser(str, type, symbol, semanticWrapper, semantic) { + if (type && symbol) { + var size = 1; + switch (type) { + case 'vec4': + size = 4; + break; + case 'vec3': + size = 3; + break; + case 'vec2': + size = 2; + break; + case 'float': + size = 1; + break; + } + + attributes[symbol] = { + // Can only be float + type: 'float', + size: size, + semantic: semantic || null + }; + + if (semantic) { + if (attribSemantics.indexOf(semantic) < 0) { + throw new Error('Unkown semantic "' + semantic + '"'); + } + else { + self.attribSemantics[semantic] = { + symbol: symbol, + type: type + }; + } + } + } + + return ['attribute', type, symbol].join(' ') + ';\n'; + } + + this.attributeTemplates = attributes; + }, + + _parseDefines: function () { + var self = this; + var shaderType = 'vertex'; + this._vertexProcessedWithoutDefine = this._vertexProcessedWithoutDefine.replace(defineRegex, _defineParser); + shaderType = 'fragment'; + this._fragmentProcessedWithoutDefine = this._fragmentProcessedWithoutDefine.replace(defineRegex, _defineParser); + + function _defineParser(str, symbol, value) { + var defines = shaderType === 'vertex' ? self.vertexDefines : self.fragmentDefines; + if (!defines[symbol]) { // Haven't been defined by user + if (value == 'false') { + defines[symbol] = false; + } + else if (value == 'true') { + defines[symbol] = true; + } + else { + defines[symbol] = value ? parseFloat(value) : null; + } + } + return ''; + } + }, + + // Return true or error msg if error happened + _buildProgram: function (_gl, vertexShaderString, fragmentShaderString) { + var cache = this._cache; + if (cache.get('program')) { + _gl.deleteProgram(cache.get('program')); + } + var program = _gl.createProgram(); + + var vertexShader = _gl.createShader(_gl.VERTEX_SHADER); + _gl.shaderSource(vertexShader, vertexShaderString); + _gl.compileShader(vertexShader); + + var fragmentShader = _gl.createShader(_gl.FRAGMENT_SHADER); + _gl.shaderSource(fragmentShader, fragmentShaderString); + _gl.compileShader(fragmentShader); + + var msg = checkShaderErrorMsg(_gl, vertexShader, vertexShaderString); + if (msg) { + return msg; + } + msg = checkShaderErrorMsg(_gl, fragmentShader, fragmentShaderString); + if (msg) { + return msg; + } + + _gl.attachShader(program, vertexShader); + _gl.attachShader(program, fragmentShader); + // Force the position bind to location 0; + if (this.attribSemantics['POSITION']) { + _gl.bindAttribLocation(program, 0, this.attribSemantics['POSITION'].symbol); + } + else { + // Else choose an attribute and bind to location 0; + var keys = Object.keys(this.attributeTemplates); + _gl.bindAttribLocation(program, 0, keys[0]); + } + + _gl.linkProgram(program); + + if (!_gl.getProgramParameter(program, _gl.LINK_STATUS)) { + return 'Could not link program\n' + 'VALIDATE_STATUS: ' + _gl.getProgramParameter(program, _gl.VALIDATE_STATUS) + ', gl error [' + _gl.getError() + ']'; + } + + // Cache uniform locations + for (var i = 0; i < this._uniformList.length; i++) { + var uniformSymbol = this._uniformList[i]; + var locationMap = cache.get('locations'); + locationMap[uniformSymbol] = _gl.getUniformLocation(program, uniformSymbol); + } + + _gl.deleteShader(vertexShader); + _gl.deleteShader(fragmentShader); + + cache.put('program', program); + }, + + /** + * Clone a new shader + * @return {qtek.Shader} + */ + clone: function () { + var shader = new Shader({ + vertex: this.vertex, + fragment: this.fragment, + vertexDefines: util.clone(this.vertexDefines), + fragmentDefines: util.clone(this.fragmentDefines) + }); + for (var name in this._textureStatus) { + shader._textureStatus[name] = util.clone(this._textureStatus[name]); + } + return shader; + }, + /** + * Dispose given context + * @param {WebGLRenderingContext} _gl + */ + dispose: function (_gl) { + var cache = this._cache; + + cache.use(_gl.__GLID__); + var program = cache.get('program'); + if (program) { + _gl.deleteProgram(program); + } + cache.deleteContext(_gl.__GLID__); + + this._locations = {}; + } + }); + + function getCacheSchema() { + return { + locations: {}, + attriblocations: {} + }; + } + + // Return true or error msg if error happened + function checkShaderErrorMsg(_gl, shader, shaderString) { + if (!_gl.getShaderParameter(shader, _gl.COMPILE_STATUS)) { + return [_gl.getShaderInfoLog(shader), addLineNumbers(shaderString)].join('\n'); + } + } + + // some util functions + function addLineNumbers(string) { + var chunks = string.split('\n'); + for (var i = 0, il = chunks.length; i < il; i ++) { + // Chrome reports shader errors on lines + // starting counting from 1 + chunks[i] = (i + 1) + ': ' + chunks[i]; + } + return chunks.join('\n'); + } + + var importRegex = /(@import)\s*([0-9a-zA-Z_\-\.]*)/g; + Shader.parseImport = function (shaderStr) { + shaderStr = shaderStr.replace(importRegex, function (str, importSymbol, importName) { + var str = Shader.source(importName); + if (str) { + // Recursively parse + return Shader.parseImport(str); + } + else { + console.error('Shader chunk "' + importName + '" not existed in library'); + return ''; + } + }); + return shaderStr; + }; + + var exportRegex = /(@export)\s*([0-9a-zA-Z_\-\.]*)\s*\n([\s\S]*?)@end/g; + + /** + * Import shader source + * @param {string} shaderStr + * @memberOf qtek.Shader + */ + Shader['import'] = function (shaderStr) { + shaderStr.replace(exportRegex, function (str, exportSymbol, exportName, code) { + var code = code.replace(/(^[\s\t\xa0\u3000]+)|([\u3000\xa0\s\t]+\x24)/g, ''); + if (code) { + var parts = exportName.split('.'); + var obj = Shader.codes; + var i = 0; + var key; + while (i < parts.length - 1) { + key = parts[i++]; + if (!obj[key]) { + obj[key] = {}; + } + obj = obj[key]; + } + key = parts[i]; + obj[key] = code; + } + return code; + }); + }; + + /** + * Library to store all the loaded shader codes + * @type {Object} + * @readOnly + * @memberOf qtek.Shader + */ + Shader.codes = {}; + + /** + * Get shader source + * @param {string} name + * @return {string} + * @memberOf qtek.Shader + */ + Shader.source = function (name) { + var parts = name.split('.'); + var obj = Shader.codes; + var i = 0; + while (obj && i < parts.length) { + var key = parts[i++]; + obj = obj[key]; + } + if (typeof obj !== 'string') { + // FIXME Use default instead + console.error('Shader "' + name + '" not existed in library'); + return ''; + } + return obj; + }; + + module.exports = Shader; + + +/***/ }, +/* 53 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Base = __webpack_require__(3); + var Texture = __webpack_require__(40); + + /** + * @constructor qtek.Material + * @extends qtek.core.Base + */ + var Material = Base.extend( + /** @lends qtek.Material# */ + { + /** + * @type {string} + */ + name: '', + + /** + * @type {Object} + */ + // uniforms: null, + + /** + * @type {qtek.Shader} + */ + // shader: null, + + /** + * @type {boolean} + */ + depthTest: true, + + /** + * @type {boolean} + */ + depthMask: true, + + /** + * @type {boolean} + */ + transparent: false, + /** + * Blend func is a callback function when the material + * have custom blending + * The gl context will be the only argument passed in tho the + * blend function + * Detail of blend function in WebGL: + * http://www.khronos.org/registry/gles/specs/2.0/es_full_spec_2.0.25.pdf + * + * Example : + * function(_gl) { + * _gl.blendEquation(_gl.FUNC_ADD); + * _gl.blendFunc(_gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA); + * } + */ + blend: null, + + // shadowTransparentMap : null + + _enabledUniforms: null, + }, function () { + if (!this.name) { + this.name = 'MATERIAL_' + this.__GUID__; + } + if (this.shader) { + this.attachShader(this.shader); + } + if (!this.uniforms) { + this.uniforms = {}; + } + }, + /** @lends qtek.Material.prototype */ + { + + /** + * @param {WebGLRenderingContext} _gl + * @param {qtek.Shader} [shader] + * @param {qtek.Material} [prevMaterial] + * @param {qtek.Shader} [prevShader] + * @return {Object} + */ + bind: function(_gl, shader, prevMaterial, prevShader) { + + // May use shader of other material if shader code are same + var shader = shader || this.shader; + + var sameShader = prevShader === shader; + + var currentTextureSlot = shader.currentTextureSlot(); + // Set uniforms + for (var u = 0; u < this._enabledUniforms.length; u++) { + var symbol = this._enabledUniforms[u]; + var uniform = this.uniforms[symbol]; + var uniformValue = uniform.value; + // When binding two materials with the same shader + // Many uniforms will be be set twice even if they have the same value + // So add a evaluation to see if the uniform is really needed to be set + if (prevMaterial && sameShader) { + if (prevMaterial.uniforms[symbol].value === uniformValue) { + continue; + } + } + + if (uniformValue === undefined) { + console.warn('Uniform value "' + symbol + '" is undefined'); + continue; + } + else if (uniformValue === null) { + // FIXME Assume material with same shader have same order uniforms + // Or if different material use same textures, + // the slot will be different and still skipped because optimization + if (uniform.type === 't') { + var slot = shader.currentTextureSlot(); + var res = shader.setUniform(_gl, '1i', symbol, slot); + if (res) { // Texture is enabled + // Still occupy the slot to make sure same texture in different materials have same slot. + shader.useCurrentTextureSlot(_gl, null); + } + } + continue; + } + else if (uniformValue instanceof Array + && !uniformValue.length) { + continue; + } + else if (uniformValue instanceof Texture) { + var slot = shader.currentTextureSlot(); + var res = shader.setUniform(_gl, '1i', symbol, slot); + if (!res) { // Texture is not enabled + continue; + } + shader.useCurrentTextureSlot(_gl, uniformValue); + } + else if (uniformValue instanceof Array) { + if (uniformValue.length === 0) { + continue; + } + // Texture Array + var exampleValue = uniformValue[0]; + + if (exampleValue instanceof Texture) { + if (!shader.hasUniform(symbol)) { + continue; + } + + var arr = []; + for (var i = 0; i < uniformValue.length; i++) { + var texture = uniformValue[i]; + + var slot = shader.currentTextureSlot(); + arr.push(slot); + + shader.useCurrentTextureSlot(_gl, texture); + } + + shader.setUniform(_gl, '1iv', symbol, arr); + } + else { + shader.setUniform(_gl, uniform.type, symbol, uniformValue); + } + } + else{ + shader.setUniform(_gl, uniform.type, symbol, uniformValue); + } + } + // Texture slot maybe used out of material. + shader.resetTextureSlot(currentTextureSlot); + }, + + /** + * @param {string} symbol + * @param {number|array|qtek.Texture|ArrayBufferView} value + */ + setUniform: function (symbol, value) { + if (value === undefined) { + console.warn('Uniform value "' + symbol + '" is undefined'); + } + var uniform = this.uniforms[symbol]; + if (uniform) { + uniform.value = value; + } + }, + + /** + * @param {Object} obj + */ + setUniforms: function(obj) { + for (var key in obj) { + var val = obj[key]; + this.setUniform(key, val); + } + }, + + /** + * Enable a uniform + * It only have effect on the uniform exists in shader. + * @param {string} symbol + */ + // enableUniform: function (symbol) { + // if (this.uniforms[symbol] && !this.isUniformEnabled(symbol)) { + // this._enabledUniforms.push(symbol); + // } + // }, + + // /** + // * Disable a uniform + // * It will not affect the uniform state in the shader. Because the shader uniforms is parsed from shader code with naive regex. When using micro to disable some uniforms in the shader. It will still try to set these uniforms in each rendering pass. We can disable these uniforms manually if we need this bit performance improvement. Mostly we can simply ignore it. + // * @param {string} symbol + // */ + // disableUniform: function (symbol) { + // var idx = this._enabledUniforms.indexOf(symbol); + // if (idx >= 0) { + // this._enabledUniforms.splice(idx, 1); + // } + // }, + + /** + * @param {string} symbol + * @return {boolean} + */ + isUniformEnabled: function (symbol) { + return this._enabledUniforms.indexOf(symbol) >= 0; + }, + + /** + * Alias of setUniform and setUniforms + * @param {object|string} symbol + * @param {number|array|qtek.Texture|ArrayBufferView} [value] + */ + set: function (symbol, value) { + if (typeof(symbol) === 'object') { + for (var key in symbol) { + var val = symbol[key]; + this.set(key, val); + } + } + else { + var uniform = this.uniforms[symbol]; + if (uniform) { + uniform.value = value; + } + } + }, + /** + * Get uniform value + * @param {string} symbol + * @return {number|array|qtek.Texture|ArrayBufferView} + */ + get: function (symbol) { + var uniform = this.uniforms[symbol]; + if (uniform) { + return uniform.value; + } + }, + /** + * Attach a shader instance + * @param {qtek.Shader} shader + * @param {boolean} keepUniform If try to keep uniform value + */ + attachShader: function(shader, keepUniform) { + if (this.shader) { + this.shader.detached(); + } + + var originalUniforms = this.uniforms; + + // Ignore if uniform can use in shader. + this.uniforms = shader.createUniforms(); + this.shader = shader; + + var uniforms = this.uniforms; + this._enabledUniforms = Object.keys(uniforms); + // Make sure uniforms are set in same order to avoid texture slot wrong + this._enabledUniforms.sort(); + + if (keepUniform) { + for (var symbol in originalUniforms) { + if (uniforms[symbol]) { + uniforms[symbol].value = originalUniforms[symbol].value; + } + } + } + + shader.attached(); + }, + + /** + * Detach a shader instance + */ + detachShader: function() { + this.shader.detached(); + this.shader = null; + this.uniforms = {}; + }, + + /** + * Clone a new material and keep uniforms, shader will not be cloned + * @return {qtek.Material} + */ + clone: function () { + var material = new this.constructor({ + name: this.name, + shader: this.shader + }); + for (var symbol in this.uniforms) { + material.uniforms[symbol].value = this.uniforms[symbol].value; + } + material.depthTest = this.depthTest; + material.depthMask = this.depthMask; + material.transparent = this.transparent; + material.blend = this.blend; + + return material; + }, + + /** + * Dispose material, if material shader is not attached to any other materials + * Shader will also be disposed + * @param {WebGLRenderingContext} gl + * @param {boolean} [disposeTexture=false] If dispose the textures used in the material + */ + dispose: function(_gl, disposeTexture) { + if (disposeTexture) { + for (var name in this.uniforms) { + var val = this.uniforms[name].value; + if (!val) { + continue; + } + if (val instanceof Texture) { + val.dispose(_gl); + } + else if (val instanceof Array) { + for (var i = 0; i < val.length; i++) { + if (val[i] instanceof Texture) { + val[i].dispose(_gl); + } + } + } + } + } + var shader = this.shader; + if (shader) { + this.detachShader(); + if (!shader.isAttachedToAny()) { + shader.dispose(_gl); + } + } + } + }); + + module.exports = Material; + + +/***/ }, +/* 54 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Renderable = __webpack_require__(55); + var glenum = __webpack_require__(34); + + /** + * @constructor qtek.Mesh + * @extends qtek.Renderable + */ + var Mesh = Renderable.extend( + /** @lends qtek.Mesh# */ + { + /** + * Used when it is a skinned mesh + * @type {qtek.Skeleton} + */ + skeleton: null, + /** + * Joints indices Meshes can share the one skeleton instance and each mesh can use one part of joints. Joints indices indicate the index of joint in the skeleton instance + * @type {number[]} + */ + joints: null + + }, function () { + if (!this.joints) { + this.joints = []; + } + }, { + render: function(_gl, shader) { + shader = shader || this.material.shader; + // Set pose matrices of skinned mesh + if (this.skeleton) { + var skinMatricesArray = this.skeleton.getSubSkinMatrices(this.__GUID__, this.joints); + shader.setUniformOfSemantic(_gl, 'SKIN_MATRIX', skinMatricesArray); + } + + return Renderable.prototype.render.call(this, _gl, shader); + } + }); + + // Enums + Mesh.POINTS = glenum.POINTS; + Mesh.LINES = glenum.LINES; + Mesh.LINE_LOOP = glenum.LINE_LOOP; + Mesh.LINE_STRIP = glenum.LINE_STRIP; + Mesh.TRIANGLES = glenum.TRIANGLES; + Mesh.TRIANGLE_STRIP = glenum.TRIANGLE_STRIP; + Mesh.TRIANGLE_FAN = glenum.TRIANGLE_FAN; + + Mesh.BACK = glenum.BACK; + Mesh.FRONT = glenum.FRONT; + Mesh.FRONT_AND_BACK = glenum.FRONT_AND_BACK; + Mesh.CW = glenum.CW; + Mesh.CCW = glenum.CCW; + + module.exports = Mesh; + + +/***/ }, +/* 55 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Node = __webpack_require__(22); + var glenum = __webpack_require__(34); + var glinfo = __webpack_require__(42); + + // Cache + var prevDrawID = 0; + var prevDrawIndicesBuffer = null; + var prevDrawIsUseIndices = true; + + var currentDrawID; + + var RenderInfo = function() { + this.triangleCount = 0; + this.vertexCount = 0; + this.drawCallCount = 0; + }; + + function VertexArrayObject( + availableAttributes, + availableAttributeSymbols, + indicesBuffer + ) { + this.availableAttributes = availableAttributes; + this.availableAttributeSymbols = availableAttributeSymbols; + this.indicesBuffer = indicesBuffer; + + this.vao = null; + } + /** + * @constructor qtek.Renderable + * @extends qtek.Node + */ + var Renderable = Node.extend( + /** @lends qtek.Renderable# */ + { + /** + * @type {qtek.Material} + */ + material: null, + + /** + * @type {qtek.Geometry} + */ + geometry: null, + + /** + * @type {number} + */ + mode: glenum.TRIANGLES, + + _drawCache: null, + + _renderInfo: null + }, function() { + this._drawCache = {}; + this._renderInfo = new RenderInfo(); + }, + /** @lends qtek.Renderable.prototype */ + { + + /** + * Render order, Nodes with smaller value renders before nodes with larger values. + * @type {Number} + */ + renderOrder: 0, + /** + * Used when mode is LINES, LINE_STRIP or LINE_LOOP + * @type {number} + */ + lineWidth: 1, + + /** + * @type {boolean} + */ + culling: true, + /** + * @type {number} + */ + cullFace: glenum.BACK, + /** + * @type {number} + */ + frontFace: glenum.CCW, + + /** + * Software frustum culling + * @type {boolean} + */ + frustumCulling: true, + /** + * @type {boolean} + */ + receiveShadow: true, + /** + * @type {boolean} + */ + castShadow: true, + /** + * @type {boolean} + */ + ignorePicking: false, + + /** + * @return {boolean} + */ + isRenderable: function() { + return this.geometry && this.material && !this.invisible + && this.geometry.vertexCount > 0; + }, + + /** + * Before render hook + * @type {Function} + * @memberOf qtek.Renderable + */ + beforeRender: function (_gl) {}, + + /** + * Before render hook + * @type {Function} + * @memberOf qtek.Renderable + */ + afterRender: function (_gl, renderStat) {}, + + getBoundingBox: function (filter, out) { + out = Node.prototype.getBoundingBox.call(this, filter, out); + if (this.geometry && this.geometry.boundingBox) { + out.union(this.geometry.boundingBox); + } + + return out; + }, + + /** + * @param {WebGLRenderingContext} _gl + * @param {qtek.Shader} [shader] May use shader of other material if shader code are same + * @return {Object} + */ + render: function (_gl, shader) { + // May use shader of other material if shader code are same + var shader = shader || this.material.shader; + var geometry = this.geometry; + + var glDrawMode = this.mode; + + var nVertex = geometry.vertexCount; + var isUseIndices = geometry.isUseIndices(); + + var uintExt = glinfo.getExtension(_gl, 'OES_element_index_uint'); + var useUintExt = uintExt && nVertex > 0xffff; + var indicesType = useUintExt ? _gl.UNSIGNED_INT : _gl.UNSIGNED_SHORT; + + var vaoExt = glinfo.getExtension(_gl, 'OES_vertex_array_object'); + + var isStatic = !geometry.dynamic; + + var renderInfo = this._renderInfo; + renderInfo.vertexCount = nVertex; + renderInfo.triangleCount = 0; + renderInfo.drawCallCount = 0; + // Draw each chunk + var drawHashChanged = false; + // Hash with shader id in case previous material has less attributes than next material + currentDrawID = _gl.__GLID__ + '-' + geometry.__GUID__ + '-' + shader.__GUID__; + + if (currentDrawID !== prevDrawID) { + drawHashChanged = true; + } + else { + // The cache will be invalid in the following cases + // 1. Geometry is splitted to multiple chunks + // 2. VAO is enabled and is binded to null after render + // 3. Geometry needs update + if ( + ((nVertex > 0xffff && !uintExt) && isUseIndices) + || (vaoExt && isStatic) + || geometry._cache.isDirty() + ) { + drawHashChanged = true; + } + } + prevDrawID = currentDrawID; + + if (!drawHashChanged) { + // Direct draw + if (prevDrawIsUseIndices) { + _gl.drawElements(glDrawMode, prevDrawIndicesBuffer.count, indicesType, 0); + renderInfo.triangleCount = prevDrawIndicesBuffer.count / 3; + } + else { + // FIXME Use vertex number in buffer + // vertexCount may get the wrong value when geometry forget to mark dirty after update + _gl.drawArrays(glDrawMode, 0, nVertex); + } + renderInfo.drawCallCount = 1; + } + else { + // Use the cache of static geometry + var vaoList = this._drawCache[currentDrawID]; + if (!vaoList) { + var chunks = geometry.getBufferChunks(_gl); + if (!chunks) { // Empty mesh + return; + } + vaoList = []; + for (var c = 0; c < chunks.length; c++) { + var chunk = chunks[c]; + var attributeBuffers = chunk.attributeBuffers; + var indicesBuffer = chunk.indicesBuffer; + + var availableAttributes = []; + var availableAttributeSymbols = []; + for (var a = 0; a < attributeBuffers.length; a++) { + var attributeBufferInfo = attributeBuffers[a]; + var name = attributeBufferInfo.name; + var semantic = attributeBufferInfo.semantic; + var symbol; + if (semantic) { + var semanticInfo = shader.attribSemantics[semantic]; + symbol = semanticInfo && semanticInfo.symbol; + } + else { + symbol = name; + } + if (symbol && shader.attributeTemplates[symbol]) { + availableAttributes.push(attributeBufferInfo); + availableAttributeSymbols.push(symbol); + } + } + + var vao = new VertexArrayObject( + availableAttributes, + availableAttributeSymbols, + indicesBuffer + ); + vaoList.push(vao); + } + if (isStatic) { + this._drawCache[currentDrawID] = vaoList; + } + } + + for (var i = 0; i < vaoList.length; i++) { + var vao = vaoList[i]; + var needsBindAttributes = true; + + // Create vertex object array cost a lot + // So we don't use it on the dynamic object + if (vaoExt && isStatic) { + // Use vertex array object + // http://blog.tojicode.com/2012/10/oesvertexarrayobject-extension.html + if (vao.vao == null) { + vao.vao = vaoExt.createVertexArrayOES(); + } else { + needsBindAttributes = false; + } + vaoExt.bindVertexArrayOES(vao.vao); + } + + var availableAttributes = vao.availableAttributes; + var indicesBuffer = vao.indicesBuffer; + + if (needsBindAttributes) { + var locationList = shader.enableAttributes(_gl, vao.availableAttributeSymbols, (vaoExt && isStatic && vao.vao)); + // Setting attributes; + for (var a = 0; a < availableAttributes.length; a++) { + var location = locationList[a]; + if (location === -1) { + continue; + } + var attributeBufferInfo = availableAttributes[a]; + var buffer = attributeBufferInfo.buffer; + var size = attributeBufferInfo.size; + var glType; + switch (attributeBufferInfo.type) { + case 'float': + glType = _gl.FLOAT; + break; + case 'byte': + glType = _gl.BYTE; + break; + case 'ubyte': + glType = _gl.UNSIGNED_BYTE; + break; + case 'short': + glType = _gl.SHORT; + break; + case 'ushort': + glType = _gl.UNSIGNED_SHORT; + break; + default: + glType = _gl.FLOAT; + break; + } + + _gl.bindBuffer(_gl.ARRAY_BUFFER, buffer); + _gl.vertexAttribPointer(location, size, glType, false, 0, 0); + } + } + if ( + glDrawMode == glenum.LINES || + glDrawMode == glenum.LINE_STRIP || + glDrawMode == glenum.LINE_LOOP + ) { + _gl.lineWidth(this.lineWidth); + } + + prevDrawIndicesBuffer = indicesBuffer; + prevDrawIsUseIndices = geometry.isUseIndices(); + // Do drawing + if (prevDrawIsUseIndices) { + if (needsBindAttributes) { + _gl.bindBuffer(_gl.ELEMENT_ARRAY_BUFFER, indicesBuffer.buffer); + } + _gl.drawElements(glDrawMode, indicesBuffer.count, indicesType, 0); + renderInfo.triangleCount += indicesBuffer.count / 3; + } else { + _gl.drawArrays(glDrawMode, 0, nVertex); + } + + if (vaoExt && isStatic) { + vaoExt.bindVertexArrayOES(null); + } + + renderInfo.drawCallCount++; + } + } + + return renderInfo; + }, + + /** + * Clone a new renderable + * @method + * @return {qtek.Renderable} + */ + clone: (function() { + var properties = [ + 'castShadow', 'receiveShadow', + 'mode', 'culling', 'cullFace', 'frontFace', + 'frustumCulling' + ]; + return function() { + var renderable = Node.prototype.clone.call(this); + + renderable.geometry = this.geometry; + renderable.material = this.material; + + for (var i = 0; i < properties.length; i++) { + var name = properties[i]; + // Try not to overwrite the prototype property + if (renderable[name] !== this[name]) { + renderable[name] = this[name]; + } + } + + return renderable; + }; + })() + }); + + Renderable.beforeFrame = function() { + prevDrawID = 0; + }; + + // Enums + Renderable.POINTS = glenum.POINTS; + Renderable.LINES = glenum.LINES; + Renderable.LINE_LOOP = glenum.LINE_LOOP; + Renderable.LINE_STRIP = glenum.LINE_STRIP; + Renderable.TRIANGLES = glenum.TRIANGLES; + Renderable.TRIANGLE_STRIP = glenum.TRIANGLE_STRIP; + Renderable.TRIANGLE_FAN = glenum.TRIANGLE_FAN; + + Renderable.BACK = glenum.BACK; + Renderable.FRONT = glenum.FRONT; + Renderable.FRONT_AND_BACK = glenum.FRONT_AND_BACK; + Renderable.CW = glenum.CW; + Renderable.CCW = glenum.CCW; + + Renderable.RenderInfo = RenderInfo; + + module.exports = Renderable; + + +/***/ }, +/* 56 */ +/***/ function(module, exports) { + + + module.exports = "\n@export qtek.compositor.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n\nvarying vec2 v_Texcoord;\n\nvoid main()\n{\n v_Texcoord = texcoord;\n gl_Position = worldViewProjection * vec4(position, 1.0);\n}\n\n@end"; + + +/***/ }, +/* 57 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Node = __webpack_require__(37); + var glinfo = __webpack_require__(42); + var glenum = __webpack_require__(34); + var FrameBuffer = __webpack_require__(44); + + /** + * @constructor qtek.compositor.SceneNode + * @extends qtek.compositor.Node + */ + var SceneNode = Node.extend( + /** @lends qtek.compositor.SceneNode# */ + { + name: 'scene', + /** + * @type {qtek.Scene} + */ + scene: null, + /** + * @type {qtek.Camera} + */ + camera: null, + /** + * @type {boolean} + */ + autoUpdateScene: true, + /** + * @type {boolean} + */ + preZ: false + + }, function() { + this.frameBuffer = new FrameBuffer(); + }, { + render: function(renderer) { + + this._rendering = true; + var _gl = renderer.gl; + + this.trigger('beforerender'); + + var renderInfo; + + if (!this.outputs) { + + renderInfo = renderer.render(this.scene, this.camera, !this.autoUpdateScene, this.preZ); + + } + else { + + var frameBuffer = this.frameBuffer; + for (var name in this.outputs) { + var parameters = this.updateParameter(name, renderer); + var outputInfo = this.outputs[name]; + var texture = this._compositor.allocateTexture(parameters); + this._outputTextures[name] = texture; + + var attachment = outputInfo.attachment || _gl.COLOR_ATTACHMENT0; + if (typeof(attachment) == 'string') { + attachment = _gl[attachment]; + } + frameBuffer.attach(texture, attachment); + } + frameBuffer.bind(renderer); + + // MRT Support in chrome + // https://www.khronos.org/registry/webgl/sdk/tests/conformance/extensions/ext-draw-buffers.html + var ext = glinfo.getExtension(_gl, 'EXT_draw_buffers'); + if (ext) { + var bufs = []; + for (var attachment in this.outputs) { + attachment = parseInt(attachment); + if (attachment >= _gl.COLOR_ATTACHMENT0 && attachment <= _gl.COLOR_ATTACHMENT0 + 8) { + bufs.push(attachment); + } + } + ext.drawBuffersEXT(bufs); + } + + // Always clear + // PENDING + renderer.saveClear(); + renderer.clearBit = glenum.DEPTH_BUFFER_BIT | glenum.COLOR_BUFFER_BIT; + renderInfo = renderer.render(this.scene, this.camera, !this.autoUpdateScene, this.preZ); + renderer.restoreClear(); + + frameBuffer.unbind(renderer); + } + + this.trigger('afterrender', renderInfo); + + this._rendering = false; + this._rendered = true; + } + }); + + module.exports = SceneNode; + + +/***/ }, +/* 58 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Node = __webpack_require__(37); + + /** + * @constructor qtek.compositor.TextureNode + * @extends qtek.compositor.Node + */ + var TextureNode = Node.extend(function() { + return /** @lends qtek.compositor.TextureNode# */ { + /** + * @type {qtek.Texture2D} + */ + texture: null, + + // Texture node must have output without parameters + outputs: { + color: {} + } + }; + }, function () { + }, { + + getOutput: function (renderer, name) { + return this.texture; + }, + + // Do nothing + beforeFrame: function () {}, + afterFrame: function () {} + }); + + module.exports = TextureNode; + + +/***/ }, +/* 59 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Base = __webpack_require__(3); + + var QEvent = Base.extend({ + cancelBubble : false + }, { + stopPropagation : function() { + this.cancelBubble = true; + } + }); + + QEvent['throw'] = function(eventType, target, props) { + + var e = new QEvent(props); + + e.type = eventType; + e.target = target; + + // enable bubbling + while (target && !e.cancelBubble) { + e.currentTarget = target; + target.trigger(eventType, e); + + target = target.getParent(); + } + }; + + module.exports = QEvent; + + +/***/ }, +/* 60 */ +/***/ function(module, exports) { + + 'use strict'; + + + /** + * Simple double linked list. Compared with array, it has O(1) remove operation. + * @constructor + * @alias qtek.core.LinkedList + */ + var LinkedList = function () { + + /** + * @type {qtek.core.LinkedList.Entry} + */ + this.head = null; + + /** + * @type {qtek.core.LinkedList.Entry} + */ + this.tail = null; + + this._length = 0; + }; + + /** + * Insert a new value at the tail + * @param {} val + * @return {qtek.core.LinkedList.Entry} + */ + LinkedList.prototype.insert = function (val) { + var entry = new LinkedList.Entry(val); + this.insertEntry(entry); + return entry; + }; + + /** + * Insert a new value at idx + * @param {number} idx + * @param {} val + * @return {qtek.core.LinkedList.Entry} + */ + LinkedList.prototype.insertAt = function (idx, val) { + if (idx < 0) { + return; + } + var next = this.head; + var cursor = 0; + while (next && cursor != idx) { + next = next.next; + cursor++; + } + if (next) { + var entry = new LinkedList.Entry(val); + var prev = next.prev; + if (!prev) { //next is head + this.head = entry; + } + else { + prev.next = entry; + entry.prev = prev; + } + entry.next = next; + next.prev = entry; + } + else { + this.insert(val); + } + }; + + LinkedList.prototype.insertBeforeEntry = function (val, next) { + var entry = new LinkedList.Entry(val); + var prev = next.prev; + if (!prev) { //next is head + this.head = entry; + } + else { + prev.next = entry; + entry.prev = prev; + } + entry.next = next; + next.prev = entry; + + this._length++; + }; + + /** + * Insert an entry at the tail + * @param {qtek.core.LinkedList.Entry} entry + */ + LinkedList.prototype.insertEntry = function (entry) { + if (!this.head) { + this.head = this.tail = entry; + } + else { + this.tail.next = entry; + entry.prev = this.tail; + this.tail = entry; + } + this._length++; + }; + + /** + * Remove entry. + * @param {qtek.core.LinkedList.Entry} entry + */ + LinkedList.prototype.remove = function (entry) { + var prev = entry.prev; + var next = entry.next; + if (prev) { + prev.next = next; + } + else { + // Is head + this.head = next; + } + if (next) { + next.prev = prev; + } + else { + // Is tail + this.tail = prev; + } + entry.next = entry.prev = null; + this._length--; + }; + + /** + * Remove entry at index. + * @param {number} idx + * @return {} + */ + LinkedList.prototype.removeAt = function (idx) { + if (idx < 0) { + return; + } + var curr = this.head; + var cursor = 0; + while (curr && cursor != idx) { + curr = curr.next; + cursor++; + } + if (curr) { + this.remove(curr); + return curr.value; + } + }; + /** + * Get head value + * @return {} + */ + LinkedList.prototype.getHead = function () { + if (this.head) { + return this.head.value; + } + }; + /** + * Get tail value + * @return {} + */ + LinkedList.prototype.getTail = function () { + if (this.tail) { + return this.tail.value; + } + }; + /** + * Get value at idx + * @param {number} idx + * @return {} + */ + LinkedList.prototype.getAt = function (idx) { + if (idx < 0) { + return; + } + var curr = this.head; + var cursor = 0; + while (curr && cursor != idx) { + curr = curr.next; + cursor++; + } + return curr.value; + }; + + /** + * @param {} value + * @return {number} + */ + LinkedList.prototype.indexOf = function (value) { + var curr = this.head; + var cursor = 0; + while (curr) { + if (curr.value === value) { + return cursor; + } + curr = curr.next; + cursor++; + } + }; + + /** + * @return {number} + */ + LinkedList.prototype.length = function () { + return this._length; + }; + + /** + * If list is empty + */ + LinkedList.prototype.isEmpty = function () { + return this._length === 0; + }; + + /** + * @param {Function} cb + * @param {} context + */ + LinkedList.prototype.forEach = function (cb, context) { + var curr = this.head; + var idx = 0; + var haveContext = typeof(context) != 'undefined'; + while (curr) { + if (haveContext) { + cb.call(context, curr.value, idx); + } + else { + cb(curr.value, idx); + } + curr = curr.next; + idx++; + } + }; + + /** + * Clear the list + */ + LinkedList.prototype.clear = function () { + this.tail = this.head = null; + this._length = 0; + }; + + /** + * @constructor + * @param {} val + */ + LinkedList.Entry = function (val) { + /** + * @type {} + */ + this.value = val; + + /** + * @type {qtek.core.LinkedList.Entry} + */ + this.next = null; + + /** + * @type {qtek.core.LinkedList.Entry} + */ + this.prev = null; + }; + + module.exports = LinkedList; + + +/***/ }, +/* 61 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var LinkedList = __webpack_require__(60); + + /** + * LRU Cache + * @constructor + * @alias qtek.core.LRU + */ + var LRU = function(maxSize) { + + this._list = new LinkedList(); + + this._map = {}; + + this._maxSize = maxSize || 10; + }; + + /** + * Set cache max size + * @param {number} size + */ + LRU.prototype.setMaxSize = function(size) { + this._maxSize = size; + }; + + /** + * @param {string} key + * @param {} value + */ + LRU.prototype.put = function(key, value) { + if (typeof(this._map[key]) == 'undefined') { + var len = this._list.length(); + if (len >= this._maxSize && len > 0) { + // Remove the least recently used + var leastUsedEntry = this._list.head; + this._list.remove(leastUsedEntry); + delete this._map[leastUsedEntry.key]; + } + + var entry = this._list.insert(value); + entry.key = key; + this._map[key] = entry; + } + }; + + /** + * @param {string} key + * @return {} + */ + LRU.prototype.get = function(key) { + var entry = this._map[key]; + if (typeof(entry) != 'undefined') { + // Put the latest used entry in the tail + if (entry !== this._list.tail) { + this._list.remove(entry); + this._list.insertEntry(entry); + } + + return entry.value; + } + }; + + /** + * @param {string} key + */ + LRU.prototype.remove = function(key) { + var entry = this._map[key]; + if (typeof(entry) != 'undefined') { + delete this._map[key]; + this._list.remove(entry); + } + }; + + /** + * Clear the cache + */ + LRU.prototype.clear = function() { + this._list.clear(); + this._map = {}; + }; + + module.exports = LRU; + + +/***/ }, +/* 62 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Base = __webpack_require__(3); + var Texture2D = __webpack_require__(39); + var Texture = __webpack_require__(40); + var Material = __webpack_require__(53); + var FrameBuffer = __webpack_require__(44); + var Shader = __webpack_require__(52); + var ForwardRenderer = __webpack_require__(63); + var Pass = __webpack_require__(47); + var Matrix4 = __webpack_require__(25); + var glinfo = __webpack_require__(42); + + function createFillCanvas(color) { + var canvas = document.createElement('canvas'); + canvas.width = canvas.height = 1; + var ctx = canvas.getContext('2d'); + ctx.fillStyle = color || '#000'; + ctx.fillRect(0, 0, 1, 1); + + return canvas; + } + + function attachTextureToSlot(gl, shader, symbol, texture, slot) { + shader.setUniform(gl, '1i', symbol, slot); + + gl.activeTexture(gl.TEXTURE0 + slot); + // Maybe texture is not loaded yet; + if (texture.isRenderable()) { + texture.bind(gl); + } + else { + // Bind texture to null + texture.unbind(gl); + } + } + + function getBeforeRenderHook1 (gl, defaultNormalMap, defaultRoughnessMap) { + + var previousNormalMap; + var previousRougnessMap; + var previousRenderable; + + return function (renderable, prevMaterial, prevShader) { + // Material not change + if (previousRenderable && previousRenderable.__standardMat === renderable.__standardMat) { + return; + } + + var standardMaterial = renderable.__standardMat; + var gBufferMat = renderable.material; + + var roughness = standardMaterial.get('roughness'); + + var normalMap = standardMaterial.get('normalMap') || defaultNormalMap; + var roughnessMap = standardMaterial.get('roughnessMap'); + var uvRepeat = standardMaterial.get('uvRepeat'); + var uvOffset = standardMaterial.get('uvOffset'); + var useRoughnessMap = !!roughnessMap; + + roughnessMap = roughnessMap || defaultRoughnessMap; + + if (prevMaterial !== gBufferMat) { + gBufferMat.set('glossiness', 1.0 - roughness); + gBufferMat.set('normalMap', normalMap); + gBufferMat.set('roughnessMap', roughnessMap); + gBufferMat.set('useRoughnessMap', +useRoughnessMap); + gBufferMat.set('uvRepeat', uvRepeat); + gBufferMat.set('uvOffset', uvOffset); + } + else { + gBufferMat.shader.setUniform( + gl, '1f', 'glossiness', 1.0 - roughness + ); + + if (previousNormalMap !== normalMap) { + attachTextureToSlot(gl, gBufferMat.shader, 'normalMap', normalMap, 0); + } + if (previousRougnessMap !== roughnessMap) { + attachTextureToSlot(gl, gBufferMat.shader, 'roughnessMap', roughnessMap, 1); + } + gBufferMat.shader.setUniform(gl, '1i', 'useRoughnessMap', +useRoughnessMap); + if (uvRepeat != null) { + gBufferMat.shader.setUniform(gl, '2f', 'uvRepeat', uvRepeat); + } + if (uvOffset != null) { + gBufferMat.shader.setUniform(gl, '2f', 'uvOffset', uvOffset); + } + } + + previousNormalMap = normalMap; + previousRougnessMap = roughnessMap; + + previousRenderable = renderable; + }; + } + + function getBeforeRenderHook2(gl, defaultDiffuseMap, defaultMetalnessMap) { + var previousDiffuseMap; + var previousRenderable; + var previousMetalnessMap; + + return function (renderable, prevMaterial, prevShader) { + // Material not change + if (previousRenderable && previousRenderable.__standardMat === renderable.__standardMat) { + return; + } + + var standardMaterial = renderable.__standardMat; + var gBufferMat = renderable.material; + + var color = standardMaterial.get('color'); + var metalness = standardMaterial.get('metalness'); + + var diffuseMap = standardMaterial.get('diffuseMap'); + var metalnessMap = standardMaterial.get('metalnessMap'); + + var uvRepeat = standardMaterial.get('uvRepeat'); + var uvOffset = standardMaterial.get('uvOffset'); + + var useMetalnessMap = !!metalnessMap; + + diffuseMap = diffuseMap || defaultDiffuseMap; + metalnessMap = metalnessMap || defaultMetalnessMap; + + if (prevMaterial !== gBufferMat) { + gBufferMat.set('color', color); + gBufferMat.set('metalness', metalness); + gBufferMat.set('diffuseMap', diffuseMap); + gBufferMat.set('metalnessMap', metalnessMap); + gBufferMat.set('useMetalnessMap', +useMetalnessMap); + gBufferMat.set('uvRepeat', uvRepeat); + gBufferMat.set('uvOffset', uvOffset); + + gBufferMat.set('linear', +standardMaterial.linear); + } + else { + gBufferMat.shader.setUniform( + gl, '1f', 'metalness', metalness + ); + + gBufferMat.shader.setUniform(gl, '3f', 'color', color); + if (previousDiffuseMap !== diffuseMap) { + attachTextureToSlot(gl, gBufferMat.shader, 'diffuseMap', diffuseMap, 0); + } + if (previousMetalnessMap !== metalnessMap) { + attachTextureToSlot(gl, gBufferMat.shader, 'metalnessMap', metalnessMap, 1); + } + gBufferMat.shader.setUniform(gl, '1i', 'useMetalnessMap', +useMetalnessMap); + gBufferMat.shader.setUniform(gl, '2f', 'uvRepeat', uvRepeat); + gBufferMat.shader.setUniform(gl, '2f', 'uvOffset', uvOffset); + + gBufferMat.shader.setUniform(gl, '1i', 'linear', +standardMaterial.linear); + } + + previousDiffuseMap = diffuseMap; + previousMetalnessMap = metalnessMap; + + previousRenderable = renderable; + }; + } + + Shader.import(__webpack_require__(67)); + Shader.import(__webpack_require__(68)); + + var GBuffer = Base.extend(function () { + + return { + + enableTargetTexture1: true, + + enableTargetTexture2: true, + + enableTargetTexture3: true, + + // - R: normal.x + // - G: normal.y + // - B: normal.z + // - A: glossiness + _gBufferTex1: new Texture2D({ + minFilter: Texture.NEAREST, + magFilter: Texture.NEAREST, + // PENDING + type: Texture.HALF_FLOAT + }), + + // - R: depth + _gBufferTex2: new Texture2D({ + minFilter: Texture.NEAREST, + magFilter: Texture.NEAREST, + // format: Texture.DEPTH_COMPONENT, + // type: Texture.UNSIGNED_INT + + format: Texture.DEPTH_STENCIL, + type: Texture.UNSIGNED_INT_24_8_WEBGL + }), + + // - R: albedo.r + // - G: albedo.g + // - B: albedo.b + // - A: metalness + _gBufferTex3: new Texture2D({ + minFilter: Texture.NEAREST, + magFilter: Texture.NEAREST + }), + + _defaultNormalMap: new Texture2D({ + image: createFillCanvas('#000') + }), + _defaultRoughnessMap: new Texture2D({ + image: createFillCanvas('#fff') + }), + _defaultMetalnessMap: new Texture2D({ + image: createFillCanvas('#fff') + }), + _defaultDiffuseMap: new Texture2D({ + image: createFillCanvas('#fff') + }), + + _frameBuffer: new FrameBuffer(), + + _gBufferMaterials: {}, + + _debugPass: new Pass({ + fragment: Shader.source('qtek.deferred.gbuffer.debug') + }) + }; + }, { + + resize: function (width, height) { + if (this._gBufferTex1.width === width + && this._gBufferTex1.height === height + ) { + return; + } + this._gBufferTex1.width = width; + this._gBufferTex1.height = height; + this._gBufferTex1.dirty(); + + this._gBufferTex2.width = width; + this._gBufferTex2.height = height; + this._gBufferTex2.dirty(); + + this._gBufferTex3.width = width; + this._gBufferTex3.height = height; + this._gBufferTex3.dirty(); + }, + + // TODO is dpr needed? + setViewport: function (x, y, width, height, dpr) { + var viewport; + if (typeof x === 'object') { + viewport = x; + } + else { + viewport = { + x: x, y: y, + width: width, height: height, + devicePixelRatio: dpr || 1 + }; + } + this._frameBuffer.viewport = viewport; + }, + + getViewport: function () { + if (this._frameBuffer.viewport) { + return this._frameBuffer.viewport; + } + else { + return { + x: 0, y: 0, + width: this._gBufferTex1.width, + height: this._gBufferTex1.height, + devicePixelRatio: 1 + }; + } + }, + + update: function (renderer, scene, camera) { + + var gl = renderer.gl; + + var frameBuffer = this._frameBuffer; + var viewport = frameBuffer.viewport; + var opaqueQueue = scene.opaqueQueue; + var oldBeforeRender = renderer.beforeRenderObject; + + gl.clearColor(0, 0, 0, 0); + gl.depthMask(true); + gl.colorMask(true, true, true, true); + gl.disable(gl.BLEND); + + var enableTargetTexture1 = this.enableTargetTexture1; + var enableTargetTexture2 = this.enableTargetTexture2; + var enableTargetTexture3 = this.enableTargetTexture3; + if (!enableTargetTexture1 && !enableTargetTexture3) { + console.warn('Can\'t disable targetTexture1 targetTexture2 both'); + enableTargetTexture1 = true; + } + + if (enableTargetTexture2) { + frameBuffer.attach(this._gBufferTex2, renderer.gl.DEPTH_STENCIL_ATTACHMENT); + } + + // PENDING, scene.boundingBoxLastFrame needs be updated if have shadow + renderer.bindSceneRendering(scene); + if (enableTargetTexture1) { + // Pass 1 + frameBuffer.attach(this._gBufferTex1); + frameBuffer.bind(renderer); + + if (viewport) { + var dpr = viewport.devicePixelRatio; + // use scissor to make sure only clear the viewport + gl.enable(gl.SCISSOR_TEST); + gl.scissor(viewport.x * dpr, viewport.y * dpr, viewport.width * dpr, viewport.height * dpr); + } + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + if (viewport) { + gl.disable(gl.SCISSOR_TEST); + } + + this._resetGBufferMaterials(); + + this._replaceGBufferMat(opaqueQueue, 1); + opaqueQueue.sort(ForwardRenderer.opaqueSortFunc); + + + // FIXME Use MRT if possible + // Pass 1 + renderer.beforeRenderObject = getBeforeRenderHook1( + gl, + this._defaultNormalMap, + this._defaultRoughnessMap + ); + renderer.renderQueue(opaqueQueue, camera); + + } + if (enableTargetTexture3) { + + // Pass 2 + frameBuffer.attach(this._gBufferTex3); + frameBuffer.bind(renderer); + + if (viewport) { + var dpr = viewport.devicePixelRatio; + // use scissor to make sure only clear the viewport + gl.enable(gl.SCISSOR_TEST); + gl.scissor(viewport.x * dpr, viewport.y * dpr, viewport.width * dpr, viewport.height * dpr); + } + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + if (viewport) { + gl.disable(gl.SCISSOR_TEST); + } + + this._replaceGBufferMat(opaqueQueue, 2); + renderer.beforeRenderObject = getBeforeRenderHook2( + gl, + this._defaultDiffuseMap, + this._defaultMetalnessMap + ); + renderer.renderQueue(opaqueQueue, camera); + + } + + renderer.bindSceneRendering(null); + + renderer.beforeRenderObject = oldBeforeRender; + this._cleanGBufferMaterials(renderer.gl); + this._restoreMaterial(opaqueQueue); + + frameBuffer.unbind(renderer); + }, + + renderDebug: function (renderer, camera, type, viewport) { + var debugTypes = { + normal: 0, + depth: 1, + position: 2, + glossiness: 3, + metalness: 4, + albedo: 5 + }; + if (debugTypes[type] == null) { + console.warn('Unkown type "' + type + '"'); + // Default use normal + type = 'normal'; + } + + renderer.saveClear(); + renderer.saveViewport(); + renderer.clearBit = renderer.gl.DEPTH_BUFFER_BIT; + + if (viewport) { + renderer.setViewport(viewport); + } + var viewProjectionInv = new Matrix4(); + Matrix4.multiply(viewProjectionInv, camera.worldTransform, camera.invProjectionMatrix); + + var debugPass = this._debugPass; + debugPass.setUniform('viewportSize', [renderer.getWidth(), renderer.getHeight()]); + debugPass.setUniform('gBufferTexture1', this._gBufferTex1); + debugPass.setUniform('gBufferTexture2', this._gBufferTex2); + debugPass.setUniform('gBufferTexture3', this._gBufferTex3); + debugPass.setUniform('debug', debugTypes[type]); + debugPass.setUniform('viewProjectionInv', viewProjectionInv._array); + debugPass.render(renderer); + + renderer.restoreViewport(); + renderer.restoreClear(); + }, + + getTargetTexture1: function () { + return this._gBufferTex1; + }, + + getTargetTexture2: function () { + return this._gBufferTex2; + }, + + getTargetTexture3: function () { + return this._gBufferTex3; + }, + + _getMaterial: function (nJoints) { + var gBufferMaterials = this._gBufferMaterials; + var obj = gBufferMaterials[nJoints]; + if (!obj) { + var mat1 = new Material({ + shader: new Shader({ + vertex: Shader.source('qtek.deferred.gbuffer.vertex'), + fragment: Shader.source('qtek.deferred.gbuffer1.fragment') + }) + }); + var mat2 = new Material({ + shader: new Shader({ + vertex: Shader.source('qtek.deferred.gbuffer.vertex'), + fragment: Shader.source('qtek.deferred.gbuffer2.fragment') + }) + }); + mat1.shader.define('vertex', 'FIRST_PASS'); + + if (nJoints > 0) { + mat1.shader.define('vertex', 'SKINNING'); + mat1.shader.define('vertex', 'JOINT_COUNT', nJoints); + mat2.shader.define('vertex', 'SKINNING'); + mat2.shader.define('vertex', 'JOINT_COUNT', nJoints); + } + + obj = { + material1: mat1, + material2: mat2 + }; + + gBufferMaterials[nJoints] = obj; + } + obj.used = true; + + return obj; + }, + + _resetGBufferMaterials: function () { + for (var key in this._gBufferMaterials) { + this._gBufferMaterials[key].used = false; + } + }, + + _cleanGBufferMaterials: function (gl) { + for (var key in this._gBufferMaterials) { + var obj = this._gBufferMaterials[key]; + if (!obj.used) { + obj.material1.dispose(gl); + obj.material2.dispose(gl); + } + } + }, + + _replaceGBufferMat: function (queue, pass) { + for (var i = 0; i < queue.length; i++) { + var renderable = queue[i]; + + if (pass === 1) { + renderable.__standardMat = renderable.material; + } + + var matObj = this._getMaterial( + renderable.joints ? renderable.joints.length : 0, + false + ); + renderable.material = pass === 1 ? matObj.material1 : matObj.material2; + } + }, + + _restoreMaterial: function (queue) { + for (var i = 0; i < queue.length; i++) { + var renderable = queue[i]; + + if (renderable.__standardMat) { + renderable.material = renderable.__standardMat; + } + } + }, + + dispose: function (gl) { + for (var name in this._gBufferMaterials) { + var matObj = this._gBufferMaterials[name]; + matObj.material1.dispose(gl); + matObj.material2.dispose(gl); + } + } + }); + + module.exports = GBuffer; + + +/***/ }, +/* 63 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + // TODO Resources like shader, texture, geometry reference management + // Trace and find out which shader, texture, geometry can be destroyed + // + // TODO prez skinning + + + var Base = __webpack_require__(3); + var glinfo = __webpack_require__(42); + var glenum = __webpack_require__(34); + var vendor = __webpack_require__(51); + var BoundingBox = __webpack_require__(26); + var Matrix4 = __webpack_require__(25); + var shaderLibrary = __webpack_require__(64); + var Material = __webpack_require__(53); + var Vector2 = __webpack_require__(13); + + // Light header + var Shader = __webpack_require__(52); + Shader['import'](__webpack_require__(65)); + + var glMatrix = __webpack_require__(14); + var mat4 = glMatrix.mat4; + var vec3 = glMatrix.vec3; + + var mat4Create = mat4.create; + + var glid = 0; + + var errorShader = {}; + + /** + * @constructor qtek.Renderer + */ + var Renderer = Base.extend(function () { + return /** @lends qtek.Renderer# */ { + + /** + * @type {HTMLCanvasElement} + * @readonly + */ + canvas: null, + + /** + * Canvas width, set by resize method + * @type {number} + * @private + */ + _width: 100, + + /** + * Canvas width, set by resize method + * @type {number} + * @private + */ + _height: 100, + + /** + * Device pixel ratio, set by setDevicePixelRatio method + * Specially for high defination display + * @see http://www.khronos.org/webgl/wiki/HandlingHighDPI + * @type {number} + * @private + */ + devicePixelRatio: window.devicePixelRatio || 1.0, + + /** + * Clear color + * @type {number[]} + */ + clearColor: [0.0, 0.0, 0.0, 0.0], + + /** + * Default: + * _gl.COLOR_BUFFER_BIT | _gl.DEPTH_BUFFER_BIT | _gl.STENCIL_BUFFER_BIT + * @type {number} + */ + clearBit: 17664, + + // Settings when getting context + // http://www.khronos.org/registry/webgl/specs/latest/#2.4 + + /** + * If enable alpha, default true + * @type {boolean} + */ + alpha: true, + /** + * If enable depth buffer, default true + * @type {boolean} + */ + depth: true, + /** + * If enable stencil buffer, default false + * @type {boolean} + */ + stencil: false, + /** + * If enable antialias, default true + * @type {boolean} + */ + antialias: true, + /** + * If enable premultiplied alpha, default true + * @type {boolean} + */ + premultipliedAlpha: true, + /** + * If preserve drawing buffer, default false + * @type {boolean} + */ + preserveDrawingBuffer: false, + /** + * If throw context error, usually turned on in debug mode + * @type {boolean} + */ + throwError: true, + /** + * WebGL Context created from given canvas + * @type {WebGLRenderingContext} + */ + gl: null, + /** + * Renderer viewport, read-only, can be set by setViewport method + * @type {Object} + */ + viewport: {}, + + // Set by FrameBuffer#bind + __currentFrameBuffer: null, + + _viewportStack: [], + _clearStack: [], + + _sceneRendering: null + }; + }, function () { + + if (!this.canvas) { + this.canvas = document.createElement('canvas'); + } + var canvas = this.canvas; + try { + var opts = { + alpha: this.alpha, + depth: this.depth, + stencil: this.stencil, + antialias: this.antialias, + premultipliedAlpha: this.premultipliedAlpha, + preserveDrawingBuffer: this.preserveDrawingBuffer + }; + + this.gl = canvas.getContext('webgl', opts) + || canvas.getContext('experimental-webgl', opts); + + if (!this.gl) { + throw new Error(); + } + + if (this.gl.__GLID__ == null) { + // gl context is not created + // Otherwise is the case mutiple renderer share the same gl context + this.gl.__GLID__ = glid++; + + glinfo.initialize(this.gl); + } + + this.resize(); + } + catch(e) { + throw 'Error creating WebGL Context ' + e; + } + }, + /** @lends qtek.Renderer.prototype. **/ + { + /** + * Resize the canvas + * @param {number} width + * @param {number} height + */ + resize: function(width, height) { + var canvas = this.canvas; + // http://www.khronos.org/webgl/wiki/HandlingHighDPI + // set the display size of the canvas. + var dpr = this.devicePixelRatio; + if (width != null) { + canvas.style.width = width + 'px'; + canvas.style.height = height + 'px'; + // set the size of the drawingBuffer + canvas.width = width * dpr; + canvas.height = height * dpr; + + this._width = width; + this._height = height; + } + else { + this._width = canvas.width / dpr; + this._height = canvas.height / dpr; + } + + this.setViewport(0, 0, this._width, this._height); + }, + + /** + * Get renderer width + * @return {number} + */ + getWidth: function () { + return this._width; + }, + + /** + * Get renderer height + * @return {number} + */ + getHeight: function () { + return this._height; + }, + + /** + * Get viewport aspect, + */ + getViewportAspect: function () { + var viewport = this.viewport; + return viewport.width / viewport.height; + }, + + /** + * Set devicePixelRatio + * @param {number} devicePixelRatio + */ + setDevicePixelRatio: function(devicePixelRatio) { + this.devicePixelRatio = devicePixelRatio; + this.resize(this._width, this._height); + }, + + /** + * Get devicePixelRatio + * @param {number} devicePixelRatio + */ + getDevicePixelRatio: function () { + return this.devicePixelRatio; + }, + + /** + * Get WebGL extionsion + * @return {object} + */ + getExtension: function (name) { + return glinfo.getExtension(this.gl, name); + }, + + /** + * Set rendering viewport + * @param {number|Object} x + * @param {number} [y] + * @param {number} [width] + * @param {number} [height] + * @param {number} [devicePixelRatio] + * Defaultly use the renderere devicePixelRatio + * It needs to be 1 when setViewport is called by frameBuffer + * + * @example + * setViewport(0,0,width,height,1) + * setViewport({ + * x: 0, + * y: 0, + * width: width, + * height: height, + * devicePixelRatio: 1 + * }) + */ + setViewport: function (x, y, width, height, dpr) { + + if (typeof x === 'object') { + var obj = x; + + x = obj.x; + y = obj.y; + width = obj.width; + height = obj.height; + dpr = obj.devicePixelRatio; + } + dpr = dpr || this.devicePixelRatio; + + this.gl.viewport( + x * dpr, y * dpr, width * dpr, height * dpr + ); + + this.viewport = { + x: x, + y: y, + width: width, + height: height, + devicePixelRatio: dpr + }; + }, + + /** + * Push current viewport into a stack + */ + saveViewport: function () { + this._viewportStack.push(this.viewport); + }, + + /** + * Pop viewport from stack, restore in the renderer + */ + restoreViewport: function () { + if (this._viewportStack.length > 0) { + this.setViewport(this._viewportStack.pop()); + } + }, + + /** + * Push current clear into a stack + */ + saveClear: function () { + this._clearStack.push({ + clearBit: this.clearBit, + clearColor: this.clearColor + }); + }, + + /** + * Pop clear from stack, restore in the renderer + */ + restoreClear: function () { + if (this._clearStack.length > 0) { + var opt = this._clearStack.pop(); + this.clearColor = opt.clearColor; + this.clearBit = opt.clearBit; + } + }, + + bindSceneRendering: function (scene) { + this._sceneRendering = scene; + }, + + // Hook before and after render each object + beforeRenderObject: function () {}, + afterRenderObject: function () {}, + /** + * Render the scene in camera to the screen or binded offline framebuffer + * @param {qtek.Scene} scene + * @param {qtek.Camera} camera + * @param {boolean} [notUpdateScene] If not call the scene.update methods in the rendering, default true + * @param {boolean} [preZ] If use preZ optimization, default false + * @return {IRenderInfo} + */ + render: function(scene, camera, notUpdateScene, preZ) { + var _gl = this.gl; + + this._sceneRendering = scene; + + var clearColor = this.clearColor; + + if (this.clearBit) { + + // Must set depth and color mask true before clear + _gl.colorMask(true, true, true, true); + _gl.depthMask(true); + var viewport = this.viewport; + var needsScissor = false; + var viewportDpr = viewport.devicePixelRatio; + if (viewport.width !== this._width || viewport.height !== this._height + || (viewportDpr && viewportDpr !== this.devicePixelRatio) + || viewport.x || viewport.y + ) { + needsScissor = true; + // http://stackoverflow.com/questions/11544608/how-to-clear-a-rectangle-area-in-webgl + // Only clear the viewport + _gl.enable(_gl.SCISSOR_TEST); + _gl.scissor(viewport.x * viewportDpr, viewport.y * viewportDpr, viewport.width * viewportDpr, viewport.height * viewportDpr); + } + _gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + _gl.clear(this.clearBit); + if (needsScissor) { + _gl.disable(_gl.SCISSOR_TEST); + } + } + + // If the scene have been updated in the prepass like shadow map + // There is no need to update it again + if (!notUpdateScene) { + scene.update(false); + } + // Update if camera not mounted on the scene + if (!camera.getScene()) { + camera.update(true); + } + + var opaqueQueue = scene.opaqueQueue; + var transparentQueue = scene.transparentQueue; + var sceneMaterial = scene.material; + + scene.trigger('beforerender', this, scene, camera); + // Sort render queue + // Calculate the object depth + if (transparentQueue.length > 0) { + var worldViewMat = mat4Create(); + var posViewSpace = vec3.create(); + for (var i = 0; i < transparentQueue.length; i++) { + var node = transparentQueue[i]; + mat4.multiplyAffine(worldViewMat, camera.viewMatrix._array, node.worldTransform._array); + vec3.transformMat4(posViewSpace, node.position._array, worldViewMat); + node.__depth = posViewSpace[2]; + } + } + opaqueQueue.sort(this.opaqueSortFunc); + transparentQueue.sort(this.transparentSortFunc); + + // Render Opaque queue + scene.trigger('beforerender:opaque', this, opaqueQueue); + + // Reset the scene bounding box; + scene.viewBoundingBoxLastFrame.min.set(Infinity, Infinity, Infinity); + scene.viewBoundingBoxLastFrame.max.set(-Infinity, -Infinity, -Infinity); + + _gl.disable(_gl.BLEND); + _gl.enable(_gl.DEPTH_TEST); + var opaqueRenderInfo = this.renderQueue(opaqueQueue, camera, sceneMaterial, preZ); + + scene.trigger('afterrender:opaque', this, opaqueQueue, opaqueRenderInfo); + scene.trigger('beforerender:transparent', this, transparentQueue); + + // Render Transparent Queue + _gl.enable(_gl.BLEND); + var transparentRenderInfo = this.renderQueue(transparentQueue, camera, sceneMaterial); + + scene.trigger('afterrender:transparent', this, transparentQueue, transparentRenderInfo); + var renderInfo = {}; + for (var name in opaqueRenderInfo) { + renderInfo[name] = opaqueRenderInfo[name] + transparentRenderInfo[name]; + } + + scene.trigger('afterrender', this, scene, camera, renderInfo); + + // Cleanup + this._sceneRendering = null; + return renderInfo; + }, + + resetRenderStatus: function () { + this._currentShader = null; + }, + /** + * Render a single renderable list in camera in sequence + * @param {qtek.Renderable[]} queue List of all renderables. + * Best to be sorted by Renderer.opaqueSortFunc or Renderer.transparentSortFunc + * @param {qtek.Camera} camera + * @param {qtek.Material} [globalMaterial] globalMaterial will override the material of each renderable + * @param {boolean} [preZ] If use preZ optimization, default false + * @return {IRenderInfo} + */ + renderQueue: function(queue, camera, globalMaterial, preZ) { + var renderInfo = { + triangleCount: 0, + vertexCount: 0, + drawCallCount: 0, + meshCount: queue.length, + renderedMeshCount: 0 + }; + + // Some common builtin uniforms + var viewport = this.viewport; + var vDpr = viewport.devicePixelRatio; + var viewportUniform = [ + viewport.x * vDpr, viewport.y * vDpr, + viewport.width * vDpr, viewport.height * vDpr + ]; + var windowDpr = this.devicePixelRatio; + var windowSizeUniform = this.__currentFrameBuffer + ? [this.__currentFrameBuffer.getTextureWidth(), this.__currentFrameBuffer.getTextureHeight()] + : [this._width * windowDpr, this._height * windowDpr]; + // DEPRECATED + var viewportSizeUniform = [ + viewportUniform[2], viewportUniform[3] + ]; + + + // Calculate view and projection matrix + mat4.copy(matrices.VIEW, camera.viewMatrix._array); + mat4.copy(matrices.PROJECTION, camera.projectionMatrix._array); + mat4.multiply(matrices.VIEWPROJECTION, camera.projectionMatrix._array, matrices.VIEW); + mat4.copy(matrices.VIEWINVERSE, camera.worldTransform._array); + mat4.invert(matrices.PROJECTIONINVERSE, matrices.PROJECTION); + mat4.invert(matrices.VIEWPROJECTIONINVERSE, matrices.VIEWPROJECTION); + + + var _gl = this.gl; + var scene = this._sceneRendering; + + var prevMaterial; + + // Status + var depthTest, depthMask; + var culling, cullFace, frontFace; + + var culledRenderQueue; + if (preZ) { + var preZPassMaterial = new Material({ + shader: shaderLibrary.get('qtek.prez') + }); + var preZPassShader = preZPassMaterial.shader; + + culledRenderQueue = []; + preZPassShader.bind(_gl); + _gl.colorMask(false, false, false, false); + _gl.depthMask(true); + _gl.enable(_gl.DEPTH_TEST); + for (var i = 0; i < queue.length; i++) { + var renderable = queue[i]; + var worldM = renderable.worldTransform._array; + var geometry = renderable.geometry; + + mat4.multiplyAffine(matrices.WORLDVIEW, matrices.VIEW , worldM); + + if (geometry.boundingBox) { + if (this.isFrustumCulled( + renderable, scene, camera, matrices.WORLDVIEW, matrices.PROJECTION + )) { + continue; + } + } + if (renderable.skeleton) { // FIXME skinned mesh + continue; + } + + mat4.multiply(matrices.WORLDVIEWPROJECTION, matrices.VIEWPROJECTION , worldM); + + if (renderable.cullFace !== cullFace) { + cullFace = renderable.cullFace; + _gl.cullFace(cullFace); + } + if (renderable.frontFace !== frontFace) { + frontFace = renderable.frontFace; + _gl.frontFace(frontFace); + } + if (renderable.culling !== culling) { + culling = renderable.culling; + culling ? _gl.enable(_gl.CULL_FACE) : _gl.disable(_gl.CULL_FACE); + } + + var semanticInfo = preZPassShader.matrixSemantics.WORLDVIEWPROJECTION; + preZPassShader.setUniform(_gl, semanticInfo.type, semanticInfo.symbol, matrices.WORLDVIEWPROJECTION); + + // PENDING If invoke beforeRender hook + renderable.render(_gl, preZPassMaterial.shader); + culledRenderQueue.push(renderable); + } + _gl.depthFunc(_gl.LEQUAL); + _gl.colorMask(true, true, true, true); + _gl.depthMask(false); + + // Reset current shader. + this._currentShader = null; + } + else { + culledRenderQueue = queue; + } + + culling = null; + cullFace = null; + frontFace = null; + + for (var i =0; i < culledRenderQueue.length; i++) { + var renderable = culledRenderQueue[i]; + var geometry = renderable.geometry; + + var worldM = renderable.worldTransform._array; + // All matrices ralated to world matrix will be updated on demand; + mat4.multiplyAffine(matrices.WORLDVIEW, matrices.VIEW , worldM); + if (geometry.boundingBox && !preZ) { + if (this.isFrustumCulled( + renderable, scene, camera, matrices.WORLDVIEW, matrices.PROJECTION + )) { + continue; + } + } + + var material = globalMaterial || renderable.material; + // StandardMaterial needs updateShader method so shader can be created on demand. + if (material !== prevMaterial) { + material.updateShader && material.updateShader(_gl); + } + + var shader = material.shader; + + mat4.copy(matrices.WORLD, worldM); + mat4.multiply(matrices.WORLDVIEWPROJECTION, matrices.VIEWPROJECTION , worldM); + if (shader.matrixSemantics.WORLDINVERSE || + shader.matrixSemantics.WORLDINVERSETRANSPOSE) { + mat4.invert(matrices.WORLDINVERSE, worldM); + } + if (shader.matrixSemantics.WORLDVIEWINVERSE || + shader.matrixSemantics.WORLDVIEWINVERSETRANSPOSE) { + mat4.invert(matrices.WORLDVIEWINVERSE, matrices.WORLDVIEW); + } + if (shader.matrixSemantics.WORLDVIEWPROJECTIONINVERSE || + shader.matrixSemantics.WORLDVIEWPROJECTIONINVERSETRANSPOSE) { + mat4.invert(matrices.WORLDVIEWPROJECTIONINVERSE, matrices.WORLDVIEWPROJECTION); + } + + var prevShader = this._currentShader; + + // Before render hook + renderable.beforeRender(_gl); + this.beforeRenderObject(renderable, prevMaterial, prevShader); + + if (!shader.isEqual(prevShader)) { + // Set lights number + if (scene && scene.isShaderLightNumberChanged(shader)) { + scene.setShaderLightNumber(shader); + } + var errMsg = shader.bind(_gl); + if (errMsg) { + + if (errorShader[shader.__GUID__]) { + continue; + } + errorShader[shader.__GUID__] = true; + + if (this.throwError) { + throw new Error(errMsg); + } + else { + this.trigger('error', errMsg); + } + } + // Set some common uniforms + shader.setUniformOfSemantic(_gl, 'VIEWPORT', viewportUniform); + shader.setUniformOfSemantic(_gl, 'WINDOW_SIZE', windowSizeUniform); + // DEPRECATED + shader.setUniformOfSemantic(_gl, 'VIEWPORT_SIZE', viewportSizeUniform); + shader.setUniformOfSemantic(_gl, 'NEAR', camera.near); + shader.setUniformOfSemantic(_gl, 'FAR', camera.far); + + // Set lights uniforms + // TODO needs optimized + if (scene) { + scene.setLightUniforms(shader, _gl); + } + + // Save current used shader in the renderer + // ALWAYS USE RENDERER TO DRAW THE MESH + this._currentShader = shader; + } + else { + shader = prevShader; + } + + if (prevMaterial !== material) { + if (!preZ) { + if (material.depthTest !== depthTest) { + material.depthTest ? + _gl.enable(_gl.DEPTH_TEST) : + _gl.disable(_gl.DEPTH_TEST); + depthTest = material.depthTest; + } + if (material.depthMask !== depthMask) { + _gl.depthMask(material.depthMask); + depthMask = material.depthMask; + } + } + material.bind(_gl, shader, prevMaterial, prevShader); + prevMaterial = material; + + // TODO cache blending + if (material.transparent) { + if (material.blend) { + material.blend(_gl); + } + else { // Default blend function + _gl.blendEquationSeparate(_gl.FUNC_ADD, _gl.FUNC_ADD); + _gl.blendFuncSeparate(_gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA, _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA); + } + } + } + + var matrixSemanticKeys = shader.matrixSemanticKeys; + for (var k = 0; k < matrixSemanticKeys.length; k++) { + var semantic = matrixSemanticKeys[k]; + var semanticInfo = shader.matrixSemantics[semantic]; + var matrix = matrices[semantic]; + if (semanticInfo.isTranspose) { + var matrixNoTranspose = matrices[semanticInfo.semanticNoTranspose]; + mat4.transpose(matrix, matrixNoTranspose); + } + shader.setUniform(_gl, semanticInfo.type, semanticInfo.symbol, matrix); + } + + if (renderable.cullFace !== cullFace) { + cullFace = renderable.cullFace; + _gl.cullFace(cullFace); + } + if (renderable.frontFace !== frontFace) { + frontFace = renderable.frontFace; + _gl.frontFace(frontFace); + } + if (renderable.culling !== culling) { + culling = renderable.culling; + culling ? _gl.enable(_gl.CULL_FACE) : _gl.disable(_gl.CULL_FACE); + } + + var objectRenderInfo = renderable.render(_gl, shader); + + if (objectRenderInfo) { + renderInfo.triangleCount += objectRenderInfo.triangleCount; + renderInfo.vertexCount += objectRenderInfo.vertexCount; + renderInfo.drawCallCount += objectRenderInfo.drawCallCount; + renderInfo.renderedMeshCount ++; + } + + // After render hook + this.afterRenderObject(renderable, objectRenderInfo); + renderable.afterRender(_gl, objectRenderInfo); + } + + if (preZ) { + // default depth func + _gl.depthFunc(_gl.LESS); + } + + return renderInfo; + }, + + /** + * If an scene object is culled by camera frustum + * + * Object can be a renderable or a light + * + * @param {qtek.Node} Scene object + * @param {qtek.Camera} camera + * @param {Array.} worldViewMat represented with array + * @param {Array.} projectionMat represented with array + */ + isFrustumCulled: (function () { + // Frustum culling + // http://www.cse.chalmers.se/~uffe/vfc_bbox.pdf + var cullingBoundingBox = new BoundingBox(); + var cullingMatrix = new Matrix4(); + return function(object, scene, camera, worldViewMat, projectionMat) { + // Bounding box can be a property of object(like light) or renderable.geometry + var geoBBox = object.boundingBox || object.geometry.boundingBox; + cullingMatrix._array = worldViewMat; + cullingBoundingBox.copy(geoBBox); + cullingBoundingBox.applyTransform(cullingMatrix); + + // Passingly update the scene bounding box + // FIXME exclude very large mesh like ground plane or terrain ? + // FIXME Only rendererable which cast shadow ? + + // FIXME boundingBox becomes much larger after transformd. + if (scene && object.isRenderable() && object.castShadow) { + scene.viewBoundingBoxLastFrame.union(cullingBoundingBox); + } + + if (object.frustumCulling) { + if (!cullingBoundingBox.intersectBoundingBox(camera.frustum.boundingBox)) { + return true; + } + + cullingMatrix._array = projectionMat; + if ( + cullingBoundingBox.max._array[2] > 0 && + cullingBoundingBox.min._array[2] < 0 + ) { + // Clip in the near plane + cullingBoundingBox.max._array[2] = -1e-20; + } + + cullingBoundingBox.applyProjection(cullingMatrix); + + var min = cullingBoundingBox.min._array; + var max = cullingBoundingBox.max._array; + + if ( + max[0] < -1 || min[0] > 1 + || max[1] < -1 || min[1] > 1 + || max[2] < -1 || min[2] > 1 + ) { + return true; + } + } + + return false; + }; + })(), + + /** + * Dispose given scene, including all geometris, textures and shaders in the scene + * @param {qtek.Scene} scene + */ + disposeScene: function(scene) { + this.disposeNode(scene, true, true); + scene.dispose(); + }, + + /** + * Dispose given node, including all geometries, textures and shaders attached on it or its descendant + * @param {qtek.Node} node + * @param {boolean} [disposeGeometry=false] If dispose the geometries used in the descendant mesh + * @param {boolean} [disposeTexture=false] If dispose the textures used in the descendant mesh + */ + disposeNode: function(root, disposeGeometry, disposeTexture) { + var materials = {}; + var _gl = this.gl; + // Dettached from parent + if (root.getParent()) { + root.getParent().remove(root); + } + root.traverse(function(node) { + if (node.geometry && disposeGeometry) { + node.geometry.dispose(_gl); + } + if (node.material) { + materials[node.material.__GUID__] = node.material; + } + // Particle system and AmbientCubemap light need to dispose + if (node.dispose) { + node.dispose(_gl); + } + }); + for (var guid in materials) { + var mat = materials[guid]; + mat.dispose(_gl, disposeTexture); + } + }, + + /** + * Dispose given shader + * @param {qtek.Shader} shader + */ + disposeShader: function(shader) { + shader.dispose(this.gl); + }, + + /** + * Dispose given geometry + * @param {qtek.Geometry} geometry + */ + disposeGeometry: function(geometry) { + geometry.dispose(this.gl); + }, + + /** + * Dispose given texture + * @param {qtek.Texture} texture + */ + disposeTexture: function(texture) { + texture.dispose(this.gl); + }, + + /** + * Dispose given frame buffer + * @param {qtek.FrameBuffer} frameBuffer + */ + disposeFrameBuffer: function(frameBuffer) { + frameBuffer.dispose(this.gl); + }, + + /** + * Dispose renderer + */ + dispose: function () { + glinfo.dispose(this.gl); + }, + + /** + * Convert screen coords to normalized device coordinates(NDC) + * Screen coords can get from mouse event, it is positioned relative to canvas element + * NDC can be used in ray casting with Camera.prototype.castRay methods + * + * @param {number} x + * @param {number} y + * @param {qtek.math.Vector2} [out] + * @return {qtek.math.Vector2} + */ + screenToNdc: function(x, y, out) { + if (!out) { + out = new Vector2(); + } + // Invert y; + y = this._height - y; + + var viewport = this.viewport; + var arr = out._array; + arr[0] = (x - viewport.x) / viewport.width; + arr[0] = arr[0] * 2 - 1; + arr[1] = (y - viewport.y) / viewport.height; + arr[1] = arr[1] * 2 - 1; + + return out; + }, + }); + + /** + * Opaque renderables compare function + * @param {qtek.Renderable} x + * @param {qtek.Renderable} y + * @return {boolean} + * @static + */ + Renderer.opaqueSortFunc = Renderer.prototype.opaqueSortFunc = function(x, y) { + // Priority renderOrder -> shader -> material -> geometry + if (x.renderOrder === y.renderOrder) { + if (x.material.shader === y.material.shader) { + if (x.material === y.material) { + return x.geometry.__GUID__ - y.geometry.__GUID__; + } + return x.material.__GUID__ - y.material.__GUID__; + } + return x.material.shader.__GUID__ - y.material.shader.__GUID__; + } + return x.renderOrder - y.renderOrder; + }; + + /** + * Transparent renderables compare function + * @param {qtek.Renderable} a + * @param {qtek.Renderable} b + * @return {boolean} + * @static + */ + Renderer.transparentSortFunc = Renderer.prototype.transparentSortFunc = function(x, y) { + // Priority renderOrder -> depth -> shader -> material -> geometry + + if (x.renderOrder === y.renderOrder) { + if (x.__depth === y.__depth) { + if (x.material.shader === y.material.shader) { + if (x.material === y.material) { + return x.geometry.__GUID__ - y.geometry.__GUID__; + } + return x.material.__GUID__ - y.material.__GUID__; + } + return x.material.shader.__GUID__ - y.material.shader.__GUID__; + } + // Depth is negative + // So farther object has smaller depth value + return x.__depth - y.__depth; + } + return x.renderOrder - y.renderOrder; + }; + + // Temporary variables + var matrices = { + WORLD: mat4Create(), + VIEW: mat4Create(), + PROJECTION: mat4Create(), + WORLDVIEW: mat4Create(), + VIEWPROJECTION: mat4Create(), + WORLDVIEWPROJECTION: mat4Create(), + + WORLDINVERSE: mat4Create(), + VIEWINVERSE: mat4Create(), + PROJECTIONINVERSE: mat4Create(), + WORLDVIEWINVERSE: mat4Create(), + VIEWPROJECTIONINVERSE: mat4Create(), + WORLDVIEWPROJECTIONINVERSE: mat4Create(), + + WORLDTRANSPOSE: mat4Create(), + VIEWTRANSPOSE: mat4Create(), + PROJECTIONTRANSPOSE: mat4Create(), + WORLDVIEWTRANSPOSE: mat4Create(), + VIEWPROJECTIONTRANSPOSE: mat4Create(), + WORLDVIEWPROJECTIONTRANSPOSE: mat4Create(), + WORLDINVERSETRANSPOSE: mat4Create(), + VIEWINVERSETRANSPOSE: mat4Create(), + PROJECTIONINVERSETRANSPOSE: mat4Create(), + WORLDVIEWINVERSETRANSPOSE: mat4Create(), + VIEWPROJECTIONINVERSETRANSPOSE: mat4Create(), + WORLDVIEWPROJECTIONINVERSETRANSPOSE: mat4Create() + }; + + Renderer.COLOR_BUFFER_BIT = glenum.COLOR_BUFFER_BIT; + Renderer.DEPTH_BUFFER_BIT = glenum.DEPTH_BUFFER_BIT; + Renderer.STENCIL_BUFFER_BIT = glenum.STENCIL_BUFFER_BIT; + + module.exports = Renderer; + + +/***/ }, +/* 64 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @export{Object} library + */ + + + var Shader = __webpack_require__(52); + var util = __webpack_require__(6); + + var _library = {}; + + /** + * @export qtek.shader.library~Libaray + */ + function ShaderLibrary () { + this._pool = {}; + } + + /** + * ### Builin shaders + * + qtek.standard + * + qtek.basic + * + qtek.lambert + * + qtek.phong + * + qtek.wireframe + * + * @namespace qtek.shader.library + */ + /** + * + * Get shader from library. use shader name and option as hash key. + * + * @param {string} name + * @param {Object|string|Array.} [option] + * @return {qtek.Shader} + * + * @example + * qtek.shader.library.get('qtek.phong', 'diffuseMap', 'normalMap'); + * qtek.shader.library.get('qtek.phong', ['diffuseMap', 'normalMap']); + * qtek.shader.library.get('qtek.phong', { + * textures: ['diffuseMap'], + * vertexDefines: {}, + * fragmentDefines: {} + * }) + */ + ShaderLibrary.prototype.get = function(name, option) { + var enabledTextures = []; + var vertexDefines = {}; + var fragmentDefines = {}; + if (typeof(option) === 'string') { + enabledTextures = Array.prototype.slice.call(arguments, 1); + } + else if (Object.prototype.toString.call(option) == '[object Object]') { + enabledTextures = option.textures || []; + vertexDefines = option.vertexDefines || {}; + fragmentDefines = option.fragmentDefines || {}; + } + else if (option instanceof Array) { + enabledTextures = option; + } + var vertexDefineKeys = Object.keys(vertexDefines); + var fragmentDefineKeys = Object.keys(fragmentDefines); + enabledTextures.sort(); + vertexDefineKeys.sort(); + fragmentDefineKeys.sort(); + + var keyArr = [name]; + keyArr = keyArr.concat(enabledTextures); + for (var i = 0; i < vertexDefineKeys.length; i++) { + keyArr.push( + vertexDefineKeys[i], + vertexDefines[vertexDefineKeys[i]] + ); + } + for (var i = 0; i < fragmentDefineKeys.length; i++) { + keyArr.push( + fragmentDefineKeys[i], + fragmentDefines[fragmentDefineKeys[i]] + ); + } + var key = keyArr.join('_'); + + if (this._pool[key]) { + return this._pool[key]; + } + else { + var source = _library[name]; + if (!source) { + console.error('Shader "' + name + '"' + ' is not in the library'); + return; + } + var shader = new Shader({ + 'vertex': source.vertex, + 'fragment': source.fragment + }); + for (var i = 0; i < enabledTextures.length; i++) { + shader.enableTexture(enabledTextures[i]); + } + for (var name in vertexDefines) { + shader.define('vertex', name, vertexDefines[name]); + } + for (var name in fragmentDefines) { + shader.define('fragment', name, fragmentDefines[name]); + } + this._pool[key] = shader; + return shader; + } + }; + + /** + * Clear shaders + */ + ShaderLibrary.prototype.clear = function() { + this._pool = {}; + }; + + /** + * @memberOf qtek.shader.library + * @param {string} name + * @param {string} vertex - Vertex shader code + * @param {string} fragment - Fragment shader code + */ + function template(name, vertex, fragment) { + _library[name] = { + vertex: vertex, + fragment: fragment + }; + } + + var defaultLibrary = new ShaderLibrary(); + + module.exports = { + createLibrary: function () { + return new ShaderLibrary(); + }, + get: function () { + return defaultLibrary.get.apply(defaultLibrary, arguments); + }, + template: template, + clear: function () { + return defaultLibrary.clear(); + } + }; + + +/***/ }, +/* 65 */ +/***/ function(module, exports, __webpack_require__) { + + + var uniformVec3Prefix = 'uniform vec3 '; + var uniformFloatPrefix = 'uniform float '; + var exportHeaderPrefix = '@export qtek.header.'; + var exportEnd = '@end'; + var unconfigurable = ':unconfigurable;'; + module.exports = [ + exportHeaderPrefix + 'directional_light', + uniformVec3Prefix + 'directionalLightDirection[DIRECTIONAL_LIGHT_COUNT]' + unconfigurable, + uniformVec3Prefix + 'directionalLightColor[DIRECTIONAL_LIGHT_COUNT]' + unconfigurable, + exportEnd, + + exportHeaderPrefix + 'ambient_light', + uniformVec3Prefix + 'ambientLightColor[AMBIENT_LIGHT_COUNT]' + unconfigurable, + exportEnd, + + exportHeaderPrefix + 'ambient_sh_light', + uniformVec3Prefix + 'ambientSHLightColor[AMBIENT_SH_LIGHT_COUNT]' + unconfigurable, + uniformVec3Prefix + 'ambientSHLightCoefficients[AMBIENT_SH_LIGHT_COUNT * 9]' + unconfigurable, + __webpack_require__(66), + exportEnd, + + exportHeaderPrefix + 'ambient_cubemap_light', + uniformVec3Prefix + 'ambientCubemapLightColor[AMBIENT_CUBEMAP_LIGHT_COUNT]' + unconfigurable, + 'uniform samplerCube ambientCubemapLightCubemap[AMBIENT_CUBEMAP_LIGHT_COUNT]' + unconfigurable, + 'uniform sampler2D ambientCubemapLightBRDFLookup[AMBIENT_CUBEMAP_LIGHT_COUNT]' + unconfigurable, + exportEnd, + + exportHeaderPrefix + 'point_light', + uniformVec3Prefix + 'pointLightPosition[POINT_LIGHT_COUNT]' + unconfigurable, + uniformFloatPrefix + 'pointLightRange[POINT_LIGHT_COUNT]' + unconfigurable, + uniformVec3Prefix + 'pointLightColor[POINT_LIGHT_COUNT]' + unconfigurable, + exportEnd, + + exportHeaderPrefix + 'spot_light', + uniformVec3Prefix + 'spotLightPosition[SPOT_LIGHT_COUNT]' + unconfigurable, + uniformVec3Prefix + 'spotLightDirection[SPOT_LIGHT_COUNT]' + unconfigurable, + uniformFloatPrefix + 'spotLightRange[SPOT_LIGHT_COUNT]' + unconfigurable, + uniformFloatPrefix + 'spotLightUmbraAngleCosine[SPOT_LIGHT_COUNT]' + unconfigurable, + uniformFloatPrefix + 'spotLightPenumbraAngleCosine[SPOT_LIGHT_COUNT]' + unconfigurable, + uniformFloatPrefix + 'spotLightFalloffFactor[SPOT_LIGHT_COUNT]' + unconfigurable, + uniformVec3Prefix + 'spotLightColor[SPOT_LIGHT_COUNT]' + unconfigurable, + exportEnd + ].join('\n'); + + +/***/ }, +/* 66 */ +/***/ function(module, exports) { + + + module.exports = "vec3 calcAmbientSHLight(int idx, vec3 N) {\n int offset = 9 * idx;\n\n return ambientSHLightCoefficients[0]\n + ambientSHLightCoefficients[1] * N.x\n + ambientSHLightCoefficients[2] * N.y\n + ambientSHLightCoefficients[3] * N.z\n + ambientSHLightCoefficients[4] * N.x * N.z\n + ambientSHLightCoefficients[5] * N.z * N.y\n + ambientSHLightCoefficients[6] * N.y * N.x\n + ambientSHLightCoefficients[7] * (3.0 * N.z * N.z - 1.0)\n + ambientSHLightCoefficients[8] * (N.x * N.x - N.y * N.y);\n}"; + + +/***/ }, +/* 67 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.deferred.gbuffer.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\n\nuniform vec2 uvRepeat;\nuniform vec2 uvOffset;\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n\n#ifdef FIRST_PASS\nattribute vec3 normal : NORMAL;\n#endif\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_COUNT] : SKIN_MATRIX;\n#endif\n\n\n#ifdef FIRST_PASS\nvarying vec3 v_Normal;\n\nattribute vec4 tangent : TANGENT;\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\n\nvarying vec2 v_Texcoord;\nvarying vec4 v_ProjPos;\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n\n#ifdef FIRST_PASS\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n bool hasTangent = dot(tangent, tangent) > 0.0;\n#endif\n\n#ifdef SKINNING\n\n @import qtek.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n\n #ifdef FIRST_PASS\n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n if (hasTangent) {\n skinnedTangent = (skinMatrixWS * vec4(tangent.xyz, 0.0)).xyz;\n }\n #endif\n#endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n\n#ifdef FIRST_PASS\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\n\n if (hasTangent) {\n v_Tangent = normalize((worldInverseTranspose * vec4(skinnedTangent, 0.0)).xyz);\n v_Bitangent = normalize(cross(v_Normal, v_Tangent) * tangent.w);\n }\n#endif\n\n v_ProjPos = gl_Position;\n}\n\n\n@end\n\n\n@export qtek.deferred.gbuffer1.fragment\n\nuniform float glossiness;\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\n\nuniform sampler2D normalMap;\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n\nuniform sampler2D roughnessMap;\n\nuniform bool useRoughnessMap;\n\nvarying vec4 v_ProjPos;\n\nvoid main()\n{\n vec3 N = v_Normal;\n\n if (dot(v_Tangent, v_Tangent) > 0.0) {\n vec3 normalTexel = texture2D(normalMap, v_Texcoord).xyz;\n if (dot(normalTexel, normalTexel) > 0.0) { N = normalTexel * 2.0 - 1.0;\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\n N = normalize(tbn * N);\n }\n }\n\n gl_FragColor.rgb = (N + 1.0) * 0.5;\n\n \n float g = glossiness;\n\n if (useRoughnessMap) {\n vec4 glossTexel = texture2D(roughnessMap, v_Texcoord);\n g = clamp(-glossTexel.r + g * 2.0, 0.0, 1.0);\n }\n\n\n gl_FragColor.a = g;\n\n }\n@end\n\n@export qtek.deferred.gbuffer2.fragment\n\nuniform sampler2D diffuseMap;\nuniform sampler2D metalnessMap;\n\nuniform vec3 color;\nuniform float metalness;\n\nuniform bool useMetalnessMap;\nuniform bool linear;\n\nvarying vec2 v_Texcoord;\n\n@import qtek.util.srgb\n\nvoid main ()\n{\n float m = metalness;\n\n if (useMetalnessMap) {\n vec4 metalnessTexel = texture2D(metalnessMap, v_Texcoord);\n m = clamp(metalnessTexel.r + (m * 2.0 - 1.0), 0.0, 1.0);\n }\n vec4 texel = texture2D(diffuseMap, v_Texcoord);\n if (linear) {\n texel = sRGBToLinear(texel);\n }\n\n gl_FragColor.rgb = texel.rgb * color;\n\n gl_FragColor.a = m;\n}\n\n@end\n\n\n@export qtek.deferred.gbuffer.debug\n\n@import qtek.deferred.chunk.light_head\nuniform int debug: 0;\n\nvoid main ()\n{\n @import qtek.deferred.chunk.gbuffer_read\n\n if (debug == 0) {\n gl_FragColor = vec4(N, 1.0);\n }\n else if (debug == 1) {\n gl_FragColor = vec4(vec3(z), 1.0);\n }\n else if (debug == 2) {\n gl_FragColor = vec4(position, 1.0);\n }\n else if (debug == 3) {\n gl_FragColor = vec4(vec3(glossiness), 1.0);\n }\n else if (debug == 4) {\n gl_FragColor = vec4(vec3(metalness), 1.0);\n }\n else {\n gl_FragColor = vec4(albedo, 1.0);\n }\n}\n@end"; + + +/***/ }, +/* 68 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.deferred.chunk.light_head\n\nuniform sampler2D gBufferTexture1;\nuniform sampler2D gBufferTexture2;\nuniform sampler2D gBufferTexture3;\n\nuniform vec2 windowSize: WINDOW_SIZE;\n\nuniform vec4 viewport: VIEWPORT;\n\nuniform mat4 viewProjectionInv;\n\n\n#ifdef DEPTH_ENCODED\n@import qtek.util.decode_float\n#endif\n\n@end\n\n@export qtek.deferred.chunk.gbuffer_read\n \n vec2 uv = gl_FragCoord.xy / windowSize;\n\n vec2 uv2 = (gl_FragCoord.xy - viewport.xy) / viewport.zw;\n\n vec4 texel1 = texture2D(gBufferTexture1, uv);\n vec4 texel3 = texture2D(gBufferTexture3, uv);\n if (dot(texel1.rgb, vec3(1.0)) == 0.0) {\n discard;\n }\n\n float glossiness = texel1.a;\n float metalness = texel3.a;\n\n vec3 N = texel1.rgb * 2.0 - 1.0;\n\n float z = texture2D(gBufferTexture2, uv).r * 2.0 - 1.0;\n\n vec2 xy = uv2 * 2.0 - 1.0;\n\n vec4 projectedPos = vec4(xy, z, 1.0);\n vec4 p4 = viewProjectionInv * projectedPos;\n\n vec3 position = p4.xyz / p4.w;\n\n vec3 albedo = texel3.rgb;\n\n vec3 diffuseColor = albedo * (1.0 - metalness);\n vec3 specularColor = mix(vec3(0.04), albedo, metalness);\n@end\n\n@export qtek.deferred.chunk.light_equation\n\nfloat D_Phong(in float g, in float ndh) {\n float a = pow(8192.0, g);\n return (a + 2.0) / 8.0 * pow(ndh, a);\n}\n\nfloat D_GGX(in float g, in float ndh) {\n float r = 1.0 - g;\n float a = r * r;\n float tmp = ndh * ndh * (a - 1.0) + 1.0;\n return a / (3.1415926 * tmp * tmp);\n}\n\nvec3 F_Schlick(in float ndv, vec3 spec) {\n return spec + (1.0 - spec) * pow(1.0 - ndv, 5.0);\n}\n\nvec3 lightEquation(\n in vec3 lightColor, in vec3 diffuseColor, in vec3 specularColor,\n in float ndl, in float ndh, in float ndv, in float g\n)\n{\n return ndl * lightColor\n * (diffuseColor + D_Phong(g, ndh) * F_Schlick(ndv, specularColor));\n}\n\n@end"; + + +/***/ }, +/* 69 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + // Light-pre pass deferred rendering + // http://www.realtimerendering.com/blog/deferred-lighting-approaches/ + + + var Base = __webpack_require__(3); + var Shader = __webpack_require__(52); + var Material = __webpack_require__(53); + var FrameBuffer = __webpack_require__(44); + var FullQuadPass = __webpack_require__(47); + var Texture2D = __webpack_require__(39); + var Texture = __webpack_require__(40); + var Mesh = __webpack_require__(54); + var SphereGeo = __webpack_require__(70); + var ConeGeo = __webpack_require__(71); + var CylinderGeo = __webpack_require__(72); + var Matrix4 = __webpack_require__(25); + var Vector3 = __webpack_require__(23); + var GBuffer = __webpack_require__(62); + + Shader.import(__webpack_require__(73)); + Shader.import(__webpack_require__(74)); + + // Light shaders + Shader.import(__webpack_require__(75)); + Shader.import(__webpack_require__(76)); + Shader.import(__webpack_require__(77)); + Shader.import(__webpack_require__(78)); + Shader.import(__webpack_require__(79)); + Shader.import(__webpack_require__(80)); + Shader.import(__webpack_require__(81)); + Shader.import(__webpack_require__(82)); + + Shader.import(__webpack_require__(83)); + + var errorShader = {}; + + var DeferredRenderer = Base.extend(function () { + + var fullQuadVertex = Shader.source('qtek.compositor.vertex'); + var lightVolumeVertex = Shader.source('qtek.deferred.light_volume.vertex'); + + var directionalLightShader = new Shader({ + vertex: fullQuadVertex, + fragment: Shader.source('qtek.deferred.directional_light') + }); + var directionalLightShaderWithShadow = directionalLightShader.clone(); + directionalLightShaderWithShadow.define('fragment', 'SHADOWMAP_ENABLED'); + + var lightAccumulateBlendFunc = function (gl) { + gl.blendEquation(gl.FUNC_ADD); + gl.blendFunc(gl.ONE, gl.ONE); + }; + + var createLightPassMat = function (shader) { + return new Material({ + shader: shader, + blend: lightAccumulateBlendFunc, + transparent: true, + depthMask: false + }); + }; + + var createVolumeShader = function (name, enableShadow) { + var shader = new Shader({ + vertex: lightVolumeVertex, + fragment: Shader.source('qtek.deferred.' + name) + }); + if (enableShadow) { + shader.define('fragment', 'SHADOWMAP_ENABLED'); + } + return shader; + }; + + // Rotate and positioning to fit the spot light + // Which the cusp of cone pointing to the positive z + // and positioned on the origin + var coneGeo = new ConeGeo({ + capSegments: 10 + }); + var mat = new Matrix4(); + mat.rotateX(Math.PI / 2) + .translate(new Vector3(0, -1, 0)); + + coneGeo.applyTransform(mat); + + var cylinderGeo = new CylinderGeo({ + capSegments: 10 + }); + // Align with x axis + mat.identity().rotateZ(Math.PI / 2); + cylinderGeo.applyTransform(mat); + + return { + + shadowMapPass: null, + + autoResize: true, + + _createLightPassMat: createLightPassMat, + + _gBuffer: new GBuffer(), + + _lightAccumFrameBuffer: new FrameBuffer({ + depthBuffer: false + }), + + _lightAccumTex: new Texture2D({ + // FIXME Device not support float texture + type: Texture.HALF_FLOAT, + minFilter: Texture.NEAREST, + magFilter: Texture.NEAREST + }), + + _fullQuadPass: new FullQuadPass({ + blendWithPrevious: true + }), + + _directionalLightMat: createLightPassMat(directionalLightShader), + _directionalLightMatWithShadow: createLightPassMat(directionalLightShaderWithShadow), + + _ambientMat: createLightPassMat(new Shader({ + vertex: fullQuadVertex, + fragment: Shader.source('qtek.deferred.ambient_light') + })), + _ambientSHMat: createLightPassMat(new Shader({ + vertex: fullQuadVertex, + fragment: Shader.source('qtek.deferred.ambient_sh_light') + })), + _ambientCubemapMat: createLightPassMat(new Shader({ + vertex: fullQuadVertex, + fragment: Shader.source('qtek.deferred.ambient_cubemap_light') + })), + + _spotLightShader: createVolumeShader('spot_light'), + _pointLightShader: createVolumeShader('point_light'), + _spotLightShaderWithShadow: createVolumeShader('spot_light', true), + _pointLightShaderWithShadow: createVolumeShader('point_light', true), + + _sphereLightShader: createVolumeShader('sphere_light'), + _tubeLightShader: createVolumeShader('tube_light'), + + _lightSphereGeo: new SphereGeo({ + widthSegments: 10, + heightSegements: 10 + }), + + _lightConeGeo: coneGeo, + + _lightCylinderGeo: cylinderGeo, + + _outputPass: new FullQuadPass({ + fragment: Shader.source('qtek.compositor.output') + }) + }; + }, { + /** + * @param {qtek.Renderer} renderer + * @param {qtek.Scene} scene + * @param {qtek.Camera} camera + * @param {Object} [opts] + * @param {boolean} [opts.renderToTarget = false] + * @param {boolean} [opts.notUpdateShadow = true] + * @param {boolean} [opts.notUpdateScene = true] + */ + render: function (renderer, scene, camera, opts) { + + opts = opts || {}; + opts.renderToTarget = opts.renderToTarget || false; + opts.notUpdateShadow = opts.notUpdateShadow || false; + opts.notUpdateScene = opts.notUpdateScene || false; + + if (!opts.notUpdateScene) { + scene.update(false, true); + } + + camera.update(true); + + // PENDING For stereo rendering + var dpr = renderer.getDevicePixelRatio(); + if (this.autoResize + && (renderer.getWidth() * dpr !== this._lightAccumTex.width + || renderer.getHeight() * dpr !== this._lightAccumTex.height) + ) { + this.resize(renderer.getWidth() * dpr, renderer.getHeight() * dpr); + } + + this._gBuffer.update(renderer, scene, camera); + + // Accumulate light buffer + this._accumulateLightBuffer(renderer, scene, camera, !opts.notUpdateShadow); + + if (!opts.renderToTarget) { + this._outputPass.setUniform('texture', this._lightAccumTex); + + this._outputPass.render(renderer); + // this._gBuffer.renderDebug(renderer, camera, 'normal'); + } + }, + + /** + * @return {qtek.Texture2D} + */ + getTargetTexture: function () { + return this._lightAccumTex; + }, + + /** + * @return {qtek.FrameBuffer} + */ + getTargetFrameBuffer: function () { + return this._lightAccumFrameBuffer; + }, + + getGBuffer: function () { + return this._gBuffer; + }, + + // TODO is dpr needed? + setViewport: function (x, y, width, height, dpr) { + this._gBuffer.setViewport(x, y, width, height, dpr); + this._lightAccumFrameBuffer.viewport = this._gBuffer.getViewport(); + }, + + // getFullQuadLightPass: function () { + // return this._fullQuadPass; + // }, + + resize: function (width, height) { + this._lightAccumTex.width = width; + this._lightAccumTex.height = height; + this._lightAccumTex.dirty(); + + // PENDING viewport ? + this._gBuffer.resize(width, height); + }, + + _accumulateLightBuffer: function (renderer, scene, camera, updateShadow) { + var gl = renderer.gl; + var lightAccumTex = this._lightAccumTex; + var lightAccumFrameBuffer = this._lightAccumFrameBuffer; + + var eyePosition = camera.getWorldPosition()._array; + + // Update volume meshes + for (var i = 0; i < scene.lights.length; i++) { + this._updateLightProxy(scene.lights[i]); + } + + var shadowMapPass = this.shadowMapPass; + if (shadowMapPass && updateShadow) { + + gl.clearColor(1, 1, 1, 1); + this._prepareLightShadow(renderer, scene, camera); + } + + this.trigger('beforelightaccumulate', renderer, scene, camera, updateShadow); + + lightAccumFrameBuffer.attach(lightAccumTex); + lightAccumFrameBuffer.bind(renderer); + var clearColor = renderer.clearColor; + + var viewport = lightAccumFrameBuffer.viewport; + if (viewport) { + var dpr = viewport.devicePixelRatio; + // use scissor to make sure only clear the viewport + gl.enable(gl.SCISSOR_TEST); + gl.scissor(viewport.x * dpr, viewport.y * dpr, viewport.width * dpr, viewport.height * dpr); + } + gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.enable(gl.BLEND); + if (viewport) { + gl.disable(gl.SCISSOR_TEST); + } + + this.trigger('startlightaccumulate', renderer, scene, camera); + + var viewProjectionInv = new Matrix4(); + Matrix4.multiply(viewProjectionInv, camera.worldTransform, camera.invProjectionMatrix); + + var volumeMeshList = []; + + for (var i = 0; i < scene.lights.length; i++) { + var light = scene.lights[i]; + var uTpl = light.uniformTemplates; + + var volumeMesh = light.volumeMesh || light.__volumeMesh; + + if (volumeMesh) { + var material = volumeMesh.material; + // Volume mesh will affect the scene bounding box when rendering + // if castShadow is true + volumeMesh.castShadow = false; + + var unknownLightType = false; + switch (light.type) { + case 'POINT_LIGHT': + material.setUniform('lightColor', uTpl.pointLightColor.value(light)); + material.setUniform('lightRange', uTpl.pointLightRange.value(light)); + material.setUniform('lightPosition', uTpl.pointLightPosition.value(light)); + break; + case 'SPOT_LIGHT': + material.setUniform('lightPosition', uTpl.spotLightPosition.value(light)); + material.setUniform('lightColor', uTpl.spotLightColor.value(light)); + material.setUniform('lightRange', uTpl.spotLightRange.value(light)); + material.setUniform('lightDirection', uTpl.spotLightDirection.value(light)); + material.setUniform('umbraAngleCosine', uTpl.spotLightUmbraAngleCosine.value(light)); + material.setUniform('penumbraAngleCosine', uTpl.spotLightPenumbraAngleCosine.value(light)); + material.setUniform('falloffFactor', uTpl.spotLightFalloffFactor.value(light)); + break; + case 'SPHERE_LIGHT': + material.setUniform('lightColor', uTpl.sphereLightColor.value(light)); + material.setUniform('lightRange', uTpl.sphereLightRange.value(light)); + material.setUniform('lightRadius', uTpl.sphereLightRadius.value(light)); + material.setUniform('lightPosition', uTpl.sphereLightPosition.value(light)); + break; + case 'TUBE_LIGHT': + material.setUniform('lightColor', uTpl.tubeLightColor.value(light)); + material.setUniform('lightRange', uTpl.tubeLightRange.value(light)); + material.setUniform('lightExtend', uTpl.tubeLightExtend.value(light)); + material.setUniform('lightPosition', uTpl.tubeLightPosition.value(light)); + break; + default: + unknownLightType = true; + } + + if (unknownLightType) { + continue; + } + + material.setUniform('eyePosition', eyePosition); + material.setUniform('viewProjectionInv', viewProjectionInv._array); + material.setUniform('gBufferTexture1', this._gBuffer.getTargetTexture1()); + material.setUniform('gBufferTexture2', this._gBuffer.getTargetTexture2()); + material.setUniform('gBufferTexture3', this._gBuffer.getTargetTexture3()); + + volumeMeshList.push(volumeMesh); + + } + else { + var pass = this._fullQuadPass; + var unknownLightType = false; + // Full quad light + switch (light.type) { + case 'AMBIENT_LIGHT': + pass.material = this._ambientMat; + pass.material.setUniform('lightColor', uTpl.ambientLightColor.value(light)); + break; + case 'AMBIENT_SH_LIGHT': + pass.material = this._ambientSHMat; + pass.material.setUniform('lightColor', uTpl.ambientSHLightColor.value(light)); + pass.material.setUniform('lightCoefficients', uTpl.ambientSHLightCoefficients.value(light)); + break; + case 'AMBIENT_CUBEMAP_LIGHT': + pass.material = this._ambientCubemapMat; + pass.material.setUniform('lightColor', uTpl.ambientCubemapLightColor.value(light)); + pass.material.setUniform('lightCubemap', uTpl.ambientCubemapLightCubemap.value(light)); + pass.material.setUniform('brdfLookup', uTpl.ambientCubemapLightBRDFLookup.value(light)); + break; + case 'DIRECTIONAL_LIGHT': + var hasShadow = shadowMapPass && light.castShadow; + pass.material = hasShadow + ? this._directionalLightMatWithShadow + : this._directionalLightMat; + if (hasShadow) { + pass.material.shader.define('fragment', 'SHADOW_CASCADE', light.shadowCascade); + } + pass.material.setUniform('lightColor', uTpl.directionalLightColor.value(light)); + pass.material.setUniform('lightDirection', uTpl.directionalLightDirection.value(light)); + break; + default: + // Unkonw light type + unknownLightType = true; + } + if (unknownLightType) { + continue; + } + + var passMaterial = pass.material; + passMaterial.setUniform('eyePosition', eyePosition); + passMaterial.setUniform('viewProjectionInv', viewProjectionInv._array); + passMaterial.setUniform('gBufferTexture1', this._gBuffer.getTargetTexture1()); + passMaterial.setUniform('gBufferTexture2', this._gBuffer.getTargetTexture2()); + passMaterial.setUniform('gBufferTexture3', this._gBuffer.getTargetTexture3()); + + // TODO + if (shadowMapPass && light.castShadow) { + passMaterial.setUniform('lightShadowMap', light.__shadowMap); + passMaterial.setUniform('lightMatrices', light.__lightMatrices); + passMaterial.setUniform('shadowCascadeClipsNear', light.__cascadeClipsNear); + passMaterial.setUniform('shadowCascadeClipsFar', light.__cascadeClipsFar); + + passMaterial.setUniform('lightShadowMapSize', light.shadowResolution); + } + + pass.renderQuad(renderer); + } + } + + this._renderVolumeMeshList(renderer, camera, volumeMeshList); + + // if (shadowMapPass && updateShadow) { // FIXME Extension may have shadow rendered ignore updateShadow flag + if (shadowMapPass && this._shadowCasters) { + shadowMapPass.restoreMaterial( + this._shadowCasters + ); + } + + this.trigger('lightaccumulate', renderer, scene, camera); + + lightAccumFrameBuffer.unbind(renderer); + + this.trigger('afterlightaccumulate', renderer, scene, camera); + + }, + + _prepareLightShadow: (function () { + var worldView = new Matrix4(); + return function (renderer, scene, camera) { + var shadowCasters; + + shadowCasters = this._shadowCasters || (this._shadowCasters = []); + var count = 0; + var queue = scene.opaqueQueue; + for (var i = 0; i < queue.length; i++) { + if (queue[i].castShadow) { + shadowCasters[count++] = queue[i]; + } + } + shadowCasters.length = count; + + this.shadowMapPass.saveMaterial(shadowCasters); + + for (var i = 0; i < scene.lights.length; i++) { + var light = scene.lights[i]; + var volumeMesh = light.volumeMesh || light.__volumeMesh; + if (!light.castShadow) { + continue; + } + + switch (light.type) { + case 'POINT_LIGHT': + case 'SPOT_LIGHT': + // Frustum culling + Matrix4.multiply(worldView, camera.viewMatrix, volumeMesh.worldTransform); + if (renderer.isFrustumCulled( + volumeMesh, null, camera, worldView._array, camera.projectionMatrix._array + )) { + continue; + } + + this._prepareSingleLightShadow( + renderer, scene, camera, light, shadowCasters, volumeMesh.material + ); + break; + case 'DIRECTIONAL_LIGHT': + this._prepareSingleLightShadow( + renderer, scene, camera, light, shadowCasters, null + ); + } + } + }; + })(), + + _prepareSingleLightShadow: function (renderer, scene, camera, light, casters, material) { + switch (light.type) { + case 'POINT_LIGHT': + var shadowMaps = []; + this.shadowMapPass.renderPointLightShadow( + renderer, light, casters, shadowMaps + ); + material.setUniform('lightShadowMap', shadowMaps[0]); + material.setUniform('lightShadowMapSize', light.shadowResolution); + break; + case 'SPOT_LIGHT': + var shadowMaps = []; + var lightMatrices = []; + this.shadowMapPass.renderSpotLightShadow( + renderer, light, casters, lightMatrices, shadowMaps + ); + material.setUniform('lightShadowMap', shadowMaps[0]); + material.setUniform('lightMatrix', lightMatrices[0]); + material.setUniform('lightShadowMapSize', light.shadowResolution); + break; + case 'DIRECTIONAL_LIGHT': + var shadowMaps = []; + var lightMatrices = []; + var cascadeClips = []; + this.shadowMapPass.renderDirectionalLightShadow( + renderer, scene, camera, light, casters, cascadeClips, lightMatrices, shadowMaps + ); + var cascadeClipsNear = cascadeClips.slice(); + var cascadeClipsFar = cascadeClips.slice(); + cascadeClipsNear.pop(); + cascadeClipsFar.shift(); + + // Iterate from far to near + cascadeClipsNear.reverse(); + cascadeClipsFar.reverse(); + lightMatrices.reverse(); + + light.__cascadeClipsNear = cascadeClipsNear; + light.__cascadeClipsFar = cascadeClipsFar; + light.__shadowMap = shadowMaps[0]; + light.__lightMatrices = lightMatrices; + break; + } + }, + + // Update light volume mesh + // Light volume mesh is rendered in light accumulate pass instead of full quad. + // It will reduce pixels significantly when local light is relatively small. + // And we can use custom volume mesh to shape the light. + // + // See "Deferred Shading Optimizations" in GDC2011 + _updateLightProxy: function (light) { + var volumeMesh; + if (light.volumeMesh) { + volumeMesh = light.volumeMesh; + } + else { + var hasShadow = this.shadowMapPass && light.castShadow; + switch (light.type) { + // Only local light (point and spot) needs volume mesh. + // Directional and ambient light renders in full quad + case 'POINT_LIGHT': + case 'SPHERE_LIGHT': + // Volume mesh created automatically + var shader = light.type === 'SPHERE_LIGHT' + ? this._sphereLightShader + : (hasShadow ? this._pointLightShaderWithShadow : this._pointLightShader); + light.__volumeMesh = light.__volumeMesh || new Mesh({ + material: this._createLightPassMat(shader), + geometry: this._lightSphereGeo, + // Disable culling + // if light volume mesh intersect camera near plane + // We need mesh inside can still be rendered + culling: false + }); + volumeMesh = light.__volumeMesh; + // castShadow changed + if (volumeMesh.material.shader !== shader) { + volumeMesh.material.attachShader(shader, true); + } + var r = light.range + (light.radius || 0); + volumeMesh.scale.set(r, r, r); + break; + case 'SPOT_LIGHT': + var shader = hasShadow ? this._spotLightShaderWithShadow : this._spotLightShader; + light.__volumeMesh = light.__volumeMesh || new Mesh({ + material: this._createLightPassMat(shader), + geometry: this._lightConeGeo, + culling: false + }); + volumeMesh = light.__volumeMesh; + // castShadow changed + if (volumeMesh.material.shader !== shader) { + volumeMesh.material.attachShader(shader, true); + } + + var aspect = Math.tan(light.penumbraAngle * Math.PI / 180); + var range = light.range; + volumeMesh.scale.set(aspect * range, aspect * range, range / 2); + break; + case 'TUBE_LIGHT': + light.__volumeMesh = light.__volumeMesh || new Mesh({ + material: this._createLightPassMat(this._tubeLightShader), + geometry: this._lightCylinderGeo, + culling: false + }); + volumeMesh = light.__volumeMesh; + var range = light.range; + volumeMesh.scale.set(light.length / 2 + range, range, range); + break; + } + } + if (volumeMesh) { + volumeMesh.update(); + // Apply light transform + Matrix4.multiply(volumeMesh.worldTransform, light.worldTransform, volumeMesh.worldTransform); + } + }, + + _renderVolumeMeshList: (function () { + var worldViewProjection = new Matrix4(); + var worldView = new Matrix4(); + var preZMaterial = new Material({ + shader: new Shader({ + vertex: Shader.source('qtek.prez.vertex'), + fragment: Shader.source('qtek.prez.fragment') + }) + }); + return function (renderer, camera, volumeMeshList) { + var gl = renderer.gl; + + gl.enable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.blendEquation(gl.FUNC_ADD); + gl.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.ONE); + gl.depthFunc(gl.LEQUAL); + + var viewport = renderer.viewport; + var dpr = viewport.devicePixelRatio; + var viewportUniform = [ + viewport.x * dpr, viewport.y * dpr, + viewport.width * dpr, viewport.height * dpr + ]; + + var windowSizeUniform = [ + this._lightAccumTex.width, + this._lightAccumTex.height + ]; + + for (var i = 0; i < volumeMeshList.length; i++) { + var volumeMesh = volumeMeshList[i]; + + // Frustum culling + Matrix4.multiply(worldView, camera.viewMatrix, volumeMesh.worldTransform); + if (renderer.isFrustumCulled( + volumeMesh, null, camera, worldView._array, camera.projectionMatrix._array + )) { + continue; + } + + // Use prez to avoid one pixel rendered twice + gl.colorMask(false, false, false, false); + gl.depthMask(true); + // depthMask must be enabled before clear DEPTH_BUFFER + gl.clear(gl.DEPTH_BUFFER_BIT); + + Matrix4.multiply(worldViewProjection, camera.projectionMatrix, worldView); + + var prezShader = preZMaterial.shader; + this._bindShader(renderer, prezShader); + + var semanticInfo = prezShader.matrixSemantics.WORLDVIEWPROJECTION; + prezShader.setUniform(gl, semanticInfo.type, semanticInfo.symbol, worldViewProjection._array); + volumeMesh.render(gl, prezShader); + + // Render light + gl.colorMask(true, true, true, true); + gl.depthMask(false); + var shader = volumeMesh.material.shader; + this._bindShader(renderer, shader); + + var semanticInfo = shader.matrixSemantics.WORLDVIEWPROJECTION; + // Set some common uniforms + shader.setUniform(gl, semanticInfo.type, semanticInfo.symbol, worldViewProjection._array); + shader.setUniformOfSemantic(gl, 'WINDOW_SIZE', windowSizeUniform); + shader.setUniformOfSemantic(gl, 'VIEWPORT', viewportUniform); + + volumeMesh.material.bind(gl); + volumeMesh.render(gl, shader); + } + + gl.depthFunc(gl.LESS); + + renderer.resetRenderStatus(); + }; + })(), + + _bindShader: function (renderer, shader) { + var errMsg = shader.bind(renderer.gl); + if (errMsg) { + + if (errorShader[shader.__GUID__]) { + return; + } + errorShader[shader.__GUID__] = true; + + if (renderer.throwError) { + throw new Error(errMsg); + } + else { + renderer.trigger('error', errMsg); + } + } + }, + + + /** + * @param {qtek.Renderer|WebGLRenderingContext} [renderer] + */ + dispose: function (renderer) { + var gl = renderer.gl || renderer; + + this._gBuffer.dispose(gl); + + this._lightAccumFrameBuffer.dispose(gl); + this._lightAccumTex.dispose(gl); + + this._pointLightShader.dispose(gl); + this._pointLightShaderWithShadow.dispose(gl); + this._spotLightShader.dispose(gl); + this._spotLightShaderWithShadow.dispose(gl); + this._sphereLightShader.dispose(gl); + this._tubeLightShader.dispose(gl); + + this._lightConeGeo.dispose(gl); + this._lightCylinderGeo.dispose(gl); + this._lightSphereGeo.dispose(gl); + + this._fullQuadPass.dispose(gl); + this._outputPass.dispose(gl); + + this._directionalLightMat.dispose(gl); + this._directionalLightMatWithShadow.dispose(gl); + + this.shadowMapPass.dispose(gl); + } + }); + + // DeferredRenderer.registerLight = function () { + + // }; + + module.exports = DeferredRenderer; + + +/***/ }, +/* 70 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var StaticGeometry = __webpack_require__(49); + var glMatrix = __webpack_require__(14); + var vec3 = glMatrix.vec3; + var vec2 = glMatrix.vec2; + var BoundingBox = __webpack_require__(26); + + /** + * @constructor qtek.geometry.Sphere + * @extends qtek.StaticGeometry + * @param {Object} [opt] + * @param {number} [widthSegments] + * @param {number} [heightSegments] + * @param {number} [phiStart] + * @param {number} [phiLength] + * @param {number} [thetaStart] + * @param {number} [thetaLength] + * @param {number} [radius] + */ + var Sphere = StaticGeometry.extend( + /** @lends qtek.geometry.Sphere# */ + { + /** + * @type {number} + */ + widthSegments: 20, + /** + * @type {number} + */ + heightSegments: 20, + + /** + * @type {number} + */ + phiStart: 0, + /** + * @type {number} + */ + phiLength: Math.PI * 2, + + /** + * @type {number} + */ + thetaStart: 0, + /** + * @type {number} + */ + thetaLength: Math.PI, + + /** + * @type {number} + */ + radius: 1 + + }, function() { + this.build(); + }, + /** @lends qtek.geometry.Sphere.prototype */ + { + /** + * Build sphere geometry + */ + build: function() { + var heightSegments = this.heightSegments; + var widthSegments = this.widthSegments; + + var positionAttr = this.attributes.position; + var texcoordAttr = this.attributes.texcoord0; + var normalAttr = this.attributes.normal; + + var vertexCount = (widthSegments + 1) * (heightSegments + 1); + positionAttr.init(vertexCount); + texcoordAttr.init(vertexCount); + normalAttr.init(vertexCount); + + var IndicesCtor = vertexCount > 0xffff ? Uint32Array : Uint16Array; + var indices = this.indices = new IndicesCtor(widthSegments * heightSegments * 6); + + var x, y, z, + u, v, + i, j; + + var radius = this.radius; + var phiStart = this.phiStart; + var phiLength = this.phiLength; + var thetaStart = this.thetaStart; + var thetaLength = this.thetaLength; + var radius = this.radius; + + var pos = []; + var uv = []; + var offset = 0; + var divider = 1 / radius; + for (j = 0; j <= heightSegments; j ++) { + for (i = 0; i <= widthSegments; i ++) { + u = i / widthSegments; + v = j / heightSegments; + + // X axis is inverted so texture can be mapped from left to right + x = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); + y = radius * Math.cos(thetaStart + v * thetaLength); + z = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); + + pos[0] = x; pos[1] = y; pos[2] = z; + uv[0] = u; uv[1] = v; + positionAttr.set(offset, pos); + texcoordAttr.set(offset, uv); + pos[0] *= divider; + pos[1] *= divider; + pos[2] *= divider; + normalAttr.set(offset, pos); + offset++; + } + } + + var i1, i2, i3, i4; + + var len = widthSegments + 1; + + var n = 0; + for (j = 0; j < heightSegments; j ++) { + for (i = 0; i < widthSegments; i ++) { + i2 = j * len + i; + i1 = (j * len + i + 1); + i4 = (j + 1) * len + i + 1; + i3 = (j + 1) * len + i; + + indices[n++] = i1; + indices[n++] = i2; + indices[n++] = i4; + + indices[n++] = i2; + indices[n++] = i3; + indices[n++] = i4; + } + } + + this.boundingBox = new BoundingBox(); + this.boundingBox.max.set(radius, radius, radius); + this.boundingBox.min.set(-radius, -radius, -radius); + } + }); + + module.exports = Sphere; + + +/***/ }, +/* 71 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var StaticGeometry = __webpack_require__(49); + var BoundingBox = __webpack_require__(26); + var glMatrix = __webpack_require__(14); + var vec3 = glMatrix.vec3; + var vec2 = glMatrix.vec2; + + /** + * @constructor qtek.geometry.Cone + * @extends qtek.StaticGeometry + * @param {Object} [opt] + * @param {number} [opt.topRadius] + * @param {number} [opt.bottomRadius] + * @param {number} [opt.height] + * @param {number} [opt.capSegments] + * @param {number} [opt.heightSegments] + */ + var Cone = StaticGeometry.extend( + /** @lends qtek.geometry.Cone# */ + { + /** + * @type {number} + */ + topRadius: 0, + + /** + * @type {number} + */ + bottomRadius: 1, + + /** + * @type {number} + */ + height: 2, + + /** + * @type {number} + */ + capSegments: 20, + + /** + * @type {number} + */ + heightSegments: 1 + }, function() { + this.build(); + }, + /** @lends qtek.geometry.Cone.prototype */ + { + /** + * Build cone geometry + */ + build: function() { + var positions = []; + var texcoords = []; + var faces = []; + positions.length = 0; + texcoords.length = 0; + faces.length = 0; + // Top cap + var capSegRadial = Math.PI * 2 / this.capSegments; + + var topCap = []; + var bottomCap = []; + + var r1 = this.topRadius; + var r2 = this.bottomRadius; + var y = this.height / 2; + + var c1 = vec3.fromValues(0, y, 0); + var c2 = vec3.fromValues(0, -y, 0); + for (var i = 0; i < this.capSegments; i++) { + var theta = i * capSegRadial; + var x = r1 * Math.sin(theta); + var z = r1 * Math.cos(theta); + topCap.push(vec3.fromValues(x, y, z)); + + x = r2 * Math.sin(theta); + z = r2 * Math.cos(theta); + bottomCap.push(vec3.fromValues(x, -y, z)); + } + + // Build top cap + positions.push(c1); + // FIXME + texcoords.push(vec2.fromValues(0, 1)); + var n = this.capSegments; + for (var i = 0; i < n; i++) { + positions.push(topCap[i]); + // FIXME + texcoords.push(vec2.fromValues(i / n, 0)); + faces.push([0, i+1, (i+1) % n + 1]); + } + + // Build bottom cap + var offset = positions.length; + positions.push(c2); + texcoords.push(vec2.fromValues(0, 1)); + for (var i = 0; i < n; i++) { + positions.push(bottomCap[i]); + // FIXME + texcoords.push(vec2.fromValues(i / n, 0)); + faces.push([offset, offset+((i+1) % n + 1), offset+i+1]); + } + + // Build side + offset = positions.length; + var n2 = this.heightSegments; + for (var i = 0; i < n; i++) { + for (var j = 0; j < n2+1; j++) { + var v = j / n2; + positions.push(vec3.lerp(vec3.create(), topCap[i], bottomCap[i], v)); + texcoords.push(vec2.fromValues(i / n, v)); + } + } + for (var i = 0; i < n; i++) { + for (var j = 0; j < n2; j++) { + var i1 = i * (n2 + 1) + j; + var i2 = ((i + 1) % n) * (n2 + 1) + j; + var i3 = ((i + 1) % n) * (n2 + 1) + j + 1; + var i4 = i * (n2 + 1) + j + 1; + faces.push([offset+i2, offset+i1, offset+i4]); + faces.push([offset+i4, offset+i3, offset+i2]); + } + } + + this.attributes.position.fromArray(positions); + this.attributes.texcoord0.fromArray(texcoords); + + this.initIndicesFromArray(faces); + + this.generateVertexNormals(); + + this.boundingBox = new BoundingBox(); + var r = Math.max(this.topRadius, this.bottomRadius); + this.boundingBox.min.set(-r, -this.height/2, -r); + this.boundingBox.max.set(r, this.height/2, r); + } + }); + + module.exports = Cone; + + +/***/ }, +/* 72 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var StaticGeometry = __webpack_require__(49); + var ConeGeometry = __webpack_require__(71); + + /** + * @constructor qtek.geometry.Cylinder + * @extends qtek.StaticGeometry + * @param {Object} [opt] + * @param {number} [opt.radius] + * @param {number} [opt.height] + * @param {number} [opt.capSegments] + * @param {number} [opt.heightSegments] + */ + var Cylinder = StaticGeometry.extend( + /** @lends qtek.geometry.Cylinder# */ + { + /** + * @type {number} + */ + radius: 1, + + /** + * @type {number} + */ + height: 2, + + /** + * @type {number} + */ + capSegments: 50, + + /** + * @type {number} + */ + heightSegments: 1 + }, function() { + this.build(); + }, + /** @lends qtek.geometry.Cylinder.prototype */ + { + /** + * Build cylinder geometry + */ + build: function() { + var cone = new ConeGeometry({ + topRadius: this.radius, + bottomRadius: this.radius, + capSegments: this.capSegments, + heightSegments: this.heightSegments, + height: this.height + }); + + this.attributes.position.value = cone.attributes.position.value; + this.attributes.normal.value = cone.attributes.normal.value; + this.attributes.texcoord0.value = cone.attributes.texcoord0.value; + this.indices = cone.indices; + + this.boundingBox = cone.boundingBox; + } + }); + + module.exports = Cylinder; + + +/***/ }, +/* 73 */ +/***/ function(module, exports) { + + + module.exports = "\n@export qtek.util.rand\nhighp float rand(vec2 uv) {\n const highp float a = 12.9898, b = 78.233, c = 43758.5453;\n highp float dt = dot(uv.xy, vec2(a,b)), sn = mod(dt, 3.141592653589793);\n return fract(sin(sn) * c);\n}\n@end\n\n@export qtek.util.calculate_attenuation\n\nuniform float attenuationFactor : 5.0;\n\nfloat lightAttenuation(float dist, float range)\n{\n float attenuation = 1.0;\n attenuation = dist*dist/(range*range+1.0);\n float att_s = attenuationFactor;\n attenuation = 1.0/(attenuation*att_s+1.0);\n att_s = 1.0/(att_s+1.0);\n attenuation = attenuation - att_s;\n attenuation /= 1.0 - att_s;\n return clamp(attenuation, 0.0, 1.0);\n}\n\n@end\n\n@export qtek.util.edge_factor\n\nfloat edgeFactor(float width)\n{\n vec3 d = fwidth(v_Barycentric);\n vec3 a3 = smoothstep(vec3(0.0), d * width, v_Barycentric);\n return min(min(a3.x, a3.y), a3.z);\n}\n\n@end\n\n@export qtek.util.encode_float\nvec4 encodeFloat(const in float depth)\n{\n \n \n const vec4 bitShifts = vec4(256.0*256.0*256.0, 256.0*256.0, 256.0, 1.0);\n const vec4 bit_mask = vec4(0.0, 1.0/256.0, 1.0/256.0, 1.0/256.0);\n vec4 res = fract(depth * bitShifts);\n res -= res.xxyz * bit_mask;\n\n return res;\n}\n@end\n\n@export qtek.util.decode_float\nfloat decodeFloat(const in vec4 color)\n{\n \n \n const vec4 bitShifts = vec4(1.0/(256.0*256.0*256.0), 1.0/(256.0*256.0), 1.0/256.0, 1.0);\n return dot(color, bitShifts);\n}\n@end\n\n\n@export qtek.util.float\n@import qtek.util.encode_float\n@import qtek.util.decode_float\n@end\n\n\n\n@export qtek.util.rgbm_decode\nvec3 RGBMDecode(vec4 rgbm, float range) {\n return range * rgbm.rgb * rgbm.a;\n }\n@end\n\n@export qtek.util.rgbm_encode\nvec4 RGBMEncode(vec3 color, float range) {\n if (dot(color, color) == 0.0) {\n return vec4(0.0);\n }\n vec4 rgbm;\n color /= range;\n rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 1e-6)), 0.0, 1.0);\n rgbm.a = ceil(rgbm.a * 255.0) / 255.0;\n rgbm.rgb = color / rgbm.a;\n return rgbm;\n}\n@end\n\n@export qtek.util.rgbm\n@import qtek.util.rgbm_decode\n@import qtek.util.rgbm_encode\n\nvec4 decodeHDR(vec4 color)\n{\n#if defined(RGBM_DECODE) || defined(RGBM)\n return vec4(RGBMDecode(color, 51.5), 1.0);\n#else\n return color;\n#endif\n}\n\nvec4 encodeHDR(vec4 color)\n{\n#if defined(RGBM_ENCODE) || defined(RGBM)\n return RGBMEncode(color.xyz, 51.5);\n#else\n return color;\n#endif\n}\n\n@end\n\n\n@export qtek.util.srgb\n\nvec4 sRGBToLinear(in vec4 value) {\n 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);\n}\n\nvec4 linearTosRGB(in vec4 value) {\n return vec4(mix(pow(value.rgb, vec3(0.41666)) * 1.055 - vec3(0.055), value.rgb * 12.92, vec3(lessThanEqual(value.rgb, vec3(0.0031308)))), value.w);\n}\n@end\n\n\n@export qtek.chunk.skin_matrix\n\nmat4 skinMatrixWS;\nif (joint.x >= 0.0)\n{\n skinMatrixWS = skinMatrix[int(joint.x)] * weight.x;\n}\nif (joint.y >= 0.0)\n{\n skinMatrixWS += skinMatrix[int(joint.y)] * weight.y;\n}\nif (joint.z >= 0.0)\n{\n skinMatrixWS += skinMatrix[int(joint.z)] * weight.z;\n}\nif (joint.w >= 0.0)\n{\n skinMatrixWS += skinMatrix[int(joint.w)] * (1.0-weight.x-weight.y-weight.z);\n}\n@end\n\n\n\n@export qtek.util.parallax_correct\n\nvec3 parallaxCorrect(in vec3 dir, in vec3 pos, in vec3 boxMin, in vec3 boxMax) {\n vec3 first = (boxMax - pos) / dir;\n vec3 second = (boxMin - pos) / dir;\n\n vec3 further = max(first, second);\n float dist = min(further.x, min(further.y, further.z));\n\n vec3 fixedPos = pos + dir * dist;\n vec3 boxCenter = (boxMax + boxMin) * 0.5;\n\n return normalize(fixedPos - boxCenter);\n}\n\n@end\n\n\n\n@export qtek.util.clamp_sample\nvec4 clampSample(const in sampler2D texture, const in vec2 coord)\n{\n#ifdef STEREO\n float eye = step(0.5, coord.x) * 0.5;\n vec2 coordClamped = clamp(coord, vec2(eye, 0.0), vec2(0.5 + eye, 1.0));\n#else\n vec2 coordClamped = clamp(coord, vec2(0.0), vec2(1.0));\n#endif\n return texture2D(texture, coordClamped);\n}\n@end"; + + +/***/ }, +/* 74 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.deferred.light_volume.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\nvarying vec3 v_Position;\n\nvoid main()\n{\n gl_Position = worldViewProjection * vec4(position, 1.0);\n\n v_Position = position;\n}\n\n@end"; + + +/***/ }, +/* 75 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.deferred.spot_light\n\n@import qtek.deferred.chunk.light_head\n\n@import qtek.deferred.chunk.light_equation\n\n@import qtek.util.calculate_attenuation\n\nuniform vec3 lightPosition;\nuniform vec3 lightDirection;\nuniform vec3 lightColor;\nuniform float umbraAngleCosine;\nuniform float penumbraAngleCosine;\nuniform float lightRange;\nuniform float falloffFactor;\n\nuniform vec3 eyePosition;\n\n#ifdef SHADOWMAP_ENABLED\nuniform sampler2D lightShadowMap;\nuniform mat4 lightMatrix;\nuniform float lightShadowMapSize;\n#endif\n\n@import qtek.plugin.shadow_map_common\n\nvoid main()\n{\n @import qtek.deferred.chunk.gbuffer_read\n\n vec3 L = lightPosition - position;\n vec3 V = normalize(eyePosition - position);\n\n float dist = length(L);\n L /= dist;\n\n\n float attenuation = lightAttenuation(dist, lightRange);\n float c = dot(-normalize(lightDirection), L);\n\n float falloff = clamp((c - umbraAngleCosine) / (penumbraAngleCosine - umbraAngleCosine), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n float ndv = clamp(dot(N, V), 0.0, 1.0);\n\n gl_FragColor.rgb = (1.0 - falloff) * attenuation * lightEquation(\n lightColor, diffuseColor, specularColor, ndl, ndh, ndv, glossiness\n );\n\n#ifdef SHADOWMAP_ENABLED\n float shadowContrib = computeShadowContrib(\n lightShadowMap, lightMatrix, position, lightShadowMapSize\n );\n gl_FragColor.rgb *= shadowContrib;\n#endif\n\n gl_FragColor.a = 1.0;\n}\n@end\n"; + + +/***/ }, +/* 76 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.deferred.directional_light\n\n@import qtek.deferred.chunk.light_head\n\n@import qtek.deferred.chunk.light_equation\n\nuniform vec3 lightDirection;\nuniform vec3 lightColor;\n\nuniform vec3 eyePosition;\n\n#ifdef SHADOWMAP_ENABLED\nuniform sampler2D lightShadowMap;\nuniform float lightShadowMapSize;\nuniform mat4 lightMatrices[SHADOW_CASCADE];\nuniform float shadowCascadeClipsNear[SHADOW_CASCADE];\nuniform float shadowCascadeClipsFar[SHADOW_CASCADE];\n#endif\n\n@import qtek.plugin.shadow_map_common\n\nvoid main()\n{\n @import qtek.deferred.chunk.gbuffer_read\n\n vec3 L = -normalize(lightDirection);\n vec3 V = normalize(eyePosition - position);\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n float ndv = clamp(dot(N, V), 0.0, 1.0);\n\n gl_FragColor.rgb = lightEquation(\n lightColor, diffuseColor, specularColor, ndl, ndh, ndv, glossiness\n );\n\n#ifdef SHADOWMAP_ENABLED\n float shadowContrib = 1.0;\n for (int _idx_ = 0; _idx_ < SHADOW_CASCADE; _idx_++) {{\n if (\n z >= shadowCascadeClipsNear[_idx_] &&\n z <= shadowCascadeClipsFar[_idx_]\n ) {\n shadowContrib = computeShadowContrib(\n lightShadowMap, lightMatrices[_idx_], position, lightShadowMapSize,\n vec2(1.0 / float(SHADOW_CASCADE), 1.0),\n vec2(float(_idx_) / float(SHADOW_CASCADE), 0.0)\n );\n }\n }}\n\n gl_FragColor.rgb *= shadowContrib;\n#endif\n\n gl_FragColor.a = 1.0;\n}\n@end\n"; + + +/***/ }, +/* 77 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.deferred.ambient_light\n\nuniform sampler2D gBufferTexture1;\nuniform sampler2D gBufferTexture3;\nuniform vec3 lightColor;\n\nuniform vec2 windowSize: WINDOW_SIZE;\n\nvoid main()\n{\n vec2 uv = gl_FragCoord.xy / windowSize;\n\n vec4 texel1 = texture2D(gBufferTexture1, uv);\n if (dot(texel1.rgb, vec3(1.0)) == 0.0) {\n discard;\n }\n\n vec3 albedo = texture2D(gBufferTexture3, uv).rgb;\n gl_FragColor.rgb = lightColor * albedo;\n gl_FragColor.a = 1.0;\n}\n@end"; + + +/***/ }, +/* 78 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.deferred.ambient_sh_light\n\nuniform sampler2D gBufferTexture1;\nuniform sampler2D gBufferTexture3;\n\nuniform vec3 lightColor;\nuniform vec3 lightCoefficients[9];\n\nuniform vec2 windowSize: WINDOW_SIZE;\n\nvec3 calcAmbientSHLight(vec3 N) {\n return lightCoefficients[0]\n + lightCoefficients[1] * N.x\n + lightCoefficients[2] * N.y\n + lightCoefficients[3] * N.z\n + lightCoefficients[4] * N.x * N.z\n + lightCoefficients[5] * N.z * N.y\n + lightCoefficients[6] * N.y * N.x\n + lightCoefficients[7] * (3.0 * N.z * N.z - 1.0)\n + lightCoefficients[8] * (N.x * N.x - N.y * N.y);\n}\n\nvoid main()\n{\n vec2 uv = gl_FragCoord.xy / windowSize;\n\n vec4 texel1 = texture2D(gBufferTexture1, uv);\n if (dot(texel1.rgb, vec3(1.0)) == 0.0) {\n discard;\n }\n vec3 N = texel1.rgb * 2.0 - 1.0;\n vec3 albedo = texture2D(gBufferTexture3, uv).rgb;\n gl_FragColor.rgb = lightColor * albedo * calcAmbientSHLight(N);\n gl_FragColor.a = 1.0;\n}\n@end"; + + +/***/ }, +/* 79 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.deferred.ambient_cubemap_light\n\n@import qtek.deferred.chunk.light_head\n\nuniform vec3 lightColor;\nuniform samplerCube lightCubemap;\nuniform sampler2D brdfLookup;\n\nuniform vec3 eyePosition;\n\n@import qtek.util.rgbm\n\nvoid main()\n{\n @import qtek.deferred.chunk.gbuffer_read\n\n vec3 V = normalize(eyePosition - position);\n vec3 L = reflect(-V, N);\n\n float ndv = clamp(dot(N, V), 0.0, 1.0);\n float rough = clamp(1.0 - glossiness, 0.0, 1.0);\n float bias = rough * 5.0;\n vec2 brdfParam = texture2D(brdfLookup, vec2(rough, ndv)).xy;\n vec3 envWeight = specularColor * brdfParam.x + brdfParam.y;\n\n vec3 envTexel = RGBMDecode(textureCubeLodEXT(lightCubemap, L, bias), 51.5);\n gl_FragColor.rgb = lightColor * envTexel * envWeight;\n\n gl_FragColor.a = 1.0;\n}\n@end"; + + +/***/ }, +/* 80 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.deferred.point_light\n\n@import qtek.deferred.chunk.light_head\n\n@import qtek.util.calculate_attenuation\n\n@import qtek.deferred.chunk.light_equation\n\nuniform vec3 lightPosition;\nuniform vec3 lightColor;\nuniform float lightRange;\n\nuniform vec3 eyePosition;\n\n#ifdef SHADOWMAP_ENABLED\nuniform samplerCube lightShadowMap;\nuniform float lightShadowMapSize;\n#endif\n\nvarying vec3 v_Position;\n\n@import qtek.plugin.shadow_map_common\n\nvoid main()\n{\n @import qtek.deferred.chunk.gbuffer_read\n\n vec3 L = lightPosition - position;\n vec3 V = normalize(eyePosition - position);\n\n float dist = length(L);\n L /= dist;\n\n vec3 H = normalize(L + V);\n\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n float ndv = clamp(dot(N, V), 0.0, 1.0);\n float attenuation = lightAttenuation(dist, lightRange);\n gl_FragColor.rgb = attenuation * lightEquation(\n lightColor, diffuseColor, specularColor, ndl, ndh, ndv, glossiness\n );\n\n#ifdef SHADOWMAP_ENABLED\n float shadowContrib = computeShadowContribOmni(\n lightShadowMap, -L * dist, lightRange\n );\n gl_FragColor.rgb *= clamp(shadowContrib, 0.0, 1.0);\n#endif\n\n gl_FragColor.a = 1.0;\n}\n@end"; + + +/***/ }, +/* 81 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.deferred.sphere_light\n\n@import qtek.deferred.chunk.light_head\n\n@import qtek.util.calculate_attenuation\n\n@import qtek.deferred.chunk.light_equation\n\nuniform vec3 lightPosition;\nuniform vec3 lightColor;\nuniform float lightRange;\nuniform float lightRadius;\n\nuniform vec3 eyePosition;\n\nvarying vec3 v_Position;\n\nvoid main()\n{\n @import qtek.deferred.chunk.gbuffer_read\n\n\n vec3 L = lightPosition - position;\n\n vec3 V = normalize(eyePosition - position);\n\n float dist = length(L);\n vec3 R = reflect(V, N);\n float tmp = dot(L, R);\n vec3 cToR = tmp * R - L;\n float d = length(cToR);\n L = L + cToR * clamp(lightRadius / d, 0.0, 1.0);\n\n L = normalize(L);\n\n vec3 H = normalize(L + V);\n\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n float ndv = clamp(dot(N, V), 0.0, 1.0);\n float attenuation = lightAttenuation(dist, lightRange);\n gl_FragColor.rgb = lightColor * ndl * attenuation;\n\n glossiness = clamp(glossiness - lightRadius / 2.0 / dist, 0.0, 1.0);\n\n gl_FragColor.rgb = attenuation * lightEquation(\n lightColor, diffuseColor, specularColor, ndl, ndh, ndv, glossiness\n );\n\n gl_FragColor.a = 1.0;\n}\n@end"; + + +/***/ }, +/* 82 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.deferred.tube_light\n\n@import qtek.deferred.chunk.light_head\n\n@import qtek.util.calculate_attenuation\n\n@import qtek.deferred.chunk.light_equation\n\nuniform vec3 lightPosition;\nuniform vec3 lightColor;\nuniform float lightRange;\nuniform vec3 lightExtend;\n\nuniform vec3 eyePosition;\n\nvarying vec3 v_Position;\n\nvoid main()\n{\n @import qtek.deferred.chunk.gbuffer_read\n\n vec3 L = lightPosition - position;\n\n vec3 V = normalize(eyePosition - position);\n\n vec3 R = reflect(V, N);\n\n vec3 L0 = lightPosition - lightExtend - position;\n vec3 L1 = lightPosition + lightExtend - position;\n vec3 LD = L1 - L0;\n\n float len0 = length(L0);\n float len1 = length(L1);\n float irra = 2.0 * clamp(dot(N, L0) / (2.0 * len0) + dot(N, L1) / (2.0 * len1), 0.0, 1.0);\n\n float LDDotR = dot(R, LD);\n float t = (LDDotR * dot(R, L0) - dot(L0, LD)) / (dot(LD, LD) - LDDotR * LDDotR);\n t = clamp(t, 0.0, 1.0);\n L = L0 + t * LD;\n float dist = length(L);\n L /= dist;\n\n vec3 H = normalize(L + V);\n\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n float ndv = clamp(dot(N, V), 0.0, 1.0);\n\n glossiness = clamp(glossiness - 0.0 / 2.0 / dist, 0.0, 1.0);\n\n gl_FragColor.rgb = lightColor * irra * lightAttenuation(dist, lightRange)\n * (diffuseColor + D_Phong(glossiness, ndh) * F_Schlick(ndv, specularColor));\n\n gl_FragColor.a = 1.0;\n}\n@end"; + + +/***/ }, +/* 83 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.prez.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_COUNT] : SKIN_MATRIX;\n#endif\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n\n#ifdef SKINNING\n\n @import qtek.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n}\n\n@end\n\n\n@export qtek.prez.fragment\n\nvoid main()\n{\n gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\n}\n\n@end"; + + +/***/ }, +/* 84 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var StaticGeometry = __webpack_require__(49); + var Plane = __webpack_require__(48); + var Matrix4 = __webpack_require__(25); + var Vector3 = __webpack_require__(23); + var BoundingBox = __webpack_require__(26); + var vendor = __webpack_require__(51); + + var planeMatrix = new Matrix4(); + + /** + * @constructor qtek.geometry.Cube + * @extends qtek.StaticGeometry + * @param {Object} [opt] + * @param {number} [opt.widthSegments] + * @param {number} [opt.heightSegments] + * @param {number} [opt.depthSegments] + * @param {boolean} [opt.inside] + */ + var Cube = StaticGeometry.extend( + /**@lends qtek.geometry.Cube# */ + { + /** + * @type {number} + */ + widthSegments: 1, + /** + * @type {number} + */ + heightSegments: 1, + /** + * @type {number} + */ + depthSegments: 1, + /** + * @type {boolean} + */ + inside: false + }, function() { + this.build(); + }, + /** @lends qtek.geometry.Cube.prototype */ + { + /** + * Build cube geometry + */ + build: function() { + + var planes = { + 'px': createPlane('px', this.depthSegments, this.heightSegments), + 'nx': createPlane('nx', this.depthSegments, this.heightSegments), + 'py': createPlane('py', this.widthSegments, this.depthSegments), + 'ny': createPlane('ny', this.widthSegments, this.depthSegments), + 'pz': createPlane('pz', this.widthSegments, this.heightSegments), + 'nz': createPlane('nz', this.widthSegments, this.heightSegments), + }; + + var attrList = ['position', 'texcoord0', 'normal']; + var vertexNumber = 0; + var faceNumber = 0; + for (var pos in planes) { + vertexNumber += planes[pos].vertexCount; + faceNumber += planes[pos].indices.length; + } + for (var k = 0; k < attrList.length; k++) { + this.attributes[attrList[k]].init(vertexNumber); + } + this.indices = new vendor.Uint16Array(faceNumber); + var faceOffset = 0; + var vertexOffset = 0; + for (var pos in planes) { + var plane = planes[pos]; + for (var k = 0; k < attrList.length; k++) { + var attrName = attrList[k]; + var attrArray = plane.attributes[attrName].value; + var attrSize = plane.attributes[attrName].size; + var isNormal = attrName === 'normal'; + for (var i = 0; i < attrArray.length; i++) { + var value = attrArray[i]; + if (this.inside && isNormal) { + value = -value; + } + this.attributes[attrName].value[i + attrSize * vertexOffset] = value; + } + } + for (var i = 0; i < plane.indices.length; i++) { + this.indices[i + faceOffset] = vertexOffset + plane.indices[i]; + } + faceOffset += plane.indices.length; + vertexOffset += plane.vertexCount; + } + + this.boundingBox = new BoundingBox(); + this.boundingBox.max.set(1, 1, 1); + this.boundingBox.min.set(-1, -1, -1); + } + }); + + function createPlane(pos, widthSegments, heightSegments) { + + planeMatrix.identity(); + + var plane = new Plane({ + widthSegments: widthSegments, + heightSegments: heightSegments + }); + + switch(pos) { + case 'px': + Matrix4.translate(planeMatrix, planeMatrix, Vector3.POSITIVE_X); + Matrix4.rotateY(planeMatrix, planeMatrix, Math.PI / 2); + break; + case 'nx': + Matrix4.translate(planeMatrix, planeMatrix, Vector3.NEGATIVE_X); + Matrix4.rotateY(planeMatrix, planeMatrix, -Math.PI / 2); + break; + case 'py': + Matrix4.translate(planeMatrix, planeMatrix, Vector3.POSITIVE_Y); + Matrix4.rotateX(planeMatrix, planeMatrix, -Math.PI / 2); + break; + case 'ny': + Matrix4.translate(planeMatrix, planeMatrix, Vector3.NEGATIVE_Y); + Matrix4.rotateX(planeMatrix, planeMatrix, Math.PI / 2); + break; + case 'pz': + Matrix4.translate(planeMatrix, planeMatrix, Vector3.POSITIVE_Z); + break; + case 'nz': + Matrix4.translate(planeMatrix, planeMatrix, Vector3.NEGATIVE_Z); + Matrix4.rotateY(planeMatrix, planeMatrix, Math.PI); + break; + } + plane.applyTransform(planeMatrix); + return plane; + } + + module.exports = Cube; + + +/***/ }, +/* 85 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Base = __webpack_require__(3); + + /** + * @constructor qtek.Joint + * @extends qtek.core.Base + */ + var Joint = Base.extend( + /** @lends qtek.Joint# */ + { + // https://github.com/KhronosGroup/glTF/issues/193#issuecomment-29216576 + /** + * Joint name + * @type {string} + */ + name: '', + /** + * Index of joint in the skeleton + * @type {number} + */ + index: -1, + /** + * Index of parent joint index, -1 if it is a root joint + * @type {number} + */ + parentIndex: -1, + + /** + * Scene node attached to + * @type {qtek.Node} + */ + node: null, + + /** + * Root scene node of the skeleton, which parent node is null or don't have a joint + * @type {qtek.Node} + */ + rootNode: null + }); + + module.exports = Joint; + + +/***/ }, +/* 86 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Node = __webpack_require__(22); + + /** + * @constructor qtek.Light + * @extends qtek.Node + */ + var Light = Node.extend(function(){ + return /** @lends qtek.Light# */ { + /** + * Light RGB color + * @type {number[]} + */ + color: [1, 1, 1], + + /** + * Light intensity + * @type {number} + */ + intensity: 1.0, + + // Config for shadow map + /** + * If light cast shadow + * @type {boolean} + */ + castShadow: true, + + /** + * Shadow map size + * @type {number} + */ + shadowResolution: 512, + + /** + * Light group, shader with same `lightGroup` will be affected + * + * Only useful in forward rendering + * @type {number} + */ + group: 0 + }; + }, + /** @lends qtek.Light.prototype. */ + { + /** + * Light type + * @type {string} + * @memberOf qtek.Light# + */ + type: '', + + /** + * @return {qtek.Light} + * @memberOf qtek.Light.prototype + */ + clone: function() { + var light = Node.prototype.clone.call(this); + light.color = Array.prototype.slice.call(this.color); + light.intensity = this.intensity; + light.castShadow = this.castShadow; + light.shadowResolution = this.shadowResolution; + + return light; + } + }); + + module.exports = Light; + + +/***/ }, +/* 87 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Light = __webpack_require__(86); + + /** + * @constructor qtek.light.Ambient + * @extends qtek.Light + */ + var AmbientLight = Light.extend({ + + castShadow: false + + }, { + + type: 'AMBIENT_LIGHT', + + uniformTemplates: { + ambientLightColor: { + type: '3f', + value: function(instance) { + var color = instance.color; + var intensity = instance.intensity; + return [color[0]*intensity, color[1]*intensity, color[2]*intensity]; + } + } + } + /** + * @method + * @name clone + * @return {qtek.light.Ambient} + * @memberOf qtek.light.Ambient.prototype + */ + }); + + module.exports = AmbientLight; + + +/***/ }, +/* 88 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + // https://docs.unrealengine.com/latest/INT/Engine/Rendering/LightingAndShadows/AmbientCubemap/define(function(require) { + + + var Light = __webpack_require__(86); + var cubemapUtil = __webpack_require__(89); + + /** + * @constructor qtek.light.AmbientCubemap + * @extends qtek.Light + */ + var AmbientCubemapLight = Light.extend({ + + /** + * @type {qtek.TextureCube} + */ + cubemap: null, + + // TODO + // range: 100, + + castShadow: false, + + _normalDistribution: null, + _brdfLookup: null + + }, { + + type: 'AMBIENT_CUBEMAP_LIGHT', + + prefilter: function (renderer) { + if (!this._brdfLookup) { + this._normalDistribution = cubemapUtil.generateNormalDistribution(); + this._brdfLookup = cubemapUtil.integrateBRDF(renderer, this._normalDistribution); + } + var cubemap = this.cubemap; + if (cubemap.__prefiltered) { + return; + } + + var result = cubemapUtil.prefilterEnvironmentMap( + renderer, cubemap, { + encodeRGBM: true + }, this._normalDistribution, this._brdfLookup + ); + this.cubemap = result.environmentMap; + this.cubemap.__prefiltered = true; + + cubemap.dispose(renderer.gl); + }, + + uniformTemplates: { + ambientCubemapLightColor: { + type: '3f', + value: function (instance) { + var color = instance.color; + var intensity = instance.intensity; + return [color[0]*intensity, color[1]*intensity, color[2]*intensity]; + } + }, + + ambientCubemapLightCubemap: { + type: 't', + value: function (instance) { + return instance.cubemap; + } + }, + + ambientCubemapLightBRDFLookup: { + type: 't', + value: function (instance) { + return instance._brdfLookup; + } + } + } + /** + * @method + * @name clone + * @return {qtek.light.AmbientCubemap} + * @memberOf qtek.light.AmbientCubemap.prototype + */ + }); + + module.exports = AmbientCubemapLight; + + +/***/ }, +/* 89 */ +/***/ function(module, exports, __webpack_require__) { + + // Cubemap prefilter utility + // http://www.unrealengine.com/files/downloads/2013SiggraphPresentationsNotes.pdf + // http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html + + + var Texture2D = __webpack_require__(39); + var TextureCube = __webpack_require__(45); + var Texture = __webpack_require__(40); + var FrameBuffer = __webpack_require__(44); + var Pass = __webpack_require__(47); + var Material = __webpack_require__(53); + var Shader = __webpack_require__(52); + var Skybox = __webpack_require__(90); + var Scene = __webpack_require__(92); + var EnvironmentMapPass = __webpack_require__(93); + var vendor = __webpack_require__(51); + var textureUtil = __webpack_require__(94); + + var integrateBRDFShaderCode = __webpack_require__(99); + var prefilterFragCode = __webpack_require__(100); + + var cubemapUtil = {}; + + var targets = ['px', 'nx', 'py', 'ny', 'pz', 'nz']; + + /** + * @param {qtek.Renderer} renderer + * @param {qtek.Texture} envMap + * @param {Object} [textureOpts] + * @param {number} [textureOpts.width] + * @param {number} [textureOpts.height] + * @param {number} [textureOpts.type] + * @param {boolean} [textureOpts.encodeRGBM] + * @param {boolean} [textureOpts.decodeRGBM] + * @param {qtek.Texture2D} [normalDistribution] + * @param {qtek.Texture2D} [brdfLookup] + */ + cubemapUtil.prefilterEnvironmentMap = function ( + renderer, envMap, textureOpts, normalDistribution, brdfLookup + ) { + // Not create other renderer, it is easy having issue of cross reference of resources like framebuffer + // PENDING preserveDrawingBuffer? + if (!brdfLookup || !normalDistribution) { + normalDistribution = cubemapUtil.generateNormalDistribution(); + brdfLookup = cubemapUtil.integrateBRDF(renderer, normalDistribution); + } + textureOpts = textureOpts || {}; + + var width = textureOpts.width || 64; + var height = textureOpts.height || 64; + + var textureType = textureOpts.type || envMap.type; + + // Use same type with given envMap + var prefilteredCubeMap = new TextureCube({ + width: width, + height: height, + type: textureType, + flipY: false, + mipmaps: [] + }); + + if (!prefilteredCubeMap.isPowerOfTwo()) { + console.warn('Width and height must be power of two to enable mipmap.'); + } + + var size = Math.min(width, height); + var mipmapNum = Math.log(size) / Math.log(2) + 1; + + var prefilterMaterial = new Material({ + shader: new Shader({ + vertex: Shader.source('qtek.skybox.vertex'), + fragment: prefilterFragCode + }) + }); + prefilterMaterial.set('normalDistribution', normalDistribution); + + textureOpts.encodeRGBM && prefilterMaterial.shader.define('fragment', 'RGBM_ENCODE'); + textureOpts.decodeRGBM && prefilterMaterial.shader.define('fragment', 'RGBM_DECODE'); + + var dummyScene = new Scene(); + var skyEnv; + + if (envMap instanceof Texture2D) { + // Convert panorama to cubemap + var envCubemap = new TextureCube({ + width: width, + height: height, + // FIXME FLOAT type will cause GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT error on iOS + type: textureType === Texture.FLOAT ? + Texture.HALF_FLOAT : textureType + }); + textureUtil.panoramaToCubeMap(renderer, envMap, envCubemap, { + // PENDING encodeRGBM so it can be decoded as RGBM + encodeRGBM: textureOpts.decodeRGBM + }); + envMap = envCubemap; + } + skyEnv = new Skybox({ + scene: dummyScene, + material: prefilterMaterial + }); + skyEnv.material.set('environmentMap', envMap); + + var envMapPass = new EnvironmentMapPass({ + texture: prefilteredCubeMap + }); + + // Force to be UNSIGNED_BYTE + if (textureOpts.encodeRGBM) { + textureType = prefilteredCubeMap.type = Texture.UNSIGNED_BYTE; + } + + var renderTargetTmp = new Texture2D({ + width: width, + height: height, + type: textureType + }); + var frameBuffer = new FrameBuffer({ + depthBuffer: false + }); + var ArrayCtor = vendor[textureType === Texture.UNSIGNED_BYTE ? 'Uint8Array' : 'Float32Array']; + for (var i = 0; i < mipmapNum; i++) { + prefilteredCubeMap.mipmaps[i] = { + pixels: {} + }; + skyEnv.material.set('roughness', i / (targets.length - 1)); + + // Tweak fov + // http://the-witness.net/news/2012/02/seamless-cube-map-filtering/ + var n = renderTargetTmp.width; + var fov = 2 * Math.atan(n / (n - 0.5)) / Math.PI * 180; + + for (var j = 0; j < targets.length; j++) { + var pixels = new ArrayCtor(renderTargetTmp.width * renderTargetTmp.height * 4); + frameBuffer.attach(renderTargetTmp); + frameBuffer.bind(renderer); + + var camera = envMapPass.getCamera(targets[j]); + camera.fov = fov; + renderer.render(dummyScene, camera); + renderer.gl.readPixels( + 0, 0, renderTargetTmp.width, renderTargetTmp.height, + Texture.RGBA, textureType, pixels + ); + + // var canvas = document.createElement('canvas'); + // var ctx = canvas.getContext('2d'); + // canvas.width = renderTargetTmp.width; + // canvas.height = renderTargetTmp.height; + // var imageData = ctx.createImageData(renderTargetTmp.width, renderTargetTmp.height); + // for (var k = 0; k < pixels.length; k++) { + // imageData.data[k] = pixels[k]; + // } + // ctx.putImageData(imageData, 0, 0); + // document.body.appendChild(canvas); + + frameBuffer.unbind(renderer); + prefilteredCubeMap.mipmaps[i].pixels[targets[j]] = pixels; + } + + renderTargetTmp.width /= 2; + renderTargetTmp.height /= 2; + renderTargetTmp.dirty(); + } + + frameBuffer.dispose(renderer.gl); + renderTargetTmp.dispose(renderer.gl); + skyEnv.dispose(renderer.gl); + // Remove gpu resource allucated in renderer + normalDistribution.dispose(renderer.gl); + + // renderer.dispose(); + + return { + environmentMap: prefilteredCubeMap, + brdfLookup: brdfLookup, + normalDistribution: normalDistribution, + maxMipmapLevel: mipmapNum + }; + }; + + cubemapUtil.integrateBRDF = function (renderer, normalDistribution) { + normalDistribution = normalDistribution || cubemapUtil.generateNormalDistribution(); + var framebuffer = new FrameBuffer({ + depthBuffer: false + }); + var pass = new Pass({ + fragment: integrateBRDFShaderCode + }); + + var texture = new Texture2D({ + width: 512, + height: 256, + type: Texture.HALF_FLOAT, + minFilter: Texture.NEAREST, + magFilter: Texture.NEAREST, + useMipmap: false + }); + pass.setUniform('normalDistribution', normalDistribution); + pass.setUniform('viewportSize', [512, 256]); + pass.attachOutput(texture); + pass.render(renderer, framebuffer); + + // FIXME Only chrome and firefox can readPixels with float type. + // framebuffer.bind(renderer); + // var pixels = new Float32Array(512 * 256 * 4); + // renderer.gl.readPixels( + // 0, 0, texture.width, texture.height, + // Texture.RGBA, Texture.FLOAT, pixels + // ); + // texture.pixels = pixels; + // texture.flipY = false; + // texture.dirty(); + // framebuffer.unbind(renderer); + + framebuffer.dispose(renderer.gl); + + return texture; + }; + + cubemapUtil.generateNormalDistribution = function (roughnessLevels, sampleSize) { + + // http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html + // GLSL not support bit operation, use lookup instead + // V -> i / N, U -> roughness + var roughnessLevels = roughnessLevels || 256; + var sampleSize = sampleSize || 1024; + + var normalDistribution = new Texture2D({ + width: roughnessLevels, + height: sampleSize, + type: Texture.FLOAT, + minFilter: Texture.NEAREST, + magFilter: Texture.NEAREST, + useMipmap: false + }); + var pixels = new Float32Array(sampleSize * roughnessLevels * 4); + for (var i = 0; i < sampleSize; i++) { + var x = i / sampleSize; + // http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators + // http://stackoverflow.com/questions/1908492/unsigned-integer-in-javascript + // http://stackoverflow.com/questions/1822350/what-is-the-javascript-operator-and-how-do-you-use-it + var y = (i << 16 | i >>> 16) >>> 0; + y = ((y & 1431655765) << 1 | (y & 2863311530) >>> 1) >>> 0; + y = ((y & 858993459) << 2 | (y & 3435973836) >>> 2) >>> 0; + y = ((y & 252645135) << 4 | (y & 4042322160) >>> 4) >>> 0; + y = (((y & 16711935) << 8 | (y & 4278255360) >>> 8) >>> 0) / 4294967296; + + for (var j = 0; j < roughnessLevels; j++) { + var roughness = j / roughnessLevels; + var a = roughness * roughness; + var phi = 2.0 * Math.PI * x; + // CDF + var cosTheta = Math.sqrt((1 - y) / (1 + (a * a - 1.0) * y)); + var sinTheta = Math.sqrt(1.0 - cosTheta * cosTheta); + var offset = (i * roughnessLevels + j) * 4; + pixels[offset] = sinTheta * Math.cos(phi); + pixels[offset + 1] = sinTheta * Math.sin(phi); + pixels[offset + 2] = cosTheta; + pixels[offset + 3] = 1.0; + } + } + normalDistribution.pixels = pixels; + + return normalDistribution; + }; + + module.exports = cubemapUtil; + + +/***/ }, +/* 90 */ +/***/ function(module, exports, __webpack_require__) { + + // TODO Should not derived from mesh? + + + var Mesh = __webpack_require__(54); + var CubeGeometry = __webpack_require__(84); + var Shader = __webpack_require__(52); + var Material = __webpack_require__(53); + + + Shader.import(__webpack_require__(91)); + /** + * @constructor qtek.plugin.Skybox + * + * @example + * var skyTex = new qtek.TextureCube(); + * skyTex.load({ + * 'px': 'assets/textures/sky/px.jpg', + * 'nx': 'assets/textures/sky/nx.jpg' + * 'py': 'assets/textures/sky/py.jpg' + * 'ny': 'assets/textures/sky/ny.jpg' + * 'pz': 'assets/textures/sky/pz.jpg' + * 'nz': 'assets/textures/sky/nz.jpg' + * }); + * var skybox = new qtek.plugin.Skybox({ + * scene: scene + * }); + * skybox.material.set('environmentMap', skyTex); + */ + var Skybox = Mesh.extend(function () { + + var skyboxShader = new Shader({ + vertex: Shader.source('qtek.skybox.vertex'), + fragment: Shader.source('qtek.skybox.fragment') + }); + var material = new Material({ + shader: skyboxShader, + depthMask: false + }); + + return { + /** + * @type {qtek.Scene} + * @memberOf qtek.plugin.Skybox.prototype + */ + scene: null, + + geometry: new CubeGeometry(), + + material: material, + + environmentMap: null, + + culling: false + }; + }, function () { + var scene = this.scene; + if (scene) { + this.attachScene(scene); + } + if (this.environmentMap) { + this.setEnvironmentMap(this.environmentMap); + } + }, { + /** + * Attach the skybox to the scene + * @param {qtek.Scene} scene + * @memberOf qtek.plugin.Skybox.prototype + */ + attachScene: function (scene) { + if (this.scene) { + this.detachScene(); + } + this.scene = scene; + scene.on('beforerender', this._beforeRenderScene, this); + }, + /** + * Detach from scene + * @memberOf qtek.plugin.Skybox.prototype + */ + detachScene: function () { + if (this.scene) { + this.scene.off('beforerender', this._beforeRenderScene, this); + } + this.scene = null; + }, + + /** + * Dispose skybox + * @param {WebGLRenderingContext} gl + */ + dispose: function (gl) { + this.detachScene(); + this.geometry.dispose(gl); + this.material.dispose(gl); + }, + + setEnvironmentMap: function (envMap) { + this.material.set('environmentMap', envMap); + }, + + _beforeRenderScene: function(renderer, scene, camera) { + this.renderSkybox(renderer, camera); + }, + + renderSkybox: function (renderer, camera) { + this.position.copy(camera.getWorldPosition()); + this.update(); + // Don't remember to disable blend + renderer.gl.disable(renderer.gl.BLEND); + renderer.renderQueue([this], camera); + } + }); + + module.exports = Skybox; + + +/***/ }, +/* 91 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.skybox.vertex\n\nuniform mat4 world : WORLD;\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\nvarying vec3 v_WorldPosition;\n\nvoid main()\n{\n v_WorldPosition = (world * vec4(position, 1.0)).xyz;\n gl_Position = worldViewProjection * vec4(position, 1.0);\n}\n\n@end\n\n@export qtek.skybox.fragment\n\nuniform mat4 viewInverse : VIEWINVERSE;\nuniform samplerCube environmentMap;\nuniform float lod: 0.0;\n\nvarying vec3 v_WorldPosition;\n\n@import qtek.util.rgbm\n\nvoid main()\n{\n vec3 eyePos = viewInverse[3].xyz;\n vec3 viewDirection = normalize(v_WorldPosition - eyePos);\n\n vec3 tex = decodeHDR(textureCubeLodEXT(environmentMap, viewDirection, lod)).rgb;\n\n#ifdef SRGB_DECODE\n tex.rgb = pow(tex.rgb, vec3(2.2));\n#endif\n\n gl_FragColor = encodeHDR(vec4(tex, 1.0));\n}\n@end"; + + +/***/ }, +/* 92 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Node = __webpack_require__(22); + var Light = __webpack_require__(86); + var BoundingBox = __webpack_require__(26); + + /** + * @constructor qtek.Scene + * @extends qtek.Node + */ + var Scene = Node.extend(function () { + return /** @lends qtek.Scene# */ { + /** + * Global material of scene + * @type {Material} + */ + material: null, + + /** + * @type {boolean} + */ + autoUpdate: true, + + /** + * Opaque renderable list, it will be updated automatically + * @type {Renderable[]} + * @readonly + */ + opaqueQueue: [], + + /** + * Opaque renderable list, it will be updated automatically + * @type {Renderable[]} + * @readonly + */ + transparentQueue: [], + + lights: [], + + + /** + * Scene bounding box in view space. + * Used when camera needs to adujst the near and far plane automatically + * so that the view frustum contains the visible objects as tightly as possible. + * Notice: + * It is updated after rendering (in the step of frustum culling passingly). So may be not so accurate, but saves a lot of calculation + * + * @type {qtek.math.BoundingBox} + */ + viewBoundingBoxLastFrame: new BoundingBox(), + + // Properties to save the light information in the scene + // Will be set in the render function + _lightUniforms: {}, + + _lightNumber: { + // groupId: { + // POINT_LIGHT: 0, + // DIRECTIONAL_LIGHT: 0, + // SPOT_LIGHT: 0, + // AMBIENT_LIGHT: 0, + // AMBIENT_SH_LIGHT: 0 + // } + }, + + _opaqueObjectCount: 0, + _transparentObjectCount: 0, + + _nodeRepository: {}, + + }; + }, function () { + this._scene = this; + }, + /** @lends qtek.Scene.prototype. */ + { + /** + * Add node to scene + * @param {Node} node + */ + addToScene: function (node) { + if (node.name) { + this._nodeRepository[node.name] = node; + } + }, + + /** + * Remove node from scene + * @param {Node} node + */ + removeFromScene: function (node) { + if (node.name) { + delete this._nodeRepository[node.name]; + } + }, + + /** + * Get node by name + * @param {string} name + * @return {Node} + * @DEPRECATED + */ + getNode: function (name) { + return this._nodeRepository[name]; + }, + + /** + * Clone a new scene node recursively, including material, skeleton. + * Shader and geometry instances will not been cloned + * @param {qtek.Node} node + * @return {qtek.Node} + */ + cloneNode: function (node) { + var newNode = node.clone(); + var materialsMap = {}; + + var cloneSkeleton = function (current, currentNew) { + if (current.skeleton) { + currentNew.skeleton = current.skeleton.clone(node, newNode); + currentNew.joints = current.joints.slice(); + } + if (current.material) { + materialsMap[current.material.__GUID__] = { + oldMat: current.material + }; + } + for (var i = 0; i < current._children.length; i++) { + cloneSkeleton(current._children[i], currentNew._children[i]); + } + }; + + cloneSkeleton(node, newNode); + + for (var guid in materialsMap) { + materialsMap[guid].newMat = materialsMap[guid].oldMat.clone(); + } + + // Replace material + newNode.traverse(function (current) { + if (current.material) { + current.material = materialsMap[current.material.__GUID__].newMat; + } + }); + + return newNode; + }, + + + /** + * Scene update + * @param {boolean} force + * @param {boolean} notUpdateLights + * Useful in deferred pipeline + */ + update: function (force, notUpdateLights) { + if (!(this.autoUpdate || force)) { + return; + } + Node.prototype.update.call(this, force); + + var lights = this.lights; + var sceneMaterialTransparent = this.material && this.material.transparent; + + this._opaqueObjectCount = 0; + this._transparentObjectCount = 0; + + lights.length = 0; + + this._updateRenderQueue(this, sceneMaterialTransparent); + + this.opaqueQueue.length = this._opaqueObjectCount; + this.transparentQueue.length = this._transparentObjectCount; + + // reset + if (!notUpdateLights) { + var lightNumber = this._lightNumber; + // Reset light numbers + for (var group in lightNumber) { + for (var type in lightNumber[group]) { + lightNumber[group][type] = 0; + } + } + for (var i = 0; i < lights.length; i++) { + var light = lights[i]; + var group = light.group; + if (!lightNumber[group]) { + lightNumber[group] = {}; + } + // User can use any type of light + lightNumber[group][light.type] = lightNumber[group][light.type] || 0; + lightNumber[group][light.type]++; + } + // PENDING Remove unused group? + + this._updateLightUniforms(); + } + }, + + // Traverse the scene and add the renderable + // object to the render queue + _updateRenderQueue: function (parent, sceneMaterialTransparent) { + if (parent.invisible) { + return; + } + + for (var i = 0; i < parent._children.length; i++) { + var child = parent._children[i]; + + if (child instanceof Light) { + this.lights.push(child); + } + if (child.isRenderable()) { + if (child.material.transparent || sceneMaterialTransparent) { + this.transparentQueue[this._transparentObjectCount++] = child; + } + else { + this.opaqueQueue[this._opaqueObjectCount++] = child; + } + } + if (child._children.length > 0) { + this._updateRenderQueue(child); + } + } + }, + + _updateLightUniforms: function () { + var lights = this.lights; + // Put the light cast shadow before the light not cast shadow + lights.sort(lightSortFunc); + + var lightUniforms = this._lightUniforms; + for (var group in lightUniforms) { + for (var symbol in lightUniforms[group]) { + lightUniforms[group][symbol].value.length = 0; + } + } + for (var i = 0; i < lights.length; i++) { + + var light = lights[i]; + var group = light.group; + + for (var symbol in light.uniformTemplates) { + + var uniformTpl = light.uniformTemplates[symbol]; + if (!lightUniforms[group]) { + lightUniforms[group] = {}; + } + if (!lightUniforms[group][symbol]) { + lightUniforms[group][symbol] = { + type: '', + value: [] + }; + } + var value = uniformTpl.value(light); + var lu = lightUniforms[group][symbol]; + lu.type = uniformTpl.type + 'v'; + switch (uniformTpl.type) { + case '1i': + case '1f': + case 't': + lu.value.push(value); + break; + case '2f': + case '3f': + case '4f': + for (var j =0; j < value.length; j++) { + lu.value.push(value[j]); + } + break; + default: + console.error('Unkown light uniform type ' + uniformTpl.type); + } + } + } + }, + + isShaderLightNumberChanged: function (shader) { + var group = shader.lightGroup; + // PENDING Performance + for (var type in this._lightNumber[group]) { + if (this._lightNumber[group][type] !== shader.lightNumber[type]) { + return true; + } + } + for (var type in shader.lightNumber) { + if (this._lightNumber[group][type] !== shader.lightNumber[type]) { + return true; + } + } + return false; + }, + + setShaderLightNumber: function (shader) { + var group = shader.lightGroup; + for (var type in this._lightNumber[group]) { + shader.lightNumber[type] = this._lightNumber[group][type]; + } + shader.dirty(); + }, + + setLightUniforms: function (shader, _gl) { + var group = shader.lightGroup; + for (var symbol in this._lightUniforms[group]) { + var lu = this._lightUniforms[group][symbol]; + if (lu.type === 'tv') { + for (var i = 0; i < lu.value.length; i++) { + var texture = lu.value[i]; + var slot = shader.currentTextureSlot(); + var result = shader.setUniform(_gl, '1i', symbol, slot); + if (result) { + shader.useCurrentTextureSlot(_gl, texture); + } + } + } + else { + shader.setUniform(_gl, lu.type, symbol, lu.value); + } + } + }, + + /** + * Dispose self, clear all the scene objects + * But resources of gl like texuture, shader will not be disposed. + * Mostly you should use disposeScene method in Renderer to do dispose. + */ + dispose: function () { + this.material = null; + this.opaqueQueue = []; + this.transparentQueue = []; + + this.lights = []; + + this._lightUniforms = {}; + + this._lightNumber = {}; + this._nodeRepository = {}; + } + }); + + function lightSortFunc(a, b) { + if (b.castShadow && !a.castShadow) { + return true; + } + } + + module.exports = Scene; + + +/***/ }, +/* 93 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Base = __webpack_require__(3); + var Vector3 = __webpack_require__(23); + var PerspectiveCamera = __webpack_require__(31); + var FrameBuffer = __webpack_require__(44); + + var targets = ['px', 'nx', 'py', 'ny', 'pz', 'nz']; + + /** + * Pass rendering scene to a environment cube map + * + * @constructor qtek.prePass.EnvironmentMap + * @extends qtek.core.Base + * @example + * // Example of car reflection + * var envMap = new qtek.TextureCube({ + * width: 256, + * height: 256 + * }); + * var envPass = new qtek.prePass.EnvironmentMap({ + * position: car.position, + * texture: envMap + * }); + * var carBody = car.getChildByName('body'); + * carBody.material.shader.enableTexture('environmentMap'); + * carBody.material.set('environmentMap', envMap); + * ... + * animation.on('frame', function(frameTime) { + * envPass.render(renderer, scene); + * renderer.render(scene, camera); + * }); + */ + var EnvironmentMapPass = Base.extend(function() { + var ret = { + /** + * Camera position + * @type {qtek.math.Vector3} + * @memberOf qtek.prePass.EnvironmentMap# + */ + position: new Vector3(), + /** + * Camera far plane + * @type {number} + * @memberOf qtek.prePass.EnvironmentMap# + */ + far: 1000, + /** + * Camera near plane + * @type {number} + * @memberOf qtek.prePass.EnvironmentMap# + */ + near: 0.1, + /** + * Environment cube map + * @type {qtek.TextureCube} + * @memberOf qtek.prePass.EnvironmentMap# + */ + texture: null, + + /** + * Used if you wan't have shadow in environment map + * @type {qtek.prePass.ShadowMap} + */ + shadowMapPass: null, + }; + var cameras = ret._cameras = { + px: new PerspectiveCamera({ fov: 90 }), + nx: new PerspectiveCamera({ fov: 90 }), + py: new PerspectiveCamera({ fov: 90 }), + ny: new PerspectiveCamera({ fov: 90 }), + pz: new PerspectiveCamera({ fov: 90 }), + nz: new PerspectiveCamera({ fov: 90 }) + }; + cameras.px.lookAt(Vector3.POSITIVE_X, Vector3.NEGATIVE_Y); + cameras.nx.lookAt(Vector3.NEGATIVE_X, Vector3.NEGATIVE_Y); + cameras.py.lookAt(Vector3.POSITIVE_Y, Vector3.POSITIVE_Z); + cameras.ny.lookAt(Vector3.NEGATIVE_Y, Vector3.NEGATIVE_Z); + cameras.pz.lookAt(Vector3.POSITIVE_Z, Vector3.NEGATIVE_Y); + cameras.nz.lookAt(Vector3.NEGATIVE_Z, Vector3.NEGATIVE_Y); + + // FIXME In windows, use one framebuffer only renders one side of cubemap + ret._frameBuffer = new FrameBuffer() + + return ret; + }, { + /** + * @param {string} target + * @return {qtek.Camera} + */ + getCamera: function (target) { + return this._cameras[target]; + }, + /** + * @param {qtek.Renderer} renderer + * @param {qtek.Scene} scene + * @param {boolean} [notUpdateScene=false] + */ + render: function(renderer, scene, notUpdateScene) { + var _gl = renderer.gl; + if (!notUpdateScene) { + scene.update(); + } + // Tweak fov + // http://the-witness.net/news/2012/02/seamless-cube-map-filtering/ + var n = this.texture.width; + var fov = 2 * Math.atan(n / (n - 0.5)) / Math.PI * 180; + + for (var i = 0; i < 6; i++) { + var target = targets[i]; + var camera = this._cameras[target]; + Vector3.copy(camera.position, this.position); + + camera.far = this.far; + camera.near = this.near; + camera.fov = fov; + + if (this.shadowMapPass) { + camera.update(); + + // update boundingBoxLastFrame + var bbox = scene.getBoundingBox(function (el) { + return !el.invisible; + }); + bbox.applyTransform(camera.viewMatrix); + scene.viewBoundingBoxLastFrame.copy(bbox); + + this.shadowMapPass.render(renderer, scene, camera, true); + } + this._frameBuffer.attach( + this.texture, _gl.COLOR_ATTACHMENT0, + _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i + ); + this._frameBuffer.bind(renderer); + renderer.render(scene, camera, true); + this._frameBuffer.unbind(renderer); + } + }, + /** + * @param {qtek.Renderer} renderer + */ + dispose: function(gl) { + this._frameBuffer.dispose(gl); + } + }); + + module.exports = EnvironmentMapPass; + + +/***/ }, +/* 94 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Texture2D = __webpack_require__(39); + var TextureCube = __webpack_require__(45); + var request = __webpack_require__(19); + var EnvironmentMapPass = __webpack_require__(93); + var Skydome = __webpack_require__(95); + var Scene = __webpack_require__(92); + + var dds = __webpack_require__(97); + var hdr = __webpack_require__(98); + + /** + * @namespace qtek.util.texture + */ + var textureUtil = { + /** + * @param {string|object} path + * @param {object} [option] + * @param {Function} [onsuccess] + * @param {Function} [onerror] + * @return {qtek.Texture} + * + * @memberOf qtek.util.texture + */ + loadTexture: function (path, option, onsuccess, onerror) { + var texture; + if (typeof(option) === 'function') { + onsuccess = option; + onerror = onsuccess; + option = {}; + } + else { + option = option || {}; + } + if (typeof(path) === 'string') { + if (path.match(/.hdr$/)) { + texture = new Texture2D({ + width: 0, + height: 0 + }); + textureUtil._fetchTexture( + path, + function (data) { + hdr.parseRGBE(data, texture, option.exposure); + texture.dirty(); + onsuccess && onsuccess(texture); + }, + onerror + ); + return texture; + } + else if (path.match(/.dds$/)) { + texture = new Texture2D({ + width: 0, + height: 0 + }); + textureUtil._fetchTexture( + path, + function (data) { + dds.parse(data, texture); + texture.dirty(); + onsuccess && onsuccess(texture); + }, + onerror + ); + } + else { + texture = new Texture2D(); + texture.load(path); + texture.success(onsuccess); + texture.error(onerror); + } + } + else if (typeof(path) == 'object' && typeof(path.px) !== 'undefined') { + var texture = new TextureCube(); + texture.load(path); + texture.success(onsuccess); + texture.error(onerror); + } + return texture; + }, + + /** + * Load a panorama texture and render it to a cube map + * @param {qtek.Renderer} renderer + * @param {string} path + * @param {qtek.TextureCube} cubeMap + * @param {object} [option] + * @param {boolean} [option.encodeRGBM] + * @param {number} [option.exposure] + * @param {Function} [onsuccess] + * @param {Function} [onerror] + * + * @memberOf qtek.util.texture + */ + loadPanorama: function (renderer, path, cubeMap, option, onsuccess, onerror) { + var self = this; + + if (typeof(option) === 'function') { + onsuccess = option; + onerror = onsuccess; + option = {}; + } + else { + option = option || {}; + } + + textureUtil.loadTexture(path, option, function (texture) { + // PENDING + texture.flipY = option.flipY || false; + self.panoramaToCubeMap(renderer, texture, cubeMap, option); + texture.dispose(renderer.gl); + onsuccess && onsuccess(cubeMap); + }, onerror); + }, + + /** + * Render a panorama texture to a cube map + * @param {qtek.Renderer} renderer + * @param {qtek.Texture2D} panoramaMap + * @param {qtek.TextureCube} cubeMap + * @param {Object} option + * @param {boolean} [option.encodeRGBM] + * + * @memberOf qtek.util.texture + */ + panoramaToCubeMap: function (renderer, panoramaMap, cubeMap, option) { + var environmentMapPass = new EnvironmentMapPass(); + var skydome = new Skydome({ + scene: new Scene() + }); + skydome.material.set('diffuseMap', panoramaMap); + + option = option || {}; + if (option.encodeRGBM) { + skydome.material.shader.define('fragment', 'RGBM_ENCODE'); + } + + environmentMapPass.texture = cubeMap; + environmentMapPass.render(renderer, skydome.scene); + environmentMapPass.texture = null; + environmentMapPass.dispose(renderer); + return cubeMap; + }, + + _fetchTexture: function (path, onsuccess, onerror) { + request.get({ + url: path, + responseType: 'arraybuffer', + onload: onsuccess, + onerror: onerror + }); + }, + + /** + * Create a chessboard texture + * @param {number} [size] + * @param {number} [unitSize] + * @param {string} [color1] + * @param {string} [color2] + * @return {qtek.Texture2D} + * + * @memberOf qtek.util.texture + */ + createChessboard: function (size, unitSize, color1, color2) { + size = size || 512; + unitSize = unitSize || 64; + color1 = color1 || 'black'; + color2 = color2 || 'white'; + + var repeat = Math.ceil(size / unitSize); + + var canvas = document.createElement('canvas'); + canvas.width = size; + canvas.height = size; + var ctx = canvas.getContext('2d'); + ctx.fillStyle = color2; + ctx.fillRect(0, 0, size, size); + + ctx.fillStyle = color1; + for (var i = 0; i < repeat; i++) { + for (var j = 0; j < repeat; j++) { + var isFill = j % 2 ? (i % 2) : (i % 2 - 1); + if (isFill) { + ctx.fillRect(i * unitSize, j * unitSize, unitSize, unitSize); + } + } + } + + var texture = new Texture2D({ + image: canvas, + anisotropic: 8 + }); + + return texture; + }, + + /** + * Create a blank pure color 1x1 texture + * @param {string} color + * @return {qtek.Texture2D} + * + * @memberOf qtek.util.texture + */ + createBlank: function (color) { + var canvas = document.createElement('canvas'); + canvas.width = 1; + canvas.height = 1; + var ctx = canvas.getContext('2d'); + ctx.fillStyle = color; + ctx.fillRect(0, 0, 1, 1); + + var texture = new Texture2D({ + image: canvas + }); + + return texture; + } + }; + + module.exports = textureUtil; + + +/***/ }, +/* 95 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Mesh = __webpack_require__(54); + var SphereGeometry = __webpack_require__(70); + var Shader = __webpack_require__(52); + var Material = __webpack_require__(53); + + Shader.import(__webpack_require__(96)); + /** + * @constructor qtek.plugin.Skydome + * + * @example + * var skyTex = new qtek.Texture2D(); + * skyTex.load('assets/textures/sky.jpg'); + * var skydome = new qtek.plugin.Skydome({ + * scene: scene + * }); + * skydome.material.set('diffuseMap', skyTex); + */ + var Skydome = Mesh.extend(function () { + + var skydomeShader = new Shader({ + vertex: Shader.source('qtek.basic.vertex'), + fragment: Shader.source('qtek.basic.fragment') + }); + skydomeShader.enableTexture('diffuseMap'); + + var material = new Material({ + shader: skydomeShader, + depthMask: false + }); + + return { + /** + * @type {qtek.Scene} + * @memberOf qtek.plugin.Skydome# + */ + scene: null, + + geometry: new SphereGeometry({ + widthSegments: 30, + heightSegments: 30, + // thetaLength: Math.PI / 2 + }), + + material: material, + + environmentMap: null, + + culling: false + }; + }, function () { + var scene = this.scene; + if (scene) { + this.attachScene(scene); + } + + if (this.environmentMap) { + this.setEnvironmentMap(this.environmentMap); + } + }, { + /** + * Attach the skybox to the scene + * @param {qtek.Scene} scene + * @memberOf qtek.plugin.Skydome.prototype + */ + attachScene: function (scene) { + if (this.scene) { + this.detachScene(); + } + this.scene = scene; + scene.on('beforerender', this._beforeRenderScene, this); + }, + /** + * Detach from scene + * @memberOf qtek.plugin.Skydome.prototype + */ + detachScene: function () { + if (this.scene) { + this.scene.off('beforerender', this._beforeRenderScene, this); + } + this.scene = null; + }, + + _beforeRenderScene: function (renderer, scene, camera) { + this.position.copy(camera.getWorldPosition()); + this.update(); + renderer.renderQueue([this], camera); + }, + + setEnvironmentMap: function (envMap) { + this.material.set('diffuseMap', envMap); + }, + + dispose: function (gl) { + this.detachScene(); + this.geometry.dispose(gl); + this.material.dispose(gl); + } + }); + + module.exports = Skydome; + + +/***/ }, +/* 96 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.basic.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\n\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 position : POSITION;\n\nattribute vec3 barycentric;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_COUNT] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Barycentric;\n\nvoid main()\n{\n vec3 skinnedPosition = position;\n\n#ifdef SKINNING\n @import qtek.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_Barycentric = barycentric;\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n}\n\n@end\n\n\n\n\n@export qtek.basic.fragment\n\nvarying vec2 v_Texcoord;\nuniform sampler2D diffuseMap;\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform vec3 emission : [0.0, 0.0, 0.0];\nuniform float alpha : 1.0;\n\nuniform float lineWidth : 0.0;\nuniform vec3 lineColor : [0.0, 0.0, 0.0];\nvarying vec3 v_Barycentric;\n\n@import qtek.util.edge_factor\n\n@import qtek.util.rgbm\n\n@import qtek.util.srgb\n\nvoid main()\n{\n\n#ifdef RENDER_TEXCOORD\n gl_FragColor = vec4(v_Texcoord, 1.0, 1.0);\n return;\n#endif\n\n gl_FragColor = vec4(color, alpha);\n\n#ifdef DIFFUSEMAP_ENABLED\n vec4 tex = decodeHDR(texture2D(diffuseMap, v_Texcoord));\n\n#ifdef SRGB_DECODE\n tex = sRGBToLinear(tex);\n#endif\n\n#if defined(DIFFUSEMAP_ALPHA_ALPHA)\n gl_FragColor.a = tex.a;\n#endif\n\n gl_FragColor.rgb *= tex.rgb;\n#endif\n\n gl_FragColor.rgb += emission;\n if( lineWidth > 0.01)\n {\n gl_FragColor.rgb = gl_FragColor.rgb * mix(lineColor, vec3(1.0), edgeFactor(lineWidth));\n }\n\n#ifdef GAMMA_ENCODE\n gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(1 / 2.2));\n#endif\n\n gl_FragColor = encodeHDR(gl_FragColor);\n\n}\n\n@end"; + + +/***/ }, +/* 97 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Texture = __webpack_require__(40); + var Texture2D = __webpack_require__(39); + var TextureCube = __webpack_require__(45); + + // http://msdn.microsoft.com/en-us/library/windows/desktop/bb943991(v=vs.85).aspx + // https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js + var DDS_MAGIC = 0x20534444; + + var DDSD_CAPS = 0x1; + var DDSD_HEIGHT = 0x2; + var DDSD_WIDTH = 0x4; + var DDSD_PITCH = 0x8; + var DDSD_PIXELFORMAT = 0x1000; + var DDSD_MIPMAPCOUNT = 0x20000; + var DDSD_LINEARSIZE = 0x80000; + var DDSD_DEPTH = 0x800000; + + var DDSCAPS_COMPLEX = 0x8; + var DDSCAPS_MIPMAP = 0x400000; + var DDSCAPS_TEXTURE = 0x1000; + + var DDSCAPS2_CUBEMAP = 0x200; + var DDSCAPS2_CUBEMAP_POSITIVEX = 0x400; + var DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800; + var DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000; + var DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000; + var DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000; + var DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000; + var DDSCAPS2_VOLUME = 0x200000; + + var DDPF_ALPHAPIXELS = 0x1; + var DDPF_ALPHA = 0x2; + var DDPF_FOURCC = 0x4; + var DDPF_RGB = 0x40; + var DDPF_YUV = 0x200; + var DDPF_LUMINANCE = 0x20000; + + function fourCCToInt32(value) { + return value.charCodeAt(0) + + (value.charCodeAt(1) << 8) + + (value.charCodeAt(2) << 16) + + (value.charCodeAt(3) << 24); + } + + function int32ToFourCC(value) { + return String.fromCharCode( + value & 0xff, + (value >> 8) & 0xff, + (value >> 16) & 0xff, + (value >> 24) & 0xff + ); + } + + var headerLengthInt = 31; // The header length in 32 bit ints + + var FOURCC_DXT1 = fourCCToInt32('DXT1'); + var FOURCC_DXT3 = fourCCToInt32('DXT3'); + var FOURCC_DXT5 = fourCCToInt32('DXT5'); + // Offsets into the header array + var off_magic = 0; + + var off_size = 1; + var off_flags = 2; + var off_height = 3; + var off_width = 4; + + var off_mipmapCount = 7; + + var off_pfFlags = 20; + var off_pfFourCC = 21; + + var off_caps = 27; + var off_caps2 = 28; + var off_caps3 = 29; + var off_caps4 = 30; + + var ret = { + parse: function(arrayBuffer, out) { + var header = new Int32Array(arrayBuffer, 0, headerLengthInt); + if (header[off_magic] !== DDS_MAGIC) { + return null; + } + if (!header(off_pfFlags) & DDPF_FOURCC) { + return null; + } + + var fourCC = header(off_pfFourCC); + var width = header[off_width]; + var height = header[off_height]; + var isCubeMap = header[off_caps2] & DDSCAPS2_CUBEMAP; + var hasMipmap = header[off_flags] & DDSD_MIPMAPCOUNT; + var blockBytes, internalFormat; + switch(fourCC) { + case FOURCC_DXT1: + blockBytes = 8; + internalFormat = Texture.COMPRESSED_RGB_S3TC_DXT1_EXT; + break; + case FOURCC_DXT3: + blockBytes = 16; + internalFormat = Texture.COMPRESSED_RGBA_S3TC_DXT3_EXT; + break; + case FOURCC_DXT5: + blockBytes = 16; + internalFormat = Texture.COMPRESSED_RGBA_S3TC_DXT5_EXT; + break; + default: + return null; + } + var dataOffset = header[off_size] + 4; + // TODO: Suppose all face are existed + var faceNumber = isCubeMap ? 6 : 1; + var mipmapCount = 1; + if (hasMipmap) { + mipmapCount = Math.max(1, header[off_mipmapCount]); + } + + var textures = []; + for (var f = 0; f < faceNumber; f++) { + var _width = width; + var _height = height; + textures[f] = new Texture2D({ + width : _width, + height : _height, + format : internalFormat + }); + var mipmaps = []; + for (var i = 0; i < mipmapCount; i++) { + var dataLength = Math.max(4, _width) / 4 * Math.max(4, _height) / 4 * blockBytes; + var byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength); + + dataOffset += dataLength; + _width *= 0.5; + _height *= 0.5; + mipmaps[i] = byteArray; + } + textures[f].pixels = mipmaps[0]; + if (hasMipmap) { + textures[f].mipmaps = mipmaps; + } + } + // TODO + // return isCubeMap ? textures : textures[0]; + if (out) { + out.width = textures[0].width; + out.height = textures[0].height; + out.format = textures[0].format; + out.pixels = textures[0].pixels; + out.mipmaps = textures[0].mipmaps; + } + else { + return textures[0]; + } + } + }; + + module.exports = ret; + + +/***/ }, +/* 98 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Texture = __webpack_require__(40); + var Texture2D = __webpack_require__(39); + var toChar = String.fromCharCode; + + var MINELEN = 8; + var MAXELEN = 0x7fff; + function rgbe2float(rgbe, buffer, offset, exposure) { + if (rgbe[3] > 0) { + var f = Math.pow(2.0, rgbe[3] - 128 - 8 + exposure); + buffer[offset + 0] = rgbe[0] * f; + buffer[offset + 1] = rgbe[1] * f; + buffer[offset + 2] = rgbe[2] * f; + } + else { + buffer[offset + 0] = 0; + buffer[offset + 1] = 0; + buffer[offset + 2] = 0; + } + buffer[offset + 3] = 1.0; + return buffer; + } + + function uint82string(array, offset, size) { + var str = ''; + for (var i = offset; i < size; i++) { + str += toChar(array[i]); + } + return str; + } + + function copyrgbe(s, t) { + t[0] = s[0]; + t[1] = s[1]; + t[2] = s[2]; + t[3] = s[3]; + } + + // TODO : check + function oldReadColors(scan, buffer, offset, xmax) { + var rshift = 0, x = 0, len = xmax; + while (len > 0) { + scan[x][0] = buffer[offset++]; + scan[x][1] = buffer[offset++]; + scan[x][2] = buffer[offset++]; + scan[x][3] = buffer[offset++]; + if (scan[x][0] === 1 && scan[x][1] === 1 && scan[x][2] === 1) { + // exp is count of repeated pixels + for (var i = (scan[x][3] << rshift) >>> 0; i > 0; i--) { + copyrgbe(scan[x-1], scan[x]); + x++; + len--; + } + rshift += 8; + } else { + x++; + len--; + rshift = 0; + } + } + return offset; + } + + function readColors(scan, buffer, offset, xmax) { + if ((xmax < MINELEN) | (xmax > MAXELEN)) { + return oldReadColors(scan, buffer, offset, xmax); + } + var i = buffer[offset++]; + if (i != 2) { + return oldReadColors(scan, buffer, offset - 1, xmax); + } + scan[0][1] = buffer[offset++]; + scan[0][2] = buffer[offset++]; + + i = buffer[offset++]; + if ((((scan[0][2] << 8) >>> 0) | i) >>> 0 !== xmax) { + return null; + } + for (var i = 0; i < 4; i++) { + for (var x = 0; x < xmax;) { + var code = buffer[offset++]; + if (code > 128) { + code = (code & 127) >>> 0; + var val = buffer[offset++]; + while (code--) { + scan[x++][i] = val; + } + } else { + while (code--) { + scan[x++][i] = buffer[offset++]; + } + } + } + } + return offset; + } + + + var ret = { + // http://www.graphics.cornell.edu/~bjw/rgbe.html + // Blender source + // http://radsite.lbl.gov/radiance/refer/Notes/picture_format.html + parseRGBE: function(arrayBuffer, texture, exposure) { + if (exposure == null) { + exposure = 0; + } + var data = new Uint8Array(arrayBuffer); + var size = data.length; + if (uint82string(data, 0, 2) !== '#?') { + return; + } + // find empty line, next line is resolution info + for (var i = 2; i < size; i++) { + if (toChar(data[i]) === '\n' && toChar(data[i+1]) === '\n') { + break; + } + } + if (i >= size) { // not found + return; + } + // find resolution info line + i += 2; + var str = ''; + for (; i < size; i++) { + var _char = toChar(data[i]); + if (_char === '\n') { + break; + } + str += _char; + } + // -Y M +X N + var tmp = str.split(' '); + var height = parseInt(tmp[1]); + var width = parseInt(tmp[3]); + if (!width || !height) { + return; + } + + // read and decode actual data + var offset = i+1; + var scanline = []; + // memzero + for (var x = 0; x < width; x++) { + scanline[x] = []; + for (var j = 0; j < 4; j++) { + scanline[x][j] = 0; + } + } + var pixels = new Float32Array(width * height * 4); + var offset2 = 0; + for (var y = 0; y < height; y++) { + var offset = readColors(scanline, data, offset, width); + if (!offset) { + return null; + } + for (var x = 0; x < width; x++) { + rgbe2float(scanline[x], pixels, offset2, exposure); + offset2 += 4; + } + } + + if (!texture) { + texture = new Texture2D(); + } + texture.width = width; + texture.height = height; + texture.pixels = pixels; + // HALF_FLOAT can't use Float32Array + texture.type = Texture.FLOAT; + return texture; + }, + + parseRGBEFromPNG: function(png) { + + } + }; + + module.exports = ret; + + +/***/ }, +/* 99 */ +/***/ function(module, exports) { + + + module.exports = "#define SAMPLE_NUMBER 1024\n#define PI 3.14159265358979\n\n\nuniform sampler2D normalDistribution;\n\nuniform vec2 viewportSize : [512, 256];\n\nconst vec3 N = vec3(0.0, 0.0, 1.0);\nconst float fSampleNumber = float(SAMPLE_NUMBER);\n\nvec3 importanceSampleNormal(float i, float roughness, vec3 N) {\n vec3 H = texture2D(normalDistribution, vec2(roughness, i)).rgb;\n\n vec3 upVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);\n vec3 tangentX = normalize(cross(upVector, N));\n vec3 tangentY = cross(N, tangentX);\n return tangentX * H.x + tangentY * H.y + N * H.z;\n}\n\nfloat G_Smith(float roughness, float NoV, float NoL) {\n float k = roughness * roughness / 2.0;\n float G1V = NoV / (NoV * (1.0 - k) + k);\n float G1L = NoL / (NoL * (1.0 - k) + k);\n return G1L * G1V;\n}\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / viewportSize;\n float NoV = uv.x;\n float roughness = uv.y;\n\n vec3 V;\n V.x = sqrt(1.0 - NoV * NoV);\n V.y = 0.0;\n V.z = NoV;\n\n float A = 0.0;\n float B = 0.0;\n\n for (int i = 0; i < SAMPLE_NUMBER; i++) {\n vec3 H = importanceSampleNormal(float(i) / fSampleNumber, roughness, N);\n vec3 L = reflect(-V, H);\n float NoL = clamp(L.z, 0.0, 1.0);\n float NoH = clamp(H.z, 0.0, 1.0);\n float VoH = clamp(dot(V, H), 0.0, 1.0);\n\n if (NoL > 0.0) {\n float G = G_Smith(roughness, NoV, NoL);\n float G_Vis = G * VoH / (NoH * NoV);\n float Fc = pow(1.0 - VoH, 5.0);\n A += (1.0 - Fc) * G_Vis;\n B += Fc * G_Vis;\n }\n }\n\n gl_FragColor = vec4(vec2(A, B) / fSampleNumber, 0.0, 1.0);\n}\n"; + + +/***/ }, +/* 100 */ +/***/ function(module, exports) { + + + module.exports = "#define SAMPLE_NUMBER 1024\n#define PI 3.14159265358979\n\nuniform mat4 viewInverse : VIEWINVERSE;\nuniform samplerCube environmentMap;\nuniform sampler2D normalDistribution;\n\nuniform float roughness : 0.5;\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_WorldPosition;\n\nconst float fSampleNumber = float(SAMPLE_NUMBER);\n\n@import qtek.util.rgbm\n\nvec3 importanceSampleNormal(float i, float roughness, vec3 N) {\n vec3 H = texture2D(normalDistribution, vec2(roughness, i)).rgb;\n\n vec3 upVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);\n vec3 tangentX = normalize(cross(upVector, N));\n vec3 tangentY = cross(N, tangentX);\n return tangentX * H.x + tangentY * H.y + N * H.z;\n}\n\nvoid main() {\n\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = normalize(v_WorldPosition - eyePos);\n\n vec3 N = V;\n vec3 R = V;\n\n vec3 prefilteredColor = vec3(0.0);\n float totalWeight = 0.0;\n\n\n for (int i = 0; i < SAMPLE_NUMBER; i++) {\n vec3 H = importanceSampleNormal(float(i) / fSampleNumber, roughness, N);\n vec3 L = reflect(-V, H);\n\n float NoL = clamp(dot(N, L), 0.0, 1.0);\n if (NoL > 0.0) {\n prefilteredColor += decodeHDR(textureCube(environmentMap, L)).rgb * NoL;\n totalWeight += NoL;\n }\n }\n\n gl_FragColor = encodeHDR(vec4(prefilteredColor / totalWeight, 1.0));\n}\n"; + + +/***/ }, +/* 101 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Light = __webpack_require__(86); + var vendor = __webpack_require__(51); + + /** + * Spherical Harmonic Ambient Light + * @constructor qtek.light.AmbientSH + * @extends qtek.Light + */ + var AmbientSHLight = Light.extend({ + + castShadow: false, + + + /** + * Spherical Harmonic Coefficients + * @type {Array.} + */ + coefficients: [], + + }, function () { + this._coefficientsTmpArr = new vendor.Float32Array(9 * 3); + }, { + + type: 'AMBIENT_SH_LIGHT', + + uniformTemplates: { + ambientSHLightColor: { + type: '3f', + value: function (instance) { + var color = instance.color; + var intensity = instance.intensity; + return [color[0] * intensity, color[1] * intensity, color[2] * intensity]; + } + }, + + ambientSHLightCoefficients: { + type: '3f', + value: function (instance) { + var coefficientsTmpArr = instance._coefficientsTmpArr; + for (var i = 0; i < instance.coefficients.length; i++) { + coefficientsTmpArr[i] = instance.coefficients[i]; + } + return coefficientsTmpArr; + } + } + } + /** + * @method + * @name clone + * @return {qtek.light.Ambient} + * @memberOf qtek.light.Ambient.prototype + */ + }); + + module.exports = AmbientSHLight; + + +/***/ }, +/* 102 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Light = __webpack_require__(86); + var Vector3 = __webpack_require__(23); + + /** + * @constructor qtek.light.Directional + * @extends qtek.Light + * + * @example + * var light = new qtek.light.Directional({ + * intensity: 0.5, + * color: [1.0, 0.0, 0.0] + * }); + * light.position.set(10, 10, 10); + * light.lookAt(qtek.math.Vector3.ZERO); + * scene.add(light); + */ + var DirectionalLight = Light.extend( + /** @lends qtek.light.Directional# */ + { + /** + * @type {number} + */ + shadowBias: 0.001, + /** + * @type {number} + */ + shadowSlopeScale: 2.0, + /** + * Shadow cascade. + * Use PSSM technique when it is larger than 1 and have a unique directional light in scene. + * @type {number} + */ + shadowCascade: 1, + + /** + * Available when shadowCascade is larger than 1 and have a unique directional light in scene. + * @type {number} + */ + cascadeSplitLogFactor: 0.2 + }, { + + type: 'DIRECTIONAL_LIGHT', + + uniformTemplates: { + directionalLightDirection: { + type: '3f', + value: function (instance) { + instance.__dir = instance.__dir || new Vector3(); + // Direction is target to eye + return instance.__dir.copy(instance.worldTransform.z).negate()._array; + } + }, + directionalLightColor: { + type: '3f', + value: function (instance) { + var color = instance.color; + var intensity = instance.intensity; + return [color[0] * intensity, color[1] * intensity, color[2] * intensity]; + } + } + }, + /** + * @return {qtek.light.Directional} + * @memberOf qtek.light.Directional.prototype + */ + clone: function () { + var light = Light.prototype.clone.call(this); + light.shadowBias = this.shadowBias; + light.shadowSlopeScale = this.shadowSlopeScale; + return light; + } + }); + + module.exports = DirectionalLight; + + +/***/ }, +/* 103 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Light = __webpack_require__(86); + + /** + * @constructor qtek.light.Point + * @extends qtek.Light + */ + var PointLight = Light.extend( + /** @lends qtek.light.Point# */ + { + /** + * @type {number} + */ + range: 100, + + /** + * @type {number} + */ + castShadow: false + }, { + + type: 'POINT_LIGHT', + + uniformTemplates: { + pointLightPosition: { + type: '3f', + value: function(instance) { + return instance.getWorldPosition()._array; + } + }, + pointLightRange: { + type: '1f', + value: function(instance) { + return instance.range; + } + }, + pointLightColor: { + type: '3f', + value: function(instance) { + var color = instance.color, + intensity = instance.intensity; + return [ color[0]*intensity, color[1]*intensity, color[2]*intensity ]; + } + } + }, + /** + * @return {qtek.light.Point} + * @memberOf qtek.light.Point.prototype + */ + clone: function() { + var light = Light.prototype.clone.call(this); + light.range = this.range; + return light; + } + }); + + module.exports = PointLight; + + +/***/ }, +/* 104 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Light = __webpack_require__(86); + + /** + * @constructor qtek.light.Sphere + * @extends {qtek.Light} + */ + var SphereLight = Light.extend( + /** @lends qtek.light.Sphere# */ + { + /** + * @type {number} + */ + range: 100, + + /** + * @type {number} + */ + radius: 5 + }, { + + type: 'SPHERE_LIGHT', + + uniformTemplates: { + sphereLightPosition: { + type: '3f', + value: function(instance) { + return instance.getWorldPosition()._array; + } + }, + sphereLightRange: { + type: '1f', + value: function(instance) { + return instance.range; + } + }, + sphereLightRadius: { + type: '1f', + value: function(instance) { + return instance.radius; + } + }, + sphereLightColor: { + type: '3f', + value: function(instance) { + var color = instance.color; + var intensity = instance.intensity; + return [color[0]*intensity, color[1]*intensity, color[2]*intensity]; + } + } + } + }); + + module.exports = SphereLight; + + +/***/ }, +/* 105 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Light = __webpack_require__(86); + var Vector3 = __webpack_require__(23); + + /** + * @constructor qtek.light.Spot + * @extends qtek.Light + */ + var SpotLight = Light.extend( + /**@lends qtek.light.Spot */ + { + /** + * @type {number} + */ + range: 20, + /** + * @type {number} + */ + umbraAngle: 30, + /** + * @type {number} + */ + penumbraAngle: 45, + /** + * @type {number} + */ + falloffFactor: 2.0, + /** + * @type {number} + */ + shadowBias: 0.0002, + /** + * @type {number} + */ + shadowSlopeScale: 2.0 + },{ + + type: 'SPOT_LIGHT', + + uniformTemplates: { + spotLightPosition: { + type: '3f', + value: function (instance) { + return instance.getWorldPosition()._array; + } + }, + spotLightRange: { + type: '1f', + value: function (instance) { + return instance.range; + } + }, + spotLightUmbraAngleCosine: { + type: '1f', + value: function (instance) { + return Math.cos(instance.umbraAngle * Math.PI / 180); + } + }, + spotLightPenumbraAngleCosine: { + type: '1f', + value: function (instance) { + return Math.cos(instance.penumbraAngle * Math.PI / 180); + } + }, + spotLightFalloffFactor: { + type: '1f', + value: function (instance) { + return instance.falloffFactor; + } + }, + spotLightDirection: { + type: '3f', + value: function (instance) { + instance.__dir = instance.__dir || new Vector3(); + // Direction is target to eye + return instance.__dir.copy(instance.worldTransform.z).negate()._array; + } + }, + spotLightColor: { + type: '3f', + value: function (instance) { + var color = instance.color; + var intensity = instance.intensity; + return [color[0] * intensity, color[1] * intensity, color[2] * intensity]; + } + } + }, + /** + * @return {qtek.light.Spot} + * @memberOf qtek.light.Spot.prototype + */ + clone: function () { + var light = Light.prototype.clone.call(this); + light.range = this.range; + light.umbraAngle = this.umbraAngle; + light.penumbraAngle = this.penumbraAngle; + light.falloffFactor = this.falloffFactor; + light.shadowBias = this.shadowBias; + light.shadowSlopeScale = this.shadowSlopeScale; + return light; + } + }); + + module.exports = SpotLight; + + +/***/ }, +/* 106 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Light = __webpack_require__(86); + var Vector3 = __webpack_require__(23); + + /** + * @constructor qtek.light.Tube + * @extends {qtek.Light} + */ + var TubeLight = Light.extend( + /** @lends qtek.light.Tube# */ + { + /** + * @type {number} + */ + range: 100, + + /** + * @type {number} + */ + length: 10 + }, { + + type: 'TUBE_LIGHT', + + uniformTemplates: { + tubeLightPosition: { + type: '3f', + value: function(instance) { + return instance.getWorldPosition()._array; + } + }, + + tubeLightExtend: { + type: '3f', + value: (function() { + var x = new Vector3(); + return function(instance) { + // Extend in x axis + return x.copy(instance.worldTransform.x) + .normalize().scale(instance.length / 2)._array; + }; + })() + }, + + tubeLightRange: { + type: '1f', + value: function(instance) { + return instance.range; + } + }, + + tubeLightColor: { + type: '3f', + value: function(instance) { + var color = instance.color; + var intensity = instance.intensity; + return [color[0]*intensity, color[1]*intensity, color[2]*intensity]; + } + } + } + }); + + module.exports = TubeLight; + + +/***/ }, +/* 107 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Base = __webpack_require__(3); + var request = __webpack_require__(19); + var util = __webpack_require__(6); + var Compositor = __webpack_require__(35); + var CompoNode = __webpack_require__(37); + var CompoSceneNode = __webpack_require__(57); + var CompoTextureNode = __webpack_require__(58); + var CompoFilterNode = __webpack_require__(46); + var Shader = __webpack_require__(52); + var Texture = __webpack_require__(40); + var Texture2D = __webpack_require__(39); + var TextureCube = __webpack_require__(45); + + var shaderSourceReg = /#source\((.*?)\)/; + var urlReg = /#url\((.*?)\)/; + + /** + * @constructor qtek.loader.FX + * @extends qtek.core.Base + */ + var FXLoader = Base.extend( + /** @lends qtek.loader.FX# */ + { + /** + * @type {string} + */ + rootPath: '', + /** + * @type {string} + */ + textureRootPath: '', + /** + * @type {string} + */ + shaderRootPath: '', + + /** + * @type {qtek.Scene} + */ + scene: null, + + /** + * @type {qtek.Camera} + */ + camera: null + }, + /** @lends qtek.loader.FX.prototype */ + { + /** + * @param {string} url + */ + load: function(url) { + var self = this; + + if (!this.rootPath) { + this.rootPath = url.slice(0, url.lastIndexOf('/')); + } + + request.get({ + url: url, + onprogress: function(percent, loaded, total) { + self.trigger('progress', percent, loaded, total); + }, + onerror: function(e) { + self.trigger('error', e); + }, + responseType: 'text', + onload: function(data) { + self.parse(JSON.parse(data)); + } + }); + }, + + /** + * @param {Object} json + * @return {qtek.compositor.Compositor} + */ + parse: function(json) { + var self = this; + var compositor = new Compositor(); + + var lib = { + textures: {}, + shaders: {}, + parameters: {} + }; + var afterLoad = function(shaderLib, textureLib) { + for (var i = 0; i < json.nodes.length; i++) { + var nodeInfo = json.nodes[i]; + var node = self._createNode(nodeInfo, lib); + if (node) { + compositor.addNode(node); + } + } + + self.trigger('success', compositor); + }; + + for (var name in json.parameters) { + var paramInfo = json.parameters[name]; + lib.parameters[name] = this._convertParameter(paramInfo); + } + this._loadShaders(json, function(shaderLib) { + // TODO load texture asynchronous + self._loadTextures(json, lib, function(textureLib) { + lib.textures = textureLib; + lib.shaders = shaderLib; + afterLoad(); + }); + }); + + return compositor; + }, + + _createNode: function(nodeInfo, lib) { + var type = nodeInfo.type || 'filter'; + var shaderSource; + var inputs; + var outputs; + + if (type === 'filter') { + var shaderExp = nodeInfo.shader.trim(); + var res = shaderSourceReg.exec(shaderExp); + if (res) { + shaderSource = Shader.source(res[1].trim()); + } + else if (shaderExp.charAt(0) === '#') { + shaderSource = lib.shaders[shaderExp.substr(1)]; + } + if (!shaderSource) { + shaderSource = shaderExp; + } + if (!shaderSource) { + return; + } + } + + if (nodeInfo.inputs) { + inputs = {}; + for (var name in nodeInfo.inputs) { + if (typeof nodeInfo.inputs[name] === 'string') { + inputs[name] = nodeInfo.inputs[name]; + } + else { + inputs[name] = { + node: nodeInfo.inputs[name].node, + pin: nodeInfo.inputs[name].pin + }; + } + } + } + if (nodeInfo.outputs) { + outputs = {}; + for (var name in nodeInfo.outputs) { + var outputInfo = nodeInfo.outputs[name]; + outputs[name] = {}; + if (outputInfo.attachment != null) { + outputs[name].attachment = outputInfo.attachment; + } + if (outputInfo.keepLastFrame != null) { + outputs[name].keepLastFrame = outputInfo.keepLastFrame; + } + if (outputInfo.outputLastFrame != null) { + outputs[name].outputLastFrame = outputInfo.outputLastFrame; + } + if (typeof(outputInfo.parameters) === 'string') { + var paramExp = outputInfo.parameters; + if (paramExp.charAt(0) === '#') { + outputs[name].parameters = lib.parameters[paramExp.substr(1)]; + } + } + else if (outputInfo.parameters) { + outputs[name].parameters = this._convertParameter(outputInfo.parameters); + } + } + } + var node; + if (type === 'scene') { + node = new CompoSceneNode({ + name: nodeInfo.name, + scene: this.scene, + camera: this.camera, + outputs: outputs + }); + } + else if (type === 'texture') { + node = new CompoTextureNode({ + name: nodeInfo.name, + outputs: outputs + }); + } + // Default is filter + else { + node = new CompoFilterNode({ + name: nodeInfo.name, + shader: shaderSource, + inputs: inputs, + outputs: outputs + }); + } + if (node) { + if (nodeInfo.parameters) { + for (var name in nodeInfo.parameters) { + var val = nodeInfo.parameters[name]; + if (typeof(val) === 'string') { + val = val.trim(); + if (val.charAt(0) === '#') { + val = lib.textures[val.substr(1)]; + } + else { + node.on( + 'beforerender', createSizeSetHandler( + name, tryConvertExpr(val) + ) + ); + } + } + node.setParameter(name, val); + } + } + if (nodeInfo.defines && node.pass) { + for (var name in nodeInfo.defines) { + var val = nodeInfo.defines[name]; + node.pass.material.shader.define('fragment', name, val); + } + } + } + return node; + }, + + _convertParameter: function(paramInfo) { + var param = {}; + if (!paramInfo) { + return param; + } + ['type', 'minFilter', 'magFilter', 'wrapS', 'wrapT', 'flipY', 'useMipmap'] + .forEach(function(name) { + var val = paramInfo[name]; + if (val != null) { + // Convert string to enum + if (typeof val === 'string') { + val = Texture[val]; + } + param[name] = val; + } + }); + ['width', 'height'] + .forEach(function(name) { + if (paramInfo[name] != null) { + var val = paramInfo[name]; + if (typeof val === 'string') { + val = val.trim(); + param[name] = createSizeParser( + name, tryConvertExpr(val) + ); + } + else { + param[name] = val; + } + } + }); + if (paramInfo.useMipmap != null) { + param.useMipmap = paramInfo.useMipmap; + } + return param; + }, + + _loadShaders: function(json, callback) { + if (!json.shaders) { + callback({}); + return; + } + var shaders = {}; + var loading = 0; + var cbd = false; + var shaderRootPath = this.shaderRootPath || this.rootPath; + util.each(json.shaders, function(shaderExp, name) { + var res = urlReg.exec(shaderExp); + if (res) { + var path = res[1]; + path = util.relative2absolute(path, shaderRootPath); + loading++; + request.get({ + url: path, + onload: function(shaderSource) { + shaders[name] = shaderSource; + Shader['import'](shaderSource); + loading--; + if (loading === 0) { + callback(shaders); + cbd = true; + } + } + }); + } + else { + shaders[name] = shaderExp; + // Try import shader + Shader['import'](shaderExp); + } + }, this); + if (loading === 0 && !cbd) { + callback(shaders); + } + }, + + _loadTextures: function(json, lib, callback) { + if (!json.textures) { + callback({}); + return; + } + var textures = {}; + var loading = 0; + + var cbd = false; + var textureRootPath = this.textureRootPath || this.rootPath; + util.each(json.textures, function(textureInfo, name) { + var texture; + var path = textureInfo.path; + var parameters = this._convertParameter(textureInfo.parameters); + if (path instanceof Array && path.length === 6) { + path = path.map(function(item) { + return util.relative2absolute(item, textureRootPath); + }); + texture = new TextureCube(parameters); + } + else if(typeof path === 'string') { + path = util.relative2absolute(path, textureRootPath); + texture = new Texture2D(parameters); + } + else { + return; + } + + texture.load(path); + loading++; + texture.once('success', function() { + textures[name] = texture; + loading--; + if (loading === 0) { + callback(textures); + cbd = true; + } + }); + }, this); + + if (loading === 0 && !cbd) { + callback(textures); + } + } + }); + + function createSizeSetHandler(name, exprFunc) { + return function (renderer) { + // PENDING viewport size or window size + var dpr = renderer.getDevicePixelRatio(); + // PENDING If multiply dpr ? + var width = renderer.getWidth(); + var height = renderer.getHeight(); + var result = exprFunc(width, height, dpr); + this.setParameter(name, result); + }; + } + + function createSizeParser(name, exprFunc) { + return function (renderer) { + var dpr = renderer.getDevicePixelRatio(); + var width = renderer.getWidth(); + var height = renderer.getHeight(); + return exprFunc(width, height, dpr); + }; + } + + function tryConvertExpr(string) { + // PENDING + var exprRes = /^expr\((.*)\)$/.exec(string); + if (exprRes) { + try { + var func = new Function('width', 'height', 'dpr', 'return ' + exprRes[1]); + // Try run t + func(1, 1); + + return func; + } + catch (e) { + throw new Error('Invalid expression.'); + } + } + } + + module.exports = FXLoader; + + +/***/ }, +/* 108 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * glTF Loader + * Specification https://github.com/KhronosGroup/glTF/blob/master/specification/README.md + * + * TODO https://github.com/KhronosGroup/glTF/issues/298 + */ + + + var Base = __webpack_require__(3); + var request = __webpack_require__(19); + var util = __webpack_require__(6); + var vendor = __webpack_require__(51); + + var Scene = __webpack_require__(92); + var Shader = __webpack_require__(52); + var Material = __webpack_require__(53); + var StandardMaterial = __webpack_require__(109); + var Mesh = __webpack_require__(54); + var Node = __webpack_require__(22); + var Texture = __webpack_require__(40); + var Texture2D = __webpack_require__(39); + var TextureCube = __webpack_require__(45); + var shaderLibrary = __webpack_require__(64); + var Skeleton = __webpack_require__(111); + var Joint = __webpack_require__(85); + var PerspectiveCamera = __webpack_require__(31); + var OrthographicCamera = __webpack_require__(30); + var PointLight = __webpack_require__(103); + var SpotLight = __webpack_require__(105); + var DirectionalLight = __webpack_require__(102); + var glenum = __webpack_require__(34); + + var Vector3 = __webpack_require__(23); + var Quaternion = __webpack_require__(24); + var BoundingBox = __webpack_require__(26); + + var SamplerClip = __webpack_require__(15); + var SkinningClip = __webpack_require__(17); + + var StaticGeometry = __webpack_require__(49); + + var glMatrix = __webpack_require__(14); + var quat = glMatrix.quat; + + // Import builtin shader + __webpack_require__(112); + + var semanticAttributeMap = { + 'NORMAL': 'normal', + 'POSITION': 'position', + 'TEXCOORD_0': 'texcoord0', + 'TEXCOORD_1': 'texcoord1', + 'WEIGHT': 'weight', + 'JOINT': 'joint', + 'COLOR': 'color' + }; + + + /** + * @typedef {Object} qtek.loader.GLTF.IResult + * @property {qtek.Scene} scene + * @property {qtek.Node} rootNode + * @property {Object.} cameras + * @property {Object.} textures + * @property {Object.} materials + * @property {Object.} skeletons + * @property {Object.} meshes + * @property {qtek.animation.SkinningClip} clip + */ + + /** + * @constructor qtek.loader.GLTF + * @extends qtek.core.Base + */ + var GLTFLoader = Base.extend( + /** @lends qtek.loader.GLTF# */ + { + /** + * @type {qtek.Node} + */ + rootNode: null, + /** + * @type {string} + */ + rootPath: '', + + /** + * @type {string} + */ + textureRootPath: '', + + /** + * @type {string} + */ + bufferRootPath: '', + + /** + * @type {string} + */ + shaderName: 'qtek.standard', + + /** + * @type {string} + */ + useStandardMaterial: false, + + /** + * @type {boolean} + */ + includeCamera: true, + + /** + * @type {boolean} + */ + includeLight: true, + + /** + * @type {boolean} + */ + includeAnimation: true, + /** + * @type {boolean} + */ + includeMesh: true, + /** + * @type {boolean} + */ + includeTexture: true + }, + + /** @lends qtek.loader.GLTF.prototype */ + { + /** + * @param {string} url + */ + load: function(url) { + var self = this; + + if (!this.rootPath) { + this.rootPath = url.slice(0, url.lastIndexOf('/')); + } + + request.get({ + url: url, + onprogress: function(percent, loaded, total) { + self.trigger('progress', percent, loaded, total); + }, + onerror: function(e) { + self.trigger('error', e); + }, + responseType: 'text', + onload: function(data) { + self.parse(JSON.parse(data)); + } + }); + }, + + /** + * @param {Object} json + * @return {qtek.loader.GLTF.IResult} + */ + parse: function(json) { + var self = this; + var loading = 0; + + var lib = { + buffers: {}, + materials: {}, + textures: {}, + meshes: {}, + joints: {}, + skeletons: {}, + cameras: {}, + nodes: {} + }; + // Mount on the root node if given + var rootNode = this.rootNode || new Scene(); + // Load buffers + util.each(json.buffers, function(bufferInfo, name) { + loading++; + var path = bufferInfo.uri; + + // DEPRECATED compatible with older version(< 1.0) + // https://github.com/KhronosGroup/glTF/wiki/glTF-0.8-to-1.0-Guide + if (path == null) { + path = bufferInfo.path; + } + self._loadBuffer(path, function(buffer) { + lib.buffers[name] = buffer; + loading--; + if (loading === 0) { + afterLoadBuffer(); + } + }, function() { + loading--; + if (loading === 0) { + afterLoadBuffer(); + } + }); + }); + + function afterLoadBuffer() { + if (self.includeMesh) { + if (self.includeTexture) { + self._parseTextures(json, lib); + } + self._parseMaterials(json, lib); + self._parseMeshes(json, lib); + } + self._parseNodes(json, lib); + + var sceneInfo = json.scenes[json.scene]; + for (var i = 0; i < sceneInfo.nodes.length; i++) { + var node = lib.nodes[sceneInfo.nodes[i]]; + node.update(); + rootNode.add(node); + } + + if (self.includeMesh) { + self._parseSkins(json, lib); + } + + var clips = {}; + var skinningClip; + if (self.includeAnimation) { + clips = self._parseAnimations(json, lib); + if (Object.keys(clips).length) { + skinningClip = new SkinningClip(); + // Default loop the skinning animation + skinningClip.setLoop(true); + for (var targetId in clips) { + skinningClip.addJointClip(clips[targetId]); + } + + for (var name in lib.skeletons) { + lib.skeletons[name].addClip(skinningClip); + } + } + } + + self.trigger('success', { + scene: self.rootNode ? null : rootNode, + rootNode: self.rootNode ? rootNode : null, + cameras: lib.cameras, + textures: lib.textures, + materials: lib.materials, + skeletons: lib.skeletons, + meshes: lib.meshes, + clips: lib.clips, + // Main skinning clip + clip: skinningClip + }); + } + + return { + scene: self.rootNode ? null : rootNode, + rootNode: self.rootNode ? rootNode : null, + cameras: lib.cameras, + textures: lib.textures, + materials: lib.materials, + skeletons: lib.skeletons, + meshes: lib.meshes, + clip: null + }; + }, + + _loadBuffer: function(path, onsuccess, onerror) { + var root = this.bufferRootPath || this.rootPath; + if (root) { + path = root + '/' + path; + } + request.get({ + url: path, + responseType: 'arraybuffer', + onload: function(buffer) { + onsuccess && onsuccess(buffer); + }, + onerror: function(buffer) { + onerror && onerror(buffer); + } + }); + }, + + // https://github.com/KhronosGroup/glTF/issues/100 + // https://github.com/KhronosGroup/glTF/issues/193 + _parseSkins: function(json, lib) { + + // Create skeletons and joints + var haveInvBindMatrices = false; + for (var name in json.skins) { + var skinInfo = json.skins[name]; + var skeleton = new Skeleton({ + name: name + }); + for (var i = 0; i < skinInfo.joints.length; i++) { + var jointId = skinInfo.joints[i]; + var joint = new Joint({ + name: jointId, + index: skeleton.joints.length + }); + skeleton.joints.push(joint); + } + if (skinInfo.inverseBindMatrices) { + haveInvBindMatrices = true; + var IBMInfo = skinInfo.inverseBindMatrices; + var bufferViewName = IBMInfo.bufferView; + var bufferViewInfo = json.bufferViews[bufferViewName]; + var buffer = lib.buffers[bufferViewInfo.buffer]; + + var offset = IBMInfo.byteOffset + bufferViewInfo.byteOffset; + var size = IBMInfo.count * 16; + + var array = new vendor.Float32Array(buffer, offset, size); + + skeleton._invBindPoseMatricesArray = array; + skeleton._skinMatricesArray = new vendor.Float32Array(array.length); + } + lib.skeletons[name] = skeleton; + } + + var bindNodeToJoint = function(jointsMap, nodeName, parentIndex, rootNode) { + var node = lib.nodes[nodeName]; + var nodeInfo = json.nodes[nodeName]; + var joint = jointsMap[nodeInfo.jointId]; + if (joint) { + // throw new Error('Joint bind to ' + nodeInfo.name + ' doesn\'t exist in skin'); + joint.node = node; + joint.parentIndex = parentIndex; + joint.rootNode = rootNode; + parentIndex = joint.index; + } + else { + // Some root node may be a simple transform joint, without deformation data. + // Which is, no vertex is attached to the joint + // PENDING + joint = new Joint({ + node: node, + rootNode: rootNode, + parentIndex: parentIndex + }); + } + + for (var i = 0; i < nodeInfo.children.length; i++) { + bindNodeToJoint(jointsMap, nodeInfo.children[i], parentIndex, rootNode); + } + + return joint; + }; + + var getJointIndex = function(joint) { + return joint.index; + }; + + var instanceSkins = {}; + + for (var name in json.nodes) { + + var nodeInfo = json.nodes[name]; + + if (nodeInfo.instanceSkin) { + var skinName = nodeInfo.instanceSkin.skin; + var skeleton = lib.skeletons[skinName]; + instanceSkins[skinName] = skeleton; + + var node = lib.nodes[name]; + var jointIndices = skeleton.joints.map(getJointIndex); + if (node instanceof Mesh) { + node.skeleton = skeleton; + node.joints = jointIndices; + var material = node.material; + if (material instanceof StandardMaterial) { + material.jointCount = jointIndices.length; + } + else { + material.shader = material.shader.clone(); + material.shader.define('vertex', 'SKINNING'); + material.shader.define('vertex', 'JOINT_COUNT', jointIndices.length); + } + } + else { + // Mesh have multiple primitives + for (var i = 0; i < node._children.length; i++) { + var child = node._children[i]; + if (child.skeleton) { + child.skeleton = skeleton; + child.joints = jointIndices; + var material = child.material; + if (material instanceof StandardMaterial) { + material.jointCount = jointIndices.length; + } + else { + material.shader = material.shader.clone(); + material.shader.define('vertex', 'SKINNING'); + material.shader.define('vertex', 'JOINT_COUNT', jointIndices.length); + } + } + } + } + + var jointsMap = {}; + for (var i = 0; i < skeleton.joints.length; i++) { + var joint = skeleton.joints[i]; + jointsMap[joint.name] = joint; + } + // Build up hierarchy from root nodes + var rootNodes = nodeInfo.instanceSkin.skeletons; + for (i = 0; i < rootNodes.length; i++) { + var rootNode = lib.nodes[rootNodes[i]]; + var rootJoint = bindNodeToJoint(jointsMap, rootNodes[i], -1, rootNode); + // Root joint may not in the skeleton + if (rootJoint) { + skeleton.roots.push(rootJoint); + } + } + } + } + + for (var name in instanceSkins) { + var skeleton = instanceSkins[name]; + if (haveInvBindMatrices) { + skeleton.updateMatricesSubArrays(); + } + else { + skeleton.updateJointMatrices(); + } + skeleton.update(); + } + }, + + _parseTextures: function(json, lib) { + var root = this.textureRootPath || this.rootPath; + util.each(json.textures, function(textureInfo, name){ + var samplerInfo = json.samplers[textureInfo.sampler]; + var parameters = {}; + ['wrapS', 'wrapT', 'magFilter', 'minFilter'] + .forEach(function(name) { + var value = samplerInfo[name]; + if (value != null) { + if (typeof(value) === 'string') { + // DEPRECATED, sampler parameter now use gl enum instead of string + value = glenum[value]; + } + parameters[name] = value; + } + }); + + var target = textureInfo.target; + var format = textureInfo.format; + if (typeof(target) === 'string') { + // DEPRECATED + target = glenum[target]; + format = glenum[format]; + } + parameters.format = format; + + if (target === glenum.TEXTURE_2D) { + var texture = new Texture2D(parameters); + var imageInfo = json.images[textureInfo.source]; + texture.load(util.relative2absolute(imageInfo.path, root)); + lib.textures[name] = texture; + } + else if(target === glenum.TEXTURE_CUBE_MAP) { + // TODO + } + }, this); + }, + + // Only phong material is support yet + // TODO support custom material + _parseMaterials: function(json, lib) { + var techniques = {}; + // Parse techniques + for (var name in json.techniques) { + var techniqueInfo = json.techniques[name]; + + // DEPRECATED compatible with older version(< 1.0) + // There are no passes in techniques now + // https://github.com/KhronosGroup/glTF/wiki/glTF-0.8-to-1.0-Guide + if (techniqueInfo.passes) { + techniques[name] = techniqueInfo.passes[techniqueInfo.pass]; + } + else { + techniques[name] = techniqueInfo; + } + } + for (var name in json.materials) { + var materialInfo = json.materials[name]; + + // DEPRECATED compatible with older version(< 1.0) + // There no instanceTechnique in material now. + // https://github.com/KhronosGroup/glTF/wiki/glTF-0.8-to-1.0-Guide + if (materialInfo.instanceTechnique) { + for (var key in materialInfo.instanceTechnique) { + materialInfo[key] = materialInfo.instanceTechnique[key]; + } + materialInfo.instanceTechnique = null; + } + var technique = techniques[materialInfo.technique]; + var uniforms = {}; + + uniforms = materialInfo.values; + for (var symbol in uniforms) { + var value = uniforms[symbol]; + // TODO: texture judgement should be more robust + if (typeof(value) === 'string') { + if (lib.textures[value]) { + uniforms[symbol] = lib.textures[value]; + } + else { + uniforms[symbol] = null; + } + } + } + var enabledTextures = []; + if (uniforms['diffuse'] instanceof Texture2D) { + enabledTextures.push('diffuseMap'); + } + if (uniforms['normalMap'] instanceof Texture2D) { + enabledTextures.push('normalMap'); + } + var material; + var isStandardMaterial = this.useStandardMaterial; + if (isStandardMaterial) { + material = new StandardMaterial({ + name: materialInfo.name + }); + } + else { + material = new Material({ + name: materialInfo.name, + shader: shaderLibrary.get(this.shaderName, enabledTextures) + }); + } + if (technique.states.depthMask != null) { + material.depthMask = technique.states.depthMask; + } + if (technique.states.depthTestEnable != null) { + material.depthTest = technique.states.depthTestEnable; + } + material.cullFace = technique.states.cullFaceEnable || false; + if (technique.states.blendEnable) { + material.transparent = true; + // TODO blend Func and blend Equation + } + + var diffuseProp = uniforms['diffuse']; + if (diffuseProp) { + // Color + if (diffuseProp instanceof Array) { + if (isStandardMaterial) { + material.color = diffuseProp.slice(0, 3); + } + else { + material.set('color', diffuseProp.slice(0, 3)); + } + } + else { // Texture + if (isStandardMaterial) { + material.diffuseMap = diffuseProp; + } + else { + material.set('diffuseMap', diffuseProp); + } + } + } + if (uniforms['normalMap'] != null) { + if (isStandardMaterial) { + material.normalMap = uniforms['normalMap']; + } + else { + material.set('normalMap', uniforms['normalMap']); + } + } + if (uniforms['emission'] != null) { + material.set('emission', uniforms['emission'].slice(0, 3)); + } + if (uniforms['shininess'] != null) { + var glossiness = Math.log(uniforms['shininess']) / Math.log(8192); + // Uniform glossiness + material.set('glossiness', glossiness); + material.set('roughness', 1 - glossiness); + material.set('shininess', uniforms['shininess']); + } + else { + material.set('glossiness', 0.5); + material.set('shininess', 0.5); + } + if (uniforms['specular'] != null) { + material.set('specularColor', uniforms['specular'].slice(0, 3)); + } + if (uniforms['transparency'] != null) { + material.set('alpha', uniforms['transparency']); + } + + lib.materials[name] = material; + } + }, + + _parseMeshes: function(json, lib) { + var self = this; + + var meshKeys = Object.keys(json.meshes); + for (var nn = 0; nn < meshKeys.length; nn++) { + var name = meshKeys[nn]; + var meshInfo = json.meshes[name]; + + lib.meshes[name] = []; + // Geometry + for (var pp = 0; pp < meshInfo.primitives.length; pp++) { + var primitiveInfo = meshInfo.primitives[pp]; + var geometry = new StaticGeometry({ + boundingBox: new BoundingBox() + }); + // Parse attributes + var semantics = Object.keys(primitiveInfo.attributes); + for (var ss = 0; ss < semantics.length; ss++) { + var semantic = semantics[ss]; + var accessorName = primitiveInfo.attributes[semantic]; + var attributeInfo = json.accessors[accessorName]; + var attributeName = semanticAttributeMap[semantic]; + if (!attributeName) { + continue; + } + var attributeType = attributeInfo.componentType; + // DEPRECATED compatible with older version(< 1.0) + if (attributeType == null) { + attributeType = attributeInfo.type; + } + var bufferViewInfo = json.bufferViews[attributeInfo.bufferView]; + var buffer = lib.buffers[bufferViewInfo.buffer]; + var byteOffset = bufferViewInfo.byteOffset + attributeInfo.byteOffset; + + var size; + var ArrayCtor; + var type; + switch(attributeType) { + case 0x8B50: // FLOAT_VEC2 + size = 2; + type = 'float'; + ArrayCtor = vendor.Float32Array; + break; + case 0x8B51: // FLOAT_VEC3 + size = 3; + type = 'float'; + ArrayCtor = vendor.Float32Array; + break; + case 0x8B52: // FLOAT_VEC4 + size = 4; + type = 'float'; + ArrayCtor = vendor.Float32Array; + break; + case 0x1406: // FLOAT + size = 1; + type = 'float'; + ArrayCtor = vendor.Float32Array; + break; + default: + console.warn('Attribute type ' + attributeInfo.type + ' not support yet'); + break; + } + var attributeArray = new ArrayCtor(buffer, byteOffset, attributeInfo.count * size); + if (semantic === 'WEIGHT' && size === 4) { + // Weight data in QTEK has only 3 component, the last component can be evaluated since it is normalized + var weightArray = new ArrayCtor(attributeInfo.count * 3); + for (var i = 0; i < attributeInfo.count; i++) { + weightArray[i * 3] = attributeArray[i * 4]; + weightArray[i * 3 + 1] = attributeArray[i * 4 + 1]; + weightArray[i * 3 + 2] = attributeArray[i * 4 + 2]; + } + geometry.attributes[attributeName].value = weightArray; + } + else { + geometry.attributes[attributeName].value = attributeArray; + } + if (semantic === 'POSITION') { + // Bounding Box + var min = attributeInfo.min; + var max = attributeInfo.max; + if (min) { + geometry.boundingBox.min.set(min[0], min[1], min[2]); + } + if (max) { + geometry.boundingBox.max.set(max[0], max[1], max[2]); + } + } + } + + // Parse indices + var indicesInfo = json.accessors[primitiveInfo.indices]; + + var bufferViewInfo = json.bufferViews[indicesInfo.bufferView]; + var buffer = lib.buffers[bufferViewInfo.buffer]; + var byteOffset = bufferViewInfo.byteOffset + indicesInfo.byteOffset; + + // index uint + if (indicesInfo.componentType === 0x1405) { // UNSIGNED_INT + geometry.indices = new vendor.Uint32Array(buffer, byteOffset, indicesInfo.count); + } + else { // UNSIGNED_SHORT, 0x1403 + geometry.indices = new vendor.Uint16Array(buffer, byteOffset, indicesInfo.count); + } + + var material = lib.materials[primitiveInfo.material]; + //Collada export from blender may not have default material + if (!material) { + material = new Material({ + shader: shaderLibrary.get(self.shaderName) + }); + } + var mesh = new Mesh({ + geometry: geometry, + material: material + }); + if (material.shader.isTextureEnabled('normalMap')) { + if (!mesh.geometry.attributes.tangent.value) { + mesh.geometry.generateTangents(); + } + } + + if (meshInfo.name) { + if (meshInfo.primitives.length > 1) { + mesh.name = [meshInfo.name, pp].join('-'); + } + else { + // PENDING name or meshInfo.name ? + mesh.name = meshInfo.name; + } + } + + lib.meshes[name].push(mesh); + } + } + }, + + _parseNodes: function(json, lib) { + + for (var name in json.nodes) { + var nodeInfo = json.nodes[name]; + var node; + if (nodeInfo.camera && this.includeCamera) { + var cameraInfo = json.cameras[nodeInfo.camera]; + + if (cameraInfo.projection === 'perspective') { + node = new PerspectiveCamera({ + name: nodeInfo.name, + aspect: cameraInfo.aspect_ratio, + fov: cameraInfo.xfov, + far: cameraInfo.zfar, + near: cameraInfo.znear + }); + } else { + // TODO + node = new OrthographicCamera(); + console.warn('TODO:Orthographic camera'); + } + node.setName(nodeInfo.name); + lib.cameras[nodeInfo.name] = node; + } + else if (nodeInfo.lights && this.includeLight) { + var lights = []; + for (var i = 0; i < nodeInfo.lights.length; i++) { + var lightInfo = json.lights[nodeInfo.lights[i]]; + var light = this._parseLight(lightInfo); + if (light) { + lights.push(light); + } + } + if (lights.length == 1) { + // Replace the node with light + node = lights[0]; + node.setName(nodeInfo.name); + } else { + node = new Node(); + node.setName(nodeInfo.name); + for (var i = 0; i < lights.length; i++) { + node.add(lights[i]); + } + } + } + else if ((nodeInfo.meshes || nodeInfo.instanceSkin) && this.includeMesh) { + // TODO one node have multiple meshes ? + var meshKey; + if (nodeInfo.meshes) { + meshKey = nodeInfo.meshes[0]; + } else { + meshKey = nodeInfo.instanceSkin.sources[0]; + } + if (meshKey) { + var primitives = lib.meshes[meshKey]; + if (primitives) { + if (primitives.length === 1) { + // Replace the node with mesh directly + node = primitives[0]; + node.setName(nodeInfo.name); + } else { + node = new Node(); + node.setName(nodeInfo.name); + for (var j = 0; j < primitives.length; j++) { + if (nodeInfo.instanceSkin) { + primitives[j].skeleton = nodeInfo.instanceSkin.skin; + } + node.add(primitives[j]); + } + } + } + } + } else { + node = new Node(); + node.setName(nodeInfo.name); + } + if (nodeInfo.matrix) { + for (var i = 0; i < 16; i++) { + node.localTransform._array[i] = nodeInfo.matrix[i]; + } + node.decomposeLocalTransform(); + } else { + if (nodeInfo.translation) { + node.position.setArray(nodeInfo.translation); + } + if (nodeInfo.rotation) { + // glTF use axis angle in rotation + // https://github.com/KhronosGroup/glTF/issues/144 + // quat.setAxisAngle(node.rotation._array, nodeInfo.rotation.slice(0, 3), nodeInfo.rotation[3]); + // node.rotation._dirty = true; + + // https://github.com/KhronosGroup/glTF/wiki/glTF-0.8-to-1.0-Guide + // From 1.0 rotation use quaternion instead + node.rotation.setArray(nodeInfo.rotation); + } + if (nodeInfo.scale) { + node.scale.setArray(nodeInfo.scale); + } + } + + lib.nodes[name] = node; + } + + // Build hierarchy + for (var name in json.nodes) { + var nodeInfo = json.nodes[name]; + var node = lib.nodes[name]; + if (nodeInfo.children) { + for (var i = 0; i < nodeInfo.children.length; i++) { + var childName = nodeInfo.children[i]; + var child = lib.nodes[childName]; + node.add(child); + } + } + } + }, + + _parseLight: function(lightInfo) { + // TODO Light parameters + switch(lightInfo.type) { + case 'point': + var light = new PointLight({ + name: lightInfo.id, + color: lightInfo.point.color, + }); + break; + case 'spot': + var light = new SpotLight({ + name: lightInfo.id, + color: lightInfo.spot.color + }); + break; + case 'directional': + var light = new DirectionalLight({ + name: lightInfo.id, + color: lightInfo.directional.color + }); + break; + default: + console.warn('Light ' + lightInfo.type + ' not support yet'); + } + + return light; + }, + + _parseAnimations: function(json, lib) { + // TODO Only support nodes animation now + + var nodeAnimationClips = lib.clips = {}; + + var quatTmp = quat.create(); + + for (var animName in json.animations) { + var animationInfo = json.animations[animName]; + var parameters = {}; + + for (var paramName in animationInfo.parameters) { + var accessorName = animationInfo.parameters[paramName]; + var accessorInfo = json.accessors[accessorName]; + + var bufferViewInfo = json.bufferViews[accessorInfo.bufferView]; + var buffer = lib.buffers[bufferViewInfo.buffer]; + var byteOffset = bufferViewInfo.byteOffset + accessorInfo.byteOffset; + switch(accessorInfo.type) { + case 0x8B50: // FLOAT_VEC2 + var size = 2; + break; + case 0x8B51: // FLOAT_VEC3 + var size = 3; + break; + case 0x8B52: // FLOAT_VEC4 + var size = 4; + break; + case 0x1406: // FLOAT + var size = 1; + break; + } + parameters[paramName] = new vendor.Float32Array(buffer, byteOffset, size * accessorInfo.count); + } + + if (!parameters.TIME || !animationInfo.channels.length) { + continue; + } + + // Use the first channels target + var targetId = animationInfo.channels[0].target.id; + var targetNode = lib.nodes[targetId]; + + // glTF use axis angle in rotation, convert to quaternion + // https://github.com/KhronosGroup/glTF/issues/144 + var rotationArr = parameters.rotation; + if (rotationArr) { + for (var i = 0; i < parameters.TIME.length; i++) { + parameters.TIME[i] *= 1000; + var offset = i * 4; + if (rotationArr) { + quatTmp[0] = rotationArr[offset]; + quatTmp[1] = rotationArr[offset + 1]; + quatTmp[2] = rotationArr[offset + 2]; + quat.setAxisAngle(quatTmp, quatTmp, rotationArr[offset + 3]); + parameters.rotation[offset] = quatTmp[0]; + parameters.rotation[offset + 1] = quatTmp[1]; + parameters.rotation[offset + 2] = quatTmp[2]; + parameters.rotation[offset + 3] = quatTmp[3]; + } + } + } + + // TODO + // if (nodeAnimationClips[targetId]) { + // continue; + // } + nodeAnimationClips[targetId] = new SamplerClip({ + name: targetNode.name, + target: targetNode + }); + var nodeAnimationClip = nodeAnimationClips[targetId]; + nodeAnimationClip.channels.time = parameters.TIME; + nodeAnimationClip.channels.rotation = parameters.rotation || null; + nodeAnimationClip.channels.position = parameters.translation || null; + nodeAnimationClip.channels.scale = parameters.scale || null; + nodeAnimationClip.life = parameters.TIME[parameters.TIME.length - 1]; + } + + return nodeAnimationClips; + } + }); + + module.exports = GLTFLoader; + + +/***/ }, +/* 109 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Material = __webpack_require__(53); + + var Shader = __webpack_require__(52); + // Import standard shader + Shader['import'](__webpack_require__(110)); + + var shaderLibrary = {}; + var shaderUsedCount = {}; + + var TEXTURE_PROPERTIES = ['diffuseMap', 'normalMap', 'roughnessMap', 'metalnessMap', 'emissiveMap', 'environmentMap', 'brdfLookup', 'ssaoMap', 'aoMap']; + var SIMPLE_PROPERTIES = ['color', 'emission', 'emissionIntensity', 'alpha', 'roughness', 'metalness', 'uvRepeat', 'uvOffset', 'aoIntensity']; + var PROPERTIES_CHANGE_SHADER = ['jointCount', 'linear', 'encodeRGBM', 'decodeRGBM']; + + var OTHER_SHADER_KEYS = [ + 'environmentMapPrefiltered', + 'linear', + 'encodeRGBM', + 'decodeRGBM', + 'parallaxCorrected' + ]; + var SHADER_KEYS = TEXTURE_PROPERTIES.concat(OTHER_SHADER_KEYS); + + var KEY_OFFSETS = SHADER_KEYS.reduce(function (obj, name, idx) { + obj[name] = 256 << idx; + return obj; + }, {}); + + function makeKey (enabledMaps, jointCount, shaderDefines) { + // jointCount from 0 to 255 + var key = jointCount; + for (var i = 0; i < enabledMaps.length; i++) { + key += KEY_OFFSETS[enabledMaps[i]]; + } + for (var i = 0; i < OTHER_SHADER_KEYS.length; i++) { + var propName = OTHER_SHADER_KEYS[i]; + if (shaderDefines[propName]) { + key += KEY_OFFSETS[propName]; + } + } + + return key; + } + + function allocateShader (enabledMaps, jointCount, shaderDefines) { + var key = makeKey(enabledMaps, jointCount, shaderDefines); + var shader = shaderLibrary[key]; + + if (!shader) { + shader = new Shader({ + vertex: Shader.source('qtek.standard.vertex'), + fragment: Shader.source('qtek.standard.fragment') + }); + shader.enableTexture(enabledMaps); + shader.define('fragment', 'USE_METALNESS'); + shader.define('fragment', 'USE_ROUGHNESS'); + if (jointCount) { + shader.define('vertex', 'SKINNING'); + shader.define('vertex', 'JOINT_COUNT', jointCount); + } + if (shaderDefines.environmentMapPrefiltered) { + shader.define('fragment', 'ENVIRONMENTMAP_PREFILTER'); + } + if (shaderDefines.linear) { + shader.define('fragment', 'SRGB_DECODE'); + } + if (shaderDefines.encodeRGBM) { + shader.define('fragment', 'RGBM_ENCODE'); + } + if (shaderDefines.decodeRGBM) { + shader.define('fragment', 'RGBM_DECODE'); + } + if (shaderDefines.parallaxCorrected) { + shader.define('fragment', 'PARALLAX_CORRECTED'); + } + + shaderLibrary[key] = shader; + shaderUsedCount[key] = 0; + } + shaderUsedCount[key]++; + + shader.__key__ = key; + + return shader; + } + function releaseShader (shader, _gl) { + var key = shader.__key__; + if (shaderLibrary[key]) { + shaderUsedCount[key]--; + if (!shaderUsedCount[key]) { + delete shaderLibrary[key]; + delete shaderUsedCount[key]; + + if (_gl) { + // Since shader may not be used on any material. We need to dispose it + shader.dispose(_gl); + } + } + } + } + + var StandardMaterial = Material.extend(function () { + + return { + + /** + * @type {Array.} + * @name color + * @default [1, 1, 1] + */ + color: [1, 1, 1], + + /** + * @type {Array.} + * @name emission + * @default [0, 0, 0] + */ + emission: [0, 0, 0], + + /** + * @type {number} + * @name emissionIntensity + * @default 0 + */ + emissionIntensity: 0, + + /** + * @type {number} + * @name roughness + * @default 0.5 + */ + roughness: 0.5, + + /** + * @type {number} + * @name metalness + * @default 0 + */ + metalness: 0, + + /** + * @type {number} + * @name alpha + * @default 1 + */ + alpha: 1, + + + /** + * @type {qtek.Texture2D} + * @name diffuseMap + */ + + /** + * @type {qtek.Texture2D} + * @name normalMap + */ + + /** + * @type {qtek.Texture2D} + * @name roughnessMap + */ + + /** + * @type {qtek.Texture2D} + * @name metalnessMap + */ + /** + * @type {qtek.Texture2D} + * @name emissiveMap + */ + + /** + * @type {qtek.TextureCube} + * @name environmentMap + */ + + /** + * @type {qtek.math.BoundingBox} + * @name environmentBox + */ + + /** + * BRDF Lookup is generated by qtek.util.cubemap.integrateBrdf + * @type {qtek.Texture2D} + * @name brdfLookup + */ + + /** + * @type {qtek.Texture2D} + * @name ssaoMap + */ + + /** + * @type {qtek.Texture2D} + * @name aoMap + */ + + /** + * @type {Array.} + * @name uvRepeat + * @default [1, 1] + */ + uvRepeat: [1, 1], + + /** + * @type {Array.} + * @name uvOffset + * @default [0, 0] + */ + uvOffset: [0, 0], + + /** + * @type {number} + * @default 1 + */ + aoIntensity: 1, + + /** + * @type {number} + * @name jointCount + * @default 0 + */ + // FIXME Redundant with mesh + jointCount: 0, + + /** + * @type {boolean} + * @name environmentMapPrefiltered + */ + environmentMapPrefiltered: false, + + /** + * @type {boolean} + * @name linear + */ + linear: false, + + /** + * @type {boolean} + * @name encodeRGBM + */ + encodeRGBM: false, + + /** + * @type {boolean} + * @name decodeRGBM + */ + decodeRGBM: false + }; + }, { + + _doUpdateShader: function (gl) { + var enabledTextures = TEXTURE_PROPERTIES.filter(function (name) { + return !!this[name]; + }, this); + if (this._shader) { + releaseShader(this._shader, gl); + this._shader.detached(); + } + + var shader = allocateShader( + enabledTextures, this.jointCount || 0, { + environmentMapPrefiltered: this.environmentMapPrefiltered, + linear: this.linear, + encodeRGBM: this.encodeRGBM, + decodeRGBM: this.decodeRGBM, + parallaxCorrected: !!this._environmentBox + } + ); + var originalUniforms = this.uniforms; + + // Ignore if uniform can use in shader. + this.uniforms = shader.createUniforms(); + this._shader = shader; + + var uniforms = this.uniforms; + this._enabledUniforms = Object.keys(uniforms); + + // Keep uniform + for (var symbol in originalUniforms) { + if (uniforms[symbol]) { + uniforms[symbol].value = originalUniforms[symbol].value; + } + } + + shader.attached(); + + this._shaderDirty = false; + }, + + updateShader: function (gl) { + if (this._shaderDirty) { + this._doUpdateShader(gl); + this._shaderDirty = false; + } + }, + + attachShader: function () { + // Do nothing. + // console.warn('StandardMaterial can\'t change shader'); + }, + + dispose: function (gl, disposeTexture) { + if (this._shader) { + releaseShader(this._shader); + } + Material.prototype.dispose.call(gl, disposeTexture); + }, + + + clone: function () { + var material = new StandardMaterial({ + name: this.name + }); + TEXTURE_PROPERTIES.forEach(function (propName) { + if (this[propName]) { + material[propName] = this[propName]; + } + }, this); + SIMPLE_PROPERTIES.concat(PROPERTIES_CHANGE_SHADER).forEach(function (propName) { + material[propName] = this[propName]; + }, this); + return material; + } + }); + + SIMPLE_PROPERTIES.forEach(function (propName) { + Object.defineProperty(StandardMaterial.prototype, propName, { + get: function () { + return this.get(propName); + }, + set: function (value) { + var uniforms = this.uniforms = this.uniforms || {}; + uniforms[propName] = uniforms[propName] || { + value: null + }; + this.setUniform(propName, value); + } + }); + }); + + TEXTURE_PROPERTIES.forEach(function (propName) { + Object.defineProperty(StandardMaterial.prototype, propName, { + get: function () { + return this.get(propName); + }, + set: function (value) { + var uniforms = this.uniforms = this.uniforms || {}; + uniforms[propName] = uniforms[propName] || { + value: null + }; + + var oldVal = this.get(propName); + this.setUniform(propName, value); + + if (!oldVal !== !value) { + this._shaderDirty = true; + } + } + }); + }); + + PROPERTIES_CHANGE_SHADER.forEach(function (propName) { + var privateKey = '_' + propName; + Object.defineProperty(StandardMaterial.prototype, propName, { + get: function () { + return this[privateKey]; + }, + set: function (value) { + var oldVal = this[privateKey]; + this[privateKey] = value; + if (oldVal !== value) { + this._shaderDirty = true; + } + } + }); + }); + + Object.defineProperty(StandardMaterial.prototype, 'environmentBox', { + get: function () { + var envBox = this._environmentBox; + if (envBox) { + envBox.min.setArray(this.get('environmentBoxMin')); + envBox.max.setArray(this.get('environmentBoxMax')); + } + return envBox; + }, + + set: function (value) { + var oldVal = this._environmentBox; + this._environmentBox = value; + + var uniforms = this.uniforms = this.uniforms || {}; + uniforms['environmentBoxMin'] = uniforms['environmentBoxMin'] || { + value: null + }; + uniforms['environmentBoxMax'] = uniforms['environmentBoxMax'] || { + value: null + }; + + // TODO Can't detect operation like box.min = new Vector() + if (value) { + this.setUniform('environmentBoxMin', value.min._array); + this.setUniform('environmentBoxMax', value.max._array); + } + + if (oldVal !== value) { + this._shaderDirty = true; + } + } + }); + + Object.defineProperty(StandardMaterial.prototype, 'shader', { + get: function () { + // PENDING + if (!this._shader) { + this._shaderDirty = true; + this.updateShader(); + } + return this._shader; + }, + set: function () { + console.warn('StandardMaterial can\'t change shader'); + } + }); + + module.exports = StandardMaterial; + + +/***/ }, +/* 110 */ +/***/ function(module, exports) { + + + module.exports = "\n\n@export qtek.standard.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\n\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n\n#if defined(AOMAP_ENABLED)\nattribute vec2 texcoord2 : TEXCOORD_1;\n#endif\n\nattribute vec3 normal : NORMAL;\nattribute vec4 tangent : TANGENT;\n\n#ifdef VERTEX_COLOR\nattribute vec4 color : COLOR;\n#endif\n\nattribute vec3 barycentric;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_COUNT] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\n\n#ifdef NORMALMAP_ENABLED\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\n#ifdef VERTEX_COLOR\nvarying vec4 v_Color;\n#endif\n\n\n#if defined(AOMAP_ENABLED)\nvarying vec2 v_Texcoord2;\n#endif\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n#ifdef SKINNING\n\n @import qtek.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n skinnedTangent = (skinMatrixWS * vec4(tangent.xyz, 0.0)).xyz;\n#endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n v_Barycentric = barycentric;\n\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\n\n#ifdef NORMALMAP_ENABLED\n v_Tangent = normalize((worldInverseTranspose * vec4(skinnedTangent, 0.0)).xyz);\n v_Bitangent = normalize(cross(v_Normal, v_Tangent) * tangent.w);\n#endif\n\n#ifdef VERTEX_COLOR\n v_Color = color;\n#endif\n\n#if defined(AOMAP_ENABLED)\n v_Texcoord2 = texcoord2;\n#endif\n}\n\n@end\n\n\n@export qtek.standard.fragment\n\n#define PI 3.14159265358979\n\n#define GLOSS_CHANEL 0\n#define ROUGHNESS_CHANEL 0\n#define METALNESS_CHANEL 1\n\nuniform mat4 viewInverse : VIEWINVERSE;\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\n\n#ifdef NORMALMAP_ENABLED\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\nuniform sampler2D normalMap;\n#endif\n\n#ifdef DIFFUSEMAP_ENABLED\nuniform sampler2D diffuseMap;\n#endif\n\n#ifdef SPECULARMAP_ENABLED\nuniform sampler2D specularMap;\n#endif\n\n#ifdef USE_ROUGHNESS\nuniform float roughness : 0.5;\n #ifdef ROUGHNESSMAP_ENABLED\nuniform sampler2D roughnessMap;\n #endif\n#else\nuniform float glossiness: 0.5;\n #ifdef GLOSSMAP_ENABLED\nuniform sampler2D glossMap;\n #endif\n#endif\n\n#ifdef METALNESSMAP_ENABLED\nuniform sampler2D metalnessMap;\n#endif\n\n#ifdef ENVIRONMENTMAP_ENABLED\nuniform samplerCube environmentMap;\n\n #ifdef PARALLAX_CORRECTED\nuniform vec3 environmentBoxMin;\nuniform vec3 environmentBoxMax;\n #endif\n\n#endif\n\n#ifdef BRDFLOOKUP_ENABLED\nuniform sampler2D brdfLookup;\n#endif\n\n#ifdef EMISSIVEMAP_ENABLED\nuniform sampler2D emissiveMap;\n#endif\n\n#ifdef SSAOMAP_ENABLED\nuniform sampler2D ssaoMap;\nuniform vec4 viewport : VIEWPORT;\n#endif\n\n#ifdef AOMAP_ENABLED\nuniform sampler2D aoMap;\nuniform float aoIntensity;\nvarying vec2 v_Texcoord2;\n#endif\n\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\n\n\n#ifdef USE_METALNESS\nuniform float metalness : 0.0;\n#else\nuniform vec3 specularColor : [0.1, 0.1, 0.1];\n#endif\n\nuniform vec3 emission : [0.0, 0.0, 0.0];\n\nuniform float emissionIntensity: 1;\n\nuniform float lineWidth : 0.0;\nuniform vec3 lineColor : [0.0, 0.0, 0.0];\nvarying vec3 v_Barycentric;\n\n#ifdef ENVIRONMENTMAP_PREFILTER\nuniform float maxMipmapLevel: 5;\n#endif\n\n#ifdef AMBIENT_LIGHT_COUNT\n@import qtek.header.ambient_light\n#endif\n\n#ifdef AMBIENT_SH_LIGHT_COUNT\n@import qtek.header.ambient_sh_light\n#endif\n\n#ifdef AMBIENT_CUBEMAP_LIGHT_COUNT\n@import qtek.header.ambient_cubemap_light\n#endif\n\n#ifdef POINT_LIGHT_COUNT\n@import qtek.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_COUNT\n@import qtek.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_COUNT\n@import qtek.header.spot_light\n#endif\n\n@import qtek.util.calculate_attenuation\n\n@import qtek.util.edge_factor\n\n@import qtek.util.rgbm\n\n@import qtek.util.srgb\n\n@import qtek.plugin.compute_shadow_map\n\n@import qtek.util.parallax_correct\n\n\nfloat G_Smith(float g, float ndv, float ndl)\n{\n float roughness = 1.0 - g;\n float k = roughness * roughness / 2.0;\n float G1V = ndv / (ndv * (1.0 - k) + k);\n float G1L = ndl / (ndl * (1.0 - k) + k);\n return G1L * G1V;\n}\nvec3 F_Schlick(float ndv, vec3 spec) {\n return spec + (1.0 - spec) * pow(1.0 - ndv, 5.0);\n}\n\nfloat D_Phong(float g, float ndh) {\n float a = pow(8192.0, g);\n return (a + 2.0) / 8.0 * pow(ndh, a);\n}\n\nfloat D_GGX(float g, float ndh) {\n float r = 1.0 - g;\n float a = r * r;\n float tmp = ndh * ndh * (a - 1.0) + 1.0;\n return a / (PI * tmp * tmp);\n}\n\n\nvoid main()\n{\n vec4 albedoColor = vec4(color, alpha);\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = normalize(eyePos - v_WorldPosition);\n\n#ifdef DIFFUSEMAP_ENABLED\n vec4 texel = texture2D(diffuseMap, v_Texcoord);\n #ifdef SRGB_DECODE\n texel = sRGBToLinear(texel);\n #endif\n albedoColor.rgb *= texel.rgb;\n #ifdef DIFFUSEMAP_ALPHA_ALPHA\n albedoColor.a *= texel.a;\n #endif\n#endif\n\n\n#ifdef USE_METALNESS\n float m = metalness;\n\n #ifdef METALNESSMAP_ENABLED\n float m2 = texture2D(metalnessMap, v_Texcoord)[METALNESS_CHANEL];\n m = clamp(m2 + (m - 0.5) * 2.0, 0.0, 1.0);\n #endif\n\n vec3 baseColor = albedoColor.rgb;\n albedoColor.rgb = baseColor * (1.0 - m);\n vec3 spec = mix(vec3(0.04), baseColor, m);\n#else\n vec3 spec = specularColor;\n#endif\n\n#ifdef USE_ROUGHNESS\n float g = 1.0 - roughness;\n #ifdef ROUGHNESSMAP_ENABLED\n float g2 = 1.0 - texture2D(roughnessMap, v_Texcoord)[ROUGHNESS_CHANEL];\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\n #endif\n#else\n float g = glossiness;\n #ifdef GLOSSMAP_ENABLED\n float g2 = texture2D(glossMap, v_Texcoord)[GLOSS_CHANEL];\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\n #endif\n#endif\n\n#ifdef SPECULARMAP_ENABLED\n spec *= texture2D(specularMap, v_Texcoord).rgb;\n#endif\n\n vec3 N = v_Normal;\n#ifdef NORMALMAP_ENABLED\n if (dot(v_Tangent, v_Tangent) > 0.0) {\n vec3 normalTexel = texture2D(normalMap, v_Texcoord).xyz;\n if (dot(normalTexel, normalTexel) > 0.0) { N = normalTexel * 2.0 - 1.0;\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\n N = normalize(tbn * N);\n }\n }\n#endif\n\n vec3 diffuseTerm = vec3(0.0, 0.0, 0.0);\n vec3 specularTerm = vec3(0.0, 0.0, 0.0);\n\n float ndv = clamp(dot(N, V), 0.0, 1.0);\n vec3 fresnelTerm = F_Schlick(ndv, spec);\n\n#ifdef AMBIENT_LIGHT_COUNT\n for(int _idx_ = 0; _idx_ < AMBIENT_LIGHT_COUNT; _idx_++)\n {{\n diffuseTerm += ambientLightColor[_idx_];\n }}\n#endif\n\n#ifdef AMBIENT_SH_LIGHT_COUNT\n for(int _idx_ = 0; _idx_ < AMBIENT_SH_LIGHT_COUNT; _idx_++)\n {{\n diffuseTerm += calcAmbientSHLight(_idx_, N) * ambientSHLightColor[_idx_];\n }}\n#endif\n\n#ifdef POINT_LIGHT_COUNT\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\n float shadowContribsPoint[POINT_LIGHT_COUNT];\n if(shadowEnabled)\n {\n computeShadowOfPointLights(v_WorldPosition, shadowContribsPoint);\n }\n#endif\n for(int _idx_ = 0; _idx_ < POINT_LIGHT_COUNT; _idx_++)\n {{\n\n vec3 lightPosition = pointLightPosition[_idx_];\n vec3 lc = pointLightColor[_idx_];\n float range = pointLightRange[_idx_];\n\n vec3 L = lightPosition - v_WorldPosition;\n\n float dist = length(L);\n float attenuation = lightAttenuation(dist, range);\n L /= dist;\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n float shadowContrib = 1.0;\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribsPoint[_idx_];\n }\n#endif\n\n vec3 li = lc * ndl * attenuation * shadowContrib;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }}\n#endif\n\n#ifdef DIRECTIONAL_LIGHT_COUNT\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n float shadowContribsDir[DIRECTIONAL_LIGHT_COUNT];\n if(shadowEnabled)\n {\n computeShadowOfDirectionalLights(v_WorldPosition, shadowContribsDir);\n }\n#endif\n for(int _idx_ = 0; _idx_ < DIRECTIONAL_LIGHT_COUNT; _idx_++)\n {{\n\n vec3 L = -normalize(directionalLightDirection[_idx_]);\n vec3 lc = directionalLightColor[_idx_];\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n float shadowContrib = 1.0;\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribsDir[_idx_];\n }\n#endif\n\n vec3 li = lc * ndl * shadowContrib;\n\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }}\n#endif\n\n#ifdef SPOT_LIGHT_COUNT\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\n float shadowContribsSpot[SPOT_LIGHT_COUNT];\n if(shadowEnabled)\n {\n computeShadowOfSpotLights(v_WorldPosition, shadowContribsSpot);\n }\n#endif\n for(int i = 0; i < SPOT_LIGHT_COUNT; i++)\n {\n vec3 lightPosition = spotLightPosition[i];\n vec3 spotLightDirection = -normalize(spotLightDirection[i]);\n vec3 lc = spotLightColor[i];\n float range = spotLightRange[i];\n float a = spotLightUmbraAngleCosine[i];\n float b = spotLightPenumbraAngleCosine[i];\n float falloffFactor = spotLightFalloffFactor[i];\n\n vec3 L = lightPosition - v_WorldPosition;\n float dist = length(L);\n float attenuation = lightAttenuation(dist, range);\n\n L /= dist;\n float c = dot(spotLightDirection, L);\n\n float falloff;\n falloff = clamp((c - a) /( b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n float shadowContrib = 1.0;\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\n if (shadowEnabled)\n {\n shadowContrib = shadowContribsSpot[i];\n }\n#endif\n\n vec3 li = lc * attenuation * (1.0 - falloff) * shadowContrib * ndl;\n\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }\n#endif\n\n vec4 outColor = albedoColor;\n outColor.rgb *= diffuseTerm;\n\n outColor.rgb += specularTerm;\n\n\n#ifdef AMBIENT_CUBEMAP_LIGHT_COUNT\n vec3 L = reflect(-V, N);\n float rough2 = clamp(1.0 - g, 0.0, 1.0);\n float bias2 = rough2 * 5.0;\n vec2 brdfParam2 = texture2D(ambientCubemapLightBRDFLookup[0], vec2(rough2, ndv)).xy;\n vec3 envWeight2 = spec * brdfParam2.x + brdfParam2.y;\n vec3 envTexel2;\n for(int _idx_ = 0; _idx_ < AMBIENT_CUBEMAP_LIGHT_COUNT; _idx_++)\n {{\n envTexel2 = RGBMDecode(textureCubeLodEXT(ambientCubemapLightCubemap[_idx_], L, bias2), 51.5);\n outColor.rgb += ambientCubemapLightColor[_idx_] * envTexel2 * envWeight2;\n }}\n#endif\n\n#ifdef ENVIRONMENTMAP_ENABLED\n\n vec3 envWeight = g * fresnelTerm;\n vec3 L = reflect(-V, N);\n\n #ifdef PARALLAX_CORRECTED\n L = parallaxCorrect(L, v_WorldPosition, environmentBoxMin, environmentBoxMax);\n #endif\n\n #ifdef ENVIRONMENTMAP_PREFILTER\n float rough = clamp(1.0 - g, 0.0, 1.0);\n float bias = rough * maxMipmapLevel;\n vec3 envTexel = decodeHDR(textureCubeLodEXT(environmentMap, L, bias)).rgb;\n\n #ifdef BRDFLOOKUP_ENABLED\n vec2 brdfParam = texture2D(brdfLookup, vec2(rough, ndv)).xy;\n envWeight = spec * brdfParam.x + brdfParam.y;\n #endif\n\n #else\n vec3 envTexel = textureCube(environmentMap, L).xyz;\n #endif\n\n outColor.rgb += envTexel * envWeight;\n#endif\n\n float aoFactor = 1.0;\n#ifdef SSAOMAP_ENABLED\n aoFactor = min(texture2D(ssaoMap, (gl_FragCoord.xy - viewport.xy) / viewport.zw).r, aoFactor);\n#endif\n\n#ifdef AOMAP_ENABLED\n aoFactor = min(1.0 - clamp((1.0 - texture2D(aoMap, v_Texcoord2).r) * aoIntensity, 0.0, 1.0), aoFactor);\n#endif\n\n outColor.rgb *= aoFactor;\n\n vec3 lEmission = emission;\n#ifdef EMISSIVEMAP_ENABLED\n lEmission *= texture2D(emissiveMap, v_Texcoord).rgb;\n#endif\n outColor.rgb += lEmission * emissionIntensity;\n\n#ifdef GAMMA_ENCODE\n outColor.rgb = pow(outColor.rgb, vec3(1 / 2.2));\n#endif\n\n if(lineWidth > 0.)\n {\n outColor.rgb = mix(lineColor, vec3(outColor.rgb), edgeFactor(lineWidth));\n }\n\n gl_FragColor = encodeHDR(outColor);\n}\n\n@end\n"; + + +/***/ }, +/* 111 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Base = __webpack_require__(3); + var Joint = __webpack_require__(85); + + var glMatrix = __webpack_require__(14); + var quat = glMatrix.quat; + var vec3 = glMatrix.vec3; + var mat4 = glMatrix.mat4; + + /** + * @constructor qtek.Skeleton + */ + var Skeleton = Base.extend(function() { + return /** @lends qtek.Skeleton# */{ + /** + * @type {string} + */ + name: '', + + /** + * root joints + * @type {Array.} + */ + roots: [], + + /** + * joints + * @type {Array.} + */ + joints: [], + + _clips: [], + + // Matrix to joint space (relative to root joint) + _invBindPoseMatricesArray: null, + + // Use subarray instead of copy back each time computing matrix + // http://jsperf.com/subarray-vs-copy-for-array-transform/5 + _jointMatricesSubArrays: [], + + // jointMatrix * currentPoseMatrix + // worldTransform is relative to the root bone + // still in model space not world space + _skinMatricesArray: null, + + _skinMatricesSubArrays: [], + + _subSkinMatricesArray: {} + }; + }, + /** @lends qtek.Skeleton.prototype */ + { + /** + * Update joints hierarchy + */ + updateHierarchy: function() { + this.roots = []; + var joints = this.joints; + for (var i = 0; i < joints.length; i++) { + var joint = joints[i]; + if (joint.parentIndex >= 0) { + var parent = joints[joint.parentIndex].node; + parent.add(joint.node); + }else{ + this.roots.push(joint); + } + } + }, + + /** + * Add a skinning clip and create a map between clip and skeleton + * @param {qtek.animation.SkinningClip} clip + * @param {Object} [mapRule] Map between joint name in skeleton and joint name in clip + */ + addClip: function(clip, mapRule) { + // Clip have been exists in + for (var i = 0; i < this._clips.length; i++) { + if (this._clips[i].clip === clip) { + return; + } + } + // Map the joint index in skeleton to joint pose index in clip + var maps = []; + for (var i = 0; i < this.joints.length; i++) { + maps[i] = -1; + } + // Create avatar + for (var i = 0; i < clip.jointClips.length; i++) { + for (var j = 0; j < this.joints.length; j++) { + var joint = this.joints[j]; + var jointPose = clip.jointClips[i]; + var jointName = joint.name; + if (mapRule) { + jointName = mapRule[jointName]; + } + if (jointPose.name === jointName) { + maps[j] = i; + break; + } + } + } + + this._clips.push({ + maps: maps, + clip: clip + }); + + return this._clips.length - 1; + }, + + /** + * @param {qtek.animation.SkinningClip} clip + */ + removeClip: function(clip) { + var idx = -1; + for (var i = 0; i < this._clips.length; i++) { + if (this._clips[i].clip === clip) { + idx = i; + break; + } + } + if (idx > 0) { + this._clips.splice(idx, 1); + } + }, + /** + * Remove all clips + */ + removeClipsAll: function() { + this._clips = []; + }, + + /** + * Get clip by index + * @param {number} index + */ + getClip: function(index) { + if (this._clips[index]) { + return this._clips[index].clip; + } + }, + + /** + * @return {number} + */ + getClipNumber: function() { + return this._clips.length; + }, + + /** + * Calculate joint matrices from node transform + * @method + */ + updateJointMatrices: (function() { + + var m4 = mat4.create(); + + return function() { + for (var i = 0; i < this.roots.length; i++) { + this.roots[i].node.update(true); + } + this._invBindPoseMatricesArray = new Float32Array(this.joints.length * 16); + this._skinMatricesArray = new Float32Array(this.joints.length * 16); + + for (var i = 0; i < this.joints.length; i++) { + var joint = this.joints[i]; + // Joint space is relative to root joint's parent, if have + // !!Parent node and joint node must all be updated + if (joint.rootNode && joint.rootNode.getParent()) { + mat4.invert(m4, joint.rootNode.getParent().worldTransform._array); + mat4.multiply( + m4, + m4, + joint.node.worldTransform._array + ); + mat4.invert(m4, m4); + } else { + mat4.copy(m4, joint.node.worldTransform._array); + mat4.invert(m4, m4); + } + + var offset = i * 16; + for (var j = 0; j < 16; j++) { + this._invBindPoseMatricesArray[offset + j] = m4[j]; + } + } + + this.updateMatricesSubArrays(); + }; + })(), + + updateMatricesSubArrays: function() { + for (var i = 0; i < this.joints.length; i++) { + this._jointMatricesSubArrays[i] = this._invBindPoseMatricesArray.subarray(i * 16, (i+1) * 16); + this._skinMatricesSubArrays[i] = this._skinMatricesArray.subarray(i * 16, (i+1) * 16); + } + }, + + /** + * Update skinning matrices + */ + update: (function() { + var m4 = mat4.create(); + return function() { + for (var i = 0; i < this.roots.length; i++) { + this.roots[i].node.update(true); + } + + for (var i = 0; i < this.joints.length; i++) { + var joint = this.joints[i]; + mat4.multiply( + this._skinMatricesSubArrays[i], + joint.node.worldTransform._array, + this._jointMatricesSubArrays[i] + ); + + // Joint space is relative to root joint's parent, if have + // PENDING + if (joint.rootNode && joint.rootNode.getParent()) { + mat4.invert(m4, joint.rootNode.getParent().worldTransform._array); + mat4.multiply( + this._skinMatricesSubArrays[i], + m4, + this._skinMatricesSubArrays[i] + ); + } + } + }; + })(), + + getSubSkinMatrices: function(meshId, joints) { + var subArray = this._subSkinMatricesArray[meshId]; + if (!subArray) { + subArray + = this._subSkinMatricesArray[meshId] + = new Float32Array(joints.length * 16); + } + var cursor = 0; + for (var i = 0; i < joints.length; i++) { + var idx = joints[i]; + for (var j = 0; j < 16; j++) { + subArray[cursor++] = this._skinMatricesArray[idx * 16 + j]; + } + } + return subArray; + }, + + /** + * Set pose and update skinning matrices + * @param {number} clipIndex + */ + setPose: function(clipIndex) { + if (!this._clips[clipIndex]) { + return; + } + + var clip = this._clips[clipIndex].clip; + var maps = this._clips[clipIndex].maps; + + for (var i = 0; i < this.joints.length; i++) { + var joint = this.joints[i]; + if (maps[i] === -1) { + continue; + } + var pose = clip.jointClips[maps[i]]; + + vec3.copy(joint.node.position._array, pose.position); + quat.copy(joint.node.rotation._array, pose.rotation); + vec3.copy(joint.node.scale._array, pose.scale); + + joint.node.position._dirty = true; + joint.node.rotation._dirty = true; + joint.node.scale._dirty = true; + } + this.update(); + }, + + clone: function (rootNode, newRootNode) { + var skeleton = new Skeleton(); + skeleton.name = this.name; + + for (var i = 0; i < this.joints.length; i++) { + var newJoint = new Joint(); + newJoint.name = this.joints[i].name; + newJoint.index = this.joints[i].index; + newJoint.parentIndex = this.joints[i].parentIndex; + + var path = this.joints[i].node.getPath(rootNode); + var rootNodePath = this.joints[i].rootNode.getPath(rootNode); + + if (path != null && rootNodePath != null) { + newJoint.node = newRootNode.queryNode(path); + newJoint.rootNode = newRootNode.queryNode(rootNodePath); + } else { + // PENDING + console.warn('Something wrong in clone, may be the skeleton root nodes is not mounted on the cloned root node.') + } + skeleton.joints.push(newJoint); + } + for (var i = 0; i < this.roots.length; i++) { + skeleton.roots.push(skeleton.joints[this.roots[i].index]); + } + + if (this._invBindPoseMatricesArray) { + var len = this._invBindPoseMatricesArray.length; + skeleton._invBindPoseMatricesArray = new Float32Array(len); + for (var i = 0; i < len; i++) { + skeleton._invBindPoseMatricesArray[i] = this._invBindPoseMatricesArray[i]; + } + + skeleton._skinMatricesArray = new Float32Array(len); + + skeleton.updateMatricesSubArrays(); + } + + skeleton.update(); + + return skeleton; + } + }); + + module.exports = Skeleton; + + +/***/ }, +/* 112 */ +/***/ function(module, exports, __webpack_require__) { + + + + var library = __webpack_require__(64); + var Shader = __webpack_require__(52); + + + Shader['import'](__webpack_require__(73)); + + // Some build in shaders + Shader['import'](__webpack_require__(96)); + Shader['import'](__webpack_require__(113)); + Shader['import'](__webpack_require__(110)); + Shader['import'](__webpack_require__(114)); + Shader['import'](__webpack_require__(91)); + Shader['import'](__webpack_require__(83)); + + library.template('qtek.basic', Shader.source('qtek.basic.vertex'), Shader.source('qtek.basic.fragment')); + library.template('qtek.lambert', Shader.source('qtek.lambert.vertex'), Shader.source('qtek.lambert.fragment')); + library.template('qtek.wireframe', Shader.source('qtek.wireframe.vertex'), Shader.source('qtek.wireframe.fragment')); + library.template('qtek.skybox', Shader.source('qtek.skybox.vertex'), Shader.source('qtek.skybox.fragment')); + library.template('qtek.prez', Shader.source('qtek.prez.vertex'), Shader.source('qtek.prez.fragment')); + library.template('qtek.standard', Shader.source('qtek.standard.vertex'), Shader.source('qtek.standard.fragment')); + + // Some build in shaders + Shader['import'](__webpack_require__(115)); + Shader['import'](__webpack_require__(116)); + Shader['import'](__webpack_require__(117)); + Shader['import'](__webpack_require__(118)); + Shader['import'](__webpack_require__(119)); + Shader['import'](__webpack_require__(120)); + Shader['import'](__webpack_require__(121)); + Shader['import'](__webpack_require__(122)); + Shader['import'](__webpack_require__(123)); + Shader['import'](__webpack_require__(124)); + Shader['import'](__webpack_require__(125)); + Shader['import'](__webpack_require__(126)); + Shader['import'](__webpack_require__(127)); + + Shader['import'](__webpack_require__(128)); + Shader['import'](__webpack_require__(129)); + + + +/***/ }, +/* 113 */ +/***/ function(module, exports) { + + + module.exports = "/**\n * http: */\n\n@export qtek.lambert.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\n\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 normal : NORMAL;\n\nattribute vec3 barycentric;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_COUNT] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n\n#ifdef SKINNING\n\n @import qtek.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n#endif\n\n gl_Position = worldViewProjection * vec4( skinnedPosition, 1.0 );\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_Normal = normalize( ( worldInverseTranspose * vec4(skinnedNormal, 0.0) ).xyz );\n v_WorldPosition = ( world * vec4( skinnedPosition, 1.0) ).xyz;\n\n v_Barycentric = barycentric;\n}\n\n@end\n\n\n@export qtek.lambert.fragment\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\n\nuniform sampler2D diffuseMap;\nuniform sampler2D alphaMap;\n\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform vec3 emission : [0.0, 0.0, 0.0];\nuniform float alpha : 1.0;\n\nuniform float lineWidth : 0.0;\nuniform vec3 lineColor : [0.0, 0.0, 0.0];\nvarying vec3 v_Barycentric;\n\n#ifdef AMBIENT_LIGHT_COUNT\n@import qtek.header.ambient_light\n#endif\n#ifdef POINT_LIGHT_COUNT\n@import qtek.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_COUNT\n@import qtek.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_COUNT\n@import qtek.header.spot_light\n#endif\n\n@import qtek.util.calculate_attenuation\n\n@import qtek.util.edge_factor\n\n@import qtek.plugin.compute_shadow_map\n\nvoid main()\n{\n#ifdef RENDER_NORMAL\n gl_FragColor = vec4(v_Normal * 0.5 + 0.5, 1.0);\n return;\n#endif\n#ifdef RENDER_TEXCOORD\n gl_FragColor = vec4(v_Texcoord, 1.0, 1.0);\n return;\n#endif\n\n gl_FragColor = vec4(color, alpha);\n\n#ifdef DIFFUSEMAP_ENABLED\n vec4 tex = texture2D( diffuseMap, v_Texcoord );\n#ifdef SRGB_DECODE\n tex.rgb = pow(tex.rgb, vec3(2.2));\n#endif\n gl_FragColor.rgb *= tex.rgb;\n#ifdef DIFFUSEMAP_ALPHA_ALPHA\n gl_FragColor.a *= tex.a;\n#endif\n#endif\n\n vec3 diffuseColor = vec3(0.0, 0.0, 0.0);\n\n#ifdef AMBIENT_LIGHT_COUNT\n for(int i = 0; i < AMBIENT_LIGHT_COUNT; i++)\n {\n diffuseColor += ambientLightColor[i];\n }\n#endif\n#ifdef POINT_LIGHT_COUNT\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\n float shadowContribsPoint[POINT_LIGHT_COUNT];\n if( shadowEnabled )\n {\n computeShadowOfPointLights(v_WorldPosition, shadowContribsPoint);\n }\n#endif\n for(int i = 0; i < POINT_LIGHT_COUNT; i++)\n {\n\n vec3 lightPosition = pointLightPosition[i];\n vec3 lightColor = pointLightColor[i];\n float range = pointLightRange[i];\n\n vec3 lightDirection = lightPosition - v_WorldPosition;\n\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range);\n\n lightDirection /= dist;\n\n float ndl = dot( v_Normal, lightDirection );\n\n float shadowContrib = 1.0;\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribsPoint[i];\n }\n#endif\n\n diffuseColor += lightColor * clamp(ndl, 0.0, 1.0) * attenuation * shadowContrib;\n }\n#endif\n#ifdef DIRECTIONAL_LIGHT_COUNT\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n float shadowContribsDir[DIRECTIONAL_LIGHT_COUNT];\n if(shadowEnabled)\n {\n computeShadowOfDirectionalLights(v_WorldPosition, shadowContribsDir);\n }\n#endif\n for(int i = 0; i < DIRECTIONAL_LIGHT_COUNT; i++)\n {\n vec3 lightDirection = -directionalLightDirection[i];\n vec3 lightColor = directionalLightColor[i];\n\n float ndl = dot(v_Normal, normalize(lightDirection));\n\n float shadowContrib = 1.0;\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribsDir[i];\n }\n#endif\n\n diffuseColor += lightColor * clamp(ndl, 0.0, 1.0) * shadowContrib;\n }\n#endif\n\n#ifdef SPOT_LIGHT_COUNT\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\n float shadowContribsSpot[SPOT_LIGHT_COUNT];\n if(shadowEnabled)\n {\n computeShadowOfSpotLights(v_WorldPosition, shadowContribsSpot);\n }\n#endif\n for(int i = 0; i < SPOT_LIGHT_COUNT; i++)\n {\n vec3 lightPosition = -spotLightPosition[i];\n vec3 spotLightDirection = -normalize( spotLightDirection[i] );\n vec3 lightColor = spotLightColor[i];\n float range = spotLightRange[i];\n float a = spotLightUmbraAngleCosine[i];\n float b = spotLightPenumbraAngleCosine[i];\n float falloffFactor = spotLightFalloffFactor[i];\n\n vec3 lightDirection = lightPosition - v_WorldPosition;\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range);\n\n lightDirection /= dist;\n float c = dot(spotLightDirection, lightDirection);\n\n float falloff;\n falloff = clamp((c - a) /( b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n\n float ndl = dot(v_Normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\n\n float shadowContrib = 1.0;\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribsSpot[i];\n }\n#endif\n diffuseColor += lightColor * ndl * attenuation * (1.0-falloff) * shadowContrib;\n }\n#endif\n\n gl_FragColor.rgb *= diffuseColor;\n gl_FragColor.rgb += emission;\n if(lineWidth > 0.01)\n {\n gl_FragColor.rgb = gl_FragColor.rgb * mix(lineColor, vec3(1.0), edgeFactor(lineWidth));\n }\n}\n\n@end"; + + +/***/ }, +/* 114 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.wireframe.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world : WORLD;\n\nattribute vec3 position : POSITION;\nattribute vec3 barycentric;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_COUNT] : SKIN_MATRIX;\n#endif\n\nvarying vec3 v_Barycentric;\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n\n @import qtek.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0 );\n\n v_Barycentric = barycentric;\n}\n\n@end\n\n\n@export qtek.wireframe.fragment\n\nuniform vec3 color : [0.0, 0.0, 0.0];\n\nuniform float alpha : 1.0;\nuniform float lineWidth : 1.0;\n\nvarying vec3 v_Barycentric;\n\n\n@import qtek.util.edge_factor\n\nvoid main()\n{\n\n gl_FragColor.rgb = color;\n gl_FragColor.a = (1.0-edgeFactor(lineWidth)) * alpha;\n}\n\n@end"; + + +/***/ }, +/* 115 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.compositor.coloradjust\n\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float brightness : 0.0;\nuniform float contrast : 1.0;\nuniform float exposure : 0.0;\nuniform float gamma : 1.0;\nuniform float saturation : 1.0;\n\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord);\n\n vec3 color = clamp(tex.rgb + vec3(brightness), 0.0, 1.0);\n color = clamp( (color-vec3(0.5))*contrast+vec3(0.5), 0.0, 1.0);\n color = clamp( color * pow(2.0, exposure), 0.0, 1.0);\n color = clamp( pow(color, vec3(gamma)), 0.0, 1.0);\n float luminance = dot( color, w );\n color = mix(vec3(luminance), color, saturation);\n\n gl_FragColor = vec4(color, tex.a);\n}\n\n@end\n\n@export qtek.compositor.brightness\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float brightness : 0.0;\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord);\n vec3 color = tex.rgb + vec3(brightness);\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export qtek.compositor.contrast\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float contrast : 1.0;\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord);\n vec3 color = (tex.rgb-vec3(0.5))*contrast+vec3(0.5);\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export qtek.compositor.exposure\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float exposure : 0.0;\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n vec3 color = tex.rgb * pow(2.0, exposure);\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export qtek.compositor.gamma\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float gamma : 1.0;\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n vec3 color = pow(tex.rgb, vec3(gamma));\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export qtek.compositor.saturation\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float saturation : 1.0;\n\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n vec3 color = tex.rgb;\n float luminance = dot(color, w);\n color = mix(vec3(luminance), color, saturation);\n gl_FragColor = vec4(color, tex.a);\n}\n@end"; + + +/***/ }, +/* 116 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.compositor.kernel.gaussian_9\nfloat gaussianKernel[9];\ngaussianKernel[0] = 0.07;\ngaussianKernel[1] = 0.09;\ngaussianKernel[2] = 0.12;\ngaussianKernel[3] = 0.14;\ngaussianKernel[4] = 0.16;\ngaussianKernel[5] = 0.14;\ngaussianKernel[6] = 0.12;\ngaussianKernel[7] = 0.09;\ngaussianKernel[8] = 0.07;\n@end\n\n@export qtek.compositor.kernel.gaussian_13\n\nfloat gaussianKernel[13];\n\ngaussianKernel[0] = 0.02;\ngaussianKernel[1] = 0.03;\ngaussianKernel[2] = 0.06;\ngaussianKernel[3] = 0.08;\ngaussianKernel[4] = 0.11;\ngaussianKernel[5] = 0.13;\ngaussianKernel[6] = 0.14;\ngaussianKernel[7] = 0.13;\ngaussianKernel[8] = 0.11;\ngaussianKernel[9] = 0.08;\ngaussianKernel[10] = 0.06;\ngaussianKernel[11] = 0.03;\ngaussianKernel[12] = 0.02;\n\n@end\n\n\n@export qtek.compositor.gaussian_blur\n\nuniform sampler2D texture; varying vec2 v_Texcoord;\n\nuniform float blurSize : 2.0;\nuniform vec2 textureSize : [512.0, 512.0];\nuniform float blurDir : 0.0;\n\n@import qtek.util.rgbm\n@import qtek.util.clamp_sample\n\nvoid main (void)\n{\n @import qtek.compositor.kernel.gaussian_9\n\n vec2 off = blurSize / textureSize;\n off *= vec2(1.0 - blurDir, blurDir);\n\n vec4 sum = vec4(0.0);\n float weightAll = 0.0;\n\n for (int i = 0; i < 9; i++) {\n float w = gaussianKernel[i];\n sum += decodeHDR(clampSample(texture, v_Texcoord + float(i - 4) * off)) * w;\n weightAll += w;\n }\n gl_FragColor = encodeHDR(sum / weightAll);\n}\n\n@end\n\n@export qtek.compositor.box_blur\n\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 3.0;\nuniform vec2 textureSize : [512.0, 512.0];\n\n@import qtek.util.rgbm\n@import qtek.util.clamp_sample\n\nvoid main(void)\n{\n\n vec4 tex = decodeHDR(texture2D(texture, v_Texcoord));\n vec2 offset = blurSize / textureSize;\n\n tex += decodeHDR(clampSample(texture, v_Texcoord + vec2(offset.x, 0.0) ));\n tex += decodeHDR(clampSample(texture, v_Texcoord + vec2(offset.x, offset.y) ));\n tex += decodeHDR(clampSample(texture, v_Texcoord + vec2(-offset.x, offset.y) ));\n tex += decodeHDR(clampSample(texture, v_Texcoord + vec2(0.0, offset.y) ));\n tex += decodeHDR(clampSample(texture, v_Texcoord + vec2(-offset.x, 0.0) ));\n tex += decodeHDR(clampSample(texture, v_Texcoord + vec2(-offset.x, -offset.y) ));\n tex += decodeHDR(clampSample(texture, v_Texcoord + vec2(offset.x, -offset.y) ));\n tex += decodeHDR(clampSample(texture, v_Texcoord + vec2(0.0, -offset.y) ));\n\n tex /= 9.0;\n\n \n gl_FragColor = encodeHDR(tex);\n}\n\n@end"; + + +/***/ }, +/* 117 */ +/***/ function(module, exports) { + + + module.exports = "\n@export qtek.compositor.lum\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\n\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord );\n float luminance = dot(tex.rgb, w);\n\n gl_FragColor = vec4(vec3(luminance), 1.0);\n}\n\n@end"; + + +/***/ }, +/* 118 */ +/***/ function(module, exports) { + + + module.exports = "\n@export qtek.compositor.lut\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\nuniform sampler2D lookup;\n\nvoid main()\n{\n\n vec4 tex = texture2D(texture, v_Texcoord);\n\n float blueColor = tex.b * 63.0;\n\n vec2 quad1;\n quad1.y = floor(floor(blueColor) / 8.0);\n quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\n vec2 quad2;\n quad2.y = floor(ceil(blueColor) / 8.0);\n quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\n vec2 texPos1;\n texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.r);\n texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.g);\n\n vec2 texPos2;\n texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.r);\n texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.g);\n\n vec4 newColor1 = texture2D(lookup, texPos1);\n vec4 newColor2 = texture2D(lookup, texPos2);\n\n vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n gl_FragColor = vec4(newColor.rgb, tex.w);\n}\n\n@end"; + + +/***/ }, +/* 119 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.compositor.vignette\n\n#define OUTPUT_ALPHA\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\n\nuniform float darkness: 1;\nuniform float offset: 1;\n\n@import qtek.util.rgbm\n\nvoid main()\n{\n vec4 texel = decodeHDR(texture2D(texture, v_Texcoord));\n\n gl_FragColor.rgb = texel.rgb;\n\n vec2 uv = (v_Texcoord - vec2(0.5)) * vec2(offset);\n\n gl_FragColor = encodeHDR(vec4(mix(texel.rgb, vec3(1.0 - darkness), dot(uv, uv)), texel.a));\n}\n\n@end"; + + +/***/ }, +/* 120 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.compositor.output\n\n#define OUTPUT_ALPHA\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\n\n@import qtek.util.rgbm\n\nvoid main()\n{\n vec4 tex = decodeHDR(texture2D(texture, v_Texcoord));\n\n gl_FragColor.rgb = tex.rgb;\n\n#ifdef OUTPUT_ALPHA\n gl_FragColor.a = tex.a;\n#else\n gl_FragColor.a = 1.0;\n#endif\n\n gl_FragColor = encodeHDR(gl_FragColor);\n}\n\n@end"; + + +/***/ }, +/* 121 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.compositor.bright\n\nuniform sampler2D texture;\n\nuniform float threshold : 1;\nuniform float scale : 1.0;\n\nuniform vec2 textureSize: [512, 512];\n\nvarying vec2 v_Texcoord;\n\nconst vec3 lumWeight = vec3(0.2125, 0.7154, 0.0721);\n\n@import qtek.util.rgbm\n\n\nvec3 median(vec3 a, vec3 b, vec3 c)\n{\n return a + b + c - min(min(a, b), c) - max(max(a, b), c);\n}\n\nvoid main()\n{\n vec3 texel = decodeHDR(texture2D(texture, v_Texcoord)).rgb;\n\n#ifdef ANTI_FLICKER\n vec3 d = 1.0 / textureSize.xyx * vec3(1.0, 1.0, 0.0);\n\n vec3 s1 = decodeHDR(texture2D(texture, v_Texcoord - d.xz)).rgb;\n vec3 s2 = decodeHDR(texture2D(texture, v_Texcoord + d.xz)).rgb;\n vec3 s3 = decodeHDR(texture2D(texture, v_Texcoord - d.zy)).rgb;\n vec3 s4 = decodeHDR(texture2D(texture, v_Texcoord + d.zy)).rgb;\n texel = median(median(texel, s1, s2), s3, s4);\n\n#endif\n\n float lum = dot(texel, lumWeight);\n vec4 color;\n if (lum > threshold)\n {\n color.rgb = texel * scale;\n }\n else\n {\n color.rgb = vec3(0.0);\n }\n color.a = 1.0;\n\n gl_FragColor = encodeHDR(color);\n}\n@end\n"; + + +/***/ }, +/* 122 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.compositor.downsample\n\nuniform sampler2D texture;\nuniform vec2 textureSize : [512, 512];\n\nvarying vec2 v_Texcoord;\n\n@import qtek.util.rgbm\nfloat brightness(vec3 c)\n{\n return max(max(c.r, c.g), c.b);\n}\n\n@import qtek.util.clamp_sample\n\nvoid main()\n{\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\n\n#ifdef ANTI_FLICKER\n vec3 s1 = decodeHDR(clampSample(texture, v_Texcoord + d.xy)).rgb;\n vec3 s2 = decodeHDR(clampSample(texture, v_Texcoord + d.zy)).rgb;\n vec3 s3 = decodeHDR(clampSample(texture, v_Texcoord + d.xw)).rgb;\n vec3 s4 = decodeHDR(clampSample(texture, v_Texcoord + d.zw)).rgb;\n\n float s1w = 1.0 / (brightness(s1) + 1.0);\n float s2w = 1.0 / (brightness(s2) + 1.0);\n float s3w = 1.0 / (brightness(s3) + 1.0);\n float s4w = 1.0 / (brightness(s4) + 1.0);\n float oneDivideSum = 1.0 / (s1w + s2w + s3w + s4w);\n\n vec4 color = vec4(\n (s1 * s1w + s2 * s2w + s3 * s3w + s4 * s4w) * oneDivideSum,\n 1.0\n );\n#else\n vec4 color = decodeHDR(clampSample(texture, v_Texcoord + d.xy));\n color += decodeHDR(clampSample(texture, v_Texcoord + d.zy));\n color += decodeHDR(clampSample(texture, v_Texcoord + d.xw));\n color += decodeHDR(clampSample(texture, v_Texcoord + d.zw));\n color *= 0.25;\n#endif\n\n gl_FragColor = encodeHDR(color);\n}\n\n@end"; + + +/***/ }, +/* 123 */ +/***/ function(module, exports) { + + + module.exports = "\n@export qtek.compositor.upsample\n\n#define HIGH_QUALITY\n\nuniform sampler2D texture;\nuniform vec2 textureSize : [512, 512];\n\nuniform float sampleScale: 0.5;\n\nvarying vec2 v_Texcoord;\n\n@import qtek.util.rgbm\n\n@import qtek.util.clamp_sample\n\nvoid main()\n{\n\n#ifdef HIGH_QUALITY\n vec4 d = vec4(1.0, 1.0, -1.0, 0.0) / textureSize.xyxy * sampleScale;\n\n vec4 s;\n s = decodeHDR(clampSample(texture, v_Texcoord - d.xy));\n s += decodeHDR(clampSample(texture, v_Texcoord - d.wy)) * 2.0;\n s += decodeHDR(clampSample(texture, v_Texcoord - d.zy));\n\n s += decodeHDR(clampSample(texture, v_Texcoord + d.zw)) * 2.0;\n s += decodeHDR(clampSample(texture, v_Texcoord )) * 4.0;\n s += decodeHDR(clampSample(texture, v_Texcoord + d.xw)) * 2.0;\n\n s += decodeHDR(clampSample(texture, v_Texcoord + d.zy));\n s += decodeHDR(clampSample(texture, v_Texcoord + d.wy)) * 2.0;\n s += decodeHDR(clampSample(texture, v_Texcoord + d.xy));\n\n gl_FragColor = encodeHDR(s / 16.0);\n#else\n vec4 d = vec4(-1.0, -1.0, +1.0, +1.0) / textureSize.xyxy;\n\n vec4 s;\n s = decodeHDR(clampSample(texture, v_Texcoord + d.xy));\n s += decodeHDR(clampSample(texture, v_Texcoord + d.zy));\n s += decodeHDR(clampSample(texture, v_Texcoord + d.xw));\n s += decodeHDR(clampSample(texture, v_Texcoord + d.zw));\n\n gl_FragColor = encodeHDR(s / 4.0);\n#endif\n}\n\n@end"; + + +/***/ }, +/* 124 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.compositor.hdr.log_lum\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\n\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\n@import qtek.util.rgbm\n\nvoid main()\n{\n vec4 tex = decodeHDR(texture2D(texture, v_Texcoord));\n float luminance = dot(tex.rgb, w);\n luminance = log(luminance + 0.001);\n\n gl_FragColor = encodeHDR(vec4(vec3(luminance), 1.0));\n}\n\n@end\n\n@export qtek.compositor.hdr.lum_adaption\nvarying vec2 v_Texcoord;\n\nuniform sampler2D adaptedLum;\nuniform sampler2D currentLum;\n\nuniform float frameTime : 0.02;\n\n@import qtek.util.rgbm\n\nvoid main()\n{\n float fAdaptedLum = decodeHDR(texture2D(adaptedLum, vec2(0.5, 0.5))).r;\n float fCurrentLum = exp(encodeHDR(texture2D(currentLum, vec2(0.5, 0.5))).r);\n\n fAdaptedLum += (fCurrentLum - fAdaptedLum) * (1.0 - pow(0.98, 30.0 * frameTime));\n gl_FragColor = encodeHDR(vec4(vec3(fAdaptedLum), 1.0));\n}\n@end\n\n@export qtek.compositor.hdr.composite\n\nuniform sampler2D texture;\n#ifdef BLOOM_ENABLED\nuniform sampler2D bloom;\n#endif\n#ifdef LENSFLARE_ENABLED\nuniform sampler2D lensflare;\nuniform sampler2D lensdirt;\n#endif\n\n#ifdef LUM_ENABLED\nuniform sampler2D lum;\n#endif\n\n#ifdef LUT_ENABLED\nuniform sampler2D lut;\n#endif\n\n#ifdef VIGNETTE\nuniform float vignetteDarkness: 1.0;\nuniform float vignetteOffset: 1.0;\n#endif\n\nuniform float exposure : 1.0;\nuniform float bloomIntensity : 0.25;\nuniform float lensflareIntensity : 1;\n\nvarying vec2 v_Texcoord;\n\nconst vec3 whiteScale = vec3(11.2);\n\n@import qtek.util.srgb\n\nvec3 uncharted2ToneMap(vec3 x)\n{\n const float A = 0.22; const float B = 0.30; const float C = 0.10; const float D = 0.20; const float E = 0.01; const float F = 0.30; \n return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;\n}\n\nvec3 filmicToneMap(vec3 color)\n{\n vec3 x = max(vec3(0.0), color - 0.004);\n return (x*(6.2*x+0.5))/(x*(6.2*x+1.7)+0.06);\n}\n\nvec3 ACESToneMapping(vec3 color)\n{\n const float A = 2.51;\n const float B = 0.03;\n const float C = 2.43;\n const float D = 0.59;\n const float E = 0.14;\n return (color * (A * color + B)) / (color * (C * color + D) + E);\n}\n\nfloat eyeAdaption(float fLum)\n{\n return mix(0.2, fLum, 0.5);\n}\n\n#ifdef LUT_ENABLED\nvec3 lutTransform(vec3 color) {\n float blueColor = color.b * 63.0;\n\n vec2 quad1;\n quad1.y = floor(floor(blueColor) / 8.0);\n quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\n vec2 quad2;\n quad2.y = floor(ceil(blueColor) / 8.0);\n quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\n vec2 texPos1;\n texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.r);\n texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g);\n\n vec2 texPos2;\n texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.r);\n texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g);\n\n vec4 newColor1 = texture2D(lut, texPos1);\n vec4 newColor2 = texture2D(lut, texPos2);\n\n vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n return newColor.rgb;\n}\n#endif\n\n@import qtek.util.rgbm\n\nvoid main()\n{\n vec4 texel = vec4(0.0);\n#ifdef TEXTURE_ENABLED\n texel += decodeHDR(texture2D(texture, v_Texcoord));\n#endif\n\n#ifdef BLOOM_ENABLED\n texel += decodeHDR(texture2D(bloom, v_Texcoord)) * bloomIntensity;\n#endif\n\n#ifdef LENSFLARE_ENABLED\n texel += decodeHDR(texture2D(lensflare, v_Texcoord)) * texture2D(lensdirt, v_Texcoord) * lensflareIntensity;\n#endif\n\n#ifdef LUM_ENABLED\n float fLum = texture2D(lum, vec2(0.5, 0.5)).r;\n float adaptedLumDest = 3.0 / (max(0.1, 1.0 + 10.0*eyeAdaption(fLum)));\n float exposureBias = adaptedLumDest * exposure;\n#else\n float exposureBias = exposure;\n#endif\n texel.rgb *= exposureBias;\n\n texel.rgb = ACESToneMapping(texel.rgb);\n texel = linearTosRGB(texel);\n\n#ifdef LUT_ENABLED\n texel.rgb = lutTransform(clamp(texel.rgb,vec3(0.0),vec3(1.0)));\n#endif\n\n#ifdef VIGNETTE\n vec2 uv = (v_Texcoord - vec2(0.5)) * vec2(vignetteOffset);\n texel.rgb = mix(texel.rgb, vec3(1.0 - vignetteDarkness), dot(uv, uv));\n#endif\n\n gl_FragColor = encodeHDR(texel);\n\n#ifdef DEBUG\n #if DEBUG == 1\n gl_FragColor = encodeHDR(decodeHDR(texture2D(texture, v_Texcoord)));\n #elif DEBUG == 2\n gl_FragColor = encodeHDR(decodeHDR(texture2D(bloom, v_Texcoord)).rgb * bloomIntensity);\n #elif DEBUG == 3\n gl_FragColor = encodeHDR(decodeHDR(texture2D(lensflare, v_Texcoord).rgb * lensflareIntensity));\n #endif\n#endif\n}\n\n@end"; + + +/***/ }, +/* 125 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.compositor.dof.coc\n\nuniform sampler2D depth;\n\nuniform float zNear: 0.1;\nuniform float zFar: 2000;\n\nuniform float focalDist: 3;\nuniform float focalRange: 1;\nuniform float focalLength: 30;\nuniform float fstop: 0.36;\n\nvarying vec2 v_Texcoord;\n\n@import qtek.util.encode_float\n\nvoid main()\n{\n float z = texture2D(depth, v_Texcoord).r;\n\n float dist = 2.0 * zNear * zFar / (zFar + zNear - z * (zFar - zNear));\n\n float aperture = 1.0 / fstop;\n\n float coc;\n\n float uppper = focalDist + focalRange;\n float lower = focalDist - focalRange;\n if (dist <= uppper && dist >= lower) {\n coc = 0.5;\n }\n else {\n float focalAdjusted = dist > uppper ? uppper : lower;\n\n coc = abs(aperture * (focalLength * (dist - focalAdjusted)) / (dist * (focalAdjusted - focalLength)));\n coc = clamp(coc, 0.0, 0.4) / 0.4000001;\n\n if (dist < lower) {\n coc = -coc;\n }\n coc = coc * 0.5 + 0.5;\n }\n\n gl_FragColor = encodeFloat(coc);\n}\n\n@end\n\n@export qtek.compositor.dof.premultiply\n\nuniform sampler2D texture;\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\n\n@import qtek.util.rgbm\n\n@import qtek.util.decode_float\n\nvoid main() {\n float fCoc = max(abs(decodeFloat(texture2D(coc, v_Texcoord)) * 2.0 - 1.0), 0.1);\n gl_FragColor = encodeHDR(\n vec4(decodeHDR(texture2D(texture, v_Texcoord)).rgb * fCoc, 1.0)\n );\n}\n@end\n\n\n@export qtek.compositor.dof.min_coc\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform vec2 textureSize : [512.0, 512.0];\n\n@import qtek.util.float\n\nvoid main()\n{\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\n\n float fCoc = decodeFloat(texture2D(coc, v_Texcoord + d.xy));\n fCoc = min(fCoc, decodeFloat(texture2D(coc, v_Texcoord + d.zy)));\n fCoc = min(fCoc, decodeFloat(texture2D(coc, v_Texcoord + d.xw)));\n fCoc = min(fCoc, decodeFloat(texture2D(coc, v_Texcoord + d.zw)));\n\n gl_FragColor = encodeFloat(fCoc);\n}\n\n@end\n\n\n@export qtek.compositor.dof.max_coc\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform vec2 textureSize : [512.0, 512.0];\n\n@import qtek.util.float\n\nvoid main()\n{\n\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\n\n float fCoc = decodeFloat(texture2D(coc, v_Texcoord + d.xy));\n fCoc = max(fCoc, decodeFloat(texture2D(coc, v_Texcoord + d.zy)));\n fCoc = max(fCoc, decodeFloat(texture2D(coc, v_Texcoord + d.xw)));\n fCoc = max(fCoc, decodeFloat(texture2D(coc, v_Texcoord + d.zw)));\n\n gl_FragColor = encodeFloat(fCoc);\n}\n\n@end\n\n\n\n\n@export qtek.compositor.dof.coc_upsample\n\n#define HIGH_QUALITY\n\nuniform sampler2D coc;\nuniform vec2 textureSize : [512, 512];\n\nuniform float sampleScale: 0.5;\n\nvarying vec2 v_Texcoord;\n\n@import qtek.util.float\n\nvoid main()\n{\n\n#ifdef HIGH_QUALITY\n vec4 d = vec4(1.0, 1.0, -1.0, 0.0) / textureSize.xyxy * sampleScale;\n\n float s;\n s = decodeFloat(texture2D(coc, v_Texcoord - d.xy));\n s += decodeFloat(texture2D(coc, v_Texcoord - d.wy)) * 2.0;\n s += decodeFloat(texture2D(coc, v_Texcoord - d.zy));\n\n s += decodeFloat(texture2D(coc, v_Texcoord + d.zw)) * 2.0;\n s += decodeFloat(texture2D(coc, v_Texcoord )) * 4.0;\n s += decodeFloat(texture2D(coc, v_Texcoord + d.xw)) * 2.0;\n\n s += decodeFloat(texture2D(coc, v_Texcoord + d.zy));\n s += decodeFloat(texture2D(coc, v_Texcoord + d.wy)) * 2.0;\n s += decodeFloat(texture2D(coc, v_Texcoord + d.xy));\n\n gl_FragColor = encodeFloat(s / 16.0);\n#else\n vec4 d = vec4(-1.0, -1.0, +1.0, +1.0) / textureSize.xyxy;\n\n float s;\n s = decodeFloat(texture2D(coc, v_Texcoord + d.xy));\n s += decodeFloat(texture2D(coc, v_Texcoord + d.zy));\n s += decodeFloat(texture2D(coc, v_Texcoord + d.xw));\n s += decodeFloat(texture2D(coc, v_Texcoord + d.zw));\n\n gl_FragColor = encodeFloat(s / 4.0);\n#endif\n}\n\n@end\n\n\n\n@export qtek.compositor.dof.upsample\n\n#define HIGH_QUALITY\n\nuniform sampler2D coc;\nuniform sampler2D texture;\nuniform vec2 textureSize : [512, 512];\n\nuniform float sampleScale: 0.5;\n\nvarying vec2 v_Texcoord;\n\n\n@import qtek.util.rgbm\n\n@import qtek.util.decode_float\n\nfloat tap(vec2 uv, inout vec4 color, float baseWeight) {\n float weight = abs(decodeFloat(texture2D(coc, uv)) * 2.0 - 1.0) * baseWeight;\n color += decodeHDR(texture2D(texture, uv)) * weight;\n return weight;\n}\n\nvoid main()\n{\n#ifdef HIGH_QUALITY\n vec4 d = vec4(1.0, 1.0, -1.0, 0.0) / textureSize.xyxy * sampleScale;\n\n vec4 color = vec4(0.0);\n float baseWeight = 1.0 / 16.0;\n float w = tap(v_Texcoord - d.xy, color, baseWeight);\n w += tap(v_Texcoord - d.wy, color, baseWeight * 2.0);\n w += tap(v_Texcoord - d.zy, color, baseWeight);\n\n w += tap(v_Texcoord + d.zw, color, baseWeight * 2.0);\n w += tap(v_Texcoord , color, baseWeight * 4.0);\n w += tap(v_Texcoord + d.xw, color, baseWeight * 2.0);\n\n w += tap(v_Texcoord + d.zy, color, baseWeight);\n w += tap(v_Texcoord + d.wy, color, baseWeight * 2.0);\n w += tap(v_Texcoord + d.xy, color, baseWeight);\n\n gl_FragColor = encodeHDR(color / w);\n#else\n vec4 d = vec4(-1.0, -1.0, +1.0, +1.0) / textureSize.xyxy;\n\n vec4 color = vec4(0.0);\n float baseWeight = 1.0 / 4.0;\n float w = tap(v_Texcoord + d.xy, color, baseWeight);\n w += tap(v_Texcoord + d.zy, color, baseWeight);\n w += tap(v_Texcoord + d.xw, color, baseWeight);\n w += tap(v_Texcoord + d.zw, color, baseWeight);\n\n gl_FragColor = encodeHDR(color / w);\n#endif\n}\n\n@end\n\n\n\n@export qtek.compositor.dof.downsample\n\nuniform sampler2D texture;\nuniform sampler2D coc;\nuniform vec2 textureSize : [512, 512];\n\nvarying vec2 v_Texcoord;\n\n@import qtek.util.rgbm\n\n@import qtek.util.decode_float\n\nfloat tap(vec2 uv, inout vec4 color) {\n float weight = abs(decodeFloat(texture2D(coc, uv)) * 2.0 - 1.0) * 0.25;\n color += decodeHDR(texture2D(texture, uv)) * weight;\n return weight;\n}\n\nvoid main()\n{\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\n\n vec4 color = vec4(0.0);\n float weight = tap(v_Texcoord + d.xy, color);\n weight += tap(v_Texcoord + d.zy, color);\n weight += tap(v_Texcoord + d.xw, color);\n weight += tap(v_Texcoord + d.zw, color);\n color /= weight;\n\n gl_FragColor = encodeHDR(color);\n}\n\n@end\n\n\n\n@export qtek.compositor.dof.hexagonal_blur_frag\n\n@import qtek.util.float\n\n\nvec4 doBlur(sampler2D targetTexture, vec2 offset) {\n#ifdef BLUR_COC\n float cocSum = 0.0;\n#else\n vec4 color = vec4(0.0);\n#endif\n\n float weightSum = 0.0;\n float kernelWeight = 1.0 / float(KERNEL_SIZE);\n\n for (int i = 0; i < KERNEL_SIZE; i++) {\n vec2 coord = v_Texcoord + offset * float(i);\n\n float w = kernelWeight;\n#ifdef BLUR_COC\n float fCoc = decodeFloat(texture2D(targetTexture, coord)) * 2.0 - 1.0;\n cocSum += clamp(fCoc, -1.0, 0.0) * w;\n#else\n float fCoc = decodeFloat(texture2D(coc, coord)) * 2.0 - 1.0;\n vec4 texel = texture2D(targetTexture, coord);\n #if !defined(BLUR_NEARFIELD)\n w *= abs(fCoc);\n #endif\n color += decodeHDR(texel) * w;\n#endif\n\n weightSum += w;\n }\n#ifdef BLUR_COC\n return encodeFloat(clamp(cocSum / weightSum, -1.0, 0.0) * 0.5 + 0.5);\n#else\n return color / weightSum;\n#endif\n}\n\n@end\n\n\n@export qtek.compositor.dof.hexagonal_blur_1\n\n#define KERNEL_SIZE 5\n\nuniform sampler2D texture;\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 1.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\n@import qtek.util.rgbm\n\n@import qtek.compositor.dof.hexagonal_blur_frag\n\nvoid main()\n{\n vec2 offset = blurSize / textureSize;\n\n#if !defined(BLUR_NEARFIELD) && !defined(BLUR_COC)\n offset *= abs(decodeFloat(texture2D(coc, v_Texcoord)) * 2.0 - 1.0);\n#endif\n\n gl_FragColor = doBlur(texture, vec2(0.0, offset.y));\n#if !defined(BLUR_COC)\n gl_FragColor = encodeHDR(gl_FragColor);\n#endif\n}\n\n@end\n\n@export qtek.compositor.dof.hexagonal_blur_2\n\n#define KERNEL_SIZE 5\n\nuniform sampler2D texture;\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 1.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\n@import qtek.util.rgbm\n\n@import qtek.compositor.dof.hexagonal_blur_frag\n\nvoid main()\n{\n vec2 offset = blurSize / textureSize;\n#if !defined(BLUR_NEARFIELD) && !defined(BLUR_COC)\n offset *= abs(decodeFloat(texture2D(coc, v_Texcoord)) * 2.0 - 1.0);\n#endif\n\n offset.y /= 2.0;\n\n gl_FragColor = doBlur(texture, -offset);\n#if !defined(BLUR_COC)\n gl_FragColor = encodeHDR(gl_FragColor);\n#endif\n}\n@end\n\n@export qtek.compositor.dof.hexagonal_blur_3\n\n#define KERNEL_SIZE 5\n\nuniform sampler2D texture1;\nuniform sampler2D texture2;\nuniform sampler2D coc;\n\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 1.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\n@import qtek.util.rgbm\n\n@import qtek.compositor.dof.hexagonal_blur_frag\n\nvoid main()\n{\n vec2 offset = blurSize / textureSize;\n\n#if !defined(BLUR_NEARFIELD) && !defined(BLUR_COC)\n offset *= abs(decodeFloat(texture2D(coc, v_Texcoord)) * 2.0 - 1.0);\n#endif\n\n offset.y /= 2.0;\n vec2 vDownRight = vec2(offset.x, -offset.y);\n\n vec4 texel1 = doBlur(texture1, -offset);\n vec4 texel2 = doBlur(texture1, vDownRight);\n vec4 texel3 = doBlur(texture2, vDownRight);\n\n#ifdef BLUR_COC\n float coc1 = decodeFloat(texel1) * 2.0 - 1.0;\n float coc2 = decodeFloat(texel2) * 2.0 - 1.0;\n float coc3 = decodeFloat(texel3) * 2.0 - 1.0;\n gl_FragColor = encodeFloat(\n ((coc1 + coc2 + coc3) / 3.0) * 0.5 + 0.5\n );\n\n#else\n vec4 color = (texel1 + texel2 + texel3) / 3.0;\n gl_FragColor = encodeHDR(color);\n#endif\n}\n\n@end\n\n@export qtek.compositor.dof.composite\n\n#define DEBUG 0\n\nuniform sampler2D original;\nuniform sampler2D blurred;\nuniform sampler2D nearfield;\nuniform sampler2D coc;\nuniform sampler2D nearcoc;\nvarying vec2 v_Texcoord;\n\n@import qtek.util.rgbm\n@import qtek.util.float\n\nvoid main()\n{\n vec4 blurredColor = decodeHDR(texture2D(blurred, v_Texcoord));\n vec4 originalColor = decodeHDR(texture2D(original, v_Texcoord));\n\n float fCoc = decodeFloat(texture2D(coc, v_Texcoord));\n\n blurredColor.rgb /= max(fCoc, 0.1);\n fCoc = abs(fCoc * 2.0 - 1.0);\n\n float weight = fCoc;\n\n#ifdef NEARFIELD_ENABLED\n vec4 nearfieldColor = decodeHDR(texture2D(nearfield, v_Texcoord));\n float fNearCoc = decodeFloat(texture2D(nearcoc, v_Texcoord));\n fNearCoc = abs(fNearCoc * 2.0 - 1.0);\n\n gl_FragColor = encodeHDR(\n mix(\n nearfieldColor, vec4(mix(originalColor.rgb, blurredColor.rgb, weight), 1.0),\n pow(1.0 - fNearCoc, 4.0)\n )\n );\n#else\n gl_FragColor = encodeHDR(vec4(mix(originalColor.rgb, blurredColor.rgb, weight), 1.0));\n#endif\n\n#if DEBUG == 1\n gl_FragColor = vec4(vec3(fCoc), 1.0);\n#elif DEBUG == 2\n gl_FragColor = vec4(vec3(fNearCoc), 1.0);\n#elif DEBUG == 3\n gl_FragColor = encodeHDR(blurredColor);\n#elif DEBUG == 4\n gl_FragColor = encodeHDR(nearfieldColor);\n#endif\n}\n\n@end"; + + +/***/ }, +/* 126 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.compositor.lensflare\n\n#define SAMPLE_NUMBER 8\n\nuniform sampler2D texture;\nuniform sampler2D lenscolor;\n\nuniform vec2 textureSize : [512, 512];\n\nuniform float dispersal : 0.3;\nuniform float haloWidth : 0.4;\nuniform float distortion : 1.0;\n\nvarying vec2 v_Texcoord;\n\n@import qtek.util.rgbm\n\nvec4 textureDistorted(\n in vec2 texcoord,\n in vec2 direction,\n in vec3 distortion\n) {\n return vec4(\n decodeHDR(texture2D(texture, texcoord + direction * distortion.r)).r,\n decodeHDR(texture2D(texture, texcoord + direction * distortion.g)).g,\n decodeHDR(texture2D(texture, texcoord + direction * distortion.b)).b,\n 1.0\n );\n}\n\nvoid main()\n{\n vec2 texcoord = -v_Texcoord + vec2(1.0); vec2 textureOffset = 1.0 / textureSize;\n\n vec2 ghostVec = (vec2(0.5) - texcoord) * dispersal;\n vec2 haloVec = normalize(ghostVec) * haloWidth;\n\n vec3 distortion = vec3(-textureOffset.x * distortion, 0.0, textureOffset.x * distortion);\n vec4 result = vec4(0.0);\n for (int i = 0; i < SAMPLE_NUMBER; i++)\n {\n vec2 offset = fract(texcoord + ghostVec * float(i));\n\n float weight = length(vec2(0.5) - offset) / length(vec2(0.5));\n weight = pow(1.0 - weight, 10.0);\n\n result += textureDistorted(offset, normalize(ghostVec), distortion) * weight;\n }\n\n result *= texture2D(lenscolor, vec2(length(vec2(0.5) - texcoord)) / length(vec2(0.5)));\n float weight = length(vec2(0.5) - fract(texcoord + haloVec)) / length(vec2(0.5));\n weight = pow(1.0 - weight, 10.0);\n vec2 offset = fract(texcoord + haloVec);\n result += textureDistorted(offset, normalize(ghostVec), distortion) * weight;\n\n gl_FragColor = result;\n}\n@end"; + + +/***/ }, +/* 127 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.compositor.blend\n#ifdef TEXTURE1_ENABLED\nuniform sampler2D texture1;\nuniform float weight1 : 1.0;\n#endif\n#ifdef TEXTURE2_ENABLED\nuniform sampler2D texture2;\nuniform float weight2 : 1.0;\n#endif\n#ifdef TEXTURE3_ENABLED\nuniform sampler2D texture3;\nuniform float weight3 : 1.0;\n#endif\n#ifdef TEXTURE4_ENABLED\nuniform sampler2D texture4;\nuniform float weight4 : 1.0;\n#endif\n#ifdef TEXTURE5_ENABLED\nuniform sampler2D texture5;\nuniform float weight5 : 1.0;\n#endif\n#ifdef TEXTURE6_ENABLED\nuniform sampler2D texture6;\nuniform float weight6 : 1.0;\n#endif\n\nvarying vec2 v_Texcoord;\n\n@import qtek.util.rgbm\n\nvoid main()\n{\n vec4 tex = vec4(0.0);\n#ifdef TEXTURE1_ENABLED\n tex += decodeHDR(texture2D(texture1, v_Texcoord)) * weight1;\n#endif\n#ifdef TEXTURE2_ENABLED\n tex += decodeHDR(texture2D(texture2, v_Texcoord)) * weight2;\n#endif\n#ifdef TEXTURE3_ENABLED\n tex += decodeHDR(texture2D(texture3, v_Texcoord)) * weight3;\n#endif\n#ifdef TEXTURE4_ENABLED\n tex += decodeHDR(texture2D(texture4, v_Texcoord)) * weight4;\n#endif\n#ifdef TEXTURE5_ENABLED\n tex += decodeHDR(texture2D(texture5, v_Texcoord)) * weight5;\n#endif\n#ifdef TEXTURE6_ENABLED\n tex += decodeHDR(texture2D(texture6, v_Texcoord)) * weight6;\n#endif\n\n gl_FragColor = encodeHDR(tex);\n}\n@end"; + + +/***/ }, +/* 128 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.compositor.fxaa\n\nuniform sampler2D texture;\nuniform vec4 viewport : VIEWPORT;\n\nvarying vec2 v_Texcoord;\n\n#define FXAA_REDUCE_MIN (1.0/128.0)\n#define FXAA_REDUCE_MUL (1.0/8.0)\n#define FXAA_SPAN_MAX 8.0\n\n@import qtek.util.rgbm\n\nvoid main()\n{\n vec2 resolution = 1.0 / viewport.zw;\n vec3 rgbNW = decodeHDR( texture2D( texture, ( gl_FragCoord.xy + vec2( -1.0, -1.0 ) ) * resolution ) ).xyz;\n vec3 rgbNE = decodeHDR( texture2D( texture, ( gl_FragCoord.xy + vec2( 1.0, -1.0 ) ) * resolution ) ).xyz;\n vec3 rgbSW = decodeHDR( texture2D( texture, ( gl_FragCoord.xy + vec2( -1.0, 1.0 ) ) * resolution ) ).xyz;\n vec3 rgbSE = decodeHDR( texture2D( texture, ( gl_FragCoord.xy + vec2( 1.0, 1.0 ) ) * resolution ) ).xyz;\n vec4 rgbaM = decodeHDR( texture2D( texture, gl_FragCoord.xy * resolution ) );\n vec3 rgbM = rgbaM.xyz;\n float opacity = rgbaM.w;\n\n vec3 luma = vec3( 0.299, 0.587, 0.114 );\n\n float lumaNW = dot( rgbNW, luma );\n float lumaNE = dot( rgbNE, luma );\n float lumaSW = dot( rgbSW, luma );\n float lumaSE = dot( rgbSE, luma );\n float lumaM = dot( rgbM, luma );\n float lumaMin = min( lumaM, min( min( lumaNW, lumaNE ), min( lumaSW, lumaSE ) ) );\n float lumaMax = max( lumaM, max( max( lumaNW, lumaNE) , max( lumaSW, lumaSE ) ) );\n\n vec2 dir;\n dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\n float dirReduce = max( ( lumaNW + lumaNE + lumaSW + lumaSE ) * ( 0.25 * FXAA_REDUCE_MUL ), FXAA_REDUCE_MIN );\n\n float rcpDirMin = 1.0 / ( min( abs( dir.x ), abs( dir.y ) ) + dirReduce );\n dir = min( vec2( FXAA_SPAN_MAX, FXAA_SPAN_MAX),\n max( vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),\n dir * rcpDirMin)) * resolution;\n\n vec3 rgbA = decodeHDR( texture2D( texture, gl_FragCoord.xy * resolution + dir * ( 1.0 / 3.0 - 0.5 ) ) ).xyz;\n rgbA += decodeHDR( texture2D( texture, gl_FragCoord.xy * resolution + dir * ( 2.0 / 3.0 - 0.5 ) ) ).xyz;\n rgbA *= 0.5;\n\n vec3 rgbB = decodeHDR( texture2D( texture, gl_FragCoord.xy * resolution + dir * -0.5 ) ).xyz;\n rgbB += decodeHDR( texture2D( texture, gl_FragCoord.xy * resolution + dir * 0.5 ) ).xyz;\n rgbB *= 0.25;\n rgbB += rgbA * 0.5;\n\n float lumaB = dot( rgbB, luma );\n\n if ( ( lumaB < lumaMin ) || ( lumaB > lumaMax ) )\n {\n gl_FragColor = vec4( rgbA, opacity );\n\n }\n else {\n\n gl_FragColor = vec4( rgbB, opacity );\n\n }\n}\n\n@end"; + + +/***/ }, +/* 129 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.compositor.fxaa3\n\nuniform sampler2D texture;\nuniform vec4 viewport : VIEWPORT;\n\nuniform float subpixel: 0.75;\nuniform float edgeThreshold: 0.125;\nuniform float edgeThresholdMin: 0.0625;\n\n\nvarying vec2 v_Texcoord;\n\n@import qtek.util.rgbm\n\nfloat FxaaLuma(vec4 rgba) { return rgba.y; }\nvec4 FxaaPixelShader(\n vec2 pos\n ,sampler2D tex\n ,vec2 fxaaQualityRcpFrame\n ,float fxaaQualitySubpix\n ,float fxaaQualityEdgeThreshold\n ,float fxaaQualityEdgeThresholdMin\n) {\n vec2 posM;\n posM.x = pos.x;\n posM.y = pos.y;\n vec4 rgbyM = decodeHDR(texture2D(texture, posM, 0.0));\n float lumaS = FxaaLuma(decodeHDR(texture2D(texture, posM + (vec2( 0.0, 1.0) * fxaaQualityRcpFrame.xy), 0.0)));\n float lumaE = FxaaLuma(decodeHDR(texture2D(texture, posM + (vec2( 1.0, 0.0) * fxaaQualityRcpFrame.xy), 0.0)));\n float lumaN = FxaaLuma(decodeHDR(texture2D(texture, posM + (vec2( 0.0,-1.0) * fxaaQualityRcpFrame.xy), 0.0)));\n float lumaW = FxaaLuma(decodeHDR(texture2D(texture, posM + (vec2(-1.0, 0.0) * fxaaQualityRcpFrame.xy), 0.0)));\n\n float maxSM = max(lumaS, rgbyM.y);\n float minSM = min(lumaS, rgbyM.y);\n float maxESM = max(lumaE, maxSM);\n float minESM = min(lumaE, minSM);\n float maxWN = max(lumaN, lumaW);\n float minWN = min(lumaN, lumaW);\n float rangeMax = max(maxWN, maxESM);\n float rangeMin = min(minWN, minESM);\n float rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold;\n float range = rangeMax - rangeMin;\n float rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled);\n bool earlyExit = range < rangeMaxClamped;\n if(earlyExit) return rgbyM;\n float lumaNW = FxaaLuma(decodeHDR(texture2D(texture, posM + (vec2(-1.0,-1.0) * fxaaQualityRcpFrame.xy), 0.0)));\n float lumaSE = FxaaLuma(decodeHDR(texture2D(texture, posM + (vec2( 1.0, 1.0) * fxaaQualityRcpFrame.xy), 0.0)));\n float lumaNE = FxaaLuma(decodeHDR(texture2D(texture, posM + (vec2( 1.0,-1.0) * fxaaQualityRcpFrame.xy), 0.0)));\n float lumaSW = FxaaLuma(decodeHDR(texture2D(texture, posM + (vec2(-1.0, 1.0) * fxaaQualityRcpFrame.xy), 0.0)));\n\n float lumaNS = lumaN + lumaS;\n float lumaWE = lumaW + lumaE;\n float subpixRcpRange = 1.0/range;\n float subpixNSWE = lumaNS + lumaWE;\n float edgeHorz1 = (-2.0 * rgbyM.y) + lumaNS;\n float edgeVert1 = (-2.0 * rgbyM.y) + lumaWE;\n float lumaNESE = lumaNE + lumaSE;\n float lumaNWNE = lumaNW + lumaNE;\n float edgeHorz2 = (-2.0 * lumaE) + lumaNESE;\n float edgeVert2 = (-2.0 * lumaN) + lumaNWNE;\n float lumaNWSW = lumaNW + lumaSW;\n float lumaSWSE = lumaSW + lumaSE;\n float edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2);\n float edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2);\n float edgeHorz3 = (-2.0 * lumaW) + lumaNWSW;\n float edgeVert3 = (-2.0 * lumaS) + lumaSWSE;\n float edgeHorz = abs(edgeHorz3) + edgeHorz4;\n float edgeVert = abs(edgeVert3) + edgeVert4;\n float subpixNWSWNESE = lumaNWSW + lumaNESE;\n float lengthSign = fxaaQualityRcpFrame.x;\n bool horzSpan = edgeHorz >= edgeVert;\n float subpixA = subpixNSWE * 2.0 + subpixNWSWNESE;\n if(!horzSpan) lumaN = lumaW;\n if(!horzSpan) lumaS = lumaE;\n if(horzSpan) lengthSign = fxaaQualityRcpFrame.y;\n float subpixB = (subpixA * (1.0/12.0)) - rgbyM.y;\n float gradientN = lumaN - rgbyM.y;\n float gradientS = lumaS - rgbyM.y;\n float lumaNN = lumaN + rgbyM.y;\n float lumaSS = lumaS + rgbyM.y;\n bool pairN = abs(gradientN) >= abs(gradientS);\n float gradient = max(abs(gradientN), abs(gradientS));\n if(pairN) lengthSign = -lengthSign;\n float subpixC = clamp(abs(subpixB) * subpixRcpRange, 0.0, 1.0);\n vec2 posB;\n posB.x = posM.x;\n posB.y = posM.y;\n vec2 offNP;\n offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x;\n offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y;\n if(!horzSpan) posB.x += lengthSign * 0.5;\n if( horzSpan) posB.y += lengthSign * 0.5;\n vec2 posN;\n posN.x = posB.x - offNP.x * 1.0;\n posN.y = posB.y - offNP.y * 1.0;\n vec2 posP;\n posP.x = posB.x + offNP.x * 1.0;\n posP.y = posB.y + offNP.y * 1.0;\n float subpixD = ((-2.0)*subpixC) + 3.0;\n float lumaEndN = FxaaLuma(decodeHDR(texture2D(texture, posN, 0.0)));\n float subpixE = subpixC * subpixC;\n float lumaEndP = FxaaLuma(decodeHDR(texture2D(texture, posP, 0.0)));\n if(!pairN) lumaNN = lumaSS;\n float gradientScaled = gradient * 1.0/4.0;\n float lumaMM = rgbyM.y - lumaNN * 0.5;\n float subpixF = subpixD * subpixE;\n bool lumaMLTZero = lumaMM < 0.0;\n lumaEndN -= lumaNN * 0.5;\n lumaEndP -= lumaNN * 0.5;\n bool doneN = abs(lumaEndN) >= gradientScaled;\n bool doneP = abs(lumaEndP) >= gradientScaled;\n if(!doneN) posN.x -= offNP.x * 1.5;\n if(!doneN) posN.y -= offNP.y * 1.5;\n bool doneNP = (!doneN) || (!doneP);\n if(!doneP) posP.x += offNP.x * 1.5;\n if(!doneP) posP.y += offNP.y * 1.5;\n if(doneNP) {\n if(!doneN) lumaEndN = FxaaLuma(decodeHDR(texture2D(texture, posN.xy, 0.0)));\n if(!doneP) lumaEndP = FxaaLuma(decodeHDR(texture2D(texture, posP.xy, 0.0)));\n if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;\n if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;\n doneN = abs(lumaEndN) >= gradientScaled;\n doneP = abs(lumaEndP) >= gradientScaled;\n if(!doneN) posN.x -= offNP.x * 2.0;\n if(!doneN) posN.y -= offNP.y * 2.0;\n doneNP = (!doneN) || (!doneP);\n if(!doneP) posP.x += offNP.x * 2.0;\n if(!doneP) posP.y += offNP.y * 2.0;\n\n if(doneNP) {\n if(!doneN) lumaEndN = FxaaLuma(decodeHDR(texture2D(texture, posN.xy, 0.0)));\n if(!doneP) lumaEndP = FxaaLuma(decodeHDR(texture2D(texture, posP.xy, 0.0)));\n if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;\n if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;\n doneN = abs(lumaEndN) >= gradientScaled;\n doneP = abs(lumaEndP) >= gradientScaled;\n if(!doneN) posN.x -= offNP.x * 4.0;\n if(!doneN) posN.y -= offNP.y * 4.0;\n doneNP = (!doneN) || (!doneP);\n if(!doneP) posP.x += offNP.x * 4.0;\n if(!doneP) posP.y += offNP.y * 4.0;\n\n if(doneNP) {\n if(!doneN) lumaEndN = FxaaLuma(decodeHDR(texture2D(texture, posN.xy, 0.0)));\n if(!doneP) lumaEndP = FxaaLuma(decodeHDR(texture2D(texture, posP.xy, 0.0)));\n if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;\n if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;\n doneN = abs(lumaEndN) >= gradientScaled;\n doneP = abs(lumaEndP) >= gradientScaled;\n if(!doneN) posN.x -= offNP.x * 12.0;\n if(!doneN) posN.y -= offNP.y * 12.0;\n doneNP = (!doneN) || (!doneP);\n if(!doneP) posP.x += offNP.x * 12.0;\n if(!doneP) posP.y += offNP.y * 12.0;\n }\n }\n }\n float dstN = posM.x - posN.x;\n float dstP = posP.x - posM.x;\n if(!horzSpan) dstN = posM.y - posN.y;\n if(!horzSpan) dstP = posP.y - posM.y;\n bool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero;\n float spanLength = (dstP + dstN);\n bool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero;\n float spanLengthRcp = 1.0/spanLength;\n bool directionN = dstN < dstP;\n float dst = min(dstN, dstP);\n bool goodSpan = directionN ? goodSpanN : goodSpanP;\n float subpixG = subpixF * subpixF;\n float pixelOffset = (dst * (-spanLengthRcp)) + 0.5;\n float subpixH = subpixG * fxaaQualitySubpix;\n float pixelOffsetGood = goodSpan ? pixelOffset : 0.0;\n float pixelOffsetSubpix = max(pixelOffsetGood, subpixH);\n if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign;\n if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign;\n return vec4(decodeHDR(texture2D(texture, posM, 0.0)).xyz, rgbyM.y);\n\n}\n\nvoid main()\n{\n vec4 color = FxaaPixelShader(\n v_Texcoord,\n texture,\n vec2(1.0) / viewport.zw,\n subpixel,\n edgeThreshold,\n edgeThresholdMin\n );\n gl_FragColor = vec4(color.rgb, 1.0);\n}\n@end"; + + +/***/ }, +/* 130 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var glMatrix = __webpack_require__(14); + var mat2 = glMatrix.mat2; + + /** + * @constructor + * @alias qtek.math.Matrix2 + */ + var Matrix2 = function() { + + /** + * Storage of Matrix2 + * @name _array + * @type {Float32Array} + */ + this._array = mat2.create(); + + /** + * @name _dirty + * @type {boolean} + */ + this._dirty = true; + }; + + Matrix2.prototype = { + + constructor: Matrix2, + + /** + * Set components from array + * @param {Float32Array|number[]} arr + */ + setArray: function (arr) { + for (var i = 0; i < this._array.length; i++) { + this._array[i] = arr[i]; + } + this._dirty = true; + return this; + }, + /** + * Clone a new Matrix2 + * @return {qtek.math.Matrix2} + */ + clone: function() { + return (new Matrix2()).copy(this); + }, + + /** + * Copy from b + * @param {qtek.math.Matrix2} b + * @return {qtek.math.Matrix2} + */ + copy: function(b) { + mat2.copy(this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Calculate the adjugate of self, in-place + * @return {qtek.math.Matrix2} + */ + adjoint: function() { + mat2.adjoint(this._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Calculate matrix determinant + * @return {number} + */ + determinant: function() { + return mat2.determinant(this._array); + }, + + /** + * Set to a identity matrix + * @return {qtek.math.Matrix2} + */ + identity: function() { + mat2.identity(this._array); + this._dirty = true; + return this; + }, + + /** + * Invert self + * @return {qtek.math.Matrix2} + */ + invert: function() { + mat2.invert(this._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Alias for mutiply + * @param {qtek.math.Matrix2} b + * @return {qtek.math.Matrix2} + */ + mul: function(b) { + mat2.mul(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Alias for multiplyLeft + * @param {qtek.math.Matrix2} a + * @return {qtek.math.Matrix2} + */ + mulLeft: function(a) { + mat2.mul(this._array, a._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Multiply self and b + * @param {qtek.math.Matrix2} b + * @return {qtek.math.Matrix2} + */ + multiply: function(b) { + mat2.multiply(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Multiply a and self, a is on the left + * @param {qtek.math.Matrix2} a + * @return {qtek.math.Matrix2} + */ + multiplyLeft: function(a) { + mat2.multiply(this._array, a._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Rotate self by a given radian + * @param {number} rad + * @return {qtek.math.Matrix2} + */ + rotate: function(rad) { + mat2.rotate(this._array, this._array, rad); + this._dirty = true; + return this; + }, + + /** + * Scale self by s + * @param {qtek.math.Vector2} s + * @return {qtek.math.Matrix2} + */ + scale: function(v) { + mat2.scale(this._array, this._array, v._array); + this._dirty = true; + return this; + }, + /** + * Transpose self, in-place. + * @return {qtek.math.Matrix2} + */ + transpose: function() { + mat2.transpose(this._array, this._array); + this._dirty = true; + return this; + }, + + toString: function() { + return '[' + Array.prototype.join.call(this._array, ',') + ']'; + }, + + toArray: function () { + return Array.prototype.slice.call(this._array); + } + }; + + /** + * @param {Matrix2} out + * @param {Matrix2} a + * @return {Matrix2} + */ + Matrix2.adjoint = function(out, a) { + mat2.adjoint(out._array, a._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix2} out + * @param {qtek.math.Matrix2} a + * @return {qtek.math.Matrix2} + */ + Matrix2.copy = function(out, a) { + mat2.copy(out._array, a._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix2} a + * @return {number} + */ + Matrix2.determinant = function(a) { + return mat2.determinant(a._array); + }; + + /** + * @param {qtek.math.Matrix2} out + * @return {qtek.math.Matrix2} + */ + Matrix2.identity = function(out) { + mat2.identity(out._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix2} out + * @param {qtek.math.Matrix2} a + * @return {qtek.math.Matrix2} + */ + Matrix2.invert = function(out, a) { + mat2.invert(out._array, a._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix2} out + * @param {qtek.math.Matrix2} a + * @param {qtek.math.Matrix2} b + * @return {qtek.math.Matrix2} + */ + Matrix2.mul = function(out, a, b) { + mat2.mul(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + + /** + * @method + * @param {qtek.math.Matrix2} out + * @param {qtek.math.Matrix2} a + * @param {qtek.math.Matrix2} b + * @return {qtek.math.Matrix2} + */ + Matrix2.multiply = Matrix2.mul; + + /** + * @param {qtek.math.Matrix2} out + * @param {qtek.math.Matrix2} a + * @param {number} rad + * @return {qtek.math.Matrix2} + */ + Matrix2.rotate = function(out, a, rad) { + mat2.rotate(out._array, a._array, rad); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix2} out + * @param {qtek.math.Matrix2} a + * @param {qtek.math.Vector2} v + * @return {qtek.math.Matrix2} + */ + Matrix2.scale = function(out, a, v) { + mat2.scale(out._array, a._array, v._array); + out._dirty = true; + return out; + }; + /** + * @param {Matrix2} out + * @param {Matrix2} a + * @return {Matrix2} + */ + Matrix2.transpose = function(out, a) { + mat2.transpose(out._array, a._array); + out._dirty = true; + return out; + }; + + module.exports = Matrix2; + + +/***/ }, +/* 131 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var glMatrix = __webpack_require__(14); + var mat2d = glMatrix.mat2d; + + /** + * @constructor + * @alias qtek.math.Matrix2d + */ + var Matrix2d = function() { + /** + * Storage of Matrix2d + * @name _array + * @type {Float32Array} + */ + this._array = mat2d.create(); + + /** + * @name _dirty + * @type {boolean} + */ + this._dirty = true; + }; + + Matrix2d.prototype = { + + constructor: Matrix2d, + + /** + * Set components from array + * @param {Float32Array|number[]} arr + */ + setArray: function (arr) { + for (var i = 0; i < this._array.length; i++) { + this._array[i] = arr[i]; + } + this._dirty = true; + return this; + }, + /** + * Clone a new Matrix2d + * @return {qtek.math.Matrix2d} + */ + clone: function() { + return (new Matrix2d()).copy(this); + }, + + /** + * Copy from b + * @param {qtek.math.Matrix2d} b + * @return {qtek.math.Matrix2d} + */ + copy: function(b) { + mat2d.copy(this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Calculate matrix determinant + * @return {number} + */ + determinant: function() { + return mat2d.determinant(this._array); + }, + + /** + * Set to a identity matrix + * @return {qtek.math.Matrix2d} + */ + identity: function() { + mat2d.identity(this._array); + this._dirty = true; + return this; + }, + + /** + * Invert self + * @return {qtek.math.Matrix2d} + */ + invert: function() { + mat2d.invert(this._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Alias for mutiply + * @param {qtek.math.Matrix2d} b + * @return {qtek.math.Matrix2d} + */ + mul: function(b) { + mat2d.mul(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Alias for multiplyLeft + * @param {qtek.math.Matrix2d} a + * @return {qtek.math.Matrix2d} + */ + mulLeft: function(b) { + mat2d.mul(this._array, b._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Multiply self and b + * @param {qtek.math.Matrix2d} b + * @return {qtek.math.Matrix2d} + */ + multiply: function(b) { + mat2d.multiply(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Multiply a and self, a is on the left + * @param {qtek.math.Matrix2d} a + * @return {qtek.math.Matrix2d} + */ + multiplyLeft: function(b) { + mat2d.multiply(this._array, b._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Rotate self by a given radian + * @param {number} rad + * @return {qtek.math.Matrix2d} + */ + rotate: function(rad) { + mat2d.rotate(this._array, this._array, rad); + this._dirty = true; + return this; + }, + + /** + * Scale self by s + * @param {qtek.math.Vector2} s + * @return {qtek.math.Matrix2d} + */ + scale: function(s) { + mat2d.scale(this._array, this._array, s._array); + this._dirty = true; + return this; + }, + + /** + * Translate self by v + * @param {qtek.math.Vector2} v + * @return {qtek.math.Matrix2d} + */ + translate: function(v) { + mat2d.translate(this._array, this._array, v._array); + this._dirty = true; + return this; + }, + + toString: function() { + return '[' + Array.prototype.join.call(this._array, ',') + ']'; + }, + + toArray: function () { + return Array.prototype.slice.call(this._array); + } + }; + + /** + * @param {qtek.math.Matrix2d} out + * @param {qtek.math.Matrix2d} a + * @return {qtek.math.Matrix2d} + */ + Matrix2d.copy = function(out, a) { + mat2d.copy(out._array, a._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix2d} a + * @return {number} + */ + Matrix2d.determinant = function(a) { + return mat2d.determinant(a._array); + }; + + /** + * @param {qtek.math.Matrix2d} out + * @return {qtek.math.Matrix2d} + */ + Matrix2d.identity = function(out) { + mat2d.identity(out._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix2d} out + * @param {qtek.math.Matrix2d} a + * @return {qtek.math.Matrix2d} + */ + Matrix2d.invert = function(out, a) { + mat2d.invert(out._array, a._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix2d} out + * @param {qtek.math.Matrix2d} a + * @param {qtek.math.Matrix2d} b + * @return {qtek.math.Matrix2d} + */ + Matrix2d.mul = function(out, a, b) { + mat2d.mul(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + + /** + * @method + * @param {qtek.math.Matrix2d} out + * @param {qtek.math.Matrix2d} a + * @param {qtek.math.Matrix2d} b + * @return {qtek.math.Matrix2d} + */ + Matrix2d.multiply = Matrix2d.mul; + + /** + * @param {qtek.math.Matrix2d} out + * @param {qtek.math.Matrix2d} a + * @param {number} rad + * @return {qtek.math.Matrix2d} + */ + Matrix2d.rotate = function(out, a, rad) { + mat2d.rotate(out._array, a._array, rad); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix2d} out + * @param {qtek.math.Matrix2d} a + * @param {qtek.math.Vector2} v + * @return {qtek.math.Matrix2d} + */ + Matrix2d.scale = function(out, a, v) { + mat2d.scale(out._array, a._array, v._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix2d} out + * @param {qtek.math.Matrix2d} a + * @param {qtek.math.Vector2} v + * @return {qtek.math.Matrix2d} + */ + Matrix2d.translate = function(out, a, v) { + mat2d.translate(out._array, a._array, v._array); + out._dirty = true; + return out; + }; + + module.exports = Matrix2d; + + +/***/ }, +/* 132 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var glMatrix = __webpack_require__(14); + var mat3 = glMatrix.mat3; + + /** + * @constructor + * @alias qtek.math.Matrix3 + */ + var Matrix3 = function () { + + /** + * Storage of Matrix3 + * @name _array + * @type {Float32Array} + */ + this._array = mat3.create(); + + /** + * @name _dirty + * @type {boolean} + */ + this._dirty = true; + }; + + Matrix3.prototype = { + + constructor: Matrix3, + + /** + * Set components from array + * @param {Float32Array|number[]} arr + */ + setArray: function (arr) { + for (var i = 0; i < this._array.length; i++) { + this._array[i] = arr[i]; + } + this._dirty = true; + return this; + }, + /** + * Calculate the adjugate of self, in-place + * @return {qtek.math.Matrix3} + */ + adjoint: function () { + mat3.adjoint(this._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Clone a new Matrix3 + * @return {qtek.math.Matrix3} + */ + clone: function () { + return (new Matrix3()).copy(this); + }, + + /** + * Copy from b + * @param {qtek.math.Matrix3} b + * @return {qtek.math.Matrix3} + */ + copy: function (b) { + mat3.copy(this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Calculate matrix determinant + * @return {number} + */ + determinant: function () { + return mat3.determinant(this._array); + }, + + /** + * Copy the values from Matrix2d a + * @param {qtek.math.Matrix2d} a + * @return {qtek.math.Matrix3} + */ + fromMat2d: function (a) { + mat3.fromMat2d(this._array, a._array); + this._dirty = true; + return this; + }, + + /** + * Copies the upper-left 3x3 values of Matrix4 + * @param {qtek.math.Matrix4} a + * @return {qtek.math.Matrix3} + */ + fromMat4: function (a) { + mat3.fromMat4(this._array, a._array); + this._dirty = true; + return this; + }, + + /** + * Calculates a rotation matrix from the given quaternion + * @param {qtek.math.Quaternion} q + * @return {qtek.math.Matrix3} + */ + fromQuat: function (q) { + mat3.fromQuat(this._array, q._array); + this._dirty = true; + return this; + }, + + /** + * Set to a identity matrix + * @return {qtek.math.Matrix3} + */ + identity: function () { + mat3.identity(this._array); + this._dirty = true; + return this; + }, + + /** + * Invert self + * @return {qtek.math.Matrix3} + */ + invert: function () { + mat3.invert(this._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Alias for mutiply + * @param {qtek.math.Matrix3} b + * @return {qtek.math.Matrix3} + */ + mul: function (b) { + mat3.mul(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Alias for multiplyLeft + * @param {qtek.math.Matrix3} a + * @return {qtek.math.Matrix3} + */ + mulLeft: function (a) { + mat3.mul(this._array, a._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Multiply self and b + * @param {qtek.math.Matrix3} b + * @return {qtek.math.Matrix3} + */ + multiply: function (b) { + mat3.multiply(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Multiply a and self, a is on the left + * @param {qtek.math.Matrix3} a + * @return {qtek.math.Matrix3} + */ + multiplyLeft: function (a) { + mat3.multiply(this._array, a._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Rotate self by a given radian + * @param {number} rad + * @return {qtek.math.Matrix3} + */ + rotate: function (rad) { + mat3.rotate(this._array, this._array, rad); + this._dirty = true; + return this; + }, + + /** + * Scale self by s + * @param {qtek.math.Vector2} s + * @return {qtek.math.Matrix3} + */ + scale: function (v) { + mat3.scale(this._array, this._array, v._array); + this._dirty = true; + return this; + }, + + /** + * Translate self by v + * @param {qtek.math.Vector2} v + * @return {qtek.math.Matrix3} + */ + translate: function (v) { + mat3.translate(this._array, this._array, v._array); + this._dirty = true; + return this; + }, + /** + * Calculates a 3x3 normal matrix (transpose inverse) from the 4x4 matrix + * @param {qtek.math.Matrix4} a + */ + normalFromMat4: function (a) { + mat3.normalFromMat4(this._array, a._array); + this._dirty = true; + return this; + }, + + /** + * Transpose self, in-place. + * @return {qtek.math.Matrix2} + */ + transpose: function () { + mat3.transpose(this._array, this._array); + this._dirty = true; + return this; + }, + + toString: function () { + return '[' + Array.prototype.join.call(this._array, ',') + ']'; + }, + + toArray: function () { + return Array.prototype.slice.call(this._array); + } + }; + /** + * @param {qtek.math.Matrix3} out + * @param {qtek.math.Matrix3} a + * @return {qtek.math.Matrix3} + */ + Matrix3.adjoint = function (out, a) { + mat3.adjoint(out._array, a._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix3} out + * @param {qtek.math.Matrix3} a + * @return {qtek.math.Matrix3} + */ + Matrix3.copy = function (out, a) { + mat3.copy(out._array, a._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix3} a + * @return {number} + */ + Matrix3.determinant = function (a) { + return mat3.determinant(a._array); + }; + + /** + * @param {qtek.math.Matrix3} out + * @return {qtek.math.Matrix3} + */ + Matrix3.identity = function (out) { + mat3.identity(out._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix3} out + * @param {qtek.math.Matrix3} a + * @return {qtek.math.Matrix3} + */ + Matrix3.invert = function (out, a) { + mat3.invert(out._array, a._array); + return out; + }; + + /** + * @param {qtek.math.Matrix3} out + * @param {qtek.math.Matrix3} a + * @param {qtek.math.Matrix3} b + * @return {qtek.math.Matrix3} + */ + Matrix3.mul = function (out, a, b) { + mat3.mul(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + + /** + * @method + * @param {qtek.math.Matrix3} out + * @param {qtek.math.Matrix3} a + * @param {qtek.math.Matrix3} b + * @return {qtek.math.Matrix3} + */ + Matrix3.multiply = Matrix3.mul; + + /** + * @param {qtek.math.Matrix3} out + * @param {qtek.math.Matrix2d} a + * @return {qtek.math.Matrix3} + */ + Matrix3.fromMat2d = function (out, a) { + mat3.fromMat2d(out._array, a._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix3} out + * @param {qtek.math.Matrix4} a + * @return {qtek.math.Matrix3} + */ + Matrix3.fromMat4 = function (out, a) { + mat3.fromMat4(out._array, a._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix3} out + * @param {qtek.math.Quaternion} a + * @return {qtek.math.Matrix3} + */ + Matrix3.fromQuat = function (out, q) { + mat3.fromQuat(out._array, q._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix3} out + * @param {qtek.math.Matrix4} a + * @return {qtek.math.Matrix3} + */ + Matrix3.normalFromMat4 = function (out, a) { + mat3.normalFromMat4(out._array, a._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix3} out + * @param {qtek.math.Matrix3} a + * @param {number} rad + * @return {qtek.math.Matrix3} + */ + Matrix3.rotate = function (out, a, rad) { + mat3.rotate(out._array, a._array, rad); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix3} out + * @param {qtek.math.Matrix3} a + * @param {qtek.math.Vector2} v + * @return {qtek.math.Matrix3} + */ + Matrix3.scale = function (out, a, v) { + mat3.scale(out._array, a._array, v._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix3} out + * @param {qtek.math.Matrix3} a + * @return {qtek.math.Matrix3} + */ + Matrix3.transpose = function (out, a) { + mat3.transpose(out._array, a._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Matrix3} out + * @param {qtek.math.Matrix3} a + * @param {qtek.math.Vector2} v + * @return {qtek.math.Matrix3} + */ + Matrix3.translate = function (out, a, v) { + mat3.translate(out._array, a._array, v._array); + out._dirty = true; + return out; + }; + + module.exports = Matrix3; + + +/***/ }, +/* 133 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Vector3 = __webpack_require__(23); + var Vector2 = __webpack_require__(13); + + /** + * Random or constant 1d, 2d, 3d vector generator + * @constructor + * @alias qtek.math.Value + */ + var Value = function() {}; + + /** + * @method + * @param {number|qtek.math.Vector2|qtek.math.Vector3} [out] + * @return {number|qtek.math.Vector2|qtek.math.Vector3} + */ + Value.prototype.get = function(out) {}; + + // Constant + var ConstantValue = function(val) { + this.get = function() { + return val; + }; + }; + ConstantValue.prototype = new Value(); + ConstantValue.prototype.constructor = ConstantValue; + + // Vector + var VectorValue = function(val) { + var Constructor = val.constructor; + this.get = function(out) { + if (!out) { + out = new Constructor(); + } + out.copy(val); + return out; + }; + }; + VectorValue.prototype = new Value(); + VectorValue.prototype.constructor = VectorValue; + //Random 1D + var Random1D = function(min, max) { + var range = max - min; + this.get = function() { + return Math.random() * range + min; + }; + }; + Random1D.prototype = new Value(); + Random1D.prototype.constructor = Random1D; + + // Random2D + var Random2D = function(min, max) { + var rangeX = max.x - min.x; + var rangeY = max.y - min.y; + + this.get = function(out) { + if (!out) { + out = new Vector2(); + } + Vector2.set( + out, + rangeX * Math.random() + min._array[0], + rangeY * Math.random() + min._array[1] + ); + + return out; + }; + }; + Random2D.prototype = new Value(); + Random2D.prototype.constructor = Random2D; + + var Random3D = function(min, max) { + var rangeX = max.x - min.x; + var rangeY = max.y - min.y; + var rangeZ = max.z - min.z; + + this.get = function(out) { + if (!out) { + out = new Vector3(); + } + Vector3.set( + out, + rangeX * Math.random() + min._array[0], + rangeY * Math.random() + min._array[1], + rangeZ * Math.random() + min._array[2] + ); + + return out; + }; + }; + Random3D.prototype = new Value(); + Random3D.prototype.constructor = Random3D; + + // Factory methods + + /** + * Create a constant 1d value generator + * @param {number} constant + * @return {qtek.math.Value} + */ + Value.constant = function(constant) { + return new ConstantValue(constant); + }; + + /** + * Create a constant vector value(2d or 3d) generator + * @param {qtek.math.Vector2|qtek.math.Vector3} vector + * @return {qtek.math.Value} + */ + Value.vector = function(vector) { + return new VectorValue(vector); + }; + + /** + * Create a random 1d value generator + * @param {number} min + * @param {number} max + * @return {qtek.math.Value} + */ + Value.random1D = function(min, max) { + return new Random1D(min, max); + }; + + /** + * Create a random 2d value generator + * @param {qtek.math.Vector2} min + * @param {qtek.math.Vector2} max + * @return {qtek.math.Value} + */ + Value.random2D = function(min, max) { + return new Random2D(min, max); + }; + + /** + * Create a random 3d value generator + * @param {qtek.math.Vector3} min + * @param {qtek.math.Vector3} max + * @return {qtek.math.Value} + */ + Value.random3D = function(min, max) { + return new Random3D(min, max); + }; + + module.exports = Value; + + +/***/ }, +/* 134 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var glMatrix = __webpack_require__(14); + var vec4 = glMatrix.vec4; + + /** + * @constructor + * @alias qtek.math.Vector4 + * @param {number} x + * @param {number} y + * @param {number} z + * @param {number} w + */ + var Vector4 = function(x, y, z, w) { + + x = x || 0; + y = y || 0; + z = z || 0; + w = w || 0; + + /** + * Storage of Vector4, read and write of x, y, z, w will change the values in _array + * All methods also operate on the _array instead of x, y, z, w components + * @name _array + * @type {Float32Array} + */ + this._array = vec4.fromValues(x, y, z, w); + + /** + * Dirty flag is used by the Node to determine + * if the matrix is updated to latest + * @name _dirty + * @type {boolean} + */ + this._dirty = true; + }; + + Vector4.prototype = { + + constructor: Vector4, + + /** + * Add b to self + * @param {qtek.math.Vector4} b + * @return {qtek.math.Vector4} + */ + add: function(b) { + vec4.add(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Set x, y and z components + * @param {number} x + * @param {number} y + * @param {number} z + * @param {number} w + * @return {qtek.math.Vector4} + */ + set: function(x, y, z, w) { + this._array[0] = x; + this._array[1] = y; + this._array[2] = z; + this._array[3] = w; + this._dirty = true; + return this; + }, + + /** + * Set x, y, z and w components from array + * @param {Float32Array|number[]} arr + * @return {qtek.math.Vector4} + */ + setArray: function(arr) { + this._array[0] = arr[0]; + this._array[1] = arr[1]; + this._array[2] = arr[2]; + this._array[3] = arr[3]; + + this._dirty = true; + return this; + }, + + /** + * Clone a new Vector4 + * @return {qtek.math.Vector4} + */ + clone: function() { + return new Vector4(this.x, this.y, this.z, this.w); + }, + + /** + * Copy from b + * @param {qtek.math.Vector4} b + * @return {qtek.math.Vector4} + */ + copy: function(b) { + vec4.copy(this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Alias for distance + * @param {qtek.math.Vector4} b + * @return {number} + */ + dist: function(b) { + return vec4.dist(this._array, b._array); + }, + + /** + * Distance between self and b + * @param {qtek.math.Vector4} b + * @return {number} + */ + distance: function(b) { + return vec4.distance(this._array, b._array); + }, + + /** + * Alias for divide + * @param {qtek.math.Vector4} b + * @return {qtek.math.Vector4} + */ + div: function(b) { + vec4.div(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Divide self by b + * @param {qtek.math.Vector4} b + * @return {qtek.math.Vector4} + */ + divide: function(b) { + vec4.divide(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Dot product of self and b + * @param {qtek.math.Vector4} b + * @return {number} + */ + dot: function(b) { + return vec4.dot(this._array, b._array); + }, + + /** + * Alias of length + * @return {number} + */ + len: function() { + return vec4.len(this._array); + }, + + /** + * Calculate the length + * @return {number} + */ + length: function() { + return vec4.length(this._array); + }, + /** + * Linear interpolation between a and b + * @param {qtek.math.Vector4} a + * @param {qtek.math.Vector4} b + * @param {number} t + * @return {qtek.math.Vector4} + */ + lerp: function(a, b, t) { + vec4.lerp(this._array, a._array, b._array, t); + this._dirty = true; + return this; + }, + + /** + * Minimum of self and b + * @param {qtek.math.Vector4} b + * @return {qtek.math.Vector4} + */ + min: function(b) { + vec4.min(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Maximum of self and b + * @param {qtek.math.Vector4} b + * @return {qtek.math.Vector4} + */ + max: function(b) { + vec4.max(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Alias for multiply + * @param {qtek.math.Vector4} b + * @return {qtek.math.Vector4} + */ + mul: function(b) { + vec4.mul(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Mutiply self and b + * @param {qtek.math.Vector4} b + * @return {qtek.math.Vector4} + */ + multiply: function(b) { + vec4.multiply(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Negate self + * @return {qtek.math.Vector4} + */ + negate: function() { + vec4.negate(this._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Normalize self + * @return {qtek.math.Vector4} + */ + normalize: function() { + vec4.normalize(this._array, this._array); + this._dirty = true; + return this; + }, + + /** + * Generate random x, y, z, w components with a given scale + * @param {number} scale + * @return {qtek.math.Vector4} + */ + random: function(scale) { + vec4.random(this._array, scale); + this._dirty = true; + return this; + }, + + /** + * Scale self + * @param {number} scale + * @return {qtek.math.Vector4} + */ + scale: function(s) { + vec4.scale(this._array, this._array, s); + this._dirty = true; + return this; + }, + /** + * Scale b and add to self + * @param {qtek.math.Vector4} b + * @param {number} scale + * @return {qtek.math.Vector4} + */ + scaleAndAdd: function(b, s) { + vec4.scaleAndAdd(this._array, this._array, b._array, s); + this._dirty = true; + return this; + }, + + /** + * Alias for squaredDistance + * @param {qtek.math.Vector4} b + * @return {number} + */ + sqrDist: function(b) { + return vec4.sqrDist(this._array, b._array); + }, + + /** + * Squared distance between self and b + * @param {qtek.math.Vector4} b + * @return {number} + */ + squaredDistance: function(b) { + return vec4.squaredDistance(this._array, b._array); + }, + + /** + * Alias for squaredLength + * @return {number} + */ + sqrLen: function() { + return vec4.sqrLen(this._array); + }, + + /** + * Squared length of self + * @return {number} + */ + squaredLength: function() { + return vec4.squaredLength(this._array); + }, + + /** + * Alias for subtract + * @param {qtek.math.Vector4} b + * @return {qtek.math.Vector4} + */ + sub: function(b) { + vec4.sub(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Subtract b from self + * @param {qtek.math.Vector4} b + * @return {qtek.math.Vector4} + */ + subtract: function(b) { + vec4.subtract(this._array, this._array, b._array); + this._dirty = true; + return this; + }, + + /** + * Transform self with a Matrix4 m + * @param {qtek.math.Matrix4} m + * @return {qtek.math.Vector4} + */ + transformMat4: function(m) { + vec4.transformMat4(this._array, this._array, m._array); + this._dirty = true; + return this; + }, + + /** + * Transform self with a Quaternion q + * @param {qtek.math.Quaternion} q + * @return {qtek.math.Vector4} + */ + transformQuat: function(q) { + vec4.transformQuat(this._array, this._array, q._array); + this._dirty = true; + return this; + }, + + toString: function() { + return '[' + Array.prototype.join.call(this._array, ',') + ']'; + }, + + toArray: function () { + return Array.prototype.slice.call(this._array); + } + }; + + var defineProperty = Object.defineProperty; + // Getter and Setter + if (defineProperty) { + + var proto = Vector4.prototype; + /** + * @name x + * @type {number} + * @memberOf qtek.math.Vector4 + * @instance + */ + defineProperty(proto, 'x', { + get: function () { + return this._array[0]; + }, + set: function (value) { + this._array[0] = value; + this._dirty = true; + } + }); + + /** + * @name y + * @type {number} + * @memberOf qtek.math.Vector4 + * @instance + */ + defineProperty(proto, 'y', { + get: function () { + return this._array[1]; + }, + set: function (value) { + this._array[1] = value; + this._dirty = true; + } + }); + + /** + * @name z + * @type {number} + * @memberOf qtek.math.Vector4 + * @instance + */ + defineProperty(proto, 'z', { + get: function () { + return this._array[2]; + }, + set: function (value) { + this._array[2] = value; + this._dirty = true; + } + }); + + /** + * @name w + * @type {number} + * @memberOf qtek.math.Vector4 + * @instance + */ + defineProperty(proto, 'w', { + get: function () { + return this._array[3]; + }, + set: function (value) { + this._array[3] = value; + this._dirty = true; + } + }); + } + + // Supply methods that are not in place + + /** + * @param {qtek.math.Vector4} out + * @param {qtek.math.Vector4} a + * @param {qtek.math.Vector4} b + * @return {qtek.math.Vector4} + */ + Vector4.add = function(out, a, b) { + vec4.add(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Vector4} out + * @param {number} x + * @param {number} y + * @param {number} z + * @return {qtek.math.Vector4} + */ + Vector4.set = function(out, x, y, z, w) { + vec4.set(out._array, x, y, z, w); + out._dirty = true; + }; + + /** + * @param {qtek.math.Vector4} out + * @param {qtek.math.Vector4} b + * @return {qtek.math.Vector4} + */ + Vector4.copy = function(out, b) { + vec4.copy(out._array, b._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Vector4} a + * @param {qtek.math.Vector4} b + * @return {number} + */ + Vector4.dist = function(a, b) { + return vec4.distance(a._array, b._array); + }; + + /** + * @method + * @param {qtek.math.Vector4} a + * @param {qtek.math.Vector4} b + * @return {number} + */ + Vector4.distance = Vector4.dist; + + /** + * @param {qtek.math.Vector4} out + * @param {qtek.math.Vector4} a + * @param {qtek.math.Vector4} b + * @return {qtek.math.Vector4} + */ + Vector4.div = function(out, a, b) { + vec4.divide(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + + /** + * @method + * @param {qtek.math.Vector4} out + * @param {qtek.math.Vector4} a + * @param {qtek.math.Vector4} b + * @return {qtek.math.Vector4} + */ + Vector4.divide = Vector4.div; + + /** + * @param {qtek.math.Vector4} a + * @param {qtek.math.Vector4} b + * @return {number} + */ + Vector4.dot = function(a, b) { + return vec4.dot(a._array, b._array); + }; + + /** + * @param {qtek.math.Vector4} a + * @return {number} + */ + Vector4.len = function(b) { + return vec4.length(b._array); + }; + + // Vector4.length = Vector4.len; + + /** + * @param {qtek.math.Vector4} out + * @param {qtek.math.Vector4} a + * @param {qtek.math.Vector4} b + * @param {number} t + * @return {qtek.math.Vector4} + */ + Vector4.lerp = function(out, a, b, t) { + vec4.lerp(out._array, a._array, b._array, t); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Vector4} out + * @param {qtek.math.Vector4} a + * @param {qtek.math.Vector4} b + * @return {qtek.math.Vector4} + */ + Vector4.min = function(out, a, b) { + vec4.min(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Vector4} out + * @param {qtek.math.Vector4} a + * @param {qtek.math.Vector4} b + * @return {qtek.math.Vector4} + */ + Vector4.max = function(out, a, b) { + vec4.max(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Vector4} out + * @param {qtek.math.Vector4} a + * @param {qtek.math.Vector4} b + * @return {qtek.math.Vector4} + */ + Vector4.mul = function(out, a, b) { + vec4.multiply(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + + /** + * @method + * @param {qtek.math.Vector4} out + * @param {qtek.math.Vector4} a + * @param {qtek.math.Vector4} b + * @return {qtek.math.Vector4} + */ + Vector4.multiply = Vector4.mul; + + /** + * @param {qtek.math.Vector4} out + * @param {qtek.math.Vector4} a + * @return {qtek.math.Vector4} + */ + Vector4.negate = function(out, a) { + vec4.negate(out._array, a._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Vector4} out + * @param {qtek.math.Vector4} a + * @return {qtek.math.Vector4} + */ + Vector4.normalize = function(out, a) { + vec4.normalize(out._array, a._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Vector4} out + * @param {number} scale + * @return {qtek.math.Vector4} + */ + Vector4.random = function(out, scale) { + vec4.random(out._array, scale); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Vector4} out + * @param {qtek.math.Vector4} a + * @param {number} scale + * @return {qtek.math.Vector4} + */ + Vector4.scale = function(out, a, scale) { + vec4.scale(out._array, a._array, scale); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Vector4} out + * @param {qtek.math.Vector4} a + * @param {qtek.math.Vector4} b + * @param {number} scale + * @return {qtek.math.Vector4} + */ + Vector4.scaleAndAdd = function(out, a, b, scale) { + vec4.scaleAndAdd(out._array, a._array, b._array, scale); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Vector4} a + * @param {qtek.math.Vector4} b + * @return {number} + */ + Vector4.sqrDist = function(a, b) { + return vec4.sqrDist(a._array, b._array); + }; + + /** + * @method + * @param {qtek.math.Vector4} a + * @param {qtek.math.Vector4} b + * @return {number} + */ + Vector4.squaredDistance = Vector4.sqrDist; + + /** + * @param {qtek.math.Vector4} a + * @return {number} + */ + Vector4.sqrLen = function(a) { + return vec4.sqrLen(a._array); + }; + /** + * @method + * @param {qtek.math.Vector4} a + * @return {number} + */ + Vector4.squaredLength = Vector4.sqrLen; + + /** + * @param {qtek.math.Vector4} out + * @param {qtek.math.Vector4} a + * @param {qtek.math.Vector4} b + * @return {qtek.math.Vector4} + */ + Vector4.sub = function(out, a, b) { + vec4.subtract(out._array, a._array, b._array); + out._dirty = true; + return out; + }; + /** + * @method + * @param {qtek.math.Vector4} out + * @param {qtek.math.Vector4} a + * @param {qtek.math.Vector4} b + * @return {qtek.math.Vector4} + */ + Vector4.subtract = Vector4.sub; + + /** + * @param {qtek.math.Vector4} out + * @param {qtek.math.Vector4} a + * @param {qtek.math.Matrix4} m + * @return {qtek.math.Vector4} + */ + Vector4.transformMat4 = function(out, a, m) { + vec4.transformMat4(out._array, a._array, m._array); + out._dirty = true; + return out; + }; + + /** + * @param {qtek.math.Vector4} out + * @param {qtek.math.Vector4} a + * @param {qtek.math.Quaternion} q + * @return {qtek.math.Vector4} + */ + Vector4.transformQuat = function(out, a, q) { + vec4.transformQuat(out._array, a._array, q._array); + out._dirty = true; + return out; + }; + + module.exports = Vector4; + + +/***/ }, +/* 135 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Base = __webpack_require__(3); + var Vector3 = __webpack_require__(23); + var Particle = __webpack_require__(136); + var Value = __webpack_require__(133); + var glMatrix = __webpack_require__(14); + var vec3 = glMatrix.vec3; + + /** + * @constructor qtek.particle.Emitter + * @extends qtek.core.Base + */ + var Emitter = Base.extend( + /** @lends qtek.particle.Emitter# */ + { + /** + * Maximum number of particles created by this emitter + * @type {number} + */ + max: 1000, + /** + * Number of particles created by this emitter each shot + * @type {number} + */ + amount: 20, + + // Init status for each particle + /** + * Particle life generator + * @type {?qtek.math.Value.} + */ + life: null, + /** + * Particle position generator + * @type {?qtek.math.Value.} + */ + position: null, + /** + * Particle rotation generator + * @type {?qtek.math.Value.} + */ + rotation: null, + /** + * Particle velocity generator + * @type {?qtek.math.Value.} + */ + velocity: null, + /** + * Particle angular velocity generator + * @type {?qtek.math.Value.} + */ + angularVelocity: null, + /** + * Particle sprite size generator + * @type {?qtek.math.Value.} + */ + spriteSize: null, + /** + * Particle weight generator + * @type {?qtek.math.Value.} + */ + weight: null, + + _particlePool: null + + }, function() { + + this._particlePool = []; + + // TODO Reduce heap memory + for (var i = 0; i < this.max; i++) { + var particle = new Particle(); + particle.emitter = this; + this._particlePool.push(particle); + + if (this.velocity) { + particle.velocity = new Vector3(); + } + if (this.angularVelocity) { + particle.angularVelocity = new Vector3(); + } + } + }, + /** @lends qtek.particle.Emitter.prototype */ + { + /** + * Emitter number of particles and push them to a given particle list. Emmit number is defined by amount property + * @param {Array.} out + */ + emit: function(out) { + var amount = Math.min(this._particlePool.length, this.amount); + + var particle; + for (var i = 0; i < amount; i++) { + particle = this._particlePool.pop(); + // Initialize particle status + if (this.position) { + this.position.get(particle.position); + } + if (this.rotation) { + this.rotation.get(particle.rotation); + } + if (this.velocity) { + this.velocity.get(particle.velocity); + } + if (this.angularVelocity) { + this.angularVelocity.get(particle.angularVelocity); + } + if (this.life) { + particle.life = this.life.get(); + } + if (this.spriteSize) { + particle.spriteSize = this.spriteSize.get(); + } + if (this.weight) { + particle.weight = this.weight.get(); + } + particle.age = 0; + + out.push(particle); + } + }, + /** + * Kill a dead particle and put it back in the pool + * @param {qtek.particle.Particle} particle + */ + kill: function(particle) { + this._particlePool.push(particle); + } + }); + + /** + * Create a constant 1d value generator. Alias for {@link qtek.math.Value.constant} + * @method qtek.particle.Emitter.constant + */ + Emitter.constant = Value.constant; + + /** + * Create a constant vector value(2d or 3d) generator. Alias for {@link qtek.math.Value.vector} + * @method qtek.particle.Emitter.vector + */ + Emitter.vector = Value.vector; + + /** + * Create a random 1d value generator. Alias for {@link qtek.math.Value.random1D} + * @method qtek.particle.Emitter.random1D + */ + Emitter.random1D = Value.random1D; + + /** + * Create a random 2d value generator. Alias for {@link qtek.math.Value.random2D} + * @method qtek.particle.Emitter.random2D + */ + Emitter.random2D = Value.random2D; + + /** + * Create a random 3d value generator. Alias for {@link qtek.math.Value.random3D} + * @method qtek.particle.Emitter.random3D + */ + Emitter.random3D = Value.random3D; + + module.exports = Emitter; + + +/***/ }, +/* 136 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Vector3 = __webpack_require__(23); + var glMatrix = __webpack_require__(14); + var vec3 = glMatrix.vec3; + + /** + * @constructor + * @alias qtek.particle.Particle + */ + var Particle = function() { + /** + * @type {qtek.math.Vector3} + */ + this.position = new Vector3(); + + /** + * Use euler angle to represent particle rotation + * @type {qtek.math.Vector3} + */ + this.rotation = new Vector3(); + + /** + * @type {?qtek.math.Vector3} + */ + this.velocity = null; + + /** + * @type {?qtek.math.Vector3} + */ + this.angularVelocity = null; + + /** + * @type {number} + */ + this.life = 1; + + /** + * @type {number} + */ + this.age = 0; + + /** + * @type {number} + */ + this.spriteSize = 1; + + /** + * @type {number} + */ + this.weight = 1; + + /** + * @type {qtek.particle.Emitter} + */ + this.emitter = null; + }; + + /** + * Update particle position + * @param {number} deltaTime + */ + Particle.prototype.update = function(deltaTime) { + if (this.velocity) { + vec3.scaleAndAdd(this.position._array, this.position._array, this.velocity._array, deltaTime); + } + if (this.angularVelocity) { + vec3.scaleAndAdd(this.rotation._array, this.rotation._array, this.angularVelocity._array, deltaTime); + } + }; + + module.exports = Particle; + + +/***/ }, +/* 137 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Base = __webpack_require__(3); + /** + * @constructor qtek.particle.Field + * @extends qtek.core.Base + */ + var Field = Base.extend({}, { + /** + * Apply a field to the particle and update the particle velocity + * @param {qtek.math.Vector3} velocity + * @param {qtek.math.Vector3} position + * @param {number} weight + * @param {number} deltaTime + * @memberOf qtek.particle.Field.prototype + */ + applyTo: function(velocity, position, weight, deltaTime) {} + }); + + module.exports = Field; + + +/***/ }, +/* 138 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Field = __webpack_require__(137); + var Vector3 = __webpack_require__(23); + var glMatrix = __webpack_require__(14); + var vec3 = glMatrix.vec3; + + /** + * @constructor qtek.particle.ForceField + * @extends qtek.particle.Field + */ + var ForceField = Field.extend(function() { + return { + force: new Vector3() + }; + }, { + applyTo: function(velocity, position, weight, deltaTime) { + if (weight > 0) { + vec3.scaleAndAdd(velocity._array, velocity._array, this.force._array, deltaTime / weight); + } + } + }); + + module.exports = ForceField; + + +/***/ }, +/* 139 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var Renderable = __webpack_require__(55); + + var StaticGeometry = __webpack_require__(49); + var Material = __webpack_require__(53); + var Shader = __webpack_require__(52); + + Shader['import'](__webpack_require__(140)); + + var particleShader = new Shader({ + vertex: Shader.source('qtek.particle.vertex'), + fragment: Shader.source('qtek.particle.fragment') + }); + particleShader.enableTexture('sprite'); + + /** + * @constructor qtek.particle.ParticleRenderable + * @extends qtek.Renderable + * + * @example + * var particleRenderable = new qtek.particle.ParticleRenderable({ + * spriteAnimationTileX: 4, + * spriteAnimationTileY: 4, + * spriteAnimationRepeat: 1 + * }); + * scene.add(particleRenderable); + * // Enable uv animation in the shader + * particleRenderable.material.shader.define('both', 'UV_ANIMATION'); + * var Emitter = qtek.particle.Emitter; + * var Vector3 = qtek.math.Vector3; + * var emitter = new Emitter({ + * max: 2000, + * amount: 100, + * life: Emitter.random1D(10, 20), + * position: Emitter.vector(new Vector3()), + * velocity: Emitter.random3D(new Vector3(-10, 0, -10), new Vector3(10, 0, 10)); + * }); + * particleRenderable.addEmitter(emitter); + * var gravityField = new qtek.particle.ForceField(); + * gravityField.force.y = -10; + * particleRenderable.addField(gravityField); + * ... + * animation.on('frame', function(frameTime) { + * particleRenderable.updateParticles(frameTime); + * renderer.render(scene, camera); + * }); + */ + var ParticleRenderable = Renderable.extend( + /** @lends qtek.particle.ParticleRenderable# */ + { + /** + * @type {boolean} + */ + loop: true, + /** + * @type {boolean} + */ + oneshot: false, + /** + * Duration of particle system in milliseconds + * @type {number} + */ + duration: 1, + + // UV Animation + /** + * @type {number} + */ + spriteAnimationTileX: 1, + /** + * @type {number} + */ + spriteAnimationTileY: 1, + /** + * @type {number} + */ + spriteAnimationRepeat: 0, + + mode: Renderable.POINTS, + + ignorePicking: true, + + _elapsedTime: 0, + + _emitting: true + + }, function(){ + + this.geometry = new StaticGeometry({ + dynamic: true + }); + + if (!this.material) { + this.material = new Material({ + shader: particleShader, + transparent: true, + depthMask: false + }); + } + + this._particles = []; + this._fields = []; + this._emitters = []; + }, + /** @lends qtek.particle.ParticleRenderable.prototype */ + { + + culling: false, + + frustumCulling: false, + + castShadow: false, + receiveShadow: false, + + /** + * Add emitter + * @param {qtek.particle.Emitter} emitter + */ + addEmitter: function(emitter) { + this._emitters.push(emitter); + }, + + /** + * Remove emitter + * @param {qtek.particle.Emitter} emitter + */ + removeEmitter: function(emitter) { + this._emitters.splice(this._emitters.indexOf(emitter), 1); + }, + + /** + * Add field + * @param {qtek.particle.Field} field + */ + addField: function(field) { + this._fields.push(field); + }, + + /** + * Remove field + * @param {qtek.particle.Field} field + */ + removeField: function(field) { + this._fields.splice(this._fields.indexOf(field), 1); + }, + + /** + * Reset the particle system. + */ + reset: function() { + // Put all the particles back + for (var i = 0; i < this._particles.length; i++) { + var p = this._particles[i]; + p.emitter.kill(p); + } + this._particles.length = 0; + this._elapsedTime = 0; + this._emitting = true; + }, + + /** + * @param {number} deltaTime + */ + updateParticles: function(deltaTime) { + + // MS => Seconds + deltaTime /= 1000; + this._elapsedTime += deltaTime; + + var particles = this._particles; + + if (this._emitting) { + for (var i = 0; i < this._emitters.length; i++) { + this._emitters[i].emit(particles); + } + if (this.oneshot) { + this._emitting = false; + } + } + + // Aging + var len = particles.length; + for (var i = 0; i < len;) { + var p = particles[i]; + p.age += deltaTime; + if (p.age >= p.life) { + p.emitter.kill(p); + particles[i] = particles[len-1]; + particles.pop(); + len--; + } else { + i++; + } + } + + for (var i = 0; i < len; i++) { + // Update + var p = particles[i]; + if (this._fields.length > 0) { + for (var j = 0; j < this._fields.length; j++) { + this._fields[j].applyTo(p.velocity, p.position, p.weight, deltaTime); + } + } + p.update(deltaTime); + } + + this._updateVertices(); + }, + + _updateVertices: function() { + var geometry = this.geometry; + // If has uv animation + var animTileX = this.spriteAnimationTileX; + var animTileY = this.spriteAnimationTileY; + var animRepeat = this.spriteAnimationRepeat; + var nUvAnimFrame = animTileY * animTileX * animRepeat; + var hasUvAnimation = nUvAnimFrame > 1; + var positions = geometry.attributes.position.value; + // Put particle status in normal + var normals = geometry.attributes.normal.value; + var uvs = geometry.attributes.texcoord0.value; + var uvs2 = geometry.attributes.texcoord1.value; + + var len = this._particles.length; + if (!positions || positions.length !== len * 3) { + // TODO Optimize + positions = geometry.attributes.position.value = new Float32Array(len * 3); + normals = geometry.attributes.normal.value = new Float32Array(len * 3); + if (hasUvAnimation) { + uvs = geometry.attributes.texcoord0.value = new Float32Array(len * 2); + uvs2 = geometry.attributes.texcoord1.value = new Float32Array(len * 2); + } + } + + var invAnimTileX = 1 / animTileX; + for (var i = 0; i < len; i++) { + var particle = this._particles[i]; + var offset = i * 3; + for (var j = 0; j < 3; j++) { + positions[offset + j] = particle.position._array[j]; + normals[offset] = particle.age / particle.life; + // normals[offset + 1] = particle.rotation; + normals[offset + 1] = 0; + normals[offset + 2] = particle.spriteSize; + } + var offset2 = i * 2; + if (hasUvAnimation) { + // TODO + var p = particle.age / particle.life; + var stage = Math.round(p * (nUvAnimFrame - 1)) * animRepeat; + var v = Math.floor(stage * invAnimTileX); + var u = stage - v * animTileX; + uvs[offset2] = u / animTileX; + uvs[offset2 + 1] = 1 - v / animTileY; + uvs2[offset2] = (u + 1) / animTileX; + uvs2[offset2 + 1] = 1 - (v + 1) / animTileY; + } + } + + geometry.dirty(); + }, + + /** + * @return {boolean} + */ + isFinished: function() { + return this._elapsedTime > this.duration && !this.loop; + }, + + /** + * @param {WebGLRenderingContext} _gl + */ + dispose: function(_gl) { + // Put all the particles back + for (var i = 0; i < this._particles.length; i++) { + var p = this._particles[i]; + p.emitter.kill(p); + } + this.geometry.dispose(_gl); + // TODO Dispose texture, shader ? + }, + + /** + * @return {qtek.particle.ParticleRenderable} + */ + clone: function() { + var particleRenderable = new ParticleRenderable({ + material: this.material + }); + particleRenderable.loop = this.loop; + particleRenderable.duration = this.duration; + particleRenderable.oneshot = this.oneshot; + particleRenderable.spriteAnimationRepeat = this.spriteAnimationRepeat; + particleRenderable.spriteAnimationTileY = this.spriteAnimationTileY; + particleRenderable.spriteAnimationTileX = this.spriteAnimationTileX; + + particleRenderable.position.copy(this.position); + particleRenderable.rotation.copy(this.rotation); + particleRenderable.scale.copy(this.scale); + + for (var i = 0; i < this._children.length; i++) { + particleRenderable.add(this._children[i].clone()); + } + return particleRenderable; + } + }); + + module.exports = ParticleRenderable; + + +/***/ }, +/* 140 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.particle.vertex\n\nuniform mat4 worldView : WORLDVIEW;\nuniform mat4 projection : PROJECTION;\n\nattribute vec3 position : POSITION;\nattribute vec3 normal : NORMAL;\n\n#ifdef UV_ANIMATION\nattribute vec2 texcoord0 : TEXCOORD_0;\nattribute vec2 texcoord1 : TEXCOORD_1;\n\nvarying vec2 v_Uv0;\nvarying vec2 v_Uv1;\n#endif\n\nvarying float v_Age;\n\nvoid main() {\n v_Age = normal.x;\n float rotation = normal.y;\n\n vec4 worldViewPosition = worldView * vec4(position, 1.0);\n gl_Position = projection * worldViewPosition;\n float w = gl_Position.w;\n gl_PointSize = normal.z * projection[0].x / w;\n\n #ifdef UV_ANIMATION\n v_Uv0 = texcoord0;\n v_Uv1 = texcoord1;\n #endif\n}\n\n@end\n\n@export qtek.particle.fragment\n\nuniform sampler2D sprite;\nuniform sampler2D gradient;\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\n\nvarying float v_Age;\n\n#ifdef UV_ANIMATION\nvarying vec2 v_Uv0;\nvarying vec2 v_Uv1;\n#endif\n\nvoid main() {\n vec4 color = vec4(color, alpha);\n #ifdef SPRITE_ENABLED\n #ifdef UV_ANIMATION\n color *= texture2D(sprite, mix(v_Uv0, v_Uv1, gl_PointCoord));\n #else\n color *= texture2D(sprite, gl_PointCoord);\n #endif\n #endif\n #ifdef GRADIENT_ENABLED\n color *= texture2D(gradient, vec2(v_Age, 0.5));\n #endif\n gl_FragColor = color;\n}\n\n@end"; + + +/***/ }, +/* 141 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Base = __webpack_require__(3); + var FrameBuffer = __webpack_require__(44); + var Texture2D = __webpack_require__(39); + var Shader = __webpack_require__(52); + var Material = __webpack_require__(53); + + Shader.import(__webpack_require__(142)); + + /** + * Pixel picking is gpu based picking, which is fast and accurate. + * But not like ray picking, it can't get the intersection point and triangle. + * @constructor qtek.picking.PixelPicking + * @extends qtek.core.Base + */ + var PixelPicking = Base.extend(function() { + return /** @lends qtek.picking.PixelPicking# */ { + /** + * Target renderer + * @type {qtek.Renderer} + */ + renderer: null, + /** + * Downsample ratio of hidden frame buffer + * @type {number} + */ + downSampleRatio: 1, + + width: 100, + height: 100, + + lookupOffset: 1, + + _frameBuffer: null, + _texture: null, + _shader: null, + + _idMaterials: [], + _lookupTable: [], + + _meshMaterials: [], + + _idOffset: 0 + }; + }, function() { + if (this.renderer) { + this.width = this.renderer.getWidth(); + this.height = this.renderer.getHeight(); + } + this._init(); + }, /** @lends qtek.picking.PixelPicking.prototype */ { + _init: function() { + this._texture = new Texture2D({ + width: this.width * this.downSampleRatio, + height: this.height * this.downSampleRatio + }); + this._frameBuffer = new FrameBuffer(); + + this._shader = new Shader({ + vertex: Shader.source('qtek.picking.color.vertex'), + fragment: Shader.source('qtek.picking.color.fragment') + }); + }, + /** + * Set picking presision + * @param {number} ratio + */ + setPrecision: function(ratio) { + this._texture.width = this.width * ratio; + this._texture.height = this.height * ratio; + this.downSampleRatio = ratio; + }, + resize: function(width, height) { + this._texture.width = width * this.downSampleRatio; + this._texture.height = height * this.downSampleRatio; + this.width = width; + this.height = height; + this._texture.dirty(); + }, + /** + * Update the picking framebuffer + * @param {number} ratio + */ + update: function(scene, camera) { + var renderer = this.renderer; + if (renderer.getWidth() !== this.width || renderer.getHeight() !== this.height) { + this.resize(renderer.width, renderer.height); + } + + this._frameBuffer.attach(this._texture); + this._frameBuffer.bind(renderer); + this._idOffset = this.lookupOffset; + this._setMaterial(scene); + renderer.render(scene, camera); + this._restoreMaterial(); + this._frameBuffer.unbind(renderer); + }, + + _setMaterial: function(root) { + for (var i =0; i < root._children.length; i++) { + var child = root._children[i]; + if (child.geometry && child.material && child.material.shader) { + var id = this._idOffset++; + var idx = id - this.lookupOffset; + var material = this._idMaterials[idx]; + if (!material) { + material = new Material({ + shader: this._shader + }); + var color = packID(id); + color[0] /= 255; + color[1] /= 255; + color[2] /= 255; + color[3] = 1.0; + material.set('color', color); + this._idMaterials[idx] = material; + } + this._meshMaterials[idx] = child.material; + this._lookupTable[idx] = child; + child.material = material; + } + if (child._children.length) { + this._setMaterial(child); + } + } + }, + + /** + * Pick the object + * @param {number} x Mouse position x + * @param {number} y Mouse position y + * @return {qtek.Node} + */ + pick: function(x, y) { + var renderer = this.renderer; + + var ratio = this.downSampleRatio; + x = Math.ceil(ratio * x); + y = Math.ceil(ratio * (this.height - y)); + + this._frameBuffer.bind(renderer); + var pixel = new Uint8Array(4); + var _gl = renderer.gl; + // TODO out of bounds ? + // preserveDrawingBuffer ? + _gl.readPixels(x, y, 1, 1, _gl.RGBA, _gl.UNSIGNED_BYTE, pixel); + this._frameBuffer.unbind(renderer); + // Skip interpolated pixel because of anti alias + if (pixel[3] === 255) { + var id = unpackID(pixel[0], pixel[1], pixel[2]); + if (id) { + var el = this._lookupTable[id - this.lookupOffset]; + return el; + } + } + }, + + _restoreMaterial: function() { + for (var i = 0; i < this._lookupTable.length; i++) { + this._lookupTable[i].material = this._meshMaterials[i]; + } + }, + + dispose: function(_gl) { + this._frameBuffer.dispose(_gl); + this._shader.dispose(_gl); + } + }); + + function packID(id){ + var r = id >> 16; + var g = (id - (r << 8)) >> 8; + var b = id - (r << 16) - (g<<8); + return [r, g, b]; + } + + function unpackID(r, g, b){ + return (r << 16) + (g<<8) + b; + } + + module.exports = PixelPicking; + + +/***/ }, +/* 142 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.picking.color.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_COUNT] : SKIN_MATRIX;\n#endif\n\nvoid main(){\n\n vec3 skinnedPosition = position;\n\n #ifdef SKINNING\n\n @import qtek.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n}\n\n@end\n\n@end\n@export qtek.picking.color.fragment\n\nuniform vec4 color : [1.0, 1.0, 1.0, 1.0];\n\nvoid main(){\n gl_FragColor = color;\n}\n\n@end"; + + +/***/ }, +/* 143 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Base = __webpack_require__(3); + var Ray = __webpack_require__(29); + var Vector2 = __webpack_require__(13); + var Vector3 = __webpack_require__(23); + var Matrix4 = __webpack_require__(25); + var Renderable = __webpack_require__(55); + var glenum = __webpack_require__(34); + + /** + * @constructor qtek.picking.RayPicking + * @extends qtek.core.Base + */ + var RayPicking = Base.extend( + /** @lends qtek.picking.RayPicking# */ + { + /** + * Target scene + * @type {qtek.Scene} + */ + scene: null, + /** + * Target camera + * @type {qtek.Camera} + */ + camera: null, + /** + * Target renderer + * @type {qtek.Renderer} + */ + renderer: null + }, function() { + this._ray = new Ray(); + this._ndc = new Vector2(); + }, + /** @lends qtek.picking.RayPicking.prototype */ + { + + /** + * Pick the nearest intersection object in the scene + * @param {number} x Mouse position x + * @param {number} y Mouse position y + * @return {qtek.picking.RayPicking~Intersection} + */ + pick: function (x, y) { + var out = this.pickAll(x, y); + return out[0] || null; + }, + + /** + * Pick all intersection objects, wich will be sorted from near to far + * @param {number} x Mouse position x + * @param {number} y Mouse position y + * @param {Array} [output] + * @return {Array.} + */ + pickAll: function (x, y, output) { + this.renderer.screenToNdc(x, y, this._ndc); + this.camera.castRay(this._ndc, this._ray); + + output = output || []; + + this._intersectNode(this.scene, output); + + output.sort(this._intersectionCompareFunc); + + return output; + }, + + _intersectNode: function (node, out) { + if ((node instanceof Renderable) && node.isRenderable()) { + if (!node.ignorePicking && node.geometry.isUseIndices()) { + this._intersectRenderable(node, out); + } + } + for (var i = 0; i < node._children.length; i++) { + this._intersectNode(node._children[i], out); + } + }, + + _intersectRenderable: (function () { + + var v1 = new Vector3(); + var v2 = new Vector3(); + var v3 = new Vector3(); + var ray = new Ray(); + var worldInverse = new Matrix4(); + + return function (renderable, out) { + + ray.copy(this._ray); + Matrix4.invert(worldInverse, renderable.worldTransform); + + ray.applyTransform(worldInverse); + + var geometry = renderable.geometry; + if (geometry.boundingBox) { + if (!ray.intersectBoundingBox(geometry.boundingBox)) { + return; + } + } + // Use user defined ray picking algorithm + if (geometry.pickByRay) { + var intersection = geometry.pickByRay(ray); + if (intersection) { + out.push(intersection); + } + return; + } + + var cullBack = (renderable.cullFace === glenum.BACK && renderable.frontFace === glenum.CCW) + || (renderable.cullFace === glenum.FRONT && renderable.frontFace === glenum.CW); + + var point; + var indices = geometry.indices; + var positionsAttr = geometry.attributes.position; + for (var i = 0; i < indices.length; i += 3) { + var i1 = indices[i]; + var i2 = indices[i + 1]; + var i3 = indices[i + 2]; + positionsAttr.get(i1, v1._array); + positionsAttr.get(i2, v2._array); + positionsAttr.get(i3, v3._array); + + if (cullBack) { + point = ray.intersectTriangle(v1, v2, v3, renderable.culling); + } + else { + point = ray.intersectTriangle(v1, v3, v2, renderable.culling); + } + if (point) { + var pointW = new Vector3(); + Vector3.transformMat4(pointW, point, renderable.worldTransform); + out.push(new RayPicking.Intersection( + point, pointW, renderable, [i1, i2, i3], i / 3, + Vector3.dist(pointW, this._ray.origin) + )); + } + } + }; + })(), + + _intersectionCompareFunc: function (a, b) { + return a.distance - b.distance; + } + }); + + /** + * @constructor qtek.picking.RayPicking~Intersection + * @param {qtek.math.Vector3} point + * @param {qtek.math.Vector3} pointWorld + * @param {qtek.Node} target + * @param {Array.} triangle + * @param {number} triangleIndex + * @param {number} distance + */ + RayPicking.Intersection = function (point, pointWorld, target, triangle, triangleIndex, distance) { + /** + * Intersection point in local transform coordinates + * @type {qtek.math.Vector3} + */ + this.point = point; + /** + * Intersection point in world transform coordinates + * @type {qtek.math.Vector3} + */ + this.pointWorld = pointWorld; + /** + * Intersection scene node + * @type {qtek.Node} + */ + this.target = target; + /** + * Intersection triangle, which is an array of vertex index + * @type {Array.} + */ + this.triangle = triangle; + /** + * Index of intersection triangle. + */ + this.triangleIndex = triangleIndex; + /** + * Distance from intersection point to ray origin + * @type {number} + */ + this.distance = distance; + }; + + module.exports = RayPicking; + + +/***/ }, +/* 144 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Base = __webpack_require__(3); + var Vector3 = __webpack_require__(23); + + /** + * @constructor qtek.plugin.FirstPersonControl + * @example + * var control = new qtek.plugin.FirstPersonControl({ + * target: camera, + * domElement: renderer.canvas + * }); + * ... + * animation.on('frame', function(frameTime) { + * control.update(frameTime); + * renderer.render(scene, camera); + * }); + */ + var FirstPersonControl = Base.extend(function() { + return /** @lends qtek.plugin.FirstPersonControl# */ { + /** + * Scene node to control, mostly it is a camera + * @type {qtek.Node} + */ + target: null, + + /** + * Target dom to bind with mouse events + * @type {HTMLElement} + */ + domElement: null, + + /** + * Mouse move sensitivity + * @type {number} + */ + sensitivity: 1, + + /** + * Target move speed + * @type {number} + */ + speed: 0.4, + + /** + * Up axis + * @type {qtek.math.Vector3} + */ + up: new Vector3(0, 1, 0), + + /** + * If lock vertical movement + * @type {boolean} + */ + verticalMoveLock: false, + + /** + * @type {qtek.animation.Animation} + */ + animation: null, + + _moveForward: false, + _moveBackward: false, + _moveLeft: false, + _moveRight: false, + + _offsetPitch: 0, + _offsetRoll: 0 + }; + }, function() { + this._lockChange = this._lockChange.bind(this); + this._keyDown = this._keyDown.bind(this); + this._keyUp = this._keyUp.bind(this); + this._mouseMove = this._mouseMove.bind(this); + + if (this.domElement) { + this.enable(); + } + }, + /** @lends qtek.plugin.FirstPersonControl.prototype */ + { + /** + * Enable control + */ + enable: function() { + // Use pointer lock + // http://www.html5rocks.com/en/tutorials/pointerlock/intro/ + var el = this.domElement; + + //Must request pointer lock after click event, can't not do it directly + //Why ? ? + el.addEventListener('click', this._requestPointerLock); + + document.addEventListener('pointerlockchange', this._lockChange); + document.addEventListener('mozpointerlockchange', this._lockChange); + document.addEventListener('webkitpointerlockchange', this._lockChange); + + document.addEventListener('keydown', this._keyDown); + document.addEventListener('keyup', this._keyUp); + + if (this.animation) { + this.animation.on('frame', this._detectMovementChange, this); + } + }, + + /** + * Disable control + */ + disable: function() { + + this.target.off('beforeupdate', this._beforeUpdateCamera); + + var el = this.domElement; + + el.exitPointerLock = el.exitPointerLock + || el.mozExitPointerLock + || el.webkitExitPointerLock; + + if (el.exitPointerLock) { + el.exitPointerLock(); + } + + this.domElement.removeEventListener('click', this._requestPointerLock); + + document.removeEventListener('pointerlockchange', this._lockChange); + document.removeEventListener('mozpointerlockchange', this._lockChange); + document.removeEventListener('webkitpointerlockchange', this._lockChange); + + document.removeEventListener('keydown', this._keyDown); + document.removeEventListener('keyup', this._keyUp); + + if (this.animation) { + this.animation.off('frame', this._detectMovementChange); + } + }, + + _requestPointerLock: function() { + var el = this; + el.requestPointerLock = el.requestPointerLock + || el.mozRequestPointerLock + || el.webkitRequestPointerLock; + + el.requestPointerLock(); + }, + + /** + * Control update. Should be invoked every frame + * @param {number} frameTime Frame time + */ + update: function(frameTime) { + var target = this.target; + + var position = this.target.position; + var xAxis = target.localTransform.x.normalize(); + var zAxis = target.localTransform.z.normalize(); + + if (this.verticalMoveLock) { + zAxis.y = 0; + zAxis.normalize(); + } + + var speed = this.speed * frameTime / 20; + + if (this._moveForward) { + // Opposite direction of z + position.scaleAndAdd(zAxis, -speed); + } + if (this._moveBackward) { + position.scaleAndAdd(zAxis, speed); + } + if (this._moveLeft) { + position.scaleAndAdd(xAxis, -speed / 2); + } + if (this._moveRight) { + position.scaleAndAdd(xAxis, speed / 2); + } + + target.rotateAround(target.position, this.up, -this._offsetPitch * frameTime * Math.PI / 360); + var xAxis = target.localTransform.x; + target.rotateAround(target.position, xAxis, -this._offsetRoll * frameTime * Math.PI / 360); + + this._offsetRoll = this._offsetPitch = 0; + }, + + _lockChange: function() { + if ( + document.pointerLockElement === this.domElement + || document.mozPointerLockElement === this.domElement + || document.webkitPointerLockElement === this.domElement + ) { + document.addEventListener('mousemove', this._mouseMove, false); + } else { + document.removeEventListener('mousemove', this._mouseMove); + } + }, + + _mouseMove: function(e) { + var dx = e.movementX || e.mozMovementX || e.webkitMovementX || 0; + var dy = e.movementY || e.mozMovementY || e.webkitMovementY || 0; + + this._offsetPitch += dx * this.sensitivity / 200; + this._offsetRoll += dy * this.sensitivity / 200; + + // Trigger change event to remind renderer do render + this.trigger('change'); + }, + + _detectMovementChange: function () { + if (this._moveForward || this._moveBackward || this._moveLeft || this._moveRight) { + this.trigger('change'); + } + }, + + _keyDown: function(e) { + switch(e.keyCode) { + case 87: //w + case 37: //up arrow + this._moveForward = true; + break; + case 83: //s + case 40: //down arrow + this._moveBackward = true; + break; + case 65: //a + case 37: //left arrow + this._moveLeft = true; + break; + case 68: //d + case 39: //right arrow + this._moveRight = true; + break; + } + // Trigger change event to remind renderer do render + this.trigger('change'); + }, + + _keyUp: function(e) { + switch(e.keyCode) { + case 87: //w + case 37: //up arrow + this._moveForward = false; + break; + case 83: //s + case 40: //down arrow + this._moveBackward = false; + break; + case 65: //a + case 37: //left arrow + this._moveLeft = false; + break; + case 68: //d + case 39: //right arrow + this._moveRight = false; + break; + } + } + }); + + module.exports = FirstPersonControl; + + +/***/ }, +/* 145 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Mesh = __webpack_require__(54); + var StaticGeometry = __webpack_require__(49); + var Plane = __webpack_require__(28); + var Vector3 = __webpack_require__(23); + var Matrix4 = __webpack_require__(25); + var Ray = __webpack_require__(29); + + var glMatrix = __webpack_require__(14); + var mat4 = glMatrix.mat4; + var vec3 = glMatrix.vec3; + var vec4 = glMatrix.vec4; + + var uvs = [[0, 0], [0, 1], [1, 1], [1, 0]]; + var tris = [0, 1, 2, 2, 3, 0]; + + var InfinitePlane = Mesh.extend({ + + camera: null, + + plane: null, + + maxGrid: 0, + + // TODO + frustumCulling: false + + }, function () { + var geometry = this.geometry = new StaticGeometry({ + dynamic: true + }); + geometry.attributes.position.init(6); + geometry.attributes.normal.init(6); + geometry.attributes.texcoord0.init(6); + geometry.indices = new Uint16Array(6); + + this.plane = new Plane(); + }, { + + updateGeometry: function () { + + var coords = this._unProjectGrid(); + if (!coords) { + return; + } + var positionAttr = this.geometry.attributes.position; + var normalAttr = this.geometry.attributes.normal; + var texcoords = this.geometry.attributes.texcoord0; + var indices = this.geometry.indices; + + for (var i = 0; i < 6; i++) { + var idx = tris[i]; + positionAttr.set(i, coords[idx]._array); + normalAttr.set(i, this.plane.normal._array); + texcoords.set(i, uvs[idx]); + indices[i] = i; + } + this.geometry.dirty(); + }, + + // http://fileadmin.cs.lth.se/graphics/theses/projects/projgrid/ + _unProjectGrid: (function () { + + var planeViewSpace = new Plane(); + var lines = [ + 0, 1, 0, 2, 1, 3, 2, 3, + 4, 5, 4, 6, 5, 7, 6, 7, + 0, 4, 1, 5, 2, 6, 3, 7 + ]; + + var start = new Vector3(); + var end = new Vector3(); + + var points = []; + + // 1----2 + // | | + // 0----3 + var coords = []; + for (var i = 0; i < 4; i++) { + coords[i] = new Vector3(0, 0); + } + + var ray = new Ray(); + + return function () { + planeViewSpace.copy(this.plane); + planeViewSpace.applyTransform(this.camera.viewMatrix); + + var frustumVertices = this.camera.frustum.vertices; + + var nPoints = 0; + // Intersect with lines of frustum + for (var i = 0; i < 12; i++) { + start._array = frustumVertices[lines[i * 2]]; + end._array = frustumVertices[lines[i * 2 + 1]]; + + var point = planeViewSpace.intersectLine(start, end, points[nPoints]); + if (point) { + if (!points[nPoints]) { + points[nPoints] = point; + } + nPoints++; + } + } + if (nPoints === 0) { + return; + } + for (var i = 0; i < nPoints; i++) { + points[i].applyProjection(this.camera.projectionMatrix); + } + var minX = points[0]._array[0]; + var minY = points[0]._array[1]; + var maxX = points[0]._array[0]; + var maxY = points[0]._array[1]; + for (var i = 1; i < nPoints; i++) { + maxX = Math.max(maxX, points[i]._array[0]); + maxY = Math.max(maxY, points[i]._array[1]); + minX = Math.min(minX, points[i]._array[0]); + minY = Math.min(minY, points[i]._array[1]); + } + if (minX == maxX || minY == maxY) { + return; + } + coords[0]._array[0] = minX; + coords[0]._array[1] = minY; + coords[1]._array[0] = minX; + coords[1]._array[1] = maxY; + coords[2]._array[0] = maxX; + coords[2]._array[1] = maxY; + coords[3]._array[0] = maxX; + coords[3]._array[1] = minY; + + for (var i = 0; i < 4; i++) { + this.camera.castRay(coords[i], ray); + ray.intersectPlane(this.plane, coords[i]); + } + + return coords; + }; + })() + }); + + module.exports = InfinitePlane; + + +/***/ }, +/* 146 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Base = __webpack_require__(3); + var Vector3 = __webpack_require__(23); + var Matrix4 = __webpack_require__(25); + + function addEvent(dom, eveType, handler) { + dom.addEventListener(eveType, handler); + } + function removeEvent(dom, eveType, handler) { + dom.removeEventListener(eveType, handler); + } + + /** + * @constructor qtek.plugin.OrbitControl + * + * @example + * + * var control = new qtek.plugin.OrbitControl({ + * target: camera, + * domElement: renderer.canvas + * }); + * // Rotate around car + * control.origin.copy(car.position); + * ... + * animation.on('frame', function(frameTime) { + * control.update(frameTime); + * renderer.render(scene, camera); + * }); + */ + var OrbitControl = Base.extend(function() { + return /** @lends qtek.plugin.OrbitControl# */ { + /** + * Scene node to control, mostly it is a camera + * @type {qtek.Node} + */ + target: null, + + /** + * Target dom to bind with mouse events + * @type {HTMLElement} + */ + domElement: null, + + /** + * Mouse move sensitivity + * @type {number} + */ + sensitivity: 1, + + /** + * Origin to rotate around + * @type {qtek.math.Vector3} + */ + origin: new Vector3(), + + /** + * Up axis + * @type {qtek.math.Vector3} + */ + up: new Vector3(0, 1, 0), + + /** + * Minimum distance from origin to target when zooming in + * @type {number} + */ + minDistance: 0, + /** + * Maximum distance from origin to target when zooming out + * @type {number} + */ + maxDistance: Infinity, + + /** + * Minimum polar angle when rotate up, it is 0 when the direction origin point to target is same with up axis + * @type {number} + */ + minPolarAngle: 0, // [0, Math.PI/2] + + /** + * Maximum polar angle when rotate down. It is PI when the direction origin point to target is opposite to up axis + * @type {number} + */ + maxPolarAngle: Math.PI, // [Math.PI/2, Math.PI] + + // Rotate around origin + _offsetPitch: 0, + _offsetRoll: 0, + + // Pan the origin + _panX: 0, + _panY: 0, + + // Offset of mouse move + _offsetX: 0, + _offsetY: 0, + + // Zoom with mouse wheel + _forward: 0, + + _op: -1 //0: ROTATE, 1: PAN + }; + }, function() { + this._mouseDown = this._mouseDown.bind(this); + this._mouseUp = this._mouseUp.bind(this); + this._mouseMove = this._mouseMove.bind(this); + this._mouseOut = this._mouseOut.bind(this); + this._mouseWheel = this._mouseWheel.bind(this); + + if (this.domElement) { + this.enable(); + } + }, + /** @lends qtek.plugin.OrbitControl.prototype */ + { + /** + * Enable control + */ + enable: function() { + var domElement = this.domElement; + addEvent(domElement, 'mousedown', this._mouseDown); + addEvent(domElement, 'touchstart', this._mouseDown); + + addEvent(domElement, 'mousewheel', this._mouseWheel); + addEvent(domElement, 'DOMMouseScroll', this._mouseWheel); + }, + + /** + * Disable control + */ + disable: function() { + var domElement = this.domElement; + removeEvent(domElement, 'mousedown', this._mouseDown); + removeEvent(domElement, 'mousemove', this._mouseMove); + removeEvent(domElement, 'mouseup', this._mouseUp); + + removeEvent(domElement, 'touchstart', this._mouseDown); + removeEvent(domElement, 'touchmove', this._mouseMove); + removeEvent(domElement, 'touchend', this._mouseUp); + + removeEvent(domElement, 'mousewheel', this._mouseWheel); + removeEvent(domElement, 'DOMMouseScroll', this._mouseWheel); + + this._mouseUp(); + }, + + _mouseWheel: function(e) { + e.preventDefault(); + var delta = e.wheelDelta // Webkit + || -e.detail; // Firefox + + this._forward += delta * this.sensitivity; + + // Trigger change event to remind renderer do render + this.trigger('change'); + }, + + _mouseDown: function(e) { + var domElement = this.domElement; + addEvent(domElement, 'mousemove', this._mouseMove); + addEvent(domElement, 'mouseup', this._mouseUp); + addEvent(domElement, 'mouseout', this._mouseOut); + + addEvent(domElement, 'touchend', this._mouseUp); + addEvent(domElement, 'touchmove', this._mouseMove); + + var x = e.pageX; + var y = e.pageY; + // Touch + if (e.targetTouches) { + var touch = e.targetTouches[0]; + x = touch.clientX; + y = touch.clientY; + + this._op = 0; + } + + this._offsetX = x; + this._offsetY = y; + + // Rotate + if (e.button === 0) { + this._op = 0; + } else if (e.button === 1) { + this._op = 1; + } + }, + + _mouseMove: function(e) { + var x = e.pageX; + var y = e.pageY; + // Touch + if (e.targetTouches) { + var touch = e.targetTouches[0]; + x = touch.clientX; + y = touch.clientY; + // PENDING + e.preventDefault(); + } + + var dx = x - this._offsetX; + var dy = y - this._offsetY; + + if (this._op === 0) { + this._offsetPitch += dx * this.sensitivity / 100; + this._offsetRoll += dy * this.sensitivity / 100; + } + else if (this._op === 1) { + var len = this.origin.distance(this.target.position); + var divider; + if (this.target.fov) { + divider = Math.sin(this.target.fov * Math.PI / 360) / 200; + } else { + divider = 1 / 200; + } + this._panX += dx * this.sensitivity * len * divider; + this._panY += dy * this.sensitivity * len * divider; + } + + this._offsetX = x; + this._offsetY = y; + + // Trigger change event to remind renderer do render + this.trigger('change'); + }, + + _mouseUp: function() { + var domElement = this.domElement; + removeEvent(domElement, 'mousemove', this._mouseMove); + removeEvent(domElement, 'mouseup', this._mouseUp); + removeEvent(domElement, 'mouseout', this._mouseOut); + + removeEvent(domElement, 'touchend', this._mouseUp); + removeEvent(domElement, 'touchmove', this._mouseMove); + + this._op = -1; + }, + + _mouseOut: function() { + this._mouseUp(); + }, + + /** + * Control update. Should be invoked every frame + * @param {number} frameTime Frame time + */ + update: function(frameTime) { + var target = this.target; + var zAxis = target.localTransform.z.normalize(); + var yAxis = target.localTransform.y.normalize(); + + if (this._op === 0 && (this._offsetPitch !== 0 || this._offsetRoll !== 0)) { + // Rotate + target.rotateAround(this.origin, this.up, -this._offsetPitch); + var xAxis = target.localTransform.x; + target.rotateAround(this.origin, xAxis, -this._offsetRoll); + + var zAxis = target.worldTransform.z.normalize(); + var phi = Math.acos(this.up.dot(zAxis)); + // Rotate back a bit + if (this._offsetRoll > 0 && phi <= this.minPolarAngle) { + target.rotateAround(this.origin, xAxis, -phi + this.minPolarAngle); + } + else if (this._offsetRoll < 0 && phi >= this.maxPolarAngle) { + target.rotateAround(this.origin, xAxis, -phi + this.maxPolarAngle); + } + this._offsetRoll = this._offsetPitch = 0; + } + else if (this._op === 1) { + // Pan + var xAxis = target.localTransform.x.normalize().scale(-this._panX); + var yAxis = target.localTransform.y.normalize().scale(this._panY); + target.position.add(xAxis).add(yAxis); + this.origin.add(xAxis).add(yAxis); + this._panX = this._panY = 0; + } + if (this._forward !== 0) { + // Zoom + var distance = target.position.distance(this.origin); + var nextDistance = distance + this._forward * distance / 5000; + if (nextDistance < this.maxDistance && nextDistance > this.minDistance) { + target.position.scaleAndAdd(zAxis, this._forward * distance / 5000); + } + this._forward = 0; + } + + } + }); + + module.exports = OrbitControl; + + +/***/ }, +/* 147 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Base = __webpack_require__(3); + var Vector4 = __webpack_require__(134); + + var ReflectionPass = Base.extend(function() { + console.warn('TODO'); + }, { + render : function(renderer, scene, camera) { + + } + }); + + module.exports = ReflectionPass; + + +/***/ }, +/* 148 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Base = __webpack_require__(3); + var glenum = __webpack_require__(34); + var Vector3 = __webpack_require__(23); + var BoundingBox = __webpack_require__(26); + var Frustum = __webpack_require__(27); + var Matrix4 = __webpack_require__(25); + var Renderer = __webpack_require__(63); + var Shader = __webpack_require__(52); + var Light = __webpack_require__(86); + var Mesh = __webpack_require__(54); + var SpotLight = __webpack_require__(105); + var DirectionalLight = __webpack_require__(102); + var PointLight = __webpack_require__(103); + var shaderLibrary = __webpack_require__(64); + var Material = __webpack_require__(53); + var FrameBuffer = __webpack_require__(44); + var Texture = __webpack_require__(40); + var Texture2D = __webpack_require__(39); + var TextureCube = __webpack_require__(45); + var PerspectiveCamera = __webpack_require__(31); + var OrthoCamera = __webpack_require__(30); + + var Pass = __webpack_require__(47); + var TexturePool = __webpack_require__(38); + + var glMatrix = __webpack_require__(14); + var mat4 = glMatrix.mat4; + var vec3 = glMatrix.vec3; + + var targets = ['px', 'nx', 'py', 'ny', 'pz', 'nz']; + + Shader['import'](__webpack_require__(149)); + + /** + * Pass rendering shadow map. + * + * @constructor qtek.prePass.ShadowMap + * @extends qtek.core.Base + * @example + * var shadowMapPass = new qtek.prePass.ShadowMap({ + * softShadow: qtek.prePass.ShadowMap.VSM + * }); + * ... + * animation.on('frame', function (frameTime) { + * shadowMapPass.render(renderer, scene, camera); + * renderer.render(scene, camera); + * }); + */ + var ShadowMapPass = Base.extend(function () { + return /** @lends qtek.prePass.ShadowMap# */ { + /** + * Soft shadow technique. + * Can be {@link qtek.prePass.ShadowMap.PCF} or {@link qtek.prePass.ShadowMap.VSM} + * @type {number} + */ + softShadow: ShadowMapPass.PCF, + + /** + * Soft shadow blur size + * @type {number} + */ + shadowBlur: 1.0, + + lightFrustumBias: 2, + + _frameBuffer: new FrameBuffer(), + + _textures: {}, + _shadowMapNumber: { + 'POINT_LIGHT': 0, + 'DIRECTIONAL_LIGHT': 0, + 'SPOT_LIGHT': 0 + }, + + _meshMaterials: {}, + _depthMaterials: {}, + _depthShaders: {}, + _distanceMaterials: {}, + + _opaqueCasters: [], + _receivers: [], + _lightsCastShadow: [], + + _lightCameras: {}, + + _texturePool: new TexturePool() + }; + }, function () { + // Gaussian filter pass for VSM + this._gaussianPassH = new Pass({ + fragment: Shader.source('qtek.compositor.gaussian_blur') + }); + this._gaussianPassV = new Pass({ + fragment: Shader.source('qtek.compositor.gaussian_blur') + }); + this._gaussianPassH.setUniform('blurSize', this.shadowBlur); + this._gaussianPassH.setUniform('blurDir', 0.0); + this._gaussianPassV.setUniform('blurSize', this.shadowBlur); + this._gaussianPassV.setUniform('blurDir', 1.0); + + this._outputDepthPass = new Pass({ + fragment: Shader.source('qtek.sm.debug_depth') + }); + }, { + /** + * Render scene to shadow textures + * @param {qtek.Renderer} renderer + * @param {qtek.Scene} scene + * @param {qtek.Camera} sceneCamera + * @param {boolean} [notUpdateScene=false] + * @memberOf qtek.prePass.ShadowMap.prototype + */ + render: function (renderer, scene, sceneCamera, notUpdateScene) { + this.trigger('beforerender', this, renderer, scene, sceneCamera); + this._renderShadowPass(renderer, scene, sceneCamera, notUpdateScene); + this.trigger('afterrender', this, renderer, scene, sceneCamera); + }, + + /** + * Debug rendering of shadow textures + * @param {qtek.Renderer} renderer + * @param {number} size + * @memberOf qtek.prePass.ShadowMap.prototype + */ + renderDebug: function (renderer, size) { + renderer.saveClear(); + var viewport = renderer.viewport; + var x = 0, y = 0; + var width = size || viewport.width / 4; + var height = width; + if (this.softShadow === ShadowMapPass.VSM) { + this._outputDepthPass.material.shader.define('fragment', 'USE_VSM'); + } + else { + this._outputDepthPass.material.shader.unDefine('fragment', 'USE_VSM'); + } + for (var name in this._textures) { + var texture = this._textures[name]; + renderer.setViewport(x, y, width * texture.width / texture.height, height); + this._outputDepthPass.setUniform('depthMap', texture); + this._outputDepthPass.render(renderer); + x += width * texture.width / texture.height; + } + renderer.setViewport(viewport); + renderer.restoreClear(); + }, + + _bindDepthMaterial: function (casters, bias, slopeScale) { + for (var i = 0; i < casters.length; i++) { + var mesh = casters[i]; + var isShadowTransparent = mesh.material.shadowTransparentMap instanceof Texture2D; + var transparentMap = mesh.material.shadowTransparentMap; + var nJoints = mesh.joints && mesh.joints.length; + var matHashKey; + var shaderHashKey; + if (isShadowTransparent) { + matHashKey = nJoints + '-' + transparentMap.__GUID__; + shaderHashKey = nJoints + 's'; + } + else { + matHashKey = nJoints; + shaderHashKey = nJoints; + } + var depthMaterial = this._depthMaterials[matHashKey]; + var depthShader = this._depthShaders[shaderHashKey]; + + if (mesh.material !== depthMaterial) { // Not binded yet + if (!depthShader) { + depthShader = new Shader({ + vertex: Shader.source('qtek.sm.depth.vertex'), + fragment: Shader.source('qtek.sm.depth.fragment') + }); + if (nJoints > 0) { + depthShader.define('vertex', 'SKINNING'); + depthShader.define('vertex', 'JOINT_COUNT', nJoints); + } + if (isShadowTransparent) { + depthShader.define('both', 'SHADOW_TRANSPARENT'); + } + this._depthShaders[shaderHashKey] = depthShader; + } + if (!depthMaterial) { + // Skinned mesh + depthMaterial = new Material({ + shader: depthShader + }); + this._depthMaterials[matHashKey] = depthMaterial; + } + + mesh.material = depthMaterial; + + if (this.softShadow === ShadowMapPass.VSM) { + depthShader.define('fragment', 'USE_VSM'); + } + else { + depthShader.unDefine('fragment', 'USE_VSM'); + } + + depthMaterial.setUniform('bias', bias); + depthMaterial.setUniform('slopeScale', slopeScale); + if (isShadowTransparent) { + depthMaterial.set('shadowTransparentMap', transparentMap); + } + } + } + }, + + _bindDistanceMaterial: function (casters, light) { + var lightPosition = light.getWorldPosition()._array; + for (var i = 0; i < casters.length; i++) { + var mesh = casters[i]; + var nJoints = mesh.joints && mesh.joints.length; + var distanceMaterial = this._distanceMaterials[nJoints]; + if (mesh.material !== distanceMaterial) { + if (!distanceMaterial) { + // Skinned mesh + distanceMaterial = new Material({ + shader: new Shader({ + vertex: Shader.source('qtek.sm.distance.vertex'), + fragment: Shader.source('qtek.sm.distance.fragment') + }) + }); + if (nJoints > 0) { + distanceMaterial.shader.define('vertex', 'SKINNING'); + distanceMaterial.shader.define('vertex', 'JOINT_COUNT', nJoints); + } + this._distanceMaterials[nJoints] = distanceMaterial; + } + mesh.material = distanceMaterial; + + if (this.softShadow === ShadowMapPass.VSM) { + distanceMaterial.shader.define('fragment', 'USE_VSM'); + } + else { + distanceMaterial.shader.unDefine('fragment', 'USE_VSM'); + } + } + + distanceMaterial.set('lightPosition', lightPosition); + distanceMaterial.set('range', light.range); + } + }, + + saveMaterial: function (casters) { + for (var i = 0; i < casters.length; i++) { + var mesh = casters[i]; + this._meshMaterials[mesh.__GUID__] = mesh.material; + } + }, + + restoreMaterial: function (casters) { + for (var i = 0; i < casters.length; i++) { + var mesh = casters[i]; + var material = this._meshMaterials[mesh.__GUID__]; + // In case restoreMaterial when no shadowMap is rendered + if (material) { + mesh.material = material; + } + } + }, + + _updateCaster: function (mesh) { + if (mesh.castShadow) { + this._opaqueCasters.push(mesh); + } + if (mesh.receiveShadow) { + this._receivers.push(mesh); + mesh.material.set('shadowEnabled', 1); + } + else { + mesh.material.set('shadowEnabled', 0); + } + if (this.softShadow === ShadowMapPass.VSM) { + mesh.material.shader.define('fragment', 'USE_VSM'); + } + else { + mesh.material.shader.unDefine('fragment', 'USE_VSM'); + } + }, + + _update: function (scene) { + for (var i = 0; i < scene.opaqueQueue.length; i++) { + this._updateCaster(scene.opaqueQueue[i]); + } + for (var i = 0; i < scene.transparentQueue.length; i++) { + // TODO Transparent object receive shadow will be very slow + // in stealth demo, still not find the reason + this._updateCaster(scene.transparentQueue[i]); + } + for (var i = 0; i < scene.lights.length; i++) { + var light = scene.lights[i]; + if (light.castShadow) { + this._lightsCastShadow.push(light); + } + } + }, + + _renderShadowPass: function (renderer, scene, sceneCamera, notUpdateScene) { + // reset + for (var name in this._shadowMapNumber) { + this._shadowMapNumber[name] = 0; + } + this._lightsCastShadow.length = 0; + this._opaqueCasters.length = 0; + this._receivers.length = 0; + + var _gl = renderer.gl; + + if (!notUpdateScene) { + scene.update(); + } + + this._update(scene); + + if (!this._lightsCastShadow.length) { + return; + } + + _gl.enable(_gl.DEPTH_TEST); + _gl.depthMask(true); + _gl.disable(_gl.BLEND); + + // Clear with high-z, so the part not rendered will not been shadowed + // TODO + // TODO restore + _gl.clearColor(1.0, 1.0, 1.0, 1.0); + + // Shadow uniforms + var spotLightShadowMaps = []; + var spotLightMatrices = []; + var directionalLightShadowMaps = []; + var directionalLightMatrices = []; + var shadowCascadeClips = []; + var pointLightShadowMaps = []; + + this.saveMaterial(this._opaqueCasters); + + var dirLightHasCascade; + // Create textures for shadow map + for (var i = 0; i < this._lightsCastShadow.length; i++) { + var light = this._lightsCastShadow[i]; + if (light instanceof DirectionalLight) { + + if (dirLightHasCascade) { + console.warn('Only one dire light supported with shadow cascade'); + continue; + } + if (light.shadowCascade > 1) { + dirLightHasCascade = light; + + if (light.shadowCascade > 4) { + console.warn('Support at most 4 cascade'); + continue; + } + } + + this.renderDirectionalLightShadow( + renderer, + scene, + sceneCamera, + light, + this._opaqueCasters, + shadowCascadeClips, + directionalLightMatrices, + directionalLightShadowMaps + ); + } + else if (light instanceof SpotLight) { + this.renderSpotLightShadow( + renderer, + light, + this._opaqueCasters, + spotLightMatrices, + spotLightShadowMaps + ); + } + else if (light instanceof PointLight) { + this.renderPointLightShadow( + renderer, + light, + this._opaqueCasters, + pointLightShadowMaps + ); + } + + this._shadowMapNumber[light.type]++; + } + this.restoreMaterial(this._opaqueCasters); + + var shadowCascadeClipsNear = shadowCascadeClips.slice(); + var shadowCascadeClipsFar = shadowCascadeClips.slice(); + shadowCascadeClipsNear.pop(); + shadowCascadeClipsFar.shift(); + + // Iterate from far to near + shadowCascadeClipsNear.reverse(); + shadowCascadeClipsFar.reverse(); + // directionalLightShadowMaps.reverse(); + directionalLightMatrices.reverse(); + + function getSize(texture) { + return texture.height; + } + var spotLightShadowMapSizes = spotLightShadowMaps.map(getSize); + var directionalLightShadowMapSizes = directionalLightShadowMaps.map(getSize); + + var shadowDefineUpdatedShader = {}; + + for (var i = 0; i < this._receivers.length; i++) { + var mesh = this._receivers[i]; + var material = mesh.material; + + var shader = material.shader; + + if (!shadowDefineUpdatedShader[shader.__GUID__]) { + var shaderNeedsUpdate = false; + for (var lightType in this._shadowMapNumber) { + var number = this._shadowMapNumber[lightType]; + var key = lightType + '_SHADOWMAP_COUNT'; + + if (shader.fragmentDefines[key] !== number && number > 0) { + shader.fragmentDefines[key] = number; + shaderNeedsUpdate = true; + } + } + if (shaderNeedsUpdate) { + shader.dirty(); + } + if (dirLightHasCascade) { + shader.define('fragment', 'SHADOW_CASCADE', dirLightHasCascade.shadowCascade); + } + else { + shader.unDefine('fragment', 'SHADOW_CASCADE'); + } + shadowDefineUpdatedShader[shader.__GUID__] = true; + } + + if (spotLightShadowMaps.length > 0) { + material.setUniform('spotLightShadowMaps', spotLightShadowMaps); + material.setUniform('spotLightMatrices', spotLightMatrices); + material.setUniform('spotLightShadowMapSizes', spotLightShadowMapSizes); + } + if (directionalLightShadowMaps.length > 0) { + material.setUniform('directionalLightShadowMaps', directionalLightShadowMaps); + if (dirLightHasCascade) { + material.setUniform('shadowCascadeClipsNear', shadowCascadeClipsNear); + material.setUniform('shadowCascadeClipsFar', shadowCascadeClipsFar); + } + material.setUniform('directionalLightMatrices', directionalLightMatrices); + material.setUniform('directionalLightShadowMapSizes', directionalLightShadowMapSizes); + } + if (pointLightShadowMaps.length > 0) { + material.setUniform('pointLightShadowMaps', pointLightShadowMaps); + } + } + }, + + renderDirectionalLightShadow: (function () { + + var splitFrustum = new Frustum(); + var splitProjMatrix = new Matrix4(); + var cropBBox = new BoundingBox(); + var cropMatrix = new Matrix4(); + var lightViewMatrix = new Matrix4(); + var lightViewProjMatrix = new Matrix4(); + var lightProjMatrix = new Matrix4(); + + return function (renderer, scene, sceneCamera, light, casters, shadowCascadeClips, directionalLightMatrices, directionalLightShadowMaps) { + + var shadowBias = light.shadowBias; + this._bindDepthMaterial(casters, shadowBias, light.shadowSlopeScale); + + casters.sort(Renderer.opaqueSortFunc); + + // Considering moving speed since the bounding box is from last frame + // TODO: add a bias + var clippedFar = Math.min(-scene.viewBoundingBoxLastFrame.min.z, sceneCamera.far); + var clippedNear = Math.max(-scene.viewBoundingBoxLastFrame.max.z, sceneCamera.near); + + var lightCamera = this._getDirectionalLightCamera(light, scene, sceneCamera); + + var lvpMat4Arr = lightViewProjMatrix._array; + lightProjMatrix.copy(lightCamera.projectionMatrix); + mat4.invert(lightViewMatrix._array, lightCamera.worldTransform._array); + mat4.multiply(lightViewMatrix._array, lightViewMatrix._array, sceneCamera.worldTransform._array); + mat4.multiply(lvpMat4Arr, lightProjMatrix._array, lightViewMatrix._array); + + var clipPlanes = []; + var rad = sceneCamera.fov / 180 * Math.PI; + var aspect = sceneCamera.aspect; + + var scaleZ = (sceneCamera.near + sceneCamera.far) / (sceneCamera.near - sceneCamera.far); + var offsetZ = 2 * sceneCamera.near * sceneCamera.far / (sceneCamera.near - sceneCamera.far); + for (var i = 0; i <= light.shadowCascade; i++) { + var clog = clippedNear * Math.pow(clippedFar / clippedNear, i / light.shadowCascade); + var cuni = clippedNear + (clippedFar - clippedNear) * i / light.shadowCascade; + var c = clog * light.cascadeSplitLogFactor + cuni * (1 - light.cascadeSplitLogFactor); + clipPlanes.push(c); + shadowCascadeClips.push(-(-c * scaleZ + offsetZ) / -c); + } + var texture = this._getTexture(light, light.shadowCascade); + directionalLightShadowMaps.push(texture); + + var viewport = renderer.viewport; + + var _gl = renderer.gl; + this._frameBuffer.attach(texture); + this._frameBuffer.bind(renderer); + _gl.clear(_gl.COLOR_BUFFER_BIT | _gl.DEPTH_BUFFER_BIT); + + for (var i = 0; i < light.shadowCascade; i++) { + // Get the splitted frustum + var nearPlane = clipPlanes[i]; + var farPlane = clipPlanes[i+1]; + mat4.perspective(splitProjMatrix._array, rad, aspect, nearPlane, farPlane); + splitFrustum.setFromProjection(splitProjMatrix); + splitFrustum.getTransformedBoundingBox(cropBBox, lightViewMatrix); + cropBBox.applyProjection(lightProjMatrix); + var _min = cropBBox.min._array; + var _max = cropBBox.max._array; + cropMatrix.ortho(_min[0], _max[0], _min[1], _max[1], 1, -1); + lightCamera.projectionMatrix.multiplyLeft(cropMatrix); + + var shadowSize = light.shadowResolution || 512; + + // Reversed, left to right => far to near + renderer.setViewport((light.shadowCascade - i - 1) * shadowSize, 0, shadowSize, shadowSize, 1); + + // Set bias seperately for each cascade + // TODO Simply divide 1.5 ? + for (var key in this._depthMaterials) { + this._depthMaterials[key].set('shadowBias', shadowBias); + } + + renderer.renderQueue(casters, lightCamera); + + // Filter for VSM + if (this.softShadow === ShadowMapPass.VSM) { + this._gaussianFilter(renderer, texture, texture.width); + } + + var matrix = new Matrix4(); + matrix.copy(lightCamera.worldTransform) + .invert() + .multiplyLeft(lightCamera.projectionMatrix); + + directionalLightMatrices.push(matrix._array); + + lightCamera.projectionMatrix.copy(lightProjMatrix); + } + + this._frameBuffer.unbind(renderer); + + renderer.setViewport(viewport); + }; + })(), + + renderSpotLightShadow: function (renderer, light, casters, spotLightMatrices, spotLightShadowMaps) { + + this._bindDepthMaterial(casters, light.shadowBias, light.shadowSlopeScale); + casters.sort(Renderer.opaqueSortFunc); + + var texture = this._getTexture(light); + var camera = this._getSpotLightCamera(light); + var _gl = renderer.gl; + + this._frameBuffer.attach(texture); + this._frameBuffer.bind(renderer); + + _gl.clear(_gl.COLOR_BUFFER_BIT | _gl.DEPTH_BUFFER_BIT); + + renderer.renderQueue(casters, camera); + + this._frameBuffer.unbind(renderer); + + // Filter for VSM + if (this.softShadow === ShadowMapPass.VSM) { + this._gaussianFilter(renderer, texture, texture.width); + } + + var matrix = new Matrix4(); + matrix.copy(camera.worldTransform) + .invert() + .multiplyLeft(camera.projectionMatrix); + + spotLightShadowMaps.push(texture); + spotLightMatrices.push(matrix._array); + }, + + renderPointLightShadow: function (renderer, light, casters, pointLightShadowMaps) { + var texture = this._getTexture(light); + var _gl = renderer.gl; + pointLightShadowMaps.push(texture); + + this._bindDistanceMaterial(casters, light); + for (var i = 0; i < 6; i++) { + var target = targets[i]; + var camera = this._getPointLightCamera(light, target); + + this._frameBuffer.attach(texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i); + this._frameBuffer.bind(renderer); + _gl.clear(_gl.COLOR_BUFFER_BIT | _gl.DEPTH_BUFFER_BIT); + + renderer.renderQueue(casters, camera); + } + this._frameBuffer.unbind(renderer); + }, + + _gaussianFilter: function (renderer, texture, size) { + var parameter = { + width: size, + height: size, + type: Texture.FLOAT + }; + var _gl = renderer.gl; + var tmpTexture = this._texturePool.get(parameter); + + this._frameBuffer.attach(tmpTexture); + this._frameBuffer.bind(renderer); + this._gaussianPassH.setUniform('texture', texture); + this._gaussianPassH.setUniform('textureWidth', size); + this._gaussianPassH.render(renderer); + + this._frameBuffer.attach(texture); + this._gaussianPassV.setUniform('texture', tmpTexture); + this._gaussianPassV.setUniform('textureHeight', size); + this._gaussianPassV.render(renderer); + this._frameBuffer.unbind(renderer); + + this._texturePool.put(tmpTexture); + }, + + _getTexture: function (light, cascade) { + var key = light.__GUID__; + var texture = this._textures[key]; + var resolution = light.shadowResolution || 512; + cascade = cascade || 1; + if (!texture) { + if (light instanceof PointLight) { + texture = new TextureCube(); + } + else { + texture = new Texture2D(); + } + // At most 4 cascade + // TODO share with height ? + texture.width = resolution * cascade; + texture.height = resolution; + if (this.softShadow === ShadowMapPass.VSM) { + texture.type = Texture.FLOAT; + texture.anisotropic = 4; + } + else { + texture.minFilter = glenum.NEAREST; + texture.magFilter = glenum.NEAREST; + texture.useMipmap = false; + } + this._textures[key] = texture; + } + + return texture; + }, + + _getPointLightCamera: function (light, target) { + if (!this._lightCameras.point) { + this._lightCameras.point = { + px: new PerspectiveCamera(), + nx: new PerspectiveCamera(), + py: new PerspectiveCamera(), + ny: new PerspectiveCamera(), + pz: new PerspectiveCamera(), + nz: new PerspectiveCamera() + }; + } + var camera = this._lightCameras.point[target]; + + camera.far = light.range; + camera.fov = 90; + camera.position.set(0, 0, 0); + switch (target) { + case 'px': + camera.lookAt(Vector3.POSITIVE_X, Vector3.NEGATIVE_Y); + break; + case 'nx': + camera.lookAt(Vector3.NEGATIVE_X, Vector3.NEGATIVE_Y); + break; + case 'py': + camera.lookAt(Vector3.POSITIVE_Y, Vector3.POSITIVE_Z); + break; + case 'ny': + camera.lookAt(Vector3.NEGATIVE_Y, Vector3.NEGATIVE_Z); + break; + case 'pz': + camera.lookAt(Vector3.POSITIVE_Z, Vector3.NEGATIVE_Y); + break; + case 'nz': + camera.lookAt(Vector3.NEGATIVE_Z, Vector3.NEGATIVE_Y); + break; + } + light.getWorldPosition(camera.position); + camera.update(); + + return camera; + }, + + _getDirectionalLightCamera: (function () { + var lightViewMatrix = new Matrix4(); + var sceneViewBoundingBox = new BoundingBox(); + var lightViewBBox = new BoundingBox(); + // Camera of directional light will be adjusted + // to contain the view frustum and scene bounding box as tightly as possible + return function (light, scene, sceneCamera) { + if (!this._lightCameras.directional) { + this._lightCameras.directional = new OrthoCamera(); + } + var camera = this._lightCameras.directional; + + sceneViewBoundingBox.copy(scene.viewBoundingBoxLastFrame); + sceneViewBoundingBox.intersection(sceneCamera.frustum.boundingBox); + // Move to the center of frustum(in world space) + camera.position + .copy(sceneViewBoundingBox.min) + .add(sceneViewBoundingBox.max) + .scale(0.5) + .transformMat4(sceneCamera.worldTransform); + camera.rotation.copy(light.rotation); + camera.scale.copy(light.scale); + camera.updateLocalTransform(); + camera.updateWorldTransform(); + + // Transform to light view space + lightViewMatrix + .copy(camera.worldTransform) + .invert() + .multiply(sceneCamera.worldTransform); + + // FIXME boundingBox becomes much larger after transformd. + lightViewBBox.copy(sceneViewBoundingBox).applyTransform(lightViewMatrix); + var min = lightViewBBox.min._array; + var max = lightViewBBox.max._array; + + // Move camera to adjust the near to 0 + // TODO: some scene object cast shadow in view will also be culled + // add a bias? + camera.position.scaleAndAdd(camera.worldTransform.z, max[2] + this.lightFrustumBias); + camera.near = 0; + camera.far = -min[2] + max[2] + this.lightFrustumBias; + camera.left = min[0] - this.lightFrustumBias; + camera.right = max[0] + this.lightFrustumBias; + camera.top = max[1] + this.lightFrustumBias; + camera.bottom = min[1] - this.lightFrustumBias; + camera.update(true); + + return camera; + }; + })(), + + _getSpotLightCamera: function (light) { + if (!this._lightCameras.spot) { + this._lightCameras.spot = new PerspectiveCamera(); + } + var camera = this._lightCameras.spot; + // Update properties + camera.fov = light.penumbraAngle * 2; + camera.far = light.range; + camera.worldTransform.copy(light.worldTransform); + camera.updateProjectionMatrix(); + mat4.invert(camera.viewMatrix._array, camera.worldTransform._array); + + return camera; + }, + + /** + * @param {qtek.Renderer|WebGLRenderingContext} [renderer] + * @memberOf qtek.prePass.ShadowMap.prototype + */ + // PENDING Renderer or WebGLRenderingContext + dispose: function (renderer) { + var _gl = renderer.gl || renderer; + + for (var guid in this._depthMaterials) { + var mat = this._depthMaterials[guid]; + mat.dispose(_gl); + } + for (var guid in this._distanceMaterials) { + var mat = this._distanceMaterials[guid]; + mat.dispose(_gl); + } + + if (this._frameBuffer) { + this._frameBuffer.dispose(_gl); + } + + for (var name in this._textures) { + this._textures[name].dispose(_gl); + } + + this._texturePool.clear(renderer.gl); + + this._depthMaterials = {}; + this._distanceMaterials = {}; + this._textures = {}; + this._lightCameras = {}; + this._shadowMapNumber = { + 'POINT_LIGHT': 0, + 'DIRECTIONAL_LIGHT': 0, + 'SPOT_LIGHT': 0 + }; + this._meshMaterials = {}; + + for (var i = 0; i < this._receivers.length; i++) { + var mesh = this._receivers[i]; + // Mesh may be disposed + if (mesh.material && mesh.material.shader) { + var material = mesh.material; + var shader = material.shader; + shader.unDefine('fragment', 'POINT_LIGHT_SHADOW_COUNT'); + shader.unDefine('fragment', 'DIRECTIONAL_LIGHT_SHADOW_COUNT'); + shader.unDefine('fragment', 'AMBIENT_LIGHT_SHADOW_COUNT'); + material.set('shadowEnabled', 0); + } + } + + this._opaqueCasters = []; + this._receivers = []; + this._lightsCastShadow = []; + } + }); + + /** + * @name qtek.prePass.ShadowMap.VSM + * @type {number} + */ + ShadowMapPass.VSM = 1; + + /** + * @name qtek.prePass.ShadowMap.PCF + * @type {number} + */ + ShadowMapPass.PCF = 2; + + module.exports = ShadowMapPass; + + +/***/ }, +/* 149 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.sm.depth.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\n#ifdef SHADOW_TRANSPARENT\nattribute vec2 texcoord : TEXCOORD_0;\n#endif\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_COUNT] : SKIN_MATRIX;\n#endif\n\nvarying vec4 v_ViewPosition;\n\n#ifdef SHADOW_TRANSPARENT\nvarying vec2 v_Texcoord;\n#endif\n\nvoid main(){\n\n vec3 skinnedPosition = position;\n\n#ifdef SKINNING\n\n @import qtek.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n\n v_ViewPosition = worldViewProjection * vec4(skinnedPosition, 1.0);\n gl_Position = v_ViewPosition;\n\n#ifdef SHADOW_TRANSPARENT\n v_Texcoord = texcoord;\n#endif\n}\n@end\n\n@export qtek.sm.depth.fragment\n\nvarying vec4 v_ViewPosition;\n\n#ifdef SHADOW_TRANSPARENT\nvarying vec2 v_Texcoord;\n#endif\n\nuniform float bias : 0.001;\nuniform float slopeScale : 1.0;\n\n#ifdef SHADOW_TRANSPARENT\nuniform sampler2D transparentMap;\n#endif\n\n@import qtek.util.encode_float\n\nvoid main(){\n float depth = v_ViewPosition.z / v_ViewPosition.w;\n \n#ifdef USE_VSM\n depth = depth * 0.5 + 0.5;\n float moment1 = depth;\n float moment2 = depth * depth;\n\n float dx = dFdx(depth);\n float dy = dFdy(depth);\n moment2 += 0.25*(dx*dx+dy*dy);\n\n gl_FragColor = vec4(moment1, moment2, 0.0, 1.0);\n#else\n float dx = dFdx(depth);\n float dy = dFdy(depth);\n depth += sqrt(dx*dx + dy*dy) * slopeScale + bias;\n\n#ifdef SHADOW_TRANSPARENT\n if (texture2D(transparentMap, v_Texcoord).a <= 0.1) {\n gl_FragColor = encodeFloat(0.9999);\n return;\n }\n#endif\n\n gl_FragColor = encodeFloat(depth * 0.5 + 0.5);\n#endif\n}\n@end\n\n@export qtek.sm.debug_depth\n\nuniform sampler2D depthMap;\nvarying vec2 v_Texcoord;\n\n@import qtek.util.decode_float\n\nvoid main() {\n vec4 tex = texture2D(depthMap, v_Texcoord);\n#ifdef USE_VSM\n gl_FragColor = vec4(tex.rgb, 1.0);\n#else\n float depth = decodeFloat(tex);\n gl_FragColor = vec4(depth, depth, depth, 1.0);\n#endif\n}\n\n@end\n\n\n@export qtek.sm.distance.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world : WORLD;\n\nattribute vec3 position : POSITION;\n\n#ifdef SKINNING\nattribute vec3 boneWeight;\nattribute vec4 boneIndex;\n\nuniform mat4 skinMatrix[JOINT_COUNT] : SKIN_MATRIX;\n#endif\n\nvarying vec3 v_WorldPosition;\n\nvoid main (){\n\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import qtek.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition , 1.0);\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n}\n\n@end\n\n@export qtek.sm.distance.fragment\n\nuniform vec3 lightPosition;\nuniform float range : 100;\n\nvarying vec3 v_WorldPosition;\n\n@import qtek.util.encode_float\n\nvoid main(){\n float dist = distance(lightPosition, v_WorldPosition);\n#ifdef USE_VSM\n gl_FragColor = vec4(dist, dist * dist, 0.0, 0.0);\n#else\n dist = dist / range;\n gl_FragColor = encodeFloat(dist);\n#endif\n}\n@end\n\n@export qtek.plugin.shadow_map_common\n\n@import qtek.util.decode_float\n\nfloat tapShadowMap(sampler2D map, vec2 uv, float z){\n vec4 tex = texture2D(map, uv);\n return step(z, decodeFloat(tex) * 2.0 - 1.0);\n}\n\nfloat pcf(sampler2D map, vec2 uv, float z, float textureSize, vec2 scale) {\n\n float shadowContrib = tapShadowMap(map, uv, z);\n vec2 offset = vec2(1.0 / textureSize) * scale;\n shadowContrib += tapShadowMap(map, uv+vec2(offset.x, 0.0), z);\n shadowContrib += tapShadowMap(map, uv+vec2(offset.x, offset.y), z);\n shadowContrib += tapShadowMap(map, uv+vec2(-offset.x, offset.y), z);\n shadowContrib += tapShadowMap(map, uv+vec2(0.0, offset.y), z);\n shadowContrib += tapShadowMap(map, uv+vec2(-offset.x, 0.0), z);\n shadowContrib += tapShadowMap(map, uv+vec2(-offset.x, -offset.y), z);\n shadowContrib += tapShadowMap(map, uv+vec2(offset.x, -offset.y), z);\n shadowContrib += tapShadowMap(map, uv+vec2(0.0, -offset.y), z);\n\n return shadowContrib / 9.0;\n}\n\nfloat pcf(sampler2D map, vec2 uv, float z, float textureSize) {\n return pcf(map, uv, z, textureSize, vec2(1.0));\n}\n\nfloat chebyshevUpperBound(vec2 moments, float z){\n float p = 0.0;\n z = z * 0.5 + 0.5;\n if (z <= moments.x) {\n p = 1.0;\n }\n float variance = moments.y - moments.x * moments.x;\n variance = max(variance, 0.0000001);\n float mD = moments.x - z;\n float pMax = variance / (variance + mD * mD);\n pMax = clamp((pMax-0.4)/(1.0-0.4), 0.0, 1.0);\n return max(p, pMax);\n}\nfloat computeShadowContrib(\n sampler2D map, mat4 lightVPM, vec3 position, float textureSize, vec2 scale, vec2 offset\n) {\n\n vec4 posInLightSpace = lightVPM * vec4(position, 1.0);\n posInLightSpace.xyz /= posInLightSpace.w;\n float z = posInLightSpace.z;\n if(all(greaterThan(posInLightSpace.xyz, vec3(-0.99, -0.99, -1.0))) &&\n all(lessThan(posInLightSpace.xyz, vec3(0.99, 0.99, 1.0)))){\n vec2 uv = (posInLightSpace.xy+1.0) / 2.0;\n\n #ifdef USE_VSM\n vec2 moments = texture2D(map, uv * scale + offset).xy;\n return chebyshevUpperBound(moments, z);\n #else\n return pcf(map, uv * scale + offset, z, textureSize, scale);\n #endif\n }\n return 1.0;\n}\n\nfloat computeShadowContrib(sampler2D map, mat4 lightVPM, vec3 position, float textureSize) {\n return computeShadowContrib(map, lightVPM, position, textureSize, vec2(1.0), vec2(0.0));\n}\n\nfloat computeShadowContribOmni(samplerCube map, vec3 direction, float range)\n{\n float dist = length(direction);\n vec4 shadowTex = textureCube(map, direction);\n\n#ifdef USE_VSM\n vec2 moments = shadowTex.xy;\n float variance = moments.y - moments.x * moments.x;\n float mD = moments.x - dist;\n float p = variance / (variance + mD * mD);\n if(moments.x + 0.001 < dist){\n return clamp(p, 0.0, 1.0);\n }else{\n return 1.0;\n }\n#else\n return step(dist, (decodeFloat(shadowTex) + 0.0002) * range);\n#endif\n}\n\n@end\n\n\n\n@export qtek.plugin.compute_shadow_map\n\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT) || defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT) || defined(POINT_LIGHT_SHADOWMAP_COUNT)\n\n#ifdef SPOT_LIGHT_SHADOWMAP_COUNT\nuniform sampler2D spotLightShadowMaps[SPOT_LIGHT_SHADOWMAP_COUNT];\nuniform mat4 spotLightMatrices[SPOT_LIGHT_SHADOWMAP_COUNT];\nuniform float spotLightShadowMapSizes[SPOT_LIGHT_SHADOWMAP_COUNT];\n#endif\n\n#ifdef DIRECTIONAL_LIGHT_SHADOWMAP_COUNT\n#if defined(SHADOW_CASCADE)\nuniform sampler2D directionalLightShadowMaps[1];\nuniform mat4 directionalLightMatrices[SHADOW_CASCADE];\nuniform float directionalLightShadowMapSizes[1];\nuniform float shadowCascadeClipsNear[SHADOW_CASCADE];\nuniform float shadowCascadeClipsFar[SHADOW_CASCADE];\n#else\nuniform sampler2D directionalLightShadowMaps[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT];\nuniform mat4 directionalLightMatrices[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT];\nuniform float directionalLightShadowMapSizes[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT];\n#endif\n#endif\n\n#ifdef POINT_LIGHT_SHADOWMAP_COUNT\nuniform samplerCube pointLightShadowMaps[POINT_LIGHT_SHADOWMAP_COUNT];\nuniform float pointLightShadowMapSizes[POINT_LIGHT_SHADOWMAP_COUNT];\n#endif\n\nuniform bool shadowEnabled : true;\n\n@import qtek.plugin.shadow_map_common\n\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\n\nvoid computeShadowOfSpotLights(vec3 position, inout float shadowContribs[SPOT_LIGHT_COUNT] ) {\n float shadowContrib;\n for(int _idx_ = 0; _idx_ < SPOT_LIGHT_SHADOWMAP_COUNT; _idx_++) {{\n shadowContrib = computeShadowContrib(\n spotLightShadowMaps[_idx_], spotLightMatrices[_idx_], position,\n spotLightShadowMapSizes[_idx_]\n );\n shadowContribs[_idx_] = shadowContrib;\n }}\n for(int _idx_ = SPOT_LIGHT_SHADOWMAP_COUNT; _idx_ < SPOT_LIGHT_COUNT; _idx_++){{\n shadowContribs[_idx_] = 1.0;\n }}\n}\n\n#endif\n\n\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n\n#ifdef SHADOW_CASCADE\n\nvoid computeShadowOfDirectionalLights(vec3 position, inout float shadowContribs[DIRECTIONAL_LIGHT_COUNT]){\n float depth = (2.0 * gl_FragCoord.z - gl_DepthRange.near - gl_DepthRange.far)\n / (gl_DepthRange.far - gl_DepthRange.near);\n\n float shadowContrib;\n shadowContribs[0] = 1.0;\n\n for (int _idx_ = 0; _idx_ < SHADOW_CASCADE; _idx_++) {{\n if (\n depth >= shadowCascadeClipsNear[_idx_] &&\n depth <= shadowCascadeClipsFar[_idx_]\n ) {\n shadowContrib = computeShadowContrib(\n directionalLightShadowMaps[0], directionalLightMatrices[_idx_], position,\n directionalLightShadowMapSizes[0],\n vec2(1.0 / float(SHADOW_CASCADE), 1.0),\n vec2(float(_idx_) / float(SHADOW_CASCADE), 0.0)\n );\n shadowContribs[0] = shadowContrib;\n }\n }}\n for(int _idx_ = DIRECTIONAL_LIGHT_SHADOWMAP_COUNT; _idx_ < DIRECTIONAL_LIGHT_COUNT; _idx_++) {{\n shadowContribs[_idx_] = 1.0;\n }}\n}\n\n#else\n\nvoid computeShadowOfDirectionalLights(vec3 position, inout float shadowContribs[DIRECTIONAL_LIGHT_COUNT]){\n float shadowContrib;\n\n for(int _idx_ = 0; _idx_ < DIRECTIONAL_LIGHT_SHADOWMAP_COUNT; _idx_++) {{\n shadowContrib = computeShadowContrib(\n directionalLightShadowMaps[_idx_], directionalLightMatrices[_idx_], position,\n directionalLightShadowMapSizes[_idx_]\n );\n shadowContribs[_idx_] = shadowContrib;\n }}\n for(int _idx_ = DIRECTIONAL_LIGHT_SHADOWMAP_COUNT; _idx_ < DIRECTIONAL_LIGHT_COUNT; _idx_++) {{\n shadowContribs[_idx_] = 1.0;\n }}\n}\n#endif\n\n#endif\n\n\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\n\nvoid computeShadowOfPointLights(vec3 position, inout float shadowContribs[POINT_LIGHT_COUNT] ){\n vec3 lightPosition;\n vec3 direction;\n for(int _idx_ = 0; _idx_ < POINT_LIGHT_SHADOWMAP_COUNT; _idx_++) {{\n lightPosition = pointLightPosition[_idx_];\n direction = position - lightPosition;\n shadowContribs[_idx_] = computeShadowContribOmni(pointLightShadowMaps[_idx_], direction, pointLightRange[_idx_]);\n }}\n for(int _idx_ = POINT_LIGHT_SHADOWMAP_COUNT; _idx_ < POINT_LIGHT_COUNT; _idx_++) {{\n shadowContribs[_idx_] = 1.0;\n }}\n}\n\n#endif\n\n#endif\n\n@end"; + + +/***/ }, +/* 150 */ +/***/ function(module, exports, __webpack_require__) { + + // Ear clipping polygon triangulation + + + var LinkedList = __webpack_require__(60); + var vendor = __webpack_require__(51); + + var VERTEX_TYPE_CONVEX = 1; + var VERTEX_TYPE_REFLEX = 2; + + function Edge(p0, p1) { + this.p0 = p0; + + this.p1 = p1; + + // Dirty trick to speed up the delete operation in linked list + this._linkedListEntry = null; + } + + function triangleArea(x0, y0, x1, y1, x2, y2) { + return (x1 - x0) * (y2 - y1) - (y1 - y0) * (x2 - x1); + } + + function isPointInTriangle(x0, y0, x1, y1, x2, y2, xi, yi) { + return !(triangleArea(x0, y0, x2, y2, xi, yi) <= 0 + || triangleArea(x0, y0, xi, yi, x1, y1) <= 0 + || triangleArea(xi, yi, x2, y2, x1, y1) <= 0); + } + + var TriangulationContext = function() { + + this._points; + + this._triangles; + + this.maxGridNumber = 50; + + this.minGridNumber = 0; + + this._gridNumber = 20; + + this._boundingBox = [[Infinity, Infinity], [-Infinity, -Infinity]]; + + this._nPoints = 0; + + this._nTriangle = 0; + + this._pointTypes; + + this._grids = []; + + this._gridWidth = 0; + this._gridHeight = 0; + + this._edgeList = new LinkedList(); + + // Map of point index and the edge out from the vertex + this._edgeOut = []; + + // Map of point index and the edge in to the vertex + this._edgeIn = []; + + this._candidates = []; + }; + + TriangulationContext.prototype.triangulate = function (points) { + this._nPoints = points.length / 2; + if (this._nPoints < 3) { + return; + } + + // PENDING Dynamic grid number or fixed grid number ? + this._gridNumber = Math.ceil(Math.sqrt(this._nPoints)); + this._gridNumber = Math.max(Math.min(this._gridNumber, this.maxGridNumber), this.minGridNumber); + + this._points = points; + + this._reset(); + + this._prepare(); + + this._earClipping(); + + this._triangles.length = this._nTriangle * 3; + + return this._triangles; + }; + + TriangulationContext.prototype._reset = function() { + + this._nTriangle = 0; + + this._edgeList.clear(); + + this._candidates.length = 0; + + this._pointTypes = new vendor.Int8Array(this._points.length); + + + this._boundingBox[0][0] = this._boundingBox[0][1] = Infinity; + this._boundingBox[1][0] = this._boundingBox[1][1] = -Infinity; + // Initialize grid + var nGrids = this._gridNumber * this._gridNumber; + var len = this._grids.length; + for (var i = 0; i < len; i++) { + this._grids[i].length = 0; + } + for (; i < nGrids; i++) { + this._grids[i] = []; + } + this._grids.length = nGrids; + + // Initialize edges + // In case the array have undefined values + if (len < this._nPoints) { + len = this._edgeIn.length; + for (var i = len; i < this._nPoints; i++) { + this._edgeIn[i] = this._edgeOut[i] = null; + } + } else { + this._edgeIn.length = this._edgeOut.length = this._nPoints; + } + }; + + // Prepare points and edges + TriangulationContext.prototype._prepare = function() { + var bb = this._boundingBox; + var n = this._nPoints; + + var points = this._points; + + // Update bounding box and determine point type is reflex or convex + for (var i = 0, j = n - 1; i < n;) { + var k = (i + 1) % n; + var x0 = points[j * 2]; + var y0 = points[j * 2 + 1]; + var x1 = points[i * 2]; + var y1 = points[i * 2 + 1]; + var x2 = points[k * 2]; + var y2 = points[k * 2 + 1]; + + if (x1 < bb[0][0]) { bb[0][0] = x1; } + if (y1 < bb[0][1]) { bb[0][1] = y1; } + if (x1 > bb[1][0]) { bb[1][0] = x1; } + if (y1 > bb[1][1]) { bb[1][1] = y1; } + + // Make the bounding box a litte bigger + // Avoid the geometry hashing will touching the bound of the bounding box + bb[0][0] -= 0.1; + bb[0][1] -= 0.1; + bb[1][0] += 0.1; + bb[1][1] += 0.1; + + var area = triangleArea(x0, y0, x1, y1, x2, y2); + + this._pointTypes[i] = area <= 0 ? VERTEX_TYPE_CONVEX : VERTEX_TYPE_REFLEX; + if (area <= 0) { + this._candidates.push(i); + } + j = i; + i++; + } + + this._gridWidth = (bb[1][0] - bb[0][0]) / this._gridNumber; + this._gridHeight = (bb[1][1] - bb[0][1]) / this._gridNumber; + + // Put the points in the grids + for (var i = 0; i < n; i++) { + if (this._pointTypes[i] === VERTEX_TYPE_REFLEX) { + var x = points[i * 2]; + var y = points[i * 2 + 1]; + var key = this._getPointHash(x, y); + this._grids[key].push(i); + } + } + + // Create edges + for (var i = 0; i < n-1; i++) { + this._addEdge(i, i+1); + } + this._addEdge(i, 0); + }; + + TriangulationContext.prototype._earClipping = function() { + var candidates = this._candidates; + var nPoints = this._nPoints; + while(candidates.length) { + var isDesperate = true; + for (var i = 0; i < candidates.length;) { + var idx = candidates[i]; + if (this._isEar(idx)) { + this._clipEar(idx); + // TODO + // candidates[i] = candidates[candidates.length - 1]; + // candidates.pop(); + candidates.splice(i, 1); + isDesperate = false; + + nPoints--; + } else { + i++; + } + } + + if (isDesperate) { + // Random pick a convex vertex when there is no more ear + // can be clipped and there are more than 3 points left + // After clip the random picked vertex, go on finding ears again + // So it can be extremely slow in worst case + // TODO + this._clipEar(candidates.pop()); + nPoints--; + } + } + }; + + TriangulationContext.prototype._isEar = function(p1) { + // Find two adjecent edges + var e0 = this._edgeIn[p1]; + var e1 = this._edgeOut[p1]; + // Find two adjecent vertices + var p0 = e0.p0; + var p2 = e1.p1; + + var points = this._points; + + var x0 = points[p0 * 2]; + var y0 = points[p0 * 2 + 1]; + var x1 = points[p1 * 2]; + var y1 = points[p1 * 2 + 1]; + var x2 = points[p2 * 2]; + var y2 = points[p2 * 2 + 1]; + + // Clipped the tiny triangles directly + if (Math.abs(triangleArea(x0, y0, x1, y1, x2, y2)) < Number.EPSILON) { + return true; + } + + var range = this._getTriangleGrids(x0, y0, x1, y1, x2, y2); + + // Find all the points in the grids covered by the triangle + // And figure out if any of them is in the triangle + for (var j = range[0][1]; j <= range[1][1]; j++) { + for (var i = range[0][0]; i <= range[1][0]; i++) { + var gridIdx = j * this._gridNumber + i; + var gridPoints = this._grids[gridIdx]; + + for (var k = 0; k < gridPoints.length; k++) { + var idx = gridPoints[k]; + if (this._pointTypes[idx] == VERTEX_TYPE_REFLEX) { + var xi = points[idx * 2]; + var yi = points[idx * 2 + 1]; + if (isPointInTriangle(x0, y0, x1, y1, x2, y2, xi, yi)) { + return false; + } + } + } + } + } + + return true; + }; + + TriangulationContext.prototype._clipEar = function(p1) { + + var e0 = this._edgeIn[p1]; + var e1 = this._edgeOut[p1]; + + var offset = this._nTriangle * 3; + this._triangles[offset] = e0.p0; + this._triangles[offset + 1] = e0.p1; + this._triangles[offset + 2] = e1.p1; + this._nTriangle++; + + var e0i = this._edgeIn[e0.p0]; + var e1o = this._edgeOut[e1.p1]; + // New candidate after clipping (convex vertex) + if (this._pointTypes[e0.p0] == VERTEX_TYPE_REFLEX) { + if (this.isTriangleConvex2(e0i.p0, e0.p0, e1.p1)) { + // PENDING + // The index in the grids also needs to be removed + // But because it needs `splice` and `indexOf` + // may cost too much + this._candidates.push(e0.p0); + this._pointTypes[e0.p0] = VERTEX_TYPE_CONVEX; + } + } + if (this._pointTypes[e1.p1] == VERTEX_TYPE_REFLEX) { + if (this.isTriangleConvex2(e0.p0, e1.p1, e1o.p1)) { + this._candidates.push(e1.p1); + this._pointTypes[e1.p1] = VERTEX_TYPE_CONVEX; + } + } + + this._removeEdge(e0); + this._removeEdge(e1); + + this._addEdge(e0.p0, e1.p1); + + }; + + TriangulationContext.prototype._addEdge = function(p0, p1) { + + var edge = new Edge(p0, p1); + this._edgeOut[p0] = edge; + this._edgeIn[p1] = edge; + var entry = this._edgeList.insert(edge); + edge._linkedListEntry = entry; + + return edge; + }; + + TriangulationContext.prototype._removeEdge = function(e) { + this._edgeList.remove(e._linkedListEntry); + this._edgeOut[e.p0] = null; + this._edgeIn[e.p1] = null; + }; + + // Get geometric hash of point + // Actually it will find the grid index by giving the point (x y) + TriangulationContext.prototype._getPointHash = function(x, y) { + var bb = this._boundingBox; + return Math.floor((y - bb[0][1]) / this._gridHeight) * this._gridNumber + + Math.floor((x - bb[0][0]) / this._gridWidth); + }; + + // Get the grid range covered by the triangle + TriangulationContext.prototype._getTriangleGrids = (function() { + var range = [[-1, -1], [-1, -1]]; + var minX, minY, maxX, maxY; + return function(x0, y0, x1, y1, x2, y2) { + var bb = this._boundingBox; + + // Use `if` instead of `min` `max` methods when having three or more params + // http://jsperf.com/min-max-multiple-param + minX = maxX = x0; + minY = maxY = y0; + if (x1 < minX) { minX = x1; } + if (y1 < minY) { minY = y1; } + if (x1 > maxX) { maxX = x1; } + if (y1 > maxY) { maxY = y1; } + if (x2 < minX) { minX = x2; } + if (y2 < minY) { minY = y2; } + if (x2 > maxX) { maxX = x2; } + if (y2 > maxY) { maxY = y2; } + + range[0][0] = Math.floor((minX - bb[0][0]) / this._gridWidth); + range[1][0] = Math.floor((maxX - bb[0][0]) / this._gridWidth); + + range[0][1] = Math.floor((minY - bb[0][1]) / this._gridHeight); + range[1][1] = Math.floor((maxY - bb[0][1]) / this._gridHeight); + + return range; + }; + })(); + + TriangulationContext.prototype.isTriangleConvex2 = function(p0, p1, p2) { + return this.triangleArea(p0, p1, p2) < 0; + }; + + TriangulationContext.prototype.triangleArea = function(p0, p1, p2) { + var points = this._points; + var x0 = points[p0 * 2]; + var y0 = points[p0 * 2 + 1]; + var x1 = points[p1 * 2]; + var y1 = points[p1 * 2 + 1]; + var x2 = points[p2 * 2]; + var y2 = points[p2 * 2 + 1]; + return (x1 - x0) * (y2 - y1) - (y1 - y0) * (x2 - x1); + }; + + var earClipping = { + + flatPoints: function (points) { + var flatPoints = new vendor.Float64Array(points.length * 2); + for (var i = 0; i < points.length; i++) { + flatPoints[i * 2] = points[i][0]; + flatPoints[i * 2 + 1] = points[i][1]; + } + return flatPoints; + }, + + triangulate: function (points) { + if (!points.length) { + return []; + } + + var triangulationCtx = new TriangulationContext(); + if (points[0] instanceof Array) { + // Assume each pt is an x,y array + return triangulationCtx.triangulate(earClipping.flatPoints(points)); + } + else if (typeof points[0] === 'number') { + return triangulationCtx.triangulate(points); + } + else { + console.warn('Inavlid points format.'); + return []; + } + } + }; + + module.exports = earClipping; + + +/***/ }, +/* 151 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + // TODO test + var StaticGeometry = __webpack_require__(49); + var Mesh = __webpack_require__(54); + var Node = __webpack_require__(22); + var StandardMaterial = __webpack_require__(109); + var BoundingBox = __webpack_require__(26); + var glMatrix = __webpack_require__(14); + var mat4 = glMatrix.mat4; + var vec3 = glMatrix.vec3; + + /** + * @namespace qtek.util.mesh + */ + var meshUtil = { + /** + * Merge multiple meshes to one. + * Note that these meshes must have the same material + * + * @param {Array.} meshes + * @param {boolean} applyWorldTransform + * @return qtek.Mesh + * @memberOf qtek.util.mesh + */ + merge: function(meshes, applyWorldTransform) { + + if (! meshes.length) { + return; + } + + var templateMesh = meshes[0]; + var templateGeo = templateMesh.geometry; + var material = templateMesh.material; + + var geometry = new StaticGeometry(); + geometry.boundingBox = new BoundingBox(); + + var attributeNames = templateGeo.getEnabledAttributes(); + + for (var i = 0; i < attributeNames.length; i++) { + var name = attributeNames[i]; + var attr = templateGeo.attributes[name]; + // Extend custom attributes + if (!geometry.attributes[name]) { + geometry.attributes[name] = attr.clone(false); + } + } + + var inverseTransposeMatrix = mat4.create(); + // Initialize the array data and merge bounding box + var nVertex = 0; + var nFace = 0; + for (var k = 0; k < meshes.length; k++) { + var currentGeo = meshes[k].geometry; + if (currentGeo.boundingBox) { + currentGeo.boundingBox.applyTransform(applyWorldTransform ? meshes[k].worldTransform : meshes[k].localTransform); + geometry.boundingBox.union(currentGeo.boundingBox); + } + nVertex += currentGeo.vertexCount; + nFace += currentGeo.triangleCount; + } + for (var n = 0; n < attributeNames.length; n++) { + var name = attributeNames[n]; + var attrib = geometry.attributes[name]; + attrib.init(nVertex); + } + if (nVertex >= 0xffff) { + geometry.indices = new Uint32Array(nFace * 3); + } + else { + geometry.indices = new Uint16Array(nFace * 3); + } + + var vertexOffset = 0; + var indicesOffset = 0; + var useIndices = templateGeo.isUseIndices(); + + for (var mm = 0; mm < meshes.length; mm++) { + var mesh = meshes[mm]; + var currentGeo = mesh.geometry; + + var nVertex = currentGeo.vertexCount; + + var matrix = applyWorldTransform ? mesh.worldTransform._array : mesh.localTransform._array; + mat4.invert(inverseTransposeMatrix, matrix); + mat4.transpose(inverseTransposeMatrix, inverseTransposeMatrix); + + for (var nn = 0; nn < attributeNames.length; nn++) { + var name = attributeNames[nn]; + var currentAttr = currentGeo.attributes[name]; + var targetAttr = geometry.attributes[name]; + // Skip the unused attributes; + if (!currentAttr.value.length) { + continue; + } + var len = currentAttr.value.length; + var size = currentAttr.size; + var offset = vertexOffset * size; + var count = len / size; + for (var i = 0; i < len; i++) { + targetAttr.value[offset + i] = currentAttr.value[i]; + } + // Transform position, normal and tangent + if (name === 'position') { + vec3.forEach(targetAttr.value, size, offset, count, vec3.transformMat4, matrix); + } + else if (name === 'normal' || name === 'tangent') { + vec3.forEach(targetAttr.value, size, offset, count, vec3.transformMat4, inverseTransposeMatrix); + } + } + + if (useIndices) { + var len = currentGeo.indices.length; + for (var i = 0; i < len; i++) { + geometry.indices[i + indicesOffset] = currentGeo.indices[i] + vertexOffset; + } + indicesOffset += len; + } + + vertexOffset += nVertex; + } + + return new Mesh({ + material: material, + geometry: geometry + }); + }, + + /** + * Split mesh into sub meshes, each mesh will have maxJointNumber joints. + * @param {qtek.Mesh} mesh + * @param {number} maxJointNumber + * @param {boolean} inPlace + * @return {qtek.Node} + * + * @memberOf qtek.util.mesh + */ + splitByJoints: function(mesh, maxJointNumber, inPlace) { + var geometry = mesh.geometry; + var skeleton = mesh.skeleton; + var material = mesh.material; + var shader = material.shader; + var joints = mesh.joints; + if (!geometry || !skeleton || !joints.length) { + return; + } + if (joints.length < maxJointNumber) { + return mesh; + } + + var shaders = {}; + + var indices = geometry.indices; + + var faceLen = geometry.triangleCount; + var rest = faceLen; + var isFaceAdded = []; + var jointValues = geometry.attributes.joint.value; + for (var i = 0; i < faceLen; i++) { + isFaceAdded[i] = false; + } + var addedJointIdxPerFace = []; + + var buckets = []; + + var getJointByIndex = function(idx) { + return joints[idx]; + }; + while (rest > 0) { + var bucketTriangles = []; + var bucketJointReverseMap = []; + var bucketJoints = []; + var subJointNumber = 0; + for (var i = 0; i < joints.length; i++) { + bucketJointReverseMap[i] = -1; + } + for (var f = 0; f < faceLen; f++) { + if (isFaceAdded[f]) { + continue; + } + var canAddToBucket = true; + var addedNumber = 0; + for (var i = 0; i < 3; i++) { + + var idx = indices[f * 3 + i]; + + for (var j = 0; j < 4; j++) { + var jointIdx = jointValues[idx * 4 + j]; + + if (jointIdx >= 0) { + if (bucketJointReverseMap[jointIdx] === -1) { + if (subJointNumber < maxJointNumber) { + bucketJointReverseMap[jointIdx] = subJointNumber; + bucketJoints[subJointNumber++] = jointIdx; + addedJointIdxPerFace[addedNumber++] = jointIdx; + } + else { + canAddToBucket = false; + } + } + } + } + } + if (!canAddToBucket) { + // Reverse operation + for (var i = 0; i < addedNumber; i++) { + bucketJointReverseMap[addedJointIdxPerFace[i]] = -1; + bucketJoints.pop(); + subJointNumber--; + } + } + else { + bucketTriangles.push(indices.subarray(f * 3, (f + 1) * 3)); + + isFaceAdded[f] = true; + rest--; + } + } + buckets.push({ + triangles: bucketTriangles, + joints: bucketJoints.map(getJointByIndex), + jointReverseMap: bucketJointReverseMap + }); + } + + var root = new Node({ + name : mesh.name + }); + var attribNames = geometry.getEnabledAttributes(); + + attribNames.splice(attribNames.indexOf('joint'), 1); + // Map from old vertex index to new vertex index + var newIndices = []; + for (var b = 0; b < buckets.length; b++) { + var bucket = buckets[b]; + var jointReverseMap = bucket.jointReverseMap; + var subJointNumber = bucket.joints.length; + + var subMat = material.clone(); + if (material instanceof StandardMaterial) { + subMat.jointCount = subJointNumber; + } + else { + var subShader = shaders[subJointNumber]; + if (!subShader) { + subShader = shader.clone(); + subShader.define('vertex', 'JOINT_COUNT', subJointNumber); + shaders[subJointNumber] = subShader; + } + material.attachShader(subShader, true); + } + material.name = [material.name, b].join('-'); + + var subGeo = new StaticGeometry(); + + var subMesh = new Mesh({ + name: [mesh.name, i].join('-'), + material: subMat, + geometry: subGeo, + skeleton: skeleton, + joints: bucket.joints.slice() + }); + var nVertex = 0; + var nVertex2 = geometry.vertexCount; + for (var i = 0; i < nVertex2; i++) { + newIndices[i] = -1; + } + // Count sub geo number + for (var f = 0; f < bucket.triangles.length; f++) { + var face = bucket.triangles[f]; + for (var i = 0; i < 3; i++) { + var idx = face[i]; + if (newIndices[idx] === -1) { + newIndices[idx] = nVertex; + nVertex++; + } + } + } + for (var a = 0; a < attribNames.length; a++) { + var attribName = attribNames[a]; + var subAttrib = subGeo.attributes[attribName]; + subAttrib.init(nVertex); + } + subGeo.attributes.joint.value = new Float32Array(nVertex * 4); + + if (nVertex > 0xffff) { + subGeo.indices = new Uint32Array(bucket.triangles.length * 3); + } + else { + subGeo.indices = new Uint16Array(bucket.triangles.length * 3); + } + + var indicesOffset = 0; + nVertex = 0; + for (var i = 0; i < nVertex2; i++) { + newIndices[i] = -1; + } + + for (var f = 0; f < bucket.triangles.length; f++) { + var triangle = bucket.triangles[f]; + for (var i = 0; i < 3; i++) { + + var idx = triangle[i]; + + if (newIndices[idx] === -1) { + newIndices[idx] = nVertex; + for (var a = 0; a < attribNames.length; a++) { + var attribName = attribNames[a]; + var attrib = geometry.attributes[attribName]; + var subAttrib = subGeo.attributes[attribName]; + var size = attrib.size; + + for (var j = 0; j < size; j++) { + subAttrib.value[nVertex * size + j] = attrib.value[idx * size + j]; + } + } + for (var j = 0; j < 4; j++) { + var jointIdx = geometry.attributes.joint.value[idx * 4 + j]; + var offset = nVertex * 4 + j; + if (jointIdx >= 0) { + subGeo.attributes.joint.value[offset] = jointReverseMap[jointIdx]; + } + else { + subGeo.attributes.joint.value[offset] = -1; + } + } + nVertex++; + } + subGeo.indices[indicesOffset++] = newIndices[idx]; + } + } + + root.add(subMesh); + } + var children = mesh.children(); + for (var i = 0; i < children.length; i++) { + root.add(children[i]); + } + root.position.copy(mesh.position); + root.rotation.copy(mesh.rotation); + root.scale.copy(mesh.scale); + + if (inPlace) { + if (mesh.getParent()) { + var parent = mesh.getParent(); + parent.remove(mesh); + parent.add(root); + } + } + return root; + } + }; + + module.exports = meshUtil; + + +/***/ }, +/* 152 */ +/***/ function(module, exports, __webpack_require__) { + + // Spherical Harmonic Helpers + + + var Texture = __webpack_require__(40); + var FrameBuffer = __webpack_require__(44); + var Texture2D = __webpack_require__(39); + var TextureCube = __webpack_require__(45); + var textureUtil = __webpack_require__(94); + var Pass = __webpack_require__(47); + var vendor = __webpack_require__(51); + var Skybox = __webpack_require__(90); + var Skydome = __webpack_require__(95); + var EnvironmentMapPass = __webpack_require__(93); + var Scene = __webpack_require__(92); + var glmatrix = __webpack_require__(14); + var vec3 = glmatrix.vec3; + var sh = {}; + + + var projectEnvMapShaderCode = __webpack_require__(153); + + var targets = ['px', 'nx', 'py', 'ny', 'pz', 'nz']; + + // Project on gpu, but needs browser to support readPixels as Float32Array. + function projectEnvironmentMapGPU(renderer, envMap) { + var shTexture = new Texture2D({ + width: 9, + height: 1, + type: Texture.FLOAT + }); + var pass = new Pass({ + fragment: projectEnvMapShaderCode + }); + pass.material.shader.define('fragment', 'TEXTURE_SIZE', envMap.width); + pass.setUniform('environmentMap', envMap); + + var framebuffer = new FrameBuffer(); + framebuffer.attach(shTexture); + pass.render(renderer, framebuffer); + + framebuffer.bind(renderer); + // TODO Only chrome and firefox support Float32Array + var pixels = new vendor.Float32Array(9 * 4); + renderer.gl.readPixels(0, 0, 9, 1, Texture.RGBA, Texture.FLOAT, pixels); + + var coeff = new vendor.Float32Array(9 * 3); + for (var i = 0; i < 9; i++) { + coeff[i * 3] = pixels[i * 4]; + coeff[i * 3 + 1] = pixels[i * 4 + 1]; + coeff[i * 3 + 2] = pixels[i * 4 + 2]; + } + framebuffer.unbind(renderer); + + framebuffer.dispose(renderer.gl); + pass.dispose(renderer.gl); + return coeff; + } + + function harmonics(normal, index){ + var x = normal[0]; + var y = normal[1]; + var z = normal[2]; + + if (index === 0) { + return 1.0; + } + else if (index === 1) { + return x; + } + else if (index === 2) { + return y; + } + else if (index === 3) { + return z; + } + else if (index === 4) { + return x * z; + } + else if (index === 5) { + return y * z; + } + else if (index === 6) { + return x * y; + } + else if (index === 7) { + return 3.0 * z * z - 1.0; + } + else { + return x * x - y * y; + } + } + + var normalTransform = { + px: [2, 1, 0, -1, -1, 1], + nx: [2, 1, 0, 1, -1, -1], + py: [0, 2, 1, 1, -1, -1], + ny: [0, 2, 1, 1, 1, 1], + pz: [0, 1, 2, -1, -1, -1], + nz: [0, 1, 2, 1, -1, 1] + }; + + // Project on cpu. + function projectEnvironmentMapCPU(renderer, cubePixels, width, height) { + var coeff = new vendor.Float32Array(9 * 3); + var normal = vec3.create(); + var texel = vec3.create(); + var fetchNormal = vec3.create(); + for (var m = 0; m < 9; m++) { + var result = vec3.create(); + for (var k = 0; k < targets.length; k++) { + var pixels = cubePixels[targets[k]]; + + var sideResult = vec3.create(); + var divider = 0; + var i = 0; + var transform = normalTransform[targets[k]]; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + + normal[0] = x / (width - 1.0) * 2.0 - 1.0; + // TODO Flip y? + normal[1] = y / (height - 1.0) * 2.0 - 1.0; + normal[2] = -1.0; + vec3.normalize(normal, normal); + + fetchNormal[0] = normal[transform[0]] * transform[3]; + fetchNormal[1] = normal[transform[1]] * transform[4]; + fetchNormal[2] = normal[transform[2]] * transform[5]; + + texel[0] = pixels[i++] / 255; + texel[1] = pixels[i++] / 255; + texel[2] = pixels[i++] / 255; + // RGBM Decode + var scale = pixels[i++] / 255 * 51.5; + texel[0] *= scale; + texel[1] *= scale; + texel[2] *= scale; + + vec3.scaleAndAdd(sideResult, sideResult, texel, harmonics(fetchNormal, m) * -normal[2]); + // -normal.z equals cos(theta) of Lambertian + divider += -normal[2]; + } + } + vec3.scaleAndAdd(result, result, sideResult, 1 / divider); + } + + coeff[m * 3] = result[0] / 6.0; + coeff[m * 3 + 1] = result[1] / 6.0; + coeff[m * 3 + 2] = result[2] / 6.0; + } + return coeff; + } + + /** + * @param {qtek.Renderer} renderer + * @param {qtek.Texture} envMap + * @param {Object} [textureOpts] + * @param {Object} [textureOpts.lod] + * @param {boolean} [textureOpts.decodeRGBM] + */ + sh.projectEnvironmentMap = function (renderer, envMap, opts) { + + // TODO sRGB + + opts = opts || {}; + opts.lod = opts.lod || 0; + + var skybox; + var dummyScene = new Scene(); + if (envMap instanceof Texture2D) { + skybox = new Skydome({ + scene: dummyScene, + environmentMap: envMap + }); + } + else { + skybox = new Skybox({ + scene: dummyScene, + environmentMap: envMap + }); + } + // Convert to rgbm + var width = 128; + var height = 128; + var rgbmTexture = new Texture2D({ + width: width, + height: height + }); + var framebuffer = new FrameBuffer(); + skybox.material.shader.define('fragment', 'RGBM_ENCODE'); + if (opts.decodeRGBM) { + skybox.material.shader.define('fragment', 'RGBM_DECODE'); + } + skybox.material.set('lod', opts.lod); + var envMapPass = new EnvironmentMapPass({ + texture: rgbmTexture + }); + var cubePixels = {}; + for (var i = 0; i < targets.length; i++) { + cubePixels[targets[i]] = new Uint8Array(width * height * 4); + var camera = envMapPass.getCamera(targets[i]); + camera.fov = 90; + framebuffer.attach(rgbmTexture); + framebuffer.bind(renderer); + renderer.render(dummyScene, camera); + renderer.gl.readPixels( + 0, 0, width, height, + Texture.RGBA, Texture.UNSIGNED_BYTE, cubePixels[targets[i]] + ); + framebuffer.unbind(renderer); + } + + skybox.dispose(renderer.gl); + framebuffer.dispose(renderer.gl); + rgbmTexture.dispose(renderer.gl); + + return projectEnvironmentMapCPU(renderer, cubePixels, width, height); + }; + + module.exports = sh; + + +/***/ }, +/* 153 */ +/***/ function(module, exports) { + + + module.exports = "uniform samplerCube environmentMap;\n\nvarying vec2 v_Texcoord;\n\n#define TEXTURE_SIZE 16\n\nmat3 front = mat3(\n 1.0, 0.0, 0.0,\n 0.0, 1.0, 0.0,\n 0.0, 0.0, 1.0\n);\n\nmat3 back = mat3(\n -1.0, 0.0, 0.0,\n 0.0, 1.0, 0.0,\n 0.0, 0.0, -1.0\n);\n\nmat3 left = mat3(\n 0.0, 0.0, -1.0,\n 0.0, 1.0, 0.0,\n 1.0, 0.0, 0.0\n);\n\nmat3 right = mat3(\n 0.0, 0.0, 1.0,\n 0.0, 1.0, 0.0,\n -1.0, 0.0, 0.0\n);\n\nmat3 up = mat3(\n 1.0, 0.0, 0.0,\n 0.0, 0.0, 1.0,\n 0.0, -1.0, 0.0\n);\n\nmat3 down = mat3(\n 1.0, 0.0, 0.0,\n 0.0, 0.0, -1.0,\n 0.0, 1.0, 0.0\n);\n\n\nfloat harmonics(vec3 normal){\n int index = int(gl_FragCoord.x);\n\n float x = normal.x;\n float y = normal.y;\n float z = normal.z;\n\n if(index==0){\n return 1.0;\n }\n else if(index==1){\n return x;\n }\n else if(index==2){\n return y;\n }\n else if(index==3){\n return z;\n }\n else if(index==4){\n return x*z;\n }\n else if(index==5){\n return y*z;\n }\n else if(index==6){\n return x*y;\n }\n else if(index==7){\n return 3.0*z*z - 1.0;\n }\n else{\n return x*x - y*y;\n }\n}\n\nvec3 sampleSide(mat3 rot)\n{\n\n vec3 result = vec3(0.0);\n float divider = 0.0;\n for (int i = 0; i < TEXTURE_SIZE * TEXTURE_SIZE; i++) {\n float x = mod(float(i), float(TEXTURE_SIZE));\n float y = float(i / TEXTURE_SIZE);\n\n vec2 sidecoord = ((vec2(x, y) + vec2(0.5, 0.5)) / vec2(TEXTURE_SIZE)) * 2.0 - 1.0;\n vec3 normal = normalize(vec3(sidecoord, -1.0));\n vec3 fetchNormal = rot * normal;\n vec3 texel = textureCube(environmentMap, fetchNormal).rgb;\n\n result += harmonics(fetchNormal) * texel * -normal.z;\n\n divider += -normal.z;\n }\n\n return result / divider;\n}\n\nvoid main()\n{\n vec3 result = (\n sampleSide(front) +\n sampleSide(back) +\n sampleSide(left) +\n sampleSide(right) +\n sampleSide(up) +\n sampleSide(down)\n ) / 6.0;\n gl_FragColor = vec4(result, 1.0);\n}"; + + +/***/ }, +/* 154 */ +/***/ function(module, exports) { + + + module.exports = '0.3.0'; + + +/***/ }, +/* 155 */ +/***/ function(module, exports, __webpack_require__) { + + // https://github.com/googlevr/webvr-polyfill/blob/master/src/cardboard-distorter.js + + // Use webvr may have scale problem. + // https://github.com/googlevr/webvr-polyfill/issues/140 + // https://github.com/googlevr/webvr-polyfill/search?q=SCALE&type=Issues&utf8=%E2%9C%93 + // https://github.com/googlevr/webvr-polyfill/issues/147 + + + + + var Mesh = __webpack_require__(54); + var Material = __webpack_require__(53); + var StaticGeometry = __webpack_require__(49); + var Shader = __webpack_require__(52); + var Base = __webpack_require__(3); + var PerspectiveCamera = __webpack_require__(31); + + Shader.import(__webpack_require__(156)); + + function lerp (a, b, t) { + return a * (1 - t) + b * t; + } + + var CardboardDistorter = Base.extend(function () { + return { + + clearColor: [0, 0, 0, 1], + + _mesh: new Mesh({ + geometry: new StaticGeometry(), + culling: false, + material: new Material({ + // FIXME Why disable depthMask will be wrong + // depthMask: false, + depthTest: false, + shader: new Shader({ + vertex: Shader.source('qtek.vr.disorter.output.vertex'), + fragment: Shader.source('qtek.vr.disorter.output.fragment') + }) + }) + }), + _fakeCamera: new PerspectiveCamera() + }; + }, { + + render: function (renderer, sourceTexture) { + var clearColor = this.clearColor; + var gl = renderer.gl; + gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + gl.clear(gl.COLOR_BUFFER_BIT); + + gl.disable(gl.BLEND); + + this._mesh.material.set('texture', sourceTexture); + + // Full size? + renderer.saveViewport(); + renderer.setViewport(0, 0, renderer.getWidth(), renderer.getHeight()); + renderer.renderQueue([this._mesh], this._fakeCamera); + renderer.restoreViewport(); + // this._mesh.material.shader.bind(renderer.gl); + // this._mesh.material.bind(renderer.gl); + // this._mesh.render(renderer.gl); + }, + + updateFromVRDisplay: function (vrDisplay) { + + // FIXME + if (vrDisplay.deviceInfo_) { + // Hardcoded mesh size + this._updateMesh(20, 20, vrDisplay.deviceInfo_); + } + else { + console.warn('Cant get vrDisplay.deviceInfo_, seems code changed'); + } + }, + + _updateMesh: function (width, height, deviceInfo) { + + var positionAttr = this._mesh.geometry.attributes.position; + var texcoordAttr = this._mesh.geometry.attributes.texcoord0; + positionAttr.init(2 * width * height); + texcoordAttr.init(2 * width * height); + + var lensFrustum = deviceInfo.getLeftEyeVisibleTanAngles(); + var noLensFrustum = deviceInfo.getLeftEyeNoLensTanAngles(); + var viewport = deviceInfo.getLeftEyeVisibleScreenRect(noLensFrustum); + var vidx = 0; + + var pos = []; + var uv = []; + + // Vertices + for (var e = 0; e < 2; e++) { + for (var j = 0; j < height; j++) { + for (var i = 0; i < width; i++, vidx++) { + var u = i / (width - 1); + var v = j / (height - 1); + + // Grid points regularly spaced in StreoScreen, and barrel distorted in + // the mesh. + var s = u; + var t = v; + var x = lerp(lensFrustum[0], lensFrustum[2], u); + var y = lerp(lensFrustum[3], lensFrustum[1], v); + var d = Math.sqrt(x * x + y * y); + var r = deviceInfo.distortion.distortInverse(d); + var p = x * r / d; + var q = y * r / d; + u = (p - noLensFrustum[0]) / (noLensFrustum[2] - noLensFrustum[0]); + v = (q - noLensFrustum[3]) / (noLensFrustum[1] - noLensFrustum[3]); + + // Convert u,v to mesh screen coordinates. + var aspect = deviceInfo.device.widthMeters / deviceInfo.device.heightMeters; + + // FIXME: The original Unity plugin multiplied U by the aspect ratio + // and didn't multiply either value by 2, but that seems to get it + // really close to correct looking for me. I hate this kind of "Don't + // know why it works" code though, and wold love a more logical + // explanation of what needs to happen here. + u = (viewport.x + u * viewport.width - 0.5) * 2.0; //* aspect; + v = (viewport.y + v * viewport.height - 0.5) * 2.0; + + pos[0] = u; + pos[1] = v; + pos[2] = 0; + + uv[0] = s * 0.5 + e * 0.5; + uv[1] = t; + + positionAttr.set(vidx, pos); + texcoordAttr.set(vidx, uv); + } + } + + var w = lensFrustum[2] - lensFrustum[0]; + lensFrustum[0] = -(w + lensFrustum[0]); + lensFrustum[2] = w - lensFrustum[2]; + w = noLensFrustum[2] - noLensFrustum[0]; + noLensFrustum[0] = -(w + noLensFrustum[0]); + noLensFrustum[2] = w - noLensFrustum[2]; + viewport.x = 1 - (viewport.x + viewport.width); + } + + // Indices + var indices = new Uint16Array(2 * (width - 1) * (height - 1) * 6); + var halfwidth = width / 2; + var halfheight = height / 2; + var vidx = 0; + var iidx = 0; + for (var e = 0; e < 2; e++) { + for (var j = 0; j < height; j++) { + for (var i = 0; i < width; i++, vidx++) { + if (i === 0 || j === 0) { + continue; + } + // Build a quad. Lower right and upper left quadrants have quads with + // the triangle diagonal flipped to get the vignette to interpolate + // correctly. + if ((i <= halfwidth) == (j <= halfheight)) { + // Quad diagonal lower left to upper right. + indices[iidx++] = vidx; + indices[iidx++] = vidx - width - 1; + indices[iidx++] = vidx - width; + indices[iidx++] = vidx - width - 1; + indices[iidx++] = vidx; + indices[iidx++] = vidx - 1; + } + else { + // Quad diagonal upper left to lower right. + indices[iidx++] = vidx - 1; + indices[iidx++] = vidx - width; + indices[iidx++] = vidx; + indices[iidx++] = vidx - width; + indices[iidx++] = vidx - 1; + indices[iidx++] = vidx - width - 1; + } + } + } + } + + this._mesh.geometry.indices = indices; + + this._mesh.geometry.dirty(); + } + }); + + module.exports = CardboardDistorter; + + +/***/ }, +/* 156 */ +/***/ function(module, exports) { + + + module.exports = "@export qtek.vr.disorter.output.vertex\n\nattribute vec2 texcoord: TEXCOORD_0;\nattribute vec3 position: POSITION;\n\nvarying vec2 v_Texcoord;\n\nvoid main()\n{\n\n v_Texcoord = texcoord;\n\n gl_Position = vec4(position.xy, 0.5, 1.0);\n}\n\n@end\n\n@export qtek.vr.disorter.output.fragment\n\nuniform sampler2D texture;\n\nvarying vec2 v_Texcoord;\n\nvoid main()\n{\n gl_FragColor = texture2D(texture, v_Texcoord);\n}\n@end"; + + +/***/ }, +/* 157 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Base = __webpack_require__(22); + var Camera = __webpack_require__(31); + var Matrix4 = __webpack_require__(25); + + var tmpProjectionMatrix = new Matrix4(); + + var StereoCamera = Base.extend(function () { + return { + + aspect: 0.5, + + _leftCamera: new Camera(), + + _rightCamera: new Camera(), + + _eyeLeft: new Matrix4(), + _eyeRight: new Matrix4(), + + _frameData: null + }; + }, { + + updateFromCamera: function (camera, focus, zoom, eyeSep) { + if (camera.transformNeedsUpdate()) { + console.warn('Node transform is not updated'); + } + + focus = focus == null ? 10 : focus; + zoom = zoom == null ? 1 : zoom; + eyeSep = eyeSep == null ? 0.064 : eyeSep; + + var fov = camera.fov; + var aspect = camera.aspect * this.aspect; + var near = camera.near; + + // Off-axis stereoscopic effect based on + // http://paulbourke.net/stereographics/stereorender/ + + tmpProjectionMatrix.copy(camera.projectionMatrix); + var eyeSep = eyeSep / 2; + var eyeSepOnProjection = eyeSep * near / focus; + var ymax = (near * Math.tan(Math.PI / 180 * fov * 0.5 ) ) / zoom; + var xmin, xmax; + + // translate xOffset + this._eyeLeft._array[12] = - eyeSep; + this._eyeRight._array[12] = eyeSep; + + // for left eye + xmin = - ymax * aspect + eyeSepOnProjection; + xmax = ymax * aspect + eyeSepOnProjection; + + tmpProjectionMatrix._array[0] = 2 * near / (xmax - xmin); + tmpProjectionMatrix._array[8] = (xmax + xmin ) / (xmax - xmin); + + this._leftCamera.projectionMatrix.copy(tmpProjectionMatrix); + + // for right eye + xmin = - ymax * aspect - eyeSepOnProjection; + xmax = ymax * aspect - eyeSepOnProjection; + + tmpProjectionMatrix._array[0] = 2 * near / (xmax - xmin); + tmpProjectionMatrix._array[8] = (xmax + xmin ) / (xmax - xmin); + + this._rightCamera.projectionMatrix.copy(tmpProjectionMatrix); + + this._leftCamera.worldTransform + .copy(camera.worldTransform) + .multiply(this._eyeLeft); + + this._rightCamera.worldTransform + .copy(camera.worldTransform) + .multiply(this._eyeRight); + + this._leftCamera.decomposeWorldTransform(); + this._leftCamera.decomposeProjectionMatrix(); + + this._rightCamera.decomposeWorldTransform(); + this._rightCamera.decomposeProjectionMatrix(); + }, + + updateFromVRDisplay: function (vrDisplay, parentNode) { + + if (typeof VRFrameData === 'undefined') { + return; + } + + var frameData = this._frameData || (this._frameData = new VRFrameData()); + vrDisplay.getFrameData(frameData); + var leftCamera = this._leftCamera; + var rightCamera = this._rightCamera; + + leftCamera.projectionMatrix.setArray(frameData.leftProjectionMatrix); + leftCamera.decomposeProjectionMatrix(); + leftCamera.viewMatrix.setArray(frameData.leftViewMatrix); + leftCamera.setViewMatrix(leftCamera.viewMatrix); + + rightCamera.projectionMatrix.setArray(frameData.rightProjectionMatrix); + rightCamera.decomposeProjectionMatrix(); + rightCamera.viewMatrix.setArray(frameData.rightViewMatrix); + rightCamera.setViewMatrix(rightCamera.viewMatrix); + + if (parentNode && parentNode.worldTransform) { + if (parentNode.transformNeedsUpdate()) { + console.warn('Node transform is not updated'); + } + leftCamera.worldTransform.multiplyLeft(parentNode.worldTransform); + leftCamera.decomposeWorldTransform(); + rightCamera.worldTransform.multiplyLeft(parentNode.worldTransform); + rightCamera.decomposeWorldTransform(); + } + }, + + getLeftCamera: function () { + return this._leftCamera; + }, + + getRightCamera: function () { + return this._rightCamera; + } + }); + + module.exports = StereoCamera; + + +/***/ } +/******/ ]) +}); +; \ No newline at end of file diff --git a/dist/qtek.min.js b/dist/qtek.min.js index 50e933168..1bd645e70 100644 --- a/dist/qtek.min.js +++ b/dist/qtek.min.js @@ -1,12 +1,13 @@ -!function(a){"undefined"!=typeof define&&define.amd?define(["exports"],a.bind(window)):a(window.qtek={})}(function(a){var b,c,d;!function(a){function e(a,b){return u.call(a,b)}function f(a,b){var c,d,e,f,g,h,i,j,k,l,m=b&&b.split("/"),n=s.map,o=n&&n["*"]||{};if(a&&"."===a.charAt(0))if(b){for(m=m.slice(0,m.length-1),a=m.concat(a.split("/")),j=0;j0&&(a.splice(j-1,2),j-=2)}a=a.join("/")}else 0===a.indexOf("./")&&(a=a.substring(2));if((m||o)&&n){for(c=a.split("/"),j=c.length;j>0;j-=1){if(d=c.slice(0,j).join("/"),m)for(k=m.length;k>0;k-=1)if(e=n[m.slice(0,k).join("/")],e&&(e=e[d])){f=e,g=j;break}if(f)break;!h&&o&&o[d]&&(h=o[d],i=j)}!f&&h&&(f=h,g=i),f&&(c.splice(0,g,f),a=c.join("/"))}return a}function g(b,c){return function(){return n.apply(a,v.call(arguments,0).concat([b,c]))}}function h(a){return function(b){return f(b,a)}}function i(a){return function(b){q[a]=b}}function j(b){if(e(r,b)){var c=r[b];delete r[b],t[b]=!0,m.apply(a,c)}if(!e(q,b)&&!e(t,b))throw new Error("No "+b);return q[b]}function k(a){var b,c=a?a.indexOf("!"):-1;return c>-1&&(b=a.substring(0,c),a=a.substring(c+1,a.length)),[b,a]}function l(a){return function(){return s&&s.config&&s.config[a]||{}}}var m,n,o,p,q={},r={},s={},t={},u=Object.prototype.hasOwnProperty,v=[].slice;o=function(a,b){var c,d=k(a),e=d[0];return a=d[1],e&&(e=f(e,b),c=j(e)),e?a=c&&c.normalize?c.normalize(a,h(b)):f(a,b):(a=f(a,b),d=k(a),e=d[0],a=d[1],e&&(c=j(e))),{f:e?e+"!"+a:a,n:a,pr:e,p:c}},p={require:function(a){return g(a)},exports:function(a){var b=q[a];return"undefined"!=typeof b?b:q[a]={}},module:function(a){return{id:a,uri:"",exports:q[a],config:l(a)}}},m=function(b,c,d,f){var h,k,l,m,n,s,u=[];if(f=f||b,"function"==typeof d){for(c=!c.length&&d.length?["require","exports","module"]:c,n=0;nd;d++)b.call(c,a[d],d,a);else for(var f in a)a.hasOwnProperty(f)&&b.call(c,a[f],f,a)},isObject:function(a){return a===Object(a)},isArray:function(a){return a instanceof Array},isArrayLike:function(a){return a?a.length===+a.length:!1},clone:function(a){if(b.isObject(a)){if(b.isArray(a))return a.slice();if(b.isArrayLike(a)){for(var c=new a.constructor(a.length),d=0;d0&&(e=1/Math.sqrt(e),a[0]=b[0]*e,a[1]=b[1]*e),a},g.dot=function(a,b){return a[0]*b[0]+a[1]*b[1]},g.cross=function(a,b,c){var d=b[0]*c[1]-b[1]*c[0];return a[0]=a[1]=0,a[2]=d,a},g.lerp=function(a,b,c,d){var e=b[0],f=b[1];return a[0]=e+d*(c[0]-e),a[1]=f+d*(c[1]-f),a},g.random=function(a,b){b=b||1;var c=2*d()*Math.PI;return a[0]=Math.cos(c)*b,a[1]=Math.sin(c)*b,a},g.transformMat2=function(a,b,c){var d=b[0],e=b[1];return a[0]=c[0]*d+c[2]*e,a[1]=c[1]*d+c[3]*e,a},g.transformMat2d=function(a,b,c){var d=b[0],e=b[1];return a[0]=c[0]*d+c[2]*e+c[4],a[1]=c[1]*d+c[3]*e+c[5],a},g.transformMat3=function(a,b,c){var d=b[0],e=b[1];return a[0]=c[0]*d+c[3]*e+c[6],a[1]=c[1]*d+c[4]*e+c[7],a},g.transformMat4=function(a,b,c){var d=b[0],e=b[1];return a[0]=c[0]*d+c[4]*e+c[12],a[1]=c[1]*d+c[5]*e+c[13],a},g.forEach=function(){var a=g.create();return function(b,c,d,e,f,g){var h,i;for(c||(c=2),d||(d=0),i=e?Math.min(e*c+d,b.length):b.length,h=d;i>h;h+=c)a[0]=b[h],a[1]=b[h+1],f(a,a,g),b[h]=a[0],b[h+1]=a[1];return b}}(),g.str=function(a){return"vec2("+a[0]+", "+a[1]+")"},"undefined"!=typeof a&&(a.vec2=g);var h={};h.create=function(){var a=new c(3);return a[0]=0,a[1]=0,a[2]=0,a},h.clone=function(a){var b=new c(3);return b[0]=a[0],b[1]=a[1],b[2]=a[2],b},h.fromValues=function(a,b,d){var e=new c(3);return e[0]=a,e[1]=b,e[2]=d,e},h.copy=function(a,b){return a[0]=b[0],a[1]=b[1],a[2]=b[2],a},h.set=function(a,b,c,d){return a[0]=b,a[1]=c,a[2]=d,a},h.add=function(a,b,c){return a[0]=b[0]+c[0],a[1]=b[1]+c[1],a[2]=b[2]+c[2],a},h.subtract=function(a,b,c){return a[0]=b[0]-c[0],a[1]=b[1]-c[1],a[2]=b[2]-c[2],a},h.sub=h.subtract,h.multiply=function(a,b,c){return a[0]=b[0]*c[0],a[1]=b[1]*c[1],a[2]=b[2]*c[2],a},h.mul=h.multiply,h.divide=function(a,b,c){return a[0]=b[0]/c[0],a[1]=b[1]/c[1],a[2]=b[2]/c[2],a},h.div=h.divide,h.min=function(a,b,c){return a[0]=Math.min(b[0],c[0]),a[1]=Math.min(b[1],c[1]),a[2]=Math.min(b[2],c[2]),a},h.max=function(a,b,c){return a[0]=Math.max(b[0],c[0]),a[1]=Math.max(b[1],c[1]),a[2]=Math.max(b[2],c[2]),a},h.scale=function(a,b,c){return a[0]=b[0]*c,a[1]=b[1]*c,a[2]=b[2]*c,a},h.scaleAndAdd=function(a,b,c,d){return a[0]=b[0]+c[0]*d,a[1]=b[1]+c[1]*d,a[2]=b[2]+c[2]*d,a},h.distance=function(a,b){var c=b[0]-a[0],d=b[1]-a[1],e=b[2]-a[2];return Math.sqrt(c*c+d*d+e*e)},h.dist=h.distance,h.squaredDistance=function(a,b){var c=b[0]-a[0],d=b[1]-a[1],e=b[2]-a[2];return c*c+d*d+e*e},h.sqrDist=h.squaredDistance,h.length=function(a){var b=a[0],c=a[1],d=a[2];return Math.sqrt(b*b+c*c+d*d)},h.len=h.length,h.squaredLength=function(a){var b=a[0],c=a[1],d=a[2];return b*b+c*c+d*d},h.sqrLen=h.squaredLength,h.negate=function(a,b){return a[0]=-b[0],a[1]=-b[1],a[2]=-b[2],a},h.normalize=function(a,b){var c=b[0],d=b[1],e=b[2],f=c*c+d*d+e*e;return f>0&&(f=1/Math.sqrt(f),a[0]=b[0]*f,a[1]=b[1]*f,a[2]=b[2]*f),a},h.dot=function(a,b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]},h.cross=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=c[0],h=c[1],i=c[2];return a[0]=e*i-f*h,a[1]=f*g-d*i,a[2]=d*h-e*g,a},h.lerp=function(a,b,c,d){var e=b[0],f=b[1],g=b[2];return a[0]=e+d*(c[0]-e),a[1]=f+d*(c[1]-f),a[2]=g+d*(c[2]-g),a},h.random=function(a,b){b=b||1;var c=2*d()*Math.PI,e=2*d()-1,f=Math.sqrt(1-e*e)*b;return a[0]=Math.cos(c)*f,a[1]=Math.sin(c)*f,a[2]=e*b,a},h.transformMat4=function(a,b,c){var d=b[0],e=b[1],f=b[2];return a[0]=c[0]*d+c[4]*e+c[8]*f+c[12],a[1]=c[1]*d+c[5]*e+c[9]*f+c[13],a[2]=c[2]*d+c[6]*e+c[10]*f+c[14],a},h.transformMat3=function(a,b,c){var d=b[0],e=b[1],f=b[2];return a[0]=d*c[0]+e*c[3]+f*c[6],a[1]=d*c[1]+e*c[4]+f*c[7],a[2]=d*c[2]+e*c[5]+f*c[8],a},h.transformQuat=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=c[0],h=c[1],i=c[2],j=c[3],k=j*d+h*f-i*e,l=j*e+i*d-g*f,m=j*f+g*e-h*d,n=-g*d-h*e-i*f;return a[0]=k*j+n*-g+l*-i-m*-h,a[1]=l*j+n*-h+m*-g-k*-i,a[2]=m*j+n*-i+k*-h-l*-g,a},h.forEach=function(){var a=h.create();return function(b,c,d,e,f,g){var h,i;for(c||(c=3),d||(d=0),i=e?Math.min(e*c+d,b.length):b.length,h=d;i>h;h+=c)a[0]=b[h],a[1]=b[h+1],a[2]=b[h+2],f(a,a,g),b[h]=a[0],b[h+1]=a[1],b[h+2]=a[2];return b}}(),h.str=function(a){return"vec3("+a[0]+", "+a[1]+", "+a[2]+")"},"undefined"!=typeof a&&(a.vec3=h);var i={};i.create=function(){var a=new c(4);return a[0]=0,a[1]=0,a[2]=0,a[3]=0,a},i.clone=function(a){var b=new c(4);return b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b},i.fromValues=function(a,b,d,e){var f=new c(4);return f[0]=a,f[1]=b,f[2]=d,f[3]=e,f},i.copy=function(a,b){return a[0]=b[0],a[1]=b[1],a[2]=b[2],a[3]=b[3],a},i.set=function(a,b,c,d,e){return a[0]=b,a[1]=c,a[2]=d,a[3]=e,a},i.add=function(a,b,c){return a[0]=b[0]+c[0],a[1]=b[1]+c[1],a[2]=b[2]+c[2],a[3]=b[3]+c[3],a},i.subtract=function(a,b,c){return a[0]=b[0]-c[0],a[1]=b[1]-c[1],a[2]=b[2]-c[2],a[3]=b[3]-c[3],a},i.sub=i.subtract,i.multiply=function(a,b,c){return a[0]=b[0]*c[0],a[1]=b[1]*c[1],a[2]=b[2]*c[2],a[3]=b[3]*c[3],a},i.mul=i.multiply,i.divide=function(a,b,c){return a[0]=b[0]/c[0],a[1]=b[1]/c[1],a[2]=b[2]/c[2],a[3]=b[3]/c[3],a},i.div=i.divide,i.min=function(a,b,c){return a[0]=Math.min(b[0],c[0]),a[1]=Math.min(b[1],c[1]),a[2]=Math.min(b[2],c[2]),a[3]=Math.min(b[3],c[3]),a},i.max=function(a,b,c){return a[0]=Math.max(b[0],c[0]),a[1]=Math.max(b[1],c[1]),a[2]=Math.max(b[2],c[2]),a[3]=Math.max(b[3],c[3]),a},i.scale=function(a,b,c){return a[0]=b[0]*c,a[1]=b[1]*c,a[2]=b[2]*c,a[3]=b[3]*c,a},i.scaleAndAdd=function(a,b,c,d){return a[0]=b[0]+c[0]*d,a[1]=b[1]+c[1]*d,a[2]=b[2]+c[2]*d,a[3]=b[3]+c[3]*d,a},i.distance=function(a,b){var c=b[0]-a[0],d=b[1]-a[1],e=b[2]-a[2],f=b[3]-a[3];return Math.sqrt(c*c+d*d+e*e+f*f)},i.dist=i.distance,i.squaredDistance=function(a,b){var c=b[0]-a[0],d=b[1]-a[1],e=b[2]-a[2],f=b[3]-a[3];return c*c+d*d+e*e+f*f},i.sqrDist=i.squaredDistance,i.length=function(a){var b=a[0],c=a[1],d=a[2],e=a[3];return Math.sqrt(b*b+c*c+d*d+e*e)},i.len=i.length,i.squaredLength=function(a){var b=a[0],c=a[1],d=a[2],e=a[3];return b*b+c*c+d*d+e*e},i.sqrLen=i.squaredLength,i.negate=function(a,b){return a[0]=-b[0],a[1]=-b[1],a[2]=-b[2],a[3]=-b[3],a},i.normalize=function(a,b){var c=b[0],d=b[1],e=b[2],f=b[3],g=c*c+d*d+e*e+f*f;return g>0&&(g=1/Math.sqrt(g),a[0]=b[0]*g,a[1]=b[1]*g,a[2]=b[2]*g,a[3]=b[3]*g),a},i.dot=function(a,b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3]},i.lerp=function(a,b,c,d){var e=b[0],f=b[1],g=b[2],h=b[3];return a[0]=e+d*(c[0]-e),a[1]=f+d*(c[1]-f),a[2]=g+d*(c[2]-g),a[3]=h+d*(c[3]-h),a},i.random=function(a,b){return b=b||1,a[0]=d(),a[1]=d(),a[2]=d(),a[3]=d(),i.normalize(a,a),i.scale(a,a,b),a},i.transformMat4=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3];return a[0]=c[0]*d+c[4]*e+c[8]*f+c[12]*g,a[1]=c[1]*d+c[5]*e+c[9]*f+c[13]*g,a[2]=c[2]*d+c[6]*e+c[10]*f+c[14]*g,a[3]=c[3]*d+c[7]*e+c[11]*f+c[15]*g,a},i.transformQuat=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=c[0],h=c[1],i=c[2],j=c[3],k=j*d+h*f-i*e,l=j*e+i*d-g*f,m=j*f+g*e-h*d,n=-g*d-h*e-i*f;return a[0]=k*j+n*-g+l*-i-m*-h,a[1]=l*j+n*-h+m*-g-k*-i,a[2]=m*j+n*-i+k*-h-l*-g,a},i.forEach=function(){var a=i.create();return function(b,c,d,e,f,g){var h,i;for(c||(c=4),d||(d=0),i=e?Math.min(e*c+d,b.length):b.length,h=d;i>h;h+=c)a[0]=b[h],a[1]=b[h+1],a[2]=b[h+2],a[3]=b[h+3],f(a,a,g),b[h]=a[0],b[h+1]=a[1],b[h+2]=a[2],b[h+3]=a[3];return b}}(),i.str=function(a){return"vec4("+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+")"},"undefined"!=typeof a&&(a.vec4=i);var j={};j.create=function(){var a=new c(4);return a[0]=1,a[1]=0,a[2]=0,a[3]=1,a},j.clone=function(a){var b=new c(4);return b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b},j.copy=function(a,b){return a[0]=b[0],a[1]=b[1],a[2]=b[2],a[3]=b[3],a},j.identity=function(a){return a[0]=1,a[1]=0,a[2]=0,a[3]=1,a},j.transpose=function(a,b){if(a===b){var c=b[1];a[1]=b[2],a[2]=c}else a[0]=b[0],a[1]=b[2],a[2]=b[1],a[3]=b[3];return a},j.invert=function(a,b){var c=b[0],d=b[1],e=b[2],f=b[3],g=c*f-e*d;return g?(g=1/g,a[0]=f*g,a[1]=-d*g,a[2]=-e*g,a[3]=c*g,a):null},j.adjoint=function(a,b){var c=b[0];return a[0]=b[3],a[1]=-b[1],a[2]=-b[2],a[3]=c,a},j.determinant=function(a){return a[0]*a[3]-a[2]*a[1]},j.multiply=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3],h=c[0],i=c[1],j=c[2],k=c[3];return a[0]=d*h+e*j,a[1]=d*i+e*k,a[2]=f*h+g*j,a[3]=f*i+g*k,a},j.mul=j.multiply,j.rotate=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3],h=Math.sin(c),i=Math.cos(c);return a[0]=d*i+e*h,a[1]=d*-h+e*i,a[2]=f*i+g*h,a[3]=f*-h+g*i,a},j.scale=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3],h=c[0],i=c[1];return a[0]=d*h,a[1]=e*i,a[2]=f*h,a[3]=g*i,a},j.str=function(a){return"mat2("+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+")"},"undefined"!=typeof a&&(a.mat2=j);var k={};k.create=function(){var a=new c(6);return a[0]=1,a[1]=0,a[2]=0,a[3]=1,a[4]=0,a[5]=0,a},k.clone=function(a){var b=new c(6);return b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b[4]=a[4],b[5]=a[5],b},k.copy=function(a,b){return a[0]=b[0],a[1]=b[1],a[2]=b[2],a[3]=b[3],a[4]=b[4],a[5]=b[5],a},k.identity=function(a){return a[0]=1,a[1]=0,a[2]=0,a[3]=1,a[4]=0,a[5]=0,a},k.invert=function(a,b){var c=b[0],d=b[1],e=b[2],f=b[3],g=b[4],h=b[5],i=c*f-d*e;return i?(i=1/i,a[0]=f*i,a[1]=-d*i,a[2]=-e*i,a[3]=c*i,a[4]=(e*h-f*g)*i,a[5]=(d*g-c*h)*i,a):null},k.determinant=function(a){return a[0]*a[3]-a[1]*a[2]},k.multiply=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3],h=b[4],i=b[5],j=c[0],k=c[1],l=c[2],m=c[3],n=c[4],o=c[5];return a[0]=d*j+e*l,a[1]=d*k+e*m,a[2]=f*j+g*l,a[3]=f*k+g*m,a[4]=j*h+l*i+n,a[5]=k*h+m*i+o,a},k.mul=k.multiply,k.rotate=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3],h=b[4],i=b[5],j=Math.sin(c),k=Math.cos(c);return a[0]=d*k+e*j,a[1]=-d*j+e*k,a[2]=f*k+g*j,a[3]=-f*j+k*g,a[4]=k*h+j*i,a[5]=k*i-j*h,a},k.scale=function(a,b,c){var d=c[0],e=c[1];return a[0]=b[0]*d,a[1]=b[1]*e,a[2]=b[2]*d,a[3]=b[3]*e,a[4]=b[4]*d,a[5]=b[5]*e,a},k.translate=function(a,b,c){return a[0]=b[0],a[1]=b[1],a[2]=b[2],a[3]=b[3],a[4]=b[4]+c[0],a[5]=b[5]+c[1],a},k.str=function(a){return"mat2d("+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+", "+a[4]+", "+a[5]+")"},"undefined"!=typeof a&&(a.mat2d=k);var l={};l.create=function(){var a=new c(9);return a[0]=1,a[1]=0,a[2]=0,a[3]=0,a[4]=1,a[5]=0,a[6]=0,a[7]=0,a[8]=1,a},l.fromMat4=function(a,b){return a[0]=b[0],a[1]=b[1],a[2]=b[2],a[3]=b[4],a[4]=b[5],a[5]=b[6],a[6]=b[8],a[7]=b[9],a[8]=b[10],a},l.clone=function(a){var b=new c(9);return b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b[4]=a[4],b[5]=a[5],b[6]=a[6],b[7]=a[7],b[8]=a[8],b},l.copy=function(a,b){return a[0]=b[0],a[1]=b[1],a[2]=b[2],a[3]=b[3],a[4]=b[4],a[5]=b[5],a[6]=b[6],a[7]=b[7],a[8]=b[8],a},l.identity=function(a){return a[0]=1,a[1]=0,a[2]=0,a[3]=0,a[4]=1,a[5]=0,a[6]=0,a[7]=0,a[8]=1,a},l.transpose=function(a,b){if(a===b){var c=b[1],d=b[2],e=b[5];a[1]=b[3],a[2]=b[6],a[3]=c,a[5]=b[7],a[6]=d,a[7]=e}else a[0]=b[0],a[1]=b[3],a[2]=b[6],a[3]=b[1],a[4]=b[4],a[5]=b[7],a[6]=b[2],a[7]=b[5],a[8]=b[8];return a},l.invert=function(a,b){var c=b[0],d=b[1],e=b[2],f=b[3],g=b[4],h=b[5],i=b[6],j=b[7],k=b[8],l=k*g-h*j,m=-k*f+h*i,n=j*f-g*i,o=c*l+d*m+e*n;return o?(o=1/o,a[0]=l*o,a[1]=(-k*d+e*j)*o,a[2]=(h*d-e*g)*o,a[3]=m*o,a[4]=(k*c-e*i)*o,a[5]=(-h*c+e*f)*o,a[6]=n*o,a[7]=(-j*c+d*i)*o,a[8]=(g*c-d*f)*o,a):null},l.adjoint=function(a,b){var c=b[0],d=b[1],e=b[2],f=b[3],g=b[4],h=b[5],i=b[6],j=b[7],k=b[8];return a[0]=g*k-h*j,a[1]=e*j-d*k,a[2]=d*h-e*g,a[3]=h*i-f*k,a[4]=c*k-e*i,a[5]=e*f-c*h,a[6]=f*j-g*i,a[7]=d*i-c*j,a[8]=c*g-d*f,a},l.determinant=function(a){var b=a[0],c=a[1],d=a[2],e=a[3],f=a[4],g=a[5],h=a[6],i=a[7],j=a[8];return b*(j*f-g*i)+c*(-j*e+g*h)+d*(i*e-f*h)},l.multiply=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3],h=b[4],i=b[5],j=b[6],k=b[7],l=b[8],m=c[0],n=c[1],o=c[2],p=c[3],q=c[4],r=c[5],s=c[6],t=c[7],u=c[8];return a[0]=m*d+n*g+o*j,a[1]=m*e+n*h+o*k,a[2]=m*f+n*i+o*l,a[3]=p*d+q*g+r*j,a[4]=p*e+q*h+r*k,a[5]=p*f+q*i+r*l,a[6]=s*d+t*g+u*j,a[7]=s*e+t*h+u*k,a[8]=s*f+t*i+u*l,a},l.mul=l.multiply,l.translate=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3],h=b[4],i=b[5],j=b[6],k=b[7],l=b[8],m=c[0],n=c[1];return a[0]=d,a[1]=e,a[2]=f,a[3]=g,a[4]=h,a[5]=i,a[6]=m*d+n*g+j,a[7]=m*e+n*h+k,a[8]=m*f+n*i+l,a},l.rotate=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3],h=b[4],i=b[5],j=b[6],k=b[7],l=b[8],m=Math.sin(c),n=Math.cos(c);return a[0]=n*d+m*g,a[1]=n*e+m*h,a[2]=n*f+m*i,a[3]=n*g-m*d,a[4]=n*h-m*e,a[5]=n*i-m*f,a[6]=j,a[7]=k,a[8]=l,a},l.scale=function(a,b,c){var d=c[0],e=c[1];return a[0]=d*b[0],a[1]=d*b[1],a[2]=d*b[2],a[3]=e*b[3],a[4]=e*b[4],a[5]=e*b[5],a[6]=b[6],a[7]=b[7],a[8]=b[8],a},l.fromMat2d=function(a,b){return a[0]=b[0],a[1]=b[1],a[2]=0,a[3]=b[2],a[4]=b[3],a[5]=0,a[6]=b[4],a[7]=b[5],a[8]=1,a},l.fromQuat=function(a,b){var c=b[0],d=b[1],e=b[2],f=b[3],g=c+c,h=d+d,i=e+e,j=c*g,k=d*g,l=d*h,m=e*g,n=e*h,o=e*i,p=f*g,q=f*h,r=f*i;return a[0]=1-l-o,a[3]=k-r,a[6]=m+q,a[1]=k+r,a[4]=1-j-o,a[7]=n-p,a[2]=m-q,a[5]=n+p,a[8]=1-j-l,a},l.normalFromMat4=function(a,b){var c=b[0],d=b[1],e=b[2],f=b[3],g=b[4],h=b[5],i=b[6],j=b[7],k=b[8],l=b[9],m=b[10],n=b[11],o=b[12],p=b[13],q=b[14],r=b[15],s=c*h-d*g,t=c*i-e*g,u=c*j-f*g,v=d*i-e*h,w=d*j-f*h,x=e*j-f*i,y=k*p-l*o,z=k*q-m*o,A=k*r-n*o,B=l*q-m*p,C=l*r-n*p,D=m*r-n*q,E=s*D-t*C+u*B+v*A-w*z+x*y;return E?(E=1/E,a[0]=(h*D-i*C+j*B)*E,a[1]=(i*A-g*D-j*z)*E,a[2]=(g*C-h*A+j*y)*E,a[3]=(e*C-d*D-f*B)*E,a[4]=(c*D-e*A+f*z)*E,a[5]=(d*A-c*C-f*y)*E,a[6]=(p*x-q*w+r*v)*E,a[7]=(q*u-o*x-r*t)*E,a[8]=(o*w-p*u+r*s)*E,a):null},l.str=function(a){return"mat3("+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+", "+a[4]+", "+a[5]+", "+a[6]+", "+a[7]+", "+a[8]+")"},"undefined"!=typeof a&&(a.mat3=l);var m={};m.create=function(){var a=new c(16);return a[0]=1,a[1]=0,a[2]=0,a[3]=0,a[4]=0,a[5]=1,a[6]=0,a[7]=0,a[8]=0,a[9]=0,a[10]=1,a[11]=0,a[12]=0,a[13]=0,a[14]=0,a[15]=1,a},m.clone=function(a){var b=new c(16);return b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b[4]=a[4],b[5]=a[5],b[6]=a[6],b[7]=a[7],b[8]=a[8],b[9]=a[9],b[10]=a[10],b[11]=a[11],b[12]=a[12],b[13]=a[13],b[14]=a[14],b[15]=a[15],b},m.copy=function(a,b){return a[0]=b[0],a[1]=b[1],a[2]=b[2],a[3]=b[3],a[4]=b[4],a[5]=b[5],a[6]=b[6],a[7]=b[7],a[8]=b[8],a[9]=b[9],a[10]=b[10],a[11]=b[11],a[12]=b[12],a[13]=b[13],a[14]=b[14],a[15]=b[15],a},m.identity=function(a){return a[0]=1,a[1]=0,a[2]=0,a[3]=0,a[4]=0,a[5]=1,a[6]=0,a[7]=0,a[8]=0,a[9]=0,a[10]=1,a[11]=0,a[12]=0,a[13]=0,a[14]=0,a[15]=1,a},m.transpose=function(a,b){if(a===b){var c=b[1],d=b[2],e=b[3],f=b[6],g=b[7],h=b[11];a[1]=b[4],a[2]=b[8],a[3]=b[12],a[4]=c,a[6]=b[9],a[7]=b[13],a[8]=d,a[9]=f,a[11]=b[14],a[12]=e,a[13]=g,a[14]=h}else a[0]=b[0],a[1]=b[4],a[2]=b[8],a[3]=b[12],a[4]=b[1],a[5]=b[5],a[6]=b[9],a[7]=b[13],a[8]=b[2],a[9]=b[6],a[10]=b[10],a[11]=b[14],a[12]=b[3],a[13]=b[7],a[14]=b[11],a[15]=b[15];return a},m.invert=function(a,b){var c=b[0],d=b[1],e=b[2],f=b[3],g=b[4],h=b[5],i=b[6],j=b[7],k=b[8],l=b[9],m=b[10],n=b[11],o=b[12],p=b[13],q=b[14],r=b[15],s=c*h-d*g,t=c*i-e*g,u=c*j-f*g,v=d*i-e*h,w=d*j-f*h,x=e*j-f*i,y=k*p-l*o,z=k*q-m*o,A=k*r-n*o,B=l*q-m*p,C=l*r-n*p,D=m*r-n*q,E=s*D-t*C+u*B+v*A-w*z+x*y;return E?(E=1/E,a[0]=(h*D-i*C+j*B)*E,a[1]=(e*C-d*D-f*B)*E,a[2]=(p*x-q*w+r*v)*E,a[3]=(m*w-l*x-n*v)*E,a[4]=(i*A-g*D-j*z)*E,a[5]=(c*D-e*A+f*z)*E,a[6]=(q*u-o*x-r*t)*E,a[7]=(k*x-m*u+n*t)*E,a[8]=(g*C-h*A+j*y)*E,a[9]=(d*A-c*C-f*y)*E,a[10]=(o*w-p*u+r*s)*E,a[11]=(l*u-k*w-n*s)*E,a[12]=(h*z-g*B-i*y)*E,a[13]=(c*B-d*z+e*y)*E,a[14]=(p*t-o*v-q*s)*E,a[15]=(k*v-l*t+m*s)*E,a):null},m.adjoint=function(a,b){var c=b[0],d=b[1],e=b[2],f=b[3],g=b[4],h=b[5],i=b[6],j=b[7],k=b[8],l=b[9],m=b[10],n=b[11],o=b[12],p=b[13],q=b[14],r=b[15];return a[0]=h*(m*r-n*q)-l*(i*r-j*q)+p*(i*n-j*m),a[1]=-(d*(m*r-n*q)-l*(e*r-f*q)+p*(e*n-f*m)),a[2]=d*(i*r-j*q)-h*(e*r-f*q)+p*(e*j-f*i),a[3]=-(d*(i*n-j*m)-h*(e*n-f*m)+l*(e*j-f*i)),a[4]=-(g*(m*r-n*q)-k*(i*r-j*q)+o*(i*n-j*m)),a[5]=c*(m*r-n*q)-k*(e*r-f*q)+o*(e*n-f*m),a[6]=-(c*(i*r-j*q)-g*(e*r-f*q)+o*(e*j-f*i)),a[7]=c*(i*n-j*m)-g*(e*n-f*m)+k*(e*j-f*i),a[8]=g*(l*r-n*p)-k*(h*r-j*p)+o*(h*n-j*l),a[9]=-(c*(l*r-n*p)-k*(d*r-f*p)+o*(d*n-f*l)),a[10]=c*(h*r-j*p)-g*(d*r-f*p)+o*(d*j-f*h),a[11]=-(c*(h*n-j*l)-g*(d*n-f*l)+k*(d*j-f*h)),a[12]=-(g*(l*q-m*p)-k*(h*q-i*p)+o*(h*m-i*l)),a[13]=c*(l*q-m*p)-k*(d*q-e*p)+o*(d*m-e*l),a[14]=-(c*(h*q-i*p)-g*(d*q-e*p)+o*(d*i-e*h)),a[15]=c*(h*m-i*l)-g*(d*m-e*l)+k*(d*i-e*h),a},m.determinant=function(a){var b=a[0],c=a[1],d=a[2],e=a[3],f=a[4],g=a[5],h=a[6],i=a[7],j=a[8],k=a[9],l=a[10],m=a[11],n=a[12],o=a[13],p=a[14],q=a[15],r=b*g-c*f,s=b*h-d*f,t=b*i-e*f,u=c*h-d*g,v=c*i-e*g,w=d*i-e*h,x=j*o-k*n,y=j*p-l*n,z=j*q-m*n,A=k*p-l*o,B=k*q-m*o,C=l*q-m*p;return r*C-s*B+t*A+u*z-v*y+w*x},m.multiply=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3],h=b[4],i=b[5],j=b[6],k=b[7],l=b[8],m=b[9],n=b[10],o=b[11],p=b[12],q=b[13],r=b[14],s=b[15],t=c[0],u=c[1],v=c[2],w=c[3];return a[0]=t*d+u*h+v*l+w*p,a[1]=t*e+u*i+v*m+w*q,a[2]=t*f+u*j+v*n+w*r,a[3]=t*g+u*k+v*o+w*s,t=c[4],u=c[5],v=c[6],w=c[7],a[4]=t*d+u*h+v*l+w*p,a[5]=t*e+u*i+v*m+w*q,a[6]=t*f+u*j+v*n+w*r,a[7]=t*g+u*k+v*o+w*s,t=c[8],u=c[9],v=c[10],w=c[11],a[8]=t*d+u*h+v*l+w*p,a[9]=t*e+u*i+v*m+w*q,a[10]=t*f+u*j+v*n+w*r,a[11]=t*g+u*k+v*o+w*s,t=c[12],u=c[13],v=c[14],w=c[15],a[12]=t*d+u*h+v*l+w*p,a[13]=t*e+u*i+v*m+w*q,a[14]=t*f+u*j+v*n+w*r,a[15]=t*g+u*k+v*o+w*s,a},m.mul=m.multiply,m.translate=function(a,b,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t=c[0],u=c[1],v=c[2];return d=b[0],e=b[1],f=b[2],g=b[3],h=b[4],i=b[5],j=b[6],k=b[7],l=b[8],m=b[9],n=b[10],o=b[11],p=b[12],q=b[13],r=b[14],s=b[15],a[0]=d+g*t,a[1]=e+g*u,a[2]=f+g*v,a[3]=g,a[4]=h+k*t,a[5]=i+k*u,a[6]=j+k*v,a[7]=k,a[8]=l+o*t,a[9]=m+o*u,a[10]=n+o*v,a[11]=o,a[12]=p+s*t,a[13]=q+s*u,a[14]=r+s*v,a[15]=s,a},m.scale=function(a,b,c){var d=c[0],e=c[1],f=c[2];return a[0]=b[0]*d,a[1]=b[1]*d,a[2]=b[2]*d,a[3]=b[3]*d,a[4]=b[4]*e,a[5]=b[5]*e,a[6]=b[6]*e,a[7]=b[7]*e,a[8]=b[8]*f,a[9]=b[9]*f,a[10]=b[10]*f,a[11]=b[11]*f,a[12]=b[12],a[13]=b[13],a[14]=b[14],a[15]=b[15],a},m.rotate=function(a,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D=e[0],E=e[1],F=e[2],G=Math.sqrt(D*D+E*E+F*F);return Math.abs(G)g?(h.cross(a,b,e),h.length(a)<1e-6&&h.cross(a,c,e),h.normalize(a,a),n.setAxisAngle(d,a,Math.PI),d):g>.999999?(d[0]=0,d[1]=0,d[2]=0,d[3]=1,d):(h.cross(a,e,f),d[0]=a[0],d[1]=a[1],d[2]=a[2],d[3]=1+g,n.normalize(d,d))}}(),n.setAxes=function(){var a=l.create();return function(b,c,d,e){return a[0]=d[0],a[3]=d[1],a[6]=d[2],a[1]=e[0],a[4]=e[1],a[7]=e[2],a[2]=-c[0],a[5]=-c[1],a[8]=-c[2],n.normalize(b,n.fromMat3(b,a))}}(),n.clone=i.clone,n.fromValues=i.fromValues,n.copy=i.copy,n.set=i.set,n.identity=function(a){return a[0]=0,a[1]=0,a[2]=0,a[3]=1,a},n.setAxisAngle=function(a,b,c){c=.5*c;var d=Math.sin(c);return a[0]=d*b[0],a[1]=d*b[1],a[2]=d*b[2],a[3]=Math.cos(c),a},n.add=i.add,n.multiply=function(a,b,c){var d=b[0],e=b[1],f=b[2],g=b[3],h=c[0],i=c[1],j=c[2],k=c[3];return a[0]=d*k+g*h+e*j-f*i,a[1]=e*k+g*i+f*h-d*j,a[2]=f*k+g*j+d*i-e*h,a[3]=g*k-d*h-e*i-f*j,a},n.mul=n.multiply,n.scale=i.scale,n.rotateX=function(a,b,c){c*=.5;var d=b[0],e=b[1],f=b[2],g=b[3],h=Math.sin(c),i=Math.cos(c);return a[0]=d*i+g*h,a[1]=e*i+f*h,a[2]=f*i-e*h,a[3]=g*i-d*h,a},n.rotateY=function(a,b,c){c*=.5;var d=b[0],e=b[1],f=b[2],g=b[3],h=Math.sin(c),i=Math.cos(c);return a[0]=d*i-f*h,a[1]=e*i+g*h,a[2]=f*i+d*h,a[3]=g*i-e*h,a},n.rotateZ=function(a,b,c){c*=.5;var d=b[0],e=b[1],f=b[2],g=b[3],h=Math.sin(c),i=Math.cos(c);return a[0]=d*i+e*h,a[1]=e*i-d*h,a[2]=f*i+g*h,a[3]=g*i-f*h,a},n.calculateW=function(a,b){var c=b[0],d=b[1],e=b[2];return a[0]=c,a[1]=d,a[2]=e,a[3]=-Math.sqrt(Math.abs(1-c*c-d*d-e*e)),a},n.dot=i.dot,n.lerp=i.lerp,n.slerp=function(a,b,c,d){var e,f,g,h,i,j=b[0],k=b[1],l=b[2],m=b[3],n=c[0],o=c[1],p=c[2],q=c[3];return f=j*n+k*o+l*p+m*q,0>f&&(f=-f,n=-n,o=-o,p=-p,q=-q),1-f>1e-6?(e=Math.acos(f),g=Math.sin(e),h=Math.sin((1-d)*e)/g,i=Math.sin(d*e)/g):(h=1-d,i=d),a[0]=h*j+i*n,a[1]=h*k+i*o,a[2]=h*l+i*p,a[3]=h*m+i*q,a},n.invert=function(a,b){var c=b[0],d=b[1],e=b[2],f=b[3],g=c*c+d*d+e*e+f*f,h=g?1/g:0;return a[0]=-c*h,a[1]=-d*h,a[2]=-e*h,a[3]=f*h,a},n.conjugate=function(a,b){return a[0]=-b[0],a[1]=-b[1],a[2]=-b[2],a[3]=b[3],a},n.length=i.length,n.len=n.length,n.squaredLength=i.squaredLength,n.sqrLen=n.squaredLength,n.normalize=i.normalize,n.fromMat3=function(a,b){var c,d=b[0]+b[4]+b[8];if(d>0)c=Math.sqrt(d+1),a[3]=.5*c,c=.5/c,a[0]=(b[7]-b[5])*c,a[1]=(b[2]-b[6])*c,a[2]=(b[3]-b[1])*c;else{var e=0; -b[4]>b[0]&&(e=1),b[8]>b[3*e+e]&&(e=2);var f=(e+1)%3,g=(e+2)%3;c=Math.sqrt(b[3*e+e]-b[3*f+f]-b[3*g+g]+1),a[e]=.5*c,c=.5/c,a[3]=(b[3*g+f]-b[3*f+g])*c,a[f]=(b[3*f+e]+b[3*e+f])*c,a[g]=(b[3*g+e]+b[3*e+g])*c}return a},n.str=function(a){return"quat("+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+")"},"undefined"!=typeof a&&(a.quat=n)}(b.exports)}(this),d("qtek/math/Vector3",["require","../dep/glmatrix"],function(a){function b(a,b,c){return b>a?b:a>c?c:a}var c=a("../dep/glmatrix"),d=c.vec3,e=function(a,b,c){a=a||0,b=b||0,c=c||0,this._array=d.fromValues(a,b,c),this._dirty=!0};if(e.prototype={constructor:e,add:function(a){return d.add(this._array,this._array,a._array),this._dirty=!0,this},set:function(a,b,c){return this._array[0]=a,this._array[1]=b,this._array[2]=c,this._dirty=!0,this},setArray:function(a){return this._array[0]=a[0],this._array[1]=a[1],this._array[2]=a[2],this._dirty=!0,this},clone:function(){return new e(this.x,this.y,this.z)},copy:function(a){return d.copy(this._array,a._array),this._dirty=!0,this},cross:function(a,b){return d.cross(this._array,a._array,b._array),this._dirty=!0,this},dist:function(a){return d.dist(this._array,a._array)},distance:function(a){return d.distance(this._array,a._array)},div:function(a){return d.div(this._array,this._array,a._array),this._dirty=!0,this},divide:function(a){return d.divide(this._array,this._array,a._array),this._dirty=!0,this},dot:function(a){return d.dot(this._array,a._array)},len:function(){return d.len(this._array)},length:function(){return d.length(this._array)},lerp:function(a,b,c){return d.lerp(this._array,a._array,b._array,c),this._dirty=!0,this},min:function(a){return d.min(this._array,this._array,a._array),this._dirty=!0,this},max:function(a){return d.max(this._array,this._array,a._array),this._dirty=!0,this},mul:function(a){return d.mul(this._array,this._array,a._array),this._dirty=!0,this},multiply:function(a){return d.multiply(this._array,this._array,a._array),this._dirty=!0,this},negate:function(){return d.negate(this._array,this._array),this._dirty=!0,this},normalize:function(){return d.normalize(this._array,this._array),this._dirty=!0,this},random:function(a){return d.random(this._array,a),this._dirty=!0,this},scale:function(a){return d.scale(this._array,this._array,a),this._dirty=!0,this},scaleAndAdd:function(a,b){return d.scaleAndAdd(this._array,this._array,a._array,b),this._dirty=!0,this},sqrDist:function(a){return d.sqrDist(this._array,a._array)},squaredDistance:function(a){return d.squaredDistance(this._array,a._array)},sqrLen:function(){return d.sqrLen(this._array)},squaredLength:function(){return d.squaredLength(this._array)},sub:function(a){return d.sub(this._array,this._array,a._array),this._dirty=!0,this},subtract:function(a){return d.subtract(this._array,this._array,a._array),this._dirty=!0,this},transformMat3:function(a){return d.transformMat3(this._array,this._array,a._array),this._dirty=!0,this},transformMat4:function(a){return d.transformMat4(this._array,this._array,a._array),this._dirty=!0,this},transformQuat:function(a){return d.transformQuat(this._array,this._array,a._array),this._dirty=!0,this},applyProjection:function(a){var b=this._array;if(a=a._array,0===a[15]){var c=-1/b[2];b[0]=a[0]*b[0]*c,b[1]=a[5]*b[1]*c,b[2]=(a[10]*b[2]+a[14])*c}else b[0]=a[0]*b[0]+a[12],b[1]=a[5]*b[1]+a[13],b[2]=a[10]*b[2]+a[14];return this._dirty=!0,this},eulerFromQuaternion:function(a,b){e.eulerFromQuaternion(this,a,b)},toString:function(){return"["+Array.prototype.join.call(this._array,",")+"]"}},Object.defineProperty){var f=e.prototype;Object.defineProperty(f,"x",{get:function(){return this._array[0]},set:function(a){this._array[0]=a,this._dirty=!0}}),Object.defineProperty(f,"y",{get:function(){return this._array[1]},set:function(a){this._array[1]=a,this._dirty=!0}}),Object.defineProperty(f,"z",{get:function(){return this._array[2]},set:function(a){this._array[2]=a,this._dirty=!0}})}return e.add=function(a,b,c){return d.add(a._array,b._array,c._array),a._dirty=!0,a},e.set=function(a,b,c,e){d.set(a._array,b,c,e),a._dirty=!0},e.copy=function(a,b){return d.copy(a._array,b._array),a._dirty=!0,a},e.cross=function(a,b,c){return d.cross(a._array,b._array,c._array),a._dirty=!0,a},e.dist=function(a,b){return d.distance(a._array,b._array)},e.distance=e.dist,e.div=function(a,b,c){return d.divide(a._array,b._array,c._array),a._dirty=!0,a},e.divide=e.div,e.dot=function(a,b){return d.dot(a._array,b._array)},e.len=function(a){return d.length(a._array)},e.lerp=function(a,b,c,e){return d.lerp(a._array,b._array,c._array,e),a._dirty=!0,a},e.min=function(a,b,c){return d.min(a._array,b._array,c._array),a._dirty=!0,a},e.max=function(a,b,c){return d.max(a._array,b._array,c._array),a._dirty=!0,a},e.mul=function(a,b,c){return d.multiply(a._array,b._array,c._array),a._dirty=!0,a},e.multiply=e.mul,e.negate=function(a,b){return d.negate(a._array,b._array),a._dirty=!0,a},e.normalize=function(a,b){return d.normalize(a._array,b._array),a._dirty=!0,a},e.random=function(a,b){return d.random(a._array,b),a._dirty=!0,a},e.scale=function(a,b,c){return d.scale(a._array,b._array,c),a._dirty=!0,a},e.scaleAndAdd=function(a,b,c,e){return d.scaleAndAdd(a._array,b._array,c._array,e),a._dirty=!0,a},e.sqrDist=function(a,b){return d.sqrDist(a._array,b._array)},e.squaredDistance=e.sqrDist,e.sqrLen=function(a){return d.sqrLen(a._array)},e.squaredLength=e.sqrLen,e.sub=function(a,b,c){return d.subtract(a._array,b._array,c._array),a._dirty=!0,a},e.subtract=e.sub,e.transformMat3=function(a,b,c){return d.transformMat3(a._array,b._array,c._array),a._dirty=!0,a},e.transformMat4=function(a,b,c){return d.transformMat4(a._array,b._array,c._array),a._dirty=!0,a},e.transformQuat=function(a,b,c){return d.transformQuat(a._array,b._array,c._array),a._dirty=!0,a},e.eulerFromQuaternion=function(a,c,d){a=a._array,c=c._array;var e=c[0],f=c[1],g=c[2],h=c[3],i=e*e,j=f*f,k=g*g,l=h*h,m=Math.atan2,n=Math.asin;switch(d&&d.toUpperCase()){case"YXZ":a[0]=n(b(2*(e*h-f*g),-1,1)),a[1]=m(2*(e*g+f*h),l-i-j+k),a[2]=m(2*(e*f+g*h),l-i+j-k);break;case"ZXY":a[0]=n(b(2*(e*h+f*g),-1,1)),a[1]=m(2*(f*h-g*e),l-i-j+k),a[2]=m(2*(g*h-e*f),l-i+j-k);break;case"ZYX":a[0]=m(2*(e*h+g*f),l-i-j+k),a[1]=n(b(2*(f*h-e*g),-1,1)),a[2]=m(2*(e*f+g*h),l+i-j-k);break;case"YZX":a[0]=m(2*(e*h-g*f),l-i+j-k),a[1]=m(2*(f*h-e*g),l+i-j-k),a[2]=n(b(2*(e*f+g*h),-1,1));break;case"XZY":a[0]=m(2*(e*h+f*g),l-i+j-k),a[1]=m(2*(e*g+f*h),l+i-j-k),a[2]=n(b(2*(g*h-e*f),-1,1));break;case"XYZ":default:a[0]=m(2*(e*h-f*g),l-i-j+k),a[1]=n(b(2*(e*g+f*h),-1,1)),a[2]=m(2*(g*h-e*f),l+i-j-k)}return a._dirty=!0,a},e.POSITIVE_X=new e(1,0,0),e.NEGATIVE_X=new e(-1,0,0),e.POSITIVE_Y=new e(0,1,0),e.NEGATIVE_Y=new e(0,-1,0),e.POSITIVE_Z=new e(0,0,1),e.NEGATIVE_Z=new e(0,0,-1),e.UP=new e(0,1,0),e.ZERO=new e(0,0,0),e}),d("qtek/math/Quaternion",["require","../dep/glmatrix"],function(a){var b=a("../dep/glmatrix"),c=b.quat,d=function(a,b,d,e){a=a||0,b=b||0,d=d||0,e=void 0===e?1:e,this._array=c.fromValues(a,b,d,e),this._dirty=!0};if(d.prototype={constructor:d,add:function(a){return c.add(this._array,this._array,a._array),this._dirty=!0,this},calculateW:function(){return c.calculateW(this._array,this._array),this._dirty=!0,this},set:function(a,b,c,d){return this._array[0]=a,this._array[1]=b,this._array[2]=c,this._array[3]=d,this._dirty=!0,this},setArray:function(a){return this._array[0]=a[0],this._array[1]=a[1],this._array[2]=a[2],this._array[3]=a[3],this._dirty=!0,this},clone:function(){return new d(this.x,this.y,this.z,this.w)},conjugate:function(){return c.conjugate(this._array,this._array),this._dirty=!0,this},copy:function(a){return c.copy(this._array,a._array),this._dirty=!0,this},dot:function(a){return c.dot(this._array,a._array)},fromMat3:function(a){return c.fromMat3(this._array,a._array),this._dirty=!0,this},fromMat4:function(){var a=b.mat3,d=a.create();return function(b){return a.fromMat4(d,b._array),a.transpose(d,d),c.fromMat3(this._array,d),this._dirty=!0,this}}(),identity:function(){return c.identity(this._array),this._dirty=!0,this},invert:function(){return c.invert(this._array,this._array),this._dirty=!0,this},len:function(){return c.len(this._array)},length:function(){return c.length(this._array)},lerp:function(a,b,d){return c.lerp(this._array,a._array,b._array,d),this._dirty=!0,this},mul:function(a){return c.mul(this._array,this._array,a._array),this._dirty=!0,this},mulLeft:function(a){return c.multiply(this._array,a._array,this._array),this._dirty=!0,this},multiply:function(a){return c.multiply(this._array,this._array,a._array),this._dirty=!0,this},multiplyLeft:function(a){return c.multiply(this._array,a._array,this._array),this._dirty=!0,this},normalize:function(){return c.normalize(this._array,this._array),this._dirty=!0,this},rotateX:function(a){return c.rotateX(this._array,this._array,a),this._dirty=!0,this},rotateY:function(a){return c.rotateY(this._array,this._array,a),this._dirty=!0,this},rotateZ:function(a){return c.rotateZ(this._array,this._array,a),this._dirty=!0,this},rotationTo:function(a,b){return c.rotationTo(this._array,a._array,b._array),this._dirty=!0,this},setAxes:function(a,b,d){return c.setAxes(this._array,a._array,b._array,d._array),this._dirty=!0,this},setAxisAngle:function(a,b){return c.setAxisAngle(this._array,a._array,b),this._dirty=!0,this},slerp:function(a,b,d){return c.slerp(this._array,a._array,b._array,d),this._dirty=!0,this},sqrLen:function(){return c.sqrLen(this._array)},squaredLength:function(){return c.squaredLength(this._array)},setFromEuler:function(){},toString:function(){return"["+Array.prototype.join.call(this._array,",")+"]"}},Object.defineProperty){var e=d.prototype;Object.defineProperty(e,"x",{get:function(){return this._array[0]},set:function(a){this._array[0]=a,this._dirty=!0}}),Object.defineProperty(e,"y",{get:function(){return this._array[1]},set:function(a){this._array[1]=a,this._dirty=!0}}),Object.defineProperty(e,"z",{get:function(){return this._array[2]},set:function(a){this._array[2]=a,this._dirty=!0}}),Object.defineProperty(e,"w",{get:function(){return this._array[3]},set:function(a){this._array[3]=a,this._dirty=!0}})}return d.add=function(a,b,d){return c.add(a._array,b._array,d._array),a._dirty=!0,a},d.set=function(a,b,d,e,f){c.set(a._array,b,d,e,f),a._dirty=!0},d.copy=function(a,b){return c.copy(a._array,b._array),a._dirty=!0,a},d.calculateW=function(a,b){return c.calculateW(a._array,b._array),a._dirty=!0,a},d.conjugate=function(a,b){return c.conjugate(a._array,b._array),a._dirty=!0,a},d.identity=function(a){return c.identity(a._array),a._dirty=!0,a},d.invert=function(a,b){return c.invert(a._array,b._array),a._dirty=!0,a},d.dot=function(a,b){return c.dot(a._array,b._array)},d.len=function(a){return c.length(a._array)},d.lerp=function(a,b,d,e){return c.lerp(a._array,b._array,d._array,e),a._dirty=!0,a},d.slerp=function(a,b,d,e){return c.slerp(a._array,b._array,d._array,e),a._dirty=!0,a},d.mul=function(a,b,d){return c.multiply(a._array,b._array,d._array),a._dirty=!0,a},d.multiply=d.mul,d.rotateX=function(a,b,d){return c.rotateX(a._array,b._array,d),a._dirty=!0,a},d.rotateY=function(a,b,d){return c.rotateY(a._array,b._array,d),a._dirty=!0,a},d.rotateZ=function(a,b,d){return c.rotateZ(a._array,b._array,d),a._dirty=!0,a},d.setAxisAngle=function(a,b,d){return c.setAxisAngle(a._array,b._array,d),a._dirty=!0,a},d.normalize=function(a,b){return c.normalize(a._array,b._array),a._dirty=!0,a},d.sqrLen=function(a){return c.sqrLen(a._array)},d.squaredLength=d.sqrLen,d.fromMat3=function(a,b){return c.fromMat3(a._array,b._array),a._dirty=!0,a},d.setAxes=function(a,b,d,e){return c.setAxes(a._array,b._array,d._array,e._array),a._dirty=!0,a},d.rotationTo=function(a,b,d){return c.rotationTo(a._array,b._array,d._array),a._dirty=!0,a},d}),d("qtek/math/Matrix4",["require","../dep/glmatrix","./Vector3"],function(a){var b=a("../dep/glmatrix"),c=a("./Vector3"),d=b.mat4,e=b.vec3,f=b.mat3,g=b.quat,h=function(){this._axisX=new c,this._axisY=new c,this._axisZ=new c,this._array=d.create(),this._dirty=!0};if(h.prototype={constructor:h,adjoint:function(){return d.adjoint(this._array,this._array),this._dirty=!0,this},clone:function(){return(new h).copy(this)},copy:function(a){return d.copy(this._array,a._array),this._dirty=!0,this},determinant:function(){return d.determinant(this._array)},fromQuat:function(a){return d.fromQuat(this._array,a._array),this._dirty=!0,this},fromRotationTranslation:function(a,b){return d.fromRotationTranslation(this._array,a._array,b._array),this._dirty=!0,this},fromMat2d:function(a){return h.fromMat2d(this,a),this},frustum:function(a,b,c,e,f,g){return d.frustum(this._array,a,b,c,e,f,g),this._dirty=!0,this},identity:function(){return d.identity(this._array),this._dirty=!0,this},invert:function(){return d.invert(this._array,this._array),this._dirty=!0,this},lookAt:function(a,b,c){return d.lookAt(this._array,a._array,b._array,c._array),this._dirty=!0,this},mul:function(a){return d.mul(this._array,this._array,a._array),this._dirty=!0,this},mulLeft:function(a){return d.mul(this._array,a._array,this._array),this._dirty=!0,this},multiply:function(a){return d.multiply(this._array,this._array,a._array),this._dirty=!0,this},multiplyLeft:function(a){return d.multiply(this._array,a._array,this._array),this._dirty=!0,this},ortho:function(a,b,c,e,f,g){return d.ortho(this._array,a,b,c,e,f,g),this._dirty=!0,this},perspective:function(a,b,c,e){return d.perspective(this._array,a,b,c,e),this._dirty=!0,this},rotate:function(a,b){return d.rotate(this._array,this._array,a,b._array),this._dirty=!0,this},rotateX:function(a){return d.rotateX(this._array,this._array,a),this._dirty=!0,this},rotateY:function(a){return d.rotateY(this._array,this._array,a),this._dirty=!0,this},rotateZ:function(a){return d.rotateZ(this._array,this._array,a),this._dirty=!0,this},scale:function(a){return d.scale(this._array,this._array,a._array),this._dirty=!0,this},translate:function(a){return d.translate(this._array,this._array,a._array),this._dirty=!0,this},transpose:function(){return d.transpose(this._array,this._array),this._dirty=!0,this},decomposeMatrix:function(){var a=e.create(),b=e.create(),c=e.create(),d=f.create();return function(h,i,j){var k=this._array;e.set(a,k[0],k[1],k[2]),e.set(b,k[4],k[5],k[6]),e.set(c,k[8],k[9],k[10]);var l=e.length(a),m=e.length(b),n=e.length(c);h&&(h.x=l,h.y=m,h.z=n,h._dirty=!0),j.set(k[12],k[13],k[14]),f.fromMat4(d,k),f.transpose(d,d),d[0]/=l,d[1]/=l,d[2]/=l,d[3]/=m,d[4]/=m,d[5]/=m,d[6]/=n,d[7]/=n,d[8]/=n,g.fromMat3(i._array,d),g.normalize(i._array,i._array),i._dirty=!0,j._dirty=!0}}(),toString:function(){return"["+Array.prototype.join.call(this._array,",")+"]"}},Object.defineProperty){var i=h.prototype;Object.defineProperty(i,"z",{get:function(){var a=this._array;return this._axisZ.set(a[8],a[9],a[10]),this._axisZ},set:function(a){var b=this._array;a=a._array,b[8]=a[0],b[9]=a[1],b[10]=a[2],this._dirty=!0}}),Object.defineProperty(i,"y",{get:function(){var a=this._array;return this._axisY.set(a[4],a[5],a[6]),this._axisY},set:function(a){var b=this._array;a=a._array,b[4]=a[0],b[5]=a[1],b[6]=a[2],this._dirty=!0}}),Object.defineProperty(i,"x",{get:function(){var a=this._array;return this._axisX.set(a[0],a[1],a[2]),this._axisX},set:function(a){var b=this._array;a=a._array,b[0]=a[0],b[1]=a[1],b[2]=a[2],this._dirty=!0}})}return h.adjoint=function(a,b){return d.adjoint(a._array,b._array),a._dirty=!0,a},h.copy=function(a,b){return d.copy(a._array,b._array),a._dirty=!0,a},h.determinant=function(a){return d.determinant(a._array)},h.identity=function(a){return d.identity(a._array),a._dirty=!0,a},h.ortho=function(a,b,c,e,f,g,h){return d.ortho(a._array,b,c,e,f,g,h),a._dirty=!0,a},h.perspective=function(a,b,c,e,f){return d.perspective(a._array,b,c,e,f),a._dirty=!0,a},h.lookAt=function(a,b,c,e){return d.lookAt(a._array,b._array,c._array,e._array),a._dirty=!0,a},h.invert=function(a,b){return d.invert(a._array,b._array),a._dirty=!0,a},h.mul=function(a,b,c){return d.mul(a._array,b._array,c._array),a._dirty=!0,a},h.multiply=h.mul,h.fromQuat=function(a,b){return d.fromQuat(a._array,b._array),a._dirty=!0,a},h.fromRotationTranslation=function(a,b,c){return d.fromRotationTranslation(a._array,b._array,c._array),a._dirty=!0,a},h.fromMat2d=function(a,b){a._dirty=!0;var b=b._array,a=a._array;return a[0]=b[0],a[4]=b[2],a[12]=b[4],a[1]=b[1],a[5]=b[3],a[13]=b[5],a},h.rotate=function(a,b,c,e){return d.rotate(a._array,b._array,c,e._array),a._dirty=!0,a},h.rotateX=function(a,b,c){return d.rotateX(a._array,b._array,c),a._dirty=!0,a},h.rotateY=function(a,b,c){return d.rotateY(a._array,b._array,c),a._dirty=!0,a},h.rotateZ=function(a,b,c){return d.rotateZ(a._array,b._array,c),a._dirty=!0,a},h.scale=function(a,b,c){return d.scale(a._array,b._array,c._array),a._dirty=!0,a},h.transpose=function(a,b){return d.transpose(a._array,b._array),a._dirty=!0,a},h.translate=function(a,b,c){return d.translate(a._array,b._array,c._array),a._dirty=!0,a},h}),d("qtek/Node",["require","./core/Base","./math/Vector3","./math/Quaternion","./math/Matrix4","./dep/glmatrix"],function(a){var b=a("./core/Base"),c=a("./math/Vector3"),d=a("./math/Quaternion"),e=a("./math/Matrix4"),f=a("./dep/glmatrix"),g=f.mat4,h=0,i=b.derive({name:"",position:null,rotation:null,scale:null,worldTransform:null,localTransform:null,autoUpdateLocalTransform:!0,_parent:null,_scene:null,_needsUpdateWorldTransform:!0,_inIterating:!1,__depth:0},function(){this.name||(this.name="NODE_"+h++),this.position||(this.position=new c),this.rotation||(this.rotation=new d),this.scale||(this.scale=new c(1,1,1)),this.worldTransform=new e,this.localTransform=new e,this._children=[]},{visible:!0,isRenderable:function(){return!1},setName:function(a){this._scene&&(delete this._scene._nodeRepository[this.name],this._scene._nodeRepository[a]=this),this.name=a},add:function(a){this._inIterating&&console.warn("Add operation can cause unpredictable error when in iterating"),a._parent!==this&&(a._parent&&a._parent.remove(a),a._parent=this,this._children.push(a),this._scene&&this._scene!==a.scene&&a.traverse(this._addSelfToScene,this))},remove:function(a){this._inIterating&&console.warn("Remove operation can cause unpredictable error when in iterating");var b=this._children.indexOf(a);0>b||(this._children.splice(b,1),a._parent=null,this._scene&&a.traverse(this._removeSelfFromScene,this))},getScene:function(){return this._scene},getParent:function(){return this._parent},_removeSelfFromScene:function(a){a._scene.removeFromScene(a),a._scene=null},_addSelfToScene:function(a){this._scene.addToScene(a),a._scene=this._scene},isAncestor:function(a){for(var b=a._parent;b;){if(b===this)return!0;b=b._parent}return!1},children:function(){return this._children.slice()},childAt:function(a){return this._children[a]},getChildByName:function(a){for(var b=0;be;e++)d[e].traverse(a,b,c);this._inIterating=!1},setLocalTransform:function(a){g.copy(this.localTransform._array,a._array),this.decomposeLocalTransform()},decomposeLocalTransform:function(a){var b=a?null:this.scale;this.localTransform.decomposeMatrix(b,this.rotation,this.position)},setWorldTransform:function(a){g.copy(this.worldTransform._array,a._array),this.decomposeWorldTransform()},decomposeWorldTransform:function(){var a=g.create();return function(b){this._parent?(g.invert(a,this._parent.worldTransform._array),g.multiply(this.localTransform._array,a,this.worldTransform._array)):g.copy(this.localTransform._array,this.worldTransform._array);var c=b?null:this.scale;this.localTransform.decomposeMatrix(c,this.rotation,this.position)}}(),updateLocalTransform:function(){var a=this.position,b=this.rotation,c=this.scale;if(a._dirty||c._dirty||b._dirty){var d=this.localTransform._array;g.fromRotationTranslation(d,b._array,a._array),g.scale(d,d,c._array),b._dirty=!1,c._dirty=!1,a._dirty=!1,this._needsUpdateWorldTransform=!0}},updateWorldTransform:function(){this._parent?g.multiply(this.worldTransform._array,this._parent.worldTransform._array,this.localTransform._array):g.copy(this.worldTransform._array,this.localTransform._array)},update:function(a){this.autoUpdateLocalTransform?this.updateLocalTransform():a=!0,(a||this._needsUpdateWorldTransform)&&(this.updateWorldTransform(),a=!0,this._needsUpdateWorldTransform=!1);for(var b=0,c=this._children.length;c>b;b++)this._children[b].update(a)},getWorldPosition:function(a){var b=this.worldTransform._array;return a?(a._array[0]=b[12],a._array[1]=b[13],a._array[2]=b[14],a):new c(b[12],b[13],b[14])},clone:function(){var a=new this.constructor;a.setName(this.name),a.position.copy(this.position),a.rotation.copy(this.rotation),a.scale.copy(this.scale);for(var b=0;bf;f++)e[f]=d.fromValues(0,0,0);this.vertices=e};return h.prototype={constructor:h,updateFromVertices:function(a){if(a.length>0){var b=this.min._array,c=this.max._array;f(b,a[0]),f(c,a[0]);for(var d=1;dc[0]&&(c[0]=e[0]),e[1]>c[1]&&(c[1]=e[1]),e[2]>c[2]&&(c[2]=e[2])}this.min._dirty=!0,this.max._dirty=!0}},union:function(a){d.min(this.min._array,this.min._array,a.min._array),d.max(this.max._array,this.max._array,a.max._array),this.min._dirty=!0,this.max._dirty=!0},intersectBoundingBox:function(a){var b=this.min._array,c=this.max._array,d=a.min._array,e=a.max._array;return!(b[0]>e[0]||b[1]>e[1]||b[2]>e[2]||c[0]i;i++)h=g[i],e(h,h,b),h[0]d[0]&&(d[0]=h[0]),h[1]>d[1]&&(d[1]=h[1]),h[2]>d[2]&&(d[2]=h[2]);this.min._dirty=!0,this.max._dirty=!0},applyProjection:function(a){(this.min._dirty||this.max._dirty)&&(this.updateVertices(),this.min._dirty=!1,this.max._dirty=!1);var b=a._array,c=this.vertices[0],d=this.vertices[3],e=this.vertices[7],f=this.min._array,g=this.max._array;if(1===b[15])f[0]=b[0]*c[0]+b[12],f[1]=b[5]*c[1]+b[13],g[2]=b[10]*c[2]+b[14],g[0]=b[0]*e[0]+b[12],g[1]=b[5]*e[1]+b[13],f[2]=b[10]*e[2]+b[14];else{var h=-1/c[2];f[0]=b[0]*c[0]*h,f[1]=b[5]*c[1]*h,g[2]=(b[10]*c[2]+b[14])*h,h=-1/d[2],g[0]=b[0]*d[0]*h,g[1]=b[5]*d[1]*h,h=-1/e[2],f[2]=(b[10]*e[2]+b[14])*h}this.min._dirty=!0,this.max._dirty=!0},updateVertices:function(){var a=this.min._array,b=this.max._array,c=this.vertices;g(c[0],a[0],a[1],a[2]),g(c[1],a[0],b[1],a[2]),g(c[2],b[0],a[1],a[2]),g(c[3],b[0],b[1],a[2]),g(c[4],a[0],a[1],b[2]),g(c[5],a[0],b[1],b[2]),g(c[6],b[0],a[1],b[2]),g(c[7],b[0],b[1],b[2])},copy:function(a){f(this.min._array,a.min._array),f(this.max._array,a.max._array),this.min._dirty=!0,this.max._dirty=!0},clone:function(){var a=new h;return a.copy(this),a}},h}),d("qtek/math/Plane",["require","./Vector3","../dep/glmatrix"],function(a){var b=a("./Vector3"),c=a("../dep/glmatrix"),d=c.vec3,e=c.mat4,f=c.vec4,g=function(a,c){this.normal=a||new b(0,1,0),this.distance=c||0};return g.prototype={constructor:g,distanceToPoint:function(a){return d.dot(a._array,this.normal._array)-this.distance},projectPoint:function(a,c){c||(c=new b);var e=this.distanceToPoint(a);return d.scaleAndAdd(c._array,a._array,this.normal._array,-e),c._dirty=!0,c},normalize:function(){var a=1/d.len(this.normal._array);d.scale(this.normal._array,a),this.distance*=a},intersectFrustum:function(a){for(var b=a.vertices,c=this.normal._array,e=d.dot(b[0]._array,c)>this.distance,f=1;8>f;f++)if(d.dot(b[f]._array,c)>this.distance!=e)return!0},intersectLine:function(){var a=d.create();return function(c,e,f){var g=this.distanceToPoint(c),h=this.distanceToPoint(e);if(g>0&&h>0||0>g&&0>h)return null;var i=this.normal._array,j=this.distance,k=c._array;d.sub(a,e._array,c._array),d.normalize(a,a);var l=d.dot(i,a);if(0===l)return null;f||(f=new b);var m=(d.dot(i,k)-j)/l;return d.scaleAndAdd(f._array,k,a,-m),f._dirty=!0,f}}(),applyTransform:function(){var a=e.create(),b=f.create(),c=f.create();return c[3]=1,function(g){g=g._array,d.scale(c,this.normal._array,this.distance),f.transformMat4(c,c,g),this.distance=d.dot(c,this.normal._array),e.invert(a,g),e.transpose(a,a),b[3]=0,d.copy(b,this.normal._array),f.transformMat4(b,b,a),d.copy(this.normal._array,b)}}(),copy:function(a){d.copy(this.normal._array,a.normal._array),this.normal._dirty=!0,this.distance=a.distance},clone:function(){var a=new g;return a.copy(this),a}},g}),d("qtek/math/Frustum",["require","./Vector3","./BoundingBox","./Plane","../dep/glmatrix"],function(a){a("./Vector3");var b=a("./BoundingBox"),c=a("./Plane"),d=a("../dep/glmatrix"),e=d.vec3,f=function(){this.planes=[];for(var a=0;6>a;a++)this.planes.push(new c);this.boundingBox=new b,this.vertices=[];for(var a=0;8>a;a++)this.vertices[a]=e.fromValues(0,0,0)};return f.prototype={setFromProjection:function(a){var b=this.planes,c=a._array,d=c[0],f=c[1],g=c[2],h=c[3],i=c[4],j=c[5],k=c[6],l=c[7],m=c[8],n=c[9],o=c[10],p=c[11],q=c[12],r=c[13],s=c[14],t=c[15];if(e.set(b[0].normal._array,h-d,l-i,p-m),b[0].distance=-(t-q),b[0].normalize(),e.set(b[1].normal._array,h+d,l+i,p+m),b[1].distance=-(t+q),b[1].normalize(),e.set(b[2].normal._array,h+f,l+j,p+n),b[2].distance=-(t+r),b[2].normalize(),e.set(b[3].normal._array,h-f,l-j,p-n),b[3].distance=-(t-r),b[3].normalize(),e.set(b[4].normal._array,h-g,l-k,p-o),b[4].distance=-(t-s),b[4].normalize(),e.set(b[5].normal._array,h+g,l+k,p+o),b[5].distance=-(t+s),b[5].normalize(),0===t){var u=j/d,v=-s/(o-1),w=-s/(o+1),x=-w/j,y=-v/j;this.boundingBox.min.set(-x*u,-x,w),this.boundingBox.max.set(x*u,x,v);var z=this.vertices;e.set(z[0],-x*u,-x,w),e.set(z[1],-x*u,x,w),e.set(z[2],x*u,-x,w),e.set(z[3],x*u,x,w),e.set(z[4],-y*u,-y,v),e.set(z[5],-y*u,y,v),e.set(z[6],y*u,-y,v),e.set(z[7],y*u,y,v)}else{var A=(-1-q)/d,B=(1-q)/d,C=(1-r)/j,D=(-1-r)/j,E=(-1-s)/o,F=(1-s)/o;this.boundingBox.min.set(A,D,F),this.boundingBox.max.set(B,C,E);for(var G=0;8>G;G++)e.copy(this.vertices[G],this.boundingBox.vertices[G])}},getTransformedBoundingBox:function(){var a=e.create();return function(b,c){var d=this.vertices,f=c._array,g=b.min._array,h=b.max._array,i=d[0];e.transformMat4(a,i,f),e.copy(g,a),e.copy(h,a);for(var j=1;8>j;j++)i=d[j],e.transformMat4(a,i,f),g[0]=Math.min(a[0],g[0]),g[1]=Math.min(a[1],g[1]),g[2]=Math.min(a[2],g[2]),h[0]=Math.max(a[0],h[0]),h[1]=Math.max(a[1],h[1]),h[2]=Math.max(a[2],h[2]);return b.min._dirty=!0,b.max._dirty=!0,b}}()},f}),d("qtek/math/Ray",["require","./Vector3","../dep/glmatrix"],function(a){var b=a("./Vector3"),c=a("../dep/glmatrix"),d=c.vec3,e=1e-5,f=function(a,c){this.origin=a||new b,this.direction=c||new b};return f.prototype={constructor:f,intersectPlane:function(a,c){var e=a.normal._array,f=a.distance,g=this.origin._array,h=this.direction._array,i=d.dot(e,h);if(0===i)return null;c||(c=new b);var j=(d.dot(e,g)-f)/i;return d.scaleAndAdd(c._array,g,h,-j),c._dirty=!0,c},mirrorAgainstPlane:function(a){var b=d.dot(a.normal._array,this.direction._array);d.scaleAndAdd(this.direction._array,this.direction._array,a.normal._array,2*-b),this.direction._dirty=!0},distanceToPoint:function(){var a=d.create();return function(b){d.sub(a,b,this.origin._array);var c=d.dot(a,this.direction._array);if(0>c)return d.distance(this.origin._array,b);var e=d.lenSquared(a);return Math.sqrt(e-c*c)}}(),intersectSphere:function(){var a=d.create();return function(c,e,f){var g=this.origin._array,h=this.direction._array;c=c._array,d.sub(a,c,g);var i=d.dot(a,h),j=d.squaredLength(a),k=j-i*i,l=e*e;if(!(k>l)){var m=Math.sqrt(l-k),n=i-m,o=i+m;return f||(f=new b),0>n?0>o?null:(d.scaleAndAdd(f._array,g,h,o),f):(d.scaleAndAdd(f._array,g,h,n),f)}}}(),intersectBoundingBox:function(a,c){var e,f,g,h,i,j,k=this.direction._array,l=this.origin._array,m=a.min._array,n=a.max._array,o=1/k[0],p=1/k[1],q=1/k[2];if(o>=0?(e=(m[0]-l[0])*o,f=(n[0]-l[0])*o):(f=(m[0]-l[0])*o,e=(n[0]-l[0])*o),p>=0?(g=(m[1]-l[1])*p,h=(n[1]-l[1])*p):(h=(m[1]-l[1])*p,g=(n[1]-l[1])*p),e>h||g>f)return null;if((g>e||e!==e)&&(e=g),(f>h||f!==f)&&(f=h),q>=0?(i=(m[2]-l[2])*q,j=(n[2]-l[2])*q):(j=(m[2]-l[2])*q,i=(n[2]-l[2])*q),e>j||i>f)return null;if((i>e||e!==e)&&(e=i),(f>j||f!==f)&&(f=j),0>f)return null;var r=e>=0?e:f;return c||(c=new b),d.scaleAndAdd(c._array,l,k,r),c},intersectTriangle:function(){var a=d.create(),c=d.create(),f=d.create(),g=d.create();return function(h,i,j,k,l,m){var n=this.direction._array,o=this.origin._array;h=h._array,i=i._array,j=j._array,d.sub(a,i,h),d.sub(c,j,h),d.cross(g,c,n);var p=d.dot(a,g);if(k){if(p>-e)return null}else if(p>-e&&e>p)return null;d.sub(f,o,h);var q=d.dot(g,f)/p;if(0>q||q>1)return null;d.cross(g,a,f);var r=d.dot(n,g)/p;if(0>r||r>1||q+r>1)return null;d.cross(g,a,c);var s=-d.dot(f,g)/p;return 0>s?null:(l||(l=new b),m&&b.set(m,1-q-r,q,r),d.scaleAndAdd(l._array,o,n,s),l)}}(),applyTransform:function(a){b.add(this.direction,this.direction,this.origin),b.transformMat4(this.origin,this.origin,a),b.transformMat4(this.direction,this.direction,a),b.sub(this.direction,this.direction,this.origin),b.normalize(this.direction,this.direction)},copy:function(a){b.copy(this.origin,a.origin),b.copy(this.direction,a.direction)},clone:function(){var a=new f;return a.copy(this),a}},f}),d("qtek/Camera",["require","./Node","./math/Matrix4","./math/Frustum","./math/BoundingBox","./math/Ray","./dep/glmatrix"],function(a){var b=a("./Node"),c=a("./math/Matrix4"),d=a("./math/Frustum"),e=a("./math/BoundingBox"),f=a("./math/Ray"),g=a("./dep/glmatrix"),h=g.mat4,i=g.vec3,j=g.vec4,k=b.derive(function(){return{projectionMatrix:new c,invProjectionMatrix:new c,viewMatrix:new c,frustum:new d,sceneBoundingBoxLastFrame:new e}},function(){this.update(!0)},{update:function(a){b.prototype.update.call(this,a),h.invert(this.viewMatrix._array,this.worldTransform._array),this.updateProjectionMatrix(),h.invert(this.invProjectionMatrix._array,this.projectionMatrix._array),this.frustum.setFromProjection(this.projectionMatrix)},updateProjectionMatrix:function(){},castRay:function(){var a=j.create();return function(b,c){var d=void 0!==c?c:new f,e=b._array[0],g=b._array[1];return j.set(a,e,g,-1,1),j.transformMat4(a,a,this.invProjectionMatrix._array),j.transformMat4(a,a,this.worldTransform._array),i.scale(d.origin._array,a,1/a[3]),j.set(a,e,g,1,1),j.transformMat4(a,a,this.invProjectionMatrix._array),j.transformMat4(a,a,this.worldTransform._array),i.scale(a,a,1/a[3]),i.sub(d.direction._array,a,d.origin._array),i.normalize(d.direction._array,d.direction._array),d.direction._dirty=!0,d.origin._dirty=!0,d -}}()});return k}),d("qtek/core/glenum",[],function(){return{DEPTH_BUFFER_BIT:256,STENCIL_BUFFER_BIT:1024,COLOR_BUFFER_BIT:16384,POINTS:0,LINES:1,LINE_LOOP:2,LINE_STRIP:3,TRIANGLES:4,TRIANGLE_STRIP:5,TRIANGLE_FAN:6,ZERO:0,ONE:1,SRC_COLOR:768,ONE_MINUS_SRC_COLOR:769,SRC_ALPHA:770,ONE_MINUS_SRC_ALPHA:771,DST_ALPHA:772,ONE_MINUS_DST_ALPHA:773,DST_COLOR:774,ONE_MINUS_DST_COLOR:775,SRC_ALPHA_SATURATE:776,FUNC_ADD:32774,BLEND_EQUATION:32777,BLEND_EQUATION_RGB:32777,BLEND_EQUATION_ALPHA:34877,FUNC_SUBTRACT:32778,FUNC_REVERSE_SUBTRACT:32779,BLEND_DST_RGB:32968,BLEND_SRC_RGB:32969,BLEND_DST_ALPHA:32970,BLEND_SRC_ALPHA:32971,CONSTANT_COLOR:32769,ONE_MINUS_CONSTANT_COLOR:32770,CONSTANT_ALPHA:32771,ONE_MINUS_CONSTANT_ALPHA:32772,BLEND_COLOR:32773,ARRAY_BUFFER:34962,ELEMENT_ARRAY_BUFFER:34963,ARRAY_BUFFER_BINDING:34964,ELEMENT_ARRAY_BUFFER_BINDING:34965,STREAM_DRAW:35040,STATIC_DRAW:35044,DYNAMIC_DRAW:35048,BUFFER_SIZE:34660,BUFFER_USAGE:34661,CURRENT_VERTEX_ATTRIB:34342,FRONT:1028,BACK:1029,FRONT_AND_BACK:1032,CULL_FACE:2884,BLEND:3042,DITHER:3024,STENCIL_TEST:2960,DEPTH_TEST:2929,SCISSOR_TEST:3089,POLYGON_OFFSET_FILL:32823,SAMPLE_ALPHA_TO_COVERAGE:32926,SAMPLE_COVERAGE:32928,NO_ERROR:0,INVALID_ENUM:1280,INVALID_VALUE:1281,INVALID_OPERATION:1282,OUT_OF_MEMORY:1285,CW:2304,CCW:2305,LINE_WIDTH:2849,ALIASED_POINT_SIZE_RANGE:33901,ALIASED_LINE_WIDTH_RANGE:33902,CULL_FACE_MODE:2885,FRONT_FACE:2886,DEPTH_RANGE:2928,DEPTH_WRITEMASK:2930,DEPTH_CLEAR_VALUE:2931,DEPTH_FUNC:2932,STENCIL_CLEAR_VALUE:2961,STENCIL_FUNC:2962,STENCIL_FAIL:2964,STENCIL_PASS_DEPTH_FAIL:2965,STENCIL_PASS_DEPTH_PASS:2966,STENCIL_REF:2967,STENCIL_VALUE_MASK:2963,STENCIL_WRITEMASK:2968,STENCIL_BACK_FUNC:34816,STENCIL_BACK_FAIL:34817,STENCIL_BACK_PASS_DEPTH_FAIL:34818,STENCIL_BACK_PASS_DEPTH_PASS:34819,STENCIL_BACK_REF:36003,STENCIL_BACK_VALUE_MASK:36004,STENCIL_BACK_WRITEMASK:36005,VIEWPORT:2978,SCISSOR_BOX:3088,COLOR_CLEAR_VALUE:3106,COLOR_WRITEMASK:3107,UNPACK_ALIGNMENT:3317,PACK_ALIGNMENT:3333,MAX_TEXTURE_SIZE:3379,MAX_VIEWPORT_DIMS:3386,SUBPIXEL_BITS:3408,RED_BITS:3410,GREEN_BITS:3411,BLUE_BITS:3412,ALPHA_BITS:3413,DEPTH_BITS:3414,STENCIL_BITS:3415,POLYGON_OFFSET_UNITS:10752,POLYGON_OFFSET_FACTOR:32824,TEXTURE_BINDING_2D:32873,SAMPLE_BUFFERS:32936,SAMPLES:32937,SAMPLE_COVERAGE_VALUE:32938,SAMPLE_COVERAGE_INVERT:32939,COMPRESSED_TEXTURE_FORMATS:34467,DONT_CARE:4352,FASTEST:4353,NICEST:4354,GENERATE_MIPMAP_HINT:33170,BYTE:5120,UNSIGNED_BYTE:5121,SHORT:5122,UNSIGNED_SHORT:5123,INT:5124,UNSIGNED_INT:5125,FLOAT:5126,DEPTH_COMPONENT:6402,ALPHA:6406,RGB:6407,RGBA:6408,LUMINANCE:6409,LUMINANCE_ALPHA:6410,UNSIGNED_SHORT_4_4_4_4:32819,UNSIGNED_SHORT_5_5_5_1:32820,UNSIGNED_SHORT_5_6_5:33635,FRAGMENT_SHADER:35632,VERTEX_SHADER:35633,MAX_VERTEX_ATTRIBS:34921,MAX_VERTEX_UNIFORM_VECTORS:36347,MAX_VARYING_VECTORS:36348,MAX_COMBINED_TEXTURE_IMAGE_UNITS:35661,MAX_VERTEX_TEXTURE_IMAGE_UNITS:35660,MAX_TEXTURE_IMAGE_UNITS:34930,MAX_FRAGMENT_UNIFORM_VECTORS:36349,SHADER_TYPE:35663,DELETE_STATUS:35712,LINK_STATUS:35714,VALIDATE_STATUS:35715,ATTACHED_SHADERS:35717,ACTIVE_UNIFORMS:35718,ACTIVE_ATTRIBUTES:35721,SHADING_LANGUAGE_VERSION:35724,CURRENT_PROGRAM:35725,NEVER:512,LESS:513,EQUAL:514,LEQUAL:515,GREATER:516,NOTEQUAL:517,GEQUAL:518,ALWAYS:519,KEEP:7680,REPLACE:7681,INCR:7682,DECR:7683,INVERT:5386,INCR_WRAP:34055,DECR_WRAP:34056,VENDOR:7936,RENDERER:7937,VERSION:7938,NEAREST:9728,LINEAR:9729,NEAREST_MIPMAP_NEAREST:9984,LINEAR_MIPMAP_NEAREST:9985,NEAREST_MIPMAP_LINEAR:9986,LINEAR_MIPMAP_LINEAR:9987,TEXTURE_MAG_FILTER:10240,TEXTURE_MIN_FILTER:10241,TEXTURE_WRAP_S:10242,TEXTURE_WRAP_T:10243,TEXTURE_2D:3553,TEXTURE:5890,TEXTURE_CUBE_MAP:34067,TEXTURE_BINDING_CUBE_MAP:34068,TEXTURE_CUBE_MAP_POSITIVE_X:34069,TEXTURE_CUBE_MAP_NEGATIVE_X:34070,TEXTURE_CUBE_MAP_POSITIVE_Y:34071,TEXTURE_CUBE_MAP_NEGATIVE_Y:34072,TEXTURE_CUBE_MAP_POSITIVE_Z:34073,TEXTURE_CUBE_MAP_NEGATIVE_Z:34074,MAX_CUBE_MAP_TEXTURE_SIZE:34076,TEXTURE0:33984,TEXTURE1:33985,TEXTURE2:33986,TEXTURE3:33987,TEXTURE4:33988,TEXTURE5:33989,TEXTURE6:33990,TEXTURE7:33991,TEXTURE8:33992,TEXTURE9:33993,TEXTURE10:33994,TEXTURE11:33995,TEXTURE12:33996,TEXTURE13:33997,TEXTURE14:33998,TEXTURE15:33999,TEXTURE16:34e3,TEXTURE17:34001,TEXTURE18:34002,TEXTURE19:34003,TEXTURE20:34004,TEXTURE21:34005,TEXTURE22:34006,TEXTURE23:34007,TEXTURE24:34008,TEXTURE25:34009,TEXTURE26:34010,TEXTURE27:34011,TEXTURE28:34012,TEXTURE29:34013,TEXTURE30:34014,TEXTURE31:34015,ACTIVE_TEXTURE:34016,REPEAT:10497,CLAMP_TO_EDGE:33071,MIRRORED_REPEAT:33648,FLOAT_VEC2:35664,FLOAT_VEC3:35665,FLOAT_VEC4:35666,INT_VEC2:35667,INT_VEC3:35668,INT_VEC4:35669,BOOL:35670,BOOL_VEC2:35671,BOOL_VEC3:35672,BOOL_VEC4:35673,FLOAT_MAT2:35674,FLOAT_MAT3:35675,FLOAT_MAT4:35676,SAMPLER_2D:35678,SAMPLER_CUBE:35680,VERTEX_ATTRIB_ARRAY_ENABLED:34338,VERTEX_ATTRIB_ARRAY_SIZE:34339,VERTEX_ATTRIB_ARRAY_STRIDE:34340,VERTEX_ATTRIB_ARRAY_TYPE:34341,VERTEX_ATTRIB_ARRAY_NORMALIZED:34922,VERTEX_ATTRIB_ARRAY_POINTER:34373,VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:34975,COMPILE_STATUS:35713,LOW_FLOAT:36336,MEDIUM_FLOAT:36337,HIGH_FLOAT:36338,LOW_INT:36339,MEDIUM_INT:36340,HIGH_INT:36341,FRAMEBUFFER:36160,RENDERBUFFER:36161,RGBA4:32854,RGB5_A1:32855,RGB565:36194,DEPTH_COMPONENT16:33189,STENCIL_INDEX:6401,STENCIL_INDEX8:36168,DEPTH_STENCIL:34041,RENDERBUFFER_WIDTH:36162,RENDERBUFFER_HEIGHT:36163,RENDERBUFFER_INTERNAL_FORMAT:36164,RENDERBUFFER_RED_SIZE:36176,RENDERBUFFER_GREEN_SIZE:36177,RENDERBUFFER_BLUE_SIZE:36178,RENDERBUFFER_ALPHA_SIZE:36179,RENDERBUFFER_DEPTH_SIZE:36180,RENDERBUFFER_STENCIL_SIZE:36181,FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:36048,FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:36049,FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL:36050,FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE:36051,COLOR_ATTACHMENT0:36064,DEPTH_ATTACHMENT:36096,STENCIL_ATTACHMENT:36128,DEPTH_STENCIL_ATTACHMENT:33306,NONE:0,FRAMEBUFFER_COMPLETE:36053,FRAMEBUFFER_INCOMPLETE_ATTACHMENT:36054,FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:36055,FRAMEBUFFER_INCOMPLETE_DIMENSIONS:36057,FRAMEBUFFER_UNSUPPORTED:36061,FRAMEBUFFER_BINDING:36006,RENDERBUFFER_BINDING:36007,MAX_RENDERBUFFER_SIZE:34024,INVALID_FRAMEBUFFER_OPERATION:1286,UNPACK_FLIP_Y_WEBGL:37440,UNPACK_PREMULTIPLY_ALPHA_WEBGL:37441,CONTEXT_LOST_WEBGL:37442,UNPACK_COLORSPACE_CONVERSION_WEBGL:37443,BROWSER_DEFAULT_WEBGL:37444}}),d("qtek/core/Cache",[],function(){var a=function(){this._contextId=0,this._caches=[],this._context={}};return a.prototype={use:function(a,b){this._caches[a]||(this._caches[a]={},b&&(this._caches[a]=b())),this._contextId=a,this._context=this._caches[a]},put:function(a,b){this._context[a]=b},get:function(a){return this._context[a]},dirty:function(a){a=a||"";var b="__dirty__"+a;this.put(b,!0)},dirtyAll:function(a){a=a||"";for(var b="__dirty__"+a,c=0;c=0?(b||(b=g.create()),g.copy(b,this.faces[a]),b):void 0},isUseFace:function(){return this.useFace&&this.faces.length>0},isSplitted:function(){return this.getVertexNumber()>65535},createAttribute:function(a,c,d,e){var f=new b.Attribute(a,c,d,e,!0);return this.attributes[a]=f,this._attributeList.push(a),f},removeAttribute:function(a){var b=this._attributeList.indexOf(a);return b>=0?(this._attributeList.splice(b,1),delete this.attributes[a],!0):!1},getEnabledAttributes:function(){if(this._enabledAttributes)return this._enabledAttributes;for(var a={},b=this.getVertexNumber(),c=0;ch;h++)f[h]=-1;return d._arrayChunks.push(c),c},k=Object.keys(a);if(e>65535&&this.isUseFace()&&!c){var l,m=0,n=[0],o=[];for(q=0;e>q;q++)o[q]=-1,f[q]=-1;if(b)for(q=0;q65535&&(m++,n[m]=q,p=0,l=j(m));for(var t=0;3>t;t++){for(var u=r[t],v=-1===f[u],w=0;wA;A++)x[p*z+A]=y[u][A]}}v?(f[u]=p,s[t]=p,p++):s[t]=f[u]}}for(var B=0;Bq;q++)H[G++]=g[q][0],H[G++]=g[q][1],H[G++]=g[q][2]}}else{var C=j(0);if(b){var H=C.indicesArray,I=this.faces.length;if(!H||3*I!==H.length){var J=e>65535?Uint32Array:Uint16Array;H=C.indicesArray=new J(3*this.faces.length)}for(var G=0,q=0;I>q;q++)H[G++]=this.faces[q][0],H[G++]=this.faces[q][1],H[G++]=this.faces[q][2]}for(var i in a){var y=a[i].value,K=a[i].type,z=a[i].size,x=C.attributeArrays[i],L=e*z;if(x&&x.length===L||(x=new h[i](L),C.attributeArrays[i]=x),1===z)for(var q=0;qA;A++)x[G++]=y[q][A]}}},_updateBuffer:function(a,c,d){var e=this._cache.get("chunks"),f=!1;if(!e){e=[];for(var g=0;g=0;g--)if(j[g].name===q){r=j[g],p=g;break}}var w;w=r?r.buffer:a.createBuffer(),a.bindBuffer(a.ARRAY_BUFFER,w),a.bufferData(a.ARRAY_BUFFER,m[q],this.hint),j[o++]=new b.AttributeBuffer(q,t,w,v,u)}j.length=o,d&&(k||(k=new b.IndicesBuffer(a.createBuffer()),i.indicesBuffer=k),k.count=n.length,a.bindBuffer(a.ELEMENT_ARRAY_BUFFER,k.buffer),a.bufferData(a.ELEMENT_ARRAY_BUFFER,n,this.hint))}},generateVertexNormals:function(){for(var a=this.faces,b=a.length,c=this.attributes.position.value,d=this.attributes.normal.value,e=g.create(),f=g.create(),h=g.create(),i=0;ij;j++){var k=a[j],l=k[0],m=k[1],n=k[2],o=c[l],p=c[m],q=c[n];g.sub(f,o,p),g.sub(h,p,q),g.cross(e,f,h),g.add(d[l],d[l],e),g.add(d[m],d[m],e),g.add(d[n],d[n],e)}for(var i=0;ik;k++){var l=a[k],m=l[0],n=l[1],o=l[2],p=c[m],q=c[n],r=c[o];g.sub(f,p,q),g.sub(h,q,r),g.cross(e,f,h),j?(g.copy(d[m],e),g.copy(d[n],e),g.copy(d[o],e)):d[m]=d[n]=d[o]=i.call(e)}},generateTangents:function(){for(var a=this.attributes.texcoord0.value,b=this.attributes.position.value,c=this.attributes.tangent.value,d=this.attributes.normal.value,e=[],f=[],h=this.getVertexNumber(),i=0;h>i;i++)e[i]=[0,0,0],f[i]=[0,0,0];for(var j=[0,0,0],k=[0,0,0],i=0;ii;i++){var I=d[i],J=e[i];g.scale(G,I,g.dot(I,J)),g.sub(G,J,G),g.normalize(G,G),g.cross(H,I,J),G[3]=g.dot(H,f[i])<0?-1:1,c[i]=G.slice()}},isUniqueVertex:function(){return this.isUseFace()?this.getVertexNumber()===3*this.faces.length:!0},generateUniqueVertex:function(){for(var a=[],b=0;bh;h++){var j=g[h];if(a[j]>0){for(var k=0;k1)console.warn("Large geometry will discard chunks when convert to StaticGeometry");else if(0===this._arrayChunks.length)return a;var d=this._arrayChunks[0],e=this.getEnabledAttributes();for(var f in e){var g=e[f],h=a.attributes[f];h||(h=a.attributes[f]={type:g.type,size:g.size,value:null},g.semantic&&(h.semantic=g.semantic)),h.value=d.attributeArrays[f]}return a.faces=d.indicesArray,this.boundingBox&&(a.boundingBox=new c,a.boundingBox.min.copy(this.boundingBox.min),a.boundingBox.max.copy(this.boundingBox.max)),a},applyTransform:function(a){var b=this.attributes.position.value,c=this.attributes.normal.value,d=this.attributes.tangent.value;a=a._array;for(var e=0;eb;b<<=1)a|=a>>b;return a+1},dispose:function(a){var b=this._cache;b.use(a.__GLID__);var c=b.get("webgl_texture");c&&a.deleteTexture(c),b.deleteContext(a.__GLID__)},isRenderable:function(){},isPowerOfTwo:function(){}});return e.BYTE=c.BYTE,e.UNSIGNED_BYTE=c.UNSIGNED_BYTE,e.SHORT=c.SHORT,e.UNSIGNED_SHORT=c.UNSIGNED_SHORT,e.INT=c.INT,e.UNSIGNED_INT=c.UNSIGNED_INT,e.FLOAT=c.FLOAT,e.HALF_FLOAT=36193,e.DEPTH_COMPONENT=c.DEPTH_COMPONENT,e.ALPHA=c.ALPHA,e.RGB=c.RGB,e.RGBA=c.RGBA,e.LUMINANCE=c.LUMINANCE,e.LUMINANCE_ALPHA=c.LUMINANCE_ALPHA,e.COMPRESSED_RGB_S3TC_DXT1_EXT=33776,e.COMPRESSED_RGBA_S3TC_DXT1_EXT=33777,e.COMPRESSED_RGBA_S3TC_DXT3_EXT=33778,e.COMPRESSED_RGBA_S3TC_DXT5_EXT=33779,e.NEAREST=c.NEAREST,e.LINEAR=c.LINEAR,e.NEAREST_MIPMAP_NEAREST=c.NEAREST_MIPMAP_NEAREST,e.LINEAR_MIPMAP_NEAREST=c.LINEAR_MIPMAP_NEAREST,e.NEAREST_MIPMAP_LINEAR=c.NEAREST_MIPMAP_LINEAR,e.LINEAR_MIPMAP_LINEAR=c.LINEAR_MIPMAP_LINEAR,e.TEXTURE_MAG_FILTER=c.TEXTURE_MAG_FILTER,e.TEXTURE_MIN_FILTER=c.TEXTURE_MIN_FILTER,e.REPEAT=c.REPEAT,e.CLAMP_TO_EDGE=c.CLAMP_TO_EDGE,e.MIRRORED_REPEAT=c.MIRRORED_REPEAT,e}),d("qtek/TextureCube",["require","./Texture","./core/glinfo","./core/glenum","./core/util"],function(a){function b(a){return"CANVAS"===a.nodeName||a.complete}var c=a("./Texture"),d=a("./core/glinfo"),e=a("./core/glenum"),f=a("./core/util"),g=["px","nx","py","ny","pz","nz"],h=c.derive(function(){return{image:{px:null,nx:null,py:null,ny:null,pz:null,nz:null},pixels:{px:null,nx:null,py:null,ny:null,pz:null,nz:null},mipmaps:[]}},{update:function(a){a.bindTexture(a.TEXTURE_CUBE_MAP,this._cache.get("webgl_texture")),this.beforeUpdate(a);var b=this.format,c=this.type;a.texParameteri(a.TEXTURE_CUBE_MAP,a.TEXTURE_WRAP_S,this.wrapS),a.texParameteri(a.TEXTURE_CUBE_MAP,a.TEXTURE_WRAP_T,this.wrapT),a.texParameteri(a.TEXTURE_CUBE_MAP,a.TEXTURE_MAG_FILTER,this.magFilter),a.texParameteri(a.TEXTURE_CUBE_MAP,a.TEXTURE_MIN_FILTER,this.minFilter);var f=d.getExtension(a,"EXT_texture_filter_anisotropic");if(f&&this.anisotropic>1&&a.texParameterf(a.TEXTURE_CUBE_MAP,f.TEXTURE_MAX_ANISOTROPY_EXT,this.anisotropic),36193===c){var g=d.getExtension(a,"OES_texture_half_float");g||(c=e.FLOAT)}if(this.mipmaps.length)for(var h=this.width,i=this.height,j=0;ji;i++){var j=g[i],k=b.image&&b.image[j];k?a.texImage2D(a.TEXTURE_CUBE_MAP_POSITIVE_X+i,c,f,f,h,k):a.texImage2D(a.TEXTURE_CUBE_MAP_POSITIVE_X+i,c,f,d,e,0,f,h,b.pixels&&b.pixels[j])}},generateMipmap:function(a){this.useMipmap&&!this.NPOT&&(a.bindTexture(a.TEXTURE_CUBE_MAP,this._cache.get("webgl_texture")),a.generateMipmap(a.TEXTURE_CUBE_MAP))},bind:function(a){a.bindTexture(a.TEXTURE_CUBE_MAP,this.getWebGLTexture(a))},unbind:function(a){a.bindTexture(a.TEXTURE_CUBE_MAP,null)},isPowerOfTwo:function(){function a(a){return 0===(a&a-1)}return this.image.px?a(this.image.px.width)&&a(this.image.px.height):a(this.width)&&a(this.height)},isRenderable:function(){return this.image.px?b(this.image.px)&&b(this.image.nx)&&b(this.image.py)&&b(this.image.ny)&&b(this.image.pz)&&b(this.image.nz):this.width&&this.height},load:function(a){var b=0,c=this;return f.each(a,function(a,d){var e=new Image;e.onload=function(){b--,0===b&&(c.dirty(),c.trigger("success",c)),e.onload=null},e.onerror=function(){b--,e.onerror=null},b++,e.src=a,c.image[d]=e}),this}});return h}),d("qtek/FrameBuffer",["require","./core/Base","./TextureCube","./core/glinfo","./core/glenum","./core/Cache"],function(a){var b=a("./core/Base"),c=a("./TextureCube"),d=a("./core/glinfo"),e=a("./core/glenum"),f=a("./core/Cache"),g=b.derive({depthBuffer:!0,_attachedTextures:null,_width:0,_height:0,_binded:!1},function(){this._cache=new f,this._attachedTextures={}},{resize:function(a,b){this._width=a,this._height=b},bind:function(a){var b=a.gl;this._binded||(b.bindFramebuffer(b.FRAMEBUFFER,this.getFrameBuffer(b)),this._binded=!0);var c=this._cache;if(c.put("viewport",a.viewport),a.setViewport(0,0,this._width,this._height,1),!c.get("depthtexture_attached")&&this.depthBuffer){c.miss("renderbuffer")&&c.put("renderbuffer",b.createRenderbuffer());var d=this._width,e=this._height,f=c.get("renderbuffer");(d!==c.get("renderbuffer_width")||e!==c.get("renderbuffer_height"))&&(b.bindRenderbuffer(b.RENDERBUFFER,f),b.renderbufferStorage(b.RENDERBUFFER,b.DEPTH_COMPONENT16,d,e),c.put("renderbuffer_width",d),c.put("renderbuffer_height",e),b.bindRenderbuffer(b.RENDERBUFFER,null)),c.get("renderbuffer_attached")||(b.framebufferRenderbuffer(b.FRAMEBUFFER,b.DEPTH_ATTACHMENT,b.RENDERBUFFER,f),c.put("renderbuffer_attached",!0))}},unbind:function(a){var b=a.gl;b.bindFramebuffer(b.FRAMEBUFFER,null),this._binded=!1,this._cache.use(b.__GLID__);var d=this._cache.get("viewport");d&&a.setViewport(d.x,d.y,d.width,d.height);for(var e in this._attachedTextures){var f=this._attachedTextures[e];if(!f.NPOT&&f.useMipmap){var g=f instanceof c?b.TEXTURE_CUBE_MAP:b.TEXTURE_2D;b.bindTexture(g,f.getWebGLTexture(b)),b.generateMipmap(g),b.bindTexture(g,null)}}},getFrameBuffer:function(a){return this._cache.use(a.__GLID__),this._cache.miss("framebuffer")&&this._cache.put("framebuffer",a.createFramebuffer()),this._cache.get("framebuffer")},attach:function(a,b,c,f,g){if(!b.width)throw new Error("The texture attached to color buffer is not a valid.");if(this._binded||(a.bindFramebuffer(a.FRAMEBUFFER,this.getFrameBuffer(a)),this._binded=!0),this._width=b.width,this._height=b.height,c=c||a.COLOR_ATTACHMENT0,f=f||a.TEXTURE_2D,g=g||0,c===a.DEPTH_ATTACHMENT){var h=d.getExtension(a,"WEBGL_depth_texture");if(!h)return console.error(" Depth texture is not supported by the browser"),void 0;if(b.format!==e.DEPTH_COMPONENT)return console.error("The texture attached to depth buffer is not a valid."),void 0;this._cache.put("renderbuffer_attached",!1),this._cache.put("depthtexture_attached",!0)}this._attachedTextures[c]=b,a.framebufferTexture2D(a.FRAMEBUFFER,c,f,b.getWebGLTexture(a),g)},detach:function(){},dispose:function(a){this._cache.use(a.__GLID__);var b=this._cache.get("renderbuffer");b&&a.deleteRenderbuffer(b);var c=this._cache.get("framebuffer");c&&a.deleteFramebuffer(c),this._attachedTextures={},this._width=this._height=0,this._cache.deleteContext(a.__GLID__)}});return g.COLOR_ATTACHMENT0=e.COLOR_ATTACHMENT0,g.DEPTH_ATTACHMENT=e.DEPTH_ATTACHMENT,g.STENCIL_ATTACHMENT=e.STENCIL_ATTACHMENT,g.DEPTH_STENCIL_ATTACHMENT=e.DEPTH_STENCIL_ATTACHMENT,g}),d("qtek/Joint",["require","./core/Base"],function(a){var b=a("./core/Base"),c=b.derive({name:"",index:-1,parentIndex:-1,node:null,rootNode:null});return c}),d("qtek/Shader",["require","./core/Base","./core/util","./core/Cache","./dep/glmatrix"],function(a){function b(){return{locations:{},attriblocations:{}}}function c(a){for(var b=a.split("\n"),c=0,d=b.length;d>c;c++)b[c]=c+1+": "+b[c];return b.join("\n")}var d=a("./core/Base"),e=a("./core/util"),f=a("./core/Cache"),g=a("./dep/glmatrix"),h=g.mat2,i=g.mat3,j=g.mat4,k=/uniform\s+(bool|float|int|vec2|vec3|vec4|ivec2|ivec3|ivec4|mat2|mat3|mat4|sampler2D|samplerCube)\s+([\w\,]+)?(\[.*?\])?\s*(:\s*([\S\s]+?))?;/g,l=/attribute\s+(float|int|vec2|vec3|vec4)\s+(\w*)\s*(:\s*(\w+))?;/g,m=/#define\s+(\w+)?(\s+[\w-.]+)?\s*\n/g,n={bool:"1i","int":"1i",sampler2D:"t",samplerCube:"t","float":"1f",vec2:"2f",vec3:"3f",vec4:"4f",ivec2:"2i",ivec3:"3i",ivec4:"4i",mat2:"m2",mat3:"m3",mat4:"m4"},o={bool:function(){return!0},"int":function(){return 0},"float":function(){return 0},sampler2D:function(){return null},samplerCube:function(){return null},vec2:function(){return[0,0]},vec3:function(){return[0,0,0]},vec4:function(){return[0,0,0,0]},ivec2:function(){return[0,0]},ivec3:function(){return[0,0,0]},ivec4:function(){return[0,0,0,0]},mat2:function(){return h.create()},mat3:function(){return i.create()},mat4:function(){return j.create()},array:function(){return[]}},p=["POSITION","NORMAL","BINORMAL","TANGENT","TEXCOORD","TEXCOORD_0","TEXCOORD_1","COLOR","JOINT","WEIGHT","SKIN_MATRIX"],q=["WORLD","VIEW","PROJECTION","WORLDVIEW","VIEWPROJECTION","WORLDVIEWPROJECTION","WORLDINVERSE","VIEWINVERSE","PROJECTIONINVERSE","WORLDVIEWINVERSE","VIEWPROJECTIONINVERSE","WORLDVIEWPROJECTIONINVERSE","WORLDTRANSPOSE","VIEWTRANSPOSE","PROJECTIONTRANSPOSE","WORLDVIEWTRANSPOSE","VIEWPROJECTIONTRANSPOSE","WORLDVIEWPROJECTIONTRANSPOSE","WORLDINVERSETRANSPOSE","VIEWINVERSETRANSPOSE","PROJECTIONINVERSETRANSPOSE","WORLDVIEWINVERSETRANSPOSE","VIEWPROJECTIONINVERSETRANSPOSE","WORLDVIEWPROJECTIONINVERSETRANSPOSE"],r={},s=1,t=2,u=3,v=d.derive(function(){return{vertex:"",fragment:"",precision:"mediump",attribSemantics:{},matrixSemantics:{},matrixSemanticKeys:[],uniformTemplates:{},attributeTemplates:{},vertexDefines:{},fragmentDefines:{},lightNumber:{},_attacheMaterialNumber:0,_uniformList:[],_textureStatus:{},_vertexProcessed:"",_fragmentProcessed:"",_currentLocationsMap:{}} -},function(){this._cache=new f,this._updateShaderString()},{setVertex:function(a){this.vertex=a,this._updateShaderString(),this.dirty()},setFragment:function(a){this.fragment=a,this._updateShaderString(),this.dirty()},bind:function(a){if(this._cache.use(a.__GLID__,b),this._currentLocationsMap=this._cache.get("locations"),this._cache.isDirty()){this._updateShaderString();var c=this._buildProgram(a,this._vertexProcessed,this._fragmentProcessed);if(this._cache.fresh(),c)return c}a.useProgram(this._cache.get("program"))},dirty:function(){this._cache.dirtyAll();for(var a=0;ak;k++)g[h++]=j[k];a.uniformMatrix4fv(f,!1,g)}else d instanceof Float32Array&&a.uniformMatrix4fv(f,!1,d)}return!0},setUniformBySemantic:function(a,b,c){var d=this.attribSemantics[b];return d?this.setUniform(a,d.type,d.symbol,c):!1},enableAttributes:function(a,b,c){var d,e=this._cache.get("program"),f=this._cache.get("attriblocations");d=c?c.__enabledAttributeList:r[a.__GLID__],d||(d=c?c.__enabledAttributeList=[]:r[a.__GLID__]=[]);for(var g=[],h=0;h0&&a.push("#define "+b.toUpperCase()+"_NUMBER "+c)}for(var d in this._textureStatus){var e=this._textureStatus[d];e.enabled&&a.push("#define "+d.toUpperCase()+"_ENABLED")}for(var d in this.vertexDefines){var f=this.vertexDefines[d];null===f?a.push("#define "+d):a.push("#define "+d+" "+f.toString())}this._vertexProcessed=a.join("\n")+"\n"+this._vertexProcessedNoDefine,a=[];for(var b in this.lightNumber){var c=this.lightNumber[b];c>0&&a.push("#define "+b.toUpperCase()+"_NUMBER "+c)}for(var d in this._textureStatus){var e=this._textureStatus[d];e.enabled&&a.push("#define "+d.toUpperCase()+"_ENABLED")}for(var d in this.fragmentDefines){var f=this.fragmentDefines[d];null===f?a.push("#define "+d):a.push("#define "+d+" "+f.toString())}var g=a.join("\n")+"\n"+this._fragmentProcessedNoDefine;this._fragmentProcessed=["precision",this.precision,"float"].join(" ")+";\n"+g},_parseUniforms:function(){function a(a,e,f,g,h,i){if(e&&f){var j,k=n[e],l=!0;if(k){if(c._uniformList.push(f),("sampler2D"===e||"samplerCube"===e)&&(c._textureStatus[f]={enabled:!1,shaderType:d}),g&&(k+="v"),i)if(p.indexOf(i)>=0)c.attribSemantics[i]={symbol:f,type:k},l=!1;else if(q.indexOf(i)>=0){var m=!1,r=i;i.match(/TRANSPOSE$/)&&(m=!0,r=i.slice(0,-9)),c.matrixSemantics[i]={symbol:f,type:k,isTranspose:m,semanticNoTranspose:r},l=!1}else if("unconfigurable"===i)l=!1;else{if(j=c._parseDefaultValue(e,i),!j)throw new Error('Unkown semantic "'+i+'"');i=""}l&&(b[f]={type:k,value:g?o.array:j||o[e],semantic:i||null})}return["uniform",e,f,g].join(" ")+";\n"}}var b={},c=this,d="vertex";this._uniformList=[],this._vertexProcessedNoDefine=this._vertexProcessedNoDefine.replace(k,a),d="fragment",this._fragmentProcessedNoDefine=this._fragmentProcessedNoDefine.replace(k,a),c.matrixSemanticKeys=Object.keys(this.matrixSemantics),this.uniformTemplates=b},_parseDefaultValue:function(a,b){var c=/\[\s*(.*)\s*\]/;{if("vec2"!==a&&"vec3"!==a&&"vec4"!==a)return"bool"===a?function(){return"true"===b.toLowerCase()?!0:!1}:"float"===a?function(){return parseFloat(b)}:void 0;var d=c.exec(b)[1];if(d){var e=d.split(/\s*,\s*/);return function(){return new Float32Array(e)}}}},createUniforms:function(){var a={};for(var b in this.uniformTemplates){var c=this.uniformTemplates[b];a[b]={type:c.type,value:c.value()}}return a},attached:function(){this._attacheMaterialNumber++},detached:function(){this._attacheMaterialNumber--},isAttachedToAny:function(){return 0!==this._attacheMaterialNumber},_parseAttributes:function(){function a(a,d,e,f,g){if(d&&e){var h=1;switch(d){case"vec4":h=4;break;case"vec3":h=3;break;case"vec2":h=2;break;case"float":h=1}if(b[e]={type:"float",size:h,semantic:g||null},g){if(p.indexOf(g)<0)throw new Error('Unkown semantic "'+g+'"');c.attribSemantics[g]={symbol:e,type:d}}}return["attribute",d,e].join(" ")+";\n"}var b={},c=this;this._vertexProcessedNoDefine=this._vertexProcessedNoDefine.replace(l,a),this.attributeTemplates=b},_parseDefines:function(){function a(a,d,e){var f="vertex"===c?b.vertexDefines:b.fragmentDefines;return f[d]||(f[d]="false"==e?!1:"true"==e?!0:e?parseFloat(e):null),""}var b=this,c="vertex";this._vertexProcessedNoDefine=this._vertexProcessedNoDefine.replace(m,a),c="fragment",this._fragmentProcessedNoDefine=this._fragmentProcessedNoDefine.replace(m,a)},_buildProgram:function(a,b,c){this._cache.get("program")&&a.deleteProgram(this._cache.get("program"));var d=a.createProgram(),e=a.createShader(a.VERTEX_SHADER);a.shaderSource(e,b),a.compileShader(e);var f=a.createShader(a.FRAGMENT_SHADER);a.shaderSource(f,c),a.compileShader(f);var g=this._checkShaderErrorMsg(a,e,b);if(g)return g;if(g=this._checkShaderErrorMsg(a,f,c))return g;if(a.attachShader(d,e),a.attachShader(d,f),this.attribSemantics.POSITION)a.bindAttribLocation(d,0,this.attribSemantics.POSITION.symbol);else{var h=Object.keys(this.attributeTemplates);a.bindAttribLocation(d,0,h[0])}if(a.linkProgram(d),!a.getProgramParameter(d,a.LINK_STATUS))return"Could not link program\nVALIDATE_STATUS: "+a.getProgramParameter(d,a.VALIDATE_STATUS)+", gl error ["+a.getError()+"]";for(var i=0;i=0&&this._enabledUniforms.splice(b,1)},isUniformEnabled:function(a){return this._enabledUniforms.indexOf(a)>=0},set:function(a,b){if("object"==typeof a)for(var c in a){var d=a[c];this.set(c,d)}else{var e=this.uniforms[a];e&&(e.value=b)}},get:function(a){var b=this.uniforms[a];return b?b.value:void 0},attachShader:function(a,b){this.shader&&this.shader.detached();var c=this.uniforms;if(this.uniforms=a.createUniforms(),this.shader=a,this._enabledUniforms=Object.keys(this.uniforms),b)for(var d in c)this.uniforms[d]&&(this.uniforms[d].value=c[d].value);a.attached()},detachShader:function(){this.shader.detached(),this.shader=null,this.uniforms={}},clone:function(){var a=new d({name:this.name,shader:this.shader});for(var b in this.uniforms)a.uniforms[b].value=this.uniforms[b].value;return a.depthTest=this.depthTest,a.depthMask=this.depthMask,a.transparent=this.transparent,a.blend=this.blend,a},dispose:function(a,b){if(b)for(var d in this.uniforms){var e=this.uniforms[d].value;if(e)if(e instanceof c)e.dispose(a);else if(e instanceof Array)for(var f=0;f65535,s=r?a.UNSIGNED_INT:a.UNSIGNED_SHORT,t=f.getExtension(a,"OES_vertex_array_object"),u=!m.dynamic,v=this._renderInfo;v.vertexNumber=o,v.faceNumber=0,v.drawCallNumber=0;var w=!1;if(c=a.__GLID__+"-"+m.__GUID__+"-"+l.__GUID__,c!==h?w=!0:(m instanceof g&&o>65535&&!q&&p||t&&u||m._cache.isDirty())&&(w=!0),h=c,w){var x=this._drawCache[c];if(!x){var y=m.getBufferChunks(a);if(!y)return;x=[];for(var z=0;z0&&this.setViewport(this._viewportSettings.pop())},saveClear:function(){this._clearSettings.push(this.clear)},restoreClear:function(){this._clearSettings.length>0&&(this.clear=this._clearSettings.pop())},render:function(a,b,c,d){var e=this.gl;this._sceneRendering=a;var f=this.color;this.clear&&(e.colorMask(!0,!0,!0,!0),e.depthMask(!0),e.clearColor(f[0],f[1],f[2],f[3]),e.clear(this.clear)),c||a.update(!1),b.getScene()||b.update(!0);var g=a.opaqueQueue,h=a.transparentQueue,i=a.material;if(a.trigger("beforerender",this,a,b),h.length>0)for(var j=k.create(),m=l.create(),n=0;n0&&a.min._array[2]<0&&(a.max._array[2]=-1e-20),a.applyProjection(b);var h=a.min._array,i=a.max._array;if(i[0]<-1||h[0]>1||i[1]<-1||h[1]>1||i[2]<-1||h[2]>1)return!0}return!1}}(),disposeScene:function(a){this.disposeNode(a,!0,!0),a.dispose()},disposeNode:function(a,b,c){var d={},e=this.gl;a.getParent()&&a.getParent().remove(a),a.traverse(function(a){a.geometry&&b&&a.geometry.dispose(e),a.material&&(d[a.material.__GUID__]=a.material),a.dispose&&a.dispose(e) -});for(var f in d){var g=d[f];g.dispose(e,c)}},disposeShader:function(a){a.dispose(this.gl)},disposeGeometry:function(a){a.dispose(this.gl)},disposeTexture:function(a){a.dispose(this.gl)},disposeFrameBuffer:function(a){a.dispose(this.gl)},dispose:function(){c.dispose(this.gl)},screenToNdc:function(a,b,c){c||(c=new i),b=this.height-b;var d=this.viewport,e=c._array;return e[0]=(a-d.x)/d.width,e[0]=2*e[0]-1,e[1]=(b-d.y)/d.height,e[1]=2*e[1]-1,c}});o.opaqueSortFunc=function(a,b){return a.material.shader===b.material.shader?a.material===b.material?a.geometry.__GUID__-b.geometry.__GUID__:a.material.__GUID__-b.material.__GUID__:a.material.shader.__GUID__-b.material.shader.__GUID__},o.transparentSortFunc=function(a,b){return a.__depth===b.__depth?a.material.shader===b.material.shader?a.material===b.material?a.geometry.__GUID__-b.geometry.__GUID__:a.material.__GUID__-b.material.__GUID__:a.material.shader.__GUID__-b.material.shader.__GUID__:a.__depth-b.__depth};var p={WORLD:k.create(),VIEW:k.create(),PROJECTION:k.create(),WORLDVIEW:k.create(),VIEWPROJECTION:k.create(),WORLDVIEWPROJECTION:k.create(),WORLDINVERSE:k.create(),VIEWINVERSE:k.create(),PROJECTIONINVERSE:k.create(),WORLDVIEWINVERSE:k.create(),VIEWPROJECTIONINVERSE:k.create(),WORLDVIEWPROJECTIONINVERSE:k.create(),WORLDTRANSPOSE:k.create(),VIEWTRANSPOSE:k.create(),PROJECTIONTRANSPOSE:k.create(),WORLDVIEWTRANSPOSE:k.create(),VIEWPROJECTIONTRANSPOSE:k.create(),WORLDVIEWPROJECTIONTRANSPOSE:k.create(),WORLDINVERSETRANSPOSE:k.create(),VIEWINVERSETRANSPOSE:k.create(),PROJECTIONINVERSETRANSPOSE:k.create(),WORLDVIEWINVERSETRANSPOSE:k.create(),VIEWPROJECTIONINVERSETRANSPOSE:k.create(),WORLDVIEWPROJECTIONINVERSETRANSPOSE:k.create()};return o.COLOR_BUFFER_BIT=d.COLOR_BUFFER_BIT,o.DEPTH_BUFFER_BIT=d.DEPTH_BUFFER_BIT,o.STENCIL_BUFFER_BIT=d.STENCIL_BUFFER_BIT,o}),d("qtek/Scene",["require","./Node","./Light"],function(a){function b(a,b){return b.castShadow&&!a.castShadow?!0:void 0}var c=a("./Node"),d=a("./Light"),e=c.derive(function(){return{material:null,autoUpdate:!0,opaqueQueue:[],transparentQueue:[],lights:[],_lightUniforms:{},_lightNumber:{POINT_LIGHT:0,DIRECTIONAL_LIGHT:0,SPOT_LIGHT:0,AMBIENT_LIGHT:0},_opaqueObjectCount:0,_transparentObjectCount:0,_nodeRepository:{}}},function(){this._scene=this},{addToScene:function(a){a.name&&(this._nodeRepository[a.name]=a)},removeFromScene:function(a){a.name&&delete this._nodeRepository[a.name]},getNode:function(a){return this._nodeRepository[a]},cloneNode:function(a){var b=a.clone(),c={},d=function(e,f){e.skeleton&&(f.skeleton=e.skeleton.clone(a,b),f.joints=e.joints.slice()),e.material&&(c[e.material.__GUID__]={oldMat:e.material});for(var g=0;g0&&this._updateRenderQueue(e)}},_updateLightUniforms:function(){var a=this.lights;a.sort(b);var c=this._lightUniforms;for(var d in c)c[d].value.length=0;for(var e=0;e=0){var d=a[c.parentIndex].node;d.add(c.node)}else this.roots.push(c)}},addClip:function(a,b){for(var c=0;c0&&this._clips.splice(b,1)},removeClipsAll:function(){this._clips=[]},getClip:function(a){return this._clips[a]?this._clips[a].clip:void 0},getClipNumber:function(){return this._clips.length},updateJointMatrices:function(){var a=g.create();return function(){for(var b=0;be;e++)this._invBindPoseMatricesArray[d+e]=a[e]}this.updateMatricesSubArrays()}}(),updateMatricesSubArrays:function(){for(var a=0;ag;g++)c[d++]=this._skinMatricesArray[16*f+g];return c},setPose:function(a){if(this._clips[a]){for(var b=this._clips[a].clip,c=this._clips[a].maps,d=0;de;e++)d._invBindPoseMatricesArray[e]=this._invBindPoseMatricesArray[e];d._skinMatricesArray=new Float32Array(j),d.updateMatricesSubArrays()}return d.update(),d}});return h}),d("qtek/StaticGeometry",["require","./Geometry","./math/BoundingBox","./dep/glmatrix","./core/glenum"],function(a){var b=a("./Geometry"),c=a("./math/BoundingBox"),d=a("./dep/glmatrix"),e=a("./core/glenum"),f=d.mat4,g=d.vec3,h=b.derive(function(){return{attributes:{position:new b.Attribute("position","float",3,"POSITION",!1),texcoord0:new b.Attribute("texcoord0","float",2,"TEXCOORD_0",!1),texcoord1:new b.Attribute("texcoord1","float",2,"TEXCOORD_1",!1),normal:new b.Attribute("normal","float",3,"NORMAL",!1),tangent:new b.Attribute("tangent","float",4,"TANGENT",!1),color:new b.Attribute("color","float",4,"COLOR",!1),weight:new b.Attribute("weight","float",3,"WEIGHT",!1),joint:new b.Attribute("joint","float",4,"JOINT",!1),barycentric:new b.Attribute("barycentric","float",3,null,!1)},hint:e.STATIC_DRAW,faces:null,_normalType:"vertex",_enabledAttributes:null}},{dirty:function(){this._cache.dirtyAll(),this._enabledAttributes=null},getVertexNumber:function(){var a=this.attributes[this.mainAttribute];return a&&a.value?a.value.length/a.size:0},getFaceNumber:function(){return this.faces?this.faces.length/3:0},getFace:function(a,b){return a=0?(b||(b=g.create()),b[0]=this.faces[3*a],b[1]=this.faces[3*a+1],b[2]=this.faces[3*a+2],b):void 0},isUseFace:function(){return this.useFace&&null!=this.faces},createAttribute:function(a,c,d,e){var f=new b.Attribute(a,c,d,e,!1);return this.attributes[a]=f,this._attributeList.push(a),f},removeAttribute:function(a){var b=this._attributeList.indexOf(a);return b>=0?(this._attributeList.splice(b,1),delete this.attributes[a],!0):!1},getEnabledAttributes:function(){if(this._enabledAttributes)return this._enabledAttributes;for(var a=[],b=this.getVertexNumber(),c=0;c=0;o--)if(f[o].name===m){l=f[o],i=o;break}}var p;p=l?l.buffer:a.createBuffer(),a.bindBuffer(a.ARRAY_BUFFER,p),a.bufferData(a.ARRAY_BUFFER,n.value,this.hint),f[j++]=new b.AttributeBuffer(m,n.type,p,n.size,n.semantic)}f.length=j,this.isUseFace()&&(g||(g=new b.IndicesBuffer(a.createBuffer()),e.indicesBuffer=g),g.count=this.faces.length,a.bindBuffer(a.ELEMENT_ARRAY_BUFFER,g.buffer),a.bufferData(a.ELEMENT_ARRAY_BUFFER,this.faces,this.hint))},generateVertexNormals:function(){var a=this.faces,b=this.attributes.position.value,c=this.attributes.normal.value;if(c&&c.length===b.length)for(var d=0;dd;d++)c[3*m+d]=c[3*m+d]+k[d],c[3*n+d]=c[3*n+d]+k[d],c[3*o+d]=c[3*o+d]+k[d]}for(var d=0;do;o++)c[3*l+o]=j[o],c[3*m+o]=j[o],c[3*n+o]=j[o]}},generateTangents:function(){var a=this.getVertexNumber();this.attributes.tangent.value||(this.attributes.tangent.value=new Float32Array(4*a));for(var b=this.attributes.texcoord0.value,c=this.attributes.position.value,d=this.attributes.tangent.value,e=this.attributes.normal.value,f=[],h=[],i=0;a>i;i++)f[i]=[0,0,0],h[i]=[0,0,0];for(var j=[0,0,0],k=[0,0,0],i=0;ii;i++){Q[0]=e[3*i],Q[1]=e[3*i+1],Q[2]=e[3*i+2];var R=f[i];g.scale(O,Q,g.dot(Q,R)),g.sub(O,R,O),g.normalize(O,O),g.cross(P,Q,R),d[4*i]=O[0],d[4*i+1]=O[1],d[4*i+2]=O[2],d[4*i+3]=g.dot(P,h[i])<0?-1:1}},isUniqueVertex:function(){return this.isUseFace()?this.getVertexNumber()===this.faces.length:!0},generateUniqueVertex:function(){for(var a=[],b=0,c=this.getVertexNumber();c>b;b++)a[b]=0;for(var d=this.getVertexNumber(),e=this.attributes,f=this.faces,g=this.getEnabledAttributes(),h=0;hb;b++)j[b]=e[i].value[b];e[i].value=j}for(var b=0;b0){for(var h=0;hn;n++)l[d*m+n]=l[k*m+n];f[b]=d,d++}a[k]++}},generateBarycentric:function(){this.isUniqueVertex()||this.generateUniqueVertex();var a=this.attributes.barycentric.value;if(!a||a.length!==3*this.faces.length){a=this.attributes.barycentric.value=new Float32Array(3*this.faces.length);for(var b=0;bc;c++){var d=this.faces[b++];a[d+c]=1}}},convertToDynamic:function(a){for(var b=0;b1&&a.texParameterf(a.TEXTURE_2D,f.TEXTURE_MAX_ANISOTROPY_EXT,this.anisotropic),36193===e){var g=c.getExtension(a,"OES_texture_half_float");g||(e=d.FLOAT)}if(this.mipmaps.length)for(var h=this.width,i=this.height,j=0;j=b.COMPRESSED_RGB_S3TC_DXT1_EXT?a.compressedTexImage2D(a.TEXTURE_2D,d,g,e,f,0,c.pixels):a.texImage2D(a.TEXTURE_2D,d,g,e,f,0,g,h,c.pixels)},generateMipmap:function(a){this.useMipmap&&!this.NPOT&&(a.bindTexture(a.TEXTURE_2D,this._cache.get("webgl_texture")),a.generateMipmap(a.TEXTURE_2D))},isPowerOfTwo:function(){var a,b;return this.image?(a=this.image.width,b=this.image.height):(a=this.width,b=this.height),0===(a&a-1)&&0===(b&b-1)},isRenderable:function(){return this.image?"CANVAS"===this.image.nodeName||this.image.complete:this.width&&this.height},bind:function(a){a.bindTexture(a.TEXTURE_2D,this.getWebGLTexture(a))},unbind:function(a){a.bindTexture(a.TEXTURE_2D,null)},load:function(a){var b=new Image,c=this;return b.onload=function(){c.dirty(),c.trigger("success",c),b.onload=null},b.onerror=function(){c.trigger("error",c),b.onerror=null},b.src=a,this.image=b,this}});return e}),d("qtek/animation/easing",[],function(){var a={Linear:function(a){return a},QuadraticIn:function(a){return a*a},QuadraticOut:function(a){return a*(2-a)},QuadraticInOut:function(a){return(a*=2)<1?.5*a*a:-.5*(--a*(a-2)-1)},CubicIn:function(a){return a*a*a},CubicOut:function(a){return--a*a*a+1},CubicInOut:function(a){return(a*=2)<1?.5*a*a*a:.5*((a-=2)*a*a+2)},QuarticIn:function(a){return a*a*a*a},QuarticOut:function(a){return 1- --a*a*a*a},QuarticInOut:function(a){return(a*=2)<1?.5*a*a*a*a:-.5*((a-=2)*a*a*a-2)},QuinticIn:function(a){return a*a*a*a*a},QuinticOut:function(a){return--a*a*a*a*a+1},QuinticInOut:function(a){return(a*=2)<1?.5*a*a*a*a*a:.5*((a-=2)*a*a*a*a+2)},SinusoidalIn:function(a){return 1-Math.cos(a*Math.PI/2)},SinusoidalOut:function(a){return Math.sin(a*Math.PI/2)},SinusoidalInOut:function(a){return.5*(1-Math.cos(Math.PI*a))},ExponentialIn:function(a){return 0===a?0:Math.pow(1024,a-1)},ExponentialOut:function(a){return 1===a?1:1-Math.pow(2,-10*a)},ExponentialInOut:function(a){return 0===a?0:1===a?1:(a*=2)<1?.5*Math.pow(1024,a-1):.5*(-Math.pow(2,-10*(a-1))+2)},CircularIn:function(a){return 1-Math.sqrt(1-a*a)},CircularOut:function(a){return Math.sqrt(1- --a*a)},CircularInOut:function(a){return(a*=2)<1?-.5*(Math.sqrt(1-a*a)-1):.5*(Math.sqrt(1-(a-=2)*a)+1)},ElasticIn:function(a){var b,c=.1,d=.4;return 0===a?0:1===a?1:(!c||1>c?(c=1,b=d/4):b=d*Math.asin(1/c)/(2*Math.PI),-(c*Math.pow(2,10*(a-=1))*Math.sin((a-b)*2*Math.PI/d)))},ElasticOut:function(a){var b,c=.1,d=.4;return 0===a?0:1===a?1:(!c||1>c?(c=1,b=d/4):b=d*Math.asin(1/c)/(2*Math.PI),c*Math.pow(2,-10*a)*Math.sin((a-b)*2*Math.PI/d)+1)},ElasticInOut:function(a){var b,c=.1,d=.4;return 0===a?0:1===a?1:(!c||1>c?(c=1,b=d/4):b=d*Math.asin(1/c)/(2*Math.PI),(a*=2)<1?-.5*c*Math.pow(2,10*(a-=1))*Math.sin((a-b)*2*Math.PI/d):.5*c*Math.pow(2,-10*(a-=1))*Math.sin((a-b)*2*Math.PI/d)+1)},BackIn:function(a){var b=1.70158;return a*a*((b+1)*a-b)},BackOut:function(a){var b=1.70158;return--a*a*((b+1)*a+b)+1},BackInOut:function(a){var b=2.5949095;return(a*=2)<1?.5*a*a*((b+1)*a-b):.5*((a-=2)*a*((b+1)*a+b)+2)},BounceIn:function(b){return 1-a.BounceOut(1-b)},BounceOut:function(a){return 1/2.75>a?7.5625*a*a:2/2.75>a?7.5625*(a-=1.5/2.75)*a+.75:2.5/2.75>a?7.5625*(a-=2.25/2.75)*a+.9375:7.5625*(a-=2.625/2.75)*a+.984375},BounceInOut:function(b){return.5>b?.5*a.BounceIn(2*b):.5*a.BounceOut(2*b-1)+.5}};return a}),d("qtek/animation/Clip",["require","./easing"],function(a){var b=a("./easing"),c=function(a){a=a||{},this.name=a.name||"",this.target=a.target,"undefined"!=typeof a.life&&(this.life=a.life),"undefined"!=typeof a.delay&&(this.delay=a.delay),"undefined"!=typeof a.gap&&(this.gap=a.gap),this.playbackRatio="undefined"!=typeof a.playbackRatio?a.playbackRatio:1,this._currentTime=(new Date).getTime(),this._startTime=this._currentTime+this.delay,this._elapsedTime=0,this._loop=void 0===a.loop?!1:a.loop,this.setLoop(this._loop),"undefined"!=typeof a.easing&&this.setEasing(a.easing),"undefined"!=typeof a.onframe&&(this.onframe=a.onframe),"undefined"!=typeof a.ondestroy&&(this.ondestroy=a.ondestroy),"undefined"!=typeof a.onrestart&&(this.onrestart=a.onrestart)};return c.prototype={gap:0,life:0,delay:0,setLoop:function(a){this._loop=a,a&&(this._loopRemained="number"==typeof a?a:1e8)},setEasing:function(a){"string"==typeof a&&(a=b[a]),this.easing=a},step:function(a){if(ab)){b>1&&(b=1);var c;return c=this.easing?this.easing(b):b,this.fire("frame",c),1==b?this._loop&&this._loopRemained>0?(this._restartInLoop(),this._loopRemained--,"restart"):(this._needsRemove=!0,"destroy"):null}},setTime:function(a){return this.step(a+this._startTime)},restart:function(){var a=(new Date).getTime();this._elapse(a);var b=this._elapsedTime%this.life;this._startTime=a-b+this.delay,this._elapsedTime=0,this._currentTime=a-b,this._needsRemove=!1},_restartInLoop:function(){var a=(new Date).getTime();this._startTime=a+this.gap,this._currentTime=a,this._elapsedTime=0},_elapse:function(a){this._elapsedTime+=(a-this._currentTime)*this.playbackRatio,this._currentTime=a},fire:function(a,b){var c="on"+a;this[c]&&this[c](this.target,b)},clone:function(){var a=new this.constructor;return a.name=this.name,a._loop=this._loop,a._loopRemained=this._loopRemained,a.life=this.life,a.gap=this.gap,a.delay=this.delay,a}},c.prototype.constructor=c,c}),d("qtek/animation/Animation",["require","./Clip","../core/Base"],function(a){function b(a,b){return a[b]}function c(a,b,c){a[b]=c}function d(a,b,c){return(b-a)*c+a}function e(a,b,c,e,f){var g=a.length;if(1==f)for(var h=0;g>h;h++)e[h]=d(a[h],b[h],c);else for(var i=a[0].length,h=0;g>h;h++)for(var j=0;i>j;j++)e[h][j]=d(a[h][j],b[h][j],c)}function f(a){return"undefined"==typeof a?!1:"string"==typeof a?!1:"number"==typeof a.length}function g(a){if(f(a)){var b=a.length;if(f(a[0])){for(var c=[],d=0;b>d;d++)c.push(n.call(a[d]));return c}return n.call(a)}return a}function h(a,b,c,d,e,f,g,h,j){var k=a.length;if(1==j)for(var l=0;k>l;l++)h[l]=i(a[l],b[l],c[l],d[l],e,f,g);else for(var m=a[0].length,l=0;k>l;l++)for(var n=0;m>n;n++)h[l][n]=i(a[l][n],b[l][n],c[l][n],d[l][n],e,f,g)}function i(a,b,c,d,e,f,g){var h=.5*(c-a),i=.5*(d-b);return(2*(b-c)+h+i)*g+(-3*(b-c)-2*h-i)*f+h*e+b}function j(a,d,e,f,g){this._tracks={},this._target=a,this._loop=d||!1,this._getter=e||b,this._setter=f||c,this._interpolater=g||null,this._clipCount=0,this._delay=0,this._doneList=[],this._onframeList=[],this._clipList=[]}var k=a("./Clip"),l=a("../core/Base"),m=window.requestAnimationFrame||window.msRequestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||function(a){setTimeout(a,16)},n=Array.prototype.slice,o=l.derive(function(){return{stage:null,_clips:[],_running:!1,_time:0}},{addClip:function(a){this._clips.push(a)},removeClip:function(a){var b=this._clips.indexOf(a);this._clips.splice(b,1)},_update:function(){for(var a=(new Date).getTime(),b=a-this._time,c=this._clips,d=c.length,e=[],f=[],g=0;d>g;g++){var h=c[g],i=h.step(a);i&&(e.push(i),f.push(h))}for(var g=0;d>g;)c[g]._needsRemove?(c[g]=c[d-1],c.pop(),d--):g++;d=e.length;for(var g=0;d>g;g++)f[g].fire(e[g]);this._time=a,this.trigger("frame",b),this.stage&&this.stage.render&&this.stage.render()},start:function(){function a(){b._running&&(m(a),b._update())}var b=this;this._running=!0,this._time=(new Date).getTime(),m(a)},stop:function(){this._running=!1},removeClipsAll:function(){this._clips=[]},animate:function(a,b){b=b||{};var c=new j(a,b.loop,b.getter,b.setter,b.interpolater);return c.animation=this,c}});return j.prototype={constructor:j,when:function(a,b){for(var c in b)this._tracks[c]||(this._tracks[c]=[],0!==a&&this._tracks[c].push({time:0,value:g(this._getter(this._target,c))})),this._tracks[c].push({time:parseInt(a),value:b[c]});return this},during:function(a){return this._onframeList.push(a),this},start:function(a){var b=this,c=this._setter,g=this._getter,j=this._interpolater,l=b._onframeList.length,m="spline"===a,n=function(){if(b._clipCount--,0===b._clipCount){b._tracks={};for(var a=b._doneList.length,c=0;a>c;c++)b._doneList[c].call(b)}},o=function(o,p){var q=o.length;if(q){var r=o[0].value,s=f(r),t=s&&f(r[0])?2:1;o.sort(function(a,b){return a.time-b.time});for(var u=o[q-1].time,v=[],w=[],x=0;q>x;x++)v.push(o[x].time/u),w.push(o[x].value);var y,x,z,A,B,C,D,E=0,F=0,G=function(a,f){if(F>f){for(y=Math.min(E+1,q-1),x=y;x>=0&&!(v[x]<=f);x--);x=Math.min(x,q-2)}else{for(x=E;q>x&&!(v[x]>f);x++);x=Math.min(x-1,q-2)}E=x,F=f;var k=v[x+1]-v[x];if(0!==k)for(z=(f-v[x])/k,m?(B=w[x],A=w[0===x?x:x-1],C=w[x>q-2?q-1:x+1],D=w[x>q-3?q-1:x+2],j?c(a,p,j(g(a,p),A,B,C,D,z)):s?h(A,B,C,D,z,z*z,z*z*z,g(a,p),t):c(a,p,i(A,B,C,D,z,z*z,z*z*z))):j?c(a,p,j(g(a,p),w[x],w[x+1],z)):s?e(w[x],w[x+1],z,g(a,p),t):c(a,p,d(w[x],w[x+1],z)),x=0;l>x;x++)b._onframeList[x](a,f)},H=new k({target:b._target,life:u,loop:b._loop,delay:b._delay,onframe:G,ondestroy:n});a&&"spline"!==a&&H.setEasing(a),b._clipList.push(H),b._clipCount++,b.animation.addClip(H)}};for(var p in this._tracks)o(this._tracks[p],p);return this},stop:function(){for(var a=0;aa)this.inputs.unshift(d);else if(this.inputs[e-1].position<=a)this.inputs.push(d);else{var f=this._findKey(a);this.inputs.splice(f,d)}return d},d.prototype.step=function(a){var c=b.prototype.step.call(this,a);return"destroy"!==c&&this.setTime(this._elapsedTime),c},d.prototype.setTime=function(a){var c=this.position,d=this.inputs,e=d.length,f=d[0].position,g=d[e-1].position;if(f>=c||c>=g){var h=f>=c?d[0]:d[e-1],i=h.clip,j=h.offset;i.setTime((a+j)%i.life),i.output instanceof b?this.output.copy(i.output):this.output.copy(i)}else{var k=this._findKey(c),l=d[k],m=d[k+1],n=l.clip,o=m.clip;n.setTime((a+l.offset)%n.life),o.setTime((a+m.offset)%o.life);var p=(this.position-l.position)/(m.position-l.position),q=n.output instanceof b?n.output:n,r=o.output instanceof b?o.output:o;this.output.blend1D(q,r,p)}},d.prototype.clone=function(a){var c=b.prototype.clone.call(this);c.output=this.output.clone();for(var d=0;de;e++)a>=c[e].position&&a=0;e--)a>=c[e].position&&a=0&&(this._cacheKey=b,this._cachePosition=a),b},d}),d("qtek/util/delaunay",["require"],function(){function a(a){var b,c,d,e,f,g,h=Number.POSITIVE_INFINITY,i=Number.POSITIVE_INFINITY,j=Number.NEGATIVE_INFINITY,k=Number.NEGATIVE_INFINITY;for(b=a.length;b--;)a[b][0]j&&(j=a[b][0]),a[b][1]k&&(k=a[b][1]);c=j-h,d=k-i,e=Math.max(c,d),f=h+.5*c,g=i+.5*d,a.push([f-20*e,g-e],[f,g+20*e],[f+20*e,g-e])}function b(a,b,c,d){var e,f,g,h,i,j,k=a[b],l=a[c],m=a[d],n=l[0]-k[0],o=l[1]-k[1],p=m[0]-k[0],q=m[1]-k[1],r=n*(k[0]+l[0])+o*(k[1]+l[1]),s=p*(k[0]+m[0])+q*(k[1]+m[1]),t=2*(n*(m[1]-l[1])-o*(m[0]-l[0]));return Math.abs(t)<1e-6?(e=Math.min(k[0],l[0],m[0]),f=Math.min(k[1],l[1],m[1]),g=.5*(Math.max(k[0],l[0],m[0])-e),h=.5*(Math.max(k[1],l[1],m[1])-f),i=e+g,j=f+h):(i=(q*r-o*s)/t,j=(n*s-p*r)/t,g=i-k[0],h=j-k[1]),{i:b,j:c,k:d,x:i,y:j,r:g*g+h*h}}function c(a){var b,c,d,e,f,g=a.length;a:for(;g;)for(c=a[--g],b=a[--g],d=g;d;)if(f=a[--d],e=a[--d],b===e&&c===f||b===f&&c===e){a.splice(g,2),a.splice(d,2),g-=2;continue a}}var d={triangulate:function(d,e){var f,g,h,i,j,k,l,m,n,o,p,q=d.length;if(3>q)return[];if(d=d.slice(0),e)for(f=q;f--;)d[f]=d[f][e];for(h=new Array(q),f=q;f--;)h[f]=f;for(h.sort(function(a,b){return d[b][0]-d[a][0]}),a(d),i=[b(d,q+0,q+1,q+2)],j=[],k=[],f=h.length;f--;){for(p=h[f],k.length=0,g=i.length;g--;)l=d[p][0]-i[g].x,l>0&&l*l>i[g].r?(j.push(i[g]),i.splice(g,1)):(m=d[p][1]-i[g].y,l*l+m*m>i[g].r||(k.push(i[g].i,i[g].j,i[g].j,i[g].k,i[g].k,i[g].i),i.splice(g,1)));for(c(k),g=k.length;g;)o=k[--g],n=k[--g],i.push(b(d,n,o,p))}for(f=i.length;f--;)j.push(i[f]);for(i.length=0,f=j.length;f--;)if(j[f].ia[0][0]&&b[0]>a[1][0]&&b[0]>a[2][0]||b[1]a[0][1]&&b[1]>a[1][1]&&b[1]>a[2][1])return null;var c=a[1][0]-a[0][0],d=a[2][0]-a[0][0],e=a[1][1]-a[0][1],f=a[2][1]-a[0][1],g=c*f-d*e;if(0===g)return null;var h=(f*(b[0]-a[0][0])-d*(b[1]-a[0][1]))/g,i=(c*(b[1]-a[0][1])-e*(b[0]-a[0][0]))/g;return 0>h||0>i||h+i>1?null:[h,i]}};return d}),d("qtek/animation/Blend2DClip",["require","./Clip","../util/delaunay","../math/Vector2"],function(a){var b=a("./Clip"),c=a("../util/delaunay"),d=a("../math/Vector2"),e=function(a){a=a||{},b.call(this,a),this.output=a.output||null,this.inputs=a.inputs||[],this.position=new d,this._cacheTriangle=null,this._triangles=[],this._updateTriangles()};return e.prototype=new b,e.prototype.constructor=e,e.prototype.addInput=function(a,b,c){var d={position:a,clip:b,offset:c||0};return this.inputs.push(d),this.life=Math.max(b.life,this.life),this._updateTriangles(),d},e.prototype._updateTriangles=function(){var a=this.inputs.map(function(a){return a.position});this._triangles=c.triangulate(a,"_array")},e.prototype.step=function(a){var c=b.prototype.step.call(this,a);return"destroy"!==c&&this.setTime(this._elapsedTime),c},e.prototype.setTime=function(a){var c=this._findTriangle(this.position);if(c){var d=c[1],e=c[2],f=c[0],g=this.inputs[f.indices[0]],h=this.inputs[f.indices[1]],i=this.inputs[f.indices[2]],j=g.clip,k=h.clip,l=i.clip; -j.setTime((a+g.offset)%j.life),k.setTime((a+h.offset)%k.life),l.setTime((a+i.offset)%l.life);var m=j.output instanceof b?j.output:j,n=k.output instanceof b?k.output:k,o=l.output instanceof b?l.output:l;this.output.blend2D(m,n,o,d,e)}},e.prototype.clone=function(a){var c=b.prototype.clone.call(this);c.output=this.output.clone();for(var d=0;d=a.time)return this.keyFrames.splice(b,0,a),b}this.life=a.time,this.keyFrames.push(a)},g.prototype.addKeyFrames=function(a){for(var c=0;cg[g.length-1].time)){if(a=h-1?h-1:this._cacheKey+1,j=i;j>=0;j--)if(g[j].time<=a&&g[j][b])c=g[j],this._cacheKey=j,this._cacheTime=a;else if(g[j][b]){d=g[j];break}}else for(var j=this._cacheKey;h>j;j++)if(g[j].time<=a&&g[j][b])c=g[j],this._cacheKey=j,this._cacheTime=a;else if(g[j][b]){d=g[j];break}if(c&&d){var k=(a-c.time)/(d.time-c.time);k=Math.max(Math.min(k,1),0),"rotation"===b?e.slerp(this[b],c[b],d[b],k):f.lerp(this[b],c[b],d[b],k)}else this._cacheKey=0,this._cacheTime=0}},g.prototype.blend1D=function(a,b,c){f.lerp(this.position,a.position,b.position,c),f.lerp(this.scale,a.scale,b.scale,c),e.slerp(this.rotation,a.rotation,b.rotation,c)},g.prototype.blend2D=function(){var a=e.create(),b=e.create();return function(c,d,f,g,h){var i=1-g-h;this.position[0]=c.position[0]*i+d.position[0]*g+f.position[0]*h,this.position[1]=c.position[1]*i+d.position[1]*g+f.position[1]*h,this.position[2]=c.position[2]*i+d.position[2]*g+f.position[2]*h,this.scale[0]=c.scale[0]*i+d.scale[0]*g+f.scale[0]*h,this.scale[1]=c.scale[1]*i+d.scale[1]*g+f.scale[1]*h,this.scale[2]=c.scale[2]*i+d.scale[2]*g+f.scale[2]*h;var j=g+h;0===j?e.copy(this.rotation,c.rotation):(e.slerp(a,c.rotation,d.rotation,j),e.slerp(b,c.rotation,f.rotation,j),e.slerp(this.rotation,a,b,h/j))}}(),g.prototype.additiveBlend=function(a,b){f.add(this.position,a.position,b.position),f.add(this.scale,a.scale,b.scale),e.multiply(this.rotation,b.rotation,a.rotation)},g.prototype.subtractiveBlend=function(a,b){f.sub(this.position,a.position,b.position),f.sub(this.scale,a.scale,b.scale),e.invert(this.rotation,b.rotation),e.multiply(this.rotation,this.rotation,a.rotation)},g.prototype.getSubClip=function(){console.warn("TODO")},g.prototype.clone=function(){var a=c.prototype.clone.call(this);return a.keyFrames=this.keyFrames,f.copy(a.position,this.position),e.copy(a.rotation,this.rotation),f.copy(a.scale,this.scale),a},g}),d("qtek/animation/SamplerClip",["require","./Clip","./TransformClip","../dep/glmatrix"],function(a){function b(a,b,c,d,e,f){var g=b[e],h=b[e+1],i=b[e+2];return a[0]=g+d*(c[f]-g),a[1]=h+d*(c[f+1]-h),a[2]=i+d*(c[f+2]-i),a}function c(a,b,c,d,e,f){var g,h,i,j,k,l=b[0+e],m=b[1+e],n=b[2+e],o=b[3+e],p=c[0+f],q=c[1+f],r=c[2+f],s=c[3+f];return h=l*p+m*q+n*r+o*s,0>h&&(h=-h,p=-p,q=-q,r=-r,s=-s),1-h>1e-6?(g=Math.acos(h),i=Math.sin(g),j=Math.sin((1-d)*g)/i,k=Math.sin(d*g)/i):(j=1-d,k=d),a[0]=j*l+k*p,a[1]=j*m+k*q,a[2]=j*n+k*r,a[3]=j*o+k*s,a}var d=a("./Clip"),e=a("./TransformClip"),f=a("../dep/glmatrix"),g=f.quat,h=f.vec3,i=function(a){a=a||{},d.call(this,a),this.position=h.create(),this.rotation=g.create(),this.scale=h.fromValues(1,1,1),this.channels={time:null,position:null,rotation:null,scale:null},this._cacheKey=0,this._cacheTime=0};return i.prototype=Object.create(d.prototype),i.prototype.constructor=i,i.prototype.step=function(a){var b=d.prototype.step.call(this,a);return"destroy"!==b&&this.setTime(this._elapsedTime),b},i.prototype.setTime=function(a){if(this.channels.time){var d=this.channels,e=d.time.length,f=-1;if(a=0;h--)if(d.time[h-1]<=a&&d.time[h]>a){f=h-1;break}}else for(var h=this._cacheKey;e-1>h;h++)if(d.time[h]<=a&&d.time[h+1]>a){f=h;break}if(f>-1){this._cacheKey=f,this._cacheTime=a;var i=f,j=f+1,k=d.time[i],l=d.time[j],m=(a-k)/(l-k);d.rotation&&c(this.rotation,d.rotation,d.rotation,m,4*i,4*j),d.position&&b(this.position,d.position,d.position,m,3*i,3*j),d.scale&&b(this.scale,d.scale,d.scale,m,3*i,3*j)}f==e-2&&(this._cacheKey=0,this._cacheTime=0)}},i.prototype.getSubClip=function(a,b){var c=new i({name:this.name}),d=this.channels.time[0];a=Math.min(Math.max(a,d),this.life),b=Math.min(Math.max(b,d),this.life);var e=this._findRange(a),f=this._findRange(b),g=f[0]-e[0]+1;0===e[1]&&0===f[1]&&(g-=1),this.channels.rotation&&(c.channels.rotation=new Float32Array(4*g)),this.channels.position&&(c.channels.position=new Float32Array(3*g)),this.channels.scale&&(c.channels.scale=new Float32Array(3*g)),this.channels.time&&(c.channels.time=new Float32Array(g)),this.setTime(a);for(var h=0;3>h;h++)c.channels.rotation[h]=this.rotation[h],c.channels.position[h]=this.position[h],c.channels.scale[h]=this.scale[h];c.channels.time[0]=0,c.channels.rotation[3]=this.rotation[3];for(var h=1;g-1>h;h++){for(var j,k=0;3>k;k++)j=e[0]+h,c.channels.rotation[4*h+k]=this.channels.rotation[4*j+k],c.channels.position[3*h+k]=this.channels.position[3*j+k],c.channels.scale[3*h+k]=this.channels.scale[3*j+k];c.channels.time[h]=this.channels.time[j]-a,c.channels.rotation[4*h+3]=this.channels.rotation[4*j+3]}this.setTime(b);for(var h=0;3>h;h++)c.channels.rotation[4*(g-1)+h]=this.rotation[h],c.channels.position[3*(g-1)+h]=this.position[h],c.channels.scale[3*(g-1)+h]=this.scale[h];return c.channels.time[g-1]=b-a,c.channels.rotation[4*(g-1)+3]=this.rotation[3],c.life=b-a,c},i.prototype._findRange=function(a){for(var b=this.channels,c=b.time.length,d=-1,e=0;c-1>e;e++)b.time[e]<=a&&b.time[e+1]>a&&(d=e);var f=0;if(d>=0)var g=b.time[d],h=b.time[d+1],f=(a-g)/(h-g);return[d,f]},i.prototype.blend1D=e.prototype.blend1D,i.prototype.blend2D=e.prototype.blend2D,i.prototype.additiveBlend=e.prototype.additiveBlend,i.prototype.subtractiveBlend=e.prototype.subtractiveBlend,i.prototype.clone=function(){var a=d.prototype.clone.call(this);return a.channels={time:this.channels.time||null,position:this.channels.position||null,rotation:this.channels.rotation||null,scale:this.channels.scale||null},h.copy(a.position,this.position),g.copy(a.rotation,this.rotation),h.copy(a.scale,this.scale),a},i}),d("qtek/animation/SkinningClip",["require","./Clip","./TransformClip","../dep/glmatrix"],function(a){var b=a("./Clip"),c=a("./TransformClip"),d=a("../dep/glmatrix"),e=d.quat,f=d.vec3,g=function(a){if(a=a||{},b.call(this,a),this.jointClips=[],this.life=0,a.jointClips&&a.jointClips.length>0)for(var d=0;d=h;h++)for(var i=h/a,j=0;b>=j;j++){var k=j/b;if(d.push([2*k-1,2*i-1,0]),e&&e.push([k,i]),f&&f.push([0,0,1]),b>j&&a>h){var l=j+h*(b+1);g.push([l,l+1,l+b+1]),g.push([l+b+1,l+1,l+b+2])}}this.boundingBox=new c,this.boundingBox.min.set(-1,-1,0),this.boundingBox.max.set(1,1,0)}});return d}),d("qtek/shader/source/compositor/vertex.essl",[],function(){return"\n@export buildin.compositor.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n\nvarying vec2 v_Texcoord;\n\nvoid main()\n{\n v_Texcoord = texcoord;\n gl_Position = worldViewProjection * vec4(position, 1.0);\n}\n\n@end"}),d("qtek/compositor/Pass",["require","../core/Base","../camera/Orthographic","../geometry/Plane","../Shader","../Material","../Mesh","../core/glinfo","../core/glenum","../shader/source/compositor/vertex.essl"],function(a){var b=a("../core/Base"),c=a("../camera/Orthographic"),d=a("../geometry/Plane"),e=a("../Shader"),f=a("../Material"),g=a("../Mesh"),h=a("../core/glinfo"),i=a("../core/glenum");e["import"](a("../shader/source/compositor/vertex.essl"));var j=new d,k=new g({geometry:j}),l=new c,m=b.derive(function(){return{fragment:"",outputs:null,material:null}},function(){var a=new e({vertex:e.source("buildin.compositor.vertex"),fragment:this.fragment}),b=new f({shader:a});a.enableTexturesAll(),this.material=b},{setUniform:function(a,b){var c=this.material.uniforms[a];c&&(c.value=b)},getUniform:function(a){var b=this.material.uniforms[a];return b?b.value:void 0},attachOutput:function(a,b){this.outputs||(this.outputs={}),b=b||i.COLOR_ATTACHMENT0,this.outputs[b]=a},detachOutput:function(a){for(var b in this.outputs)this.outputs[b]===a&&(this.outputs[b]=null)},bind:function(a,b){if(this.outputs)for(var c in this.outputs){var d=this.outputs[c];d&&b.attach(a.gl,d,c)}b&&b.bind(a)},unbind:function(a,b){b.unbind(a)},render:function(a,b){var c=a.gl;if(b){this.bind(a,b);var d=h.getExtension(c,"EXT_draw_buffers");if(d&&this.outputs){var e=[];for(var f in this.outputs)f=+f,f>=c.COLOR_ATTACHMENT0&&f<=c.COLOR_ATTACHMENT0+8&&e.push(f);d.drawBuffersEXT(e)}}this.trigger("beforerender",this,a),c.disable(c.BLEND),c.clear(c.DEPTH_BUFFER_BIT),this.renderQuad(a),this.trigger("afterrender",this,a),b&&this.unbind(a,b)},renderQuad:function(a){k.material=this.material,a.renderQueue([k],l)}});return m}),d("qtek/compositor/Node",["require","../core/Base","./Pass","../FrameBuffer"],function(a){var b=a("../core/Base"),c=a("./Pass"),d=a("../FrameBuffer"),e=b.derive(function(){return{name:"",inputs:{},outputs:null,shader:"",inputLinks:{},outputLinks:{},pass:null,_prevOutputTextures:{},_outputTextures:{},_outputReferences:{},_rendering:!1,_rendered:!1,_compositor:null}},function(){var a=new c({fragment:this.shader});this.pass=a,this.outputs&&(this.frameBuffer=new d({depthBuffer:!1}))},{render:function(a,b){this.trigger("beforerender",a),this._rendering=!0;var c=a.gl;for(var d in this.inputLinks){var e=this.inputLinks[d],f=e.node.getOutput(a,e.pin);this.pass.setUniform(d,f)}if(this.outputs){this.pass.outputs={};for(var g in this.outputs){var h=this.updateParameter(g,a),i=this.outputs[g],j=this._compositor.allocateTexture(h);this._outputTextures[g]=j;var k=i.attachment||c.COLOR_ATTACHMENT0;"string"==typeof k&&(k=c[k]),this.pass.outputs[k]=j}this.pass.render(a,this.frameBuffer)}else this.pass.outputs=null,this.pass.render(a,b);for(var d in this.inputLinks){var e=this.inputLinks[d];e.node.removeReference(e.pin)}this._rendering=!1,this._rendered=!0,this.trigger("afterrender",a)},updateParameter:function(a,b){var c=this.outputs[a],d=c.parameters,e=c._parametersCopy;if(e||(e=c._parametersCopy={}),d)for(var f in d)"width"!==f&&"height"!==f&&(e[f]=d[f]);var g,h;return g=d.width instanceof Function?d.width(b):d.width,h=d.height instanceof Function?d.height(b):d.height,(e.width!==g||e.height!==h)&&this._outputTextures[a]&&this._outputTextures[a].dispose(b.gl),e.width=g,e.height=h,e},setParameter:function(a,b){this.pass.setUniform(a,b)},getParameter:function(a){return this.pass.getUniform(a)},setParameters:function(a){for(var b in a)this.setParameter(b,a[b])},setShader:function(a){var b=this.pass.material;b.shader.setFragment(a),b.attachShader(b.shader,!0)},getOutput:function(a,b){if(void 0===b)return b=a,this._outputTextures[b];var c=this.outputs[b];if(c)return this._rendered?c.outputLastFrame?this._prevOutputTextures[b]:this._outputTextures[b]:this._rendering?(this._prevOutputTextures[b]||(this._prevOutputTextures[b]=this._compositor.allocateTexture(c.parameters||{})),this._prevOutputTextures[b]):(this.render(a),this._outputTextures[b])},removeReference:function(a){if(this._outputReferences[a]--,0===this._outputReferences[a]){var b=this.outputs[a];b.keepLastFrame?(this._prevOutputTextures[a]&&this._compositor.releaseTexture(this._prevOutputTextures[a]),this._prevOutputTextures[a]=this._outputTextures[a]):this._compositor.releaseTexture(this._outputTextures[a])}},link:function(a,b,c){this.inputLinks[a]={node:b,pin:c},b.outputLinks[c]||(b.outputLinks[c]=[]),b.outputLinks[c].push({node:this,pin:a});var d=this.pass.material.shader;d.enableTexture(a)},clear:function(){this.inputLinks={},this.outputLinks={};var a=this.pass.material.shader;a.disableTexturesAll()},updateReference:function(a){if(!this._rendering){this._rendering=!0;for(var b in this.inputLinks){var c=this.inputLinks[b];c.node.updateReference(c.pin)}this._rendering=!1}a&&this._outputReferences[a]++},beforeFrame:function(){this._rendered=!1;for(var a in this.outputLinks)this._outputReferences[a]=0},afterFrame:function(){for(var a in this.outputLinks)if(this._outputReferences[a]>0){var b=this.outputs[a];b.keepLastFrame?(this._prevOutputTextures[a]&&this._compositor.releaseTexture(this._prevOutputTextures[a]),this._prevOutputTextures[a]=this._outputTextures[a]):this._compositor.releaseTexture(this._outputTextures[a])}}});return e}),d("qtek/compositor/SceneNode",["require","./Node","../core/glinfo"],function(a){var b=a("./Node"),c=a("../core/glinfo"),d=b.derive({name:"scene",scene:null,camera:null,autoUpdateScene:!0,preZ:!1},function(){this.frameBuffer&&(this.frameBuffer.depthBuffer=!0)},{render:function(a){this._rendering=!0;var b=a.gl;this.trigger("beforerender");var d;if(this.outputs){var e=this.frameBuffer;for(var f in this.outputs){var g=this.updateParameter(f,a),h=this.outputs[f],i=this._compositor.allocateTexture(g);this._outputTextures[f]=i;var j=h.attachment||b.COLOR_ATTACHMENT0;"string"==typeof j&&(j=b[j]),e.attach(a.gl,i,j)}e.bind(a);var k=c.getExtension(b,"EXT_draw_buffers");if(k){var l=[];for(var j in this.outputs)j=parseInt(j),j>=b.COLOR_ATTACHMENT0&&j<=b.COLOR_ATTACHMENT0+8&&l.push(j);k.drawBuffersEXT(l)}d=a.render(this.scene,this.camera,!this.autoUpdateScene,this.preZ),e.unbind(a)}else d=a.render(this.scene,this.camera,!this.autoUpdateScene,this.preZ);this.trigger("afterrender",d),this._rendering=!1,this._rendered=!0}});return d}),d("qtek/compositor/TextureNode",["require","./Node","../Shader"],function(a){var b=a("./Node"),c=a("../Shader"),d=b.derive(function(){return{shader:c.source("buildin.compositor.output"),texture:null}},{render:function(a,b){this._rendering=!0;var c=a.gl;if(this.pass.setUniform("texture",this.texture),this.outputs){this.pass.outputs={};for(var d in this.outputs){var e=this.updateParameter(d,a),f=this.outputs[d],g=this._compositor.allocateTexture(e);this._outputTextures[d]=g;var h=f.attachment||c.COLOR_ATTACHMENT0;"string"==typeof h&&(h=c[h]),this.pass.outputs[h]=g}this.pass.render(a,this.frameBuffer)}else this.pass.outputs=null,this.pass.render(a,b);this._rendering=!1,this._rendered=!0}});return d}),d("qtek/core/Event",["require","./Base"],function(a){var b=a("./Base"),c=b.derive({cancelBubble:!1},{stopPropagation:function(){this.cancelBubble=!0}});return c["throw"]=function(a,b,d){var e=new c(d);for(e.type=a,e.target=b;b&&!e.cancelBubble;)e.currentTarget=b,b.trigger(a,e),b=b.getParent()},c}),d("qtek/core/LinkedList",["require"],function(){var a=function(){this.head=null,this.tail=null,this._length=0};return a.prototype.insert=function(b){var c=new a.Entry(b);return this.insertEntry(c),c},a.prototype.insertAt=function(b,c){if(!(0>b)){for(var d=this.head,e=0;d&&e!=b;)d=d.next,e++;if(d){var f=new a.Entry(c),g=d.prev;g.next=f,f.prev=g,f.next=d,d.prev=f}else this.insert(c)}},a.prototype.insertEntry=function(a){this.head?(this.tail.next=a,a.prev=this.tail,this.tail=a):this.head=this.tail=a,this._length++},a.prototype.remove=function(a){var b=a.prev,c=a.next;b?b.next=c:this.head=c,c?c.prev=b:this.tail=b,a.next=a.prev=null,this._length--},a.prototype.removeAt=function(a){if(!(0>a)){for(var b=this.head,c=0;b&&c!=a;)b=b.next,c++;return b?(this.remove(b),b.value):void 0}},a.prototype.getHead=function(){return this.head?this.head.value:void 0},a.prototype.getTail=function(){return this.tail?this.tail.value:void 0},a.prototype.getAt=function(a){if(!(0>a)){for(var b=this.head,c=0;b&&c!=a;)b=b.next,c++;return b.value}},a.prototype.indexOf=function(a){for(var b=this.head,c=0;b;){if(b.value===a)return c;b=b.next,c++}},a.prototype.length=function(){return this._length},a.prototype.isEmpty=function(){return 0===this._length},a.prototype.forEach=function(a,b){for(var c=this.head,d=0,e="undefined"!=typeof b;c;)e?a.call(b,c.value,d):a(c.value,d),c=c.next,d++},a.prototype.clear=function(){this.tail=this.head=null,this._length=0},a.Entry=function(a){this.value=a,this.next=null,this.prev=null},a}),d("qtek/core/LRU",["require","./LinkedList"],function(a){var b=a("./LinkedList"),c=function(a){this._list=new b,this._map={},this._maxSize=a||10};return c.prototype.setMaxSize=function(a){this._maxSize=a},c.prototype.put=function(a,b){if("undefined"==typeof this._map[a]){var c=this._list.length();if(c>=this._maxSize&&c>0){var d=this._list.head;this._list.remove(d),delete this._map[d.key]}var e=this._list.insert(b);e.key=a,this._map[a]=e}},c.prototype.get=function(a){var b=this._map[a];return"undefined"!=typeof b?(b!==this._list.tail&&(this._list.remove(b),this._list.insertEntry(b)),b.value):void 0},c.prototype.remove=function(a){var b=this._map[a];"undefined"!=typeof b&&(delete this._map[a],this._list.remove(b))},c.prototype.clear=function(){this._list.clear(),this._map={}},c}),d("qtek/deferred/StandardMaterial",["require","../core/Base"],function(a){var b=a("../core/Base"),c=function(){},d=b.derive({color:null,emission:null,specularColor:null,glossiness:.5,diffuseMap:null,normalMap:null,environmentMap:null,diffuseAlphaUsage:"none",uvRepeat:null,uvOffset:null},function(){this.color=this.color||[1,1,1],this.emission=this.emission||[0,0,0],this.specularColor=this.specularColor||[.5,.5,.5],this.uvRepeat=this.uvRepeat||[1,1],this.uvOffset=this.uvOffset||[0,0],this.shader=new c},{});return d}),d("qtek/geometry/Sphere",["require","../DynamicGeometry","../dep/glmatrix","../math/BoundingBox"],function(a){var b=a("../DynamicGeometry"),c=a("../dep/glmatrix"),d=c.vec3,e=c.vec2,f=a("../math/BoundingBox"),g=b.derive({widthSegments:20,heightSegments:20,phiStart:0,phiLength:2*Math.PI,thetaStart:0,thetaLength:Math.PI,radius:1},function(){this.build()},{build:function(){var a=this.attributes.position.value,b=this.attributes.texcoord0.value,c=this.attributes.normal.value;a.length=0,b.length=0,c.length=0,this.faces.length=0;var g,h,i,j,k,l,m,n,o=this.heightSegments,p=this.widthSegments,q=this.radius,r=this.phiStart,s=this.phiLength,t=this.thetaStart,u=this.thetaLength,q=this.radius;for(m=0;o>=m;m++)for(l=0;p>=l;l++)j=l/p,k=m/o,g=-q*Math.cos(r+j*s)*Math.sin(t+k*u),h=q*Math.cos(t+k*u),i=q*Math.sin(r+j*s)*Math.sin(t+k*u),a.push(d.fromValues(g,h,i)),b.push(e.fromValues(j,k)),n=d.fromValues(g,h,i),d.normalize(n,n),c.push(n);var v,w,x,y,z=this.faces,A=p+1;for(m=0;o>m;m++)for(l=0;p>l;l++)w=m*A+l,v=m*A+l+1,y=(m+1)*A+l+1,x=(m+1)*A+l,z.push(d.fromValues(v,w,y)),z.push(d.fromValues(w,x,y));this.boundingBox=new f,this.boundingBox.max.set(q,q,q),this.boundingBox.min.set(-q,-q,-q)}});return g}),d("qtek/geometry/Cone",["require","../DynamicGeometry","../math/BoundingBox","../dep/glmatrix"],function(a){var b=a("../DynamicGeometry"),c=a("../math/BoundingBox"),d=a("../dep/glmatrix"),e=d.vec3,f=d.vec2,g=b.derive({topRadius:0,bottomRadius:1,height:2,capSegments:20,heightSegments:1},function(){this.build()},{build:function(){var a=this.attributes.position.value,b=this.attributes.texcoord0.value,d=this.faces;a.length=0,b.length=0,d.length=0;for(var g=2*Math.PI/this.capSegments,h=[],i=[],j=this.topRadius,k=this.bottomRadius,l=this.height/2,m=e.fromValues(0,l,0),n=e.fromValues(0,-l,0),o=0;oo;o++)a.push(h[o]),b.push(f.fromValues(o/s,0)),d.push([0,o+1,(o+1)%s+1]);var t=a.length;a.push(n),b.push(f.fromValues(0,1));for(var o=0;s>o;o++)a.push(i[o]),b.push(f.fromValues(o/s,0)),d.push([t,t+((o+1)%s+1),t+o+1]);t=a.length;for(var u=this.heightSegments,o=0;s>o;o++)for(var v=0;u+1>v;v++){var w=v/u;a.push(e.lerp(e.create(),h[o],i[o],w)),b.push(f.fromValues(o/s,w))}for(var o=0;s>o;o++)for(var v=0;u>v;v++){var x=o*(u+1)+v,y=(o+1)%s*(u+1)+v,z=(o+1)%s*(u+1)+v+1,A=o*(u+1)+v+1;d.push([t+y,t+x,t+A]),d.push([t+A,t+z,t+y])}this.generateVertexNormals(),this.boundingBox=new c;var B=Math.max(this.topRadius,this.bottomRadius);this.boundingBox.min.set(-B,-this.height/2,-B),this.boundingBox.max.set(B,this.height/2,B)}});return g}),d("qtek/shader/source/util.essl",[],function(){return"// Use light attenuation formula in\n// http://blog.slindev.com/2011/01/10/natural-light-attenuation/\n@export buildin.util.calculate_attenuation\n\nuniform float attenuationFactor : 5.0;\n\nfloat lightAttenuation(float dist, float range)\n{\n float attenuation = 1.0;\n if( range > 0.0)\n {\n attenuation = dist*dist/(range*range);\n float att_s = attenuationFactor;\n attenuation = 1.0/(attenuation*att_s+1.0);\n att_s = 1.0/(att_s+1.0);\n attenuation = attenuation - att_s;\n attenuation /= 1.0 - att_s;\n }\n return clamp(attenuation, 0.0, 1.0);\n}\n\n@end\n\n//http://codeflow.org/entries/2012/aug/02/easy-wireframe-display-with-barycentric-coordinates/\n@export buildin.util.edge_factor\n\nfloat edgeFactor(float width)\n{\n vec3 d = fwidth(v_Barycentric);\n vec3 a3 = smoothstep(vec3(0.0), d * width, v_Barycentric);\n return min(min(a3.x, a3.y), a3.z);\n}\n\n@end\n\n// Pack depth\n// Float value can only be [0.0 - 1.0) ?\n@export buildin.util.encode_float\nvec4 encodeFloat( const in float depth )\n{\n\n const vec4 bitShifts = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );\n\n const vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );\n vec4 res = fract( depth * bitShifts );\n res -= res.xxyz * bit_mask;\n\n return res;\n}\n@end\n\n@export buildin.util.decode_float\nfloat decodeFloat(const in vec4 colour)\n{\n const vec4 bitShifts = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n return dot(colour, bitShifts);\n}\n@end\n\n// http://graphicrants.blogspot.com/2009/04/rgbm-color-encoding.html\n@export buildin.util.rgbm_decode\nvec3 RGBMDecode(vec4 rgbm, float range) {\n return range * rgbm.rgb * rgbm.a;\n}\n@end\n\n@export buildin.util.rgbm_encode\nvec4 RGBMEncode(vec3 color, float range) {\n vec4 rgbm;\n color *= 1.0 / range;\n rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 1e-6 ) ), 0.0, 1.0);\n rgbm.a = ceil(rgbm.a * 255.0) / 255.0;\n rgbm.rgb = color / rgbm.a;\n return rgbm;\n}\n@end\n\n\n@export buildin.chunk.skin_matrix\n\n// Weighted Sum Skinning Matrix\nmat4 skinMatrixWS;\nif (joint.x >= 0.0)\n{\n skinMatrixWS = skinMatrix[int(joint.x)] * weight.x;\n}\nif (joint.y >= 0.0)\n{\n skinMatrixWS += skinMatrix[int(joint.y)] * weight.y;\n}\nif (joint.z >= 0.0)\n{\n skinMatrixWS += skinMatrix[int(joint.z)] * weight.z;\n}\nif (joint.w >= 0.0)\n{\n skinMatrixWS += skinMatrix[int(joint.w)] * (1.0-weight.x-weight.y-weight.z);\n}\n@end\n" -}),d("qtek/shader/source/deferred/gbuffer.essl",[],function(){return"@export buildin.deferred.gbuffer.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\n\nuniform vec2 uvRepeat;\nuniform vec2 uvOffset;\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 normal : NORMAL;\nattribute vec4 tangent : TANGENT;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\n\n#ifdef NORMALMAP_ENABLED\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\nvarying vec4 v_ProjPos;\n\nvoid main()\n{\n \n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n // Upper skinMatrix \n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n skinnedTangent = (skinMatrixWS * vec4(tangent.xyz, 0.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\n \n #ifdef NORMALMAP_ENABLED\n v_Tangent = normalize((worldInverseTranspose * vec4(skinnedTangent, 0.0)).xyz);\n v_Bitangent = normalize(cross(v_Normal, v_Tangent) * tangent.w);\n #endif\n\n v_ProjPos = gl_Position;\n}\n\n\n@end\n\n\n@export buildin.deferred.gbuffer.fragment\n\nuniform sampler2D diffuseMap;\nuniform float glossiness;\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\n\n#ifdef NORMALMAP_ENABLED\nuniform sampler2D normalMap;\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\nvarying vec4 v_ProjPos;\n\nvoid main()\n{\n vec3 N = v_Normal;\n #ifdef NORMALMAP_ENABLED\n N = texture2D(normalMap, v_Texcoord).xyz * 2.0 - 1.0;\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\n N = tbn * N;\n #endif\n\n // N.z can be recovered from sqrt(1 - dot(N.xy, N.xy));\n gl_FragColor.rg = (N.xy + 1.0) * 0.5;\n\n // Depth\n gl_FragColor.b = v_ProjPos.z / v_ProjPos.w;\n\n gl_FragColor.a = glossiness;\n #ifdef DIFFUSEMAP_ENABLED\n // Ouptut glossiness to alpha channel\n gl_FragColor.a *= texture2D(diffuseMap, v_Texcoord).a;\n #endif\n\n}\n@end"}),d("qtek/shader/source/deferred/chunk.essl",[],function(){return"@export buildin.deferred.chunk.light_head\nuniform sampler2D normalTex;\nuniform vec2 viewportSize;\n\nuniform mat4 viewProjectionInv;\n\nconst vec3 LUM = vec3(0.2125, 0.7154, 0.0721);\n@end\n\n@export buildin.deferred.chunk.gbuffer_read\n vec2 uv = gl_FragCoord.xy / viewportSize;\n\n vec4 tex = texture2D(normalTex, uv);\n // Is empty\n if (dot(tex.rgb, vec3(1.0)) == 0.0) {\n discard;\n }\n\n vec3 N;\n N.xy = tex.rg * 2.0 - 1.0;\n N.z = sqrt(1.0 - dot(N.xy, N.xy));\n\n // Depth value in depth texture is 0 - 1\n // float z = texture2D(depthTex, uv).r * 2.0 - 1.0;\n float z = tex.b;\n\n float glossiness = tex.a;\n\n vec2 xy = uv * 2.0 - 1.0;\n\n vec4 projectedPos = vec4(xy, z, 1.0);\n vec4 p4 = viewProjectionInv * projectedPos;\n\n vec3 position = p4.xyz / p4.w;\n@end\n\n@export buildin.deferred.chunk.light_equation\n\nfloat D_Phong(float g, float ndh) {\n // from black ops 2\n float a = pow(8192.0, g);\n return (a + 2.0) / 8.0 * pow(ndh, a);\n}\n\n@end"}),d("qtek/shader/source/deferred/lightvolume.essl",[],function(){return"@export buildin.deferred.light_volume.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\nvarying vec3 v_Position;\n\nvoid main()\n{\n gl_Position = worldViewProjection * vec4(position, 1.0);\n\n v_Position = position;\n}\n\n@end"}),d("qtek/shader/source/deferred/spot.essl",[],function(){return"@export buildin.deferred.spot_light\n\n@import buildin.deferred.chunk.light_head\n\n@import buildin.deferred.chunk.light_equation\n\n@import buildin.util.calculate_attenuation\n\nuniform vec3 lightPosition;\nuniform vec3 lightDirection;\nuniform vec3 lightColor;\nuniform float umbraAngleCosine;\nuniform float penumbraAngleCosine;\nuniform float lightRange;\nuniform float falloffFactor;\n\nuniform vec3 eyePosition;\n\nvoid main()\n{\n @import buildin.deferred.chunk.gbuffer_read\n\n vec3 L = lightPosition - position;\n vec3 V = normalize(eyePosition - position);\n\n float dist = length(L);\n L /= dist;\n\n float attenuation = lightAttenuation(dist, lightRange);\n float c = dot(-lightDirection, L);\n\n float falloff = clamp((c - umbraAngleCosine) / (penumbraAngleCosine - umbraAngleCosine), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n // Diffuse term\n gl_FragColor.rgb = lightColor * ndl * (1.0 - falloff) * attenuation;\n if (dot(gl_FragColor.rgb, vec3(1.0)) == 0.0) // Reduce blending\n {\n discard;\n }\n gl_FragColor.a = dot(LUM, gl_FragColor.rgb * D_Phong(glossiness, ndh));\n}\n@end\n"}),d("qtek/shader/source/deferred/directional.essl",[],function(){return"@export buildin.deferred.directional_light\n\n@import buildin.deferred.chunk.light_head\n\n@import buildin.deferred.chunk.light_equation\n\nuniform vec3 lightDirection;\nuniform vec3 lightColor;\n\nuniform vec3 eyePosition;\n\nvoid main()\n{\n @import buildin.deferred.chunk.gbuffer_read\n\n vec3 L = -normalize(lightDirection);\n vec3 V = normalize(eyePosition - position);\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n gl_FragColor.rgb = ndl * lightColor;\n gl_FragColor.a = dot(LUM, gl_FragColor.rgb * D_Phong(glossiness, ndh));\n}\n@end\n"}),d("qtek/shader/source/deferred/ambient.essl",[],function(){return"@export buildin.deferred.ambient_light\nuniform sampler2D normalTexture;\nuniform vec3 lightColor;\n\nvarying vec2 v_Texcoord;\n\nvoid main()\n{\n vec4 tex = texture2D(normalTexture, v_Texcoord);\n vec3 normal = tex.rgb * 2.0 - 1.0;\n\n gl_FragColor.rgb = lightColor * (clamp(normal.y * 0.7, 0.0, 1.0) + 0.3);\n gl_FragColor.a = 0.0;\n}\n@end"}),d("qtek/shader/source/deferred/point.essl",[],function(){return"@export buildin.deferred.point_light\n\n@import buildin.deferred.chunk.light_head\n\n@import buildin.util.calculate_attenuation\n\n@import buildin.deferred.chunk.light_equation\n\nuniform vec3 lightPosition;\nuniform vec3 lightColor;\nuniform float lightRange;\n\nuniform vec3 eyePosition;\n\nvarying vec3 v_Position;\n\nvoid main()\n{\n @import buildin.deferred.chunk.gbuffer_read\n\n vec3 L = lightPosition - position;\n vec3 V = normalize(eyePosition - position);\n\n float dist = length(L);\n L /= dist;\n\n vec3 H = normalize(L + V);\n\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n float attenuation = lightAttenuation(dist, lightRange);\n // Diffuse term\n gl_FragColor.rgb = lightColor * ndl * attenuation;\n if (dot(gl_FragColor.rgb, vec3(1.0)) == 0.0) // Reduce blending\n {\n discard;\n }\n // // Specular luminance\n gl_FragColor.a = dot(LUM, gl_FragColor.rgb * D_Phong(glossiness, ndh));\n}\n@end"}),d("qtek/shader/source/deferred/output.essl",[],function(){return"@export buildin.deferred.output.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world: WORLD;\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n\nuniform vec2 uvRepeat;\nuniform vec2 uvOffset;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\n\nvarying vec4 v_ProjPos;\n\nvarying vec3 v_WorldPos;\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n\n v_WorldPos = (world * vec4(skinnedPosition, 1.0)).xyz;\n\n v_ProjPos = gl_Position;\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n}\n@end\n\n@export buildin.deferred.output.fragment\n\nuniform sampler2D diffuseMap;\n\nuniform sampler2D lightAccumTex;\nuniform sampler2D normalTex;\n\nuniform vec3 color;\nuniform vec3 specularColor;\nuniform vec3 emission;\n\nuniform vec3 eyePosition;\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_WorldPos;\nvarying vec4 v_ProjPos;\n\nconst vec3 LUM = vec3(0.2125, 0.7154, 0.0721);\n\n// Fresnel\nvec3 F_Schlick(float ndv, vec3 spec) {\n return spec + (1.0 - spec) * pow(1.0 - ndv, 5.0);\n}\n\nvoid main()\n{\n vec2 uv = (v_ProjPos.xy / v_ProjPos.w + 1.0) * 0.5;\n\n vec3 V = normalize(eyePosition - v_WorldPos);\n\n vec3 albedo = color;\n #ifdef diffuseMap\n albedo *= texture2D(diffuseMap, v_Texcoord);\n #endif\n\n vec4 diffSpec = texture2D(lightAccumTex, uv);\n vec3 N;\n vec2 tex = texture2D(normalTex, uv).rg;\n N.xy = tex * 2.0 - 1.0;\n N.z = sqrt(1.0 - dot(N.xy, N.xy));\n\n vec3 diffTerm = diffSpec.rgb;\n // PENDING\n vec3 specTerm = diffTerm * diffSpec.a / (dot(LUM, diffTerm) + 0.1);\n vec3 fresnelTerm = F_Schlick(clamp(dot(N, V), 0.0, 1.0), specularColor);\n\n gl_FragColor.rgb = albedo * diffTerm + fresnelTerm * specTerm + emission;\n gl_FragColor.a = 1.0;\n}\n@end\n\n"}),d("qtek/shader/source/prez.essl",[],function(){return"// Shader for prez pass\n@export buildin.prez.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n \n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n \n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n}\n\n@end\n\n\n@export buildin.prez.fragment\n\nvoid main()\n{\n gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\n}\n\n@end"}),d("qtek/deferred/Renderer",["require","../core/Base","../Shader","./StandardMaterial","../Material","../FrameBuffer","../compositor/Pass","../Texture2D","../Texture","../math/BoundingBox","../Mesh","../geometry/Sphere","../geometry/Cone","../math/Matrix4","../math/Vector3","../Renderer","../shader/source/util.essl","../shader/source/deferred/gbuffer.essl","../shader/source/deferred/chunk.essl","../shader/source/deferred/lightvolume.essl","../shader/source/deferred/spot.essl","../shader/source/deferred/directional.essl","../shader/source/deferred/ambient.essl","../shader/source/deferred/point.essl","../shader/source/deferred/output.essl","../shader/source/prez.essl"],function(a){var b=a("../core/Base"),c=a("../Shader"),d=a("./StandardMaterial"),e=a("../Material"),f=a("../FrameBuffer"),g=a("../compositor/Pass"),h=a("../Texture2D"),i=a("../Texture");a("../math/BoundingBox");var j=a("../Mesh"),k=a("../geometry/Sphere"),l=a("../geometry/Cone"),m=a("../math/Matrix4"),n=a("../math/Vector3"),o=a("../Renderer");c.import(a("../shader/source/util.essl")),c.import(a("../shader/source/deferred/gbuffer.essl")),c.import(a("../shader/source/deferred/chunk.essl")),c.import(a("../shader/source/deferred/lightvolume.essl")),c.import(a("../shader/source/deferred/spot.essl")),c.import(a("../shader/source/deferred/directional.essl")),c.import(a("../shader/source/deferred/ambient.essl")),c.import(a("../shader/source/deferred/point.essl")),c.import(a("../shader/source/deferred/output.essl")),c.import(a("../shader/source/prez.essl"));var p={},q=b.derive(function(){var a=new c({vertex:c.source("buildin.deferred.gbuffer.vertex"),fragment:c.source("buildin.deferred.gbuffer.fragment")}),b=a.clone();b.enableTexture("diffuseMap");var d=a.clone();d.enableTexture("normalMap");var j=b.clone();j.enableTexture("normalMap");var o=new c({vertex:c.source("buildin.deferred.output.vertex"),fragment:c.source("buildin.deferred.output.fragment")}),p=o.clone();p.enableTexture("diffuseMap");var q=c.source("buildin.compositor.vertex"),r=c.source("buildin.deferred.light_volume.vertex"),s=function(a){a.blendEquation(a.FUNC_ADD),a.blendFunc(a.ONE,a.ONE)},t=function(a){return new e({shader:a,blend:s,transparent:!0,depthMask:!1})},u=new l({bottomRadius:1,height:2,capSegments:10}),v=new m;return v.rotateX(Math.PI/2),v.translate(new n(0,0,-1)),u.applyTransform(v),{_gBufferShader:a,_gBufferDiffShader:b,_gBufferDiffNormShader:j,_gBufferNormShader:d,_outputShader:o,_outputDiffShader:p,_gBufferFrameBuffer:new f,_gBufferTex:new h({width:0,height:0,type:i.FLOAT,minFilter:i.NEAREST,magFilter:i.NEAREST}),_lightAccumFrameBuffer:new f,_lightAccumTex:new h({type:i.FLOAT,minFilter:i.NEAREST,magFilter:i.NEAREST}),_fullQuadPass:new g,_directionalLightMat:t(new c({vertex:q,fragment:c.source("buildin.deferred.directional_light")})),_ambientMat:t(new c({vertex:q,fragment:c.source("buildin.deferred.ambient_light")})),_spotLightShader:new c({vertex:r,fragment:c.source("buildin.deferred.spot_light")}),_pointLightShader:new c({vertex:r,fragment:c.source("buildin.deferred.point_light")}),_createLightPassMat:t,_lightSphereGeo:new k({widthSegments:10,heightSegements:10}),_lightConeGeo:u}},{render:function(a,b,c){var d=a.gl;b.update(!1,!0),c.update(!0);var e=b.opaqueQueue;e.sort(o.opaqueSortFunc);for(var f=0;f1?[i.name,k].join("-"):i.name),b.meshes[h].push(K)}}},_parseNodes:function(a,b){for(var c in a.nodes){var d,e=a.nodes[c];if(e.camera&&this.includeCamera){var f=a.cameras[e.camera];"perspective"===f.projection?d=new m({name:e.name,aspect:f.aspect_ratio,fov:f.xfov,far:f.zfar,near:f.znear}):(d=new n,console.warn("TODO:Orthographic camera")),d.setName(e.name),b.cameras[e.name]=d}else if(e.lights&&this.includeLight){for(var g=[],i=0;ii;i++)d.localTransform._array[i]=e.matrix[i];d.decomposeLocalTransform()}else e.translation&&d.position.setArray(e.translation),e.rotation&&(x.setAxisAngle(d.rotation._array,e.rotation.slice(0,3),e.rotation[3]),d.rotation._dirty=!0),e.scale&&d.scale.setArray(e.scale);b.nodes[c]=d}for(var c in a.nodes){var e=a.nodes[c],d=b.nodes[c];if(e.children)for(var i=0;i>16,c=255&a>>8,d=255&a;return[b/255,c/255,d/255]}var d=a("../core/Base"),e=a("../core/request"),f=a("../core/util"),g=a("../Shader"),h=a("../Material"),i=a("../DynamicGeometry"),j=a("../Mesh"),k=a("../Node"),l=a("../Texture2D");a("../TextureCube");var m=a("../shader/library"),n=a("../Skeleton"),o=a("../Joint"),p=a("../math/Vector3"),q=a("../math/Quaternion"),r=a("../core/glenum"),s=a("../animation/SkinningClip"),t=a("../dep/glmatrix"),u=t.vec3,v=t.quat,w=d.derive({rootPath:"",textureRootPath:""},{load:function(a){var b=this;this.rootPath||(this.rootPath=a.slice(0,a.lastIndexOf("/"))),e.get({url:a,onprogress:function(a,c,d){b.trigger("progress",a,c,d)},onerror:function(a){b.trigger("error",a)},responseType:"text",onload:function(a){b.parse(JSON.parse(a))}})},parse:function(a){var b,c,d=this._parseGeometry(a),e=a.skinIndices,f=a.skinWeights,g=e&&e.length&&f&&f.length;g?(c=this._parseSkeleton(a),b=c.joints.length):b=0;for(var h=[],i=0;i=0?(E=D[a],q=e[E],r=q.attributes,s=r.position.value,t=r.normal.value,u=[r.texcoord0.value,r.texcoord1.value],v=r.color.value,x=r.weight.value,w=r.joint.value,F[b]=!1,C[a]):(s.push([j[3*a],j[3*a+1],j[3*a+2]]),p&&(x.push([n[2*a],n[2*a+1],0]),w.push([m[2*a],m[2*a+1],-1,-1])),C[a]=f[J],D[a]=J,F[b]=!0,f[J]++)}for(var e=[],f=[],g=0;gg;g++)G[g]=[0,0],H[g]=[0,0,0],I[g]=[0,0,0];for(var J=0;B>A;){var K=h[A++],L=b(K,0),M=b(K,1),N=b(K,2),O=b(K,3),P=b(K,4),Q=b(K,5),R=b(K,6),S=b(K,7),T=L?4:3;M&&(J=h[A+(L?4:3)],e[J]||(e[J]=new i),q=e[J],r=q.attributes,s=r.position.value,t=r.normal.value,u=[r.texcoord0.value,r.texcoord1.value],v=r.color.value,x=r.weight.value,w=r.joint.value,y=q.faces);var U,V,W,X,Y,Z,$,_,ab,bb;if(L?(U=h[A++],V=h[A++],W=h[A++],X=h[A++],Y=d(U,0),Z=d(V,1),$=d(X,2),_=d(V,3),ab=d(W,4),bb=d(X,5),y.push([Y,Z,$],[_,ab,bb])):(Y=h[A++],Z=h[A++],$=h[A++],Y=d(Y,0),Z=d(Z,1),$=d($,2),y.push([Y,Z,$])),M&&A++,N)for(var g=0;z>g;g++){var cb=o[g],db=y[A++],eb=cb[2*db],fb=cb[2*db+1];L?(F[0]&&(u[g][Y]=[eb,fb]),F[1]&&(u[g][Z]=[eb,fb]),F[2]&&(u[g][$]=[eb,fb]),F[3]&&(u[g][_]=[eb,fb]),F[4]&&(u[g][ab]=[eb,fb]),F[5]&&(u[g][bb]=[eb,fb])):(F[0]&&(u[g][Y]=[eb,fb]),F[1]&&(u[g][Z]=[eb,fb]),F[2]&&(u[g][$]=[eb,fb]))}if(O)for(var g=0;z>g;g++){for(var cb=o[g],gb=0;T>gb;gb++){var db=h[A++];G[gb][0]=cb[2*db],G[gb][1]=cb[2*db+1]}L?(F[0]&&(u[g][Y]=G[0].slice()),F[1]&&(u[g][Z]=G[1].slice()),F[2]&&(u[g][$]=G[3].slice()),F[3]&&(u[g][_]=G[1].slice()),F[4]&&(u[g][ab]=G[2].slice()),F[5]&&(u[g][bb]=G[3].slice())):(F[0]&&(u[g][Y]=G[0].slice()),F[1]&&(u[g][Z]=G[1].slice()),F[2]&&(u[g][$]=G[2].slice()))}if(P){var hb=3*h[A++],ib=k[hb++],jb=k[hb++],kb=k[hb];L?(F[0]&&(t[Y]=[ib,jb,kb]),F[1]&&(t[Z]=[ib,jb,kb]),F[2]&&(t[$]=[ib,jb,kb]),F[3]&&(t[_]=[ib,jb,kb]),F[4]&&(t[ab]=[ib,jb,kb]),F[5]&&(t[bb]=[ib,jb,kb])):(F[0]&&(t[Y]=[ib,jb,kb]),F[1]&&(t[Z]=[ib,jb,kb]),F[2]&&(t[$]=[ib,jb,kb]))}if(Q){for(var g=0;T>g;g++){var hb=3*h[A++];H[g][0]=k[hb++],H[g][1]=k[hb++],H[g][2]=k[hb]}L?(F[0]&&(t[Y]=H[0].slice()),F[1]&&(t[Z]=H[1].slice()),F[2]&&(t[$]=H[3].slice()),F[3]&&(t[_]=H[1].slice()),F[4]&&(t[ab]=H[2].slice()),F[5]&&(t[bb]=H[3].slice())):(F[0]&&(t[Y]=H[0].slice()),F[1]&&(t[Z]=H[1].slice()),F[2]&&(t[$]=H[2].slice()))}if(R){var lb=h[A++],mb=c(l[lb]);L?(F[0]&&(v[Y]=mb),F[1]&&(v[Z]=mb),F[2]&&(v[$]=mb),F[3]&&(v[_]=mb),F[4]&&(v[ab]=mb),F[5]&&(v[bb]=mb)):(F[0]&&(v[Y]=mb),F[1]&&(v[Z]=mb),F[2]&&(v[$]=mb))}if(S){for(var g=0;T>g;g++){var lb=h[A++];I[g]=c(l[lb])}L?(F[0]&&(v[Y]=I[0].slice()),F[1]&&(v[Z]=I[1].slice()),F[2]&&(v[$]=I[3].slice()),F[3]&&(v[_]=I[1].slice()),F[4]&&(v[ab]=I[2].slice()),F[5]&&(v[bb]=I[3].slice())):(F[0]&&(v[Y]=I[0].slice()),F[1]&&(v[Z]=I[1].slice()),F[2]&&(v[$]=I[2].slice()))}}return e},_parseSkeleton:function(a){for(var b=[],c=a.bones,d=0;dj;j++)i.enableTexture(f[j]);i.define("vertex","SKINNING"),i.define("vertex","JOINT_NUMBER",b)}var k=new h({shader:i});return a.colorDiffuse?k.set("color",a.colorDiffuse):a.DbgColor&&k.set("color",c(a.DbgColor)),a.colorSpecular&&k.set("specular",a.colorSpecular),void 0!==a.transparent&&a.transparent&&(k.transparent=!0),void 0!==a.depthTest&&(k.depthTest=a.depthTest),void 0!==a.depthWrite&&(k.depthMask=a.depthWrite),a.transparency&&a.transparency<1&&k.set("opacity",a.transparency),a.specularCoef&&k.set("shininess",a.specularCoef),a.mapDiffuse&&k.set("diffuseMap",this._loadTexture(a.mapDiffuse,a.mapDiffuseWrap)),a.mapBump&&k.set("normalMap",this._loadTexture(a.mapBump,a.mapBumpWrap)),a.mapNormal&&k.set("normalMap",this._loadTexture(a.mapNormal,a.mapBumpWrap)),k},_loadTexture:function(a,b){var c=new Image,d=new l;d.image=c,b&&b.length&&(d.wrapS=r[b[0].toUpperCase()],d.wrapT=r[b[1].toUpperCase()]),c.onload=function(){d.dirty()};var e=this.textureRootPath||this.rootPath;return c.src=f.relative2absolute(a,e),d}});return w}),d("qtek/math/Matrix2",["require","../dep/glmatrix"],function(a){var b=a("../dep/glmatrix"),c=b.mat2,d=function(){this._array=c.create(),this._dirty=!0};return d.prototype={constructor:d,clone:function(){return(new d).copy(this)},copy:function(a){return c.copy(this._array,a._array),this._dirty=!0,this},adjoint:function(){return c.adjoint(this._array,this._array),this._dirty=!0,this},determinant:function(){return c.determinant(this._array)},identity:function(){return c.identity(this._array),this._dirty=!0,this},invert:function(){return c.invert(this._array,this._array),this._dirty=!0,this},mul:function(a){return c.mul(this._array,this._array,a._array),this._dirty=!0,this},mulLeft:function(a){return c.mul(this._array,a._array,this._array),this._dirty=!0,this},multiply:function(a){return c.multiply(this._array,this._array,a._array),this._dirty=!0,this},multiplyLeft:function(a){return c.multiply(this._array,a._array,this._array),this._dirty=!0,this},rotate:function(a){return c.rotate(this._array,this._array,a),this._dirty=!0,this},scale:function(a){return c.scale(this._array,this._array,a._array),this._dirty=!0,this},transpose:function(){return c.transpose(this._array,this._array),this._dirty=!0,this},toString:function(){return"["+Array.prototype.join.call(this._array,",")+"]"}},d.adjoint=function(a,b){return c.adjoint(a._array,b._array),a._dirty=!0,a},d.copy=function(a,b){return c.copy(a._array,b._array),a._dirty=!0,a},d.determinant=function(a){return c.determinant(a._array)},d.identity=function(a){return c.identity(a._array),a._dirty=!0,a},d.invert=function(a,b){return c.invert(a._array,b._array),a._dirty=!0,a},d.mul=function(a,b,d){return c.mul(a._array,b._array,d._array),a._dirty=!0,a},d.multiply=d.mul,d.rotate=function(a,b,d){return c.rotate(a._array,b._array,d),a._dirty=!0,a},d.scale=function(a,b,d){return c.scale(a._array,b._array,d._array),a._dirty=!0,a},d.transpose=function(a,b){return c.transpose(a._array,b._array),a._dirty=!0,a},d}),d("qtek/math/Matrix2d",["require","../dep/glmatrix"],function(a){var b=a("../dep/glmatrix"),c=b.mat2d,d=function(){this._array=c.create(),this._dirty=!0};return d.prototype={constructor:d,clone:function(){return(new d).copy(this)},copy:function(a){return c.copy(this._array,a._array),this._dirty=!0,this},determinant:function(){return c.determinant(this._array)},identity:function(){return c.identity(this._array),this._dirty=!0,this},invert:function(){return c.invert(this._array,this._array),this._dirty=!0,this},mul:function(a){return c.mul(this._array,this._array,a._array),this._dirty=!0,this},mulLeft:function(a){return c.mul(this._array,a._array,this._array),this._dirty=!0,this},multiply:function(a){return c.multiply(this._array,this._array,a._array),this._dirty=!0,this},multiplyLeft:function(a){return c.multiply(this._array,a._array,this._array),this._dirty=!0,this},rotate:function(a){return c.rotate(this._array,this._array,a),this._dirty=!0,this},scale:function(a){return c.scale(this._array,this._array,a._array),this._dirty=!0,this},translate:function(a){return c.translate(this._array,this._array,a._array),this._dirty=!0,this},toString:function(){return"["+Array.prototype.join.call(this._array,",")+"]"}},d.copy=function(a,b){return c.copy(a._array,b._array),a._dirty=!0,a},d.determinant=function(a){return c.determinant(a._array)},d.identity=function(a){return c.identity(a._array),a._dirty=!0,a},d.invert=function(a,b){return c.invert(a._array,b._array),a._dirty=!0,a},d.mul=function(a,b,d){return c.mul(a._array,b._array,d._array),a._dirty=!0,a},d.multiply=d.mul,d.rotate=function(a,b,d){return c.rotate(a._array,b._array,d),a._dirty=!0,a},d.scale=function(a,b,d){return c.scale(a._array,b._array,d._array),a._dirty=!0,a},d.translate=function(a,b,d){return c.translate(a._array,b._array,d._array),a._dirty=!0,a},d}),d("qtek/math/Matrix3",["require","../dep/glmatrix"],function(a){var b=a("../dep/glmatrix"),c=b.mat3,d=function(){this._array=c.create(),this._dirty=!0};return d.prototype={constructor:d,adjoint:function(){return c.adjoint(this._array,this._array),this._dirty=!0,this},clone:function(){return(new d).copy(this)},copy:function(a){return c.copy(this._array,a._array),this._dirty=!0,this},determinant:function(){return c.determinant(this._array)},fromMat2d:function(a){return c.fromMat2d(this._array,a._array),this._dirty=!0,this},fromMat4:function(a){return c.fromMat4(this._array,a._array),this._dirty=!0,this},fromQuat:function(a){return c.fromQuat(this._array,a._array),this._dirty=!0,this},identity:function(){return c.identity(this._array),this._dirty=!0,this},invert:function(){return c.invert(this._array,this._array),this._dirty=!0,this},mul:function(a){return c.mul(this._array,this._array,a._array),this._dirty=!0,this},mulLeft:function(a){return c.mul(this._array,a._array,this._array),this._dirty=!0,this},multiply:function(a){return c.multiply(this._array,this._array,a._array),this._dirty=!0,this},multiplyLeft:function(a){return c.multiply(this._array,a._array,this._array),this._dirty=!0,this},rotate:function(a){return c.rotate(this._array,this._array,a),this._dirty=!0,this},scale:function(a){return c.scale(this._array,this._array,a._array),this._dirty=!0,this},translate:function(a){return c.translate(this._array,this._array,a._array),this._dirty=!0,this},normalFromMat4:function(a){return c.normalFromMat4(this._array,a._array),this._dirty=!0,this},transpose:function(){return c.transpose(this._array,this._array),this._dirty=!0,this},toString:function(){return"["+Array.prototype.join.call(this._array,",")+"]"}},d.adjoint=function(a,b){return c.adjoint(a._array,b._array),a._dirty=!0,a},d.copy=function(a,b){return c.copy(a._array,b._array),a._dirty=!0,a},d.determinant=function(a){return c.determinant(a._array)},d.identity=function(a){return c.identity(a._array),a._dirty=!0,a},d.invert=function(a,b){return c.invert(a._array,b._array),a},d.mul=function(a,b,d){return c.mul(a._array,b._array,d._array),a._dirty=!0,a},d.multiply=d.mul,d.fromMat2d=function(a,b){return c.fromMat2d(a._array,b._array),a._dirty=!0,a},d.fromMat4=function(a,b){return c.fromMat4(a._array,b._array),a._dirty=!0,a},d.fromQuat=function(a,b){return c.fromQuat(a._array,b._array),a._dirty=!0,a},d.normalFromMat4=function(a,b){return c.normalFromMat4(a._array,b._array),a._dirty=!0,a},d.rotate=function(a,b,d){return c.rotate(a._array,b._array,d),a._dirty=!0,a},d.scale=function(a,b,d){return c.scale(a._array,b._array,d._array),a._dirty=!0,a},d.transpose=function(a,b){return c.transpose(a._array,b._array),a._dirty=!0,a},d.translate=function(a,b,d){return c.translate(a._array,b._array,d._array),a._dirty=!0,a},d}),d("qtek/math/Value",["require","./Vector3","./Vector2"],function(a){var b=a("./Vector3"),c=a("./Vector2"),d=function(){};d.prototype.get=function(){};var e=function(a){this.get=function(){return a}};e.prototype=new d,e.prototype.constructor=e;var f=function(a){var b=a.constructor;this.get=function(c){return c||(c=new b),c.copy(a),c}};f.prototype=new d,f.prototype.constructor=f;var g=function(a,b){var c=b-a;this.get=function(){return Math.random()*c+a}};g.prototype=new d,g.prototype.constructor=g;var h=function(a,b){var d=b.x-a.x,e=b.y-a.y;this.get=function(b){return b||(b=new c),c.set(b,d*Math.random()+a._array[0],e*Math.random()+a._array[1]),b}};h.prototype=new d,h.prototype.constructor=h;var i=function(a,c){var d=c.x-a.x,e=c.y-a.y,f=c.z-a.z;this.get=function(c){return c||(c=new b),b.set(c,d*Math.random()+a._array[0],e*Math.random()+a._array[1],f*Math.random()+a._array[2]),c}};return i.prototype=new d,i.prototype.constructor=i,d.constant=function(a){return new e(a)},d.vector=function(a){return new f(a)},d.random1D=function(a,b){return new g(a,b)},d.random2D=function(a,b){return new h(a,b)},d.random3D=function(a,b){return new i(a,b)},d}),d("qtek/math/Vector4",["require","../dep/glmatrix"],function(a){var b=a("../dep/glmatrix"),c=b.vec4,d=function(a,b,d,e){a=a||0,b=b||0,d=d||0,e=e||0,this._array=c.fromValues(a,b,d,e),this._dirty=!0};if(d.prototype={constructor:d,add:function(a){return c.add(this._array,this._array,a._array),this._dirty=!0,this},set:function(a,b,c,d){return this._array[0]=a,this._array[1]=b,this._array[2]=c,this._array[3]=d,this._dirty=!0,this},setArray:function(a){return this._array[0]=a[0],this._array[1]=a[1],this._array[2]=a[2],this._array[3]=a[3],this._dirty=!0,this},clone:function(){return new d(this.x,this.y,this.z,this.w)},copy:function(a){return c.copy(this._array,a._array),this._dirty=!0,this},dist:function(a){return c.dist(this._array,a._array)},distance:function(a){return c.distance(this._array,a._array)},div:function(a){return c.div(this._array,this._array,a._array),this._dirty=!0,this},divide:function(a){return c.divide(this._array,this._array,a._array),this._dirty=!0,this},dot:function(a){return c.dot(this._array,a._array)},len:function(){return c.len(this._array)},length:function(){return c.length(this._array)},lerp:function(a,b,d){return c.lerp(this._array,a._array,b._array,d),this._dirty=!0,this},min:function(a){return c.min(this._array,this._array,a._array),this._dirty=!0,this},max:function(a){return c.max(this._array,this._array,a._array),this._dirty=!0,this},mul:function(a){return c.mul(this._array,this._array,a._array),this._dirty=!0,this},multiply:function(a){return c.multiply(this._array,this._array,a._array),this._dirty=!0,this},negate:function(){return c.negate(this._array,this._array),this._dirty=!0,this},normalize:function(){return c.normalize(this._array,this._array),this._dirty=!0,this},random:function(a){return c.random(this._array,a),this._dirty=!0,this},scale:function(a){return c.scale(this._array,this._array,a),this._dirty=!0,this},scaleAndAdd:function(a,b){return c.scaleAndAdd(this._array,this._array,a._array,b),this._dirty=!0,this},sqrDist:function(a){return c.sqrDist(this._array,a._array)},squaredDistance:function(a){return c.squaredDistance(this._array,a._array)},sqrLen:function(){return c.sqrLen(this._array)},squaredLength:function(){return c.squaredLength(this._array)},sub:function(a){return c.sub(this._array,this._array,a._array),this._dirty=!0,this},subtract:function(a){return c.subtract(this._array,this._array,a._array),this._dirty=!0,this},transformMat4:function(a){return c.transformMat4(this._array,this._array,a._array),this._dirty=!0,this},transformQuat:function(a){return c.transformQuat(this._array,this._array,a._array),this._dirty=!0,this},toString:function(){return"["+Array.prototype.join.call(this._array,",")+"]"}},Object.defineProperty){var e=d.prototype;Object.defineProperty(e,"x",{get:function(){return this._array[0]},set:function(a){this._array[0]=a,this._dirty=!0}}),Object.defineProperty(e,"y",{get:function(){return this._array[1]},set:function(a){this._array[1]=a,this._dirty=!0}}),Object.defineProperty(e,"z",{get:function(){return this._array[2]},set:function(a){this._array[2]=a,this._dirty=!0}}),Object.defineProperty(e,"w",{get:function(){return this._array[3]},set:function(a){this._array[3]=a,this._dirty=!0}})}return d.add=function(a,b,d){return c.add(a._array,b._array,d._array),a._dirty=!0,a},d.set=function(a,b,d,e,f){c.set(a._array,b,d,e,f),a._dirty=!0},d.copy=function(a,b){return c.copy(a._array,b._array),a._dirty=!0,a},d.dist=function(a,b){return c.distance(a._array,b._array)},d.distance=d.dist,d.div=function(a,b,d){return c.divide(a._array,b._array,d._array),a._dirty=!0,a},d.divide=d.div,d.dot=function(a,b){return c.dot(a._array,b._array)},d.len=function(a){return c.length(a._array)},d.lerp=function(a,b,d,e){return c.lerp(a._array,b._array,d._array,e),a._dirty=!0,a},d.min=function(a,b,d){return c.min(a._array,b._array,d._array),a._dirty=!0,a},d.max=function(a,b,d){return c.max(a._array,b._array,d._array),a._dirty=!0,a},d.mul=function(a,b,d){return c.multiply(a._array,b._array,d._array),a._dirty=!0,a},d.multiply=d.mul,d.negate=function(a,b){return c.negate(a._array,b._array),a._dirty=!0,a},d.normalize=function(a,b){return c.normalize(a._array,b._array),a._dirty=!0,a},d.random=function(a,b){return c.random(a._array,b),a._dirty=!0,a},d.scale=function(a,b,d){return c.scale(a._array,b._array,d),a._dirty=!0,a},d.scaleAndAdd=function(a,b,d,e){return c.scaleAndAdd(a._array,b._array,d._array,e),a._dirty=!0,a},d.sqrDist=function(a,b){return c.sqrDist(a._array,b._array)},d.squaredDistance=d.sqrDist,d.sqrLen=function(a){return c.sqrLen(a._array)},d.squaredLength=d.sqrLen,d.sub=function(a,b,d){return c.subtract(a._array,b._array,d._array),a._dirty=!0,a},d.subtract=d.sub,d.transformMat4=function(a,b,d){return c.transformMat4(a._array,b._array,d._array),a._dirty=!0,a},d.transformQuat=function(a,b,d){return c.transformQuat(a._array,b._array,d._array),a._dirty=!0,a},d}),d("qtek/particleSystem/Particle",["require","../math/Vector3","../dep/glmatrix"],function(a){var b=a("../math/Vector3"),c=a("../dep/glmatrix"),d=c.vec3,e=function(){this.position=new b,this.rotation=new b,this.velocity=null,this.angularVelocity=null,this.life=1,this.age=0,this.spriteSize=1,this.weight=1,this.emitter=null};return e.prototype.update=function(a){this.velocity&&d.scaleAndAdd(this.position._array,this.position._array,this.velocity._array,a),this.angularVelocity&&d.scaleAndAdd(this.rotation._array,this.rotation._array,this.angularVelocity._array,a)},e}),d("qtek/particleSystem/Emitter",["require","../core/Base","../math/Vector3","./Particle","../math/Value","../dep/glmatrix"],function(a){var b=a("../core/Base"),c=a("../math/Vector3"),d=a("./Particle"),e=a("../math/Value"),f=a("../dep/glmatrix");f.vec3;var g=b.derive({max:1e3,amount:20,life:null,position:null,rotation:null,velocity:null,angularVelocity:null,spriteSize:null,weight:null,_particlePool:null},function(){this._particlePool=[];for(var a=0;ad;d++)b=this._particlePool.pop(),this.position&&this.position.get(b.position),this.rotation&&this.rotation.get(b.rotation),this.velocity&&this.velocity.get(b.velocity),this.angularVelocity&&this.angularVelocity.get(b.angularVelocity),this.life&&(b.life=this.life.get()),this.spriteSize&&(b.spriteSize=this.spriteSize.get()),this.weight&&(b.weight=this.weight.get()),b.age=0,a.push(b)},kill:function(a){this._particlePool.push(a)}});return g.constant=e.constant,g.vector=e.vector,g.random1D=e.random1D,g.random2D=e.random2D,g.random3D=e.random3D,g}),d("qtek/particleSystem/Field",["require","../core/Base"],function(a){var b=a("../core/Base"),c=b.derive({},{applyTo:function(){}});return c}),d("qtek/particleSystem/ForceField",["require","./Field","../math/Vector3","../dep/glmatrix"],function(a){var b=a("./Field"),c=a("../math/Vector3"),d=a("../dep/glmatrix"),e=d.vec3,f=b.derive(function(){return{force:new c}},{applyTo:function(a,b,c,d){c>0&&e.scaleAndAdd(a._array,a._array,this.force._array,d/c)}});return f}),d("qtek/particleSystem/particle.essl",[],function(){return"@export buildin.particle.vertex\n\nuniform mat4 worldView : WORLDVIEW;\nuniform mat4 projection : PROJECTION;\n\nattribute vec3 position : POSITION;\nattribute vec3 normal : NORMAL;\n\n#ifdef UV_ANIMATION\nattribute vec2 texcoord0 : TEXCOORD_0;\nattribute vec2 texcoord1 : TEXCOORD_1;\n\nvarying vec2 v_Uv0;\nvarying vec2 v_Uv1;\n#endif\n\nvarying float v_Age;\n\nvoid main() {\n v_Age = normal.x;\n float rotation = normal.y;\n\n vec4 worldViewPosition = worldView * vec4(position, 1.0);\n gl_Position = projection * worldViewPosition;\n float w = gl_Position.w;\n // TODO\n gl_PointSize = normal.z * projection[0].x / w;\n\n #ifdef UV_ANIMATION\n v_Uv0 = texcoord0;\n v_Uv1 = texcoord1;\n #endif\n}\n\n@end\n\n@export buildin.particle.fragment\n\nuniform sampler2D sprite;\nuniform sampler2D gradient;\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\n\nvarying float v_Age;\n\n#ifdef UV_ANIMATION\nvarying vec2 v_Uv0;\nvarying vec2 v_Uv1;\n#endif\n\nvoid main() {\n vec4 color = vec4(color, alpha);\n #ifdef SPRITE_ENABLED\n #ifdef UV_ANIMATION\n color *= texture2D(sprite, mix(v_Uv0, v_Uv1, gl_PointCoord));\n #else\n color *= texture2D(sprite, gl_PointCoord);\n #endif\n #endif\n #ifdef GRADIENT_ENABLED\n color *= texture2D(gradient, vec2(v_Age, 0.5));\n #endif\n gl_FragColor = color;\n}\n\n@end"}),d("qtek/particleSystem/ParticleRenderable",["require","../Renderable","../math/Vector3","../core/glenum","../StaticGeometry","../Material","../Shader","../dep/glmatrix","./particle.essl"],function(a){var b=a("../Renderable");a("../math/Vector3"),a("../core/glenum");var c=a("../StaticGeometry"),d=a("../Material"),e=a("../Shader"),f=a("../dep/glmatrix");f.vec3,e["import"](a("./particle.essl"));var g=new e({vertex:e.source("buildin.particle.vertex"),fragment:e.source("buildin.particle.fragment")});g.enableTexture("sprite");var h=b.derive({loop:!0,oneshot:!1,duration:1,spriteAnimationTileX:1,spriteAnimationTileY:1,spriteAnimationRepeat:0,mode:b.POINTS,_elapsedTime:0,_emitting:!0},function(){this.geometry=new c({dynamic:!0}),this.material||(this.material=new d({shader:g,transparent:!0,depthMask:!1})),this._particles=[],this._fields=[],this._emitters=[]},{culling:!1,frustumCulling:!1,castShadow:!1,receiveShadow:!1,addEmitter:function(a){this._emitters.push(a)},removeEmitter:function(a){this._emitters.splice(this._emitters.indexOf(a),1)},addField:function(a){this._fields.push(a)},removeField:function(a){this._fields.splice(this._fields.indexOf(a),1)},reset:function(){for(var a=0;ac;){var e=b[c];e.age+=a,e.age>=e.life?(e.emitter.kill(e),b[c]=b[d-1],b.pop(),d--):c++}for(var c=0;d>c;c++){var e=b[c];if(this._fields.length>0)for(var f=0;f1,g=a.attributes.position.value,h=a.attributes.normal.value,i=a.attributes.texcoord0.value,j=a.attributes.texcoord1.value,k=this._particles.length;g&&g.length===3*k||(g=a.attributes.position.value=new Float32Array(3*k),h=a.attributes.normal.value=new Float32Array(3*k),f&&(i=a.attributes.texcoord0.value=new Float32Array(2*k),j=a.attributes.texcoord1.value=new Float32Array(2*k)));for(var l=1/b,m=0;k>m;m++){for(var n=this._particles[m],o=3*m,p=0;3>p;p++)g[o+p]=n.position._array[p],h[o]=n.age/n.life,h[o+1]=0,h[o+2]=n.spriteSize;var q=2*m;if(f){var r=n.age/n.life,s=Math.round(r*(e-1))*d,t=Math.floor(s*l),u=s-t*b;i[q]=u/b,i[q+1]=1-t/c,j[q]=(u+1)/b,j[q+1]=1-(t+1)/c}}a.dirty()},render:function(a){return this._updateVertices(),b.prototype.render.call(this,a)},isFinished:function(){return this._elapsedTime>this.duration&&!this.loop},dispose:function(a){for(var b=0;b>16,c=a-(b<<8)>>8,d=a-(b<<16)-(c<<8);return[b,c,d]}function c(a,b,c){return(a<<16)+(b<<8)+c}var d=a("../core/Base"),e=a("../FrameBuffer"),f=a("../Texture2D"),g=a("../Shader"),h=a("../Material");g.import(a("./color.essl"));var i=d.derive(function(){return{renderer:null,downSampleRatio:1,width:100,height:100,lookupOffset:1,_frameBuffer:null,_texture:null,_shader:null,_idMaterials:[],_lookupTable:[],_meshMaterials:[],_idOffset:0}},function(){this.renderer&&(this.width=this.renderer.width,this.height=this.renderer.height),this._init()},{_init:function(){this._texture=new f({width:this.width*this.downSampleRatio,height:this.height*this.downSampleRatio}),this._frameBuffer=new e,this._shader=new g({vertex:g.source("buildin.picking.color.vertex"),fragment:g.source("buildin.picking.color.fragment")})},setPrecision:function(a){this._texture.width=this.width*a,this._texture.height=this.height*a,this.downSampleRatio=a},resize:function(a,b){this._texture.width=a*this.downSampleRatio,this._texture.height=b*this.downSampleRatio,this.width=a,this.height=b,this._texture.dirty()},update:function(a,b){var c=this.renderer;(c.width!==this.width||c.height!==this.height)&&this.resize(c.width,c.height),this._frameBuffer.attach(c.gl,this._texture),this._frameBuffer.bind(c),this._idOffset=this.lookupOffset,this._setMaterial(a),c.render(a,b),this._restoreMaterial(),this._frameBuffer.unbind(c)},_setMaterial:function(a){for(var c=0;ck;k++){var l=j[k];b[f]=a[l]._array,c[f]=g,d[f]=i[l],f++}e[0]=[0,1,2],e[1]=[3,4,5],this.geometry.dirty()}},_unProjectGrid:function(){for(var a=new d,b=[0,1,0,2,1,3,2,3,4,5,4,6,5,7,6,7,0,4,1,5,2,6,3,7],c=new e,g=new e,h=[],i=[],j=0;4>j;j++)i[j]=new e(0,0);var k=new f;return function(){a.copy(this.plane),a.applyTransform(this.camera.viewMatrix);for(var d=this.camera.frustum.vertices,e=0,f=0;12>f;f++){c._array=d[b[2*f]],g._array=d[b[2*f+1]];var j=a.intersectLine(c,g,h[e]);j&&(h[e]||(h[e]=j),e++)}if(0!==e){for(var f=0;e>f;f++)h[f].applyProjection(this.camera.projectionMatrix);for(var l=h[0]._array[0],m=h[0]._array[1],n=h[0]._array[0],o=h[0]._array[1],f=1;e>f;f++)n=Math.max(n,h[f]._array[0]),o=Math.max(o,h[f]._array[1]),l=Math.min(l,h[f]._array[0]),m=Math.min(m,h[f]._array[1]);if(l!=n&&m!=o){i[0]._array[0]=l,i[0]._array[1]=m,i[1]._array[0]=l,i[1]._array[1]=o,i[2]._array[0]=n,i[2]._array[1]=o,i[3]._array[0]=n,i[3]._array[1]=m;for(var f=0;4>f;f++)this.camera.castRay(i[f],k),k.intersectPlane(this.plane,i[f]);return i}}}}()});return k}),d("qtek/plugin/OrbitControl",["require","../core/Base","../math/Vector3","../math/Matrix4"],function(a){var b=a("../core/Base"),c=a("../math/Vector3");a("../math/Matrix4");var d=b.derive(function(){return{target:null,domElement:null,sensitivity:1,origin:new c,up:new c(0,1,0),minDistance:0,maxDistance:1/0,minPolarAngle:0,maxPolarAngle:Math.PI,_offsetPitch:0,_offsetRoll:0,_panX:0,_panY:0,_offsetX:0,_offsetY:0,_forward:0,_op:-1}},function(){this._mouseDown=this._mouseDown.bind(this),this._mouseUp=this._mouseUp.bind(this),this._mouseMove=this._mouseMove.bind(this),this._mouseOut=this._mouseOut.bind(this),this._mouseWheel=this._mouseWheel.bind(this),this.domElement&&this.enable()},{enable:function(){var a=this.domElement;a.addEventListener("mousedown",this._mouseDown),a.addEventListener("mousewheel",this._mouseWheel),a.addEventListener("DOMMouseScroll",this._mouseWheel),a.addEventListener("touchstart",this._mouseDown)},disable:function(){this.domElement.removeEventListener("mousedown",this._mouseDown),this.domElement.removeEventListener("mousewheel",this._mouseWheel),this.domElement.removeEventListener("DOMMouseScroll",this._mouseWheel),this.domElement.removeEventListener("touchstart",this._mouseDown),this._mouseUp()},_mouseWheel:function(a){a.preventDefault();var b=a.wheelDelta||-a.detail;this._forward+=b*this.sensitivity},_mouseDown:function(a){document.addEventListener("mousemove",this._mouseMove),document.addEventListener("mouseup",this._mouseUp),document.addEventListener("mouseout",this._mouseOut),document.addEventListener("touchend",this._mouseUp),document.addEventListener("touchmove",this._mouseMove),this._offsetX=a.pageX,this._offsetY=a.pageY,0===a.button?this._op=0:1===a.button&&(this._op=1)},_mouseMove:function(a){var b=a.pageX-this._offsetX,c=a.pageY-this._offsetY;if(0===this._op)this._offsetPitch+=b*this.sensitivity/100,this._offsetRoll+=c*this.sensitivity/100;else if(1===this._op){var d,e=this.origin.distance(this.target.position);d=this.target.fov?Math.sin(this.target.fov*Math.PI/360)/200:.005,this._panX+=b*this.sensitivity*e*d,this._panY+=c*this.sensitivity*e*d}this._offsetX=a.pageX,this._offsetY=a.pageY},_mouseUp:function(){document.removeEventListener("mousemove",this._mouseMove),document.removeEventListener("mouseup",this._mouseUp),document.removeEventListener("mouseout",this._mouseOut),document.removeEventListener("touchend",this._mouseUp),document.removeEventListener("touchmove",this._mouseMove),this._op=-1},_mouseOut:function(){this._mouseUp()},update:function(){var a=this.target,b=a.localTransform.z.normalize(),c=a.localTransform.y.normalize();if(0===this._op&&0!==this._offsetPitch&&0!==this._offsetRoll){a.rotateAround(this.origin,this.up,-this._offsetPitch);var d=a.localTransform.x;a.rotateAround(this.origin,d,-this._offsetRoll);var b=a.worldTransform.z.normalize(),e=Math.acos(this.up.dot(b));this._offsetRoll>0&&e<=this.minPolarAngle?a.rotateAround(this.origin,d,-e+this.minPolarAngle):this._offsetRoll<0&&e>=this.maxPolarAngle&&a.rotateAround(this.origin,d,-e+this.maxPolarAngle),this._offsetRoll=this._offsetPitch=0}else if(1===this._op){var d=a.localTransform.x.normalize().scale(-this._panX),c=a.localTransform.y.normalize().scale(this._panY);a.position.add(d).add(c),this.origin.add(d).add(c),this._panX=this._panY=0}if(0!==this._forward){var f=a.position.distance(this.origin),g=f+this._forward*f/5e3;gthis.minDistance&&a.position.scaleAndAdd(b,this._forward*f/5e3),this._forward=0}}});return d}),d("qtek/plugin/Skybox",["require","../Mesh","../geometry/Cube","../Shader","../Material"],function(a){var b,c=a("../Mesh"),d=a("../geometry/Cube"),e=a("../Shader"),f=a("../Material"),g=c.derive(function(){b||(b=new e({vertex:e.source("buildin.skybox.vertex"),fragment:e.source("buildin.skybox.fragment")}));var a=new f({shader:b,depthMask:!1});return{scene:null,geometry:new d,material:a,culling:!1}},function(){var a=this.scene;a&&this.attachScene(a)},{attachScene:function(a){this.scene&&this.detachScene(),this.scene=a,a.on("beforerender",this._beforeRenderScene,this)},detachScene:function(){this.scene&&this.scene.off("beforerender",this._beforeRenderScene,this),this.scene=null},dispose:function(){this.detachScene()},_beforeRenderScene:function(a,b,c){this.position.copy(c.getWorldPosition()),this.update(),a.renderQueue([this],c)}});return g}),d("qtek/plugin/Skydome",["require","../Mesh","../geometry/Sphere","../Shader","../Material","../shader/library"],function(a){var b=a("../Mesh"),c=a("../geometry/Sphere"),d=a("../Shader"),e=a("../Material");a("../shader/library");var f,g=b.derive(function(){f||(f=new d({vertex:d.source("buildin.basic.vertex"),fragment:d.source("buildin.basic.fragment")}),f.enableTexture("diffuseMap"));var a=new e({shader:f,depthMask:!1});return{scene:null,geometry:new c({widthSegments:30,heightSegments:30}),material:a,culling:!1}},function(){var a=this.scene;a&&this.attachScene(a)},{attachScene:function(a){this.scene&&this.detachScene(),this.scene=a,a.on("beforerender",this._beforeRenderScene,this)},detachScene:function(){this.scene&&this.scene.off("beforerender",this._beforeRenderScene,this),this.scene=null},_beforeRenderScene:function(a,b,c){this.position.copy(c.getWorldPosition()),this.update(),a.renderQueue([this],c)},dispose:function(){this.detachScene()}});return g}),d("qtek/prePass/EnvironmentMap",["require","../core/Base","../math/Vector3","../camera/Perspective","../core/glenum","../FrameBuffer","../TextureCube"],function(a){var b=a("../core/Base"),c=a("../math/Vector3"),d=a("../camera/Perspective");a("../core/glenum");var e=a("../FrameBuffer");a("../TextureCube");var f=["px","nx","py","ny","pz","nz"],g=b.derive(function(){var a={position:new c,far:1e3,near:.1,texture:null};return a._cameras={px:new d({fov:90}),nx:new d({fov:90}),py:new d({fov:90}),ny:new d({fov:90}),pz:new d({fov:90}),nz:new d({fov:90})},a._cameras.px.lookAt(c.POSITIVE_X,c.NEGATIVE_Y),a._cameras.nx.lookAt(c.NEGATIVE_X,c.NEGATIVE_Y),a._cameras.py.lookAt(c.POSITIVE_Y,c.POSITIVE_Z),a._cameras.ny.lookAt(c.NEGATIVE_Y,c.NEGATIVE_Z),a._cameras.pz.lookAt(c.POSITIVE_Z,c.NEGATIVE_Y),a._cameras.nz.lookAt(c.NEGATIVE_Z,c.NEGATIVE_Y),a._frameBuffers={px:new e,nx:new e,py:new e,ny:new e,pz:new e,nz:new e},a},{render:function(a,b,d){var e=a.gl;d||b.update(!0);for(var g=this.texture.width,h=180*(2*Math.atan(g/(g-.5))/Math.PI),i=0;6>i;i++){var j=f[i],k=this._cameras[j];c.copy(k.position,this.position),k.far=this.far,k.near=this.near,k.fov=h,this._frameBuffers[j].attach(e,this.texture,e.COLOR_ATTACHMENT0,e.TEXTURE_CUBE_MAP_POSITIVE_X+i),this._frameBuffers[j].bind(a),a.render(b,k,!0),this._frameBuffers[j].unbind(a)}},dispose:function(a){for(var b=0;6>b;b++){var c=f[b];this._frameBuffers[c].dispose(a.gl)}}});return g}),d("qtek/prePass/Reflection",["require","../core/Base","../math/Vector4"],function(a){var b=a("../core/Base");a("../math/Vector4");var c=b.derive(function(){console.warn("TODO")},{render:function(){}});return c}),d("qtek/prePass/ShadowMap",["require","../core/Base","../core/glenum","../math/Vector3","../math/BoundingBox","../math/Frustum","../math/Matrix4","../Renderer","../Shader","../Light","../Mesh","../light/Spot","../light/Directional","../light/Point","../shader/library","../Material","../FrameBuffer","../Texture2D","../TextureCube","../camera/Perspective","../camera/Orthographic","../compositor/Pass","../compositor/TexturePool","../dep/glmatrix"],function(a){var b=a("../core/Base"),c=a("../core/glenum"),d=a("../math/Vector3"),e=a("../math/BoundingBox"),f=a("../math/Frustum"),g=a("../math/Matrix4"),h=a("../Renderer"),i=a("../Shader");a("../Light"),a("../Mesh");var j=a("../light/Spot"),k=a("../light/Directional"),l=a("../light/Point");a("../shader/library");var m=a("../Material"),n=a("../FrameBuffer"),o=a("../Texture2D"),p=a("../TextureCube"),q=a("../camera/Perspective"),r=a("../camera/Orthographic"),s=a("../compositor/Pass"),t=a("../compositor/TexturePool"),u=a("../dep/glmatrix"),v=u.mat4;u.vec3;var w=["px","nx","py","ny","pz","nz"],x=b.derive(function(){return{softShadow:x.PCF,shadowBlur:1,shadowCascade:1,cascadeSplitLogFactor:.2,lightFrustumBias:10,_frameBuffer:new n,_textures:{},_shadowMapNumber:{POINT_LIGHT:0,DIRECTIONAL_LIGHT:0,SPOT_LIGHT:0},_meshMaterials:{},_depthMaterials:{},_depthShaders:{},_distanceMaterials:{},_opaqueCasters:[],_receivers:[],_lightsCastShadow:[],_lightCameras:{},_texturePool:new t}},function(){this._gaussianPassH=new s({fragment:i.source("buildin.compositor.gaussian_blur_h")}),this._gaussianPassV=new s({fragment:i.source("buildin.compositor.gaussian_blur_v")}),this._gaussianPassH.setUniform("blurSize",this.shadowBlur),this._gaussianPassV.setUniform("blurSize",this.shadowBlur),this._outputDepthPass=new s({fragment:i.source("buildin.sm.debug_depth")})},{render:function(a,b,c){this.trigger("beforerender",this,a,b,c),this._renderShadowPass(a,b,c),this.trigger("afterrender",this,a,b,c)},renderDebug:function(a,b){var d=a.clear;a.clear=c.DEPTH_BUFFER_BIT;var e=a.viewport,f=0,g=0,h=b||e.width/4,i=h;this.softShadow===x.VSM?this._outputDepthPass.material.shader.define("fragment","USE_VSM"):this._outputDepthPass.material.shader.unDefine("fragment","USE_VSM");for(var j in this._textures)a.setViewport(f,g,h,i),this._outputDepthPass.setUniform("depthMap",this._textures[j]),this._outputDepthPass.render(a),f+=h;a.setViewport(e),a.clear=d},_bindDepthMaterial:function(a,b,c){for(var d=0;d0&&(n.define("vertex","SKINNING"),n.define("vertex","JOINT_NUMBER",k)),h&&n.define("both","SHADOW_TRANSPARENT"),this._depthShaders[f]=n),l||(l=new m({shader:n}),this._depthMaterials[e]=l),this._meshMaterials[g.__GUID__]=g.material,g.material=l,this.softShadow===x.VSM?n.define("fragment","USE_VSM"):n.unDefine("fragment","USE_VSM"),l.setUniform("bias",b),l.setUniform("slopeScale",c),h&&l.set("shadowTransparentMap",j))}},_bindDistanceMaterial:function(a,b){for(var c=0;c0&&(f.shader.define("vertex","SKINNING"),f.shader.define("vertex","JOINT_NUMBER",e)),this._distanceMaterials[e]=f),this._meshMaterials[d.__GUID__]=d.material,d.material=f,this.softShadow===x.VSM?f.shader.define("fragment","USE_VSM"):f.shader.unDefine("fragment","USE_VSM"),f.set("lightPosition",b.position._array),f.set("range",5*b.range))}},_restoreMaterial:function(a){for(var b=0;b1&&this._shadowMapNumber.DIRECTIONAL_LIGHT>1&&console.warn("There is only one directional light can cast shadow when using cascaded shadow map");var r=m.slice(),s=m.slice();r.pop(),s.shift(),r.reverse(),s.reverse(),h.reverse(),i.reverse();for(var p=0;p0&&(v.fragmentDefines[z]=y,w=!0)}w&&v.dirty(),this.shadowCascade>1?v.define("fragment","SHADOW_CASCADE",this.shadowCascade):v.unDefine("fragment","SHADOW_CASCADE"),v.__shadowDefineUpdated=!0}f.length>0&&(u.setUniform("spotLightShadowMaps",f),u.setUniform("spotLightMatrices",g)),h.length>0&&(u.setUniform("directionalLightShadowMaps",h),this.shadowCascade>1&&(u.setUniform("shadowCascadeClipsNear",r),u.setUniform("shadowCascadeClipsFar",s)),u.setUniform("directionalLightMatrices",i)),n.length>0&&(u.setUniform("pointLightShadowMaps",n),u.setUniform("pointLightRanges",o)),u.__shadowUniformUpdated=!0}}}},_renderDirectionalLightShadow:function(){var a=new f,b=new g,c=new e,d=new g,i=new g,j=new g,k=0,l=0;return function(e,f,m,n,o,p,q,r){var s=f.shadowBias;this._bindDepthMaterial(o,s,f.shadowSlopeScale),o.sort(h.opaqueSortFunc);var t=n.far,u=-n.sceneBoundingBoxLastFrame.min.z;l=Math.max(u-k,0),k=u,u+=l,u>n.near&&(n.far=Math.min(n.far,u)),n.updateProjectionMatrix(),n.frustum.setFromProjection(n.projectionMatrix);var w=this._getDirectionalLightCamera(f,m,n),y=i._array;v.copy(y,w.worldTransform._array),v.invert(y,y),v.multiply(y,w.projectionMatrix._array,y),v.multiply(y,y,n.worldTransform._array),j.copy(w.projectionMatrix);for(var z=[],A=n.near,B=n.far,C=n.fov/180*Math.PI,D=n.aspect,E=(A+t)/(A-t),F=2*A*t/(A-t),G=0;G<=this.shadowCascade;G++){var H=A*Math.pow(B/A,G/this.shadowCascade),I=A+(B-A)*G/this.shadowCascade,J=H*this.cascadeSplitLogFactor+I*(1-this.cascadeSplitLogFactor);z.push(J),p.push(-(-J*E+F)/-J)}for(var G=0;Gh;h++){var i=w[h],j=this._getPointLightCamera(b,i);this._frameBuffer.attach(a.gl,f,g.COLOR_ATTACHMENT0,g.TEXTURE_CUBE_MAP_POSITIVE_X+h),this._frameBuffer.bind(a),g.clear(g.COLOR_BUFFER_BIT|g.DEPTH_BUFFER_BIT),a.renderQueue(c,j),this._frameBuffer.unbind(a)}},_gaussianFilter:function(a,b,d){var e={width:d,height:d,type:c.FLOAT},f=a.gl,g=this._texturePool.get(e);this._frameBuffer.attach(f,g),this._frameBuffer.bind(a),this._gaussianPassH.setUniform("texture",b),this._gaussianPassH.setUniform("textureWidth",d),this._gaussianPassH.render(a),this._frameBuffer.unbind(a),this._frameBuffer.attach(f,b),this._frameBuffer.bind(a),this._gaussianPassV.setUniform("texture",g),this._gaussianPassV.setUniform("textureHeight",d),this._gaussianPassV.render(a),this._frameBuffer.unbind(a),this._texturePool.put(g)},_getTexture:function(a,b){var d=this._textures[a],e=b.shadowResolution||512;return d||(d=b instanceof l?new p:new o,d.width=e,d.height=e,this.softShadow===x.VSM?(d.type=c.FLOAT,d.anisotropic=4):(d.minFilter=c.LINEAR,d.magFilter=c.LINEAR,d.useMipmap=!1),this._textures[a]=d),d},_getPointLightCamera:function(a,b){this._lightCameras.point||(this._lightCameras.point={px:new q,nx:new q,py:new q,ny:new q,pz:new q,nz:new q});var c=this._lightCameras.point[b];switch(c.far=a.range,c.fov=90,c.position.set(0,0,0),b){case"px":c.lookAt(d.POSITIVE_X,d.NEGATIVE_Y);break;case"nx":c.lookAt(d.NEGATIVE_X,d.NEGATIVE_Y);break;case"py":c.lookAt(d.POSITIVE_Y,d.POSITIVE_Z);break;case"ny":c.lookAt(d.NEGATIVE_Y,d.NEGATIVE_Z);break;case"pz":c.lookAt(d.POSITIVE_Z,d.NEGATIVE_Y);break;case"nz":c.lookAt(d.NEGATIVE_Z,d.NEGATIVE_Y)}return c.position.copy(a.position),c.update(),c},_getDirectionalLightCamera:function(){var a=new g,b=new e;return function(c,d,e){this._lightCameras.directional||(this._lightCameras.directional=new r);var f=this._lightCameras.directional;f.position.copy(e.frustum.boundingBox.min).add(e.frustum.boundingBox.max).scale(.5).transformMat4(e.worldTransform),f.rotation.copy(c.rotation),f.scale.copy(c.scale),f.updateLocalTransform(),f.updateWorldTransform(),a.copy(f.worldTransform).invert().multiply(e.worldTransform),e.frustum.getTransformedBoundingBox(b,a);var g=b.min._array,h=b.max._array;return f.position.scaleAndAdd(f.worldTransform.z,h[2]+this.lightFrustumBias),f.near=0,f.far=-g[2]+h[2]+this.lightFrustumBias,f.left=g[0]-this.lightFrustumBias,f.right=h[0]+this.lightFrustumBias,f.top=h[1]+this.lightFrustumBias,f.bottom=g[1]-this.lightFrustumBias,f.update(!0),f}}(),_getSpotLightCamera:function(a){this._lightCameras.spot||(this._lightCameras.spot=new q);var b=this._lightCameras.spot;return b.fov=2*a.penumbraAngle,b.far=a.range,b.worldTransform.copy(a.worldTransform),b.updateProjectionMatrix(),v.invert(b.viewMatrix._array,b.worldTransform._array),b},dispose:function(a){var b=a.gl;for(var c in this._depthMaterials){var d=this._depthMaterials[c];d.dispose(b)}for(var c in this._distanceMaterials){var d=this._distanceMaterials[c];d.dispose(b)}this._frameBuffer&&this._frameBuffer.dispose(b);for(var e in this._textures)this._textures[e].dispose(b);this._texturePool.clear(a.gl),this._depthMaterials={},this._distanceMaterials={},this._textures={},this._lightCameras={},this._shadowMapNumber={POINT_LIGHT:0,DIRECTIONAL_LIGHT:0,SPOT_LIGHT:0},this._meshMaterials={};for(var f=0;f 0.01)\n {\n gl_FragColor.rgb = gl_FragColor.rgb * mix(lineColor, vec3(1.0), edgeFactor(lineWidth));\n }\n}\n\n@end"}),d("qtek/shader/source/lambert.essl",[],function(){return"/**\n * http://en.wikipedia.org/wiki/Lambertian_reflectance\n */\n\n@export buildin.lambert.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\n\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 normal : NORMAL;\n\nattribute vec3 barycentric;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n // Upper 3x3 of skinMatrix is orthogonal\n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4( skinnedPosition, 1.0 );\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_Normal = normalize( ( worldInverseTranspose * vec4(skinnedNormal, 0.0) ).xyz );\n v_WorldPosition = ( world * vec4( skinnedPosition, 1.0) ).xyz;\n\n v_Barycentric = barycentric;\n}\n\n@end\n\n\n@export buildin.lambert.fragment\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\n\nuniform sampler2D diffuseMap;\nuniform sampler2D alphaMap;\n\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform vec3 emission : [0.0, 0.0, 0.0];\nuniform float alpha : 1.0;\n\n// Uniforms for wireframe\nuniform float lineWidth : 0.0;\nuniform vec3 lineColor : [0.0, 0.0, 0.0];\nvarying vec3 v_Barycentric;\n\n#ifdef AMBIENT_LIGHT_NUMBER\n@import buildin.header.ambient_light\n#endif\n#ifdef POINT_LIGHT_NUMBER\n@import buildin.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_NUMBER\n@import buildin.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_NUMBER\n@import buildin.header.spot_light\n#endif\n\n#extension GL_OES_standard_derivatives : enable\n// Import util functions and uniforms needed\n@import buildin.util.calculate_attenuation\n\n@import buildin.util.edge_factor\n\n@import buildin.plugin.compute_shadow_map\n\nvoid main()\n{\n #ifdef RENDER_NORMAL\n gl_FragColor = vec4(v_Normal, 1.0);\n return;\n #endif\n #ifdef RENDER_TEXCOORD\n gl_FragColor = vec4(v_Texcoord, 1.0, 1.0);\n return;\n #endif\n\n gl_FragColor = vec4(color, alpha);\n\n #ifdef DIFFUSEMAP_ENABLED\n vec4 tex = texture2D( diffuseMap, v_Texcoord );\n #ifdef SRGB_DECODE\n tex.rgb = pow(tex.rgb, vec3(2.2));\n #endif\n gl_FragColor.rgb *= tex.rgb;\n #ifdef DIFFUSEMAP_ALPHA_ALPHA\n gl_FragColor.a *= tex.a;\n #endif\n #endif\n\n vec3 diffuseColor = vec3(0.0, 0.0, 0.0);\n \n #ifdef AMBIENT_LIGHT_NUMBER\n for(int i = 0; i < AMBIENT_LIGHT_NUMBER; i++)\n {\n diffuseColor += ambientLightColor[i];\n }\n #endif\n // Compute point light color\n #ifdef POINT_LIGHT_NUMBER\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[POINT_LIGHT_NUMBER];\n if( shadowEnabled )\n {\n computeShadowOfPointLights( v_WorldPosition, shadowContribs );\n }\n #endif\n for(int i = 0; i < POINT_LIGHT_NUMBER; i++)\n {\n\n vec3 lightPosition = pointLightPosition[i];\n vec3 lightColor = pointLightColor[i];\n float range = pointLightRange[i];\n\n vec3 lightDirection = lightPosition - v_WorldPosition;\n\n // Calculate point light attenuation\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range);\n\n // Normalize vectors\n lightDirection /= dist;\n\n float ndl = dot( v_Normal, lightDirection );\n\n float shadowContrib = 1.0;\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n diffuseColor += lightColor * clamp(ndl, 0.0, 1.0) * attenuation * shadowContrib;\n }\n #endif\n #ifdef DIRECTIONAL_LIGHT_NUMBER\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[DIRECTIONAL_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfDirectionalLights( v_WorldPosition, shadowContribs );\n }\n #endif\n for(int i = 0; i < DIRECTIONAL_LIGHT_NUMBER; i++)\n {\n vec3 lightDirection = -directionalLightDirection[i];\n vec3 lightColor = directionalLightColor[i];\n \n float ndl = dot( v_Normal, normalize( lightDirection ) );\n\n float shadowContrib = 1.0;\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n diffuseColor += lightColor * clamp(ndl, 0.0, 1.0) * shadowContrib;\n }\n #endif\n \n #ifdef SPOT_LIGHT_NUMBER\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[SPOT_LIGHT_NUMBER];\n if( shadowEnabled )\n {\n computeShadowOfSpotLights( v_WorldPosition, shadowContribs );\n }\n #endif\n for(int i = 0; i < SPOT_LIGHT_NUMBER; i++)\n {\n vec3 lightPosition = -spotLightPosition[i];\n vec3 spotLightDirection = -normalize( spotLightDirection[i] );\n vec3 lightColor = spotLightColor[i];\n float range = spotLightRange[i];\n float a = spotLightUmbraAngleCosine[i];\n float b = spotLightPenumbraAngleCosine[i];\n float falloffFactor = spotLightFalloffFactor[i];\n\n vec3 lightDirection = lightPosition - v_WorldPosition;\n // Calculate attenuation\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range); \n\n // Normalize light direction\n lightDirection /= dist;\n // Calculate spot light fall off\n float c = dot(spotLightDirection, lightDirection);\n\n float falloff;\n falloff = clamp((c - a) /( b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n\n float ndl = dot(v_Normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n diffuseColor += lightColor * ndl * attenuation * (1.0-falloff) * shadowContrib;\n\n }\n #endif\n\n gl_FragColor.rgb *= diffuseColor;\n gl_FragColor.rgb += emission;\n if(lineWidth > 0.01)\n {\n gl_FragColor.rgb = gl_FragColor.rgb * mix(lineColor, vec3(1.0), edgeFactor(lineWidth));\n }\n}\n\n@end"}),d("qtek/shader/source/phong.essl",[],function(){return"\n// http://en.wikipedia.org/wiki/Blinn%E2%80%93Phong_shading_model\n\n@export buildin.phong.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\n\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 normal : NORMAL;\nattribute vec4 tangent : TANGENT;\n\n#ifdef VERTEX_COLOR\nattribute vec4 color : COLOR;\n#endif\n\nattribute vec3 barycentric;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\n\n#ifdef NORMALMAP_ENABLED\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\n#ifdef VERTEX_COLOR\nvarying vec4 v_Color;\n#endif\n\nvoid main()\n{\n \n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n #ifdef SKINNING\n \n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n // Upper 3x3 of skinMatrix is orthogonal\n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n skinnedTangent = (skinMatrixWS * vec4(tangent.xyz, 0.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n v_Barycentric = barycentric;\n\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\n \n #ifdef NORMALMAP_ENABLED\n v_Tangent = normalize((worldInverseTranspose * vec4(skinnedTangent, 0.0)).xyz);\n v_Bitangent = normalize(cross(v_Normal, v_Tangent) * tangent.w);\n #endif\n\n #ifdef VERTEX_COLOR\n v_Color = color;\n #endif\n}\n\n@end\n\n\n@export buildin.phong.fragment\n\nuniform mat4 viewInverse : VIEWINVERSE;\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\n\n#ifdef NORMALMAP_ENABLED\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\nuniform sampler2D diffuseMap;\nuniform sampler2D normalMap;\nuniform sampler2D specularMap;\nuniform samplerCube environmentMap;\n\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\n\nuniform float shininess : 30;\n\nuniform vec3 specularColor : [1.0, 1.0, 1.0];\nuniform vec3 emission : [0.0, 0.0, 0.0];\n\nuniform float reflectivity : 0.5;\n\n// Uniforms for wireframe\nuniform float lineWidth : 0.0;\nuniform vec3 lineColor : [0.0, 0.0, 0.0];\nvarying vec3 v_Barycentric;\n\n#ifdef AMBIENT_LIGHT_NUMBER\n@import buildin.header.ambient_light\n#endif\n#ifdef POINT_LIGHT_NUMBER\n@import buildin.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_NUMBER\n@import buildin.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_NUMBER\n@import buildin.header.spot_light\n#endif\n\n#extension GL_OES_standard_derivatives : enable\n// Import util functions and uniforms needed\n@import buildin.util.calculate_attenuation\n\n@import buildin.util.edge_factor\n\n@import buildin.plugin.compute_shadow_map\n\nvoid main()\n{\n #ifdef RENDER_TEXCOORD\n gl_FragColor = vec4(v_Texcoord, 1.0, 1.0);\n return;\n #endif\n\n vec4 finalColor = vec4(color, alpha);\n\n vec3 eyePos = viewInverse[3].xyz;\n vec3 viewDirection = normalize(eyePos - v_WorldPosition);\n\n #ifdef DIFFUSEMAP_ENABLED\n vec4 tex = texture2D(diffuseMap, v_Texcoord);\n #ifdef SRGB_DECODE\n tex.rgb = pow(tex.rgb, vec3(2.2));\n #endif\n finalColor.rgb *= tex.rgb;\n #ifdef DIFFUSEMAP_ALPHA_ALPHA\n finalColor.a *= tex.a;\n #endif\n #endif\n\n vec3 spec = specularColor;\n #ifdef SPECULARMAP_ENABLED\n spec *= texture2D(specularMap, v_Texcoord).rgb;\n #endif\n\n vec3 normal = v_Normal;\n #ifdef NORMALMAP_ENABLED\n normal = texture2D(normalMap, v_Texcoord).xyz * 2.0 - 1.0;\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\n normal = normalize(tbn * normal);\n #endif\n\n #ifdef RENDER_NORMAL\n gl_FragColor = vec4(normal, 1.0);\n return;\n #endif\n\n // Diffuse part of all lights\n vec3 diffuseTerm = vec3(0.0, 0.0, 0.0);\n // Specular part of all lights\n vec3 specularTerm = vec3(0.0, 0.0, 0.0);\n \n #ifdef AMBIENT_LIGHT_NUMBER\n for(int i = 0; i < AMBIENT_LIGHT_NUMBER; i++)\n {\n diffuseTerm += ambientLightColor[i];\n }\n #endif\n #ifdef POINT_LIGHT_NUMBER\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[POINT_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfPointLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < POINT_LIGHT_NUMBER; i++)\n {\n vec3 lightPosition = pointLightPosition[i];\n vec3 lightColor = pointLightColor[i];\n float range = pointLightRange[i];\n\n vec3 lightDirection = lightPosition - v_WorldPosition;\n\n // Calculate point light attenuation\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range); \n\n // Normalize vectors\n lightDirection /= dist;\n vec3 halfVector = normalize(lightDirection + viewDirection);\n\n float ndh = dot(normal, halfVector);\n ndh = clamp(ndh, 0.0, 1.0);\n\n float ndl = dot(normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lightColor * ndl * attenuation * shadowContrib;\n\n diffuseTerm += li;\n if (shininess > 0.0)\n {\n specularTerm += li * pow(ndh, shininess);\n }\n\n }\n #endif\n\n #ifdef DIRECTIONAL_LIGHT_NUMBER\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[DIRECTIONAL_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfDirectionalLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < DIRECTIONAL_LIGHT_NUMBER; i++)\n {\n\n vec3 lightDirection = -normalize(directionalLightDirection[i]);\n vec3 lightColor = directionalLightColor[i];\n\n vec3 halfVector = normalize(lightDirection + viewDirection);\n\n float ndh = dot(normal, halfVector);\n ndh = clamp(ndh, 0.0, 1.0);\n\n float ndl = dot(normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lightColor * ndl * shadowContrib;\n\n diffuseTerm += li;\n if (shininess > 0.0)\n {\n specularTerm += li * pow(ndh, shininess);\n }\n }\n #endif\n\n #ifdef SPOT_LIGHT_NUMBER\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[SPOT_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfSpotLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < SPOT_LIGHT_NUMBER; i++)\n {\n vec3 lightPosition = spotLightPosition[i];\n vec3 spotLightDirection = -normalize(spotLightDirection[i]);\n vec3 lightColor = spotLightColor[i];\n float range = spotLightRange[i];\n float a = spotLightUmbraAngleCosine[i];\n float b = spotLightPenumbraAngleCosine[i];\n float falloffFactor = spotLightFalloffFactor[i];\n\n vec3 lightDirection = lightPosition - v_WorldPosition;\n // Calculate attenuation\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range); \n\n // Normalize light direction\n lightDirection /= dist;\n // Calculate spot light fall off\n float c = dot(spotLightDirection, lightDirection);\n\n float falloff;\n // Fomular from real-time-rendering\n falloff = clamp((c - a) /( b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n\n vec3 halfVector = normalize(lightDirection + viewDirection);\n\n float ndh = dot(normal, halfVector);\n ndh = clamp(ndh, 0.0, 1.0);\n\n float ndl = dot(normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n if (shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lightColor * ndl * attenuation * (1.0-falloff) * shadowContrib;\n\n diffuseTerm += li;\n if (shininess > 0.0)\n {\n specularTerm += li * pow(ndh, shininess);\n }\n }\n #endif\n\n finalColor.rgb *= diffuseTerm;\n finalColor.rgb += specularTerm * spec;\n finalColor.rgb += emission;\n\n #ifdef ENVIRONMENTMAP_ENABLED\n vec3 envTex = textureCube(environmentMap, reflect(-viewDirection, normal)).xyz;\n finalColor.rgb = finalColor.rgb + envTex * reflectivity;\n #endif\n\n if(lineWidth > 0.01)\n {\n finalColor.rgb = finalColor.rgb * mix(lineColor, vec3(1.0), edgeFactor(lineWidth));\n }\n\n #ifdef GAMMA_ENCODE\n finalColor.rgb = pow(finalColor.rgb, vec3(1 / 2.2));\n #endif\n\n gl_FragColor = finalColor;\n}\n\n@end"}),d("qtek/shader/source/standard.essl",[],function(){return"\n// http://blog.selfshadow.com/publications/s2013-shading-course/\n\n@export buildin.standard.vertex\n\n@import buildin.phong.vertex\n\n@end\n\n\n@export buildin.standard.fragment\n\n#define PI 3.14159265358979\n\nuniform mat4 viewInverse : VIEWINVERSE;\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\n\n#ifdef NORMALMAP_ENABLED\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\nuniform sampler2D diffuseMap;\nuniform sampler2D normalMap;\nuniform sampler2D specularMap;\nuniform samplerCube environmentMap;\n\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\n\nuniform float glossiness : 0.5;\n\nuniform vec3 specularColor : [0.1, 0.1, 0.1];\nuniform vec3 emission : [0.0, 0.0, 0.0];\n\n// Uniforms for wireframe\nuniform float lineWidth : 0.0;\nuniform vec3 lineColor : [0.0, 0.0, 0.0];\nvarying vec3 v_Barycentric;\n\n#ifdef AMBIENT_LIGHT_NUMBER\n@import buildin.header.ambient_light\n#endif\n#ifdef POINT_LIGHT_NUMBER\n@import buildin.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_NUMBER\n@import buildin.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_NUMBER\n@import buildin.header.spot_light\n#endif\n\n#extension GL_OES_standard_derivatives : enable\n\n// Import util functions and uniforms needed\n@import buildin.util.calculate_attenuation\n\n@import buildin.util.edge_factor\n\n@import buildin.plugin.compute_shadow_map\n\n\nfloat G_Smith(float glossiness, float ndv, float ndl)\n{\n // float k = (roughness+1.0) * (roughness+1.0) * 0.125;\n float roughness = 1.0 - glossiness;\n float k = roughness * roughness / 2.0;\n float G1V = ndv / (ndv * (1.0 - k) + k);\n float G1L = ndl / (ndl * (1.0 - k) + k);\n return G1L * G1V;\n}\n// Fresnel\nvec3 F_Schlick(float ndv, vec3 spec) {\n return spec + (1.0 - spec) * pow(1.0 - ndv, 5.0);\n}\n\nfloat D_Phong(float g, float ndh) {\n // from black ops 2\n float a = pow(8192.0, g);\n return (a + 2.0) / 8.0 * pow(ndh, a);\n}\n\nfloat D_GGX(float g, float ndh) {\n float r = 1.0 - g;\n float a = r * r;\n float tmp = ndh * ndh * (a - 1.0) + 1.0;\n return a / (PI * tmp * tmp);\n}\n\nvoid main()\n{\n #ifdef RENDER_TEXCOORD\n gl_FragColor = vec4(v_Texcoord, 1.0, 1.0);\n return;\n #endif\n\n vec4 finalColor = vec4(color, alpha);\n\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = normalize(eyePos - v_WorldPosition);\n float g = glossiness;\n\n #ifdef DIFFUSEMAP_ENABLED\n vec4 tex = texture2D(diffuseMap, v_Texcoord);\n #ifdef SRGB_DECODE\n tex.rgb = pow(tex.rgb, vec3(2.2));\n #endif\n finalColor.rgb *= tex.rgb;\n #ifdef DIFFUSEMAP_ALPHA_ALPHA\n finalColor.a *= tex.a;\n #endif\n #ifdef DIFFUSEMAP_ALPHA_GLOSS\n g *= tex.a;\n #endif\n #endif\n\n vec3 spec = specularColor;\n #ifdef SPECULARMAP_ENABLED\n spec *= texture2D(specularMap, v_Texcoord).rgb;\n #endif\n\n vec3 N = v_Normal;\n #ifdef NORMALMAP_ENABLED\n N = texture2D(normalMap, v_Texcoord).xyz * 2.0 - 1.0;\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\n N = normalize(tbn * N);\n #endif\n\n #ifdef RENDER_NORMAL\n gl_FragColor = vec4(N, 1.0);\n return;\n #endif\n\n #ifdef RENDER_GLOSSINESS\n gl_FragColor = vec4(vec3(g), 1.0);\n return;\n #endif\n\n // Diffuse part of all lights\n vec3 diffuseTerm = vec3(0.0, 0.0, 0.0);\n // Specular part of all lights\n vec3 specularTerm = vec3(0.0, 0.0, 0.0);\n \n vec3 fresnelTerm = F_Schlick(clamp(dot(N, V), 0.0, 1.0), spec);\n \n #ifdef AMBIENT_LIGHT_NUMBER\n for(int i = 0; i < AMBIENT_LIGHT_NUMBER; i++)\n {\n // Hemisphere ambient lighting from cryengine\n diffuseTerm += ambientLightColor[i] * (clamp(N.y * 0.7, 0.0, 1.0) + 0.3);\n // diffuseTerm += ambientLightColor[i];\n }\n #endif\n #ifdef POINT_LIGHT_NUMBER\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[POINT_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfPointLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < POINT_LIGHT_NUMBER; i++)\n {\n\n vec3 lightPosition = pointLightPosition[i];\n vec3 lc = pointLightColor[i];\n float range = pointLightRange[i];\n\n vec3 L = lightPosition - v_WorldPosition;\n\n // Calculate point light attenuation\n float dist = length(L);\n float attenuation = lightAttenuation(dist, range);\n L /= dist;\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lc * ndl * attenuation * shadowContrib;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }\n #endif\n\n #ifdef DIRECTIONAL_LIGHT_NUMBER\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[DIRECTIONAL_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfDirectionalLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < DIRECTIONAL_LIGHT_NUMBER; i++)\n {\n\n vec3 L = -normalize(directionalLightDirection[i]);\n vec3 lc = directionalLightColor[i];\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lc * ndl * shadowContrib;\n\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }\n #endif\n\n #ifdef SPOT_LIGHT_NUMBER\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n float shadowContribs[SPOT_LIGHT_NUMBER];\n if(shadowEnabled)\n {\n computeShadowOfSpotLights(v_WorldPosition, shadowContribs);\n }\n #endif\n for(int i = 0; i < SPOT_LIGHT_NUMBER; i++)\n {\n vec3 lightPosition = spotLightPosition[i];\n vec3 spotLightDirection = -normalize(spotLightDirection[i]);\n vec3 lc = spotLightColor[i];\n float range = spotLightRange[i];\n float a = spotLightUmbraAngleCosine[i];\n float b = spotLightPenumbraAngleCosine[i];\n float falloffFactor = spotLightFalloffFactor[i];\n\n vec3 L = lightPosition - v_WorldPosition;\n // Calculate attenuation\n float dist = length(L);\n float attenuation = lightAttenuation(dist, range); \n\n // Normalize light direction\n L /= dist;\n // Calculate spot light fall off\n float c = dot(spotLightDirection, L);\n\n float falloff;\n // Fomular from real-time-rendering\n falloff = clamp((c - a) /( b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n float shadowContrib = 1.0;\n #if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n if (shadowEnabled)\n {\n shadowContrib = shadowContribs[i];\n }\n #endif\n\n vec3 li = lc * attenuation * (1.0-falloff) * shadowContrib * ndl;\n\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }\n #endif\n\n finalColor.rgb *= diffuseTerm;\n finalColor.rgb += specularTerm;\n finalColor.rgb += emission;\n\n #ifdef ENVIRONMENTMAP_ENABLED\n vec3 envTex = textureCube(environmentMap, reflect(-V, N)).xyz;;\n finalColor.rgb = finalColor.rgb + envTex * g * fresnelTerm;\n #endif\n\n if(lineWidth > 0.)\n {\n finalColor.rgb = finalColor.rgb * mix(lineColor, vec3(1.0), edgeFactor(lineWidth));\n }\n\n #ifdef GAMMA_ENCODE\n finalColor.rgb = pow(finalColor.rgb, vec3(1 / 2.2));\n #endif\n gl_FragColor = finalColor;\n}\n\n@end\n\n\n@export buildin.physical.vertex\n\n@import buildin.standard.vertex\n\n@end\n\n@export buildin.physical.fragment\n\n@import buildin.standard.fragment\n\n@end"}),d("qtek/shader/source/wireframe.essl",[],function(){return"@export buildin.wireframe.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world : WORLD;\n\nattribute vec3 position : POSITION;\nattribute vec3 barycentric;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec3 v_Barycentric;\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n #ifdef SKINNING\n\n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0 );\n\n v_Barycentric = barycentric;\n}\n\n@end\n\n\n@export buildin.wireframe.fragment\n\nuniform vec3 color : [0.0, 0.0, 0.0];\n\nuniform float alpha : 1.0;\nuniform float lineWidth : 1.0;\n\nvarying vec3 v_Barycentric;\n\n#extension GL_OES_standard_derivatives : enable\n\n@import buildin.util.edge_factor\n\nvoid main()\n{\n\n gl_FragColor.rgb = color;\n gl_FragColor.a = ( 1.0-edgeFactor(lineWidth) ) * alpha;\n}\n\n@end"}),d("qtek/shader/source/skybox.essl",[],function(){return"@export buildin.skybox.vertex\n\nuniform mat4 world : WORLD;\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\nvarying vec3 v_WorldPosition;\n\nvoid main()\n{\n v_WorldPosition = (world * vec4(position, 1.0)).xyz;\n gl_Position = worldViewProjection * vec4(position, 1.0);\n}\n\n@end\n\n@export buildin.skybox.fragment\n\nuniform mat4 viewInverse : VIEWINVERSE;\nuniform samplerCube environmentMap;\n\nvarying vec3 v_WorldPosition;\n\nvoid main()\n{\n vec3 eyePos = viewInverse[3].xyz;\n vec3 viewDirection = normalize(v_WorldPosition - eyePos);\n\n vec3 tex = textureCube(environmentMap, viewDirection).xyz;\n\n #ifdef SRGB_DECODE\n tex.rgb = pow(tex.rgb, vec3(2.2));\n #endif\n \n gl_FragColor = vec4(tex, 1.0);\n}\n@end"}),d("qtek/shader/source/shadowmap.essl",[],function(){return"\n@export buildin.sm.depth.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\n#ifdef SHADOW_TRANSPARENT \nattribute vec2 texcoord : TEXCOORD_0;\n#endif\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec4 v_ViewPosition;\n\n#ifdef SHADOW_TRANSPARENT\nvarying vec2 v_Texcoord;\n#endif\n\nvoid main(){\n \n vec3 skinnedPosition = position;\n \n #ifdef SKINNING\n\n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n\n v_ViewPosition = worldViewProjection * vec4(skinnedPosition, 1.0);\n gl_Position = v_ViewPosition;\n\n #ifdef SHADOW_TRANSPARENT\n v_Texcoord = texcoord;\n #endif\n}\n@end\n\n@export buildin.sm.depth.fragment\n\nvarying vec4 v_ViewPosition;\n\n#ifdef SHADOW_TRANSPARENT\nvarying vec2 v_Texcoord;\n#endif\n\nuniform float bias : 0.001;\nuniform float slopeScale : 1.0;\n\n#ifdef SHADOW_TRANSPARENT\nuniform sampler2D transparentMap;\n#endif\n\n#extension GL_OES_standard_derivatives : enable\n\n@import buildin.util.encode_float\n\nvoid main(){\n // Whats the difference between gl_FragCoord.z and this v_ViewPosition\n // gl_FragCoord consider the polygon offset ?\n float depth = v_ViewPosition.z / v_ViewPosition.w;\n // float depth = gl_FragCoord.z / gl_FragCoord.w;\n\n #ifdef USE_VSM\n depth = depth * 0.5 + 0.5;\n float moment1 = depth;\n float moment2 = depth * depth;\n\n // Adjusting moments using partial derivative\n float dx = dFdx(depth);\n float dy = dFdy(depth);\n moment2 += 0.25*(dx*dx+dy*dy);\n\n gl_FragColor = vec4(moment1, moment2, 0.0, 1.0);\n #else\n // Add slope scaled bias using partial derivative\n float dx = dFdx(depth);\n float dy = dFdy(depth);\n depth += sqrt(dx*dx + dy*dy) * slopeScale + bias;\n\n #ifdef SHADOW_TRANSPARENT\n if (texture2D(transparentMap, v_Texcoord).a <= 0.1) {\n // Hi-Z\n gl_FragColor = encodeFloat(0.9999);\n return;\n }\n #endif\n\n gl_FragColor = encodeFloat(depth * 0.5 + 0.5);\n #endif\n}\n@end\n\n@export buildin.sm.debug_depth\n\nuniform sampler2D depthMap;\nvarying vec2 v_Texcoord;\n\n@import buildin.util.decode_float\n\nvoid main() {\n vec4 tex = texture2D(depthMap, v_Texcoord);\n #ifdef USE_VSM\n gl_FragColor = vec4(tex.rgb, 1.0);\n #else\n float depth = decodeFloat(tex);\n gl_FragColor = vec4(depth, depth, depth, 1.0);\n #endif\n}\n\n@end\n\n\n@export buildin.sm.distance.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world : WORLD;\n\nattribute vec3 position : POSITION;\n\n#ifdef SKINNING\nattribute vec3 boneWeight;\nattribute vec4 boneIndex;\n\nuniform mat4 skinMatrix[JOINT_NUMBER] : SKIN_MATRIX;\n#endif\n\nvarying vec3 v_WorldPosition;\n\nvoid main (){\n\n vec3 skinnedPosition = position;\n #ifdef SKINNING\n @import buildin.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n #endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition , 1.0);\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n}\n\n@end\n\n@export buildin.sm.distance.fragment\n\nuniform vec3 lightPosition;\nuniform float range : 100;\n\nvarying vec3 v_WorldPosition;\n\n@import buildin.util.encode_float\n\nvoid main(){\n float dist = distance(lightPosition, v_WorldPosition);\n #ifdef USE_VSM\n gl_FragColor = vec4(dist, dist * dist, 0.0, 0.0);\n #else\n dist = dist / range;\n gl_FragColor = encodeFloat(dist);\n #endif\n}\n@end\n\n@export buildin.plugin.compute_shadow_map\n\n#if defined(SPOT_LIGHT_SHADOWMAP_NUMBER) || defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER) || defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n\n#ifdef SPOT_LIGHT_SHADOWMAP_NUMBER\nuniform sampler2D spotLightShadowMaps[SPOT_LIGHT_SHADOWMAP_NUMBER];\nuniform mat4 spotLightMatrices[SPOT_LIGHT_SHADOWMAP_NUMBER];\n#endif\n\n#ifdef DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER\n#if defined(SHADOW_CASCADE)\nuniform sampler2D directionalLightShadowMaps[SHADOW_CASCADE];\nuniform mat4 directionalLightMatrices[SHADOW_CASCADE];\nuniform float shadowCascadeClipsNear[SHADOW_CASCADE];\nuniform float shadowCascadeClipsFar[SHADOW_CASCADE];\n#else\nuniform sampler2D directionalLightShadowMaps[DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER];\nuniform mat4 directionalLightMatrices[DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER];\n#endif\n#endif\n\n#ifdef POINT_LIGHT_SHADOWMAP_NUMBER\nuniform samplerCube pointLightShadowMaps[POINT_LIGHT_SHADOWMAP_NUMBER];\nuniform float pointLightRanges[POINT_LIGHT_SHADOWMAP_NUMBER];\n#endif\n\nuniform bool shadowEnabled : true;\n\n@import buildin.util.decode_float\n\n#if defined(DIRECTIONAL_LIGHT_NUMBER) || defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n\nfloat tapShadowMap(sampler2D map, vec2 uv, float z){\n vec4 tex = texture2D(map, uv);\n return decodeFloat(tex) * 2.0 - 1.0 < z ? 0.0 : 1.0;\n}\n\nfloat pcf(sampler2D map, vec2 uv, float z){\n\n float shadowContrib = tapShadowMap(map, uv, z);\n float offset = 1.0 / 2048.0;\n shadowContrib += tapShadowMap(map, uv+vec2(offset, 0.0), z);\n shadowContrib += tapShadowMap(map, uv+vec2(offset, offset), z);\n shadowContrib += tapShadowMap(map, uv+vec2(-offset, offset), z);\n shadowContrib += tapShadowMap(map, uv+vec2(0.0, offset), z);\n shadowContrib += tapShadowMap(map, uv+vec2(-offset, 0.0), z);\n shadowContrib += tapShadowMap(map, uv+vec2(-offset, -offset), z);\n shadowContrib += tapShadowMap(map, uv+vec2(offset, -offset), z);\n shadowContrib += tapShadowMap(map, uv+vec2(0.0, -offset), z);\n\n return shadowContrib / 9.0;\n}\nfloat chebyshevUpperBound(vec2 moments, float z){\n float p = 0.0;\n z = z * 0.5 + 0.5;\n if (z <= moments.x) {\n p = 1.0;\n }\n float variance = moments.y - moments.x * moments.x;\n // http://fabiensanglard.net/shadowmappingVSM/\n variance = max(variance, 0.0000001);\n // Compute probabilistic upper bound. \n float mD = moments.x - z;\n float pMax = variance / (variance + mD * mD);\n // Now reduce light-bleeding by removing the [0, x] tail and linearly rescaling (x, 1]\n // TODO : bleedBias parameter ?\n pMax = clamp((pMax-0.4)/(1.0-0.4), 0.0, 1.0);\n return max(p, pMax);\n}\nfloat computeShadowContrib(sampler2D map, mat4 lightVPM, vec3 position){\n \n vec4 posInLightSpace = lightVPM * vec4(v_WorldPosition, 1.0);\n posInLightSpace.xyz /= posInLightSpace.w;\n float z = posInLightSpace.z;\n // In frustum\n if(all(greaterThan(posInLightSpace.xyz, vec3(-0.99, -0.99, -1.0))) &&\n all(lessThan(posInLightSpace.xyz, vec3(0.99, 0.99, 1.0)))){\n // To texture uv\n vec2 uv = (posInLightSpace.xy+1.0) / 2.0;\n\n #ifdef USE_VSM\n vec2 moments = texture2D(map, uv).xy;\n return chebyshevUpperBound(moments, z);\n #else\n return pcf(map, uv, z);\n #endif\n }\n return 1.0;\n}\n\n#endif\n\n#ifdef POINT_LIGHT_SHADOWMAP_NUMBER\n\nfloat computeShadowOfCube(samplerCube map, vec3 direction, float range){\n vec4 shadowTex = textureCube(map, direction);\n float dist = length(direction);\n\n #ifdef USE_VSM\n vec2 moments = shadowTex.xy;\n float variance = moments.y - moments.x * moments.x;\n float mD = moments.x - dist;\n float p = variance / (variance + mD * mD);\n if(moments.x + 0.001 < dist){\n return clamp(p, 0.0, 1.0);\n }else{\n return 1.0;\n }\n #else\n if((decodeFloat(shadowTex) + 0.0002) * range < dist){\n return 0.0;\n }else{\n return 1.0;\n }\n #endif\n}\n#endif\n\n#if defined(SPOT_LIGHT_SHADOWMAP_NUMBER)\n\nvoid computeShadowOfSpotLights(vec3 position, inout float shadowContribs[SPOT_LIGHT_NUMBER] ){\n for(int i = 0; i < SPOT_LIGHT_SHADOWMAP_NUMBER; i++){\n float shadowContrib = computeShadowContrib(spotLightShadowMaps[i], spotLightMatrices[i], position);\n shadowContribs[i] = shadowContrib;\n }\n // set default fallof of rest lights\n for(int i = SPOT_LIGHT_SHADOWMAP_NUMBER; i < SPOT_LIGHT_NUMBER; i++){\n shadowContribs[i] = 1.0;\n }\n}\n\n#endif\n\n\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER)\n\n#ifdef SHADOW_CASCADE\n\nvoid computeShadowOfDirectionalLights(vec3 position, inout float shadowContribs[DIRECTIONAL_LIGHT_NUMBER]){\n // http://www.opengl.org/wiki/Compute_eye_space_from_window_space\n float depth = (2.0 * gl_FragCoord.z - gl_DepthRange.near - gl_DepthRange.far)\n / (gl_DepthRange.far - gl_DepthRange.near);\n\n // Pixels not in light box are lighted\n // TODO\n shadowContribs[0] = 1.0;\n\n for (int i = 0; i < SHADOW_CASCADE; i++) {\n if (\n depth >= shadowCascadeClipsNear[i] &&\n depth <= shadowCascadeClipsFar[i]\n ) {\n float shadowContrib = computeShadowContrib(directionalLightShadowMaps[i], directionalLightMatrices[i], position);\n // TODO Will get a sampler needs to be be uniform error in native gl\n shadowContribs[0] = shadowContrib;\n }\n }\n // set default fallof of rest lights\n for(int i = DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER; i < DIRECTIONAL_LIGHT_NUMBER; i++){\n shadowContribs[i] = 1.0;\n }\n}\n\n#else\n\nvoid computeShadowOfDirectionalLights(vec3 position, inout float shadowContribs[DIRECTIONAL_LIGHT_NUMBER]){\n for(int i = 0; i < DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER; i++){\n float shadowContrib = computeShadowContrib(directionalLightShadowMaps[i], directionalLightMatrices[i], position);\n shadowContribs[i] = shadowContrib;\n }\n // set default fallof of rest lights\n for(int i = DIRECTIONAL_LIGHT_SHADOWMAP_NUMBER; i < DIRECTIONAL_LIGHT_NUMBER; i++){\n shadowContribs[i] = 1.0;\n }\n}\n#endif\n\n#endif\n\n\n#if defined(POINT_LIGHT_SHADOWMAP_NUMBER)\n\nvoid computeShadowOfPointLights(vec3 position, inout float shadowContribs[POINT_LIGHT_NUMBER] ){\n for(int i = 0; i < POINT_LIGHT_SHADOWMAP_NUMBER; i++){\n vec3 lightPosition = pointLightPosition[i];\n vec3 direction = position - lightPosition;\n shadowContribs[i] = computeShadowOfCube(pointLightShadowMaps[i], direction, pointLightRanges[i]);\n }\n for(int i = POINT_LIGHT_SHADOWMAP_NUMBER; i < POINT_LIGHT_NUMBER; i++){\n shadowContribs[i] = 1.0;\n }\n}\n\n#endif\n\n#endif\n\n@end" -}),d("qtek/shader/source/compositor/coloradjust.essl",[],function(){return'@export buildin.compositor.coloradjust\n\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float brightness : 0.0;\nuniform float contrast : 1.0;\nuniform float exposure : 0.0;\nuniform float gamma : 1.0;\nuniform float saturation : 1.0;\n\n// Values from "Graphics Shaders: Theory and Practice" by Bailey and Cunningham\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord);\n\n // brightness\n vec3 color = clamp(tex.rgb + vec3(brightness), 0.0, 1.0);\n // contrast\n color = clamp( (color-vec3(0.5))*contrast+vec3(0.5), 0.0, 1.0);\n // exposure\n color = clamp( color * pow(2.0, exposure), 0.0, 1.0);\n // gamma\n color = clamp( pow(color, vec3(gamma)), 0.0, 1.0);\n // saturation\n float luminance = dot( color, w );\n color = mix(vec3(luminance), color, saturation);\n \n gl_FragColor = vec4(color, tex.a);\n}\n\n@end\n\n// Seperate shader for float texture\n@export buildin.compositor.brightness\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float brightness : 0.0;\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord);\n vec3 color = tex.rgb + vec3(brightness);\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export buildin.compositor.contrast\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float contrast : 1.0;\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord);\n vec3 color = (tex.rgb-vec3(0.5))*contrast+vec3(0.5);\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export buildin.compositor.exposure\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float exposure : 0.0;\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n vec3 color = tex.rgb * pow(2.0, exposure);\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export buildin.compositor.gamma\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float gamma : 1.0;\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n vec3 color = pow(tex.rgb, vec3(gamma));\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export buildin.compositor.saturation\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float saturation : 1.0;\n\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n vec3 color = tex.rgb;\n float luminance = dot(color, w);\n color = mix(vec3(luminance), color, saturation);\n gl_FragColor = vec4(color, tex.a);\n}\n@end'}),d("qtek/shader/source/compositor/blur.essl",[],function(){return"@export buildin.compositor.gaussian_blur_h\n\nuniform sampler2D texture; // the texture with the scene you want to blur\nvarying vec2 v_Texcoord;\n \nuniform float blurSize : 2.0; \nuniform float textureWidth : 512.0;\n\nvoid main(void)\n{\n vec4 sum = vec4(0.0);\n float blurOffset = blurSize / textureWidth;\n // blur in y (vertical)\n // take nine samples, with the distance blurSize between them\n sum += texture2D(texture, vec2(max(v_Texcoord.x - 4.0*blurOffset, 0.0), v_Texcoord.y)) * 0.05;\n sum += texture2D(texture, vec2(max(v_Texcoord.x - 3.0*blurOffset, 0.0), v_Texcoord.y)) * 0.09;\n sum += texture2D(texture, vec2(max(v_Texcoord.x - 2.0*blurOffset, 0.0), v_Texcoord.y)) * 0.12;\n sum += texture2D(texture, vec2(max(v_Texcoord.x - blurOffset, 0.0), v_Texcoord.y)) * 0.15;\n sum += texture2D(texture, vec2(v_Texcoord.x, v_Texcoord.y)) * 0.18;\n sum += texture2D(texture, vec2(min(v_Texcoord.x + blurOffset, 1.0), v_Texcoord.y)) * 0.15;\n sum += texture2D(texture, vec2(min(v_Texcoord.x + 2.0*blurOffset, 1.0), v_Texcoord.y)) * 0.12;\n sum += texture2D(texture, vec2(min(v_Texcoord.x + 3.0*blurOffset, 1.0), v_Texcoord.y)) * 0.09;\n sum += texture2D(texture, vec2(min(v_Texcoord.x + 4.0*blurOffset, 1.0), v_Texcoord.y)) * 0.05;\n \n gl_FragColor = sum;\n}\n\n@end\n\n@export buildin.compositor.gaussian_blur_v\n\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\n \nuniform float blurSize : 2.0;\nuniform float textureHeight : 512.0;\n \nvoid main(void)\n{\n vec4 sum = vec4(0.0);\n float blurOffset = blurSize / textureHeight;\n // blur in y (vertical)\n // take nine samples, with the distance blurSize between them\n sum += texture2D(texture, vec2(v_Texcoord.x, max(v_Texcoord.y - 4.0*blurOffset, 0.0))) * 0.05;\n sum += texture2D(texture, vec2(v_Texcoord.x, max(v_Texcoord.y - 3.0*blurOffset, 0.0))) * 0.09;\n sum += texture2D(texture, vec2(v_Texcoord.x, max(v_Texcoord.y - 2.0*blurOffset, 0.0))) * 0.12;\n sum += texture2D(texture, vec2(v_Texcoord.x, max(v_Texcoord.y - blurOffset, 0.0))) * 0.15;\n sum += texture2D(texture, vec2(v_Texcoord.x, v_Texcoord.y)) * 0.18;\n sum += texture2D(texture, vec2(v_Texcoord.x, min(v_Texcoord.y + blurOffset, 1.0))) * 0.15;\n sum += texture2D(texture, vec2(v_Texcoord.x, min(v_Texcoord.y + 2.0*blurOffset, 1.0))) * 0.12;\n sum += texture2D(texture, vec2(v_Texcoord.x, min(v_Texcoord.y + 3.0*blurOffset, 1.0))) * 0.09;\n sum += texture2D(texture, vec2(v_Texcoord.x, min(v_Texcoord.y + 4.0*blurOffset, 1.0))) * 0.05;\n \n gl_FragColor = sum;\n}\n\n@end\n\n@export buildin.compositor.box_blur\n\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 3.0;\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n\n vec4 tex = texture2D(texture, v_Texcoord);\n vec2 offset = blurSize / textureSize;\n\n tex += texture2D(texture, v_Texcoord + vec2(offset.x, 0.0) );\n tex += texture2D(texture, v_Texcoord + vec2(offset.x, offset.y) );\n tex += texture2D(texture, v_Texcoord + vec2(-offset.x, offset.y) );\n tex += texture2D(texture, v_Texcoord + vec2(0.0, offset.y) );\n tex += texture2D(texture, v_Texcoord + vec2(-offset.x, 0.0) );\n tex += texture2D(texture, v_Texcoord + vec2(-offset.x, -offset.y) );\n tex += texture2D(texture, v_Texcoord + vec2(offset.x, -offset.y) );\n tex += texture2D(texture, v_Texcoord + vec2(0.0, -offset.y) );\n\n tex /= 9.0;\n\n gl_FragColor = tex;\n}\n\n@end\n\n// http://www.slideshare.net/DICEStudio/five-rendering-ideas-from-battlefield-3-need-for-speed-the-run\n@export buildin.compositor.hexagonal_blur_mrt_1\n\n// MRT in chrome\n// https://www.khronos.org/registry/webgl/sdk/tests/conformance/extensions/webgl-draw-buffers.html\n#extension GL_EXT_draw_buffers : require\n\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 2.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n vec2 offset = blurSize / textureSize;\n\n vec4 color = vec4(0.0);\n // Top\n for(int i = 0; i < 10; i++){\n color += 1.0/10.0 * texture2D(texture, v_Texcoord + vec2(0.0, offset.y * float(i)) );\n }\n gl_FragData[0] = color;\n vec4 color2 = vec4(0.0);\n // Down left\n for(int i = 0; i < 10; i++){\n color2 += 1.0/10.0 * texture2D(texture, v_Texcoord - vec2(offset.x * float(i), offset.y * float(i)) );\n }\n gl_FragData[1] = (color + color2) / 2.0;\n}\n\n@end\n\n@export buildin.compositor.hexagonal_blur_mrt_2\n\nuniform sampler2D texture0;\nuniform sampler2D texture1;\n\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 2.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n vec2 offset = blurSize / textureSize;\n\n vec4 color1 = vec4(0.0);\n // Down left\n for(int i = 0; i < 10; i++){\n color1 += 1.0/10.0 * texture2D(texture0, v_Texcoord - vec2(offset.x * float(i), offset.y * float(i)) );\n }\n vec4 color2 = vec4(0.0);\n // Down right\n for(int i = 0; i < 10; i++){\n color2 += 1.0/10.0 * texture2D(texture1, v_Texcoord + vec2(offset.x * float(i), -offset.y * float(i)) );\n }\n\n gl_FragColor = (color1 + color2) / 2.0;\n}\n\n@end\n\n\n@export buildin.compositor.hexagonal_blur_1\n\n#define KERNEL_SIZE 10\n\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 1.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n vec2 offset = blurSize / textureSize;\n\n vec4 color = vec4(0.0);\n float fKernelSize = float(KERNEL_SIZE);\n // Top\n for(int i = 0; i < KERNEL_SIZE; i++){\n color += 1.0 / fKernelSize * texture2D(texture, v_Texcoord + vec2(0.0, offset.y * float(i)) );\n }\n gl_FragColor = color;\n}\n\n@end\n\n@export buildin.compositor.hexagonal_blur_2\n\n#define KERNEL_SIZE 10\n\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 1.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n vec2 offset = blurSize / textureSize;\n offset.y /= 2.0;\n\n vec4 color = vec4(0.0);\n float fKernelSize = float(KERNEL_SIZE);\n // Down left\n for(int i = 0; i < KERNEL_SIZE; i++){\n color += 1.0/fKernelSize * texture2D(texture, v_Texcoord - vec2(offset.x * float(i), offset.y * float(i)) );\n }\n gl_FragColor = color;\n}\n@end\n\n@export buildin.compositor.hexagonal_blur_3\n\n#define KERNEL_SIZE 10\n\nuniform sampler2D texture1;\nuniform sampler2D texture2;\n\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 1.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\nvoid main(void){\n vec2 offset = blurSize / textureSize;\n offset.y /= 2.0;\n\n vec4 color1 = vec4(0.0);\n float fKernelSize = float(KERNEL_SIZE);\n // Down left\n for(int i = 0; i < KERNEL_SIZE; i++){\n color1 += 1.0/fKernelSize * texture2D(texture1, v_Texcoord - vec2(offset.x * float(i), offset.y * float(i)) );\n }\n vec4 color2 = vec4(0.0);\n // Down right\n for(int i = 0; i < KERNEL_SIZE; i++){\n color2 += 1.0/fKernelSize * texture2D(texture1, v_Texcoord + vec2(offset.x * float(i), -offset.y * float(i)) );\n }\n\n vec4 color3 = vec4(0.0);\n // Down right\n for(int i = 0; i < KERNEL_SIZE; i++){\n color3 += 1.0/fKernelSize * texture2D(texture2, v_Texcoord + vec2(offset.x * float(i), -offset.y * float(i)) );\n }\n\n gl_FragColor = (color1 + color2 + color3) / 3.0;\n}\n\n@end"}),d("qtek/shader/source/compositor/lum.essl",[],function(){return"\n@export buildin.compositor.lum\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\n\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord );\n float luminance = dot(tex.rgb, w);\n\n gl_FragColor = vec4(vec3(luminance), 1.0);\n}\n\n@end"}),d("qtek/shader/source/compositor/lut.essl",[],function(){return"\n// https://github.com/BradLarson/GPUImage?source=c\n@export buildin.compositor.lut\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\nuniform sampler2D lookup;\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n\n float blueColor = tex.b * 63.0;\n \n vec2 quad1;\n quad1.y = floor(floor(blueColor) / 8.0);\n quad1.x = floor(blueColor) - (quad1.y * 8.0);\n \n vec2 quad2;\n quad2.y = floor(ceil(blueColor) / 8.0);\n quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n \n vec2 texPos1;\n texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.r);\n texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.g);\n \n vec2 texPos2;\n texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.r);\n texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.g);\n \n vec4 newColor1 = texture2D(lookup, texPos1);\n vec4 newColor2 = texture2D(lookup, texPos2);\n \n vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n gl_FragColor = vec4(newColor.rgb, tex.w);\n}\n\n@end"}),d("qtek/shader/source/compositor/output.essl",[],function(){return"@export buildin.compositor.output\n\n#define OUTPUT_ALPHA;\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n\n gl_FragColor.rgb = tex.rgb;\n\n #ifdef OUTPUT_ALPHA\n gl_FragColor.a = tex.a;\n #else\n gl_FragColor.a = 1.0;\n #endif\n\n}\n\n@end"}),d("qtek/shader/source/compositor/hdr.essl",[],function(){return"// HDR Pipeline\n@export buildin.compositor.hdr.bright\n\nuniform sampler2D texture;\nuniform float threshold : 1;\nuniform float scale : 1.0;\n\nvarying vec2 v_Texcoord;\n\nconst vec3 lumWeight = vec3(0.2125, 0.7154, 0.0721);\n\n@import buildin.util.rgbm_decode\n@import buildin.util.rgbm_encode\n\nvoid main()\n{\n #ifdef TEXTURE_ENABLED\n #ifdef RGBM_DECODE\n vec3 tex = RGBMDecode(texture2D(texture, v_Texcoord));\n #else\n vec3 tex = texture2D(texture, v_Texcoord).rgb;\n #endif\n #else\n vec3 tex = vec3(0.0);\n #endif\n\n float lum = dot(tex, lumWeight);\n if (lum > threshold)\n {\n gl_FragColor.rgb = tex * scale;\n }\n else\n {\n gl_FragColor.rgb = vec3(0.0);\n }\n gl_FragColor.a = 1.0;\n\n #ifdef RGBM_ENCODE\n gl_FragColor.rgba = RGBMEncode(gl_FragColor.rgb);\n #endif\n}\n@end\n\n@export buildin.compositor.hdr.log_lum\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\n\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n float luminance = dot(tex.rgb, w);\n luminance = log(luminance + 0.001);\n\n gl_FragColor = vec4(vec3(luminance), 1.0);\n}\n\n@end\n\n@export buildin.compositor.hdr.lum_adaption\nvarying vec2 v_Texcoord;\n\nuniform sampler2D adaptedLum;\nuniform sampler2D currentLum;\n\nuniform float frameTime : 0.02;\n\nvoid main()\n{\n float fAdaptedLum = texture2D(adaptedLum, vec2(0.5, 0.5)).r;\n float fCurrentLum = exp(texture2D(currentLum, vec2(0.5, 0.5)).r);\n\n fAdaptedLum += (fCurrentLum - fAdaptedLum) * (1.0 - pow(0.98, 30.0 * frameTime));\n gl_FragColor.rgb = vec3(fAdaptedLum);\n gl_FragColor.a = 1.0;\n}\n@end\n\n// Tone mapping with gamma correction\n// http://filmicgames.com/archives/75\n@export buildin.compositor.hdr.tonemapping\n\nuniform sampler2D texture;\nuniform sampler2D bloom;\nuniform sampler2D lensflare;\nuniform sampler2D lum;\n\nuniform float exposure : 1.0;\n\nvarying vec2 v_Texcoord;\n\nconst float A = 0.22; // Shoulder Strength\nconst float B = 0.30; // Linear Strength\nconst float C = 0.10; // Linear Angle\nconst float D = 0.20; // Toe Strength\nconst float E = 0.01; // Toe Numerator\nconst float F = 0.30; // Toe Denominator\nconst vec3 whiteScale = vec3(11.2);\n\nvec3 uncharted2ToneMap(vec3 x)\n{\n return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;\n}\n\nvec3 filmicToneMap(vec3 color)\n{\n vec3 x = max(vec3(0.0), color - 0.004);\n return (x*(6.2*x+0.5))/(x*(6.2*x+1.7)+0.06);\n}\n\nfloat eyeAdaption(float fLum)\n{\n return mix(0.2, fLum, 0.5);\n}\n\nvoid main()\n{\n vec3 tex = vec3(0.0);\n float a = 1.0;\n #ifdef TEXTURE_ENABLED\n vec4 res = texture2D(texture, v_Texcoord);\n a = res.a;\n tex = res.rgb;\n #endif\n\n #ifdef BLOOM_ENABLED\n tex += texture2D(bloom, v_Texcoord).rgb * 0.25;\n #endif\n\n #ifdef LENSFLARE_ENABLED\n tex += texture2D(lensflare, v_Texcoord).rgb;\n #endif\n\n // Adjust exposure\n // From KlayGE\n #ifdef LUM_ENABLED\n float fLum = texture2D(lum, vec2(0.5, 0.5)).r;\n float adaptedLumDest = 3.0 / (max(0.1, 1.0 + 10.0*eyeAdaption(fLum)));\n float exposureBias = adaptedLumDest * exposure;\n #else\n float exposureBias = exposure;\n #endif\n tex *= exposureBias;\n\n // Do tone mapping\n vec3 color = uncharted2ToneMap(tex) / uncharted2ToneMap(whiteScale);\n color = pow(color, vec3(1.0/2.2));\n // vec3 color = filmicToneMap(tex);\n\n #ifdef RGBM_ENCODE\n gl_FragColor.rgba = RGBMEncode(color);\n #else\n gl_FragColor = vec4(color, a);\n #endif\n}\n\n@end"}),d("qtek/shader/source/compositor/lensflare.essl",[],function(){return"// john-chapman-graphics.blogspot.co.uk/2013/02/pseudo-lens-flare.html\n@export buildin.compositor.lensflare\n\n#define SAMPLE_NUMBER 8\n\nuniform sampler2D texture;\nuniform sampler2D lensColor;\n\nuniform vec2 textureSize : [512, 512];\n\nuniform float dispersal : 0.3;\nuniform float haloWidth : 0.4;\nuniform float distortion : 1.0;\n\nvarying vec2 v_Texcoord;\n\nvec4 textureDistorted(\n in vec2 texcoord,\n in vec2 direction,\n in vec3 distortion\n) {\n return vec4(\n texture2D(texture, texcoord + direction * distortion.r).r,\n texture2D(texture, texcoord + direction * distortion.g).g,\n texture2D(texture, texcoord + direction * distortion.b).b,\n 1.0\n );\n}\n\nvoid main()\n{\n vec2 texcoord = -v_Texcoord + vec2(1.0); // Flip texcoords\n vec2 textureOffset = 1.0 / textureSize;\n\n vec2 ghostVec = (vec2(0.5) - texcoord) * dispersal;\n vec2 haloVec = normalize(ghostVec) * haloWidth;\n\n vec3 distortion = vec3(-textureOffset.x * distortion, 0.0, textureOffset.x * distortion);\n //Sample ghost\n vec4 result = vec4(0.0);\n for (int i = 0; i < SAMPLE_NUMBER; i++)\n {\n vec2 offset = fract(texcoord + ghostVec * float(i));\n\n float weight = length(vec2(0.5) - offset) / length(vec2(0.5));\n weight = pow(1.0 - weight, 10.0);\n\n result += textureDistorted(offset, normalize(ghostVec), distortion) * weight;\n }\n\n result *= texture2D(lensColor, vec2(length(vec2(0.5) - texcoord)) / length(vec2(0.5)));\n //Sample halo\n float weight = length(vec2(0.5) - fract(texcoord + haloVec)) / length(vec2(0.5));\n weight = pow(1.0 - weight, 10.0);\n vec2 offset = fract(texcoord + haloVec);\n result += textureDistorted(offset, normalize(ghostVec), distortion) * weight;\n\n gl_FragColor = result;\n}\n@end"}),d("qtek/shader/source/compositor/blend.essl",[],function(){return"@export buildin.compositor.blend\n// Blend at most 4 textures\n#ifdef TEXTURE1_ENABLED\nuniform sampler2D texture1;\nuniform float weight1 : 1.0;\n#endif\n#ifdef TEXTURE2_ENABLED\nuniform sampler2D texture2;\nuniform float weight2 : 1.0;\n#endif\n#ifdef TEXTURE3_ENABLED\nuniform sampler2D texture3;\nuniform float weight3 : 1.0;\n#endif\n#ifdef TEXTURE4_ENABLED\nuniform sampler2D texture4;\nuniform float weight4 : 1.0;\n#endif\n\nvarying vec2 v_Texcoord;\n\nvoid main()\n{\n vec3 tex = vec3(0.0);\n #ifdef TEXTURE1_ENABLED\n tex += texture2D(texture1, v_Texcoord).rgb * weight1;\n #endif\n #ifdef TEXTURE2_ENABLED\n tex += texture2D(texture2, v_Texcoord).rgb * weight2;\n #endif\n #ifdef TEXTURE3_ENABLED\n tex += texture2D(texture3, v_Texcoord).rgb * weight3;\n #endif\n #ifdef TEXTURE4_ENABLED\n tex += texture2D(texture4, v_Texcoord).rgb * weight4;\n #endif\n\n gl_FragColor = vec4(tex, 1.0);\n}\n@end"}),d("qtek/shader/source/compositor/fxaa.essl",[],function(){return"// https://github.com/mitsuhiko/webgl-meincraft/blob/master/assets/shaders/fxaa.glsl\n@export buildin.compositor.fxaa\n\nuniform sampler2D texture;\nuniform vec2 viewportSize : [512, 512];\n\nvarying vec2 v_Texcoord;\n\n#define FXAA_REDUCE_MIN (1.0/128.0)\n#define FXAA_REDUCE_MUL (1.0/8.0)\n#define FXAA_SPAN_MAX 8.0\n\nvoid main()\n{\n vec2 resolution = 1.0 / viewportSize;\n vec3 rgbNW = texture2D( texture, ( gl_FragCoord.xy + vec2( -1.0, -1.0 ) ) * resolution ).xyz;\n vec3 rgbNE = texture2D( texture, ( gl_FragCoord.xy + vec2( 1.0, -1.0 ) ) * resolution ).xyz;\n vec3 rgbSW = texture2D( texture, ( gl_FragCoord.xy + vec2( -1.0, 1.0 ) ) * resolution ).xyz;\n vec3 rgbSE = texture2D( texture, ( gl_FragCoord.xy + vec2( 1.0, 1.0 ) ) * resolution ).xyz;\n vec4 rgbaM = texture2D( texture, gl_FragCoord.xy * resolution );\n vec3 rgbM = rgbaM.xyz;\n float opacity = rgbaM.w;\n\n vec3 luma = vec3( 0.299, 0.587, 0.114 );\n\n float lumaNW = dot( rgbNW, luma );\n float lumaNE = dot( rgbNE, luma );\n float lumaSW = dot( rgbSW, luma );\n float lumaSE = dot( rgbSE, luma );\n float lumaM = dot( rgbM, luma );\n float lumaMin = min( lumaM, min( min( lumaNW, lumaNE ), min( lumaSW, lumaSE ) ) );\n float lumaMax = max( lumaM, max( max( lumaNW, lumaNE) , max( lumaSW, lumaSE ) ) );\n\n vec2 dir;\n dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\n float dirReduce = max( ( lumaNW + lumaNE + lumaSW + lumaSE ) * ( 0.25 * FXAA_REDUCE_MUL ), FXAA_REDUCE_MIN );\n\n float rcpDirMin = 1.0 / ( min( abs( dir.x ), abs( dir.y ) ) + dirReduce );\n dir = min( vec2( FXAA_SPAN_MAX, FXAA_SPAN_MAX),\n max( vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),\n dir * rcpDirMin)) * resolution;\n\n vec3 rgbA = texture2D( texture, gl_FragCoord.xy * resolution + dir * ( 1.0 / 3.0 - 0.5 ) ).xyz;\n rgbA += texture2D( texture, gl_FragCoord.xy * resolution + dir * ( 2.0 / 3.0 - 0.5 ) ).xyz;\n rgbA *= 0.5;\n\n vec3 rgbB = texture2D( texture, gl_FragCoord.xy * resolution + dir * -0.5 ).xyz;\n rgbB += texture2D( texture, gl_FragCoord.xy * resolution + dir * 0.5 ).xyz;\n rgbB *= 0.25;\n rgbB += rgbA * 0.5;\n\n float lumaB = dot( rgbB, luma );\n\n if ( ( lumaB < lumaMin ) || ( lumaB > lumaMax ) )\n {\n\n gl_FragColor = vec4( rgbA, opacity );\n\n } else {\n\n gl_FragColor = vec4( rgbB, opacity );\n\n }\n}\n\n@end"}),d("qtek/shader/buildin",["require","./library","../Shader","./source/basic.essl","./source/lambert.essl","./source/phong.essl","./source/standard.essl","./source/wireframe.essl","./source/skybox.essl","./source/util.essl","./source/prez.essl","./source/shadowmap.essl","./source/compositor/coloradjust.essl","./source/compositor/blur.essl","./source/compositor/lum.essl","./source/compositor/lut.essl","./source/compositor/output.essl","./source/compositor/hdr.essl","./source/compositor/lensflare.essl","./source/compositor/blend.essl","./source/compositor/fxaa.essl"],function(a){var b=a("./library"),c=a("../Shader");c["import"](a("./source/basic.essl")),c["import"](a("./source/lambert.essl")),c["import"](a("./source/phong.essl")),c["import"](a("./source/standard.essl")),c["import"](a("./source/wireframe.essl")),c["import"](a("./source/skybox.essl")),c["import"](a("./source/util.essl")),c["import"](a("./source/prez.essl")),c["import"](a("./source/shadowmap.essl")),b.template("buildin.basic",c.source("buildin.basic.vertex"),c.source("buildin.basic.fragment")),b.template("buildin.lambert",c.source("buildin.lambert.vertex"),c.source("buildin.lambert.fragment")),b.template("buildin.phong",c.source("buildin.phong.vertex"),c.source("buildin.phong.fragment")),b.template("buildin.wireframe",c.source("buildin.wireframe.vertex"),c.source("buildin.wireframe.fragment")),b.template("buildin.skybox",c.source("buildin.skybox.vertex"),c.source("buildin.skybox.fragment")),b.template("buildin.prez",c.source("buildin.prez.vertex"),c.source("buildin.prez.fragment")),b.template("buildin.standard",c.source("buildin.standard.vertex"),c.source("buildin.standard.fragment")),b.template("buildin.physical",c.source("buildin.physical.vertex"),c.source("buildin.physical.fragment")),c["import"](a("./source/compositor/coloradjust.essl")),c["import"](a("./source/compositor/blur.essl")),c["import"](a("./source/compositor/lum.essl")),c["import"](a("./source/compositor/lut.essl")),c["import"](a("./source/compositor/output.essl")),c["import"](a("./source/compositor/hdr.essl")),c["import"](a("./source/compositor/lensflare.essl")),c["import"](a("./source/compositor/blend.essl")),c["import"](a("./source/compositor/fxaa.essl"))}),d("qtek/util/dds",["require","../Texture","../Texture2D","../TextureCube"],function(a){function b(a){return a.charCodeAt(0)+(a.charCodeAt(1)<<8)+(a.charCodeAt(2)<<16)+(a.charCodeAt(3)<<24)}var c=a("../Texture"),d=a("../Texture2D");a("../TextureCube");var e=542327876,f=131072,g=512,h=4,i=31,j=b("DXT1"),k=b("DXT3"),l=b("DXT5"),m=0,n=1,o=2,p=3,q=4,r=7,s=20,t=21,u=28,v={parse:function(a,b){var v=new Int32Array(a,0,i);if(v[m]!==e)return null;if(!v(s)&h)return null;var w,x,y=v(t),z=v[q],A=v[p],B=v[u]&g,C=v[o]&f;switch(y){case j:w=8,x=c.COMPRESSED_RGB_S3TC_DXT1_EXT;break;case k:w=16,x=c.COMPRESSED_RGBA_S3TC_DXT3_EXT;break;case l:w=16,x=c.COMPRESSED_RGBA_S3TC_DXT5_EXT;break;default:return null}var D=v[n]+4,E=B?6:1,F=1;C&&(F=Math.max(1,v[r]));for(var G=[],H=0;E>H;H++){var I=z,J=A;G[H]=new d({width:I,height:J,format:x});for(var K=[],L=0;F>L;L++){var M=Math.max(4,I)/4*Math.max(4,J)/4*w,N=new Uint8Array(a,D,M);D+=M,I*=.5,J*=.5,K[L]=N}G[H].pixels=K[0],C&&(G[H].mipmaps=K)}return b?(b.width=G[0].width,b.height=G[0].height,b.format=G[0].format,b.pixels=G[0].pixels,b.mipmaps=G[0].mipmaps,void 0):G[0]}};return v}),d("qtek/util/hdr",["require","../Texture","../Texture2D"],function(a){function b(a,b,c,d){if(a[3]>0){var e=Math.pow(2,a[3]-128-8+d);b[c+0]=a[0]*e,b[c+1]=a[1]*e,b[c+2]=a[2]*e}else b[c+0]=0,b[c+1]=0,b[c+2]=0;return b[c+3]=1,b}function c(a,b,c){for(var d="",e=b;c>e;e++)d+=i(a[e]);return d}function d(a,b){b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3]}function e(a,b,c,e){for(var f=0,g=0,h=e;h>0;)if(a[g][0]=b[c++],a[g][1]=b[c++],a[g][2]=b[c++],a[g][3]=b[c++],1===a[g][0]&&1===a[g][1]&&1===a[g][2]){for(var i=a[g][3]<>>0;i>0;i--)d(a[g-1],a[g]),g++,h--;f+=8}else g++,h--,f=0;return c}function f(a,b,c,d){if(j>d|d>k)return e(a,b,c,d);var f=b[c++];if(2!=f)return e(a,b,c-1,d);if(a[0][1]=b[c++],a[0][2]=b[c++],f=b[c++],(a[0][2]<<8>>>0|f)>>>0!==d)return null;for(var f=0;4>f;f++)for(var g=0;d>g;){var h=b[c++];if(h>128){h=(127&h)>>>0;for(var i=b[c++];h--;)a[g++][f]=i}else for(;h--;)a[g++][f]=b[c++]}return c}var g=a("../Texture"),h=a("../Texture2D"),i=String.fromCharCode,j=8,k=32767,l={parseRGBE:function(a,d,e){void 0===e&&(e=0);var j=new Uint8Array(a),k=j.length;if("#?"===c(j,0,2)){for(var l=2;k>l&&("\n"!==i(j[l])||"\n"!==i(j[l+1]));l++);if(!(l>=k)){l+=2;for(var m="";k>l;l++){var n=i(j[l]);if("\n"===n)break;m+=n}var o=m.split(" "),p=parseInt(o[1]),q=parseInt(o[3]);if(q&&p){for(var r=l+1,s=[],t=0;q>t;t++){s[t]=[];for(var u=0;4>u;u++)s[t][u]=0}for(var v=new Float32Array(4*q*p),w=0,x=0;p>x;x++){var r=f(s,j,r,q);if(!r)return null;for(var t=0;q>t;t++)b(s[t],v,w,e),w+=4}return d||(d=new h),d.width=q,d.height=p,d.pixels=v,d.type=g.FLOAT,d}}}},parseRGBEFromPNG:function(){}};return l}),d("qtek/util/mesh",["require","../Geometry","../DynamicGeometry","../StaticGeometry","../Mesh","../Node","../Material","../Shader","../math/BoundingBox","../dep/glmatrix"],function(a){a("../Geometry");var b=a("../DynamicGeometry"),c=a("../StaticGeometry"),d=a("../Mesh"),e=a("../Node"),f=a("../Material");a("../Shader");var g=a("../math/BoundingBox"),h=a("../dep/glmatrix"),i=h.mat4,j=h.vec3,k=Array.prototype.slice,l={merge:function(a,e){if(a.length){var f=a[0],h=f.geometry,k=f.material,l=h instanceof c,m=l?new c:new b;m.boundingBox=new g;var n=m.faces,o=h.getEnabledAttributes();l||(o=Object.keys(o));for(var p=0;p=65535?new Uint32Array(3*u):new Uint16Array(3*u)}for(var z=0,A=0,B=h.isUseFace(),C=0;Cp;p++)H.value[K+p]=G.value[p];"position"===q?j.forEach(H.value,J,K,L,j.transformMat4,E):("normal"===q||"tangent"===q)&&j.forEach(H.value,J,K,L,j.transformMat4,s)}else for(var p=0;t>p;p++)if("position"===q){var M=j.create();j.transformMat4(M,G.value[p],E),H.value.push(M)}else if("normal"===q||"tangent"===q){var M=j.create();j.transformMat4(M,G.value[p],s),H.value.push(M)}else H.value.push(G.value[p])}if(B){var I=w.faces.length;if(l){for(var p=0;I>p;p++)m.faces[p+A]=w.faces[p]+z;A+=I}else for(var p=0;I>p;p++){var N=[],O=w.faces[p];N[0]=O[0]+z,N[1]=O[1]+z,N[2]=O[2]+z,n.push(N)}}z+=t}return new d({material:k,geometry:m})}},splitByJoints:function(a,g,h){var i=a.geometry,j=a.skeleton,l=a.material,m=l.shader,n=a.joints;if(i&&j&&n.length){if(n.lengthv;v++)t[v]=!1;for(var w=[],x=[],y=function(a){return n[a]};s>0;){for(var z=[],A=[],B=[],C=0,v=0;vD;D++)if(!t[D]){for(var E=!0,F=0,v=0;3>v;v++)for(var G=o?q[3*D+v]:q[D][v],H=0;4>H;H++){var I;I=o?u[4*G+H]:u[G][H],I>=0&&-1===A[I]&&(g>C?(A[I]=C,B[C++]=I,w[F++]=I):E=!1)}if(E)o?z.push(q.subarray(3*D,3*(D+1))):z.push(q[D]),t[D]=!0,s--;else for(var v=0;F>v;v++)A[w[v]]=-1,B.pop(),C--}x.push({faces:z,joints:B.map(y),jointReverseMap:A})}var J=new e({name:a.name}),K=i.getEnabledAttributes();o||(K=Object.keys(K)),K.splice(K.indexOf("joint"),1);for(var L=[],M=0;Mv;v++)L[v]=-1;for(var D=0;Dv;v++){var G=X[v];-1===L[G]&&(L[G]=V,V++)}if(o){for(var Y=0;Y65535?new Uint32Array(3*N.faces.length):new Uint16Array(3*N.faces.length)}var _=0;V=0;for(var v=0;W>v;v++)L[v]=-1;for(var D=0;Dv;v++){var G=X[v];if(-1===L[G]){L[G]=V;for(var Y=0;YH;H++)$.value[V*cb+H]=bb.value[G*cb+H];else $.value[V]=1===bb.size?bb.value[G]:k.call(bb.value[G])}if(o)for(var H=0;4>H;H++){var I=i.attributes.joint.value[4*G+H],db=4*V+H;T.attributes.joint.value[db]=I>=0?O[I]:-1}else for(var eb=T.attributes.joint.value[V]=[-1,-1,-1,-1],H=0;4>H;H++){var I=i.attributes.joint.value[G][H];I>=0&&(eb[H]=O[I])}V++}o?T.faces[_++]=L[G]:ab.push(L[G])}o||T.faces.push(ab)}J.add(U)}for(var fb=a.children(),v=0;vi;i++)for(var j=0;f>j;j++){var k=j%2?i%2:i%2-1;k&&h.fillRect(i*c,j*c,c,c)}var l=new b({image:g,anisotropic:8});return l},createBlank:function(a){var c=document.createElement("canvas");c.width=1,c.height=1;var d=c.getContext("2d");d.fillStyle=a,d.fillRect(0,0,1,1);var e=new b({image:c});return e}};return j}),d("qtek/qtek",["require","qtek/Camera","qtek/DynamicGeometry","qtek/FrameBuffer","qtek/Geometry","qtek/Joint","qtek/Light","qtek/Material","qtek/Mesh","qtek/Node","qtek/Renderable","qtek/Renderer","qtek/Scene","qtek/Shader","qtek/Skeleton","qtek/StaticGeometry","qtek/Texture","qtek/Texture2D","qtek/TextureCube","qtek/animation/Animation","qtek/animation/Blend1DClip","qtek/animation/Blend2DClip","qtek/animation/Clip","qtek/animation/SamplerClip","qtek/animation/SkinningClip","qtek/animation/TransformClip","qtek/animation/easing","qtek/async/Task","qtek/async/TaskGroup","qtek/camera/Orthographic","qtek/camera/Perspective","qtek/compositor/Compositor","qtek/compositor/Graph","qtek/compositor/Node","qtek/compositor/Pass","qtek/compositor/SceneNode","qtek/compositor/TextureNode","qtek/compositor/TexturePool","qtek/core/Base","qtek/core/Cache","qtek/core/Event","qtek/core/LRU","qtek/core/LinkedList","qtek/core/glenum","qtek/core/glinfo","qtek/core/mixin/derive","qtek/core/mixin/notifier","qtek/core/request","qtek/core/util","qtek/deferred/Renderer","qtek/deferred/StandardMaterial","qtek/dep/glmatrix","qtek/geometry/Cone","qtek/geometry/Cube","qtek/geometry/Cylinder","qtek/geometry/Plane","qtek/geometry/Sphere","qtek/light/Ambient","qtek/light/Directional","qtek/light/Point","qtek/light/Spot","qtek/loader/FX","qtek/loader/GLTF","qtek/loader/ThreeModel","qtek/math/BoundingBox","qtek/math/Frustum","qtek/math/Matrix2","qtek/math/Matrix2d","qtek/math/Matrix3","qtek/math/Matrix4","qtek/math/Plane","qtek/math/Quaternion","qtek/math/Ray","qtek/math/Value","qtek/math/Vector2","qtek/math/Vector3","qtek/math/Vector4","qtek/particleSystem/Emitter","qtek/particleSystem/Field","qtek/particleSystem/ForceField","qtek/particleSystem/Particle","qtek/particleSystem/ParticleRenderable","qtek/picking/PixelPicking","qtek/picking/RayPicking","qtek/plugin/FirstPersonControl","qtek/plugin/InfinitePlane","qtek/plugin/OrbitControl","qtek/plugin/Skybox","qtek/plugin/Skydome","qtek/prePass/EnvironmentMap","qtek/prePass/Reflection","qtek/prePass/ShadowMap","qtek/shader/buildin","qtek/shader/library","qtek/util/dds","qtek/util/delaunay","qtek/util/hdr","qtek/util/mesh","qtek/util/texture"],function(a){var b={Camera:a("qtek/Camera"),DynamicGeometry:a("qtek/DynamicGeometry"),FrameBuffer:a("qtek/FrameBuffer"),Geometry:a("qtek/Geometry"),Joint:a("qtek/Joint"),Light:a("qtek/Light"),Material:a("qtek/Material"),Mesh:a("qtek/Mesh"),Node:a("qtek/Node"),Renderable:a("qtek/Renderable"),Renderer:a("qtek/Renderer"),Scene:a("qtek/Scene"),Shader:a("qtek/Shader"),Skeleton:a("qtek/Skeleton"),StaticGeometry:a("qtek/StaticGeometry"),Texture:a("qtek/Texture"),Texture2D:a("qtek/Texture2D"),TextureCube:a("qtek/TextureCube"),animation:{Animation:a("qtek/animation/Animation"),Blend1DClip:a("qtek/animation/Blend1DClip"),Blend2DClip:a("qtek/animation/Blend2DClip"),Clip:a("qtek/animation/Clip"),SamplerClip:a("qtek/animation/SamplerClip"),SkinningClip:a("qtek/animation/SkinningClip"),TransformClip:a("qtek/animation/TransformClip"),easing:a("qtek/animation/easing")},async:{Task:a("qtek/async/Task"),TaskGroup:a("qtek/async/TaskGroup")},camera:{Orthographic:a("qtek/camera/Orthographic"),Perspective:a("qtek/camera/Perspective")},compositor:{Compositor:a("qtek/compositor/Compositor"),Graph:a("qtek/compositor/Graph"),Node:a("qtek/compositor/Node"),Pass:a("qtek/compositor/Pass"),SceneNode:a("qtek/compositor/SceneNode"),TextureNode:a("qtek/compositor/TextureNode"),TexturePool:a("qtek/compositor/TexturePool")},core:{Base:a("qtek/core/Base"),Cache:a("qtek/core/Cache"),Event:a("qtek/core/Event"),LRU:a("qtek/core/LRU"),LinkedList:a("qtek/core/LinkedList"),glenum:a("qtek/core/glenum"),glinfo:a("qtek/core/glinfo"),mixin:{derive:a("qtek/core/mixin/derive"),notifier:a("qtek/core/mixin/notifier")},request:a("qtek/core/request"),util:a("qtek/core/util")},deferred:{Renderer:a("qtek/deferred/Renderer"),StandardMaterial:a("qtek/deferred/StandardMaterial")},dep:{glmatrix:a("qtek/dep/glmatrix")},geometry:{Cone:a("qtek/geometry/Cone"),Cube:a("qtek/geometry/Cube"),Cylinder:a("qtek/geometry/Cylinder"),Plane:a("qtek/geometry/Plane"),Sphere:a("qtek/geometry/Sphere")},light:{Ambient:a("qtek/light/Ambient"),Directional:a("qtek/light/Directional"),Point:a("qtek/light/Point"),Spot:a("qtek/light/Spot")},loader:{FX:a("qtek/loader/FX"),GLTF:a("qtek/loader/GLTF"),ThreeModel:a("qtek/loader/ThreeModel")},math:{BoundingBox:a("qtek/math/BoundingBox"),Frustum:a("qtek/math/Frustum"),Matrix2:a("qtek/math/Matrix2"),Matrix2d:a("qtek/math/Matrix2d"),Matrix3:a("qtek/math/Matrix3"),Matrix4:a("qtek/math/Matrix4"),Plane:a("qtek/math/Plane"),Quaternion:a("qtek/math/Quaternion"),Ray:a("qtek/math/Ray"),Value:a("qtek/math/Value"),Vector2:a("qtek/math/Vector2"),Vector3:a("qtek/math/Vector3"),Vector4:a("qtek/math/Vector4")},particleSystem:{Emitter:a("qtek/particleSystem/Emitter"),Field:a("qtek/particleSystem/Field"),ForceField:a("qtek/particleSystem/ForceField"),Particle:a("qtek/particleSystem/Particle"),ParticleRenderable:a("qtek/particleSystem/ParticleRenderable")},picking:{PixelPicking:a("qtek/picking/PixelPicking"),RayPicking:a("qtek/picking/RayPicking")},plugin:{FirstPersonControl:a("qtek/plugin/FirstPersonControl"),InfinitePlane:a("qtek/plugin/InfinitePlane"),OrbitControl:a("qtek/plugin/OrbitControl"),Skybox:a("qtek/plugin/Skybox"),Skydome:a("qtek/plugin/Skydome")},prePass:{EnvironmentMap:a("qtek/prePass/EnvironmentMap"),Reflection:a("qtek/prePass/Reflection"),ShadowMap:a("qtek/prePass/ShadowMap")},shader:{buildin:a("qtek/shader/buildin"),library:a("qtek/shader/library")},util:{dds:a("qtek/util/dds"),delaunay:a("qtek/util/delaunay"),hdr:a("qtek/util/hdr"),mesh:a("qtek/util/mesh"),texture:a("qtek/util/texture")}};return b.version="0.2.1",b}),d("qtek",["qtek/qtek"],function(a){return a});var e=c("qtek");for(var f in e)a[f]=e[f]}); \ No newline at end of file +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.qtek=e():t.qtek=e()}(this,function(){return function(t){function e(r){if(n[r])return n[r].exports;var i=n[r]={exports:{},id:r,loaded:!1};return t[r].call(i.exports,i,i.exports,e),i.loaded=!0,i.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){t.exports=n(116)},function(t,e,n){!function(t){"use strict";var n={};n.exports=e,function(t){if(!e)var e=1e-6;if(!n)var n="undefined"!=typeof Float32Array?Float32Array:Array;if(!r)var r=Math.random;var i={};i.setMatrixArrayType=function(t){n=t},"undefined"!=typeof t&&(t.glMatrix=i);var a=Math.PI/180;i.toRadian=function(t){return t*a};var o={};o.create=function(){var t=new n(2);return t[0]=0,t[1]=0,t},o.clone=function(t){var e=new n(2);return e[0]=t[0],e[1]=t[1],e},o.fromValues=function(t,e){var r=new n(2);return r[0]=t,r[1]=e,r},o.copy=function(t,e){return t[0]=e[0],t[1]=e[1],t},o.set=function(t,e,n){return t[0]=e,t[1]=n,t},o.add=function(t,e,n){return t[0]=e[0]+n[0],t[1]=e[1]+n[1],t},o.subtract=function(t,e,n){return t[0]=e[0]-n[0],t[1]=e[1]-n[1],t},o.sub=o.subtract,o.multiply=function(t,e,n){return t[0]=e[0]*n[0],t[1]=e[1]*n[1],t},o.mul=o.multiply,o.divide=function(t,e,n){return t[0]=e[0]/n[0],t[1]=e[1]/n[1],t},o.div=o.divide,o.min=function(t,e,n){return t[0]=Math.min(e[0],n[0]),t[1]=Math.min(e[1],n[1]),t},o.max=function(t,e,n){return t[0]=Math.max(e[0],n[0]),t[1]=Math.max(e[1],n[1]),t},o.scale=function(t,e,n){return t[0]=e[0]*n,t[1]=e[1]*n,t},o.scaleAndAdd=function(t,e,n,r){return t[0]=e[0]+n[0]*r,t[1]=e[1]+n[1]*r,t},o.distance=function(t,e){var n=e[0]-t[0],r=e[1]-t[1];return Math.sqrt(n*n+r*r)},o.dist=o.distance,o.squaredDistance=function(t,e){var n=e[0]-t[0],r=e[1]-t[1];return n*n+r*r},o.sqrDist=o.squaredDistance,o.length=function(t){var e=t[0],n=t[1];return Math.sqrt(e*e+n*n)},o.len=o.length,o.squaredLength=function(t){var e=t[0],n=t[1];return e*e+n*n},o.sqrLen=o.squaredLength,o.negate=function(t,e){return t[0]=-e[0],t[1]=-e[1],t},o.inverse=function(t,e){return t[0]=1/e[0],t[1]=1/e[1],t},o.normalize=function(t,e){var n=e[0],r=e[1],i=n*n+r*r;return i>0&&(i=1/Math.sqrt(i),t[0]=e[0]*i,t[1]=e[1]*i),t},o.dot=function(t,e){return t[0]*e[0]+t[1]*e[1]},o.cross=function(t,e,n){var r=e[0]*n[1]-e[1]*n[0];return t[0]=t[1]=0,t[2]=r,t},o.lerp=function(t,e,n,r){var i=e[0],a=e[1];return t[0]=i+r*(n[0]-i),t[1]=a+r*(n[1]-a),t},o.random=function(t,e){e=e||1;var n=2*r()*Math.PI;return t[0]=Math.cos(n)*e,t[1]=Math.sin(n)*e,t},o.transformMat2=function(t,e,n){var r=e[0],i=e[1];return t[0]=n[0]*r+n[2]*i,t[1]=n[1]*r+n[3]*i,t},o.transformMat2d=function(t,e,n){var r=e[0],i=e[1];return t[0]=n[0]*r+n[2]*i+n[4],t[1]=n[1]*r+n[3]*i+n[5],t},o.transformMat3=function(t,e,n){var r=e[0],i=e[1];return t[0]=n[0]*r+n[3]*i+n[6],t[1]=n[1]*r+n[4]*i+n[7],t},o.transformMat4=function(t,e,n){var r=e[0],i=e[1];return t[0]=n[0]*r+n[4]*i+n[12],t[1]=n[1]*r+n[5]*i+n[13],t},o.forEach=function(){var t=o.create();return function(e,n,r,i,a,o){var s,u;for(n||(n=2),r||(r=0),u=i?Math.min(i*n+r,e.length):e.length,s=r;s0&&(a=1/Math.sqrt(a),t[0]=e[0]*a,t[1]=e[1]*a,t[2]=e[2]*a),t},s.dot=function(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]},s.cross=function(t,e,n){var r=e[0],i=e[1],a=e[2],o=n[0],s=n[1],u=n[2];return t[0]=i*u-a*s,t[1]=a*o-r*u,t[2]=r*s-i*o,t},s.lerp=function(t,e,n,r){var i=e[0],a=e[1],o=e[2];return t[0]=i+r*(n[0]-i),t[1]=a+r*(n[1]-a),t[2]=o+r*(n[2]-o),t},s.random=function(t,e){e=e||1;var n=2*r()*Math.PI,i=2*r()-1,a=Math.sqrt(1-i*i)*e;return t[0]=Math.cos(n)*a,t[1]=Math.sin(n)*a,t[2]=i*e,t},s.transformMat4=function(t,e,n){var r=e[0],i=e[1],a=e[2],o=n[3]*r+n[7]*i+n[11]*a+n[15];return o=o||1,t[0]=(n[0]*r+n[4]*i+n[8]*a+n[12])/o,t[1]=(n[1]*r+n[5]*i+n[9]*a+n[13])/o,t[2]=(n[2]*r+n[6]*i+n[10]*a+n[14])/o,t},s.transformMat3=function(t,e,n){var r=e[0],i=e[1],a=e[2];return t[0]=r*n[0]+i*n[3]+a*n[6],t[1]=r*n[1]+i*n[4]+a*n[7],t[2]=r*n[2]+i*n[5]+a*n[8],t},s.transformQuat=function(t,e,n){var r=e[0],i=e[1],a=e[2],o=n[0],s=n[1],u=n[2],l=n[3],c=l*r+s*a-u*i,h=l*i+u*r-o*a,f=l*a+o*i-s*r,d=-o*r-s*i-u*a;return t[0]=c*l+d*-o+h*-u-f*-s,t[1]=h*l+d*-s+f*-o-c*-u,t[2]=f*l+d*-u+c*-s-h*-o,t},s.rotateX=function(t,e,n,r){var i=[],a=[];return i[0]=e[0]-n[0],i[1]=e[1]-n[1],i[2]=e[2]-n[2],a[0]=i[0],a[1]=i[1]*Math.cos(r)-i[2]*Math.sin(r),a[2]=i[1]*Math.sin(r)+i[2]*Math.cos(r),t[0]=a[0]+n[0],t[1]=a[1]+n[1],t[2]=a[2]+n[2],t},s.rotateY=function(t,e,n,r){var i=[],a=[];return i[0]=e[0]-n[0],i[1]=e[1]-n[1],i[2]=e[2]-n[2],a[0]=i[2]*Math.sin(r)+i[0]*Math.cos(r),a[1]=i[1],a[2]=i[2]*Math.cos(r)-i[0]*Math.sin(r),t[0]=a[0]+n[0],t[1]=a[1]+n[1],t[2]=a[2]+n[2],t},s.rotateZ=function(t,e,n,r){var i=[],a=[];return i[0]=e[0]-n[0],i[1]=e[1]-n[1],i[2]=e[2]-n[2],a[0]=i[0]*Math.cos(r)-i[1]*Math.sin(r),a[1]=i[0]*Math.sin(r)+i[1]*Math.cos(r),a[2]=i[2],t[0]=a[0]+n[0],t[1]=a[1]+n[1],t[2]=a[2]+n[2],t},s.forEach=function(){var t=s.create();return function(e,n,r,i,a,o){var s,u;for(n||(n=3),r||(r=0),u=i?Math.min(i*n+r,e.length):e.length,s=r;s1?0:Math.acos(i)},s.str=function(t){return"vec3("+t[0]+", "+t[1]+", "+t[2]+")"},"undefined"!=typeof t&&(t.vec3=s);var u={};u.create=function(){var t=new n(4);return t[0]=0,t[1]=0,t[2]=0,t[3]=0,t},u.clone=function(t){var e=new n(4);return e[0]=t[0],e[1]=t[1],e[2]=t[2],e[3]=t[3],e},u.fromValues=function(t,e,r,i){var a=new n(4);return a[0]=t,a[1]=e,a[2]=r,a[3]=i,a},u.copy=function(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t},u.set=function(t,e,n,r,i){return t[0]=e,t[1]=n,t[2]=r,t[3]=i,t},u.add=function(t,e,n){return t[0]=e[0]+n[0],t[1]=e[1]+n[1],t[2]=e[2]+n[2],t[3]=e[3]+n[3],t},u.subtract=function(t,e,n){return t[0]=e[0]-n[0],t[1]=e[1]-n[1],t[2]=e[2]-n[2],t[3]=e[3]-n[3],t},u.sub=u.subtract,u.multiply=function(t,e,n){return t[0]=e[0]*n[0],t[1]=e[1]*n[1],t[2]=e[2]*n[2],t[3]=e[3]*n[3],t},u.mul=u.multiply,u.divide=function(t,e,n){return t[0]=e[0]/n[0],t[1]=e[1]/n[1],t[2]=e[2]/n[2],t[3]=e[3]/n[3],t},u.div=u.divide,u.min=function(t,e,n){return t[0]=Math.min(e[0],n[0]),t[1]=Math.min(e[1],n[1]),t[2]=Math.min(e[2],n[2]),t[3]=Math.min(e[3],n[3]),t},u.max=function(t,e,n){return t[0]=Math.max(e[0],n[0]),t[1]=Math.max(e[1],n[1]),t[2]=Math.max(e[2],n[2]),t[3]=Math.max(e[3],n[3]),t},u.scale=function(t,e,n){return t[0]=e[0]*n,t[1]=e[1]*n,t[2]=e[2]*n,t[3]=e[3]*n,t},u.scaleAndAdd=function(t,e,n,r){return t[0]=e[0]+n[0]*r,t[1]=e[1]+n[1]*r,t[2]=e[2]+n[2]*r,t[3]=e[3]+n[3]*r,t},u.distance=function(t,e){var n=e[0]-t[0],r=e[1]-t[1],i=e[2]-t[2],a=e[3]-t[3];return Math.sqrt(n*n+r*r+i*i+a*a)},u.dist=u.distance,u.squaredDistance=function(t,e){var n=e[0]-t[0],r=e[1]-t[1],i=e[2]-t[2],a=e[3]-t[3];return n*n+r*r+i*i+a*a},u.sqrDist=u.squaredDistance,u.length=function(t){var e=t[0],n=t[1],r=t[2],i=t[3];return Math.sqrt(e*e+n*n+r*r+i*i)},u.len=u.length,u.squaredLength=function(t){var e=t[0],n=t[1],r=t[2],i=t[3];return e*e+n*n+r*r+i*i},u.sqrLen=u.squaredLength,u.negate=function(t,e){return t[0]=-e[0],t[1]=-e[1],t[2]=-e[2],t[3]=-e[3],t},u.inverse=function(t,e){return t[0]=1/e[0],t[1]=1/e[1],t[2]=1/e[2],t[3]=1/e[3],t},u.normalize=function(t,e){var n=e[0],r=e[1],i=e[2],a=e[3],o=n*n+r*r+i*i+a*a;return o>0&&(o=1/Math.sqrt(o),t[0]=e[0]*o,t[1]=e[1]*o,t[2]=e[2]*o,t[3]=e[3]*o),t},u.dot=function(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]+t[3]*e[3]},u.lerp=function(t,e,n,r){var i=e[0],a=e[1],o=e[2],s=e[3];return t[0]=i+r*(n[0]-i),t[1]=a+r*(n[1]-a),t[2]=o+r*(n[2]-o),t[3]=s+r*(n[3]-s),t},u.random=function(t,e){return e=e||1,t[0]=r(),t[1]=r(),t[2]=r(),t[3]=r(),u.normalize(t,t),u.scale(t,t,e),t},u.transformMat4=function(t,e,n){var r=e[0],i=e[1],a=e[2],o=e[3];return t[0]=n[0]*r+n[4]*i+n[8]*a+n[12]*o,t[1]=n[1]*r+n[5]*i+n[9]*a+n[13]*o,t[2]=n[2]*r+n[6]*i+n[10]*a+n[14]*o,t[3]=n[3]*r+n[7]*i+n[11]*a+n[15]*o,t},u.transformQuat=function(t,e,n){var r=e[0],i=e[1],a=e[2],o=n[0],s=n[1],u=n[2],l=n[3],c=l*r+s*a-u*i,h=l*i+u*r-o*a,f=l*a+o*i-s*r,d=-o*r-s*i-u*a;return t[0]=c*l+d*-o+h*-u-f*-s,t[1]=h*l+d*-s+f*-o-c*-u,t[2]=f*l+d*-u+c*-s-h*-o,t},u.forEach=function(){var t=u.create();return function(e,n,r,i,a,o){var s,u;for(n||(n=4),r||(r=0),u=i?Math.min(i*n+r,e.length):e.length,s=r;s.999999?(r[0]=0,r[1]=0,r[2]=0,r[3]=1,r):(s.cross(t,i,a),r[0]=t[0],r[1]=t[1],r[2]=t[2],r[3]=1+o,d.normalize(r,r))}}(),d.setAxes=function(){var t=h.create();return function(e,n,r,i){return t[0]=r[0],t[3]=r[1],t[6]=r[2],t[1]=i[0],t[4]=i[1],t[7]=i[2],t[2]=-n[0],t[5]=-n[1],t[8]=-n[2],d.normalize(e,d.fromMat3(e,t))}}(),d.clone=u.clone,d.fromValues=u.fromValues,d.copy=u.copy,d.set=u.set,d.identity=function(t){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t},d.setAxisAngle=function(t,e,n){n=.5*n;var r=Math.sin(n);return t[0]=r*e[0],t[1]=r*e[1],t[2]=r*e[2],t[3]=Math.cos(n),t},d.add=u.add,d.multiply=function(t,e,n){var r=e[0],i=e[1],a=e[2],o=e[3],s=n[0],u=n[1],l=n[2],c=n[3];return t[0]=r*c+o*s+i*l-a*u,t[1]=i*c+o*u+a*s-r*l,t[2]=a*c+o*l+r*u-i*s,t[3]=o*c-r*s-i*u-a*l,t},d.mul=d.multiply,d.scale=u.scale,d.rotateX=function(t,e,n){n*=.5;var r=e[0],i=e[1],a=e[2],o=e[3],s=Math.sin(n),u=Math.cos(n);return t[0]=r*u+o*s,t[1]=i*u+a*s,t[2]=a*u-i*s,t[3]=o*u-r*s,t},d.rotateY=function(t,e,n){n*=.5;var r=e[0],i=e[1],a=e[2],o=e[3],s=Math.sin(n),u=Math.cos(n);return t[0]=r*u-a*s,t[1]=i*u+o*s,t[2]=a*u+r*s,t[3]=o*u-i*s,t},d.rotateZ=function(t,e,n){n*=.5;var r=e[0],i=e[1],a=e[2],o=e[3],s=Math.sin(n),u=Math.cos(n);return t[0]=r*u+i*s,t[1]=i*u-r*s,t[2]=a*u+o*s,t[3]=o*u-a*s,t},d.calculateW=function(t,e){var n=e[0],r=e[1],i=e[2];return t[0]=n,t[1]=r,t[2]=i,t[3]=Math.sqrt(Math.abs(1-n*n-r*r-i*i)),t},d.dot=u.dot,d.lerp=u.lerp,d.slerp=function(t,e,n,r){var i,a,o,s,u,l=e[0],c=e[1],h=e[2],f=e[3],d=n[0],_=n[1],p=n[2],m=n[3];return a=l*d+c*_+h*p+f*m,a<0&&(a=-a,d=-d,_=-_,p=-p,m=-m),1-a>1e-6?(i=Math.acos(a),o=Math.sin(i),s=Math.sin((1-r)*i)/o,u=Math.sin(r*i)/o):(s=1-r,u=r),t[0]=s*l+u*d,t[1]=s*c+u*_,t[2]=s*h+u*p,t[3]=s*f+u*m,t},d.invert=function(t,e){var n=e[0],r=e[1],i=e[2],a=e[3],o=n*n+r*r+i*i+a*a,s=o?1/o:0;return t[0]=-n*s,t[1]=-r*s,t[2]=-i*s,t[3]=a*s,t},d.conjugate=function(t,e){return t[0]=-e[0],t[1]=-e[1],t[2]=-e[2],t[3]=e[3],t},d.length=u.length,d.len=d.length,d.squaredLength=u.squaredLength,d.sqrLen=d.squaredLength,d.normalize=u.normalize,d.fromMat3=function(t,e){var n,r=e[0]+e[4]+e[8];if(r>0)n=Math.sqrt(r+1),t[3]=.5*n,n=.5/n,t[0]=(e[5]-e[7])*n,t[1]=(e[6]-e[2])*n,t[2]=(e[1]-e[3])*n;else{var i=0;e[4]>e[0]&&(i=1),e[8]>e[3*i+i]&&(i=2);var a=(i+1)%3,o=(i+2)%3;n=Math.sqrt(e[3*i+i]-e[3*a+a]-e[3*o+o]+1),t[i]=.5*n,n=.5/n,t[3]=(e[3*a+o]-e[3*o+a])*n,t[a]=(e[3*a+i]+e[3*i+a])*n,t[o]=(e[3*o+i]+e[3*i+o])*n}return t},d.str=function(t){return"quat("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},"undefined"!=typeof t&&(t.quat=d)}(n.exports)}(this)},function(t,e,n){"use strict";var r=n(66),i=n(40),a=n(17),o=function(){this.__GUID__=a.genGUID()};o.__initializers__=[function(t){a.extend(this,t)}],a.extend(o,r),a.extend(o.prototype,i),t.exports=o},function(t,e,n){"use strict";function r(t,e,n){return tn?n:t}var i=n(1),a=i.vec3,o=function(t,e,n){t=t||0,e=e||0,n=n||0,this._array=a.fromValues(t,e,n),this._dirty=!0};o.prototype={constructor:o,add:function(t){return a.add(this._array,this._array,t._array),this._dirty=!0,this},set:function(t,e,n){return this._array[0]=t,this._array[1]=e,this._array[2]=n,this._dirty=!0,this},setArray:function(t){return this._array[0]=t[0],this._array[1]=t[1],this._array[2]=t[2],this._dirty=!0,this},clone:function(){return new o(this.x,this.y,this.z)},copy:function(t){return a.copy(this._array,t._array),this._dirty=!0,this},cross:function(t,e){return a.cross(this._array,t._array,e._array),this._dirty=!0,this},dist:function(t){return a.dist(this._array,t._array)},distance:function(t){return a.distance(this._array,t._array)},div:function(t){return a.div(this._array,this._array,t._array),this._dirty=!0,this},divide:function(t){return a.divide(this._array,this._array,t._array),this._dirty=!0,this},dot:function(t){return a.dot(this._array,t._array)},len:function(){return a.len(this._array)},length:function(){return a.length(this._array)},lerp:function(t,e,n){return a.lerp(this._array,t._array,e._array,n),this._dirty=!0,this},min:function(t){return a.min(this._array,this._array,t._array),this._dirty=!0,this},max:function(t){return a.max(this._array,this._array,t._array),this._dirty=!0,this},mul:function(t){return a.mul(this._array,this._array,t._array),this._dirty=!0,this},multiply:function(t){return a.multiply(this._array,this._array,t._array),this._dirty=!0,this},negate:function(){return a.negate(this._array,this._array),this._dirty=!0,this},normalize:function(){return a.normalize(this._array,this._array),this._dirty=!0,this},random:function(t){return a.random(this._array,t),this._dirty=!0,this},scale:function(t){return a.scale(this._array,this._array,t),this._dirty=!0,this},scaleAndAdd:function(t,e){return a.scaleAndAdd(this._array,this._array,t._array,e),this._dirty=!0,this},sqrDist:function(t){return a.sqrDist(this._array,t._array)},squaredDistance:function(t){return a.squaredDistance(this._array,t._array)},sqrLen:function(){return a.sqrLen(this._array)},squaredLength:function(){return a.squaredLength(this._array)},sub:function(t){return a.sub(this._array,this._array,t._array),this._dirty=!0,this},subtract:function(t){return a.subtract(this._array,this._array,t._array),this._dirty=!0,this},transformMat3:function(t){return a.transformMat3(this._array,this._array,t._array),this._dirty=!0,this},transformMat4:function(t){return a.transformMat4(this._array,this._array,t._array),this._dirty=!0,this},transformQuat:function(t){return a.transformQuat(this._array,this._array,t._array),this._dirty=!0,this},applyProjection:function(t){var e=this._array;if(t=t._array,0===t[15]){var n=-1/e[2];e[0]=t[0]*e[0]*n,e[1]=t[5]*e[1]*n,e[2]=(t[10]*e[2]+t[14])*n}else e[0]=t[0]*e[0]+t[12],e[1]=t[5]*e[1]+t[13],e[2]=t[10]*e[2]+t[14];return this._dirty=!0,this},eulerFromQuat:function(t,e){o.eulerFromQuat(this,t,e)},eulerFromMat3:function(t,e){o.eulerFromMat3(this,t,e); +},toString:function(){return"["+Array.prototype.join.call(this._array,",")+"]"},toArray:function(){return Array.prototype.slice.call(this._array)}};var s=Object.defineProperty;if(s){var u=o.prototype;s(u,"x",{get:function(){return this._array[0]},set:function(t){this._array[0]=t,this._dirty=!0}}),s(u,"y",{get:function(){return this._array[1]},set:function(t){this._array[1]=t,this._dirty=!0}}),s(u,"z",{get:function(){return this._array[2]},set:function(t){this._array[2]=t,this._dirty=!0}})}o.add=function(t,e,n){return a.add(t._array,e._array,n._array),t._dirty=!0,t},o.set=function(t,e,n,r){a.set(t._array,e,n,r),t._dirty=!0},o.copy=function(t,e){return a.copy(t._array,e._array),t._dirty=!0,t},o.cross=function(t,e,n){return a.cross(t._array,e._array,n._array),t._dirty=!0,t},o.dist=function(t,e){return a.distance(t._array,e._array)},o.distance=o.dist,o.div=function(t,e,n){return a.divide(t._array,e._array,n._array),t._dirty=!0,t},o.divide=o.div,o.dot=function(t,e){return a.dot(t._array,e._array)},o.len=function(t){return a.length(t._array)},o.lerp=function(t,e,n,r){return a.lerp(t._array,e._array,n._array,r),t._dirty=!0,t},o.min=function(t,e,n){return a.min(t._array,e._array,n._array),t._dirty=!0,t},o.max=function(t,e,n){return a.max(t._array,e._array,n._array),t._dirty=!0,t},o.mul=function(t,e,n){return a.multiply(t._array,e._array,n._array),t._dirty=!0,t},o.multiply=o.mul,o.negate=function(t,e){return a.negate(t._array,e._array),t._dirty=!0,t},o.normalize=function(t,e){return a.normalize(t._array,e._array),t._dirty=!0,t},o.random=function(t,e){return a.random(t._array,e),t._dirty=!0,t},o.scale=function(t,e,n){return a.scale(t._array,e._array,n),t._dirty=!0,t},o.scaleAndAdd=function(t,e,n,r){return a.scaleAndAdd(t._array,e._array,n._array,r),t._dirty=!0,t},o.sqrDist=function(t,e){return a.sqrDist(t._array,e._array)},o.squaredDistance=o.sqrDist,o.sqrLen=function(t){return a.sqrLen(t._array)},o.squaredLength=o.sqrLen,o.sub=function(t,e,n){return a.subtract(t._array,e._array,n._array),t._dirty=!0,t},o.subtract=o.sub,o.transformMat3=function(t,e,n){return a.transformMat3(t._array,e._array,n._array),t._dirty=!0,t},o.transformMat4=function(t,e,n){return a.transformMat4(t._array,e._array,n._array),t._dirty=!0,t},o.transformQuat=function(t,e,n){return a.transformQuat(t._array,e._array,n._array),t._dirty=!0,t};var l=Math.atan2,c=Math.asin,h=Math.abs;o.eulerFromQuat=function(t,e,n){t._dirty=!0,e=e._array;var i=t._array,a=e[0],o=e[1],s=e[2],u=e[3],h=a*a,f=o*o,d=s*s,_=u*u,n=(n||"XYZ").toUpperCase();switch(n){case"XYZ":i[0]=l(2*(a*u-o*s),_-h-f+d),i[1]=c(r(2*(a*s+o*u),-1,1)),i[2]=l(2*(s*u-a*o),_+h-f-d);break;case"YXZ":i[0]=c(r(2*(a*u-o*s),-1,1)),i[1]=l(2*(a*s+o*u),_-h-f+d),i[2]=l(2*(a*o+s*u),_-h+f-d);break;case"ZXY":i[0]=c(r(2*(a*u+o*s),-1,1)),i[1]=l(2*(o*u-s*a),_-h-f+d),i[2]=l(2*(s*u-a*o),_-h+f-d);break;case"ZYX":i[0]=l(2*(a*u+s*o),_-h-f+d),i[1]=c(r(2*(o*u-a*s),-1,1)),i[2]=l(2*(a*o+s*u),_+h-f-d);break;case"YZX":i[0]=l(2*(a*u-s*o),_-h+f-d),i[1]=l(2*(o*u-a*s),_+h-f-d),i[2]=c(r(2*(a*o+s*u),-1,1));break;case"XZY":i[0]=l(2*(a*u+o*s),_-h+f-d),i[1]=l(2*(a*s+o*u),_+h-f-d),i[2]=c(r(2*(s*u-a*o),-1,1));break;default:console.warn("Unkown order: "+n)}return t},o.eulerFromMat3=function(t,e,n){var i=e._array,a=i[0],o=i[3],s=i[6],u=i[1],f=i[4],d=i[7],_=i[2],p=i[5],m=i[8],v=t._array,n=(n||"XYZ").toUpperCase();switch(n){case"XYZ":v[1]=c(r(s,-1,1)),h(s)<.99999?(v[0]=l(-d,m),v[2]=l(-o,a)):(v[0]=l(p,f),v[2]=0);break;case"YXZ":v[0]=c(-r(d,-1,1)),h(d)<.99999?(v[1]=l(s,m),v[2]=l(u,f)):(v[1]=l(-_,a),v[2]=0);break;case"ZXY":v[0]=c(r(p,-1,1)),h(p)<.99999?(v[1]=l(-_,m),v[2]=l(-o,f)):(v[1]=0,v[2]=l(u,a));break;case"ZYX":v[1]=c(-r(_,-1,1)),h(_)<.99999?(v[0]=l(p,m),v[2]=l(u,a)):(v[0]=0,v[2]=l(-o,f));break;case"YZX":v[2]=c(r(u,-1,1)),h(u)<.99999?(v[0]=l(-d,f),v[1]=l(-_,a)):(v[0]=0,v[1]=l(s,m));break;case"XZY":v[2]=c(-r(o,-1,1)),h(o)<.99999?(v[0]=l(p,f),v[1]=l(s,a)):(v[0]=l(-d,m),v[1]=0);break;default:console.warn("Unkown order: "+n)}return t._dirty=!0,t},o.POSITIVE_X=new o(1,0,0),o.NEGATIVE_X=new o((-1),0,0),o.POSITIVE_Y=new o(0,1,0),o.NEGATIVE_Y=new o(0,(-1),0),o.POSITIVE_Z=new o(0,0,1),o.NEGATIVE_Z=new o(0,0,(-1)),o.UP=new o(0,1,0),o.ZERO=new o(0,0,0),t.exports=o},function(t,e,n){"use strict";function r(){return{locations:{},attriblocations:{}}}function i(t,e,n){if(!t.getShaderParameter(e,t.COMPILE_STATUS))return[t.getShaderInfoLog(e),a(n)].join("\n")}function a(t){for(var e=t.split("\n"),n=0,r=e.length;n0&&r.push("#define "+i.toUpperCase()+"_COUNT "+a)}for(var o in n){var s=n[o];s.enabled&&r.push("#define "+o.toUpperCase()+"_ENABLED")}for(var o in t){var u=t[o];null===u?r.push("#define "+o):r.push("#define "+o+" "+u.toString())}return r.join("\n")},_unrollLoop:function(t,e){function n(t,n,i,a){var o="";isNaN(n)&&(n=n in e?e[n]:r[n]),isNaN(i)&&(i=i in e?e[i]:r[i]);for(var s=parseInt(n);s=0)n.attribSemantics[u]={symbol:a,type:c},h=!1;else if(E.indexOf(u)>=0){var f=!1,d=u;u.match(/TRANSPOSE$/)&&(f=!0,d=u.slice(0,-9)),n.matrixSemantics[u]={symbol:a,type:c,isTranspose:f,semanticNoTranspose:d},h=!1}else if(T.indexOf(u)>=0)n.uniformSemantics[u]={symbol:a,type:c},h=!1;else if("unconfigurable"===u)h=!1;else{if(l=n._parseDefaultValue(i,u),!l)throw new Error('Unkown semantic "'+u+'"');u=""}h&&(e[a]={type:c,value:o?y.array:l||y[i],semantic:u||null})}return["uniform",i,a,o].join(" ")+";\n"}}var e={},n=this,r="vertex";this._uniformList=[],this._vertexProcessedWithoutDefine=this._vertexProcessedWithoutDefine.replace(_,t),r="fragment",this._fragmentProcessedWithoutDefine=this._fragmentProcessedWithoutDefine.replace(_,t),n.matrixSemanticKeys=Object.keys(this.matrixSemantics),this.uniformTemplates=e},_parseDefaultValue:function(t,e){var n=/\[\s*(.*)\s*\]/;{if("vec2"!==t&&"vec3"!==t&&"vec4"!==t)return"bool"===t?function(){return"true"===e.toLowerCase()}:"float"===t?function(){return parseFloat(e)}:"int"===t?function(){return parseInt(e)}:void 0;var r=n.exec(e)[1];if(r){var i=r.split(/\s*,\s*/);return function(){return new l.Float32Array(i)}}}},createUniforms:function(){var t={};for(var e in this.uniformTemplates){var n=this.uniformTemplates[e];t[e]={type:n.type,value:n.value()}}return t},attached:function(){this._attacheMaterialNumber++},detached:function(){this._attacheMaterialNumber--},isAttachedToAny:function(){return 0!==this._attacheMaterialNumber},_parseAttributes:function(){function t(t,r,i,a,o){if(r&&i){var s=1;switch(r){case"vec4":s=4;break;case"vec3":s=3;break;case"vec2":s=2;break;case"float":s=1}if(e[i]={type:"float",size:s,semantic:o||null},o){if(x.indexOf(o)<0)throw new Error('Unkown semantic "'+o+'"');n.attribSemantics[o]={symbol:i,type:r}}}return["attribute",r,i].join(" ")+";\n"}var e={},n=this;this._vertexProcessedWithoutDefine=this._vertexProcessedWithoutDefine.replace(p,t),this.attributeTemplates=e},_parseDefines:function(){function t(t,r,i){var a="vertex"===n?e.vertexDefines:e.fragmentDefines;return a[r]||("false"==i?a[r]=!1:"true"==i?a[r]=!0:a[r]=i?parseFloat(i):null),""}var e=this,n="vertex";this._vertexProcessedWithoutDefine=this._vertexProcessedWithoutDefine.replace(m,t),n="fragment",this._fragmentProcessedWithoutDefine=this._fragmentProcessedWithoutDefine.replace(m,t)},_buildProgram:function(t,e,n){var r=this._cache;r.get("program")&&t.deleteProgram(r.get("program"));var a=t.createProgram(),o=t.createShader(t.VERTEX_SHADER);t.shaderSource(o,e),t.compileShader(o);var s=t.createShader(t.FRAGMENT_SHADER);t.shaderSource(s,n),t.compileShader(s);var u=i(t,o,e);if(u)return u;if(u=i(t,s,n))return u;if(t.attachShader(a,o),t.attachShader(a,s),this.attribSemantics.POSITION)t.bindAttribLocation(a,0,this.attribSemantics.POSITION.symbol);else{var l=Object.keys(this.attributeTemplates);t.bindAttribLocation(a,0,l[0])}if(t.linkProgram(a),!t.getProgramParameter(a,t.LINK_STATUS))return"Could not link program\nVALIDATE_STATUS: "+t.getProgramParameter(a,t.VALIDATE_STATUS)+", gl error ["+t.getError()+"]";for(var c=0;c=0},set:function(t,e){if("object"==typeof t)for(var n in t){var r=t[n];this.set(n,r)}else{var i=this.uniforms[t];i&&(i.value=e)}},get:function(t){var e=this.uniforms[t];if(e)return e.value},attachShader:function(t,e){this.shader&&this.shader.detached();var n=this.uniforms;this.uniforms=t.createUniforms(),this.shader=t;var r=this.uniforms;if(this._enabledUniforms=Object.keys(r),this._enabledUniforms.sort(),e)for(var i in n)r[i]&&(r[i].value=n[i].value);t.attached()},detachShader:function(){this.shader.detached(),this.shader=null,this.uniforms={}},clone:function(){var t=new this.constructor({name:this.name,shader:this.shader});for(var e in this.uniforms)t.uniforms[e].value=this.uniforms[e].value;return t.depthTest=this.depthTest,t.depthMask=this.depthMask,t.transparent=this.transparent,t.blend=this.blend,t},dispose:function(t,e){if(e)for(var n in this.uniforms){var r=this.uniforms[n].value;if(r)if(r instanceof i)r.dispose(t);else if(r instanceof Array)for(var a=0;a>e;return t+1},dispose:function(t){var e=this._cache;e.use(t.__GLID__);var n=e.get("webgl_texture");n&&t.deleteTexture(n),e.deleteContext(t.__GLID__)},isRenderable:function(){},isPowerOfTwo:function(){}});o.BYTE=i.BYTE,o.UNSIGNED_BYTE=i.UNSIGNED_BYTE,o.SHORT=i.SHORT,o.UNSIGNED_SHORT=i.UNSIGNED_SHORT,o.INT=i.INT,o.UNSIGNED_INT=i.UNSIGNED_INT,o.FLOAT=i.FLOAT,o.HALF_FLOAT=36193,o.UNSIGNED_INT_24_8_WEBGL=34042,o.DEPTH_COMPONENT=i.DEPTH_COMPONENT,o.DEPTH_STENCIL=i.DEPTH_STENCIL,o.ALPHA=i.ALPHA,o.RGB=i.RGB,o.RGBA=i.RGBA,o.LUMINANCE=i.LUMINANCE,o.LUMINANCE_ALPHA=i.LUMINANCE_ALPHA,o.COMPRESSED_RGB_S3TC_DXT1_EXT=33776,o.COMPRESSED_RGBA_S3TC_DXT1_EXT=33777,o.COMPRESSED_RGBA_S3TC_DXT3_EXT=33778,o.COMPRESSED_RGBA_S3TC_DXT5_EXT=33779,o.NEAREST=i.NEAREST,o.LINEAR=i.LINEAR,o.NEAREST_MIPMAP_NEAREST=i.NEAREST_MIPMAP_NEAREST,o.LINEAR_MIPMAP_NEAREST=i.LINEAR_MIPMAP_NEAREST,o.NEAREST_MIPMAP_LINEAR=i.NEAREST_MIPMAP_LINEAR,o.LINEAR_MIPMAP_LINEAR=i.LINEAR_MIPMAP_LINEAR,o.REPEAT=i.REPEAT,o.CLAMP_TO_EDGE=i.CLAMP_TO_EDGE,o.MIRRORED_REPEAT=i.MIRRORED_REPEAT,t.exports=o},function(t,e,n){var r=n(7),i=n(16),a=n(5),o=n(50),s=o.isPowerOfTwo,u=r.extend(function(){return{image:null,pixels:null,mipmaps:[]}},{update:function(t){t.bindTexture(t.TEXTURE_2D,this._cache.get("webgl_texture")),this.beforeUpdate(t);var e=this.format,n=this.type;t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,this.wrapS),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,this.wrapT),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,this.magFilter),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,this.minFilter);var r=i.getExtension(t,"EXT_texture_filter_anisotropic");if(r&&this.anisotropic>1&&t.texParameterf(t.TEXTURE_2D,r.TEXTURE_MAX_ANISOTROPY_EXT,this.anisotropic),36193===n){var o=i.getExtension(t,"OES_texture_half_float");o||(n=a.FLOAT)}if(this.mipmaps.length)for(var s=this.width,u=this.height,l=0;l=r.COMPRESSED_RGB_S3TC_DXT1_EXT?t.compressedTexImage2D(t.TEXTURE_2D,n,o,i,a,0,e.pixels):t.texImage2D(t.TEXTURE_2D,n,o,i,a,0,o,s,e.pixels)},generateMipmap:function(t){this.useMipmap&&!this.NPOT&&(t.bindTexture(t.TEXTURE_2D,this._cache.get("webgl_texture")),t.generateMipmap(t.TEXTURE_2D))},isPowerOfTwo:function(){var t,e;return this.image?(t=this.image.width,e=this.image.height):(t=this.width,e=this.height),s(t)&&s(e)},isRenderable:function(){return this.image?"CANVAS"===this.image.nodeName||"VIDEO"===this.image.nodeName||this.image.complete:!(!this.width||!this.height)},bind:function(t){t.bindTexture(t.TEXTURE_2D,this.getWebGLTexture(t))},unbind:function(t){t.bindTexture(t.TEXTURE_2D,null)},load:function(t){var e=new Image,n=this;return e.onload=function(){n.dirty(),n.trigger("success",n),e.onload=null},e.onerror=function(){n.trigger("error",n),e.onerror=null},e.src=t,this.image=e,this}});t.exports=u},function(t,e,n){"use strict";var r=n(3),i=n(1),a=i.vec3,o=a.copy,s=a.set,u=function(t,e){this.min=t||new r(1/0,1/0,1/0),this.max=e||new r((-(1/0)),(-(1/0)),(-(1/0))); +};u.prototype={constructor:u,updateFromVertices:function(t){if(t.length>0){var e=this.min,n=this.max,r=e._array,i=n._array;o(r,t[0]),o(i,t[0]);for(var a=1;ai[0]&&(i[0]=s[0]),s[1]>i[1]&&(i[1]=s[1]),s[2]>i[2]&&(i[2]=s[2])}e._dirty=!0,n._dirty=!0}},union:function(t){var e=this.min,n=this.max;return a.min(e._array,e._array,t.min._array),a.max(n._array,n._array,t.max._array),e._dirty=!0,n._dirty=!0,this},intersection:function(t){var e=this.min,n=this.max;return a.max(e._array,e._array,t.min._array),a.min(n._array,n._array,t.max._array),e._dirty=!0,n._dirty=!0,this},intersectBoundingBox:function(t){var e=this.min._array,n=this.max._array,r=t.min._array,i=t.max._array;return!(e[0]>i[0]||e[1]>i[1]||e[2]>i[2]||n[0]=i[0]&&n[1]>=i[1]&&n[2]>=i[2]},containPoint:function(t){var e=this.min._array,n=this.max._array,r=t._array;return e[0]<=r[0]&&e[1]<=r[1]&&e[2]<=r[2]&&n[0]>=r[0]&&n[1]>=r[1]&&n[2]>=r[2]},isFinite:function(){var t=this.min._array,e=this.max._array;return isFinite(t[0])&&isFinite(t[1])&&isFinite(t[2])&&isFinite(e[0])&&isFinite(e[1])&&isFinite(e[2])},applyTransform:function(){var t=a.create(),e=a.create(),n=a.create(),r=a.create(),i=a.create(),o=a.create();return function(a){var s=this.min._array,u=this.max._array,l=a._array;return t[0]=l[0]*s[0],t[1]=l[1]*s[0],t[2]=l[2]*s[0],e[0]=l[0]*u[0],e[1]=l[1]*u[0],e[2]=l[2]*u[0],n[0]=l[4]*s[1],n[1]=l[5]*s[1],n[2]=l[6]*s[1],r[0]=l[4]*u[1],r[1]=l[5]*u[1],r[2]=l[6]*u[1],i[0]=l[8]*s[2],i[1]=l[9]*s[2],i[2]=l[10]*s[2],o[0]=l[8]*u[2],o[1]=l[9]*u[2],o[2]=l[10]*u[2],s[0]=Math.min(t[0],e[0])+Math.min(n[0],r[0])+Math.min(i[0],o[0])+l[12],s[1]=Math.min(t[1],e[1])+Math.min(n[1],r[1])+Math.min(i[1],o[1])+l[13],s[2]=Math.min(t[2],e[2])+Math.min(n[2],r[2])+Math.min(i[2],o[2])+l[14],u[0]=Math.max(t[0],e[0])+Math.max(n[0],r[0])+Math.max(i[0],o[0])+l[12],u[1]=Math.max(t[1],e[1])+Math.max(n[1],r[1])+Math.max(i[1],o[1])+l[13],u[2]=Math.max(t[2],e[2])+Math.max(n[2],r[2])+Math.max(i[2],o[2])+l[14],this.min._dirty=!0,this.max._dirty=!0,this}}(),applyProjection:function(t){var e=this.min._array,n=this.max._array,r=t._array,i=e[0],a=e[1],o=e[2],s=n[0],u=n[1],l=e[2],c=n[0],h=n[1],f=n[2];if(1===r[15])e[0]=r[0]*i+r[12],e[1]=r[5]*a+r[13],n[2]=r[10]*o+r[14],n[0]=r[0]*c+r[12],n[1]=r[5]*h+r[13],e[2]=r[10]*f+r[14];else{var d=-1/o;e[0]=r[0]*i*d,e[1]=r[5]*a*d,n[2]=(r[10]*o+r[14])*d,d=-1/l,n[0]=r[0]*s*d,n[1]=r[5]*u*d,d=-1/f,e[2]=(r[10]*f+r[14])*d}return this.min._dirty=!0,this.max._dirty=!0,this},updateVertices:function(){var t=this.vertices;if(!t){for(var t=[],e=0;e<8;e++)t[e]=a.fromValues(0,0,0);this.vertices=t}var n=this.min._array,r=this.max._array;return s(t[0],n[0],n[1],n[2]),s(t[1],n[0],r[1],n[2]),s(t[2],r[0],n[1],n[2]),s(t[3],r[0],r[1],n[2]),s(t[4],n[0],n[1],r[2]),s(t[5],n[0],r[1],r[2]),s(t[6],r[0],n[1],r[2]),s(t[7],r[0],r[1],r[2]),this},copy:function(t){var e=this.min,n=this.max;return o(e._array,t.min._array),o(n._array,t.max._array),e._dirty=!0,n._dirty=!0,this},clone:function(){var t=new u;return t.copy(this),t}},t.exports=u},function(t,e,n){"use strict";var r=n(1),i=n(3),a=r.mat4,o=r.vec3,s=r.mat3,u=r.quat,l=function(){this._axisX=new i,this._axisY=new i,this._axisZ=new i,this._array=a.create(),this._dirty=!0};l.prototype={constructor:l,setArray:function(t){for(var e=0;eo[0]&&(o[0]=u),l>o[1]&&(o[1]=l),h>o[2]&&(o[2]=h)}n._dirty=!0,r._dirty=!0}},dirty:function(){for(var t=this.getEnabledAttributes(),e=0;e=0){e||(e=f());var n=this.indices;return e[0]=n[3*t],e[1]=n[3*t+1],e[2]=n[3*t+2],e}},setTriangleIndices:function(t,e){var n=this.indices;n[3*t]=e[0],n[3*t+1]=e[1],n[3*t+2]=e[2]},isUseIndices:function(){return this.indices},initIndicesFromArray:function(t){var e,n=this.vertexCount>65535?s.Uint32Array:s.Uint16Array;if(t[0]&&t[0].length){var r=0,i=3;e=new n(t.length*i);for(var a=0;a=0&&(e.splice(n,1),delete this.attributes[t],!0)},getEnabledAttributes:function(){var t=this._enabledAttributes,e=this._attributeList;if(t)return t;for(var n=[],r=this.vertexCount,i=0;i65535&&(this.indices=new s.Uint32Array(this.indices));for(var r=0,i=this.attributes,a=this.indices,e=0;e0){for(var u=0;u1&&t.texParameterf(t.TEXTURE_CUBE_MAP,r.TEXTURE_MAX_ANISOTROPY_EXT,this.anisotropic),36193===n){var i=a.getExtension(t,"OES_texture_half_float");i||(n=o.FLOAT)}if(this.mipmaps.length)for(var s=this.width,u=this.height,l=0;l1&&(e=1);var n;return n=this.easing?this.easing(e):e,this.fire("frame",n),1===e?this._loop&&this._loopRemained>0?(this._restartInLoop(t),this._loopRemained--,"restart"):(this._needsRemove=!0,"finish"):null}},setTime:function(t){return this.step(t+this._startTime)},restart:function(t){var e=0;t=t,t&&(this._elapse(t),e=this._elapsedTime%this.life),t=t||(new Date).getTime(),this._startTime=t-e+this.delay,this._elapsedTime=0,this._currentTime=t-e,this._needsRemove=!1},_restartInLoop:function(t){this._startTime=t+this.gap,this._currentTime=t,this._elapsedTime=0},_elapse:function(t){this._elapsedTime+=(t-this._currentTime)*this.playbackRate,this._currentTime=t},fire:function(t,e){var n="on"+t;this[n]&&this[n](this.target,e)},clone:function(){var t=new this.constructor;return t.name=this.name,t._loop=this._loop,t._loopRemained=this._loopRemained,t.life=this.life,t.gap=this.gap,t.delay=this.delay,t}},a.prototype.constructor=a,t.exports=a},function(t,e,n){"use strict";var r=n(2),i=n(30),a=n(42),o=n(4),s=n(6),u=n(15),l=n(16),c=n(5);o["import"](n(131));var h=new a,f=new u({geometry:h,frustumCulling:!1}),d=new i,_=r.extend(function(){return{fragment:"",outputs:null,material:null,blendWithPrevious:!1,clearColor:!1,clearDepth:!0}},function(){var t=new o({vertex:o.source("qtek.compositor.vertex"),fragment:this.fragment}),e=new s({shader:t});t.enableTexturesAll(),this.material=e},{setUniform:function(t,e){var n=this.material.uniforms[t];n&&(n.value=e)},getUniform:function(t){var e=this.material.uniforms[t];if(e)return e.value},attachOutput:function(t,e){this.outputs||(this.outputs={}),e=e||c.COLOR_ATTACHMENT0,this.outputs[e]=t},detachOutput:function(t){for(var e in this.outputs)this.outputs[e]===t&&(this.outputs[e]=null)},bind:function(t,e){if(this.outputs)for(var n in this.outputs){var r=this.outputs[n];r&&e.attach(r,n)}e&&e.bind(t)},unbind:function(t,e){e.unbind(t)},render:function(t,e){var n=t.gl;if(e){this.bind(t,e);var r=l.getExtension(n,"EXT_draw_buffers");if(r&&this.outputs){var i=[];for(var a in this.outputs)a=+a,a>=n.COLOR_ATTACHMENT0&&a<=n.COLOR_ATTACHMENT0+8&&i.push(a);r.drawBuffersEXT(i)}}this.trigger("beforerender",this,t);var o=this.clearDepth?n.DEPTH_BUFFER_BIT:0;if(n.depthMask(!0),this.clearColor){o|=n.COLOR_BUFFER_BIT,n.colorMask(!0,!0,!0,!0);var s=this.clearColor;s instanceof Array&&n.clearColor(s[0],s[1],s[2],s[3])}n.clear(o),this.blendWithPrevious?(n.enable(n.BLEND),this.material.transparent=!0):(n.disable(n.BLEND),this.material.transparent=!1),this.renderQuad(t),this.trigger("afterrender",this,t),e&&this.unbind(t,e)},renderQuad:function(t){f.material=this.material,t.renderQueue([f],d)},dispose:function(t){this.material.dispose(t)}});t.exports=_},function(t,e,n){"use strict";var r=n(33),i=r.extend({fov:50,aspect:1,near:.1,far:2e3},{updateProjectionMatrix:function(){var t=this.fov/180*Math.PI;this.projectionMatrix.perspective(t,this.aspect,this.near,this.far)},decomposeProjectionMatrix:function(){var t=this.projectionMatrix._array,e=2*Math.atan(1/t[5]);this.fov=e/Math.PI*180,this.aspect=t[5]/t[0],this.near=t[14]/(t[10]-1),this.far=t[14]/(t[10]+1)},clone:function(){var t=r.prototype.clone.call(this);return t.fov=this.fov,t.aspect=this.aspect,t.near=this.near,t.far=this.far,t}});t.exports=i},function(t,e,n){"use strict";var r=n(2),i=r.extend(function(){return{name:"",inputLinks:{},outputLinks:{},_prevOutputTextures:{},_outputTextures:{},_outputReferences:{},_rendering:!1,_rendered:!1,_compositor:null}},{updateParameter:function(t,e){var n=this.outputs[t],r=n.parameters,i=n._parametersCopy;if(i||(i=n._parametersCopy={}),r)for(var a in r)"width"!==a&&"height"!==a&&(i[a]=r[a]);var o,s;return o=r.width instanceof Function?r.width.call(this,e):r.width,s=r.height instanceof Function?r.height.call(this,e):r.height,i.width===o&&i.height===s||this._outputTextures[t]&&this._outputTextures[t].dispose(e.gl),i.width=o,i.height=s,i},setParameter:function(t,e){},getParameter:function(t){},setParameters:function(t){for(var e in t)this.setParameter(e,t[e])},render:function(){},getOutput:function(t,e){if(null==e)return e=t,this._outputTextures[e];var n=this.outputs[e];if(n)return this._rendered?n.outputLastFrame?this._prevOutputTextures[e]:this._outputTextures[e]:this._rendering?(this._prevOutputTextures[e]||(this._prevOutputTextures[e]=this._compositor.allocateTexture(n.parameters||{})),this._prevOutputTextures[e]):(this.render(t),this._outputTextures[e])},removeReference:function(t){if(this._outputReferences[t]--,0===this._outputReferences[t]){var e=this.outputs[t];e.keepLastFrame?(this._prevOutputTextures[t]&&this._compositor.releaseTexture(this._prevOutputTextures[t]),this._prevOutputTextures[t]=this._outputTextures[t]):this._compositor.releaseTexture(this._outputTextures[t])}},link:function(t,e,n){this.inputLinks[t]={node:e,pin:n},e.outputLinks[n]||(e.outputLinks[n]=[]),e.outputLinks[n].push({node:this,pin:t});var r=this.pass.material.shader;r.enableTexture(t)},clear:function(){this.inputLinks={},this.outputLinks={}},updateReference:function(t){if(!this._rendering){this._rendering=!0;for(var e in this.inputLinks){var n=this.inputLinks[e];n.node.updateReference(n.pin)}this._rendering=!1}t&&this._outputReferences[t]++},beforeFrame:function(){this._rendered=!1;for(var t in this.outputLinks)this._outputReferences[t]=0},afterFrame:function(){for(var t in this.outputLinks)if(this._outputReferences[t]>0){var e=this.outputs[t];e.keepLastFrame?(this._prevOutputTextures[t]&&this._compositor.releaseTexture(this._prevOutputTextures[t]),this._prevOutputTextures[t]=this._outputTextures[t]):this._compositor.releaseTexture(this._outputTextures[t])}}});t.exports=i},function(t,e,n){"use strict";function r(t,e){if(e.castShadow&&!t.castShadow)return!0}var i=n(19),a=n(11),o=n(9),s=i.extend(function(){return{material:null,autoUpdate:!0,opaqueQueue:[],transparentQueue:[],lights:[],viewBoundingBoxLastFrame:new o,_lightUniforms:{},_lightNumber:{},_opaqueObjectCount:0,_transparentObjectCount:0,_nodeRepository:{}}},function(){this._scene=this},{addToScene:function(t){t.name&&(this._nodeRepository[t.name]=t)},removeFromScene:function(t){t.name&&delete this._nodeRepository[t.name]},getNode:function(t){return this._nodeRepository[t]},cloneNode:function(t){var e=t.clone(),n={},r=function(i,a){i.skeleton&&(a.skeleton=i.skeleton.clone(t,e),a.joints=i.joints.slice()),i.material&&(n[i.material.__GUID__]={oldMat:i.material});for(var o=0;o0&&this._updateRenderQueue(r)}},_updateLightUniforms:function(){var t=this.lights;t.sort(r);var e=this._lightUniforms;for(var n in e)for(var i in e[n])e[n][i].value.length=0;for(var a=0;a0},beforeRender:function(t){},afterRender:function(t,e){},getBoundingBox:function(t,e){return e=a.prototype.getBoundingBox.call(this,t,e),this.geometry&&this.geometry.boundingBox&&e.union(this.geometry.boundingBox),e},render:function(t,e){var e=e||this.material.shader,n=this.geometry,a=this.mode,h=n.vertexCount,f=n.isUseIndices(),d=s.getExtension(t,"OES_element_index_uint"),_=d&&h>65535,p=_?t.UNSIGNED_INT:t.UNSIGNED_SHORT,m=s.getExtension(t,"OES_vertex_array_object"),v=!n.dynamic,g=this._renderInfo;g.vertexCount=h,g.triangleCount=0,g.drawCallCount=0;var y=!1;if(i=t.__GLID__+"-"+n.__GUID__+"-"+e.__GUID__,i!==u?y=!0:(h>65535&&!d&&f||m&&v||n._cache.isDirty())&&(y=!0),u=i,y){var x=this._drawCache[i];if(!x){var T=n.getBufferChunks(t);if(!T)return;x=[];for(var E=0;Eh)){var f=Math.sqrt(h-c),d=u-f,_=u+f;return i||(i=new r),d<0?_<0?null:(a.scaleAndAdd(i._array,o,s,_),i):(a.scaleAndAdd(i._array,o,s,d),i)}}}(),intersectBoundingBox:function(t,e){var n,i,o,s,u,l,c=this.direction._array,h=this.origin._array,f=t.min._array,d=t.max._array,_=1/c[0],p=1/c[1],m=1/c[2];if(_>=0?(n=(f[0]-h[0])*_,i=(d[0]-h[0])*_):(i=(f[0]-h[0])*_,n=(d[0]-h[0])*_),p>=0?(o=(f[1]-h[1])*p,s=(d[1]-h[1])*p):(s=(f[1]-h[1])*p,o=(d[1]-h[1])*p),n>s||o>i)return null;if((o>n||n!==n)&&(n=o),(s=0?(u=(f[2]-h[2])*m,l=(d[2]-h[2])*m):(l=(f[2]-h[2])*m,u=(d[2]-h[2])*m),n>l||u>i)return null;if((u>n||n!==n)&&(n=u),(l=0?n:i;return e||(e=new r),a.scaleAndAdd(e._array,h,c,v),e},intersectTriangle:function(){var t=a.create(),e=a.create(),n=a.create(),i=a.create();return function(s,u,l,c,h,f){var d=this.direction._array,_=this.origin._array;s=s._array,u=u._array,l=l._array,a.sub(t,u,s),a.sub(e,l,s),a.cross(i,e,d);var p=a.dot(t,i);if(c){if(p>-o)return null}else if(p>-o&&p1)return null;a.cross(i,t,n);var v=a.dot(d,i)/p;if(v<0||v>1||m+v>1)return null;a.cross(i,t,e);var g=-a.dot(n,i)/p;return g<0?null:(h||(h=new r),f&&r.set(f,1-m-v,m,v),a.scaleAndAdd(h._array,_,d,g),h)}}(),applyTransform:function(t){r.add(this.direction,this.direction,this.origin),r.transformMat4(this.origin,this.origin,t),r.transformMat4(this.direction,this.direction,t),r.sub(this.direction,this.direction,this.origin),r.normalize(this.direction,this.direction)},copy:function(t){r.copy(this.origin,t.origin),r.copy(this.direction,t.direction)},clone:function(){var t=new s;return t.copy(this),t}},t.exports=s},function(t,e,n){var r=n(2),i=n(3),a=n(22),o=n(14),s=["px","nx","py","ny","pz","nz"],u=r.extend(function(){var t={position:new i,far:1e3,near:.1,texture:null,shadowMapPass:null},e=t._cameras={px:new a({fov:90}),nx:new a({fov:90}),py:new a({fov:90}),ny:new a({fov:90}),pz:new a({fov:90}),nz:new a({fov:90})};return e.px.lookAt(i.POSITIVE_X,i.NEGATIVE_Y),e.nx.lookAt(i.NEGATIVE_X,i.NEGATIVE_Y),e.py.lookAt(i.POSITIVE_Y,i.POSITIVE_Z),e.ny.lookAt(i.NEGATIVE_Y,i.NEGATIVE_Z),e.pz.lookAt(i.POSITIVE_Z,i.NEGATIVE_Y),e.nz.lookAt(i.NEGATIVE_Z,i.NEGATIVE_Y),t._frameBuffer=new o,t},{getCamera:function(t){return this._cameras[t]},render:function(t,e,n){var r=t.gl;n||e.update();for(var a=this.texture.width,o=2*Math.atan(a/(a-.5))/Math.PI*180,u=0;u<6;u++){var l=s[u],c=this._cameras[l];if(i.copy(c.position,this.position),c.far=this.far,c.near=this.near,c.fov=o,this.shadowMapPass){c.update();var h=e.getBoundingBox(function(t){return!t.invisible});h.applyTransform(c.viewMatrix),e.viewBoundingBoxLastFrame.copy(h),this.shadowMapPass.render(t,e,c,!0)}this._frameBuffer.attach(this.texture,r.COLOR_ATTACHMENT0,r.TEXTURE_CUBE_MAP_POSITIVE_X+u),this._frameBuffer.bind(t),t.render(e,c,!0),this._frameBuffer.unbind(t)}},dispose:function(t){this._frameBuffer.dispose(t)}});t.exports=u},function(t,e,n){"use strict";var r=n(19),i=n(10),a=n(47),o=n(31),s=n(1),u=s.vec3,l=s.vec4,c=r.extend(function(){return{projectionMatrix:new i,invProjectionMatrix:new i,viewMatrix:new i,frustum:new a}},function(){this.update(!0)},{update:function(t){r.prototype.update.call(this,t),i.invert(this.viewMatrix,this.worldTransform),this.updateProjectionMatrix(),i.invert(this.invProjectionMatrix,this.projectionMatrix),this.frustum.setFromProjection(this.projectionMatrix)},setViewMatrix:function(t){i.invert(this.worldTransform,t),this.decomposeWorldTransform()},decomposeProjectionMatrix:function(){},setProjectionMatrix:function(t){i.copy(this.projectionMatrix,t),i.invert(this.invProjectionMatrix,t),this.decomposeProjectionMatrix()},updateProjectionMatrix:function(){},castRay:function(){var t=l.create();return function(e,n){var r=void 0!==n?n:new o,i=e._array[0],a=e._array[1];return l.set(t,i,a,-1,1),l.transformMat4(t,t,this.invProjectionMatrix._array),l.transformMat4(t,t,this.worldTransform._array),u.scale(r.origin._array,t,1/t[3]),l.set(t,i,a,1,1),l.transformMat4(t,t,this.invProjectionMatrix._array),l.transformMat4(t,t,this.worldTransform._array),u.scale(t,t,1/t[3]),u.sub(r.direction._array,t,r.origin._array),u.normalize(r.direction._array,r.direction._array),r.direction._dirty=!0,r.origin._dirty=!0,r}}()});t.exports=c},function(t,e,n){"use strict";var r=n(2),i=r.extend({name:"",index:-1,parentIndex:-1,node:null,rootNode:null});t.exports=i},function(t,e,n){"use strict";var r=n(2),i=n(16),a=n(5),o=(n(13),n(9)),s=n(10),u=n(28),l=n(6),c=n(27),h=n(4);h["import"](n(76));var f=n(1),d=f.mat4,_=f.vec3,p=d.create,m=0,v={},g=r.extend(function(){return{canvas:null,_width:100,_height:100,devicePixelRatio:window.devicePixelRatio||1,clearColor:[0,0,0,0],clearBit:17664,alpha:!0,depth:!0,stencil:!1,antialias:!0,premultipliedAlpha:!0,preserveDrawingBuffer:!1,throwError:!0,gl:null,viewport:{},__currentFrameBuffer:null,_viewportStack:[],_clearStack:[],_sceneRendering:null}},function(){this.canvas||(this.canvas=document.createElement("canvas"));var t=this.canvas;try{var e={alpha:this.alpha,depth:this.depth,stencil:this.stencil,antialias:this.antialias,premultipliedAlpha:this.premultipliedAlpha,preserveDrawingBuffer:this.preserveDrawingBuffer};if(this.gl=t.getContext("webgl",e)||t.getContext("experimental-webgl",e),!this.gl)throw new Error;null==this.gl.__GLID__&&(this.gl.__GLID__=m++,i.initialize(this.gl)),this.resize()}catch(n){throw"Error creating WebGL Context "+n}},{resize:function(t,e){var n=this.canvas,r=this.devicePixelRatio;null!=t?(n.style.width=t+"px",n.style.height=e+"px",n.width=t*r,n.height=e*r,this._width=t,this._height=e):(this._width=n.width/r,this._height=n.height/r),this.setViewport(0,0,this._width,this._height)},getWidth:function(){return this._width},getHeight:function(){return this._height},getViewportAspect:function(){var t=this.viewport;return t.width/t.height},setDevicePixelRatio:function(t){this.devicePixelRatio=t,this.resize(this._width,this._height)},getDevicePixelRatio:function(){return this.devicePixelRatio},getExtension:function(t){return i.getExtension(this.gl,t)},setViewport:function(t,e,n,r,i){if("object"==typeof t){var a=t;t=a.x,e=a.y,n=a.width,r=a.height,i=a.devicePixelRatio}i=i||this.devicePixelRatio,this.gl.viewport(t*i,e*i,n*i,r*i),this.viewport={x:t,y:e,width:n,height:r,devicePixelRatio:i}},saveViewport:function(){this._viewportStack.push(this.viewport)},restoreViewport:function(){this._viewportStack.length>0&&this.setViewport(this._viewportStack.pop())},saveClear:function(){this._clearStack.push({clearBit:this.clearBit,clearColor:this.clearColor})},restoreClear:function(){if(this._clearStack.length>0){var t=this._clearStack.pop();this.clearColor=t.clearColor,this.clearBit=t.clearBit}},bindSceneRendering:function(t){this._sceneRendering=t},beforeRenderObject:function(){},afterRenderObject:function(){},render:function(t,e,n,r){var i=this.gl;this._sceneRendering=t;var a=this.clearColor;if(this.clearBit){i.colorMask(!0,!0,!0,!0),i.depthMask(!0);var o=this.viewport,s=!1,u=o.devicePixelRatio;(o.width!==this._width||o.height!==this._height||u&&u!==this.devicePixelRatio||o.x||o.y)&&(s=!0,i.enable(i.SCISSOR_TEST),i.scissor(o.x*u,o.y*u,o.width*u,o.height*u)),i.clearColor(a[0],a[1],a[2],a[3]),i.clear(this.clearBit),s&&i.disable(i.SCISSOR_TEST)}n||t.update(!1),e.getScene()||e.update(!0);var l=t.opaqueQueue,c=t.transparentQueue,h=t.material;if(t.trigger("beforerender",this,t,e),c.length>0)for(var f=p(),m=_.create(),v=0;v0&&t.min._array[2]<0&&(t.max._array[2]=-1e-20),t.applyProjection(e);var u=t.min._array,l=t.max._array;if(l[0]<-1||u[0]>1||l[1]<-1||u[1]>1||l[2]<-1||u[2]>1)return!0}return!1}}(),disposeScene:function(t){this.disposeNode(t,!0,!0),t.dispose()},disposeNode:function(t,e,n){var r={},i=this.gl;t.getParent()&&t.getParent().remove(t),t.traverse(function(t){t.geometry&&e&&t.geometry.dispose(i),t.material&&(r[t.material.__GUID__]=t.material),t.dispose&&t.dispose(i)});for(var a in r){var o=r[a];o.dispose(i,n)}},disposeShader:function(t){t.dispose(this.gl)},disposeGeometry:function(t){t.dispose(this.gl)},disposeTexture:function(t){t.dispose(this.gl)},disposeFrameBuffer:function(t){t.dispose(this.gl)},dispose:function(){i.dispose(this.gl)},screenToNdc:function(t,e,n){n||(n=new c),e=this._height-e;var r=this.viewport,i=n._array;return i[0]=(t-r.x)/r.width,i[0]=2*i[0]-1,i[1]=(e-r.y)/r.height,i[1]=2*i[1]-1,n}});g.opaqueSortFunc=g.prototype.opaqueSortFunc=function(t,e){return t.renderOrder===e.renderOrder?t.material.shader===e.material.shader?t.material===e.material?t.geometry.__GUID__-e.geometry.__GUID__:t.material.__GUID__-e.material.__GUID__:t.material.shader.__GUID__-e.material.shader.__GUID__:t.renderOrder-e.renderOrder},g.transparentSortFunc=g.prototype.transparentSortFunc=function(t,e){return t.renderOrder===e.renderOrder?t.__depth===e.__depth?t.material.shader===e.material.shader?t.material===e.material?t.geometry.__GUID__-e.geometry.__GUID__:t.material.__GUID__-e.material.__GUID__:t.material.shader.__GUID__-e.material.shader.__GUID__:t.__depth-e.__depth:t.renderOrder-e.renderOrder};var y={WORLD:p(),VIEW:p(),PROJECTION:p(),WORLDVIEW:p(),VIEWPROJECTION:p(),WORLDVIEWPROJECTION:p(),WORLDINVERSE:p(),VIEWINVERSE:p(),PROJECTIONINVERSE:p(),WORLDVIEWINVERSE:p(),VIEWPROJECTIONINVERSE:p(),WORLDVIEWPROJECTIONINVERSE:p(),WORLDTRANSPOSE:p(),VIEWTRANSPOSE:p(),PROJECTIONTRANSPOSE:p(),WORLDVIEWTRANSPOSE:p(),VIEWPROJECTIONTRANSPOSE:p(),WORLDVIEWPROJECTIONTRANSPOSE:p(),WORLDINVERSETRANSPOSE:p(),VIEWINVERSETRANSPOSE:p(),PROJECTIONINVERSETRANSPOSE:p(),WORLDVIEWINVERSETRANSPOSE:p(),VIEWPROJECTIONINVERSETRANSPOSE:p(),WORLDVIEWPROJECTIONINVERSETRANSPOSE:p()};g.COLOR_BUFFER_BIT=a.COLOR_BUFFER_BIT,g.DEPTH_BUFFER_BIT=a.DEPTH_BUFFER_BIT,g.STENCIL_BUFFER_BIT=a.STENCIL_BUFFER_BIT,t.exports=g},function(t,e,n){"use strict";function r(t,e,n){for(var r=e,i=0;i=t.time)return this.keyFrames.splice(e,0,t),e}this.life=t.time,this.keyFrames.push(t)},u.prototype.addKeyFrames=function(t){for(var e=0;ei[i.length-1].time)){if(t=a-1?a-1:this._cacheKey+1,l=u;l>=0;l--)if(i[l].time<=t&&i[l][e])n=i[l],this._cacheKey=l,this._cacheTime=t;else if(i[l][e]){r=i[l];break}}else for(var l=this._cacheKey;l65535?Uint32Array:Uint16Array,p=this.indices=new _(e*t*6),m=this.radius,v=this.phiStart,g=this.phiLength,y=this.thetaStart,x=this.thetaLength,m=this.radius,T=[],E=[],b=0,S=1/m;for(d=0;d<=t;d++)for(f=0;f<=e;f++)c=f/e,h=d/t,s=-m*Math.cos(v+c*g)*Math.sin(y+h*x),u=m*Math.cos(y+h*x),l=m*Math.sin(v+c*g)*Math.sin(y+h*x),T[0]=s,T[1]=u,T[2]=l,E[0]=c,E[1]=h,n.set(b,T),r.set(b,E),T[0]*=S,T[1]*=S,T[2]*=S,i.set(b,T),b++;var N,A,w,M,C=e+1,R=0;for(d=0;dthis.distance,i=1;i<8;i++)if(a.dot(e[i]._array,n)>this.distance!=r)return!0},intersectLine:function(){var t=a.create();return function(e,n,i){var o=this.distanceToPoint(e),s=this.distanceToPoint(n);if(o>0&&s>0||o<0&&s<0)return null;var u=this.normal._array,l=this.distance,c=e._array;a.sub(t,n._array,e._array),a.normalize(t,t);var h=a.dot(u,t);if(0===h)return null;i||(i=new r);var f=(a.dot(u,c)-l)/h;return a.scaleAndAdd(i._array,c,t,-f),i._dirty=!0,i}}(),applyTransform:function(){var t=o.create(),e=s.create(),n=s.create();return n[3]=1,function(r){r=r._array,a.scale(n,this.normal._array,this.distance),s.transformMat4(n,n,r),this.distance=a.dot(n,this.normal._array),o.invert(t,r),o.transpose(t,t),e[3]=0,a.copy(e,this.normal._array),s.transformMat4(e,e,t),a.copy(this.normal._array,e)}}(),copy:function(t){a.copy(this.normal._array,t.normal._array),this.normal._dirty=!0,this.distance=t.distance},clone:function(){var t=new u;return t.copy(this),t}},t.exports=u},function(t,e,n){"use strict";var r=n(1),i=r.quat,a=function(t,e,n,r){t=t||0,e=e||0,n=n||0,r=void 0===r?1:r,this._array=i.fromValues(t,e,n,r),this._dirty=!0};a.prototype={constructor:a,add:function(t){return i.add(this._array,this._array,t._array),this._dirty=!0,this},calculateW:function(){return i.calculateW(this._array,this._array),this._dirty=!0,this},set:function(t,e,n,r){return this._array[0]=t,this._array[1]=e,this._array[2]=n,this._array[3]=r,this._dirty=!0,this},setArray:function(t){return this._array[0]=t[0],this._array[1]=t[1],this._array[2]=t[2],this._array[3]=t[3],this._dirty=!0,this},clone:function(){return new a(this.x,this.y,this.z,this.w)},conjugate:function(){return i.conjugate(this._array,this._array),this._dirty=!0,this},copy:function(t){return i.copy(this._array,t._array),this._dirty=!0,this},dot:function(t){return i.dot(this._array,t._array)},fromMat3:function(t){return i.fromMat3(this._array,t._array),this._dirty=!0,this},fromMat4:function(){var t=r.mat3,e=t.create();return function(n){return t.fromMat4(e,n._array),t.transpose(e,e),i.fromMat3(this._array,e),this._dirty=!0,this}}(),identity:function(){return i.identity(this._array),this._dirty=!0,this},invert:function(){return i.invert(this._array,this._array),this._dirty=!0,this},len:function(){return i.len(this._array)},length:function(){return i.length(this._array)},lerp:function(t,e,n){return i.lerp(this._array,t._array,e._array,n),this._dirty=!0,this},mul:function(t){return i.mul(this._array,this._array,t._array),this._dirty=!0,this},mulLeft:function(t){return i.multiply(this._array,t._array,this._array),this._dirty=!0,this},multiply:function(t){return i.multiply(this._array,this._array,t._array),this._dirty=!0,this},multiplyLeft:function(t){return i.multiply(this._array,t._array,this._array),this._dirty=!0,this},normalize:function(){return i.normalize(this._array,this._array),this._dirty=!0,this},rotateX:function(t){return i.rotateX(this._array,this._array,t),this._dirty=!0,this},rotateY:function(t){return i.rotateY(this._array,this._array,t),this._dirty=!0,this},rotateZ:function(t){return i.rotateZ(this._array,this._array,t),this._dirty=!0,this},rotationTo:function(t,e){return i.rotationTo(this._array,t._array,e._array),this._dirty=!0,this},setAxes:function(t,e,n){return i.setAxes(this._array,t._array,e._array,n._array),this._dirty=!0,this},setAxisAngle:function(t,e){return i.setAxisAngle(this._array,t._array,e),this._dirty=!0,this},slerp:function(t,e,n){return i.slerp(this._array,t._array,e._array,n),this._dirty=!0,this},sqrLen:function(){return i.sqrLen(this._array)},squaredLength:function(){return i.squaredLength(this._array)},fromEuler:function(t,e){return a.fromEuler(this,t,e)},toString:function(){return"["+Array.prototype.join.call(this._array,",")+"]"},toArray:function(){return Array.prototype.slice.call(this._array)}};var o=Object.defineProperty;if(o){var s=a.prototype;o(s,"x",{get:function(){return this._array[0]},set:function(t){this._array[0]=t,this._dirty=!0}}),o(s,"y",{get:function(){return this._array[1]},set:function(t){this._array[1]=t,this._dirty=!0}}),o(s,"z",{get:function(){return this._array[2]},set:function(t){this._array[2]=t,this._dirty=!0}}),o(s,"w",{get:function(){return this._array[3]},set:function(t){this._array[3]=t,this._dirty=!0}})}a.add=function(t,e,n){return i.add(t._array,e._array,n._array),t._dirty=!0,t},a.set=function(t,e,n,r,a){i.set(t._array,e,n,r,a),t._dirty=!0},a.copy=function(t,e){return i.copy(t._array,e._array),t._dirty=!0,t},a.calculateW=function(t,e){return i.calculateW(t._array,e._array),t._dirty=!0,t},a.conjugate=function(t,e){return i.conjugate(t._array,e._array),t._dirty=!0,t},a.identity=function(t){return i.identity(t._array),t._dirty=!0,t},a.invert=function(t,e){return i.invert(t._array,e._array),t._dirty=!0,t},a.dot=function(t,e){return i.dot(t._array,e._array)},a.len=function(t){return i.length(t._array)},a.lerp=function(t,e,n,r){return i.lerp(t._array,e._array,n._array,r),t._dirty=!0,t},a.slerp=function(t,e,n,r){return i.slerp(t._array,e._array,n._array,r),t._dirty=!0,t},a.mul=function(t,e,n){return i.multiply(t._array,e._array,n._array),t._dirty=!0,t},a.multiply=a.mul,a.rotateX=function(t,e,n){return i.rotateX(t._array,e._array,n),t._dirty=!0,t},a.rotateY=function(t,e,n){return i.rotateY(t._array,e._array,n),t._dirty=!0,t},a.rotateZ=function(t,e,n){return i.rotateZ(t._array,e._array,n),t._dirty=!0,t},a.setAxisAngle=function(t,e,n){return i.setAxisAngle(t._array,e._array,n),t._dirty=!0,t},a.normalize=function(t,e){return i.normalize(t._array,e._array),t._dirty=!0,t},a.sqrLen=function(t){return i.sqrLen(t._array)},a.squaredLength=a.sqrLen,a.fromMat3=function(t,e){return i.fromMat3(t._array,e._array),t._dirty=!0,t},a.setAxes=function(t,e,n,r){return i.setAxes(t._array,e._array,n._array,r._array),t._dirty=!0,t},a.rotationTo=function(t,e,n){return i.rotationTo(t._array,e._array,n._array),t._dirty=!0,t},a.fromEuler=function(t,e,n){t._dirty=!0,e=e._array;var r=t._array,i=Math.cos(e[0]/2),a=Math.cos(e[1]/2),o=Math.cos(e[2]/2),s=Math.sin(e[0]/2),u=Math.sin(e[1]/2),l=Math.sin(e[2]/2),n=(n||"XYZ").toUpperCase();switch(n){case"XYZ":r[0]=s*a*o+i*u*l,r[1]=i*u*o-s*a*l,r[2]=i*a*l+s*u*o,r[3]=i*a*o-s*u*l;break;case"YXZ":r[0]=s*a*o+i*u*l,r[1]=i*u*o-s*a*l,r[2]=i*a*l-s*u*o,r[3]=i*a*o+s*u*l;break;case"ZXY":r[0]=s*a*o-i*u*l,r[1]=i*u*o+s*a*l,r[2]=i*a*l+s*u*o,r[3]=i*a*o-s*u*l;break;case"ZYX":r[0]=s*a*o-i*u*l,r[1]=i*u*o+s*a*l,r[2]=i*a*l-s*u*o,r[3]=i*a*o+s*u*l;break;case"YZX":r[0]=s*a*o+i*u*l,r[1]=i*u*o+s*a*l,r[2]=i*a*l-s*u*o,r[3]=i*a*o-s*u*l;break;case"XZY":r[0]=s*a*o-i*u*l,r[1]=i*u*o-s*a*l,r[2]=i*a*l+s*u*o,r[3]=i*a*o+s*u*l}},t.exports=a},function(t,e){var n={};n.isPowerOfTwo=function(t){return 0===(t&t-1)},n.nextPowerOfTwo=function(t){return t--,t|=t>>1,t|=t>>2,t|=t>>4,t|=t>>8,t|=t>>16,t++,t},n.nearestPowerOfTwo=function(t){return Math.pow(2,Math.round(Math.log(t)/Math.LN2))},t.exports=n},function(t,e,n){var r=n(15),i=n(68),a=n(4),o=n(6);a["import"](n(78));var s=r.extend(function(){var t=new a({vertex:a.source("qtek.skybox.vertex"),fragment:a.source("qtek.skybox.fragment") +}),e=new o({shader:t,depthMask:!1});return{scene:null,geometry:new i,material:e,environmentMap:null,culling:!1}},function(){var t=this.scene;t&&this.attachScene(t),this.environmentMap&&this.setEnvironmentMap(this.environmentMap)},{attachScene:function(t){this.scene&&this.detachScene(),this.scene=t,t.on("beforerender",this._beforeRenderScene,this)},detachScene:function(){this.scene&&this.scene.off("beforerender",this._beforeRenderScene,this),this.scene=null},dispose:function(t){this.detachScene(),this.geometry.dispose(t),this.material.dispose(t)},setEnvironmentMap:function(t){this.material.set("environmentMap",t)},_beforeRenderScene:function(t,e,n){this.renderSkybox(t,n)},renderSkybox:function(t,e){this.position.copy(e.getWorldPosition()),this.update(),t.gl.disable(t.gl.BLEND),t.renderQueue([this],e)}});t.exports=s},function(t,e,n){var r=n(15),i=n(43),a=n(4),o=n(6);a["import"](n(75));var s=r.extend(function(){var t=new a({vertex:a.source("qtek.basic.vertex"),fragment:a.source("qtek.basic.fragment")});t.enableTexture("diffuseMap");var e=new o({shader:t,depthMask:!1});return{scene:null,geometry:new i({widthSegments:30,heightSegments:30}),material:e,environmentMap:null,culling:!1}},function(){var t=this.scene;t&&this.attachScene(t),this.environmentMap&&this.setEnvironmentMap(this.environmentMap)},{attachScene:function(t){this.scene&&this.detachScene(),this.scene=t,t.on("beforerender",this._beforeRenderScene,this)},detachScene:function(){this.scene&&this.scene.off("beforerender",this._beforeRenderScene,this),this.scene=null},_beforeRenderScene:function(t,e,n){this.position.copy(n.getWorldPosition()),this.update(),t.renderQueue([this],n)},setEnvironmentMap:function(t){this.material.set("diffuseMap",t)},dispose:function(t){this.detachScene(),this.geometry.dispose(t),this.material.dispose(t)}});t.exports=s},function(t,e,n){"use strict";var r=n(8),i=n(18),a=n(26),o=n(32),s=n(52),u=n(24),l=n(82),c=n(84),h={loadTexture:function(t,e,n,a){var o;if("function"==typeof e?(n=e,a=n,e={}):e=e||{},"string"==typeof t){if(t.match(/.hdr$/))return o=new r({width:0,height:0}),h._fetchTexture(t,function(t){c.parseRGBE(t,o,e.exposure),o.dirty(),n&&n(o)},a),o;t.match(/.dds$/)?(o=new r({width:0,height:0}),h._fetchTexture(t,function(t){l.parse(t,o),o.dirty(),n&&n(o)},a)):(o=new r,o.load(t),o.success(n),o.error(a))}else if("object"==typeof t&&"undefined"!=typeof t.px){var o=new i;o.load(t),o.success(n),o.error(a)}return o},loadPanorama:function(t,e,n,r,i,a){var o=this;"function"==typeof r?(i=r,a=i,r={}):r=r||{},h.loadTexture(e,r,function(e){e.flipY=r.flipY||!1,o.panoramaToCubeMap(t,e,n,r),e.dispose(t.gl),i&&i(n)},a)},panoramaToCubeMap:function(t,e,n,r){var i=new o,a=new s({scene:new u});return a.material.set("diffuseMap",e),r=r||{},r.encodeRGBM&&a.material.shader.define("fragment","RGBM_ENCODE"),i.texture=n,i.render(t,a.scene),i.texture=null,i.dispose(t),n},_fetchTexture:function(t,e,n){a.get({url:t,responseType:"arraybuffer",onload:e,onerror:n})},createChessboard:function(t,e,n,i){t=t||512,e=e||64,n=n||"black",i=i||"white";var a=Math.ceil(t/e),o=document.createElement("canvas");o.width=t,o.height=t;var s=o.getContext("2d");s.fillStyle=i,s.fillRect(0,0,t,t),s.fillStyle=n;for(var u=0;u=0){var r=t[n.parentIndex].node;r.add(n.node)}else this.roots.push(n)}},addClip:function(t,e){for(var n=0;n0&&this._clips.splice(e,1)},removeClipsAll:function(){this._clips=[]},getClip:function(t){if(this._clips[t])return this._clips[t].clip},getClipNumber:function(){return this._clips.length},updateJointMatrices:function(){var t=u.create();return function(){for(var e=0;ei;if(a)t.length=i;else for(var o=r;o=0&&!(E[A]<=e);A--);A=Math.min(A,v-2)}else{for(A=O;Ae);A++);A=Math.min(A-1,v-2)}O=A,k=e;var n=E[A+1]-E[A];0!==n&&(R=(e-E[A])/n,m?(I=b[A],L=b[0===A?A:A-1],P=b[A>v-2?v-1:A+1],D=b[A>v-3?v-1:A+2],u?_(t,i,u(d(t,i),L,I,P,D,R)):y?l(L,I,P,D,R,R*R,R*R*R,d(t,i),x):_(t,i,c(L,I,P,D,R,R*R,R*R*R))):u?_(t,i,u(d(t,i),b[A],b[A+1],R)):y?o(b[A],b[A+1],R,d(t,i),x):_(t,i,a(b[A],b[A+1],R)))},B=new p({target:t._target,life:T,loop:t._loop,delay:t._delay,onframe:F,onfinish:n});return e&&"spline"!==e&&B.setEasing(e),B}}}function _(t,e,n,a,o){this._tracks={},this._target=t,this._loop=e||!1,this._getter=n||r,this._setter=a||i,this._interpolater=o||null,this._delay=0,this._doneList=[],this._onframeList=[],this._clipList=[]}var p=n(20),m=Array.prototype.slice;_.prototype={constructor:_,when:function(t,e){for(var n in e)this._tracks[n]||(this._tracks[n]=[],0!==t&&this._tracks[n].push({time:0,value:u(this._getter(this._target,n))})),this._tracks[n].push({time:parseInt(t),value:e[n]});return this},during:function(t){return this._onframeList.push(t),this},_doneCallback:function(){this._tracks={},this._clipList.length=0;for(var t=this._doneList,e=t.length,n=0;n1e-6?(o=Math.acos(s),u=Math.sin(o),l=Math.sin((1-r)*o)/u,c=Math.sin(r*o)/u):(l=1-r,c=r),t[0]=l*h+c*p,t[1]=l*f+c*m,t[2]=l*d+c*v,t[3]=l*_+c*g,t}var a=n(20),o=n(37),s=n(1),u=s.quat,l=s.vec3,c=function(t){t=t||{},a.call(this,t),this.position=l.create(),this.rotation=u.create(),this.scale=l.fromValues(1,1,1),this.channels={time:null,position:null,rotation:null,scale:null},this._cacheKey=0,this._cacheTime=0};c.prototype=Object.create(a.prototype),c.prototype.constructor=c,c.prototype.step=function(t){var e=a.prototype.step.call(this,t);return"finish"!==e&&this.setTime(this._elapsedTime),e},c.prototype.setTime=function(t){if(this.channels.time){var e=this.channels,n=e.time.length,a=-1;if(t=0;s--)if(e.time[s-1]<=t&&e.time[s]>t){a=s-1;break}}else for(var s=this._cacheKey;st){a=s;break}if(a>-1){this._cacheKey=a,this._cacheTime=t;var u=a,l=a+1,c=e.time[u],h=e.time[l],f=(t-c)/(h-c);e.rotation&&i(this.rotation,e.rotation,e.rotation,f,4*u,4*l),e.position&&r(this.position,e.position,e.position,f,3*u,3*l),e.scale&&r(this.scale,e.scale,e.scale,f,3*u,3*l)}a==n-2&&(this._cacheKey=0,this._cacheTime=0)}},c.prototype.getSubClip=function(t,e){var n=new c({name:this.name}),r=this.channels.time[0];t=Math.min(Math.max(t,r),this.life),e=Math.min(Math.max(e,r),this.life);var i=this._findRange(t),a=this._findRange(e),o=a[0]-i[0]+1;0===i[1]&&0===a[1]&&(o-=1),this.channels.rotation&&(n.channels.rotation=new Float32Array(4*o)),this.channels.position&&(n.channels.position=new Float32Array(3*o)),this.channels.scale&&(n.channels.scale=new Float32Array(3*o)),this.channels.time&&(n.channels.time=new Float32Array(o)),this.setTime(t);for(var s=0;s<3;s++)n.channels.rotation[s]=this.rotation[s],n.channels.position[s]=this.position[s],n.channels.scale[s]=this.scale[s];n.channels.time[0]=0,n.channels.rotation[3]=this.rotation[3];for(var s=1;st&&(r=i);var a=0;if(r>=0)var o=e.time[r],s=e.time[r+1],a=(t-o)/(s-o);return[r,a]},c.prototype.blend1D=o.prototype.blend1D,c.prototype.blend2D=o.prototype.blend2D,c.prototype.additiveBlend=o.prototype.additiveBlend,c.prototype.subtractiveBlend=o.prototype.subtractiveBlend,c.prototype.clone=function(){var t=a.prototype.clone.call(this);return t.channels={time:this.channels.time||null,position:this.channels.position||null,rotation:this.channels.rotation||null,scale:this.channels.scale||null},l.copy(t.position,this.position),u.copy(t.rotation,this.rotation),l.copy(t.scale,this.scale),t},t.exports=c},function(t,e,n){"use strict";var r=n(20),i=n(37),a=n(1),o=a.quat,s=a.vec3,u=function(t){if(t=t||{},r.call(this,t),this.jointClips=[],this.life=0,t.jointClips&&t.jointClips.length>0)for(var e=0;e0){var e=this.outputs[t];e.keepLastFrame?(this._prevOutputTextures[t]&&this._compositor.releaseTexture(this._prevOutputTextures[t]),this._prevOutputTextures[t]=this._outputTextures[t]):this._compositor.releaseTexture(this._outputTextures[t])}}});t.exports=a},function(t,e,n){"use strict";var r=n(2),i=n(23),a=r.extend(function(){return{nodes:[]}},{dirty:function(){this._dirty=!0},addNode:function(t){this.nodes.indexOf(t)>=0||(this.nodes.push(t),this._dirty=!0)},removeNode:function(t){"string"==typeof t&&(t=this.getNodeByName(t));var e=this.nodes.indexOf(t);e>=0&&(this.nodes.splice(e,1),this._dirty=!0)},getNodeByName:function(t){for(var e=0;e=e.COLOR_ATTACHMENT0&&c<=e.COLOR_ATTACHMENT0+8&&f.push(c);h.drawBuffersEXT(f)}t.saveClear(),t.clearBit=a.DEPTH_BUFFER_BIT|a.COLOR_BUFFER_BIT,n=t.render(this.scene,this.camera,!this.autoUpdateScene,this.preZ),t.restoreClear(),r.unbind(t)}else n=t.render(this.scene,this.camera,!this.autoUpdateScene,this.preZ);this.trigger("afterrender",n),this._rendering=!1,this._rendered=!0}});t.exports=s},function(t,e,n){"use strict";var r=n(23),i=r.extend(function(){return{texture:null,outputs:{color:{}}}},function(){},{getOutput:function(t,e){return this.texture},beforeFrame:function(){},afterFrame:function(){}});t.exports=i},function(t,e){"use strict";function n(t,e,n){"object"==typeof e&&(n=e,e=null);var a,o=this;if(!(t instanceof Function)){a=[];for(var s in t)t.hasOwnProperty(s)&&a.push(s)}var u=function(e){if(o.apply(this,arguments),t instanceof Function?r(this,t.call(this,e)):i(this,t,a),this.constructor===u)for(var n=u.__initializers__,s=0;s0&&(r.shader.define("vertex","SKINNING"),r.shader.define("vertex","JOINT_COUNT",t),i.shader.define("vertex","SKINNING"),i.shader.define("vertex","JOINT_COUNT",t)),n={material1:r,material2:i},e[t]=n}return n.used=!0,n},_resetGBufferMaterials:function(){for(var t in this._gBufferMaterials)this._gBufferMaterials[t].used=!1},_cleanGBufferMaterials:function(t){for(var e in this._gBufferMaterials){var n=this._gBufferMaterials[e];n.used||(n.material1.dispose(t),n.material2.dispose(t))}},_replaceGBufferMat:function(t,e){for(var n=0;n 0.01)\n {\n gl_FragColor.rgb = gl_FragColor.rgb * mix(lineColor, vec3(1.0), edgeFactor(lineWidth));\n }\n\n#ifdef GAMMA_ENCODE\n gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(1 / 2.2));\n#endif\n\n gl_FragColor = encodeHDR(gl_FragColor);\n\n}\n\n@end"},function(t,e,n){var r="uniform vec3 ",i="uniform float ",a="@export qtek.header.",o="@end",s=":unconfigurable;";t.exports=[a+"directional_light",r+"directionalLightDirection[DIRECTIONAL_LIGHT_COUNT]"+s,r+"directionalLightColor[DIRECTIONAL_LIGHT_COUNT]"+s,o,a+"ambient_light",r+"ambientLightColor[AMBIENT_LIGHT_COUNT]"+s,o,a+"ambient_sh_light",r+"ambientSHLightColor[AMBIENT_SH_LIGHT_COUNT]"+s,r+"ambientSHLightCoefficients[AMBIENT_SH_LIGHT_COUNT * 9]"+s,n(144),o,a+"ambient_cubemap_light",r+"ambientCubemapLightColor[AMBIENT_CUBEMAP_LIGHT_COUNT]"+s,"uniform samplerCube ambientCubemapLightCubemap[AMBIENT_CUBEMAP_LIGHT_COUNT]"+s,"uniform sampler2D ambientCubemapLightBRDFLookup[AMBIENT_CUBEMAP_LIGHT_COUNT]"+s,o,a+"point_light",r+"pointLightPosition[POINT_LIGHT_COUNT]"+s,i+"pointLightRange[POINT_LIGHT_COUNT]"+s,r+"pointLightColor[POINT_LIGHT_COUNT]"+s,o,a+"spot_light",r+"spotLightPosition[SPOT_LIGHT_COUNT]"+s,r+"spotLightDirection[SPOT_LIGHT_COUNT]"+s,i+"spotLightRange[SPOT_LIGHT_COUNT]"+s,i+"spotLightUmbraAngleCosine[SPOT_LIGHT_COUNT]"+s,i+"spotLightPenumbraAngleCosine[SPOT_LIGHT_COUNT]"+s,i+"spotLightFalloffFactor[SPOT_LIGHT_COUNT]"+s,r+"spotLightColor[SPOT_LIGHT_COUNT]"+s,o].join("\n")},function(t,e){t.exports="@export qtek.prez.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_COUNT] : SKIN_MATRIX;\n#endif\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n\n#ifdef SKINNING\n\n @import qtek.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n}\n\n@end\n\n\n@export qtek.prez.fragment\n\nvoid main()\n{\n gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\n}\n\n@end"},function(t,e){t.exports="@export qtek.skybox.vertex\n\nuniform mat4 world : WORLD;\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\nvarying vec3 v_WorldPosition;\n\nvoid main()\n{\n v_WorldPosition = (world * vec4(position, 1.0)).xyz;\n gl_Position = worldViewProjection * vec4(position, 1.0);\n}\n\n@end\n\n@export qtek.skybox.fragment\n\nuniform mat4 viewInverse : VIEWINVERSE;\nuniform samplerCube environmentMap;\nuniform float lod: 0.0;\n\nvarying vec3 v_WorldPosition;\n\n@import qtek.util.rgbm\n\nvoid main()\n{\n vec3 eyePos = viewInverse[3].xyz;\n vec3 viewDirection = normalize(v_WorldPosition - eyePos);\n\n vec3 tex = decodeHDR(textureCubeLodEXT(environmentMap, viewDirection, lod)).rgb;\n\n#ifdef SRGB_DECODE\n tex.rgb = pow(tex.rgb, vec3(2.2));\n#endif\n\n gl_FragColor = encodeHDR(vec4(tex, 1.0));\n}\n@end"},function(t,e){t.exports="\n\n@export qtek.standard.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\n\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n\n#if defined(AOMAP_ENABLED)\nattribute vec2 texcoord2 : TEXCOORD_1;\n#endif\n\nattribute vec3 normal : NORMAL;\nattribute vec4 tangent : TANGENT;\n\n#ifdef VERTEX_COLOR\nattribute vec4 color : COLOR;\n#endif\n\nattribute vec3 barycentric;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_COUNT] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\n\n#ifdef NORMALMAP_ENABLED\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\n#ifdef VERTEX_COLOR\nvarying vec4 v_Color;\n#endif\n\n\n#if defined(AOMAP_ENABLED)\nvarying vec2 v_Texcoord2;\n#endif\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n#ifdef SKINNING\n\n @import qtek.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n skinnedTangent = (skinMatrixWS * vec4(tangent.xyz, 0.0)).xyz;\n#endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n v_Barycentric = barycentric;\n\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\n\n#ifdef NORMALMAP_ENABLED\n v_Tangent = normalize((worldInverseTranspose * vec4(skinnedTangent, 0.0)).xyz);\n v_Bitangent = normalize(cross(v_Normal, v_Tangent) * tangent.w);\n#endif\n\n#ifdef VERTEX_COLOR\n v_Color = color;\n#endif\n\n#if defined(AOMAP_ENABLED)\n v_Texcoord2 = texcoord2;\n#endif\n}\n\n@end\n\n\n@export qtek.standard.fragment\n\n#define PI 3.14159265358979\n\n#define GLOSS_CHANEL 0\n#define ROUGHNESS_CHANEL 0\n#define METALNESS_CHANEL 1\n\nuniform mat4 viewInverse : VIEWINVERSE;\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\n\n#ifdef NORMALMAP_ENABLED\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\nuniform sampler2D normalMap;\n#endif\n\n#ifdef DIFFUSEMAP_ENABLED\nuniform sampler2D diffuseMap;\n#endif\n\n#ifdef SPECULARMAP_ENABLED\nuniform sampler2D specularMap;\n#endif\n\n#ifdef USE_ROUGHNESS\nuniform float roughness : 0.5;\n #ifdef ROUGHNESSMAP_ENABLED\nuniform sampler2D roughnessMap;\n #endif\n#else\nuniform float glossiness: 0.5;\n #ifdef GLOSSMAP_ENABLED\nuniform sampler2D glossMap;\n #endif\n#endif\n\n#ifdef METALNESSMAP_ENABLED\nuniform sampler2D metalnessMap;\n#endif\n\n#ifdef ENVIRONMENTMAP_ENABLED\nuniform samplerCube environmentMap;\n\n #ifdef PARALLAX_CORRECTED\nuniform vec3 environmentBoxMin;\nuniform vec3 environmentBoxMax;\n #endif\n\n#endif\n\n#ifdef BRDFLOOKUP_ENABLED\nuniform sampler2D brdfLookup;\n#endif\n\n#ifdef EMISSIVEMAP_ENABLED\nuniform sampler2D emissiveMap;\n#endif\n\n#ifdef SSAOMAP_ENABLED\nuniform sampler2D ssaoMap;\nuniform vec4 viewport : VIEWPORT;\n#endif\n\n#ifdef AOMAP_ENABLED\nuniform sampler2D aoMap;\nuniform float aoIntensity;\nvarying vec2 v_Texcoord2;\n#endif\n\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\n\n\n#ifdef USE_METALNESS\nuniform float metalness : 0.0;\n#else\nuniform vec3 specularColor : [0.1, 0.1, 0.1];\n#endif\n\nuniform vec3 emission : [0.0, 0.0, 0.0];\n\nuniform float emissionIntensity: 1;\n\nuniform float lineWidth : 0.0;\nuniform vec3 lineColor : [0.0, 0.0, 0.0];\nvarying vec3 v_Barycentric;\n\n#ifdef ENVIRONMENTMAP_PREFILTER\nuniform float maxMipmapLevel: 5;\n#endif\n\n#ifdef AMBIENT_LIGHT_COUNT\n@import qtek.header.ambient_light\n#endif\n\n#ifdef AMBIENT_SH_LIGHT_COUNT\n@import qtek.header.ambient_sh_light\n#endif\n\n#ifdef AMBIENT_CUBEMAP_LIGHT_COUNT\n@import qtek.header.ambient_cubemap_light\n#endif\n\n#ifdef POINT_LIGHT_COUNT\n@import qtek.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_COUNT\n@import qtek.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_COUNT\n@import qtek.header.spot_light\n#endif\n\n@import qtek.util.calculate_attenuation\n\n@import qtek.util.edge_factor\n\n@import qtek.util.rgbm\n\n@import qtek.util.srgb\n\n@import qtek.plugin.compute_shadow_map\n\n@import qtek.util.parallax_correct\n\n\nfloat G_Smith(float g, float ndv, float ndl)\n{\n float roughness = 1.0 - g;\n float k = roughness * roughness / 2.0;\n float G1V = ndv / (ndv * (1.0 - k) + k);\n float G1L = ndl / (ndl * (1.0 - k) + k);\n return G1L * G1V;\n}\nvec3 F_Schlick(float ndv, vec3 spec) {\n return spec + (1.0 - spec) * pow(1.0 - ndv, 5.0);\n}\n\nfloat D_Phong(float g, float ndh) {\n float a = pow(8192.0, g);\n return (a + 2.0) / 8.0 * pow(ndh, a);\n}\n\nfloat D_GGX(float g, float ndh) {\n float r = 1.0 - g;\n float a = r * r;\n float tmp = ndh * ndh * (a - 1.0) + 1.0;\n return a / (PI * tmp * tmp);\n}\n\n\nvoid main()\n{\n vec4 albedoColor = vec4(color, alpha);\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = normalize(eyePos - v_WorldPosition);\n\n#ifdef DIFFUSEMAP_ENABLED\n vec4 texel = texture2D(diffuseMap, v_Texcoord);\n #ifdef SRGB_DECODE\n texel = sRGBToLinear(texel);\n #endif\n albedoColor.rgb *= texel.rgb;\n #ifdef DIFFUSEMAP_ALPHA_ALPHA\n albedoColor.a *= texel.a;\n #endif\n#endif\n\n\n#ifdef USE_METALNESS\n float m = metalness;\n\n #ifdef METALNESSMAP_ENABLED\n float m2 = texture2D(metalnessMap, v_Texcoord)[METALNESS_CHANEL];\n m = clamp(m2 + (m - 0.5) * 2.0, 0.0, 1.0);\n #endif\n\n vec3 baseColor = albedoColor.rgb;\n albedoColor.rgb = baseColor * (1.0 - m);\n vec3 spec = mix(vec3(0.04), baseColor, m);\n#else\n vec3 spec = specularColor;\n#endif\n\n#ifdef USE_ROUGHNESS\n float g = 1.0 - roughness;\n #ifdef ROUGHNESSMAP_ENABLED\n float g2 = 1.0 - texture2D(roughnessMap, v_Texcoord)[ROUGHNESS_CHANEL];\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\n #endif\n#else\n float g = glossiness;\n #ifdef GLOSSMAP_ENABLED\n float g2 = texture2D(glossMap, v_Texcoord)[GLOSS_CHANEL];\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\n #endif\n#endif\n\n#ifdef SPECULARMAP_ENABLED\n spec *= texture2D(specularMap, v_Texcoord).rgb;\n#endif\n\n vec3 N = v_Normal;\n#ifdef NORMALMAP_ENABLED\n if (dot(v_Tangent, v_Tangent) > 0.0) {\n vec3 normalTexel = texture2D(normalMap, v_Texcoord).xyz;\n if (dot(normalTexel, normalTexel) > 0.0) { N = normalTexel * 2.0 - 1.0;\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\n N = normalize(tbn * N);\n }\n }\n#endif\n\n vec3 diffuseTerm = vec3(0.0, 0.0, 0.0);\n vec3 specularTerm = vec3(0.0, 0.0, 0.0);\n\n float ndv = clamp(dot(N, V), 0.0, 1.0);\n vec3 fresnelTerm = F_Schlick(ndv, spec);\n\n#ifdef AMBIENT_LIGHT_COUNT\n for(int _idx_ = 0; _idx_ < AMBIENT_LIGHT_COUNT; _idx_++)\n {{\n diffuseTerm += ambientLightColor[_idx_];\n }}\n#endif\n\n#ifdef AMBIENT_SH_LIGHT_COUNT\n for(int _idx_ = 0; _idx_ < AMBIENT_SH_LIGHT_COUNT; _idx_++)\n {{\n diffuseTerm += calcAmbientSHLight(_idx_, N) * ambientSHLightColor[_idx_];\n }}\n#endif\n\n#ifdef POINT_LIGHT_COUNT\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\n float shadowContribsPoint[POINT_LIGHT_COUNT];\n if(shadowEnabled)\n {\n computeShadowOfPointLights(v_WorldPosition, shadowContribsPoint);\n }\n#endif\n for(int _idx_ = 0; _idx_ < POINT_LIGHT_COUNT; _idx_++)\n {{\n\n vec3 lightPosition = pointLightPosition[_idx_];\n vec3 lc = pointLightColor[_idx_];\n float range = pointLightRange[_idx_];\n\n vec3 L = lightPosition - v_WorldPosition;\n\n float dist = length(L);\n float attenuation = lightAttenuation(dist, range);\n L /= dist;\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n float shadowContrib = 1.0;\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribsPoint[_idx_];\n }\n#endif\n\n vec3 li = lc * ndl * attenuation * shadowContrib;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }}\n#endif\n\n#ifdef DIRECTIONAL_LIGHT_COUNT\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n float shadowContribsDir[DIRECTIONAL_LIGHT_COUNT];\n if(shadowEnabled)\n {\n computeShadowOfDirectionalLights(v_WorldPosition, shadowContribsDir);\n }\n#endif\n for(int _idx_ = 0; _idx_ < DIRECTIONAL_LIGHT_COUNT; _idx_++)\n {{\n\n vec3 L = -normalize(directionalLightDirection[_idx_]);\n vec3 lc = directionalLightColor[_idx_];\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n float shadowContrib = 1.0;\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribsDir[_idx_];\n }\n#endif\n\n vec3 li = lc * ndl * shadowContrib;\n\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }}\n#endif\n\n#ifdef SPOT_LIGHT_COUNT\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\n float shadowContribsSpot[SPOT_LIGHT_COUNT];\n if(shadowEnabled)\n {\n computeShadowOfSpotLights(v_WorldPosition, shadowContribsSpot);\n }\n#endif\n for(int i = 0; i < SPOT_LIGHT_COUNT; i++)\n {\n vec3 lightPosition = spotLightPosition[i];\n vec3 spotLightDirection = -normalize(spotLightDirection[i]);\n vec3 lc = spotLightColor[i];\n float range = spotLightRange[i];\n float a = spotLightUmbraAngleCosine[i];\n float b = spotLightPenumbraAngleCosine[i];\n float falloffFactor = spotLightFalloffFactor[i];\n\n vec3 L = lightPosition - v_WorldPosition;\n float dist = length(L);\n float attenuation = lightAttenuation(dist, range);\n\n L /= dist;\n float c = dot(spotLightDirection, L);\n\n float falloff;\n falloff = clamp((c - a) /( b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n\n float shadowContrib = 1.0;\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\n if (shadowEnabled)\n {\n shadowContrib = shadowContribsSpot[i];\n }\n#endif\n\n vec3 li = lc * attenuation * (1.0 - falloff) * shadowContrib * ndl;\n\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }\n#endif\n\n vec4 outColor = albedoColor;\n outColor.rgb *= diffuseTerm;\n\n outColor.rgb += specularTerm;\n\n\n#ifdef AMBIENT_CUBEMAP_LIGHT_COUNT\n vec3 L = reflect(-V, N);\n float rough2 = clamp(1.0 - g, 0.0, 1.0);\n float bias2 = rough2 * 5.0;\n vec2 brdfParam2 = texture2D(ambientCubemapLightBRDFLookup[0], vec2(rough2, ndv)).xy;\n vec3 envWeight2 = spec * brdfParam2.x + brdfParam2.y;\n vec3 envTexel2;\n for(int _idx_ = 0; _idx_ < AMBIENT_CUBEMAP_LIGHT_COUNT; _idx_++)\n {{\n envTexel2 = RGBMDecode(textureCubeLodEXT(ambientCubemapLightCubemap[_idx_], L, bias2), 51.5);\n outColor.rgb += ambientCubemapLightColor[_idx_] * envTexel2 * envWeight2;\n }}\n#endif\n\n#ifdef ENVIRONMENTMAP_ENABLED\n\n vec3 envWeight = g * fresnelTerm;\n vec3 L = reflect(-V, N);\n\n #ifdef PARALLAX_CORRECTED\n L = parallaxCorrect(L, v_WorldPosition, environmentBoxMin, environmentBoxMax);\n #endif\n\n #ifdef ENVIRONMENTMAP_PREFILTER\n float rough = clamp(1.0 - g, 0.0, 1.0);\n float bias = rough * maxMipmapLevel;\n vec3 envTexel = decodeHDR(textureCubeLodEXT(environmentMap, L, bias)).rgb;\n\n #ifdef BRDFLOOKUP_ENABLED\n vec2 brdfParam = texture2D(brdfLookup, vec2(rough, ndv)).xy;\n envWeight = spec * brdfParam.x + brdfParam.y;\n #endif\n\n #else\n vec3 envTexel = textureCube(environmentMap, L).xyz;\n #endif\n\n outColor.rgb += envTexel * envWeight;\n#endif\n\n float aoFactor = 1.0;\n#ifdef SSAOMAP_ENABLED\n aoFactor = min(texture2D(ssaoMap, (gl_FragCoord.xy - viewport.xy) / viewport.zw).r, aoFactor);\n#endif\n\n#ifdef AOMAP_ENABLED\n aoFactor = min(1.0 - clamp((1.0 - texture2D(aoMap, v_Texcoord2).r) * aoIntensity, 0.0, 1.0), aoFactor);\n#endif\n\n outColor.rgb *= aoFactor;\n\n vec3 lEmission = emission;\n#ifdef EMISSIVEMAP_ENABLED\n lEmission *= texture2D(emissiveMap, v_Texcoord).rgb;\n#endif\n outColor.rgb += lEmission * emissionIntensity;\n\n#ifdef GAMMA_ENCODE\n outColor.rgb = pow(outColor.rgb, vec3(1 / 2.2));\n#endif\n\n if(lineWidth > 0.)\n {\n outColor.rgb = mix(lineColor, vec3(outColor.rgb), edgeFactor(lineWidth));\n }\n\n gl_FragColor = encodeHDR(outColor);\n}\n\n@end\n"; +},function(t,e){t.exports="\n@export qtek.util.rand\nhighp float rand(vec2 uv) {\n const highp float a = 12.9898, b = 78.233, c = 43758.5453;\n highp float dt = dot(uv.xy, vec2(a,b)), sn = mod(dt, 3.141592653589793);\n return fract(sin(sn) * c);\n}\n@end\n\n@export qtek.util.calculate_attenuation\n\nuniform float attenuationFactor : 5.0;\n\nfloat lightAttenuation(float dist, float range)\n{\n float attenuation = 1.0;\n attenuation = dist*dist/(range*range+1.0);\n float att_s = attenuationFactor;\n attenuation = 1.0/(attenuation*att_s+1.0);\n att_s = 1.0/(att_s+1.0);\n attenuation = attenuation - att_s;\n attenuation /= 1.0 - att_s;\n return clamp(attenuation, 0.0, 1.0);\n}\n\n@end\n\n@export qtek.util.edge_factor\n\nfloat edgeFactor(float width)\n{\n vec3 d = fwidth(v_Barycentric);\n vec3 a3 = smoothstep(vec3(0.0), d * width, v_Barycentric);\n return min(min(a3.x, a3.y), a3.z);\n}\n\n@end\n\n@export qtek.util.encode_float\nvec4 encodeFloat(const in float depth)\n{\n \n \n const vec4 bitShifts = vec4(256.0*256.0*256.0, 256.0*256.0, 256.0, 1.0);\n const vec4 bit_mask = vec4(0.0, 1.0/256.0, 1.0/256.0, 1.0/256.0);\n vec4 res = fract(depth * bitShifts);\n res -= res.xxyz * bit_mask;\n\n return res;\n}\n@end\n\n@export qtek.util.decode_float\nfloat decodeFloat(const in vec4 color)\n{\n \n \n const vec4 bitShifts = vec4(1.0/(256.0*256.0*256.0), 1.0/(256.0*256.0), 1.0/256.0, 1.0);\n return dot(color, bitShifts);\n}\n@end\n\n\n@export qtek.util.float\n@import qtek.util.encode_float\n@import qtek.util.decode_float\n@end\n\n\n\n@export qtek.util.rgbm_decode\nvec3 RGBMDecode(vec4 rgbm, float range) {\n return range * rgbm.rgb * rgbm.a;\n }\n@end\n\n@export qtek.util.rgbm_encode\nvec4 RGBMEncode(vec3 color, float range) {\n if (dot(color, color) == 0.0) {\n return vec4(0.0);\n }\n vec4 rgbm;\n color /= range;\n rgbm.a = clamp(max(max(color.r, color.g), max(color.b, 1e-6)), 0.0, 1.0);\n rgbm.a = ceil(rgbm.a * 255.0) / 255.0;\n rgbm.rgb = color / rgbm.a;\n return rgbm;\n}\n@end\n\n@export qtek.util.rgbm\n@import qtek.util.rgbm_decode\n@import qtek.util.rgbm_encode\n\nvec4 decodeHDR(vec4 color)\n{\n#if defined(RGBM_DECODE) || defined(RGBM)\n return vec4(RGBMDecode(color, 51.5), 1.0);\n#else\n return color;\n#endif\n}\n\nvec4 encodeHDR(vec4 color)\n{\n#if defined(RGBM_ENCODE) || defined(RGBM)\n return RGBMEncode(color.xyz, 51.5);\n#else\n return color;\n#endif\n}\n\n@end\n\n\n@export qtek.util.srgb\n\nvec4 sRGBToLinear(in vec4 value) {\n 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);\n}\n\nvec4 linearTosRGB(in vec4 value) {\n return vec4(mix(pow(value.rgb, vec3(0.41666)) * 1.055 - vec3(0.055), value.rgb * 12.92, vec3(lessThanEqual(value.rgb, vec3(0.0031308)))), value.w);\n}\n@end\n\n\n@export qtek.chunk.skin_matrix\n\nmat4 skinMatrixWS;\nif (joint.x >= 0.0)\n{\n skinMatrixWS = skinMatrix[int(joint.x)] * weight.x;\n}\nif (joint.y >= 0.0)\n{\n skinMatrixWS += skinMatrix[int(joint.y)] * weight.y;\n}\nif (joint.z >= 0.0)\n{\n skinMatrixWS += skinMatrix[int(joint.z)] * weight.z;\n}\nif (joint.w >= 0.0)\n{\n skinMatrixWS += skinMatrix[int(joint.w)] * (1.0-weight.x-weight.y-weight.z);\n}\n@end\n\n\n\n@export qtek.util.parallax_correct\n\nvec3 parallaxCorrect(in vec3 dir, in vec3 pos, in vec3 boxMin, in vec3 boxMax) {\n vec3 first = (boxMax - pos) / dir;\n vec3 second = (boxMin - pos) / dir;\n\n vec3 further = max(first, second);\n float dist = min(further.x, min(further.y, further.z));\n\n vec3 fixedPos = pos + dir * dist;\n vec3 boxCenter = (boxMax + boxMin) * 0.5;\n\n return normalize(fixedPos - boxCenter);\n}\n\n@end\n\n\n\n@export qtek.util.clamp_sample\nvec4 clampSample(const in sampler2D texture, const in vec2 coord)\n{\n#ifdef STEREO\n float eye = step(0.5, coord.x) * 0.5;\n vec2 coordClamped = clamp(coord, vec2(eye, 0.0), vec2(0.5 + eye, 1.0));\n#else\n vec2 coordClamped = clamp(coord, vec2(0.0), vec2(1.0));\n#endif\n return texture2D(texture, coordClamped);\n}\n@end"},function(t,e,n){var r=n(8),i=n(18),a=n(7),o=n(14),s=n(21),u=n(6),l=n(4),c=n(51),h=n(24),f=n(32),d=n(13),_=n(53),p=n(151),m=n(152),v={},g=["px","nx","py","ny","pz","nz"];v.prefilterEnvironmentMap=function(t,e,n,s,p){p&&s||(s=v.generateNormalDistribution(),p=v.integrateBRDF(t,s)),n=n||{};var y=n.width||64,x=n.height||64,T=n.type||e.type,E=new i({width:y,height:x,type:T,flipY:!1,mipmaps:[]});E.isPowerOfTwo()||console.warn("Width and height must be power of two to enable mipmap.");var b=Math.min(y,x),S=Math.log(b)/Math.log(2)+1,N=new u({shader:new l({vertex:l.source("qtek.skybox.vertex"),fragment:m})});N.set("normalDistribution",s),n.encodeRGBM&&N.shader.define("fragment","RGBM_ENCODE"),n.decodeRGBM&&N.shader.define("fragment","RGBM_DECODE");var A,w=new h;if(e instanceof r){var M=new i({width:y,height:x,type:T===a.FLOAT?a.HALF_FLOAT:T});_.panoramaToCubeMap(t,e,M,{encodeRGBM:n.decodeRGBM}),e=M}A=new c({scene:w,material:N}),A.material.set("environmentMap",e);var C=new f({texture:E});n.encodeRGBM&&(T=E.type=a.UNSIGNED_BYTE);for(var R=new r({width:y,height:x,type:T}),L=new o({depthBuffer:!1}),I=d[T===a.UNSIGNED_BYTE?"Uint8Array":"Float32Array"],P=0;P>>16)>>>0;u=((1431655765&u)<<1|(2863311530&u)>>>1)>>>0,u=((858993459&u)<<2|(3435973836&u)>>>2)>>>0,u=((252645135&u)<<4|(4042322160&u)>>>4)>>>0,u=(((16711935&u)<<8|(4278255360&u)>>>8)>>>0)/4294967296;for(var l=0;ll&&(l=t[e][0]),t[e][1]c&&(c=t[e][1]);n=l-s,r=c-u,i=Math.max(n,r),a=s+.5*n,o=u+.5*r,t.push([a-20*i,o-i],[a,o+20*i],[a+20*i,o-i])}function r(t,e,n,r){var i,a,o,s,u,l,c=t[e],h=t[n],f=t[r],d=h[0]-c[0],_=h[1]-c[1],p=f[0]-c[0],m=f[1]-c[1],v=d*(c[0]+h[0])+_*(c[1]+h[1]),g=p*(c[0]+f[0])+m*(c[1]+f[1]),y=2*(d*(f[1]-h[1])-_*(f[0]-h[0]));return Math.abs(y)<1e-6?(i=Math.min(c[0],h[0],f[0]),a=Math.min(c[1],h[1],f[1]),o=.5*(Math.max(c[0],h[0],f[0])-i),s=.5*(Math.max(c[1],h[1],f[1])-a),u=i+o,l=a+s):(u=(m*v-_*g)/y,l=(d*g-p*v)/y,o=u-c[0],s=l-c[1]),{i:e,j:n,k:r,x:u,y:l,r:o*o+s*s}}function i(t){var e,n,r,i,a,o=t.length;t:for(;o;)for(n=t[--o],e=t[--o],r=o;r;)if(a=t[--r],i=t[--r],e===i&&n===a||e===a&&n===i){t.splice(o,2),t.splice(r,2),o-=2;continue t}}var a={triangulate:function(t,e){var a,o,s,u,l,c,h,f,d,_,p,m=t.length;if(m<3)return[];if(t=t.slice(0),e)for(a=m;a--;)t[a]=t[a][e];for(s=new Array(m),a=m;a--;)s[a]=a;for(s.sort(function(e,n){return t[n][0]-t[e][0]}),n(t),u=[r(t,m+0,m+1,m+2)],l=[],c=[],a=s.length;a--;){for(p=s[a],c.length=0,o=u.length;o--;)h=t[p][0]-u[o].x,h>0&&h*h>u[o].r?(l.push(u[o]),u.splice(o,1)):(f=t[p][1]-u[o].y,h*h+f*f>u[o].r||(c.push(u[o].i,u[o].j,u[o].j,u[o].k,u[o].k,u[o].i),u.splice(o,1)));for(i(c),o=c.length;o;)_=c[--o],d=c[--o],u.push(r(t,d,_,p))}for(a=u.length;a--;)l.push(u[a]);for(u.length=0,a=l.length;a--;)if(l[a].it[0][0]&&e[0]>t[1][0]&&e[0]>t[2][0]||e[1]t[0][1]&&e[1]>t[1][1]&&e[1]>t[2][1])return null;var n=t[1][0]-t[0][0],r=t[2][0]-t[0][0],i=t[1][1]-t[0][1],a=t[2][1]-t[0][1],o=n*a-r*i;if(0===o)return null;var s=(a*(e[0]-t[0][0])-r*(e[1]-t[0][1]))/o,u=(n*(e[1]-t[0][1])-i*(e[0]-t[0][0]))/o;return s<0||u<0||s+u>1?null:[s,u]}};t.exports=a},function(t,e,n){"use strict";function r(t,e,n,r){if(t[3]>0){var i=Math.pow(2,t[3]-128-8+r);e[n+0]=t[0]*i,e[n+1]=t[1]*i,e[n+2]=t[2]*i}else e[n+0]=0,e[n+1]=0,e[n+2]=0;return e[n+3]=1,e}function i(t,e,n){for(var r="",i=e;i0;)if(t[o][0]=e[n++],t[o][1]=e[n++],t[o][2]=e[n++],t[o][3]=e[n++],1===t[o][0]&&1===t[o][1]&&1===t[o][2]){for(var u=t[o][3]<>>0;u>0;u--)a(t[o-1],t[o]),o++,s--;i+=8}else o++,s--,i=0;return n}function s(t,e,n,r){if(rf)return o(t,e,n,r);var i=e[n++];if(2!=i)return o(t,e,n-1,r);if(t[0][1]=e[n++],t[0][2]=e[n++],i=e[n++],(t[0][2]<<8>>>0|i)>>>0!==r)return null;for(var i=0;i<4;i++)for(var a=0;a128){s=(127&s)>>>0;for(var u=e[n++];s--;)t[a++][i]=u}else for(;s--;)t[a++][i]=e[n++]}return n}var u=n(7),l=n(8),c=String.fromCharCode,h=8,f=32767,d={parseRGBE:function(t,e,n){null==n&&(n=0);var a=new Uint8Array(t),o=a.length;if("#?"===i(a,0,2)){for(var h=2;h=o)){h+=2;for(var f="";h=0&&this._clips.splice(e,1)},removeAnimator:function(t){for(var e=t.getClips(),n=0;nt)this.inputs.unshift(r);else if(this.inputs[i-1].position<=t)this.inputs.push(r);else{var a=this._findKey(t);this.inputs.splice(a,r)}return r},a.prototype.step=function(t){var e=r.prototype.step.call(this,t);return"finish"!==e&&this.setTime(this._elapsedTime),e},a.prototype.setTime=function(t){var e=this.position,n=this.inputs,i=n.length,a=n[0].position,o=n[i-1].position;if(e<=a||e>=o){var s=e<=a?n[0]:n[i-1],u=s.clip,l=s.offset;u.setTime((t+l)%u.life),u.output instanceof r?this.output.copy(u.output):this.output.copy(u)}else{var c=this._findKey(e),h=n[c],f=n[c+1],d=h.clip,_=f.clip;d.setTime((t+h.offset)%d.life),_.setTime((t+f.offset)%_.life);var p=(this.position-h.position)/(f.position-h.position),m=d.output instanceof r?d.output:d,v=_.output instanceof r?_.output:_;this.output.blend1D(m,v,p)}},a.prototype.clone=function(t){var e=r.prototype.clone.call(this);e.output=this.output.clone();for(var n=0;n=n[i].position&&t=0;i--)t>=n[i].position&&t=0&&(this._cacheKey=e,this._cachePosition=t),e},t.exports=a},function(t,e,n){"use strict";var r=n(20),i=n(83),a=n(27),o=function(t){t=t||{},r.call(this,t),this.output=t.output||null,this.inputs=t.inputs||[],this.position=new a,this._cacheTriangle=null,this._triangles=[],this._updateTriangles()};o.prototype=new r,o.prototype.constructor=o,o.prototype.addInput=function(t,e,n){var r={position:t,clip:e,offset:n||0};return this.inputs.push(r),this.life=Math.max(e.life,this.life),this._updateTriangles(),r},o.prototype._updateTriangles=function(){var t=this.inputs.map(function(t){return t.position});this._triangles=i.triangulate(t,"_array")},o.prototype.step=function(t){var e=r.prototype.step.call(this,t);return"finish"!==e&&this.setTime(this._elapsedTime),e},o.prototype.setTime=function(t){var e=this._findTriangle(this.position);if(e){var n=e[1],i=e[2],a=e[0],o=this.inputs[a.indices[0]],s=this.inputs[a.indices[1]],u=this.inputs[a.indices[2]],l=o.clip,c=s.clip,h=u.clip;l.setTime((t+o.offset)%l.life),c.setTime((t+s.offset)%c.life),h.setTime((t+u.offset)%h.life);var f=l.output instanceof r?l.output:l,d=c.output instanceof r?c.output:c,_=h.output instanceof r?h.output:h;this.output.blend2D(f,d,_,n,i)}},o.prototype.clone=function(t){var e=r.prototype.clone.call(this);e.output=this.output.clone();for(var n=0;n0,l=e.color;e.depth=0,u&&p.set(l,0,0,0,0);for(var c=!0,h=1/r,f=0;f-g&&_-g&&m-g&&v0){var v=d[0],w=(v[0]+1)*u,M=(-v[1]+1)*c;e.fillStyle=m,"rectangle"===N?e.fillRect(w-A,M-A,S,S):"circle"===N&&(e.beginPath(),e.arc(w,M,A,0,2*Math.PI),e.fill())}}}e.restore()},dispose:function(){this._triangles.clear(),this._lines.clear(),this._points.clear(),this._primitives=[],this.ctx=null,this.canvas=null}});t.exports=T},function(t,e,n){"use strict";var r=n(2),i=r.extend({cancelBubble:!1},{stopPropagation:function(){this.cancelBubble=!0}});i["throw"]=function(t,e,n){var r=new i(n);for(r.type=t,r.target=e;e&&!r.cancelBubble;)r.currentTarget=e,e.trigger(t,r),e=e.getParent()},t.exports=i},function(t,e,n){"use strict";var r=n(39),i=function(t){this._list=new r,this._map={},this._maxSize=t||10};i.prototype.setMaxSize=function(t){this._maxSize=t},i.prototype.put=function(t,e){if("undefined"==typeof this._map[t]){var n=this._list.length();if(n>=this._maxSize&&n>0){var r=this._list.head;this._list.remove(r),delete this._map[r.key]}var i=this._list.insert(e);i.key=t,this._map[t]=i}},i.prototype.get=function(t){var e=this._map[t];if("undefined"!=typeof e)return e!==this._list.tail&&(this._list.remove(e),this._list.insertEntry(e)),e.value},i.prototype.remove=function(t){var e=this._map[t];"undefined"!=typeof e&&(delete this._map[t],this._list.remove(e))},i.prototype.clear=function(){this._list.clear(),this._map={}},t.exports=i},function(t,e,n){"use strict";var r=n(2),i=n(4),a=n(6),o=n(14),s=n(21),u=n(8),l=n(7),c=n(15),h=n(43),f=n(41),d=n(69),_=n(10),p=n(3),m=n(67);i["import"](n(80)),i["import"](n(139)),i["import"](n(142)),i["import"](n(137)),i["import"](n(133)),i["import"](n(135)),i["import"](n(134)),i["import"](n(140)),i["import"](n(141)),i["import"](n(143)),i["import"](n(77));var v={},g=r.extend(function(){var t=i.source("qtek.compositor.vertex"),e=i.source("qtek.deferred.light_volume.vertex"),n=new i({vertex:t,fragment:i.source("qtek.deferred.directional_light")}),r=n.clone();r.define("fragment","SHADOWMAP_ENABLED");var c=function(t){t.blendEquation(t.FUNC_ADD),t.blendFunc(t.ONE,t.ONE)},v=function(t){return new a({shader:t,blend:c,transparent:!0,depthMask:!1})},g=function(t,n){var r=new i({vertex:e,fragment:i.source("qtek.deferred."+t)});return n&&r.define("fragment","SHADOWMAP_ENABLED"),r},y=new f({capSegments:10}),x=new _;x.rotateX(Math.PI/2).translate(new p(0,(-1),0)),y.applyTransform(x);var T=new d({capSegments:10});return x.identity().rotateZ(Math.PI/2),T.applyTransform(x),{shadowMapPass:null,autoResize:!0,_createLightPassMat:v,_gBuffer:new m,_lightAccumFrameBuffer:new o({depthBuffer:!1}),_lightAccumTex:new u({type:l.HALF_FLOAT,minFilter:l.NEAREST,magFilter:l.NEAREST}),_fullQuadPass:new s({blendWithPrevious:!0}),_directionalLightMat:v(n),_directionalLightMatWithShadow:v(r),_ambientMat:v(new i({vertex:t,fragment:i.source("qtek.deferred.ambient_light")})),_ambientSHMat:v(new i({vertex:t,fragment:i.source("qtek.deferred.ambient_sh_light")})),_ambientCubemapMat:v(new i({vertex:t,fragment:i.source("qtek.deferred.ambient_cubemap_light")})),_spotLightShader:g("spot_light"),_pointLightShader:g("point_light"),_spotLightShaderWithShadow:g("spot_light",!0),_pointLightShaderWithShadow:g("point_light",!0),_sphereLightShader:g("sphere_light"),_tubeLightShader:g("tube_light"),_lightSphereGeo:new h({widthSegments:10,heightSegements:10}),_lightConeGeo:y,_lightCylinderGeo:T,_outputPass:new s({fragment:i.source("qtek.compositor.output")})}},{render:function(t,e,n,r){r=r||{},r.renderToTarget=r.renderToTarget||!1,r.notUpdateShadow=r.notUpdateShadow||!1,r.notUpdateScene=r.notUpdateScene||!1,r.notUpdateScene||e.update(!1,!0),n.update(!0);var i=t.getDevicePixelRatio();!this.autoResize||t.getWidth()*i===this._lightAccumTex.width&&t.getHeight()*i===this._lightAccumTex.height||this.resize(t.getWidth()*i,t.getHeight()*i),this._gBuffer.update(t,e,n),this._accumulateLightBuffer(t,e,n,!r.notUpdateShadow),r.renderToTarget||(this._outputPass.setUniform("texture",this._lightAccumTex),this._outputPass.render(t))},getTargetTexture:function(){return this._lightAccumTex},getTargetFrameBuffer:function(){return this._lightAccumFrameBuffer},getGBuffer:function(){return this._gBuffer},setViewport:function(t,e,n,r,i){this._gBuffer.setViewport(t,e,n,r,i),this._lightAccumFrameBuffer.viewport=this._gBuffer.getViewport()},resize:function(t,e){this._lightAccumTex.width=t,this._lightAccumTex.height=e,this._lightAccumTex.dirty(),this._gBuffer.resize(t,e)},_accumulateLightBuffer:function(t,e,n,r){for(var i=t.gl,a=this._lightAccumTex,o=this._lightAccumFrameBuffer,s=n.getWorldPosition()._array,u=0;u1?F.name=[s.name,l].join("-"):F.name=s.name),e.meshes[a].push(F)}}},_parseNodes:function(t,e){for(var n in t.nodes){var r,i=t.nodes[n];if(i.camera&&this.includeCamera){var a=t.cameras[i.camera];"perspective"===a.projection?r=new m({name:i.name,aspect:a.aspect_ratio,fov:a.xfov,far:a.zfar,near:a.znear}):(r=new v,console.warn("TODO:Orthographic camera")),r.setName(i.name),e.cameras[i.name]=r}else if(i.lights&&this.includeLight){for(var o=[],s=0;s0&&o.scaleAndAdd(t._array,t._array,this.force._array,r/n)}});t.exports=s},function(t,e,n){"use strict";var r=n(29),i=n(12),a=n(6),o=n(4);o["import"](n(107));var s=new o({vertex:o.source("qtek.particle.vertex"),fragment:o.source("qtek.particle.fragment")});s.enableTexture("sprite");var u=r.extend({loop:!0,oneshot:!1,duration:1,spriteAnimationTileX:1,spriteAnimationTileY:1,spriteAnimationRepeat:0,mode:r.POINTS,ignorePicking:!0,_elapsedTime:0,_emitting:!0},function(){this.geometry=new i({dynamic:!0}),this.material||(this.material=new a({shader:s,transparent:!0,depthMask:!1})),this._particles=[],this._fields=[],this._emitters=[]},{culling:!1,frustumCulling:!1,castShadow:!1,receiveShadow:!1,addEmitter:function(t){this._emitters.push(t)},removeEmitter:function(t){this._emitters.splice(this._emitters.indexOf(t),1)},addField:function(t){this._fields.push(t)},removeField:function(t){this._fields.splice(this._fields.indexOf(t),1)},reset:function(){for(var t=0;t=i.life?(i.emitter.kill(i),e[n]=e[r-1],e.pop(),r--):n++}for(var n=0;n0)for(var a=0;a1,o=t.attributes.position.value,s=t.attributes.normal.value,u=t.attributes.texcoord0.value,l=t.attributes.texcoord1.value,c=this._particles.length;o&&o.length===3*c||(o=t.attributes.position.value=new Float32Array(3*c),s=t.attributes.normal.value=new Float32Array(3*c),a&&(u=t.attributes.texcoord0.value=new Float32Array(2*c),l=t.attributes.texcoord1.value=new Float32Array(2*c)));for(var h=1/e,f=0;fthis.duration&&!this.loop},dispose:function(t){for(var e=0;e>16,n=t-(e<<8)>>8,r=t-(e<<16)-(n<<8);return[e,n,r]}function i(t,e,n){return(t<<16)+(e<<8)+n}var a=n(2),o=n(14),s=n(8),u=n(4),l=n(6);u["import"](n(110));var c=a.extend(function(){return{renderer:null,downSampleRatio:1,width:100,height:100,lookupOffset:1,_frameBuffer:null,_texture:null,_shader:null,_idMaterials:[],_lookupTable:[],_meshMaterials:[],_idOffset:0}},function(){this.renderer&&(this.width=this.renderer.getWidth(),this.height=this.renderer.getHeight()),this._init()},{_init:function(){this._texture=new s({width:this.width*this.downSampleRatio,height:this.height*this.downSampleRatio}),this._frameBuffer=new o,this._shader=new u({vertex:u.source("qtek.picking.color.vertex"),fragment:u.source("qtek.picking.color.fragment")})},setPrecision:function(t){this._texture.width=this.width*t,this._texture.height=this.height*t,this.downSampleRatio=t},resize:function(t,e){this._texture.width=t*this.downSampleRatio,this._texture.height=e*this.downSampleRatio,this.width=t,this.height=e,this._texture.dirty()},update:function(t,e){var n=this.renderer;n.getWidth()===this.width&&n.getHeight()===this.height||this.resize(n.width,n.height),this._frameBuffer.attach(this._texture),this._frameBuffer.bind(n),this._idOffset=this.lookupOffset,this._setMaterial(t),n.render(t,e),this._restoreMaterial(),this._frameBuffer.unbind(n)},_setMaterial:function(t){for(var e=0;e0&&a<=this.minPolarAngle?e.rotateAround(this.origin,i,-a+this.minPolarAngle):this._offsetRoll<0&&a>=this.maxPolarAngle&&e.rotateAround(this.origin,i,-a+this.maxPolarAngle),this._offsetRoll=this._offsetPitch=0}if(0!==this._forward){var o=e.position.distance(this.origin),s=o+this._forward*o/5e3;sthis.minDistance&&e.position.scaleAndAdd(n,this._forward*o/5e3),this._forward=0}}}));t.exports=s},function(t,e,n){var r=n(2),i=(n(71),r.extend(function(){console.warn("TODO")},{render:function(t,e,n){}}));t.exports=i},function(t,e,n){var r=n(2),i=n(5),a=n(3),o=n(9),s=n(47),u=n(10),l=n(35),c=n(4),h=(n(11),n(15),n(46)),f=n(44),d=n(45),_=(n(28),n(6)),p=n(14),m=n(7),v=n(8),g=n(18),y=n(22),x=n(30),T=n(21),E=n(38),b=n(1),S=b.mat4,N=(b.vec3,["px","nx","py","ny","pz","nz"]);c["import"](n(146));var A=r.extend(function(){return{softShadow:A.PCF,shadowBlur:1,lightFrustumBias:2,_frameBuffer:new p,_textures:{},_shadowMapNumber:{POINT_LIGHT:0,DIRECTIONAL_LIGHT:0,SPOT_LIGHT:0},_meshMaterials:{},_depthMaterials:{},_depthShaders:{},_distanceMaterials:{},_opaqueCasters:[],_receivers:[],_lightsCastShadow:[],_lightCameras:{},_texturePool:new E}},function(){this._gaussianPassH=new T({fragment:c.source("qtek.compositor.gaussian_blur")}),this._gaussianPassV=new T({fragment:c.source("qtek.compositor.gaussian_blur")}),this._gaussianPassH.setUniform("blurSize",this.shadowBlur),this._gaussianPassH.setUniform("blurDir",0),this._gaussianPassV.setUniform("blurSize",this.shadowBlur),this._gaussianPassV.setUniform("blurDir",1),this._outputDepthPass=new T({fragment:c.source("qtek.sm.debug_depth")})},{render:function(t,e,n,r){this.trigger("beforerender",this,t,e,n),this._renderShadowPass(t,e,n,r),this.trigger("afterrender",this,t,e,n)},renderDebug:function(t,e){t.saveClear();var n=t.viewport,r=0,i=0,a=e||n.width/4,o=a;this.softShadow===A.VSM?this._outputDepthPass.material.shader.define("fragment","USE_VSM"):this._outputDepthPass.material.shader.unDefine("fragment","USE_VSM");for(var s in this._textures){var u=this._textures[s];t.setViewport(r,i,a*u.width/u.height,o),this._outputDepthPass.setUniform("depthMap",u),this._outputDepthPass.render(t),r+=a*u.width/u.height}t.setViewport(n),t.restoreClear()},_bindDepthMaterial:function(t,e,n){for(var r=0;r0&&(f.define("vertex","SKINNING"),f.define("vertex","JOINT_COUNT",l)),s&&f.define("both","SHADOW_TRANSPARENT"),this._depthShaders[a]=f),h||(h=new _({shader:f}),this._depthMaterials[i]=h),o.material=h,this.softShadow===A.VSM?f.define("fragment","USE_VSM"):f.unDefine("fragment","USE_VSM"),h.setUniform("bias",e),h.setUniform("slopeScale",n),s&&h.set("shadowTransparentMap",u))}},_bindDistanceMaterial:function(t,e){for(var n=e.getWorldPosition()._array,r=0;r0&&(o.shader.define("vertex","SKINNING"),o.shader.define("vertex","JOINT_COUNT",a)),this._distanceMaterials[a]=o),i.material=o,this.softShadow===A.VSM?o.shader.define("fragment","USE_VSM"):o.shader.unDefine("fragment","USE_VSM")),o.set("lightPosition",n),o.set("range",e.range)}},saveMaterial:function(t){for(var e=0;e1&&(m=g,g.shadowCascade>4)){console.warn("Support at most 4 cascade");continue}this.renderDirectionalLightShadow(t,e,n,g,this._opaqueCasters,_,c,l)}else g instanceof h?this.renderSpotLightShadow(t,g,this._opaqueCasters,u,s):g instanceof d&&this.renderPointLightShadow(t,g,this._opaqueCasters,p);this._shadowMapNumber[g.type]++}this.restoreMaterial(this._opaqueCasters);var y=_.slice(),x=_.slice();y.pop(),x.shift(),y.reverse(),x.reverse(),c.reverse();for(var T=s.map(i),E=l.map(i),b={},v=0;v0&&(A.fragmentDefines[R]=C,w=!0)}w&&A.dirty(),m?A.define("fragment","SHADOW_CASCADE",m.shadowCascade):A.unDefine("fragment","SHADOW_CASCADE"),b[A.__GUID__]=!0}s.length>0&&(N.setUniform("spotLightShadowMaps",s),N.setUniform("spotLightMatrices",u),N.setUniform("spotLightShadowMapSizes",T)),l.length>0&&(N.setUniform("directionalLightShadowMaps",l),m&&(N.setUniform("shadowCascadeClipsNear",y),N.setUniform("shadowCascadeClipsFar",x)),N.setUniform("directionalLightMatrices",c),N.setUniform("directionalLightShadowMapSizes",E)),p.length>0&&N.setUniform("pointLightShadowMaps",p)}}},renderDirectionalLightShadow:function(){var t=new s,e=new u,n=new o,r=new u,i=new u,a=new u,c=new u;return function(o,s,h,f,d,_,p,m){var v=f.shadowBias;this._bindDepthMaterial(d,v,f.shadowSlopeScale),d.sort(l.opaqueSortFunc);var g=Math.min(-s.viewBoundingBoxLastFrame.min.z,h.far),y=Math.max(-s.viewBoundingBoxLastFrame.max.z,h.near),x=this._getDirectionalLightCamera(f,s,h),T=a._array;c.copy(x.projectionMatrix),S.invert(i._array,x.worldTransform._array),S.multiply(i._array,i._array,h.worldTransform._array),S.multiply(T,c._array,i._array);for(var E=[],b=h.fov/180*Math.PI,N=h.aspect,w=(h.near+h.far)/(h.near-h.far),M=2*h.near*h.far/(h.near-h.far),C=0;C<=f.shadowCascade;C++){var R=y*Math.pow(g/y,C/f.shadowCascade),L=y+(g-y)*C/f.shadowCascade,I=R*f.cascadeSplitLogFactor+L*(1-f.cascadeSplitLogFactor);E.push(I),_.push(-(-I*w+M)/-I)}var P=this._getTexture(f,f.shadowCascade);m.push(P);var D=o.viewport,O=o.gl;this._frameBuffer.attach(P),this._frameBuffer.bind(o),O.clear(O.COLOR_BUFFER_BIT|O.DEPTH_BUFFER_BIT);for(var C=0;C threshold)\n {\n color.rgb = texel * scale;\n }\n else\n {\n color.rgb = vec3(0.0);\n }\n color.a = 1.0;\n\n gl_FragColor = encodeHDR(color);\n}\n@end\n"; +},function(t,e){t.exports="@export qtek.compositor.coloradjust\n\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float brightness : 0.0;\nuniform float contrast : 1.0;\nuniform float exposure : 0.0;\nuniform float gamma : 1.0;\nuniform float saturation : 1.0;\n\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord);\n\n vec3 color = clamp(tex.rgb + vec3(brightness), 0.0, 1.0);\n color = clamp( (color-vec3(0.5))*contrast+vec3(0.5), 0.0, 1.0);\n color = clamp( color * pow(2.0, exposure), 0.0, 1.0);\n color = clamp( pow(color, vec3(gamma)), 0.0, 1.0);\n float luminance = dot( color, w );\n color = mix(vec3(luminance), color, saturation);\n\n gl_FragColor = vec4(color, tex.a);\n}\n\n@end\n\n@export qtek.compositor.brightness\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float brightness : 0.0;\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord);\n vec3 color = tex.rgb + vec3(brightness);\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export qtek.compositor.contrast\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float contrast : 1.0;\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord);\n vec3 color = (tex.rgb-vec3(0.5))*contrast+vec3(0.5);\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export qtek.compositor.exposure\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float exposure : 0.0;\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n vec3 color = tex.rgb * pow(2.0, exposure);\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export qtek.compositor.gamma\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float gamma : 1.0;\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n vec3 color = pow(tex.rgb, vec3(gamma));\n gl_FragColor = vec4(color, tex.a);\n}\n@end\n\n@export qtek.compositor.saturation\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n\nuniform float saturation : 1.0;\n\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n vec3 color = tex.rgb;\n float luminance = dot(color, w);\n color = mix(vec3(luminance), color, saturation);\n gl_FragColor = vec4(color, tex.a);\n}\n@end"},function(t,e){t.exports="@export qtek.compositor.dof.coc\n\nuniform sampler2D depth;\n\nuniform float zNear: 0.1;\nuniform float zFar: 2000;\n\nuniform float focalDist: 3;\nuniform float focalRange: 1;\nuniform float focalLength: 30;\nuniform float fstop: 0.36;\n\nvarying vec2 v_Texcoord;\n\n@import qtek.util.encode_float\n\nvoid main()\n{\n float z = texture2D(depth, v_Texcoord).r;\n\n float dist = 2.0 * zNear * zFar / (zFar + zNear - z * (zFar - zNear));\n\n float aperture = 1.0 / fstop;\n\n float coc;\n\n float uppper = focalDist + focalRange;\n float lower = focalDist - focalRange;\n if (dist <= uppper && dist >= lower) {\n coc = 0.5;\n }\n else {\n float focalAdjusted = dist > uppper ? uppper : lower;\n\n coc = abs(aperture * (focalLength * (dist - focalAdjusted)) / (dist * (focalAdjusted - focalLength)));\n coc = clamp(coc, 0.0, 0.4) / 0.4000001;\n\n if (dist < lower) {\n coc = -coc;\n }\n coc = coc * 0.5 + 0.5;\n }\n\n gl_FragColor = encodeFloat(coc);\n}\n\n@end\n\n@export qtek.compositor.dof.premultiply\n\nuniform sampler2D texture;\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\n\n@import qtek.util.rgbm\n\n@import qtek.util.decode_float\n\nvoid main() {\n float fCoc = max(abs(decodeFloat(texture2D(coc, v_Texcoord)) * 2.0 - 1.0), 0.1);\n gl_FragColor = encodeHDR(\n vec4(decodeHDR(texture2D(texture, v_Texcoord)).rgb * fCoc, 1.0)\n );\n}\n@end\n\n\n@export qtek.compositor.dof.min_coc\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform vec2 textureSize : [512.0, 512.0];\n\n@import qtek.util.float\n\nvoid main()\n{\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\n\n float fCoc = decodeFloat(texture2D(coc, v_Texcoord + d.xy));\n fCoc = min(fCoc, decodeFloat(texture2D(coc, v_Texcoord + d.zy)));\n fCoc = min(fCoc, decodeFloat(texture2D(coc, v_Texcoord + d.xw)));\n fCoc = min(fCoc, decodeFloat(texture2D(coc, v_Texcoord + d.zw)));\n\n gl_FragColor = encodeFloat(fCoc);\n}\n\n@end\n\n\n@export qtek.compositor.dof.max_coc\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform vec2 textureSize : [512.0, 512.0];\n\n@import qtek.util.float\n\nvoid main()\n{\n\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\n\n float fCoc = decodeFloat(texture2D(coc, v_Texcoord + d.xy));\n fCoc = max(fCoc, decodeFloat(texture2D(coc, v_Texcoord + d.zy)));\n fCoc = max(fCoc, decodeFloat(texture2D(coc, v_Texcoord + d.xw)));\n fCoc = max(fCoc, decodeFloat(texture2D(coc, v_Texcoord + d.zw)));\n\n gl_FragColor = encodeFloat(fCoc);\n}\n\n@end\n\n\n\n\n@export qtek.compositor.dof.coc_upsample\n\n#define HIGH_QUALITY\n\nuniform sampler2D coc;\nuniform vec2 textureSize : [512, 512];\n\nuniform float sampleScale: 0.5;\n\nvarying vec2 v_Texcoord;\n\n@import qtek.util.float\n\nvoid main()\n{\n\n#ifdef HIGH_QUALITY\n vec4 d = vec4(1.0, 1.0, -1.0, 0.0) / textureSize.xyxy * sampleScale;\n\n float s;\n s = decodeFloat(texture2D(coc, v_Texcoord - d.xy));\n s += decodeFloat(texture2D(coc, v_Texcoord - d.wy)) * 2.0;\n s += decodeFloat(texture2D(coc, v_Texcoord - d.zy));\n\n s += decodeFloat(texture2D(coc, v_Texcoord + d.zw)) * 2.0;\n s += decodeFloat(texture2D(coc, v_Texcoord )) * 4.0;\n s += decodeFloat(texture2D(coc, v_Texcoord + d.xw)) * 2.0;\n\n s += decodeFloat(texture2D(coc, v_Texcoord + d.zy));\n s += decodeFloat(texture2D(coc, v_Texcoord + d.wy)) * 2.0;\n s += decodeFloat(texture2D(coc, v_Texcoord + d.xy));\n\n gl_FragColor = encodeFloat(s / 16.0);\n#else\n vec4 d = vec4(-1.0, -1.0, +1.0, +1.0) / textureSize.xyxy;\n\n float s;\n s = decodeFloat(texture2D(coc, v_Texcoord + d.xy));\n s += decodeFloat(texture2D(coc, v_Texcoord + d.zy));\n s += decodeFloat(texture2D(coc, v_Texcoord + d.xw));\n s += decodeFloat(texture2D(coc, v_Texcoord + d.zw));\n\n gl_FragColor = encodeFloat(s / 4.0);\n#endif\n}\n\n@end\n\n\n\n@export qtek.compositor.dof.upsample\n\n#define HIGH_QUALITY\n\nuniform sampler2D coc;\nuniform sampler2D texture;\nuniform vec2 textureSize : [512, 512];\n\nuniform float sampleScale: 0.5;\n\nvarying vec2 v_Texcoord;\n\n\n@import qtek.util.rgbm\n\n@import qtek.util.decode_float\n\nfloat tap(vec2 uv, inout vec4 color, float baseWeight) {\n float weight = abs(decodeFloat(texture2D(coc, uv)) * 2.0 - 1.0) * baseWeight;\n color += decodeHDR(texture2D(texture, uv)) * weight;\n return weight;\n}\n\nvoid main()\n{\n#ifdef HIGH_QUALITY\n vec4 d = vec4(1.0, 1.0, -1.0, 0.0) / textureSize.xyxy * sampleScale;\n\n vec4 color = vec4(0.0);\n float baseWeight = 1.0 / 16.0;\n float w = tap(v_Texcoord - d.xy, color, baseWeight);\n w += tap(v_Texcoord - d.wy, color, baseWeight * 2.0);\n w += tap(v_Texcoord - d.zy, color, baseWeight);\n\n w += tap(v_Texcoord + d.zw, color, baseWeight * 2.0);\n w += tap(v_Texcoord , color, baseWeight * 4.0);\n w += tap(v_Texcoord + d.xw, color, baseWeight * 2.0);\n\n w += tap(v_Texcoord + d.zy, color, baseWeight);\n w += tap(v_Texcoord + d.wy, color, baseWeight * 2.0);\n w += tap(v_Texcoord + d.xy, color, baseWeight);\n\n gl_FragColor = encodeHDR(color / w);\n#else\n vec4 d = vec4(-1.0, -1.0, +1.0, +1.0) / textureSize.xyxy;\n\n vec4 color = vec4(0.0);\n float baseWeight = 1.0 / 4.0;\n float w = tap(v_Texcoord + d.xy, color, baseWeight);\n w += tap(v_Texcoord + d.zy, color, baseWeight);\n w += tap(v_Texcoord + d.xw, color, baseWeight);\n w += tap(v_Texcoord + d.zw, color, baseWeight);\n\n gl_FragColor = encodeHDR(color / w);\n#endif\n}\n\n@end\n\n\n\n@export qtek.compositor.dof.downsample\n\nuniform sampler2D texture;\nuniform sampler2D coc;\nuniform vec2 textureSize : [512, 512];\n\nvarying vec2 v_Texcoord;\n\n@import qtek.util.rgbm\n\n@import qtek.util.decode_float\n\nfloat tap(vec2 uv, inout vec4 color) {\n float weight = abs(decodeFloat(texture2D(coc, uv)) * 2.0 - 1.0) * 0.25;\n color += decodeHDR(texture2D(texture, uv)) * weight;\n return weight;\n}\n\nvoid main()\n{\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\n\n vec4 color = vec4(0.0);\n float weight = tap(v_Texcoord + d.xy, color);\n weight += tap(v_Texcoord + d.zy, color);\n weight += tap(v_Texcoord + d.xw, color);\n weight += tap(v_Texcoord + d.zw, color);\n color /= weight;\n\n gl_FragColor = encodeHDR(color);\n}\n\n@end\n\n\n\n@export qtek.compositor.dof.hexagonal_blur_frag\n\n@import qtek.util.float\n\n\nvec4 doBlur(sampler2D targetTexture, vec2 offset) {\n#ifdef BLUR_COC\n float cocSum = 0.0;\n#else\n vec4 color = vec4(0.0);\n#endif\n\n float weightSum = 0.0;\n float kernelWeight = 1.0 / float(KERNEL_SIZE);\n\n for (int i = 0; i < KERNEL_SIZE; i++) {\n vec2 coord = v_Texcoord + offset * float(i);\n\n float w = kernelWeight;\n#ifdef BLUR_COC\n float fCoc = decodeFloat(texture2D(targetTexture, coord)) * 2.0 - 1.0;\n cocSum += clamp(fCoc, -1.0, 0.0) * w;\n#else\n float fCoc = decodeFloat(texture2D(coc, coord)) * 2.0 - 1.0;\n vec4 texel = texture2D(targetTexture, coord);\n #if !defined(BLUR_NEARFIELD)\n w *= abs(fCoc);\n #endif\n color += decodeHDR(texel) * w;\n#endif\n\n weightSum += w;\n }\n#ifdef BLUR_COC\n return encodeFloat(clamp(cocSum / weightSum, -1.0, 0.0) * 0.5 + 0.5);\n#else\n return color / weightSum;\n#endif\n}\n\n@end\n\n\n@export qtek.compositor.dof.hexagonal_blur_1\n\n#define KERNEL_SIZE 5\n\nuniform sampler2D texture;\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 1.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\n@import qtek.util.rgbm\n\n@import qtek.compositor.dof.hexagonal_blur_frag\n\nvoid main()\n{\n vec2 offset = blurSize / textureSize;\n\n#if !defined(BLUR_NEARFIELD) && !defined(BLUR_COC)\n offset *= abs(decodeFloat(texture2D(coc, v_Texcoord)) * 2.0 - 1.0);\n#endif\n\n gl_FragColor = doBlur(texture, vec2(0.0, offset.y));\n#if !defined(BLUR_COC)\n gl_FragColor = encodeHDR(gl_FragColor);\n#endif\n}\n\n@end\n\n@export qtek.compositor.dof.hexagonal_blur_2\n\n#define KERNEL_SIZE 5\n\nuniform sampler2D texture;\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 1.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\n@import qtek.util.rgbm\n\n@import qtek.compositor.dof.hexagonal_blur_frag\n\nvoid main()\n{\n vec2 offset = blurSize / textureSize;\n#if !defined(BLUR_NEARFIELD) && !defined(BLUR_COC)\n offset *= abs(decodeFloat(texture2D(coc, v_Texcoord)) * 2.0 - 1.0);\n#endif\n\n offset.y /= 2.0;\n\n gl_FragColor = doBlur(texture, -offset);\n#if !defined(BLUR_COC)\n gl_FragColor = encodeHDR(gl_FragColor);\n#endif\n}\n@end\n\n@export qtek.compositor.dof.hexagonal_blur_3\n\n#define KERNEL_SIZE 5\n\nuniform sampler2D texture1;\nuniform sampler2D texture2;\nuniform sampler2D coc;\n\nvarying vec2 v_Texcoord;\n\nuniform float blurSize : 1.0;\n\nuniform vec2 textureSize : [512.0, 512.0];\n\n@import qtek.util.rgbm\n\n@import qtek.compositor.dof.hexagonal_blur_frag\n\nvoid main()\n{\n vec2 offset = blurSize / textureSize;\n\n#if !defined(BLUR_NEARFIELD) && !defined(BLUR_COC)\n offset *= abs(decodeFloat(texture2D(coc, v_Texcoord)) * 2.0 - 1.0);\n#endif\n\n offset.y /= 2.0;\n vec2 vDownRight = vec2(offset.x, -offset.y);\n\n vec4 texel1 = doBlur(texture1, -offset);\n vec4 texel2 = doBlur(texture1, vDownRight);\n vec4 texel3 = doBlur(texture2, vDownRight);\n\n#ifdef BLUR_COC\n float coc1 = decodeFloat(texel1) * 2.0 - 1.0;\n float coc2 = decodeFloat(texel2) * 2.0 - 1.0;\n float coc3 = decodeFloat(texel3) * 2.0 - 1.0;\n gl_FragColor = encodeFloat(\n ((coc1 + coc2 + coc3) / 3.0) * 0.5 + 0.5\n );\n\n#else\n vec4 color = (texel1 + texel2 + texel3) / 3.0;\n gl_FragColor = encodeHDR(color);\n#endif\n}\n\n@end\n\n@export qtek.compositor.dof.composite\n\n#define DEBUG 0\n\nuniform sampler2D original;\nuniform sampler2D blurred;\nuniform sampler2D nearfield;\nuniform sampler2D coc;\nuniform sampler2D nearcoc;\nvarying vec2 v_Texcoord;\n\n@import qtek.util.rgbm\n@import qtek.util.float\n\nvoid main()\n{\n vec4 blurredColor = decodeHDR(texture2D(blurred, v_Texcoord));\n vec4 originalColor = decodeHDR(texture2D(original, v_Texcoord));\n\n float fCoc = decodeFloat(texture2D(coc, v_Texcoord));\n\n blurredColor.rgb /= max(fCoc, 0.1);\n fCoc = abs(fCoc * 2.0 - 1.0);\n\n float weight = fCoc;\n\n#ifdef NEARFIELD_ENABLED\n vec4 nearfieldColor = decodeHDR(texture2D(nearfield, v_Texcoord));\n float fNearCoc = decodeFloat(texture2D(nearcoc, v_Texcoord));\n fNearCoc = abs(fNearCoc * 2.0 - 1.0);\n\n gl_FragColor = encodeHDR(\n mix(\n nearfieldColor, vec4(mix(originalColor.rgb, blurredColor.rgb, weight), 1.0),\n pow(1.0 - fNearCoc, 4.0)\n )\n );\n#else\n gl_FragColor = encodeHDR(vec4(mix(originalColor.rgb, blurredColor.rgb, weight), 1.0));\n#endif\n\n#if DEBUG == 1\n gl_FragColor = vec4(vec3(fCoc), 1.0);\n#elif DEBUG == 2\n gl_FragColor = vec4(vec3(fNearCoc), 1.0);\n#elif DEBUG == 3\n gl_FragColor = encodeHDR(blurredColor);\n#elif DEBUG == 4\n gl_FragColor = encodeHDR(nearfieldColor);\n#endif\n}\n\n@end"},function(t,e){t.exports="@export qtek.compositor.downsample\n\nuniform sampler2D texture;\nuniform vec2 textureSize : [512, 512];\n\nvarying vec2 v_Texcoord;\n\n@import qtek.util.rgbm\nfloat brightness(vec3 c)\n{\n return max(max(c.r, c.g), c.b);\n}\n\n@import qtek.util.clamp_sample\n\nvoid main()\n{\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\n\n#ifdef ANTI_FLICKER\n vec3 s1 = decodeHDR(clampSample(texture, v_Texcoord + d.xy)).rgb;\n vec3 s2 = decodeHDR(clampSample(texture, v_Texcoord + d.zy)).rgb;\n vec3 s3 = decodeHDR(clampSample(texture, v_Texcoord + d.xw)).rgb;\n vec3 s4 = decodeHDR(clampSample(texture, v_Texcoord + d.zw)).rgb;\n\n float s1w = 1.0 / (brightness(s1) + 1.0);\n float s2w = 1.0 / (brightness(s2) + 1.0);\n float s3w = 1.0 / (brightness(s3) + 1.0);\n float s4w = 1.0 / (brightness(s4) + 1.0);\n float oneDivideSum = 1.0 / (s1w + s2w + s3w + s4w);\n\n vec4 color = vec4(\n (s1 * s1w + s2 * s2w + s3 * s3w + s4 * s4w) * oneDivideSum,\n 1.0\n );\n#else\n vec4 color = decodeHDR(clampSample(texture, v_Texcoord + d.xy));\n color += decodeHDR(clampSample(texture, v_Texcoord + d.zy));\n color += decodeHDR(clampSample(texture, v_Texcoord + d.xw));\n color += decodeHDR(clampSample(texture, v_Texcoord + d.zw));\n color *= 0.25;\n#endif\n\n gl_FragColor = encodeHDR(color);\n}\n\n@end"},function(t,e){t.exports="@export qtek.compositor.fxaa\n\nuniform sampler2D texture;\nuniform vec4 viewport : VIEWPORT;\n\nvarying vec2 v_Texcoord;\n\n#define FXAA_REDUCE_MIN (1.0/128.0)\n#define FXAA_REDUCE_MUL (1.0/8.0)\n#define FXAA_SPAN_MAX 8.0\n\n@import qtek.util.rgbm\n\nvoid main()\n{\n vec2 resolution = 1.0 / viewport.zw;\n vec3 rgbNW = decodeHDR( texture2D( texture, ( gl_FragCoord.xy + vec2( -1.0, -1.0 ) ) * resolution ) ).xyz;\n vec3 rgbNE = decodeHDR( texture2D( texture, ( gl_FragCoord.xy + vec2( 1.0, -1.0 ) ) * resolution ) ).xyz;\n vec3 rgbSW = decodeHDR( texture2D( texture, ( gl_FragCoord.xy + vec2( -1.0, 1.0 ) ) * resolution ) ).xyz;\n vec3 rgbSE = decodeHDR( texture2D( texture, ( gl_FragCoord.xy + vec2( 1.0, 1.0 ) ) * resolution ) ).xyz;\n vec4 rgbaM = decodeHDR( texture2D( texture, gl_FragCoord.xy * resolution ) );\n vec3 rgbM = rgbaM.xyz;\n float opacity = rgbaM.w;\n\n vec3 luma = vec3( 0.299, 0.587, 0.114 );\n\n float lumaNW = dot( rgbNW, luma );\n float lumaNE = dot( rgbNE, luma );\n float lumaSW = dot( rgbSW, luma );\n float lumaSE = dot( rgbSE, luma );\n float lumaM = dot( rgbM, luma );\n float lumaMin = min( lumaM, min( min( lumaNW, lumaNE ), min( lumaSW, lumaSE ) ) );\n float lumaMax = max( lumaM, max( max( lumaNW, lumaNE) , max( lumaSW, lumaSE ) ) );\n\n vec2 dir;\n dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\n float dirReduce = max( ( lumaNW + lumaNE + lumaSW + lumaSE ) * ( 0.25 * FXAA_REDUCE_MUL ), FXAA_REDUCE_MIN );\n\n float rcpDirMin = 1.0 / ( min( abs( dir.x ), abs( dir.y ) ) + dirReduce );\n dir = min( vec2( FXAA_SPAN_MAX, FXAA_SPAN_MAX),\n max( vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),\n dir * rcpDirMin)) * resolution;\n\n vec3 rgbA = decodeHDR( texture2D( texture, gl_FragCoord.xy * resolution + dir * ( 1.0 / 3.0 - 0.5 ) ) ).xyz;\n rgbA += decodeHDR( texture2D( texture, gl_FragCoord.xy * resolution + dir * ( 2.0 / 3.0 - 0.5 ) ) ).xyz;\n rgbA *= 0.5;\n\n vec3 rgbB = decodeHDR( texture2D( texture, gl_FragCoord.xy * resolution + dir * -0.5 ) ).xyz;\n rgbB += decodeHDR( texture2D( texture, gl_FragCoord.xy * resolution + dir * 0.5 ) ).xyz;\n rgbB *= 0.25;\n rgbB += rgbA * 0.5;\n\n float lumaB = dot( rgbB, luma );\n\n if ( ( lumaB < lumaMin ) || ( lumaB > lumaMax ) )\n {\n gl_FragColor = vec4( rgbA, opacity );\n\n }\n else {\n\n gl_FragColor = vec4( rgbB, opacity );\n\n }\n}\n\n@end"},function(t,e){t.exports="@export qtek.compositor.fxaa3\n\nuniform sampler2D texture;\nuniform vec4 viewport : VIEWPORT;\n\nuniform float subpixel: 0.75;\nuniform float edgeThreshold: 0.125;\nuniform float edgeThresholdMin: 0.0625;\n\n\nvarying vec2 v_Texcoord;\n\n@import qtek.util.rgbm\n\nfloat FxaaLuma(vec4 rgba) { return rgba.y; }\nvec4 FxaaPixelShader(\n vec2 pos\n ,sampler2D tex\n ,vec2 fxaaQualityRcpFrame\n ,float fxaaQualitySubpix\n ,float fxaaQualityEdgeThreshold\n ,float fxaaQualityEdgeThresholdMin\n) {\n vec2 posM;\n posM.x = pos.x;\n posM.y = pos.y;\n vec4 rgbyM = decodeHDR(texture2D(texture, posM, 0.0));\n float lumaS = FxaaLuma(decodeHDR(texture2D(texture, posM + (vec2( 0.0, 1.0) * fxaaQualityRcpFrame.xy), 0.0)));\n float lumaE = FxaaLuma(decodeHDR(texture2D(texture, posM + (vec2( 1.0, 0.0) * fxaaQualityRcpFrame.xy), 0.0)));\n float lumaN = FxaaLuma(decodeHDR(texture2D(texture, posM + (vec2( 0.0,-1.0) * fxaaQualityRcpFrame.xy), 0.0)));\n float lumaW = FxaaLuma(decodeHDR(texture2D(texture, posM + (vec2(-1.0, 0.0) * fxaaQualityRcpFrame.xy), 0.0)));\n\n float maxSM = max(lumaS, rgbyM.y);\n float minSM = min(lumaS, rgbyM.y);\n float maxESM = max(lumaE, maxSM);\n float minESM = min(lumaE, minSM);\n float maxWN = max(lumaN, lumaW);\n float minWN = min(lumaN, lumaW);\n float rangeMax = max(maxWN, maxESM);\n float rangeMin = min(minWN, minESM);\n float rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold;\n float range = rangeMax - rangeMin;\n float rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled);\n bool earlyExit = range < rangeMaxClamped;\n if(earlyExit) return rgbyM;\n float lumaNW = FxaaLuma(decodeHDR(texture2D(texture, posM + (vec2(-1.0,-1.0) * fxaaQualityRcpFrame.xy), 0.0)));\n float lumaSE = FxaaLuma(decodeHDR(texture2D(texture, posM + (vec2( 1.0, 1.0) * fxaaQualityRcpFrame.xy), 0.0)));\n float lumaNE = FxaaLuma(decodeHDR(texture2D(texture, posM + (vec2( 1.0,-1.0) * fxaaQualityRcpFrame.xy), 0.0)));\n float lumaSW = FxaaLuma(decodeHDR(texture2D(texture, posM + (vec2(-1.0, 1.0) * fxaaQualityRcpFrame.xy), 0.0)));\n\n float lumaNS = lumaN + lumaS;\n float lumaWE = lumaW + lumaE;\n float subpixRcpRange = 1.0/range;\n float subpixNSWE = lumaNS + lumaWE;\n float edgeHorz1 = (-2.0 * rgbyM.y) + lumaNS;\n float edgeVert1 = (-2.0 * rgbyM.y) + lumaWE;\n float lumaNESE = lumaNE + lumaSE;\n float lumaNWNE = lumaNW + lumaNE;\n float edgeHorz2 = (-2.0 * lumaE) + lumaNESE;\n float edgeVert2 = (-2.0 * lumaN) + lumaNWNE;\n float lumaNWSW = lumaNW + lumaSW;\n float lumaSWSE = lumaSW + lumaSE;\n float edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2);\n float edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2);\n float edgeHorz3 = (-2.0 * lumaW) + lumaNWSW;\n float edgeVert3 = (-2.0 * lumaS) + lumaSWSE;\n float edgeHorz = abs(edgeHorz3) + edgeHorz4;\n float edgeVert = abs(edgeVert3) + edgeVert4;\n float subpixNWSWNESE = lumaNWSW + lumaNESE;\n float lengthSign = fxaaQualityRcpFrame.x;\n bool horzSpan = edgeHorz >= edgeVert;\n float subpixA = subpixNSWE * 2.0 + subpixNWSWNESE;\n if(!horzSpan) lumaN = lumaW;\n if(!horzSpan) lumaS = lumaE;\n if(horzSpan) lengthSign = fxaaQualityRcpFrame.y;\n float subpixB = (subpixA * (1.0/12.0)) - rgbyM.y;\n float gradientN = lumaN - rgbyM.y;\n float gradientS = lumaS - rgbyM.y;\n float lumaNN = lumaN + rgbyM.y;\n float lumaSS = lumaS + rgbyM.y;\n bool pairN = abs(gradientN) >= abs(gradientS);\n float gradient = max(abs(gradientN), abs(gradientS));\n if(pairN) lengthSign = -lengthSign;\n float subpixC = clamp(abs(subpixB) * subpixRcpRange, 0.0, 1.0);\n vec2 posB;\n posB.x = posM.x;\n posB.y = posM.y;\n vec2 offNP;\n offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x;\n offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y;\n if(!horzSpan) posB.x += lengthSign * 0.5;\n if( horzSpan) posB.y += lengthSign * 0.5;\n vec2 posN;\n posN.x = posB.x - offNP.x * 1.0;\n posN.y = posB.y - offNP.y * 1.0;\n vec2 posP;\n posP.x = posB.x + offNP.x * 1.0;\n posP.y = posB.y + offNP.y * 1.0;\n float subpixD = ((-2.0)*subpixC) + 3.0;\n float lumaEndN = FxaaLuma(decodeHDR(texture2D(texture, posN, 0.0)));\n float subpixE = subpixC * subpixC;\n float lumaEndP = FxaaLuma(decodeHDR(texture2D(texture, posP, 0.0)));\n if(!pairN) lumaNN = lumaSS;\n float gradientScaled = gradient * 1.0/4.0;\n float lumaMM = rgbyM.y - lumaNN * 0.5;\n float subpixF = subpixD * subpixE;\n bool lumaMLTZero = lumaMM < 0.0;\n lumaEndN -= lumaNN * 0.5;\n lumaEndP -= lumaNN * 0.5;\n bool doneN = abs(lumaEndN) >= gradientScaled;\n bool doneP = abs(lumaEndP) >= gradientScaled;\n if(!doneN) posN.x -= offNP.x * 1.5;\n if(!doneN) posN.y -= offNP.y * 1.5;\n bool doneNP = (!doneN) || (!doneP);\n if(!doneP) posP.x += offNP.x * 1.5;\n if(!doneP) posP.y += offNP.y * 1.5;\n if(doneNP) {\n if(!doneN) lumaEndN = FxaaLuma(decodeHDR(texture2D(texture, posN.xy, 0.0)));\n if(!doneP) lumaEndP = FxaaLuma(decodeHDR(texture2D(texture, posP.xy, 0.0)));\n if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;\n if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;\n doneN = abs(lumaEndN) >= gradientScaled;\n doneP = abs(lumaEndP) >= gradientScaled;\n if(!doneN) posN.x -= offNP.x * 2.0;\n if(!doneN) posN.y -= offNP.y * 2.0;\n doneNP = (!doneN) || (!doneP);\n if(!doneP) posP.x += offNP.x * 2.0;\n if(!doneP) posP.y += offNP.y * 2.0;\n\n if(doneNP) {\n if(!doneN) lumaEndN = FxaaLuma(decodeHDR(texture2D(texture, posN.xy, 0.0)));\n if(!doneP) lumaEndP = FxaaLuma(decodeHDR(texture2D(texture, posP.xy, 0.0)));\n if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;\n if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;\n doneN = abs(lumaEndN) >= gradientScaled;\n doneP = abs(lumaEndP) >= gradientScaled;\n if(!doneN) posN.x -= offNP.x * 4.0;\n if(!doneN) posN.y -= offNP.y * 4.0;\n doneNP = (!doneN) || (!doneP);\n if(!doneP) posP.x += offNP.x * 4.0;\n if(!doneP) posP.y += offNP.y * 4.0;\n\n if(doneNP) {\n if(!doneN) lumaEndN = FxaaLuma(decodeHDR(texture2D(texture, posN.xy, 0.0)));\n if(!doneP) lumaEndP = FxaaLuma(decodeHDR(texture2D(texture, posP.xy, 0.0)));\n if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;\n if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;\n doneN = abs(lumaEndN) >= gradientScaled;\n doneP = abs(lumaEndP) >= gradientScaled;\n if(!doneN) posN.x -= offNP.x * 12.0;\n if(!doneN) posN.y -= offNP.y * 12.0;\n doneNP = (!doneN) || (!doneP);\n if(!doneP) posP.x += offNP.x * 12.0;\n if(!doneP) posP.y += offNP.y * 12.0;\n }\n }\n }\n float dstN = posM.x - posN.x;\n float dstP = posP.x - posM.x;\n if(!horzSpan) dstN = posM.y - posN.y;\n if(!horzSpan) dstP = posP.y - posM.y;\n bool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero;\n float spanLength = (dstP + dstN);\n bool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero;\n float spanLengthRcp = 1.0/spanLength;\n bool directionN = dstN < dstP;\n float dst = min(dstN, dstP);\n bool goodSpan = directionN ? goodSpanN : goodSpanP;\n float subpixG = subpixF * subpixF;\n float pixelOffset = (dst * (-spanLengthRcp)) + 0.5;\n float subpixH = subpixG * fxaaQualitySubpix;\n float pixelOffsetGood = goodSpan ? pixelOffset : 0.0;\n float pixelOffsetSubpix = max(pixelOffsetGood, subpixH);\n if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign;\n if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign;\n return vec4(decodeHDR(texture2D(texture, posM, 0.0)).xyz, rgbyM.y);\n\n}\n\nvoid main()\n{\n vec4 color = FxaaPixelShader(\n v_Texcoord,\n texture,\n vec2(1.0) / viewport.zw,\n subpixel,\n edgeThreshold,\n edgeThresholdMin\n );\n gl_FragColor = vec4(color.rgb, 1.0);\n}\n@end"},function(t,e){t.exports="@export qtek.compositor.hdr.log_lum\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\n\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\n@import qtek.util.rgbm\n\nvoid main()\n{\n vec4 tex = decodeHDR(texture2D(texture, v_Texcoord));\n float luminance = dot(tex.rgb, w);\n luminance = log(luminance + 0.001);\n\n gl_FragColor = encodeHDR(vec4(vec3(luminance), 1.0));\n}\n\n@end\n\n@export qtek.compositor.hdr.lum_adaption\nvarying vec2 v_Texcoord;\n\nuniform sampler2D adaptedLum;\nuniform sampler2D currentLum;\n\nuniform float frameTime : 0.02;\n\n@import qtek.util.rgbm\n\nvoid main()\n{\n float fAdaptedLum = decodeHDR(texture2D(adaptedLum, vec2(0.5, 0.5))).r;\n float fCurrentLum = exp(encodeHDR(texture2D(currentLum, vec2(0.5, 0.5))).r);\n\n fAdaptedLum += (fCurrentLum - fAdaptedLum) * (1.0 - pow(0.98, 30.0 * frameTime));\n gl_FragColor = encodeHDR(vec4(vec3(fAdaptedLum), 1.0));\n}\n@end\n\n@export qtek.compositor.hdr.composite\n\nuniform sampler2D texture;\n#ifdef BLOOM_ENABLED\nuniform sampler2D bloom;\n#endif\n#ifdef LENSFLARE_ENABLED\nuniform sampler2D lensflare;\nuniform sampler2D lensdirt;\n#endif\n\n#ifdef LUM_ENABLED\nuniform sampler2D lum;\n#endif\n\n#ifdef LUT_ENABLED\nuniform sampler2D lut;\n#endif\n\n#ifdef VIGNETTE\nuniform float vignetteDarkness: 1.0;\nuniform float vignetteOffset: 1.0;\n#endif\n\nuniform float exposure : 1.0;\nuniform float bloomIntensity : 0.25;\nuniform float lensflareIntensity : 1;\n\nvarying vec2 v_Texcoord;\n\nconst vec3 whiteScale = vec3(11.2);\n\n@import qtek.util.srgb\n\nvec3 uncharted2ToneMap(vec3 x)\n{\n const float A = 0.22; const float B = 0.30; const float C = 0.10; const float D = 0.20; const float E = 0.01; const float F = 0.30; \n return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;\n}\n\nvec3 filmicToneMap(vec3 color)\n{\n vec3 x = max(vec3(0.0), color - 0.004);\n return (x*(6.2*x+0.5))/(x*(6.2*x+1.7)+0.06);\n}\n\nvec3 ACESToneMapping(vec3 color)\n{\n const float A = 2.51;\n const float B = 0.03;\n const float C = 2.43;\n const float D = 0.59;\n const float E = 0.14;\n return (color * (A * color + B)) / (color * (C * color + D) + E);\n}\n\nfloat eyeAdaption(float fLum)\n{\n return mix(0.2, fLum, 0.5);\n}\n\n#ifdef LUT_ENABLED\nvec3 lutTransform(vec3 color) {\n float blueColor = color.b * 63.0;\n\n vec2 quad1;\n quad1.y = floor(floor(blueColor) / 8.0);\n quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\n vec2 quad2;\n quad2.y = floor(ceil(blueColor) / 8.0);\n quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\n vec2 texPos1;\n texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.r);\n texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g);\n\n vec2 texPos2;\n texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.r);\n texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g);\n\n vec4 newColor1 = texture2D(lut, texPos1);\n vec4 newColor2 = texture2D(lut, texPos2);\n\n vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n return newColor.rgb;\n}\n#endif\n\n@import qtek.util.rgbm\n\nvoid main()\n{\n vec4 texel = vec4(0.0);\n#ifdef TEXTURE_ENABLED\n texel += decodeHDR(texture2D(texture, v_Texcoord));\n#endif\n\n#ifdef BLOOM_ENABLED\n texel += decodeHDR(texture2D(bloom, v_Texcoord)) * bloomIntensity;\n#endif\n\n#ifdef LENSFLARE_ENABLED\n texel += decodeHDR(texture2D(lensflare, v_Texcoord)) * texture2D(lensdirt, v_Texcoord) * lensflareIntensity;\n#endif\n\n#ifdef LUM_ENABLED\n float fLum = texture2D(lum, vec2(0.5, 0.5)).r;\n float adaptedLumDest = 3.0 / (max(0.1, 1.0 + 10.0*eyeAdaption(fLum)));\n float exposureBias = adaptedLumDest * exposure;\n#else\n float exposureBias = exposure;\n#endif\n texel.rgb *= exposureBias;\n\n texel.rgb = ACESToneMapping(texel.rgb);\n texel = linearTosRGB(texel);\n\n#ifdef LUT_ENABLED\n texel.rgb = lutTransform(clamp(texel.rgb,vec3(0.0),vec3(1.0)));\n#endif\n\n#ifdef VIGNETTE\n vec2 uv = (v_Texcoord - vec2(0.5)) * vec2(vignetteOffset);\n texel.rgb = mix(texel.rgb, vec3(1.0 - vignetteDarkness), dot(uv, uv));\n#endif\n\n gl_FragColor = encodeHDR(texel);\n\n#ifdef DEBUG\n #if DEBUG == 1\n gl_FragColor = encodeHDR(decodeHDR(texture2D(texture, v_Texcoord)));\n #elif DEBUG == 2\n gl_FragColor = encodeHDR(decodeHDR(texture2D(bloom, v_Texcoord)).rgb * bloomIntensity);\n #elif DEBUG == 3\n gl_FragColor = encodeHDR(decodeHDR(texture2D(lensflare, v_Texcoord).rgb * lensflareIntensity));\n #endif\n#endif\n}\n\n@end"},function(t,e){t.exports="@export qtek.compositor.lensflare\n\n#define SAMPLE_NUMBER 8\n\nuniform sampler2D texture;\nuniform sampler2D lenscolor;\n\nuniform vec2 textureSize : [512, 512];\n\nuniform float dispersal : 0.3;\nuniform float haloWidth : 0.4;\nuniform float distortion : 1.0;\n\nvarying vec2 v_Texcoord;\n\n@import qtek.util.rgbm\n\nvec4 textureDistorted(\n in vec2 texcoord,\n in vec2 direction,\n in vec3 distortion\n) {\n return vec4(\n decodeHDR(texture2D(texture, texcoord + direction * distortion.r)).r,\n decodeHDR(texture2D(texture, texcoord + direction * distortion.g)).g,\n decodeHDR(texture2D(texture, texcoord + direction * distortion.b)).b,\n 1.0\n );\n}\n\nvoid main()\n{\n vec2 texcoord = -v_Texcoord + vec2(1.0); vec2 textureOffset = 1.0 / textureSize;\n\n vec2 ghostVec = (vec2(0.5) - texcoord) * dispersal;\n vec2 haloVec = normalize(ghostVec) * haloWidth;\n\n vec3 distortion = vec3(-textureOffset.x * distortion, 0.0, textureOffset.x * distortion);\n vec4 result = vec4(0.0);\n for (int i = 0; i < SAMPLE_NUMBER; i++)\n {\n vec2 offset = fract(texcoord + ghostVec * float(i));\n\n float weight = length(vec2(0.5) - offset) / length(vec2(0.5));\n weight = pow(1.0 - weight, 10.0);\n\n result += textureDistorted(offset, normalize(ghostVec), distortion) * weight;\n }\n\n result *= texture2D(lenscolor, vec2(length(vec2(0.5) - texcoord)) / length(vec2(0.5)));\n float weight = length(vec2(0.5) - fract(texcoord + haloVec)) / length(vec2(0.5));\n weight = pow(1.0 - weight, 10.0);\n vec2 offset = fract(texcoord + haloVec);\n result += textureDistorted(offset, normalize(ghostVec), distortion) * weight;\n\n gl_FragColor = result;\n}\n@end"; +},function(t,e){t.exports="\n@export qtek.compositor.lum\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\n\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord );\n float luminance = dot(tex.rgb, w);\n\n gl_FragColor = vec4(vec3(luminance), 1.0);\n}\n\n@end"},function(t,e){t.exports="\n@export qtek.compositor.lut\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\nuniform sampler2D lookup;\n\nvoid main()\n{\n\n vec4 tex = texture2D(texture, v_Texcoord);\n\n float blueColor = tex.b * 63.0;\n\n vec2 quad1;\n quad1.y = floor(floor(blueColor) / 8.0);\n quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\n vec2 quad2;\n quad2.y = floor(ceil(blueColor) / 8.0);\n quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\n vec2 texPos1;\n texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.r);\n texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.g);\n\n vec2 texPos2;\n texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.r);\n texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * tex.g);\n\n vec4 newColor1 = texture2D(lookup, texPos1);\n vec4 newColor2 = texture2D(lookup, texPos2);\n\n vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n gl_FragColor = vec4(newColor.rgb, tex.w);\n}\n\n@end"},function(t,e){t.exports="@export qtek.compositor.output\n\n#define OUTPUT_ALPHA\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\n\n@import qtek.util.rgbm\n\nvoid main()\n{\n vec4 tex = decodeHDR(texture2D(texture, v_Texcoord));\n\n gl_FragColor.rgb = tex.rgb;\n\n#ifdef OUTPUT_ALPHA\n gl_FragColor.a = tex.a;\n#else\n gl_FragColor.a = 1.0;\n#endif\n\n gl_FragColor = encodeHDR(gl_FragColor);\n}\n\n@end"},function(t,e){t.exports="\n@export qtek.compositor.upsample\n\n#define HIGH_QUALITY\n\nuniform sampler2D texture;\nuniform vec2 textureSize : [512, 512];\n\nuniform float sampleScale: 0.5;\n\nvarying vec2 v_Texcoord;\n\n@import qtek.util.rgbm\n\n@import qtek.util.clamp_sample\n\nvoid main()\n{\n\n#ifdef HIGH_QUALITY\n vec4 d = vec4(1.0, 1.0, -1.0, 0.0) / textureSize.xyxy * sampleScale;\n\n vec4 s;\n s = decodeHDR(clampSample(texture, v_Texcoord - d.xy));\n s += decodeHDR(clampSample(texture, v_Texcoord - d.wy)) * 2.0;\n s += decodeHDR(clampSample(texture, v_Texcoord - d.zy));\n\n s += decodeHDR(clampSample(texture, v_Texcoord + d.zw)) * 2.0;\n s += decodeHDR(clampSample(texture, v_Texcoord )) * 4.0;\n s += decodeHDR(clampSample(texture, v_Texcoord + d.xw)) * 2.0;\n\n s += decodeHDR(clampSample(texture, v_Texcoord + d.zy));\n s += decodeHDR(clampSample(texture, v_Texcoord + d.wy)) * 2.0;\n s += decodeHDR(clampSample(texture, v_Texcoord + d.xy));\n\n gl_FragColor = encodeHDR(s / 16.0);\n#else\n vec4 d = vec4(-1.0, -1.0, +1.0, +1.0) / textureSize.xyxy;\n\n vec4 s;\n s = decodeHDR(clampSample(texture, v_Texcoord + d.xy));\n s += decodeHDR(clampSample(texture, v_Texcoord + d.zy));\n s += decodeHDR(clampSample(texture, v_Texcoord + d.xw));\n s += decodeHDR(clampSample(texture, v_Texcoord + d.zw));\n\n gl_FragColor = encodeHDR(s / 4.0);\n#endif\n}\n\n@end"},function(t,e){t.exports="\n@export qtek.compositor.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n\nvarying vec2 v_Texcoord;\n\nvoid main()\n{\n v_Texcoord = texcoord;\n gl_Position = worldViewProjection * vec4(position, 1.0);\n}\n\n@end"},function(t,e){t.exports="@export qtek.compositor.vignette\n\n#define OUTPUT_ALPHA\n\nvarying vec2 v_Texcoord;\n\nuniform sampler2D texture;\n\nuniform float darkness: 1;\nuniform float offset: 1;\n\n@import qtek.util.rgbm\n\nvoid main()\n{\n vec4 texel = decodeHDR(texture2D(texture, v_Texcoord));\n\n gl_FragColor.rgb = texel.rgb;\n\n vec2 uv = (v_Texcoord - vec2(0.5)) * vec2(offset);\n\n gl_FragColor = encodeHDR(vec4(mix(texel.rgb, vec3(1.0 - darkness), dot(uv, uv)), texel.a));\n}\n\n@end"},function(t,e){t.exports="@export qtek.deferred.ambient_light\n\nuniform sampler2D gBufferTexture1;\nuniform sampler2D gBufferTexture3;\nuniform vec3 lightColor;\n\nuniform vec2 windowSize: WINDOW_SIZE;\n\nvoid main()\n{\n vec2 uv = gl_FragCoord.xy / windowSize;\n\n vec4 texel1 = texture2D(gBufferTexture1, uv);\n if (dot(texel1.rgb, vec3(1.0)) == 0.0) {\n discard;\n }\n\n vec3 albedo = texture2D(gBufferTexture3, uv).rgb;\n gl_FragColor.rgb = lightColor * albedo;\n gl_FragColor.a = 1.0;\n}\n@end"},function(t,e){t.exports="@export qtek.deferred.ambient_cubemap_light\n\n@import qtek.deferred.chunk.light_head\n\nuniform vec3 lightColor;\nuniform samplerCube lightCubemap;\nuniform sampler2D brdfLookup;\n\nuniform vec3 eyePosition;\n\n@import qtek.util.rgbm\n\nvoid main()\n{\n @import qtek.deferred.chunk.gbuffer_read\n\n vec3 V = normalize(eyePosition - position);\n vec3 L = reflect(-V, N);\n\n float ndv = clamp(dot(N, V), 0.0, 1.0);\n float rough = clamp(1.0 - glossiness, 0.0, 1.0);\n float bias = rough * 5.0;\n vec2 brdfParam = texture2D(brdfLookup, vec2(rough, ndv)).xy;\n vec3 envWeight = specularColor * brdfParam.x + brdfParam.y;\n\n vec3 envTexel = RGBMDecode(textureCubeLodEXT(lightCubemap, L, bias), 51.5);\n gl_FragColor.rgb = lightColor * envTexel * envWeight;\n\n gl_FragColor.a = 1.0;\n}\n@end"},function(t,e){t.exports="@export qtek.deferred.ambient_sh_light\n\nuniform sampler2D gBufferTexture1;\nuniform sampler2D gBufferTexture3;\n\nuniform vec3 lightColor;\nuniform vec3 lightCoefficients[9];\n\nuniform vec2 windowSize: WINDOW_SIZE;\n\nvec3 calcAmbientSHLight(vec3 N) {\n return lightCoefficients[0]\n + lightCoefficients[1] * N.x\n + lightCoefficients[2] * N.y\n + lightCoefficients[3] * N.z\n + lightCoefficients[4] * N.x * N.z\n + lightCoefficients[5] * N.z * N.y\n + lightCoefficients[6] * N.y * N.x\n + lightCoefficients[7] * (3.0 * N.z * N.z - 1.0)\n + lightCoefficients[8] * (N.x * N.x - N.y * N.y);\n}\n\nvoid main()\n{\n vec2 uv = gl_FragCoord.xy / windowSize;\n\n vec4 texel1 = texture2D(gBufferTexture1, uv);\n if (dot(texel1.rgb, vec3(1.0)) == 0.0) {\n discard;\n }\n vec3 N = texel1.rgb * 2.0 - 1.0;\n vec3 albedo = texture2D(gBufferTexture3, uv).rgb;\n gl_FragColor.rgb = lightColor * albedo * calcAmbientSHLight(N);\n gl_FragColor.a = 1.0;\n}\n@end"},function(t,e){t.exports="@export qtek.deferred.chunk.light_head\n\nuniform sampler2D gBufferTexture1;\nuniform sampler2D gBufferTexture2;\nuniform sampler2D gBufferTexture3;\n\nuniform vec2 windowSize: WINDOW_SIZE;\n\nuniform vec4 viewport: VIEWPORT;\n\nuniform mat4 viewProjectionInv;\n\n\n#ifdef DEPTH_ENCODED\n@import qtek.util.decode_float\n#endif\n\n@end\n\n@export qtek.deferred.chunk.gbuffer_read\n \n vec2 uv = gl_FragCoord.xy / windowSize;\n\n vec2 uv2 = (gl_FragCoord.xy - viewport.xy) / viewport.zw;\n\n vec4 texel1 = texture2D(gBufferTexture1, uv);\n vec4 texel3 = texture2D(gBufferTexture3, uv);\n if (dot(texel1.rgb, vec3(1.0)) == 0.0) {\n discard;\n }\n\n float glossiness = texel1.a;\n float metalness = texel3.a;\n\n vec3 N = texel1.rgb * 2.0 - 1.0;\n\n float z = texture2D(gBufferTexture2, uv).r * 2.0 - 1.0;\n\n vec2 xy = uv2 * 2.0 - 1.0;\n\n vec4 projectedPos = vec4(xy, z, 1.0);\n vec4 p4 = viewProjectionInv * projectedPos;\n\n vec3 position = p4.xyz / p4.w;\n\n vec3 albedo = texel3.rgb;\n\n vec3 diffuseColor = albedo * (1.0 - metalness);\n vec3 specularColor = mix(vec3(0.04), albedo, metalness);\n@end\n\n@export qtek.deferred.chunk.light_equation\n\nfloat D_Phong(in float g, in float ndh) {\n float a = pow(8192.0, g);\n return (a + 2.0) / 8.0 * pow(ndh, a);\n}\n\nfloat D_GGX(in float g, in float ndh) {\n float r = 1.0 - g;\n float a = r * r;\n float tmp = ndh * ndh * (a - 1.0) + 1.0;\n return a / (3.1415926 * tmp * tmp);\n}\n\nvec3 F_Schlick(in float ndv, vec3 spec) {\n return spec + (1.0 - spec) * pow(1.0 - ndv, 5.0);\n}\n\nvec3 lightEquation(\n in vec3 lightColor, in vec3 diffuseColor, in vec3 specularColor,\n in float ndl, in float ndh, in float ndv, in float g\n)\n{\n return ndl * lightColor\n * (diffuseColor + D_Phong(g, ndh) * F_Schlick(ndv, specularColor));\n}\n\n@end"},function(t,e){t.exports="@export qtek.deferred.directional_light\n\n@import qtek.deferred.chunk.light_head\n\n@import qtek.deferred.chunk.light_equation\n\nuniform vec3 lightDirection;\nuniform vec3 lightColor;\n\nuniform vec3 eyePosition;\n\n#ifdef SHADOWMAP_ENABLED\nuniform sampler2D lightShadowMap;\nuniform float lightShadowMapSize;\nuniform mat4 lightMatrices[SHADOW_CASCADE];\nuniform float shadowCascadeClipsNear[SHADOW_CASCADE];\nuniform float shadowCascadeClipsFar[SHADOW_CASCADE];\n#endif\n\n@import qtek.plugin.shadow_map_common\n\nvoid main()\n{\n @import qtek.deferred.chunk.gbuffer_read\n\n vec3 L = -normalize(lightDirection);\n vec3 V = normalize(eyePosition - position);\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n float ndv = clamp(dot(N, V), 0.0, 1.0);\n\n gl_FragColor.rgb = lightEquation(\n lightColor, diffuseColor, specularColor, ndl, ndh, ndv, glossiness\n );\n\n#ifdef SHADOWMAP_ENABLED\n float shadowContrib = 1.0;\n for (int _idx_ = 0; _idx_ < SHADOW_CASCADE; _idx_++) {{\n if (\n z >= shadowCascadeClipsNear[_idx_] &&\n z <= shadowCascadeClipsFar[_idx_]\n ) {\n shadowContrib = computeShadowContrib(\n lightShadowMap, lightMatrices[_idx_], position, lightShadowMapSize,\n vec2(1.0 / float(SHADOW_CASCADE), 1.0),\n vec2(float(_idx_) / float(SHADOW_CASCADE), 0.0)\n );\n }\n }}\n\n gl_FragColor.rgb *= shadowContrib;\n#endif\n\n gl_FragColor.a = 1.0;\n}\n@end\n"},function(t,e){t.exports="@export qtek.deferred.gbuffer.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\n\nuniform vec2 uvRepeat;\nuniform vec2 uvOffset;\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n\n#ifdef FIRST_PASS\nattribute vec3 normal : NORMAL;\n#endif\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_COUNT] : SKIN_MATRIX;\n#endif\n\n\n#ifdef FIRST_PASS\nvarying vec3 v_Normal;\n\nattribute vec4 tangent : TANGENT;\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n\n\nvarying vec2 v_Texcoord;\nvarying vec4 v_ProjPos;\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n\n#ifdef FIRST_PASS\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n bool hasTangent = dot(tangent, tangent) > 0.0;\n#endif\n\n#ifdef SKINNING\n\n @import qtek.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n\n #ifdef FIRST_PASS\n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n if (hasTangent) {\n skinnedTangent = (skinMatrixWS * vec4(tangent.xyz, 0.0)).xyz;\n }\n #endif\n#endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n\n#ifdef FIRST_PASS\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\n\n if (hasTangent) {\n v_Tangent = normalize((worldInverseTranspose * vec4(skinnedTangent, 0.0)).xyz);\n v_Bitangent = normalize(cross(v_Normal, v_Tangent) * tangent.w);\n }\n#endif\n\n v_ProjPos = gl_Position;\n}\n\n\n@end\n\n\n@export qtek.deferred.gbuffer1.fragment\n\nuniform float glossiness;\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\n\nuniform sampler2D normalMap;\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n\nuniform sampler2D roughnessMap;\n\nuniform bool useRoughnessMap;\n\nvarying vec4 v_ProjPos;\n\nvoid main()\n{\n vec3 N = v_Normal;\n\n if (dot(v_Tangent, v_Tangent) > 0.0) {\n vec3 normalTexel = texture2D(normalMap, v_Texcoord).xyz;\n if (dot(normalTexel, normalTexel) > 0.0) { N = normalTexel * 2.0 - 1.0;\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\n N = normalize(tbn * N);\n }\n }\n\n gl_FragColor.rgb = (N + 1.0) * 0.5;\n\n \n float g = glossiness;\n\n if (useRoughnessMap) {\n vec4 glossTexel = texture2D(roughnessMap, v_Texcoord);\n g = clamp(-glossTexel.r + g * 2.0, 0.0, 1.0);\n }\n\n\n gl_FragColor.a = g;\n\n }\n@end\n\n@export qtek.deferred.gbuffer2.fragment\n\nuniform sampler2D diffuseMap;\nuniform sampler2D metalnessMap;\n\nuniform vec3 color;\nuniform float metalness;\n\nuniform bool useMetalnessMap;\nuniform bool linear;\n\nvarying vec2 v_Texcoord;\n\n@import qtek.util.srgb\n\nvoid main ()\n{\n float m = metalness;\n\n if (useMetalnessMap) {\n vec4 metalnessTexel = texture2D(metalnessMap, v_Texcoord);\n m = clamp(metalnessTexel.r + (m * 2.0 - 1.0), 0.0, 1.0);\n }\n vec4 texel = texture2D(diffuseMap, v_Texcoord);\n if (linear) {\n texel = sRGBToLinear(texel);\n }\n\n gl_FragColor.rgb = texel.rgb * color;\n\n gl_FragColor.a = m;\n}\n\n@end\n\n\n@export qtek.deferred.gbuffer.debug\n\n@import qtek.deferred.chunk.light_head\nuniform int debug: 0;\n\nvoid main ()\n{\n @import qtek.deferred.chunk.gbuffer_read\n\n if (debug == 0) {\n gl_FragColor = vec4(N, 1.0);\n }\n else if (debug == 1) {\n gl_FragColor = vec4(vec3(z), 1.0);\n }\n else if (debug == 2) {\n gl_FragColor = vec4(position, 1.0);\n }\n else if (debug == 3) {\n gl_FragColor = vec4(vec3(glossiness), 1.0);\n }\n else if (debug == 4) {\n gl_FragColor = vec4(vec3(metalness), 1.0);\n }\n else {\n gl_FragColor = vec4(albedo, 1.0);\n }\n}\n@end"},function(t,e){t.exports="@export qtek.deferred.light_volume.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\nvarying vec3 v_Position;\n\nvoid main()\n{\n gl_Position = worldViewProjection * vec4(position, 1.0);\n\n v_Position = position;\n}\n\n@end"},function(t,e){t.exports="@export qtek.deferred.point_light\n\n@import qtek.deferred.chunk.light_head\n\n@import qtek.util.calculate_attenuation\n\n@import qtek.deferred.chunk.light_equation\n\nuniform vec3 lightPosition;\nuniform vec3 lightColor;\nuniform float lightRange;\n\nuniform vec3 eyePosition;\n\n#ifdef SHADOWMAP_ENABLED\nuniform samplerCube lightShadowMap;\nuniform float lightShadowMapSize;\n#endif\n\nvarying vec3 v_Position;\n\n@import qtek.plugin.shadow_map_common\n\nvoid main()\n{\n @import qtek.deferred.chunk.gbuffer_read\n\n vec3 L = lightPosition - position;\n vec3 V = normalize(eyePosition - position);\n\n float dist = length(L);\n L /= dist;\n\n vec3 H = normalize(L + V);\n\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n float ndv = clamp(dot(N, V), 0.0, 1.0);\n float attenuation = lightAttenuation(dist, lightRange);\n gl_FragColor.rgb = attenuation * lightEquation(\n lightColor, diffuseColor, specularColor, ndl, ndh, ndv, glossiness\n );\n\n#ifdef SHADOWMAP_ENABLED\n float shadowContrib = computeShadowContribOmni(\n lightShadowMap, -L * dist, lightRange\n );\n gl_FragColor.rgb *= clamp(shadowContrib, 0.0, 1.0);\n#endif\n\n gl_FragColor.a = 1.0;\n}\n@end"},function(t,e){t.exports="@export qtek.deferred.sphere_light\n\n@import qtek.deferred.chunk.light_head\n\n@import qtek.util.calculate_attenuation\n\n@import qtek.deferred.chunk.light_equation\n\nuniform vec3 lightPosition;\nuniform vec3 lightColor;\nuniform float lightRange;\nuniform float lightRadius;\n\nuniform vec3 eyePosition;\n\nvarying vec3 v_Position;\n\nvoid main()\n{\n @import qtek.deferred.chunk.gbuffer_read\n\n\n vec3 L = lightPosition - position;\n\n vec3 V = normalize(eyePosition - position);\n\n float dist = length(L);\n vec3 R = reflect(V, N);\n float tmp = dot(L, R);\n vec3 cToR = tmp * R - L;\n float d = length(cToR);\n L = L + cToR * clamp(lightRadius / d, 0.0, 1.0);\n\n L = normalize(L);\n\n vec3 H = normalize(L + V);\n\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n float ndv = clamp(dot(N, V), 0.0, 1.0);\n float attenuation = lightAttenuation(dist, lightRange);\n gl_FragColor.rgb = lightColor * ndl * attenuation;\n\n glossiness = clamp(glossiness - lightRadius / 2.0 / dist, 0.0, 1.0);\n\n gl_FragColor.rgb = attenuation * lightEquation(\n lightColor, diffuseColor, specularColor, ndl, ndh, ndv, glossiness\n );\n\n gl_FragColor.a = 1.0;\n}\n@end"},function(t,e){t.exports="@export qtek.deferred.spot_light\n\n@import qtek.deferred.chunk.light_head\n\n@import qtek.deferred.chunk.light_equation\n\n@import qtek.util.calculate_attenuation\n\nuniform vec3 lightPosition;\nuniform vec3 lightDirection;\nuniform vec3 lightColor;\nuniform float umbraAngleCosine;\nuniform float penumbraAngleCosine;\nuniform float lightRange;\nuniform float falloffFactor;\n\nuniform vec3 eyePosition;\n\n#ifdef SHADOWMAP_ENABLED\nuniform sampler2D lightShadowMap;\nuniform mat4 lightMatrix;\nuniform float lightShadowMapSize;\n#endif\n\n@import qtek.plugin.shadow_map_common\n\nvoid main()\n{\n @import qtek.deferred.chunk.gbuffer_read\n\n vec3 L = lightPosition - position;\n vec3 V = normalize(eyePosition - position);\n\n float dist = length(L);\n L /= dist;\n\n\n float attenuation = lightAttenuation(dist, lightRange);\n float c = dot(-normalize(lightDirection), L);\n\n float falloff = clamp((c - umbraAngleCosine) / (penumbraAngleCosine - umbraAngleCosine), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n\n vec3 H = normalize(L + V);\n float ndl = clamp(dot(N, L), 0.0, 1.0);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n float ndv = clamp(dot(N, V), 0.0, 1.0);\n\n gl_FragColor.rgb = (1.0 - falloff) * attenuation * lightEquation(\n lightColor, diffuseColor, specularColor, ndl, ndh, ndv, glossiness\n );\n\n#ifdef SHADOWMAP_ENABLED\n float shadowContrib = computeShadowContrib(\n lightShadowMap, lightMatrix, position, lightShadowMapSize\n );\n gl_FragColor.rgb *= shadowContrib;\n#endif\n\n gl_FragColor.a = 1.0;\n}\n@end\n"},function(t,e){t.exports="@export qtek.deferred.tube_light\n\n@import qtek.deferred.chunk.light_head\n\n@import qtek.util.calculate_attenuation\n\n@import qtek.deferred.chunk.light_equation\n\nuniform vec3 lightPosition;\nuniform vec3 lightColor;\nuniform float lightRange;\nuniform vec3 lightExtend;\n\nuniform vec3 eyePosition;\n\nvarying vec3 v_Position;\n\nvoid main()\n{\n @import qtek.deferred.chunk.gbuffer_read\n\n vec3 L = lightPosition - position;\n\n vec3 V = normalize(eyePosition - position);\n\n vec3 R = reflect(V, N);\n\n vec3 L0 = lightPosition - lightExtend - position;\n vec3 L1 = lightPosition + lightExtend - position;\n vec3 LD = L1 - L0;\n\n float len0 = length(L0);\n float len1 = length(L1);\n float irra = 2.0 * clamp(dot(N, L0) / (2.0 * len0) + dot(N, L1) / (2.0 * len1), 0.0, 1.0);\n\n float LDDotR = dot(R, LD);\n float t = (LDDotR * dot(R, L0) - dot(L0, LD)) / (dot(LD, LD) - LDDotR * LDDotR);\n t = clamp(t, 0.0, 1.0);\n L = L0 + t * LD;\n float dist = length(L);\n L /= dist;\n\n vec3 H = normalize(L + V);\n\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n float ndv = clamp(dot(N, V), 0.0, 1.0);\n\n glossiness = clamp(glossiness - 0.0 / 2.0 / dist, 0.0, 1.0);\n\n gl_FragColor.rgb = lightColor * irra * lightAttenuation(dist, lightRange)\n * (diffuseColor + D_Phong(glossiness, ndh) * F_Schlick(ndv, specularColor));\n\n gl_FragColor.a = 1.0;\n}\n@end"},function(t,e){t.exports="vec3 calcAmbientSHLight(int idx, vec3 N) {\n int offset = 9 * idx;\n\n return ambientSHLightCoefficients[0]\n + ambientSHLightCoefficients[1] * N.x\n + ambientSHLightCoefficients[2] * N.y\n + ambientSHLightCoefficients[3] * N.z\n + ambientSHLightCoefficients[4] * N.x * N.z\n + ambientSHLightCoefficients[5] * N.z * N.y\n + ambientSHLightCoefficients[6] * N.y * N.x\n + ambientSHLightCoefficients[7] * (3.0 * N.z * N.z - 1.0)\n + ambientSHLightCoefficients[8] * (N.x * N.x - N.y * N.y);\n}"},function(t,e){t.exports="/**\n * http: */\n\n@export qtek.lambert.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\n\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\n\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 normal : NORMAL;\n\nattribute vec3 barycentric;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_COUNT] : SKIN_MATRIX;\n#endif\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n\n#ifdef SKINNING\n\n @import qtek.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n#endif\n\n gl_Position = worldViewProjection * vec4( skinnedPosition, 1.0 );\n\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_Normal = normalize( ( worldInverseTranspose * vec4(skinnedNormal, 0.0) ).xyz );\n v_WorldPosition = ( world * vec4( skinnedPosition, 1.0) ).xyz;\n\n v_Barycentric = barycentric;\n}\n\n@end\n\n\n@export qtek.lambert.fragment\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\n\nuniform sampler2D diffuseMap;\nuniform sampler2D alphaMap;\n\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform vec3 emission : [0.0, 0.0, 0.0];\nuniform float alpha : 1.0;\n\nuniform float lineWidth : 0.0;\nuniform vec3 lineColor : [0.0, 0.0, 0.0];\nvarying vec3 v_Barycentric;\n\n#ifdef AMBIENT_LIGHT_COUNT\n@import qtek.header.ambient_light\n#endif\n#ifdef POINT_LIGHT_COUNT\n@import qtek.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_COUNT\n@import qtek.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_COUNT\n@import qtek.header.spot_light\n#endif\n\n@import qtek.util.calculate_attenuation\n\n@import qtek.util.edge_factor\n\n@import qtek.plugin.compute_shadow_map\n\nvoid main()\n{\n#ifdef RENDER_NORMAL\n gl_FragColor = vec4(v_Normal * 0.5 + 0.5, 1.0);\n return;\n#endif\n#ifdef RENDER_TEXCOORD\n gl_FragColor = vec4(v_Texcoord, 1.0, 1.0);\n return;\n#endif\n\n gl_FragColor = vec4(color, alpha);\n\n#ifdef DIFFUSEMAP_ENABLED\n vec4 tex = texture2D( diffuseMap, v_Texcoord );\n#ifdef SRGB_DECODE\n tex.rgb = pow(tex.rgb, vec3(2.2));\n#endif\n gl_FragColor.rgb *= tex.rgb;\n#ifdef DIFFUSEMAP_ALPHA_ALPHA\n gl_FragColor.a *= tex.a;\n#endif\n#endif\n\n vec3 diffuseColor = vec3(0.0, 0.0, 0.0);\n\n#ifdef AMBIENT_LIGHT_COUNT\n for(int i = 0; i < AMBIENT_LIGHT_COUNT; i++)\n {\n diffuseColor += ambientLightColor[i];\n }\n#endif\n#ifdef POINT_LIGHT_COUNT\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\n float shadowContribsPoint[POINT_LIGHT_COUNT];\n if( shadowEnabled )\n {\n computeShadowOfPointLights(v_WorldPosition, shadowContribsPoint);\n }\n#endif\n for(int i = 0; i < POINT_LIGHT_COUNT; i++)\n {\n\n vec3 lightPosition = pointLightPosition[i];\n vec3 lightColor = pointLightColor[i];\n float range = pointLightRange[i];\n\n vec3 lightDirection = lightPosition - v_WorldPosition;\n\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range);\n\n lightDirection /= dist;\n\n float ndl = dot( v_Normal, lightDirection );\n\n float shadowContrib = 1.0;\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribsPoint[i];\n }\n#endif\n\n diffuseColor += lightColor * clamp(ndl, 0.0, 1.0) * attenuation * shadowContrib;\n }\n#endif\n#ifdef DIRECTIONAL_LIGHT_COUNT\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n float shadowContribsDir[DIRECTIONAL_LIGHT_COUNT];\n if(shadowEnabled)\n {\n computeShadowOfDirectionalLights(v_WorldPosition, shadowContribsDir);\n }\n#endif\n for(int i = 0; i < DIRECTIONAL_LIGHT_COUNT; i++)\n {\n vec3 lightDirection = -directionalLightDirection[i];\n vec3 lightColor = directionalLightColor[i];\n\n float ndl = dot(v_Normal, normalize(lightDirection));\n\n float shadowContrib = 1.0;\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribsDir[i];\n }\n#endif\n\n diffuseColor += lightColor * clamp(ndl, 0.0, 1.0) * shadowContrib;\n }\n#endif\n\n#ifdef SPOT_LIGHT_COUNT\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\n float shadowContribsSpot[SPOT_LIGHT_COUNT];\n if(shadowEnabled)\n {\n computeShadowOfSpotLights(v_WorldPosition, shadowContribsSpot);\n }\n#endif\n for(int i = 0; i < SPOT_LIGHT_COUNT; i++)\n {\n vec3 lightPosition = -spotLightPosition[i];\n vec3 spotLightDirection = -normalize( spotLightDirection[i] );\n vec3 lightColor = spotLightColor[i];\n float range = spotLightRange[i];\n float a = spotLightUmbraAngleCosine[i];\n float b = spotLightPenumbraAngleCosine[i];\n float falloffFactor = spotLightFalloffFactor[i];\n\n vec3 lightDirection = lightPosition - v_WorldPosition;\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range);\n\n lightDirection /= dist;\n float c = dot(spotLightDirection, lightDirection);\n\n float falloff;\n falloff = clamp((c - a) /( b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n\n float ndl = dot(v_Normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\n\n float shadowContrib = 1.0;\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribsSpot[i];\n }\n#endif\n diffuseColor += lightColor * ndl * attenuation * (1.0-falloff) * shadowContrib;\n }\n#endif\n\n gl_FragColor.rgb *= diffuseColor;\n gl_FragColor.rgb += emission;\n if(lineWidth > 0.01)\n {\n gl_FragColor.rgb = gl_FragColor.rgb * mix(lineColor, vec3(1.0), edgeFactor(lineWidth));\n }\n}\n\n@end"},function(t,e){t.exports="@export qtek.sm.depth.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\n\nattribute vec3 position : POSITION;\n\n#ifdef SHADOW_TRANSPARENT\nattribute vec2 texcoord : TEXCOORD_0;\n#endif\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_COUNT] : SKIN_MATRIX;\n#endif\n\nvarying vec4 v_ViewPosition;\n\n#ifdef SHADOW_TRANSPARENT\nvarying vec2 v_Texcoord;\n#endif\n\nvoid main(){\n\n vec3 skinnedPosition = position;\n\n#ifdef SKINNING\n\n @import qtek.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n\n v_ViewPosition = worldViewProjection * vec4(skinnedPosition, 1.0);\n gl_Position = v_ViewPosition;\n\n#ifdef SHADOW_TRANSPARENT\n v_Texcoord = texcoord;\n#endif\n}\n@end\n\n@export qtek.sm.depth.fragment\n\nvarying vec4 v_ViewPosition;\n\n#ifdef SHADOW_TRANSPARENT\nvarying vec2 v_Texcoord;\n#endif\n\nuniform float bias : 0.001;\nuniform float slopeScale : 1.0;\n\n#ifdef SHADOW_TRANSPARENT\nuniform sampler2D transparentMap;\n#endif\n\n@import qtek.util.encode_float\n\nvoid main(){\n float depth = v_ViewPosition.z / v_ViewPosition.w;\n \n#ifdef USE_VSM\n depth = depth * 0.5 + 0.5;\n float moment1 = depth;\n float moment2 = depth * depth;\n\n float dx = dFdx(depth);\n float dy = dFdy(depth);\n moment2 += 0.25*(dx*dx+dy*dy);\n\n gl_FragColor = vec4(moment1, moment2, 0.0, 1.0);\n#else\n float dx = dFdx(depth);\n float dy = dFdy(depth);\n depth += sqrt(dx*dx + dy*dy) * slopeScale + bias;\n\n#ifdef SHADOW_TRANSPARENT\n if (texture2D(transparentMap, v_Texcoord).a <= 0.1) {\n gl_FragColor = encodeFloat(0.9999);\n return;\n }\n#endif\n\n gl_FragColor = encodeFloat(depth * 0.5 + 0.5);\n#endif\n}\n@end\n\n@export qtek.sm.debug_depth\n\nuniform sampler2D depthMap;\nvarying vec2 v_Texcoord;\n\n@import qtek.util.decode_float\n\nvoid main() {\n vec4 tex = texture2D(depthMap, v_Texcoord);\n#ifdef USE_VSM\n gl_FragColor = vec4(tex.rgb, 1.0);\n#else\n float depth = decodeFloat(tex);\n gl_FragColor = vec4(depth, depth, depth, 1.0);\n#endif\n}\n\n@end\n\n\n@export qtek.sm.distance.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world : WORLD;\n\nattribute vec3 position : POSITION;\n\n#ifdef SKINNING\nattribute vec3 boneWeight;\nattribute vec4 boneIndex;\n\nuniform mat4 skinMatrix[JOINT_COUNT] : SKIN_MATRIX;\n#endif\n\nvarying vec3 v_WorldPosition;\n\nvoid main (){\n\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import qtek.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition , 1.0);\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n}\n\n@end\n\n@export qtek.sm.distance.fragment\n\nuniform vec3 lightPosition;\nuniform float range : 100;\n\nvarying vec3 v_WorldPosition;\n\n@import qtek.util.encode_float\n\nvoid main(){\n float dist = distance(lightPosition, v_WorldPosition);\n#ifdef USE_VSM\n gl_FragColor = vec4(dist, dist * dist, 0.0, 0.0);\n#else\n dist = dist / range;\n gl_FragColor = encodeFloat(dist);\n#endif\n}\n@end\n\n@export qtek.plugin.shadow_map_common\n\n@import qtek.util.decode_float\n\nfloat tapShadowMap(sampler2D map, vec2 uv, float z){\n vec4 tex = texture2D(map, uv);\n return step(z, decodeFloat(tex) * 2.0 - 1.0);\n}\n\nfloat pcf(sampler2D map, vec2 uv, float z, float textureSize, vec2 scale) {\n\n float shadowContrib = tapShadowMap(map, uv, z);\n vec2 offset = vec2(1.0 / textureSize) * scale;\n shadowContrib += tapShadowMap(map, uv+vec2(offset.x, 0.0), z);\n shadowContrib += tapShadowMap(map, uv+vec2(offset.x, offset.y), z);\n shadowContrib += tapShadowMap(map, uv+vec2(-offset.x, offset.y), z);\n shadowContrib += tapShadowMap(map, uv+vec2(0.0, offset.y), z);\n shadowContrib += tapShadowMap(map, uv+vec2(-offset.x, 0.0), z);\n shadowContrib += tapShadowMap(map, uv+vec2(-offset.x, -offset.y), z);\n shadowContrib += tapShadowMap(map, uv+vec2(offset.x, -offset.y), z);\n shadowContrib += tapShadowMap(map, uv+vec2(0.0, -offset.y), z);\n\n return shadowContrib / 9.0;\n}\n\nfloat pcf(sampler2D map, vec2 uv, float z, float textureSize) {\n return pcf(map, uv, z, textureSize, vec2(1.0));\n}\n\nfloat chebyshevUpperBound(vec2 moments, float z){\n float p = 0.0;\n z = z * 0.5 + 0.5;\n if (z <= moments.x) {\n p = 1.0;\n }\n float variance = moments.y - moments.x * moments.x;\n variance = max(variance, 0.0000001);\n float mD = moments.x - z;\n float pMax = variance / (variance + mD * mD);\n pMax = clamp((pMax-0.4)/(1.0-0.4), 0.0, 1.0);\n return max(p, pMax);\n}\nfloat computeShadowContrib(\n sampler2D map, mat4 lightVPM, vec3 position, float textureSize, vec2 scale, vec2 offset\n) {\n\n vec4 posInLightSpace = lightVPM * vec4(position, 1.0);\n posInLightSpace.xyz /= posInLightSpace.w;\n float z = posInLightSpace.z;\n if(all(greaterThan(posInLightSpace.xyz, vec3(-0.99, -0.99, -1.0))) &&\n all(lessThan(posInLightSpace.xyz, vec3(0.99, 0.99, 1.0)))){\n vec2 uv = (posInLightSpace.xy+1.0) / 2.0;\n\n #ifdef USE_VSM\n vec2 moments = texture2D(map, uv * scale + offset).xy;\n return chebyshevUpperBound(moments, z);\n #else\n return pcf(map, uv * scale + offset, z, textureSize, scale);\n #endif\n }\n return 1.0;\n}\n\nfloat computeShadowContrib(sampler2D map, mat4 lightVPM, vec3 position, float textureSize) {\n return computeShadowContrib(map, lightVPM, position, textureSize, vec2(1.0), vec2(0.0));\n}\n\nfloat computeShadowContribOmni(samplerCube map, vec3 direction, float range)\n{\n float dist = length(direction);\n vec4 shadowTex = textureCube(map, direction);\n\n#ifdef USE_VSM\n vec2 moments = shadowTex.xy;\n float variance = moments.y - moments.x * moments.x;\n float mD = moments.x - dist;\n float p = variance / (variance + mD * mD);\n if(moments.x + 0.001 < dist){\n return clamp(p, 0.0, 1.0);\n }else{\n return 1.0;\n }\n#else\n return step(dist, (decodeFloat(shadowTex) + 0.0002) * range);\n#endif\n}\n\n@end\n\n\n\n@export qtek.plugin.compute_shadow_map\n\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT) || defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT) || defined(POINT_LIGHT_SHADOWMAP_COUNT)\n\n#ifdef SPOT_LIGHT_SHADOWMAP_COUNT\nuniform sampler2D spotLightShadowMaps[SPOT_LIGHT_SHADOWMAP_COUNT];\nuniform mat4 spotLightMatrices[SPOT_LIGHT_SHADOWMAP_COUNT];\nuniform float spotLightShadowMapSizes[SPOT_LIGHT_SHADOWMAP_COUNT];\n#endif\n\n#ifdef DIRECTIONAL_LIGHT_SHADOWMAP_COUNT\n#if defined(SHADOW_CASCADE)\nuniform sampler2D directionalLightShadowMaps[1];\nuniform mat4 directionalLightMatrices[SHADOW_CASCADE];\nuniform float directionalLightShadowMapSizes[1];\nuniform float shadowCascadeClipsNear[SHADOW_CASCADE];\nuniform float shadowCascadeClipsFar[SHADOW_CASCADE];\n#else\nuniform sampler2D directionalLightShadowMaps[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT];\nuniform mat4 directionalLightMatrices[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT];\nuniform float directionalLightShadowMapSizes[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT];\n#endif\n#endif\n\n#ifdef POINT_LIGHT_SHADOWMAP_COUNT\nuniform samplerCube pointLightShadowMaps[POINT_LIGHT_SHADOWMAP_COUNT];\nuniform float pointLightShadowMapSizes[POINT_LIGHT_SHADOWMAP_COUNT];\n#endif\n\nuniform bool shadowEnabled : true;\n\n@import qtek.plugin.shadow_map_common\n\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\n\nvoid computeShadowOfSpotLights(vec3 position, inout float shadowContribs[SPOT_LIGHT_COUNT] ) {\n float shadowContrib;\n for(int _idx_ = 0; _idx_ < SPOT_LIGHT_SHADOWMAP_COUNT; _idx_++) {{\n shadowContrib = computeShadowContrib(\n spotLightShadowMaps[_idx_], spotLightMatrices[_idx_], position,\n spotLightShadowMapSizes[_idx_]\n );\n shadowContribs[_idx_] = shadowContrib;\n }}\n for(int _idx_ = SPOT_LIGHT_SHADOWMAP_COUNT; _idx_ < SPOT_LIGHT_COUNT; _idx_++){{\n shadowContribs[_idx_] = 1.0;\n }}\n}\n\n#endif\n\n\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n\n#ifdef SHADOW_CASCADE\n\nvoid computeShadowOfDirectionalLights(vec3 position, inout float shadowContribs[DIRECTIONAL_LIGHT_COUNT]){\n float depth = (2.0 * gl_FragCoord.z - gl_DepthRange.near - gl_DepthRange.far)\n / (gl_DepthRange.far - gl_DepthRange.near);\n\n float shadowContrib;\n shadowContribs[0] = 1.0;\n\n for (int _idx_ = 0; _idx_ < SHADOW_CASCADE; _idx_++) {{\n if (\n depth >= shadowCascadeClipsNear[_idx_] &&\n depth <= shadowCascadeClipsFar[_idx_]\n ) {\n shadowContrib = computeShadowContrib(\n directionalLightShadowMaps[0], directionalLightMatrices[_idx_], position,\n directionalLightShadowMapSizes[0],\n vec2(1.0 / float(SHADOW_CASCADE), 1.0),\n vec2(float(_idx_) / float(SHADOW_CASCADE), 0.0)\n );\n shadowContribs[0] = shadowContrib;\n }\n }}\n for(int _idx_ = DIRECTIONAL_LIGHT_SHADOWMAP_COUNT; _idx_ < DIRECTIONAL_LIGHT_COUNT; _idx_++) {{\n shadowContribs[_idx_] = 1.0;\n }}\n}\n\n#else\n\nvoid computeShadowOfDirectionalLights(vec3 position, inout float shadowContribs[DIRECTIONAL_LIGHT_COUNT]){\n float shadowContrib;\n\n for(int _idx_ = 0; _idx_ < DIRECTIONAL_LIGHT_SHADOWMAP_COUNT; _idx_++) {{\n shadowContrib = computeShadowContrib(\n directionalLightShadowMaps[_idx_], directionalLightMatrices[_idx_], position,\n directionalLightShadowMapSizes[_idx_]\n );\n shadowContribs[_idx_] = shadowContrib;\n }}\n for(int _idx_ = DIRECTIONAL_LIGHT_SHADOWMAP_COUNT; _idx_ < DIRECTIONAL_LIGHT_COUNT; _idx_++) {{\n shadowContribs[_idx_] = 1.0;\n }}\n}\n#endif\n\n#endif\n\n\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\n\nvoid computeShadowOfPointLights(vec3 position, inout float shadowContribs[POINT_LIGHT_COUNT] ){\n vec3 lightPosition;\n vec3 direction;\n for(int _idx_ = 0; _idx_ < POINT_LIGHT_SHADOWMAP_COUNT; _idx_++) {{\n lightPosition = pointLightPosition[_idx_];\n direction = position - lightPosition;\n shadowContribs[_idx_] = computeShadowContribOmni(pointLightShadowMaps[_idx_], direction, pointLightRange[_idx_]);\n }}\n for(int _idx_ = POINT_LIGHT_SHADOWMAP_COUNT; _idx_ < POINT_LIGHT_COUNT; _idx_++) {{\n shadowContribs[_idx_] = 1.0;\n }}\n}\n\n#endif\n\n#endif\n\n@end"; +},function(t,e){t.exports="@export qtek.wireframe.vertex\n\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world : WORLD;\n\nattribute vec3 position : POSITION;\nattribute vec3 barycentric;\n\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\n\nuniform mat4 skinMatrix[JOINT_COUNT] : SKIN_MATRIX;\n#endif\n\nvarying vec3 v_Barycentric;\n\nvoid main()\n{\n\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n\n @import qtek.chunk.skin_matrix\n\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0 );\n\n v_Barycentric = barycentric;\n}\n\n@end\n\n\n@export qtek.wireframe.fragment\n\nuniform vec3 color : [0.0, 0.0, 0.0];\n\nuniform float alpha : 1.0;\nuniform float lineWidth : 1.0;\n\nvarying vec3 v_Barycentric;\n\n\n@import qtek.util.edge_factor\n\nvoid main()\n{\n\n gl_FragColor.rgb = color;\n gl_FragColor.a = (1.0-edgeFactor(lineWidth)) * alpha;\n}\n\n@end"},function(t,e,n){function r(t,e){this.p0=t,this.p1=e,this._linkedListEntry=null}function i(t,e,n,r,i,a){return(n-t)*(a-r)-(r-e)*(i-n)}function a(t,e,n,r,a,o,s,u){return!(i(t,e,a,o,s,u)<=0||i(t,e,s,u,n,r)<=0||i(s,u,a,o,n,r)<=0)}var o=n(39),s=n(13),u=1,l=2,c=function(){this._points,this._triangles,this.maxGridNumber=50,this.minGridNumber=0,this._gridNumber=20,this._boundingBox=[[1/0,1/0],[-(1/0),-(1/0)]],this._nPoints=0,this._nTriangle=0,this._pointTypes,this._grids=[],this._gridWidth=0,this._gridHeight=0,this._edgeList=new o,this._edgeOut=[],this._edgeIn=[],this._candidates=[]};c.prototype.triangulate=function(t){if(this._nPoints=t.length/2,!(this._nPoints<3))return this._gridNumber=Math.ceil(Math.sqrt(this._nPoints)),this._gridNumber=Math.max(Math.min(this._gridNumber,this.maxGridNumber),this.minGridNumber),this._points=t,this._reset(),this._prepare(),this._earClipping(),this._triangles.length=3*this._nTriangle,this._triangles},c.prototype._reset=function(){this._nTriangle=0,this._edgeList.clear(),this._candidates.length=0,this._pointTypes=new s.Int8Array(this._points.length),this._boundingBox[0][0]=this._boundingBox[0][1]=1/0,this._boundingBox[1][0]=this._boundingBox[1][1]=-(1/0);for(var t=this._gridNumber*this._gridNumber,e=this._grids.length,n=0;nt[1][0]&&(t[1][0]=h),f>t[1][1]&&(t[1][1]=f),t[0][0]-=.1,t[0][1]-=.1,t[1][0]+=.1,t[1][1]+=.1;var p=i(s,c,h,f,d,_);this._pointTypes[r]=p<=0?u:l,p<=0&&this._candidates.push(r),a=r,r++}this._gridWidth=(t[1][0]-t[0][0])/this._gridNumber,this._gridHeight=(t[1][1]-t[0][1])/this._gridNumber;for(var r=0;rn&&(n=s),u>r&&(r=u),ln&&(n=l),c>r&&(r=c),i[0][0]=Math.floor((t-h[0][0])/this._gridWidth),i[1][0]=Math.floor((n-h[0][0])/this._gridWidth),i[0][1]=Math.floor((e-h[0][1])/this._gridHeight),i[1][1]=Math.floor((r-h[0][1])/this._gridHeight),i}}(),c.prototype.isTriangleConvex2=function(t,e,n){return this.triangleArea(t,e,n)<0},c.prototype.triangleArea=function(t,e,n){var r=this._points,i=r[2*t],a=r[2*t+1],o=r[2*e],s=r[2*e+1],u=r[2*n],l=r[2*n+1];return(o-i)*(l-s)-(s-a)*(u-o)};var h={flatPoints:function(t){for(var e=new s.Float64Array(2*t.length),n=0;n=65535?u.indices=new Uint32Array(3*v):u.indices=new Uint16Array(3*v);for(var E=0,b=0,S=a.isUseIndices(),N=0;N0;){for(var E=[],b=[],S=[],N=0,g=0;g=0&&b[L]===-1&&(N65535?H.indices=new Uint32Array(3*k.triangles.length):H.indices=new Uint16Array(3*k.triangles.length);var Y=0;G=0;for(var g=0;g=0?H.attributes.joint.value[Z]=F[L]:H.attributes.joint.value[Z]=-1}G++}H.indices[Y++]=D[C]}I.add(W)}for(var $=t.children(),g=0;g<$.length;g++)I.add($[g]);if(I.position.copy(t.position),I.rotation.copy(t.rotation),I.scale.copy(t.scale),n&&t.getParent()){var tt=t.getParent();tt.remove(t),tt.add(I)}return I}}};t.exports=h},function(t,e,n){function r(t,e){var n=t[0],r=t[1],i=t[2];return 0===e?1:1===e?n:2===e?r:3===e?i:4===e?n*i:5===e?r*i:6===e?n*r:7===e?3*i*i-1:n*n-r*r}function i(t,e,n,i){for(var a=new u.Float32Array(27),o=_.create(),s=_.create(),l=_.create(),c=0;c<9;c++){for(var h=_.create(),f=0;f 0.0) {\n float G = G_Smith(roughness, NoV, NoL);\n float G_Vis = G * VoH / (NoH * NoV);\n float Fc = pow(1.0 - VoH, 5.0);\n A += (1.0 - Fc) * G_Vis;\n B += Fc * G_Vis;\n }\n }\n\n gl_FragColor = vec4(vec2(A, B) / fSampleNumber, 0.0, 1.0);\n}\n"},function(t,e){t.exports="#define SAMPLE_NUMBER 1024\n#define PI 3.14159265358979\n\nuniform mat4 viewInverse : VIEWINVERSE;\nuniform samplerCube environmentMap;\nuniform sampler2D normalDistribution;\n\nuniform float roughness : 0.5;\n\nvarying vec2 v_Texcoord;\nvarying vec3 v_WorldPosition;\n\nconst float fSampleNumber = float(SAMPLE_NUMBER);\n\n@import qtek.util.rgbm\n\nvec3 importanceSampleNormal(float i, float roughness, vec3 N) {\n vec3 H = texture2D(normalDistribution, vec2(roughness, i)).rgb;\n\n vec3 upVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);\n vec3 tangentX = normalize(cross(upVector, N));\n vec3 tangentY = cross(N, tangentX);\n return tangentX * H.x + tangentY * H.y + N * H.z;\n}\n\nvoid main() {\n\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = normalize(v_WorldPosition - eyePos);\n\n vec3 N = V;\n vec3 R = V;\n\n vec3 prefilteredColor = vec3(0.0);\n float totalWeight = 0.0;\n\n\n for (int i = 0; i < SAMPLE_NUMBER; i++) {\n vec3 H = importanceSampleNormal(float(i) / fSampleNumber, roughness, N);\n vec3 L = reflect(-V, H);\n\n float NoL = clamp(dot(N, L), 0.0, 1.0);\n if (NoL > 0.0) {\n prefilteredColor += decodeHDR(textureCube(environmentMap, L)).rgb * NoL;\n totalWeight += NoL;\n }\n }\n\n gl_FragColor = encodeHDR(vec4(prefilteredColor / totalWeight, 1.0));\n}\n"},function(t,e){t.exports="uniform samplerCube environmentMap;\n\nvarying vec2 v_Texcoord;\n\n#define TEXTURE_SIZE 16\n\nmat3 front = mat3(\n 1.0, 0.0, 0.0,\n 0.0, 1.0, 0.0,\n 0.0, 0.0, 1.0\n);\n\nmat3 back = mat3(\n -1.0, 0.0, 0.0,\n 0.0, 1.0, 0.0,\n 0.0, 0.0, -1.0\n);\n\nmat3 left = mat3(\n 0.0, 0.0, -1.0,\n 0.0, 1.0, 0.0,\n 1.0, 0.0, 0.0\n);\n\nmat3 right = mat3(\n 0.0, 0.0, 1.0,\n 0.0, 1.0, 0.0,\n -1.0, 0.0, 0.0\n);\n\nmat3 up = mat3(\n 1.0, 0.0, 0.0,\n 0.0, 0.0, 1.0,\n 0.0, -1.0, 0.0\n);\n\nmat3 down = mat3(\n 1.0, 0.0, 0.0,\n 0.0, 0.0, -1.0,\n 0.0, 1.0, 0.0\n);\n\n\nfloat harmonics(vec3 normal){\n int index = int(gl_FragCoord.x);\n\n float x = normal.x;\n float y = normal.y;\n float z = normal.z;\n\n if(index==0){\n return 1.0;\n }\n else if(index==1){\n return x;\n }\n else if(index==2){\n return y;\n }\n else if(index==3){\n return z;\n }\n else if(index==4){\n return x*z;\n }\n else if(index==5){\n return y*z;\n }\n else if(index==6){\n return x*y;\n }\n else if(index==7){\n return 3.0*z*z - 1.0;\n }\n else{\n return x*x - y*y;\n }\n}\n\nvec3 sampleSide(mat3 rot)\n{\n\n vec3 result = vec3(0.0);\n float divider = 0.0;\n for (int i = 0; i < TEXTURE_SIZE * TEXTURE_SIZE; i++) {\n float x = mod(float(i), float(TEXTURE_SIZE));\n float y = float(i / TEXTURE_SIZE);\n\n vec2 sidecoord = ((vec2(x, y) + vec2(0.5, 0.5)) / vec2(TEXTURE_SIZE)) * 2.0 - 1.0;\n vec3 normal = normalize(vec3(sidecoord, -1.0));\n vec3 fetchNormal = rot * normal;\n vec3 texel = textureCube(environmentMap, fetchNormal).rgb;\n\n result += harmonics(fetchNormal) * texel * -normal.z;\n\n divider += -normal.z;\n }\n\n return result / divider;\n}\n\nvoid main()\n{\n vec3 result = (\n sampleSide(front) +\n sampleSide(back) +\n sampleSide(left) +\n sampleSide(right) +\n sampleSide(up) +\n sampleSide(down)\n ) / 6.0;\n gl_FragColor = vec4(result, 1.0);\n}"},function(t,e){t.exports="0.3.0"},function(t,e,n){function r(t,e,n){return t*(1-n)+e*n}var i=n(15),a=n(6),o=n(12),s=n(4),u=n(2),l=n(22);s["import"](n(157));var c=u.extend(function(){return{clearColor:[0,0,0,1],_mesh:new i({geometry:new o,culling:!1,material:new a({depthTest:!1,shader:new s({vertex:s.source("qtek.vr.disorter.output.vertex"),fragment:s.source("qtek.vr.disorter.output.fragment")})})}),_fakeCamera:new l}},{render:function(t,e){var n=this.clearColor,r=t.gl;r.clearColor(n[0],n[1],n[2],n[3]),r.clear(r.COLOR_BUFFER_BIT),r.disable(r.BLEND),this._mesh.material.set("texture",e),t.saveViewport(),t.setViewport(0,0,t.getWidth(),t.getHeight()),t.renderQueue([this._mesh],this._fakeCamera),t.restoreViewport()},updateFromVRDisplay:function(t){t.deviceInfo_?this._updateMesh(20,20,t.deviceInfo_):console.warn("Cant get vrDisplay.deviceInfo_, seems code changed")},_updateMesh:function(t,e,n){var i=this._mesh.geometry.attributes.position,a=this._mesh.geometry.attributes.texcoord0;i.init(2*t*e),a.init(2*t*e);for(var o=n.getLeftEyeVisibleTanAngles(),s=n.getLeftEyeNoLensTanAngles(),u=n.getLeftEyeVisibleScreenRect(s),l=0,c=[],h=[],f=0;f<2;f++){for(var d=0;d} triangle + * @param {number} triangleIndex * @param {number} distance */ - RayPicking.Intersection = function (point, pointWorld, target, triangle, distance) { + RayPicking.Intersection = function (point, pointWorld, target, triangle, triangleIndex, distance) { /** * Intersection point in local transform coordinates * @type {qtek.math.Vector3} @@ -175,6 +176,10 @@ define(function(require) { * @type {Array.} */ this.triangle = triangle; + /** + * Index of intersection triangle. + */ + this.triangleIndex = triangleIndex; /** * Distance from intersection point to ray origin * @type {number} diff --git a/tests/js/config.js b/tests/js/config.js index 8a5a21cba..c611013b1 100644 --- a/tests/js/config.js +++ b/tests/js/config.js @@ -1,12 +1,12 @@ requirejs.config({ paths: { - // qtek: '../dist/qtek.min' + qtek: '../dist/qtek.min' }, // urlArgs: "bust=" + (new Date()).getTime(), // Debug - packages: [{ - name: 'qtek', - location: '../src', - main: 'qtek' - }] + // packages: [{ + // name: 'qtek', + // location: '../src', + // main: 'qtek' + // }] }); \ No newline at end of file