diff --git a/dist/claygl.es.js b/dist/claygl.es.js index f1e716cd4..c935a38f2 100644 --- a/dist/claygl.es.js +++ b/dist/claygl.es.js @@ -9643,39 +9643,87 @@ Vector3.eulerFromMat3 = function (out, m, order) { return out; }; -// TODO return new. -/** - * @type {clay.Vector3} - */ -Vector3.POSITIVE_X = new Vector3(1, 0, 0); -/** - * @type {clay.Vector3} - */ -Vector3.NEGATIVE_X = new Vector3(-1, 0, 0); -/** - * @type {clay.Vector3} - */ -Vector3.POSITIVE_Y = new Vector3(0, 1, 0); -/** - * @type {clay.Vector3} - */ -Vector3.NEGATIVE_Y = new Vector3(0, -1, 0); -/** - * @type {clay.Vector3} - */ -Vector3.POSITIVE_Z = new Vector3(0, 0, 1); -/** - * @type {clay.Vector3} - */ -Vector3.NEGATIVE_Z = new Vector3(0, 0, -1); -/** - * @type {clay.Vector3} - */ -Vector3.UP = new Vector3(0, 1, 0); -/** - * @type {clay.Vector3} - */ -Vector3.ZERO = new Vector3(0, 0, 0); +Object.defineProperties(Vector3, { + /** + * @type {clay.Vector3} + * @readOnly + * @memberOf clay.Vector3 + */ + POSITIVE_X: { + get: function () { + return new Vector3(1, 0, 0); + } + }, + /** + * @type {clay.Vector3} + * @readOnly + * @memberOf clay.Vector3 + */ + NEGATIVE_X: { + get: function () { + return new Vector3(-1, 0, 0); + } + }, + /** + * @type {clay.Vector3} + * @readOnly + * @memberOf clay.Vector3 + */ + POSITIVE_Y: { + get: function () { + return new Vector3(0, 1, 0); + } + }, + /** + * @type {clay.Vector3} + * @readOnly + * @memberOf clay.Vector3 + */ + NEGATIVE_Y: { + get: function () { + return new Vector3(0, -1, 0); + } + }, + /** + * @type {clay.Vector3} + * @readOnly + * @memberOf clay.Vector3 + */ + POSITIVE_Z: { + get: function () { + return new Vector3(0, 0, 1); + } + }, + /** + * @type {clay.Vector3} + * @readOnly + */ + NEGATIVE_Z: { + get: function () { + return new Vector3(0, 0, -1); + } + }, + /** + * @type {clay.Vector3} + * @readOnly + * @memberOf clay.Vector3 + */ + UP: { + get: function () { + return new Vector3(0, 1, 0); + } + }, + /** + * @type {clay.Vector3} + * @readOnly + * @memberOf clay.Vector3 + */ + ZERO: { + get: function () { + return new Vector3(); + } + } +}); var vec3$3 = glmatrix.vec3; @@ -10702,1891 +10750,2164 @@ Matrix4.translate = function(out, a, v) { return out; }; -var DIRTY_PREFIX = '__dt__'; - -var Cache = function () { +/** + * Simple double linked list. Compared with array, it has O(1) remove operation. + * @constructor + * @alias clay.core.LinkedList + */ +var LinkedList = function () { - this._contextId = 0; + /** + * @type {clay.core.LinkedList.Entry} + */ + this.head = null; - this._caches = []; + /** + * @type {clay.core.LinkedList.Entry} + */ + this.tail = null; - this._context = {}; + this._length = 0; }; -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); - }, +/** + * Insert a new value at the tail + * @param {} val + * @return {clay.core.LinkedList.Entry} + */ +LinkedList.prototype.insert = function (val) { + var entry = new LinkedList.Entry(val); + this.insertEntry(entry); + return entry; +}; - 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; - } +/** + * Insert a new value at idx + * @param {number} idx + * @param {} val + * @return {clay.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; } - }, - - 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; - } + else { + prev.next = entry; + entry.prev = prev; } - }, - - 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); + entry.next = next; + next.prev = entry; + } + else { + this.insert(val); } }; -Cache.prototype.constructor = Cache; +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++; +}; /** - * Base class for all textures like compressed texture, texture2d, texturecube - * TODO mapping - */ -/** - * @constructor - * @alias clay.Texture - * @extends clay.core.Base + * Insert an entry at the tail + * @param {clay.core.LinkedList.Entry} entry */ -var Texture$1 = Base.extend( /** @lends clay.Texture# */ { - /** - * Texture width, readonly when the texture source is image - * @type {number} - */ - width: 512, - /** - * Texture height, readonly when the texture source is image - * @type {number} - */ - height: 512, - /** - * Texel data type. - * Possible values: - * + {@link clay.Texture.UNSIGNED_BYTE} - * + {@link clay.Texture.HALF_FLOAT} - * + {@link clay.Texture.FLOAT} - * + {@link clay.Texture.UNSIGNED_INT_24_8_WEBGL} - * + {@link clay.Texture.UNSIGNED_INT} - * @type {number} - */ - type: glenum.UNSIGNED_BYTE, - /** - * Format of texel data - * Possible values: - * + {@link clay.Texture.RGBA} - * + {@link clay.Texture.DEPTH_COMPONENT} - * + {@link clay.Texture.DEPTH_STENCIL} - * @type {number} - */ - format: glenum.RGBA, - /** - * Texture wrap. Default to be REPEAT. - * Possible values: - * + {@link clay.Texture.CLAMP_TO_EDGE} - * + {@link clay.Texture.REPEAT} - * + {@link clay.Texture.MIRRORED_REPEAT} - * @type {number} - */ - wrapS: glenum.REPEAT, - /** - * Texture wrap. Default to be REPEAT. - * Possible values: - * + {@link clay.Texture.CLAMP_TO_EDGE} - * + {@link clay.Texture.REPEAT} - * + {@link clay.Texture.MIRRORED_REPEAT} - * @type {number} - */ - wrapT: glenum.REPEAT, +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 {clay.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) { /** - * Possible values: - * + {@link clay.Texture.NEAREST} - * + {@link clay.Texture.LINEAR} - * + {@link clay.Texture.NEAREST_MIPMAP_NEAREST} - * + {@link clay.Texture.LINEAR_MIPMAP_NEAREST} - * + {@link clay.Texture.NEAREST_MIPMAP_LINEAR} - * + {@link clay.Texture.LINEAR_MIPMAP_LINEAR} - * @type {number} + * @type {} */ - minFilter: glenum.LINEAR_MIPMAP_LINEAR, + this.value = val; + /** - * Possible values: - * + {@link clay.Texture.NEAREST} - * + {@link clay.Texture.LINEAR} - * @type {number} + * @type {clay.core.LinkedList.Entry} */ - magFilter: glenum.LINEAR, + this.next = null; + /** - * If enable mimap. - * @type {boolean} + * @type {clay.core.LinkedList.Entry} */ - useMipmap: true, + this.prev = null; +}; + +/** + * LRU Cache + * @constructor + * @alias clay.core.LRU + */ +var LRU$1 = function (maxSize) { + + this._list = new LinkedList(); + + this._map = {}; + + this._maxSize = maxSize || 10; +}; + +/** + * Set cache max size + * @param {number} size + */ +LRU$1.prototype.setMaxSize = function (size) { + this._maxSize = size; +}; + +/** + * @param {string} key + * @param {} value + */ +LRU$1.prototype.put = function (key, value) { + if (!this._map.hasOwnProperty(key)) { + 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$1.prototype.get = function (key) { + var entry = this._map[key]; + if (this._map.hasOwnProperty(key)) { + // 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$1.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$1.prototype.clear = function () { + this._list.clear(); + this._map = {}; +}; + +/** + * @namespace clay.core.color + */ +var colorUtil = {}; + +var kCSSColorTable = { + 'transparent': [0,0,0,0], 'aliceblue': [240,248,255,1], + 'antiquewhite': [250,235,215,1], 'aqua': [0,255,255,1], + 'aquamarine': [127,255,212,1], 'azure': [240,255,255,1], + 'beige': [245,245,220,1], 'bisque': [255,228,196,1], + 'black': [0,0,0,1], 'blanchedalmond': [255,235,205,1], + 'blue': [0,0,255,1], 'blueviolet': [138,43,226,1], + 'brown': [165,42,42,1], 'burlywood': [222,184,135,1], + 'cadetblue': [95,158,160,1], 'chartreuse': [127,255,0,1], + 'chocolate': [210,105,30,1], 'coral': [255,127,80,1], + 'cornflowerblue': [100,149,237,1], 'cornsilk': [255,248,220,1], + 'crimson': [220,20,60,1], 'cyan': [0,255,255,1], + 'darkblue': [0,0,139,1], 'darkcyan': [0,139,139,1], + 'darkgoldenrod': [184,134,11,1], 'darkgray': [169,169,169,1], + 'darkgreen': [0,100,0,1], 'darkgrey': [169,169,169,1], + 'darkkhaki': [189,183,107,1], 'darkmagenta': [139,0,139,1], + 'darkolivegreen': [85,107,47,1], 'darkorange': [255,140,0,1], + 'darkorchid': [153,50,204,1], 'darkred': [139,0,0,1], + 'darksalmon': [233,150,122,1], 'darkseagreen': [143,188,143,1], + 'darkslateblue': [72,61,139,1], 'darkslategray': [47,79,79,1], + 'darkslategrey': [47,79,79,1], 'darkturquoise': [0,206,209,1], + 'darkviolet': [148,0,211,1], 'deeppink': [255,20,147,1], + 'deepskyblue': [0,191,255,1], 'dimgray': [105,105,105,1], + 'dimgrey': [105,105,105,1], 'dodgerblue': [30,144,255,1], + 'firebrick': [178,34,34,1], 'floralwhite': [255,250,240,1], + 'forestgreen': [34,139,34,1], 'fuchsia': [255,0,255,1], + 'gainsboro': [220,220,220,1], 'ghostwhite': [248,248,255,1], + 'gold': [255,215,0,1], 'goldenrod': [218,165,32,1], + 'gray': [128,128,128,1], 'green': [0,128,0,1], + 'greenyellow': [173,255,47,1], 'grey': [128,128,128,1], + 'honeydew': [240,255,240,1], 'hotpink': [255,105,180,1], + 'indianred': [205,92,92,1], 'indigo': [75,0,130,1], + 'ivory': [255,255,240,1], 'khaki': [240,230,140,1], + 'lavender': [230,230,250,1], 'lavenderblush': [255,240,245,1], + 'lawngreen': [124,252,0,1], 'lemonchiffon': [255,250,205,1], + 'lightblue': [173,216,230,1], 'lightcoral': [240,128,128,1], + 'lightcyan': [224,255,255,1], 'lightgoldenrodyellow': [250,250,210,1], + 'lightgray': [211,211,211,1], 'lightgreen': [144,238,144,1], + 'lightgrey': [211,211,211,1], 'lightpink': [255,182,193,1], + 'lightsalmon': [255,160,122,1], 'lightseagreen': [32,178,170,1], + 'lightskyblue': [135,206,250,1], 'lightslategray': [119,136,153,1], + 'lightslategrey': [119,136,153,1], 'lightsteelblue': [176,196,222,1], + 'lightyellow': [255,255,224,1], 'lime': [0,255,0,1], + 'limegreen': [50,205,50,1], 'linen': [250,240,230,1], + 'magenta': [255,0,255,1], 'maroon': [128,0,0,1], + 'mediumaquamarine': [102,205,170,1], 'mediumblue': [0,0,205,1], + 'mediumorchid': [186,85,211,1], 'mediumpurple': [147,112,219,1], + 'mediumseagreen': [60,179,113,1], 'mediumslateblue': [123,104,238,1], + 'mediumspringgreen': [0,250,154,1], 'mediumturquoise': [72,209,204,1], + 'mediumvioletred': [199,21,133,1], 'midnightblue': [25,25,112,1], + 'mintcream': [245,255,250,1], 'mistyrose': [255,228,225,1], + 'moccasin': [255,228,181,1], 'navajowhite': [255,222,173,1], + 'navy': [0,0,128,1], 'oldlace': [253,245,230,1], + 'olive': [128,128,0,1], 'olivedrab': [107,142,35,1], + 'orange': [255,165,0,1], 'orangered': [255,69,0,1], + 'orchid': [218,112,214,1], 'palegoldenrod': [238,232,170,1], + 'palegreen': [152,251,152,1], 'paleturquoise': [175,238,238,1], + 'palevioletred': [219,112,147,1], 'papayawhip': [255,239,213,1], + 'peachpuff': [255,218,185,1], 'peru': [205,133,63,1], + 'pink': [255,192,203,1], 'plum': [221,160,221,1], + 'powderblue': [176,224,230,1], 'purple': [128,0,128,1], + 'red': [255,0,0,1], 'rosybrown': [188,143,143,1], + 'royalblue': [65,105,225,1], 'saddlebrown': [139,69,19,1], + 'salmon': [250,128,114,1], 'sandybrown': [244,164,96,1], + 'seagreen': [46,139,87,1], 'seashell': [255,245,238,1], + 'sienna': [160,82,45,1], 'silver': [192,192,192,1], + 'skyblue': [135,206,235,1], 'slateblue': [106,90,205,1], + 'slategray': [112,128,144,1], 'slategrey': [112,128,144,1], + 'snow': [255,250,250,1], 'springgreen': [0,255,127,1], + 'steelblue': [70,130,180,1], 'tan': [210,180,140,1], + 'teal': [0,128,128,1], 'thistle': [216,191,216,1], + 'tomato': [255,99,71,1], 'turquoise': [64,224,208,1], + 'violet': [238,130,238,1], 'wheat': [245,222,179,1], + 'white': [255,255,255,1], 'whitesmoke': [245,245,245,1], + 'yellow': [255,255,0,1], 'yellowgreen': [154,205,50,1] +}; + +function clampCssByte(i) { // Clamp to integer 0 .. 255. + i = Math.round(i); // Seems to be what Chrome does (vs truncation). + return i < 0 ? 0 : i > 255 ? 255 : i; +} + +function clampCssAngle(i) { // Clamp to integer 0 .. 360. + i = Math.round(i); // Seems to be what Chrome does (vs truncation). + return i < 0 ? 0 : i > 360 ? 360 : i; +} + +function clampCssFloat(f) { // Clamp to float 0.0 .. 1.0. + return f < 0 ? 0 : f > 1 ? 1 : f; +} + +function parseCssInt(str) { // int or percentage. + if (str.length && str.charAt(str.length - 1) === '%') { + return clampCssByte(parseFloat(str) / 100 * 255); + } + return clampCssByte(parseInt(str, 10)); +} + +function parseCssFloat(str) { // float or percentage. + if (str.length && str.charAt(str.length - 1) === '%') { + return clampCssFloat(parseFloat(str) / 100); + } + return clampCssFloat(parseFloat(str)); +} + +function cssHueToRgb(m1, m2, h) { + if (h < 0) { + h += 1; + } + else if (h > 1) { + h -= 1; + } + + if (h * 6 < 1) { + return m1 + (m2 - m1) * h * 6; + } + if (h * 2 < 1) { + return m2; + } + if (h * 3 < 2) { + return m1 + (m2 - m1) * (2/3 - h) * 6; + } + return m1; +} + +function lerpNumber(a, b, p) { + return a + (b - a) * p; +} + +function setRgba(out, r, g, b, a) { + out[0] = r; out[1] = g; out[2] = b; out[3] = a; + return out; +} +function copyRgba(out, a) { + out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3]; + return out; +} + +var colorCache = new LRU$1(20); +var lastRemovedArr = null; + +function putToCache(colorStr, rgbaArr) { + // Reuse removed array + if (lastRemovedArr) { + copyRgba(lastRemovedArr, rgbaArr); + } + lastRemovedArr = colorCache.put(colorStr, lastRemovedArr || (rgbaArr.slice())); +} + +/** + * @name clay.core.color.parse + * @param {string} colorStr + * @param {Array.} out + * @return {Array.} + */ +colorUtil.parse = function (colorStr, rgbaArr) { + if (!colorStr) { + return; + } + rgbaArr = rgbaArr || []; + + var cached = colorCache.get(colorStr); + if (cached) { + return copyRgba(rgbaArr, cached); + } + + // colorStr may be not string + colorStr = colorStr + ''; + // Remove all whitespace, not compliant, but should just be more accepting. + var str = colorStr.replace(/ /g, '').toLowerCase(); + + // Color keywords (and transparent) lookup. + if (str in kCSSColorTable) { + copyRgba(rgbaArr, kCSSColorTable[str]); + putToCache(colorStr, rgbaArr); + return rgbaArr; + } + + // #abc and #abc123 syntax. + if (str.charAt(0) === '#') { + if (str.length === 4) { + var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing. + if (!(iv >= 0 && iv <= 0xfff)) { + setRgba(rgbaArr, 0, 0, 0, 1); + return; // Covers NaN. + } + setRgba(rgbaArr, + ((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8), + (iv & 0xf0) | ((iv & 0xf0) >> 4), + (iv & 0xf) | ((iv & 0xf) << 4), + 1 + ); + putToCache(colorStr, rgbaArr); + return rgbaArr; + } + else if (str.length === 7) { + var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing. + if (!(iv >= 0 && iv <= 0xffffff)) { + setRgba(rgbaArr, 0, 0, 0, 1); + return; // Covers NaN. + } + setRgba(rgbaArr, + (iv & 0xff0000) >> 16, + (iv & 0xff00) >> 8, + iv & 0xff, + 1 + ); + putToCache(colorStr, rgbaArr); + return rgbaArr; + } + + return; + } + var op = str.indexOf('('), ep = str.indexOf(')'); + if (op !== -1 && ep + 1 === str.length) { + var fname = str.substr(0, op); + var params = str.substr(op + 1, ep - (op + 1)).split(','); + var alpha = 1; // To allow case fallthrough. + switch (fname) { + case 'rgba': + if (params.length !== 4) { + setRgba(rgbaArr, 0, 0, 0, 1); + return; + } + alpha = parseCssFloat(params.pop()); // jshint ignore:line + // Fall through. + case 'rgb': + if (params.length !== 3) { + setRgba(rgbaArr, 0, 0, 0, 1); + return; + } + setRgba(rgbaArr, + parseCssInt(params[0]), + parseCssInt(params[1]), + parseCssInt(params[2]), + alpha + ); + putToCache(colorStr, rgbaArr); + return rgbaArr; + case 'hsla': + if (params.length !== 4) { + setRgba(rgbaArr, 0, 0, 0, 1); + return; + } + params[3] = parseCssFloat(params[3]); + hsla2rgba(params, rgbaArr); + putToCache(colorStr, rgbaArr); + return rgbaArr; + case 'hsl': + if (params.length !== 3) { + setRgba(rgbaArr, 0, 0, 0, 1); + return; + } + hsla2rgba(params, rgbaArr); + putToCache(colorStr, rgbaArr); + return rgbaArr; + default: + return; + } + } + + setRgba(rgbaArr, 0, 0, 0, 1); + return; +}; + +colorUtil.parseToFloat = function (colorStr, rgbaArr) { + rgbaArr = colorUtil.parse(colorStr, rgbaArr); + if (!rgbaArr) { + return; + } + rgbaArr[0] /= 255; + rgbaArr[1] /= 255; + rgbaArr[2] /= 255; + return rgbaArr; +}; + +/** + * @name clay.core.color.hsla2rgba + * @param {Array.} hsla + * @param {Array.} rgba + * @return {Array.} rgba + */ +function hsla2rgba(hsla, rgba) { + var h = (((parseFloat(hsla[0]) % 360) + 360) % 360) / 360; // 0 .. 1 + // NOTE(deanm): According to the CSS spec s/l should only be + // percentages, but we don't bother and let float or percentage. + var s = parseCssFloat(hsla[1]); + var l = parseCssFloat(hsla[2]); + var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; + var m1 = l * 2 - m2; + + rgba = rgba || []; + setRgba(rgba, + clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255), + clampCssByte(cssHueToRgb(m1, m2, h) * 255), + clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255), + 1 + ); + + if (hsla.length === 4) { + rgba[3] = hsla[3]; + } + + return rgba; +} + +/** + * @name clay.core.color.rgba2hsla + * @param {Array.} rgba + * @return {Array.} hsla + */ +function rgba2hsla(rgba) { + if (!rgba) { + return; + } + + // RGB from 0 to 255 + var R = rgba[0] / 255; + var G = rgba[1] / 255; + var B = rgba[2] / 255; + + var vMin = Math.min(R, G, B); // Min. value of RGB + var vMax = Math.max(R, G, B); // Max. value of RGB + var delta = vMax - vMin; // Delta RGB value + + var L = (vMax + vMin) / 2; + var H; + var S; + // HSL results from 0 to 1 + if (delta === 0) { + H = 0; + S = 0; + } + else { + if (L < 0.5) { + S = delta / (vMax + vMin); + } + else { + S = delta / (2 - vMax - vMin); + } + + var deltaR = (((vMax - R) / 6) + (delta / 2)) / delta; + var deltaG = (((vMax - G) / 6) + (delta / 2)) / delta; + var deltaB = (((vMax - B) / 6) + (delta / 2)) / delta; + + if (R === vMax) { + H = deltaB - deltaG; + } + else if (G === vMax) { + H = (1 / 3) + deltaR - deltaB; + } + else if (B === vMax) { + H = (2 / 3) + deltaG - deltaR; + } + + if (H < 0) { + H += 1; + } + + if (H > 1) { + H -= 1; + } + } + + var hsla = [H * 360, S, L]; + + if (rgba[3] != null) { + hsla.push(rgba[3]); + } + + return hsla; +} + +/** + * @name clay.core.color.lift + * @param {string} color + * @param {number} level + * @return {string} + */ +colorUtil.lift = function (color, level) { + var colorArr = colorUtil.parse(color); + if (colorArr) { + for (var i = 0; i < 3; i++) { + if (level < 0) { + colorArr[i] = colorArr[i] * (1 - level) | 0; + } + else { + colorArr[i] = ((255 - colorArr[i]) * level + colorArr[i]) | 0; + } + } + return colorUtil.stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb'); + } +}; + +/** + * @name clay.core.color.toHex + * @param {string} color + * @return {string} + */ +colorUtil.toHex = function (color) { + var colorArr = colorUtil.parse(color); + if (colorArr) { + return ((1 << 24) + (colorArr[0] << 16) + (colorArr[1] << 8) + (+colorArr[2])).toString(16).slice(1); + } +}; + +/** + * Map value to color. Faster than lerp methods because color is represented by rgba array. + * @name clay.core.color + * @param {number} normalizedValue A float between 0 and 1. + * @param {Array.>} colors List of rgba color array + * @param {Array.} [out] Mapped gba color array + * @return {Array.} will be null/undefined if input illegal. + */ +colorUtil.fastLerp = function (normalizedValue, colors, out) { + if (!(colors && colors.length) + || !(normalizedValue >= 0 && normalizedValue <= 1) + ) { + return; + } + + out = out || []; + + var value = normalizedValue * (colors.length - 1); + var leftIndex = Math.floor(value); + var rightIndex = Math.ceil(value); + var leftColor = colors[leftIndex]; + var rightColor = colors[rightIndex]; + var dv = value - leftIndex; + out[0] = clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)); + out[1] = clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)); + out[2] = clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)); + out[3] = clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv)); + + return out; +}; + +colorUtil.fastMapToColor = colorUtil.fastLerp; + +/** + * @param {number} normalizedValue A float between 0 and 1. + * @param {Array.} colors Color list. + * @param {boolean=} fullOutput Default false. + * @return {(string|Object)} Result color. If fullOutput, + * return {color: ..., leftIndex: ..., rightIndex: ..., value: ...}, + */ +colorUtil.lerp = function (normalizedValue, colors, fullOutput) { + if (!(colors && colors.length) + || !(normalizedValue >= 0 && normalizedValue <= 1) + ) { + return; + } + + var value = normalizedValue * (colors.length - 1); + var leftIndex = Math.floor(value); + var rightIndex = Math.ceil(value); + var leftColor = colorUtil.parse(colors[leftIndex]); + var rightColor = colorUtil.parse(colors[rightIndex]); + var dv = value - leftIndex; + + var color = colorUtil.stringify( + [ + clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)), + clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)), + clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)), + clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv)) + ], + 'rgba' + ); + + return fullOutput + ? { + color: color, + leftIndex: leftIndex, + rightIndex: rightIndex, + value: value + } + : color; +}; + +/** + * @deprecated + */ +colorUtil.mapToColor = colorUtil.lerp; + +/** + * @name clay.core.color + * @param {string} color + * @param {number=} h 0 ~ 360, ignore when null. + * @param {number=} s 0 ~ 1, ignore when null. + * @param {number=} l 0 ~ 1, ignore when null. + * @return {string} Color string in rgba format. + */ +colorUtil.modifyHSL = function (color, h, s, l) { + color = colorUtil.parse(color); - /** - * Anisotropic filtering, enabled if value is larger than 1 - * @see https://developer.mozilla.org/en-US/docs/Web/API/EXT_texture_filter_anisotropic - * @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 - /** - * If flip in y axis for given image source - * @type {boolean} - * @default true - */ - flipY: true, + if (color) { + color = rgba2hsla(color); + h != null && (color[0] = clampCssAngle(h)); + s != null && (color[1] = parseCssFloat(s)); + l != null && (color[2] = parseCssFloat(l)); - /** - * A flag to indicate if texture source is sRGB - */ - sRGB: true, - /** - * @type {number} - * @default 4 - */ - unpackAlignment: 4, - /** - * @type {boolean} - * @default false - */ - premultiplyAlpha: false, + return colorUtil.stringify(hsla2rgba(color), 'rgba'); + } +}; - /** - * Dynamic option for texture like video - * @type {boolean} - */ - dynamic: false, +/** + * @param {string} color + * @param {number=} alpha 0 ~ 1 + * @return {string} Color string in rgba format. + */ +colorUtil.modifyAlpha = function (color, alpha) { + color = colorUtil.parse(color); - NPOT: false, + if (color && alpha != null) { + color[3] = clampCssFloat(alpha); + return colorUtil.stringify(color, 'rgba'); + } +}; - // PENDING - // Init it here to avoid deoptimization when it's assigned in application dynamically - __used: 0 +/** + * @param {Array.} arrColor like [12,33,44,0.4] + * @param {string} type 'rgba', 'hsva', ... + * @return {string} Result color. (If input illegal, return undefined). + */ +colorUtil.stringify = function (arrColor, type) { + if (!arrColor || !arrColor.length) { + return; + } + var colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2]; + if (type === 'rgba' || type === 'hsva' || type === 'hsla') { + colorStr += ',' + arrColor[3]; + } + return type + '(' + colorStr + ')'; +}; -}, function () { - this._cache = new Cache(); -}, -/** @lends clay.Texture.prototype */ -{ +var parseColor$1 = colorUtil.parseToFloat; - getWebGLTexture: function (renderer) { - var _gl = renderer.gl; - var cache = this._cache; - cache.use(renderer.__uid__); +var programKeyCache = {}; - 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(renderer); +function getDefineCode(defines) { + var defineKeys = Object.keys(defines); + defineKeys.sort(); + var defineStr = []; + // Custom Defines + for (var i = 0; i < defineKeys.length; i++) { + var key = defineKeys[i]; + var value = defines[key]; + if (value === null) { + defineStr.push(key); } - else if (cache.isDirty()) { - this.update(renderer); - cache.fresh(); + else{ + defineStr.push(key + ' ' + value.toString()); } + } + return defineStr.join('\n'); +} - return cache.get('webgl_texture'); - }, +function getProgramKey(vertexDefines, fragmentDefines, enabledTextures) { + enabledTextures.sort(); + var defineStr = []; + for (var i = 0; i < enabledTextures.length; i++) { + var symbol = enabledTextures[i]; + defineStr.push(symbol); + } + var key = getDefineCode(vertexDefines) + '\n' + + getDefineCode(fragmentDefines) + '\n' + + defineStr.join('\n'); - bind: function () {}, - unbind: function () {}, + if (programKeyCache[key]) { + return programKeyCache[key]; + } - /** - * Mark texture is dirty and update in the next frame - */ - dirty: function () { - if (this._cache) { - this._cache.dirtyAll(); - } - }, + var id = util$1.genGUID(); + programKeyCache[key] = id; + return id; +} - update: function (renderer) {}, +/** + * Material defines the appearance of mesh surface, like `color`, `roughness`, `metalness`, etc. + * It contains a {@link clay.Shader} and corresponding uniforms. + * + * Here is a basic example to create a standard material +```js +var material = new clay.Material({ + shader: new clay.Shader( + clay.Shader.source('clay.vertex'), + clay.Shader.source('clay.fragment') + ) +}); +``` + * @constructor clay.Material + * @extends clay.core.Base + */ +var Material = Base.extend(function () { + return /** @lends clay.Material# */ { + /** + * @type {string} + */ + name: '', - // Update the common parameters of texture - updateCommon: function (renderer) { - var _gl = renderer.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); + /** + * @type {Object} + */ + // uniforms: null, - // Use of none-power of two texture - // http://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences - if (this.format === glenum.DEPTH_COMPONENT) { - this.useMipmap = false; + /** + * @type {clay.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, + + /** + * If update texture status automatically. + */ + autoUpdateTextureStatus: true, + + uniforms: {}, + vertexDefines: {}, + fragmentDefines: {}, + _textureStatus: {}, + + // shadowTransparentMap : null + + // PENDING enable the uniform that only used in shader. + _enabledUniforms: null, + }; +}, function () { + if (!this.name) { + this.name = 'MATERIAL_' + this.__uid__; + } + + if (this.shader) { + // Keep status, mainly preset uniforms, vertexDefines and fragmentDefines + this.attachShader(this.shader, true); + } +}, +/** @lends clay.Material.prototype */ +{ + precision: 'highp', + + /** + * Set material uniform + * @example + * mat.setUniform('color', [1, 1, 1, 1]); + * @param {string} symbol + * @param {number|array|clay.Texture|ArrayBufferView} value + */ + setUniform: function (symbol, value) { + if (value === undefined) { + console.warn('Uniform value "' + symbol + '" is undefined'); } + var uniform = this.uniforms[symbol]; + if (uniform) { - var sRGBExt = renderer.getGLExtension('EXT_sRGB'); - // Fallback - if (this.format === Texture$1.SRGB && !sRGBExt) { - this.format = Texture$1.RGB; - } - if (this.format === Texture$1.SRGB_ALPHA && !sRGBExt) { - this.format = Texture$1.RGBA; - } + if (typeof value === 'string') { + // Try to parse as a color. Invalid color string will return null. + value = parseColor$1(value) || value; + } - this.NPOT = !this.isPowerOfTwo(); - }, + uniform.value = value; - getAvailableWrapS: function () { - if (this.NPOT) { - return glenum.CLAMP_TO_EDGE; - } - return this.wrapS; - }, - getAvailableWrapT: function () { - if (this.NPOT) { - return glenum.CLAMP_TO_EDGE; - } - return this.wrapT; - }, - getAvailableMinFilter: function () { - var minFilter = this.minFilter; - if (this.NPOT || !this.useMipmap) { - if (minFilter === glenum.NEAREST_MIPMAP_NEAREST || - minFilter === glenum.NEAREST_MIPMAP_LINEAR - ) { - return glenum.NEAREST; - } - else if (minFilter === glenum.LINEAR_MIPMAP_LINEAR || - minFilter === glenum.LINEAR_MIPMAP_NEAREST - ) { - return glenum.LINEAR; - } - else { - return minFilter; + if (this.autoUpdateTextureStatus && uniform.type === 't') { + if (value) { + this.enableTexture(symbol); + } + else { + this.disableTexture(symbol); + } } } - else { - return minFilter; - } - }, - getAvailableMagFilter: function () { - return this.magFilter; }, - nextHighestPowerOfTwo: function (x) { - --x; - for (var i = 1; i < 32; i <<= 1) { - x = x | x >> i; - } - return x + 1; - }, /** - * @param {clay.Renderer} renderer + * @param {Object} obj */ - dispose: function (renderer) { - - var cache = this._cache; - - cache.use(renderer.__uid__); - - var webglTexture = cache.get('webgl_texture'); - if (webglTexture){ - renderer.gl.deleteTexture(webglTexture); + setUniforms: function(obj) { + for (var key in obj) { + var val = obj[key]; + this.setUniform(key, val); } - cache.deleteContext(renderer.__uid__); - }, - /** - * Test if image of texture is valid and loaded. - * @return {boolean} - */ - isRenderable: function () {}, /** - * Test if texture size is power of two + * @param {string} symbol * @return {boolean} */ - isPowerOfTwo: function () {} -}); + isUniformEnabled: function (symbol) { + return this._enabledUniforms.indexOf(symbol) >= 0; + }, -Object.defineProperty(Texture$1.prototype, 'width', { - get: function () { - return this._width; + getEnabledUniforms: function () { + return this._enabledUniforms; }, - set: function (value) { - this._width = value; - } -}); -Object.defineProperty(Texture$1.prototype, 'height', { - get: function () { - return this._height; + getTextureUniforms: function () { + return this._textureUniforms; }, - set: function (value) { - this._height = value; - } -}); - -/* DataType */ - -/** - * @type {number} - */ -Texture$1.BYTE = glenum.BYTE; -/** - * @type {number} - */ -Texture$1.UNSIGNED_BYTE = glenum.UNSIGNED_BYTE; -/** - * @type {number} - */ -Texture$1.SHORT = glenum.SHORT; -/** - * @type {number} - */ -Texture$1.UNSIGNED_SHORT = glenum.UNSIGNED_SHORT; -/** - * @type {number} - */ -Texture$1.INT = glenum.INT; -/** - * @type {number} - */ -Texture$1.UNSIGNED_INT = glenum.UNSIGNED_INT; -/** - * @type {number} - */ -Texture$1.FLOAT = glenum.FLOAT; -/** - * @type {number} - */ -Texture$1.HALF_FLOAT = 0x8D61; - -/** - * UNSIGNED_INT_24_8_WEBGL for WEBGL_depth_texture extension - * @type {number} - */ -Texture$1.UNSIGNED_INT_24_8_WEBGL = 34042; - -/* PixelFormat */ -/** - * @type {number} - */ -Texture$1.DEPTH_COMPONENT = glenum.DEPTH_COMPONENT; -/** - * @type {number} - */ -Texture$1.DEPTH_STENCIL = glenum.DEPTH_STENCIL; -/** - * @type {number} - */ -Texture$1.ALPHA = glenum.ALPHA; -/** - * @type {number} - */ -Texture$1.RGB = glenum.RGB; -/** - * @type {number} - */ -Texture$1.RGBA = glenum.RGBA; -/** - * @type {number} - */ -Texture$1.LUMINANCE = glenum.LUMINANCE; -/** - * @type {number} - */ -Texture$1.LUMINANCE_ALPHA = glenum.LUMINANCE_ALPHA; - -/** - * @see https://www.khronos.org/registry/webgl/extensions/EXT_sRGB/ - * @type {number} - */ -Texture$1.SRGB = 0x8C40; -/** - * @see https://www.khronos.org/registry/webgl/extensions/EXT_sRGB/ - * @type {number} - */ -Texture$1.SRGB_ALPHA = 0x8C42; - -/* Compressed Texture */ -Texture$1.COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0; -Texture$1.COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1; -Texture$1.COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2; -Texture$1.COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3; - -/* TextureMagFilter */ -/** - * @type {number} - */ -Texture$1.NEAREST = glenum.NEAREST; -/** - * @type {number} - */ -Texture$1.LINEAR = glenum.LINEAR; - -/* TextureMinFilter */ -/** - * @type {number} - */ -Texture$1.NEAREST_MIPMAP_NEAREST = glenum.NEAREST_MIPMAP_NEAREST; -/** - * @type {number} - */ -Texture$1.LINEAR_MIPMAP_NEAREST = glenum.LINEAR_MIPMAP_NEAREST; -/** - * @type {number} - */ -Texture$1.NEAREST_MIPMAP_LINEAR = glenum.NEAREST_MIPMAP_LINEAR; -/** - * @type {number} - */ -Texture$1.LINEAR_MIPMAP_LINEAR = glenum.LINEAR_MIPMAP_LINEAR; - -/* TextureWrapMode */ -/** - * @type {number} - */ -Texture$1.REPEAT = glenum.REPEAT; -/** - * @type {number} - */ -Texture$1.CLAMP_TO_EDGE = glenum.CLAMP_TO_EDGE; -/** - * @type {number} - */ -Texture$1.MIRRORED_REPEAT = glenum.MIRRORED_REPEAT; - -/** - * Simple double linked list. Compared with array, it has O(1) remove operation. - * @constructor - * @alias clay.core.LinkedList - */ -var LinkedList = function () { - - /** - * @type {clay.core.LinkedList.Entry} - */ - this.head = null; /** - * @type {clay.core.LinkedList.Entry} - */ - this.tail = null; - - this._length = 0; -}; - -/** - * Insert a new value at the tail - * @param {} val - * @return {clay.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 {clay.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; + * Alias of setUniform and setUniforms + * @param {object|string} symbol + * @param {number|array|clay.Texture|ArrayBufferView} [value] + */ + set: function (symbol, value) { + if (typeof(symbol) === 'object') { + for (var key in symbol) { + var val = symbol[key]; + this.setUniform(key, val); + } } else { - prev.next = entry; - entry.prev = prev; + this.setUniform(symbol, value); } - entry.next = next; - next.prev = entry; - } - else { - this.insert(val); - } -}; + }, + /** + * Get uniform value + * @param {string} symbol + * @return {number|array|clay.Texture|ArrayBufferView} + */ + get: function (symbol) { + var uniform = this.uniforms[symbol]; + if (uniform) { + return uniform.value; + } + }, + /** + * Attach a shader instance + * @param {clay.Shader} shader + * @param {boolean} keepStatus If try to keep uniform and texture + */ + attachShader: function(shader, keepStatus) { + var originalUniforms = this.uniforms; -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; + // Ignore if uniform can use in shader. + this.uniforms = shader.createUniforms(); + this.shader = shader; - this._length++; -}; + 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(); + this._textureUniforms = this._enabledUniforms.filter(function (uniformName) { + var type = this.uniforms[uniformName].type; + return type === 't' || type === 'tv'; + }, this); -/** - * Insert an entry at the tail - * @param {clay.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++; -}; + var originalVertexDefines = this.vertexDefines; + var originalFragmentDefines = this.fragmentDefines; -/** - * Remove entry. - * @param {clay.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--; -}; + this.vertexDefines = util$1.clone(shader.vertexDefines); + this.fragmentDefines = util$1.clone(shader.fragmentDefines); -/** - * 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; -}; + if (keepStatus) { + for (var symbol in originalUniforms) { + if (uniforms[symbol]) { + uniforms[symbol].value = originalUniforms[symbol].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; + util$1.defaults(this.vertexDefines, originalVertexDefines); + util$1.defaults(this.fragmentDefines, originalFragmentDefines); } - curr = curr.next; - cursor++; - } -}; -/** - * @return {number} - */ -LinkedList.prototype.length = function () { - return this._length; -}; + var textureStatus = {}; + for (var key in shader.textures) { + textureStatus[key] = { + shaderType: shader.textures[key].shaderType, + type: shader.textures[key].type, + enabled: (keepStatus && this._textureStatus[key]) ? this._textureStatus[key].enabled : false + }; + } -/** - * If list is empty - */ -LinkedList.prototype.isEmpty = function () { - return this._length === 0; -}; + this._textureStatus = textureStatus; -/** - * @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); + this._programKey = ''; + }, + + /** + * Clone a new material and keep uniforms, shader will not be cloned + * @return {clay.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; + + material.vertexDefines = util$1.clone(this.vertexDefines); + material.fragmentDefines = util$1.clone(this.fragmentDefines); + material.enableTexture(this.getEnabledTextures()); + material.precision = this.precision; + + return material; + }, + + /** + * Add a #define macro 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'; } - else { - cb(curr.value, idx); + val = val != null ? val : null; + if (shaderType === 'vertex' || shaderType === 'both') { + if (vertexDefines[symbol] !== val) { + vertexDefines[symbol] = val; + // Mark as dirty + this._programKey = ''; + } } - curr = curr.next; - idx++; - } -}; - -/** - * Clear the list - */ -LinkedList.prototype.clear = function () { - this.tail = this.head = null; - this._length = 0; -}; + if (shaderType === 'fragment' || shaderType === 'both') { + if (fragmentDefines[symbol] !== val) { + fragmentDefines[symbol] = val; + if (shaderType !== 'both') { + this._programKey = ''; + } + } + } + }, -/** - * @constructor - * @param {} val - */ -LinkedList.Entry = function (val) { /** - * @type {} + * Remove a #define macro in shader code + * @param {string} shaderType Can be vertex, fragment or both + * @param {string} symbol */ - this.value = val; + 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._programKey = ''; + } + } + if (shaderType === 'fragment' || shaderType === 'both') { + if (this.isDefined('fragment', symbol)) { + delete this.fragmentDefines[symbol]; + if (shaderType !== 'both') { + this._programKey = ''; + } + } + } + }, /** - * @type {clay.core.LinkedList.Entry} + * If macro is defined in shader. + * @param {string} shaderType Can be vertex, fragment or both + * @param {string} symbol */ - this.next = null; - + isDefined: function (shaderType, symbol) { + // PENDING hasOwnProperty ? + switch (shaderType) { + case 'vertex': + return this.vertexDefines[symbol] !== undefined; + case 'fragment': + return this.fragmentDefines[symbol] !== undefined; + } + }, /** - * @type {clay.core.LinkedList.Entry} + * Get macro value defined in shader. + * @param {string} shaderType Can be vertex, fragment or both + * @param {string} symbol */ - this.prev = null; -}; - -/** - * LRU Cache - * @constructor - * @alias clay.core.LRU - */ -var LRU$1 = function(maxSize) { - - this._list = new LinkedList(); - - this._map = {}; - - this._maxSize = maxSize || 10; -}; - -/** - * Set cache max size - * @param {number} size - */ -LRU$1.prototype.setMaxSize = function(size) { - this._maxSize = size; -}; - -/** - * @param {string} key - * @param {} value - */ -LRU$1.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]; + getDefine: function (shaderType, symbol) { + switch(shaderType) { + case 'vertex': + return this.vertexDefines[symbol]; + case 'fragment': + return this.fragmentDefines[symbol]; } - - var entry = this._list.insert(value); - entry.key = key; - this._map[key] = entry; - } -}; - -/** - * @param {string} key - * @return {} - */ -LRU$1.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); + }, + /** + * Enable a texture, actually it will add a #define macro 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 (Array.isArray(symbol)) { + for (var i = 0; i < symbol.length; i++) { + this.enableTexture(symbol[i]); + } + return; } - return entry.value; - } -}; - -/** - * @param {string} key - */ -LRU$1.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$1.prototype.clear = function() { - this._list.clear(); - this._map = {}; -}; - -/** - * @namespace clay.core.color - */ -var colorUtil = {}; - -var kCSSColorTable = { - 'transparent': [0,0,0,0], 'aliceblue': [240,248,255,1], - 'antiquewhite': [250,235,215,1], 'aqua': [0,255,255,1], - 'aquamarine': [127,255,212,1], 'azure': [240,255,255,1], - 'beige': [245,245,220,1], 'bisque': [255,228,196,1], - 'black': [0,0,0,1], 'blanchedalmond': [255,235,205,1], - 'blue': [0,0,255,1], 'blueviolet': [138,43,226,1], - 'brown': [165,42,42,1], 'burlywood': [222,184,135,1], - 'cadetblue': [95,158,160,1], 'chartreuse': [127,255,0,1], - 'chocolate': [210,105,30,1], 'coral': [255,127,80,1], - 'cornflowerblue': [100,149,237,1], 'cornsilk': [255,248,220,1], - 'crimson': [220,20,60,1], 'cyan': [0,255,255,1], - 'darkblue': [0,0,139,1], 'darkcyan': [0,139,139,1], - 'darkgoldenrod': [184,134,11,1], 'darkgray': [169,169,169,1], - 'darkgreen': [0,100,0,1], 'darkgrey': [169,169,169,1], - 'darkkhaki': [189,183,107,1], 'darkmagenta': [139,0,139,1], - 'darkolivegreen': [85,107,47,1], 'darkorange': [255,140,0,1], - 'darkorchid': [153,50,204,1], 'darkred': [139,0,0,1], - 'darksalmon': [233,150,122,1], 'darkseagreen': [143,188,143,1], - 'darkslateblue': [72,61,139,1], 'darkslategray': [47,79,79,1], - 'darkslategrey': [47,79,79,1], 'darkturquoise': [0,206,209,1], - 'darkviolet': [148,0,211,1], 'deeppink': [255,20,147,1], - 'deepskyblue': [0,191,255,1], 'dimgray': [105,105,105,1], - 'dimgrey': [105,105,105,1], 'dodgerblue': [30,144,255,1], - 'firebrick': [178,34,34,1], 'floralwhite': [255,250,240,1], - 'forestgreen': [34,139,34,1], 'fuchsia': [255,0,255,1], - 'gainsboro': [220,220,220,1], 'ghostwhite': [248,248,255,1], - 'gold': [255,215,0,1], 'goldenrod': [218,165,32,1], - 'gray': [128,128,128,1], 'green': [0,128,0,1], - 'greenyellow': [173,255,47,1], 'grey': [128,128,128,1], - 'honeydew': [240,255,240,1], 'hotpink': [255,105,180,1], - 'indianred': [205,92,92,1], 'indigo': [75,0,130,1], - 'ivory': [255,255,240,1], 'khaki': [240,230,140,1], - 'lavender': [230,230,250,1], 'lavenderblush': [255,240,245,1], - 'lawngreen': [124,252,0,1], 'lemonchiffon': [255,250,205,1], - 'lightblue': [173,216,230,1], 'lightcoral': [240,128,128,1], - 'lightcyan': [224,255,255,1], 'lightgoldenrodyellow': [250,250,210,1], - 'lightgray': [211,211,211,1], 'lightgreen': [144,238,144,1], - 'lightgrey': [211,211,211,1], 'lightpink': [255,182,193,1], - 'lightsalmon': [255,160,122,1], 'lightseagreen': [32,178,170,1], - 'lightskyblue': [135,206,250,1], 'lightslategray': [119,136,153,1], - 'lightslategrey': [119,136,153,1], 'lightsteelblue': [176,196,222,1], - 'lightyellow': [255,255,224,1], 'lime': [0,255,0,1], - 'limegreen': [50,205,50,1], 'linen': [250,240,230,1], - 'magenta': [255,0,255,1], 'maroon': [128,0,0,1], - 'mediumaquamarine': [102,205,170,1], 'mediumblue': [0,0,205,1], - 'mediumorchid': [186,85,211,1], 'mediumpurple': [147,112,219,1], - 'mediumseagreen': [60,179,113,1], 'mediumslateblue': [123,104,238,1], - 'mediumspringgreen': [0,250,154,1], 'mediumturquoise': [72,209,204,1], - 'mediumvioletred': [199,21,133,1], 'midnightblue': [25,25,112,1], - 'mintcream': [245,255,250,1], 'mistyrose': [255,228,225,1], - 'moccasin': [255,228,181,1], 'navajowhite': [255,222,173,1], - 'navy': [0,0,128,1], 'oldlace': [253,245,230,1], - 'olive': [128,128,0,1], 'olivedrab': [107,142,35,1], - 'orange': [255,165,0,1], 'orangered': [255,69,0,1], - 'orchid': [218,112,214,1], 'palegoldenrod': [238,232,170,1], - 'palegreen': [152,251,152,1], 'paleturquoise': [175,238,238,1], - 'palevioletred': [219,112,147,1], 'papayawhip': [255,239,213,1], - 'peachpuff': [255,218,185,1], 'peru': [205,133,63,1], - 'pink': [255,192,203,1], 'plum': [221,160,221,1], - 'powderblue': [176,224,230,1], 'purple': [128,0,128,1], - 'red': [255,0,0,1], 'rosybrown': [188,143,143,1], - 'royalblue': [65,105,225,1], 'saddlebrown': [139,69,19,1], - 'salmon': [250,128,114,1], 'sandybrown': [244,164,96,1], - 'seagreen': [46,139,87,1], 'seashell': [255,245,238,1], - 'sienna': [160,82,45,1], 'silver': [192,192,192,1], - 'skyblue': [135,206,235,1], 'slateblue': [106,90,205,1], - 'slategray': [112,128,144,1], 'slategrey': [112,128,144,1], - 'snow': [255,250,250,1], 'springgreen': [0,255,127,1], - 'steelblue': [70,130,180,1], 'tan': [210,180,140,1], - 'teal': [0,128,128,1], 'thistle': [216,191,216,1], - 'tomato': [255,99,71,1], 'turquoise': [64,224,208,1], - 'violet': [238,130,238,1], 'wheat': [245,222,179,1], - 'white': [255,255,255,1], 'whitesmoke': [245,245,245,1], - 'yellow': [255,255,0,1], 'yellowgreen': [154,205,50,1] -}; - -function clampCssByte(i) { // Clamp to integer 0 .. 255. - i = Math.round(i); // Seems to be what Chrome does (vs truncation). - return i < 0 ? 0 : i > 255 ? 255 : i; -} + var status = this._textureStatus[symbol]; + if (status) { + var isEnabled = status.enabled; + if (!isEnabled) { + status.enabled = true; + this._programKey = ''; + } + } + }, + /** + * Enable all textures used in the shader + */ + enableTexturesAll: function () { + var textureStatus = this._textureStatus; + for (var symbol in textureStatus) { + textureStatus[symbol].enabled = true; + } -function clampCssAngle(i) { // Clamp to integer 0 .. 360. - i = Math.round(i); // Seems to be what Chrome does (vs truncation). - return i < 0 ? 0 : i > 360 ? 360 : i; -} + this._programKey = ''; + }, + /** + * Disable a texture, it remove a #define macro in the shader + * @param {string} symbol + */ + disableTexture: function (symbol) { + if (Array.isArray(symbol)) { + for (var i = 0; i < symbol.length; i++) { + this.disableTexture(symbol[i]); + } + return; + } -function clampCssFloat(f) { // Clamp to float 0.0 .. 1.0. - return f < 0 ? 0 : f > 1 ? 1 : f; -} + var status = this._textureStatus[symbol]; + if (status) { + var isDisabled = ! status.enabled; + if (!isDisabled) { + status.enabled = false; + this._programKey = ''; + } + } + }, + /** + * Disable all textures used in the shader + */ + disableTexturesAll: function () { + var textureStatus = this._textureStatus; + for (var symbol in textureStatus) { + textureStatus[symbol].enabled = false; + } -function parseCssInt(str) { // int or percentage. - if (str.length && str.charAt(str.length - 1) === '%') { - return clampCssByte(parseFloat(str) / 100 * 255); - } - return clampCssByte(parseInt(str, 10)); -} + this._programKey = ''; + }, + /** + * If texture of given type is enabled. + * @param {string} symbol + * @return {boolean} + */ + isTextureEnabled: function (symbol) { + var textureStatus = this._textureStatus; + return !!textureStatus[symbol] + && textureStatus[symbol].enabled; + }, -function parseCssFloat(str) { // float or percentage. - if (str.length && str.charAt(str.length - 1) === '%') { - return clampCssFloat(parseFloat(str) / 100); - } - return clampCssFloat(parseFloat(str)); -} + /** + * Get all enabled textures + * @return {string[]} + */ + getEnabledTextures: function () { + var enabledTextures = []; + var textureStatus = this._textureStatus; + for (var symbol in textureStatus) { + if (textureStatus[symbol].enabled) { + enabledTextures.push(symbol); + } + } + return enabledTextures; + }, -function cssHueToRgb(m1, m2, h) { - if (h < 0) { - h += 1; - } - else if (h > 1) { - h -= 1; - } + /** + * Mark defines are updated. + */ + dirtyDefines: function () { + this._programKey = ''; + }, - if (h * 6 < 1) { - return m1 + (m2 - m1) * h * 6; - } - if (h * 2 < 1) { - return m2; - } - if (h * 3 < 2) { - return m1 + (m2 - m1) * (2/3 - h) * 6; + getProgramKey: function () { + if (!this._programKey) { + this._programKey = getProgramKey( + this.vertexDefines, this.fragmentDefines, this.getEnabledTextures() + ); + } + return this._programKey; } - return m1; -} - -function lerpNumber(a, b, p) { - return a + (b - a) * p; -} +}); -function setRgba(out, r, g, b, a) { - out[0] = r; out[1] = g; out[2] = b; out[3] = a; - return out; -} -function copyRgba(out, a) { - out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3]; - return out; -} +var DIRTY_PREFIX = '__dt__'; -var colorCache = new LRU$1(20); -var lastRemovedArr = null; +var Cache = function () { -function putToCache(colorStr, rgbaArr) { - // Reuse removed array - if (lastRemovedArr) { - copyRgba(lastRemovedArr, rgbaArr); - } - lastRemovedArr = colorCache.put(colorStr, lastRemovedArr || (rgbaArr.slice())); -} + this._contextId = 0; -/** - * @name clay.core.color.parse - * @param {string} colorStr - * @param {Array.} out - * @return {Array.} - */ -colorUtil.parse = function (colorStr, rgbaArr) { - if (!colorStr) { - return; - } - rgbaArr = rgbaArr || []; + this._caches = []; - var cached = colorCache.get(colorStr); - if (cached) { - return copyRgba(rgbaArr, cached); - } + this._context = {}; +}; - // colorStr may be not string - colorStr = colorStr + ''; - // Remove all whitespace, not compliant, but should just be more accepting. - var str = colorStr.replace(/ /g, '').toLowerCase(); +Cache.prototype = { - // Color keywords (and transparent) lookup. - if (str in kCSSColorTable) { - copyRgba(rgbaArr, kCSSColorTable[str]); - putToCache(colorStr, rgbaArr); - return rgbaArr; - } + use: function (contextId, documentSchema) { + var caches = this._caches; + if (!caches[contextId]) { + caches[contextId] = {}; - // #abc and #abc123 syntax. - if (str.charAt(0) === '#') { - if (str.length === 4) { - var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing. - if (!(iv >= 0 && iv <= 0xfff)) { - setRgba(rgbaArr, 0, 0, 0, 1); - return; // Covers NaN. + if (documentSchema) { + caches[contextId] = documentSchema(); } - setRgba(rgbaArr, - ((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8), - (iv & 0xf0) | ((iv & 0xf0) >> 4), - (iv & 0xf) | ((iv & 0xf) << 4), - 1 - ); - putToCache(colorStr, rgbaArr); - return rgbaArr; } - else if (str.length === 7) { - var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing. - if (!(iv >= 0 && iv <= 0xffffff)) { - setRgba(rgbaArr, 0, 0, 0, 1); - return; // Covers NaN. + 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; } - setRgba(rgbaArr, - (iv & 0xff0000) >> 16, - (iv & 0xff00) >> 8, - iv & 0xff, - 1 - ); - putToCache(colorStr, rgbaArr); - return rgbaArr; } + }, - return; - } - var op = str.indexOf('('), ep = str.indexOf(')'); - if (op !== -1 && ep + 1 === str.length) { - var fname = str.substr(0, op); - var params = str.substr(op + 1, ep - (op + 1)).split(','); - var alpha = 1; // To allow case fallthrough. - switch (fname) { - case 'rgba': - if (params.length !== 4) { - setRgba(rgbaArr, 0, 0, 0, 1); - return; - } - alpha = parseCssFloat(params.pop()); // jshint ignore:line - // Fall through. - case 'rgb': - if (params.length !== 3) { - setRgba(rgbaArr, 0, 0, 0, 1); - return; - } - setRgba(rgbaArr, - parseCssInt(params[0]), - parseCssInt(params[1]), - parseCssInt(params[2]), - alpha - ); - putToCache(colorStr, rgbaArr); - return rgbaArr; - case 'hsla': - if (params.length !== 4) { - setRgba(rgbaArr, 0, 0, 0, 1); - return; - } - params[3] = parseCssFloat(params[3]); - hsla2rgba(params, rgbaArr); - putToCache(colorStr, rgbaArr); - return rgbaArr; - case 'hsl': - if (params.length !== 3) { - setRgba(rgbaArr, 0, 0, 0, 1); - return; - } - hsla2rgba(params, rgbaArr); - putToCache(colorStr, rgbaArr); - return rgbaArr; - default: - return; - } - } + isDirty: function (field) { + field = field || ''; + var key = DIRTY_PREFIX + field; + var context = this._context; + return !context.hasOwnProperty(key) + || context[key] === true; + }, - setRgba(rgbaArr, 0, 0, 0, 1); - return; -}; + deleteContext: function (contextId) { + delete this._caches[contextId]; + this._context = {}; + }, -colorUtil.parseToFloat = function (colorStr, rgbaArr) { - rgbaArr = colorUtil.parse(colorStr, rgbaArr); - if (!rgbaArr) { - return; + 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); } - rgbaArr[0] /= 255; - rgbaArr[1] /= 255; - rgbaArr[2] /= 255; - return rgbaArr; }; +Cache.prototype.constructor = Cache; + /** - * @name clay.core.color.hsla2rgba - * @param {Array.} hsla - * @param {Array.} rgba - * @return {Array.} rgba + * Base class for all textures like compressed texture, texture2d, texturecube + * TODO mapping */ -function hsla2rgba(hsla, rgba) { - var h = (((parseFloat(hsla[0]) % 360) + 360) % 360) / 360; // 0 .. 1 - // NOTE(deanm): According to the CSS spec s/l should only be - // percentages, but we don't bother and let float or percentage. - var s = parseCssFloat(hsla[1]); - var l = parseCssFloat(hsla[2]); - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; +/** + * @constructor + * @alias clay.Texture + * @extends clay.core.Base + */ +var Texture = Base.extend( /** @lends clay.Texture# */ { + /** + * Texture width, readonly when the texture source is image + * @type {number} + */ + width: 512, + /** + * Texture height, readonly when the texture source is image + * @type {number} + */ + height: 512, + /** + * Texel data type. + * Possible values: + * + {@link clay.Texture.UNSIGNED_BYTE} + * + {@link clay.Texture.HALF_FLOAT} + * + {@link clay.Texture.FLOAT} + * + {@link clay.Texture.UNSIGNED_INT_24_8_WEBGL} + * + {@link clay.Texture.UNSIGNED_INT} + * @type {number} + */ + type: glenum.UNSIGNED_BYTE, + /** + * Format of texel data + * Possible values: + * + {@link clay.Texture.RGBA} + * + {@link clay.Texture.DEPTH_COMPONENT} + * + {@link clay.Texture.DEPTH_STENCIL} + * @type {number} + */ + format: glenum.RGBA, + /** + * Texture wrap. Default to be REPEAT. + * Possible values: + * + {@link clay.Texture.CLAMP_TO_EDGE} + * + {@link clay.Texture.REPEAT} + * + {@link clay.Texture.MIRRORED_REPEAT} + * @type {number} + */ + wrapS: glenum.REPEAT, + /** + * Texture wrap. Default to be REPEAT. + * Possible values: + * + {@link clay.Texture.CLAMP_TO_EDGE} + * + {@link clay.Texture.REPEAT} + * + {@link clay.Texture.MIRRORED_REPEAT} + * @type {number} + */ + wrapT: glenum.REPEAT, + /** + * Possible values: + * + {@link clay.Texture.NEAREST} + * + {@link clay.Texture.LINEAR} + * + {@link clay.Texture.NEAREST_MIPMAP_NEAREST} + * + {@link clay.Texture.LINEAR_MIPMAP_NEAREST} + * + {@link clay.Texture.NEAREST_MIPMAP_LINEAR} + * + {@link clay.Texture.LINEAR_MIPMAP_LINEAR} + * @type {number} + */ + minFilter: glenum.LINEAR_MIPMAP_LINEAR, + /** + * Possible values: + * + {@link clay.Texture.NEAREST} + * + {@link clay.Texture.LINEAR} + * @type {number} + */ + magFilter: glenum.LINEAR, + /** + * If enable mimap. + * @type {boolean} + */ + useMipmap: true, - rgba = rgba || []; - setRgba(rgba, - clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255), - clampCssByte(cssHueToRgb(m1, m2, h) * 255), - clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255), - 1 - ); + /** + * Anisotropic filtering, enabled if value is larger than 1 + * @see https://developer.mozilla.org/en-US/docs/Web/API/EXT_texture_filter_anisotropic + * @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 + /** + * If flip in y axis for given image source + * @type {boolean} + * @default true + */ + flipY: true, + + /** + * A flag to indicate if texture source is sRGB + */ + sRGB: true, + /** + * @type {number} + * @default 4 + */ + unpackAlignment: 4, + /** + * @type {boolean} + * @default false + */ + premultiplyAlpha: false, + + /** + * Dynamic option for texture like video + * @type {boolean} + */ + dynamic: false, + + NPOT: false, + + // PENDING + // Init it here to avoid deoptimization when it's assigned in application dynamically + __used: 0 + +}, function () { + this._cache = new Cache(); +}, +/** @lends clay.Texture.prototype */ +{ + + getWebGLTexture: function (renderer) { + var _gl = renderer.gl; + var cache = this._cache; + cache.use(renderer.__uid__); + + 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(renderer); + } + else if (cache.isDirty()) { + this.update(renderer); + cache.fresh(); + } - if (hsla.length === 4) { - rgba[3] = hsla[3]; - } + return cache.get('webgl_texture'); + }, - return rgba; -} + bind: function () {}, + unbind: function () {}, -/** - * @name clay.core.color.rgba2hsla - * @param {Array.} rgba - * @return {Array.} hsla - */ -function rgba2hsla(rgba) { - if (!rgba) { - return; - } + /** + * Mark texture is dirty and update in the next frame + */ + dirty: function () { + if (this._cache) { + this._cache.dirtyAll(); + } + }, - // RGB from 0 to 255 - var R = rgba[0] / 255; - var G = rgba[1] / 255; - var B = rgba[2] / 255; + update: function (renderer) {}, - var vMin = Math.min(R, G, B); // Min. value of RGB - var vMax = Math.max(R, G, B); // Max. value of RGB - var delta = vMax - vMin; // Delta RGB value + // Update the common parameters of texture + updateCommon: function (renderer) { + var _gl = renderer.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); - var L = (vMax + vMin) / 2; - var H; - var S; - // HSL results from 0 to 1 - if (delta === 0) { - H = 0; - S = 0; - } - else { - if (L < 0.5) { - S = delta / (vMax + vMin); + // Use of none-power of two texture + // http://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences + if (this.format === glenum.DEPTH_COMPONENT) { + this.useMipmap = false; } - else { - S = delta / (2 - vMax - vMin); + + var sRGBExt = renderer.getGLExtension('EXT_sRGB'); + // Fallback + if (this.format === Texture.SRGB && !sRGBExt) { + this.format = Texture.RGB; + } + if (this.format === Texture.SRGB_ALPHA && !sRGBExt) { + this.format = Texture.RGBA; } - var deltaR = (((vMax - R) / 6) + (delta / 2)) / delta; - var deltaG = (((vMax - G) / 6) + (delta / 2)) / delta; - var deltaB = (((vMax - B) / 6) + (delta / 2)) / delta; + this.NPOT = !this.isPowerOfTwo(); + }, - if (R === vMax) { - H = deltaB - deltaG; + getAvailableWrapS: function () { + if (this.NPOT) { + return glenum.CLAMP_TO_EDGE; } - else if (G === vMax) { - H = (1 / 3) + deltaR - deltaB; + return this.wrapS; + }, + getAvailableWrapT: function () { + if (this.NPOT) { + return glenum.CLAMP_TO_EDGE; } - else if (B === vMax) { - H = (2 / 3) + deltaG - deltaR; + return this.wrapT; + }, + getAvailableMinFilter: function () { + var minFilter = this.minFilter; + if (this.NPOT || !this.useMipmap) { + if (minFilter === glenum.NEAREST_MIPMAP_NEAREST || + minFilter === glenum.NEAREST_MIPMAP_LINEAR + ) { + return glenum.NEAREST; + } + else if (minFilter === glenum.LINEAR_MIPMAP_LINEAR || + minFilter === glenum.LINEAR_MIPMAP_NEAREST + ) { + return glenum.LINEAR; + } + else { + return minFilter; + } + } + else { + return minFilter; } + }, + getAvailableMagFilter: function () { + return this.magFilter; + }, - if (H < 0) { - H += 1; + nextHighestPowerOfTwo: function (x) { + --x; + for (var i = 1; i < 32; i <<= 1) { + x = x | x >> i; } + return x + 1; + }, + /** + * @param {clay.Renderer} renderer + */ + dispose: function (renderer) { - if (H > 1) { - H -= 1; + var cache = this._cache; + + cache.use(renderer.__uid__); + + var webglTexture = cache.get('webgl_texture'); + if (webglTexture){ + renderer.gl.deleteTexture(webglTexture); } - } + cache.deleteContext(renderer.__uid__); - var hsla = [H * 360, S, L]; + }, + /** + * Test if image of texture is valid and loaded. + * @return {boolean} + */ + isRenderable: function () {}, - if (rgba[3] != null) { - hsla.push(rgba[3]); + /** + * Test if texture size is power of two + * @return {boolean} + */ + isPowerOfTwo: function () {} +}); + +Object.defineProperty(Texture.prototype, 'width', { + get: function () { + return this._width; + }, + set: function (value) { + this._width = value; + } +}); +Object.defineProperty(Texture.prototype, 'height', { + get: function () { + return this._height; + }, + set: function (value) { + this._height = value; } +}); - return hsla; -} +/* DataType */ /** - * @name clay.core.color.lift - * @param {string} color - * @param {number} level - * @return {string} + * @type {number} */ -colorUtil.lift = function (color, level) { - var colorArr = colorUtil.parse(color); - if (colorArr) { - for (var i = 0; i < 3; i++) { - if (level < 0) { - colorArr[i] = colorArr[i] * (1 - level) | 0; - } - else { - colorArr[i] = ((255 - colorArr[i]) * level + colorArr[i]) | 0; - } - } - return colorUtil.stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb'); - } -}; - +Texture.BYTE = glenum.BYTE; /** - * @name clay.core.color.toHex - * @param {string} color - * @return {string} + * @type {number} */ -colorUtil.toHex = function (color) { - var colorArr = colorUtil.parse(color); - if (colorArr) { - return ((1 << 24) + (colorArr[0] << 16) + (colorArr[1] << 8) + (+colorArr[2])).toString(16).slice(1); - } -}; - +Texture.UNSIGNED_BYTE = glenum.UNSIGNED_BYTE; /** - * Map value to color. Faster than lerp methods because color is represented by rgba array. - * @name clay.core.color - * @param {number} normalizedValue A float between 0 and 1. - * @param {Array.>} colors List of rgba color array - * @param {Array.} [out] Mapped gba color array - * @return {Array.} will be null/undefined if input illegal. + * @type {number} */ -colorUtil.fastLerp = function (normalizedValue, colors, out) { - if (!(colors && colors.length) - || !(normalizedValue >= 0 && normalizedValue <= 1) - ) { - return; - } - - out = out || []; - - var value = normalizedValue * (colors.length - 1); - var leftIndex = Math.floor(value); - var rightIndex = Math.ceil(value); - var leftColor = colors[leftIndex]; - var rightColor = colors[rightIndex]; - var dv = value - leftIndex; - out[0] = clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)); - out[1] = clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)); - out[2] = clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)); - out[3] = clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv)); - - return out; -}; +Texture.SHORT = glenum.SHORT; +/** + * @type {number} + */ +Texture.UNSIGNED_SHORT = glenum.UNSIGNED_SHORT; +/** + * @type {number} + */ +Texture.INT = glenum.INT; +/** + * @type {number} + */ +Texture.UNSIGNED_INT = glenum.UNSIGNED_INT; +/** + * @type {number} + */ +Texture.FLOAT = glenum.FLOAT; +/** + * @type {number} + */ +Texture.HALF_FLOAT = 0x8D61; -colorUtil.fastMapToColor = colorUtil.fastLerp; +/** + * UNSIGNED_INT_24_8_WEBGL for WEBGL_depth_texture extension + * @type {number} + */ +Texture.UNSIGNED_INT_24_8_WEBGL = 34042; +/* PixelFormat */ +/** + * @type {number} + */ +Texture.DEPTH_COMPONENT = glenum.DEPTH_COMPONENT; +/** + * @type {number} + */ +Texture.DEPTH_STENCIL = glenum.DEPTH_STENCIL; +/** + * @type {number} + */ +Texture.ALPHA = glenum.ALPHA; +/** + * @type {number} + */ +Texture.RGB = glenum.RGB; +/** + * @type {number} + */ +Texture.RGBA = glenum.RGBA; /** - * @param {number} normalizedValue A float between 0 and 1. - * @param {Array.} colors Color list. - * @param {boolean=} fullOutput Default false. - * @return {(string|Object)} Result color. If fullOutput, - * return {color: ..., leftIndex: ..., rightIndex: ..., value: ...}, + * @type {number} */ -colorUtil.lerp = function (normalizedValue, colors, fullOutput) { - if (!(colors && colors.length) - || !(normalizedValue >= 0 && normalizedValue <= 1) - ) { - return; - } +Texture.LUMINANCE = glenum.LUMINANCE; +/** + * @type {number} + */ +Texture.LUMINANCE_ALPHA = glenum.LUMINANCE_ALPHA; - var value = normalizedValue * (colors.length - 1); - var leftIndex = Math.floor(value); - var rightIndex = Math.ceil(value); - var leftColor = colorUtil.parse(colors[leftIndex]); - var rightColor = colorUtil.parse(colors[rightIndex]); - var dv = value - leftIndex; +/** + * @see https://www.khronos.org/registry/webgl/extensions/EXT_sRGB/ + * @type {number} + */ +Texture.SRGB = 0x8C40; +/** + * @see https://www.khronos.org/registry/webgl/extensions/EXT_sRGB/ + * @type {number} + */ +Texture.SRGB_ALPHA = 0x8C42; - var color = colorUtil.stringify( - [ - clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)), - clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)), - clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)), - clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv)) - ], - 'rgba' - ); +/* 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; - return fullOutput - ? { - color: color, - leftIndex: leftIndex, - rightIndex: rightIndex, - value: value - } - : color; -}; +/* TextureMagFilter */ +/** + * @type {number} + */ +Texture.NEAREST = glenum.NEAREST; +/** + * @type {number} + */ +Texture.LINEAR = glenum.LINEAR; +/* TextureMinFilter */ /** - * @deprecated + * @type {number} */ -colorUtil.mapToColor = colorUtil.lerp; +Texture.NEAREST_MIPMAP_NEAREST = glenum.NEAREST_MIPMAP_NEAREST; +/** + * @type {number} + */ +Texture.LINEAR_MIPMAP_NEAREST = glenum.LINEAR_MIPMAP_NEAREST; +/** + * @type {number} + */ +Texture.NEAREST_MIPMAP_LINEAR = glenum.NEAREST_MIPMAP_LINEAR; +/** + * @type {number} + */ +Texture.LINEAR_MIPMAP_LINEAR = glenum.LINEAR_MIPMAP_LINEAR; +/* TextureWrapMode */ /** - * @name clay.core.color - * @param {string} color - * @param {number=} h 0 ~ 360, ignore when null. - * @param {number=} s 0 ~ 1, ignore when null. - * @param {number=} l 0 ~ 1, ignore when null. - * @return {string} Color string in rgba format. + * @type {number} */ -colorUtil.modifyHSL = function (color, h, s, l) { - color = colorUtil.parse(color); +Texture.REPEAT = glenum.REPEAT; +/** + * @type {number} + */ +Texture.CLAMP_TO_EDGE = glenum.CLAMP_TO_EDGE; +/** + * @type {number} + */ +Texture.MIRRORED_REPEAT = glenum.MIRRORED_REPEAT; - if (color) { - color = rgba2hsla(color); - h != null && (color[0] = clampCssAngle(h)); - s != null && (color[1] = parseCssFloat(s)); - l != null && (color[2] = parseCssFloat(l)); +var mathUtil = {}; - return colorUtil.stringify(hsla2rgba(color), 'rgba'); - } +mathUtil.isPowerOfTwo = function (value) { + return (value & (value - 1)) === 0; }; -/** - * @param {string} color - * @param {number=} alpha 0 ~ 1 - * @return {string} Color string in rgba format. - */ -colorUtil.modifyAlpha = function (color, alpha) { - color = colorUtil.parse(color); +mathUtil.nextPowerOfTwo = function (value) { + value --; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value ++; - if (color && alpha != null) { - color[3] = clampCssFloat(alpha); - return colorUtil.stringify(color, 'rgba'); - } + return value; }; -/** - * @param {Array.} arrColor like [12,33,44,0.4] - * @param {string} type 'rgba', 'hsva', ... - * @return {string} Result color. (If input illegal, return undefined). - */ -colorUtil.stringify = function (arrColor, type) { - if (!arrColor || !arrColor.length) { - return; - } - var colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2]; - if (type === 'rgba' || type === 'hsva' || type === 'hsla') { - colorStr += ',' + arrColor[3]; - } - return type + '(' + colorStr + ')'; +mathUtil.nearestPowerOfTwo = function (value) { + return Math.pow( 2, Math.round( Math.log( value ) / Math.LN2 ) ); }; -var parseColor$1 = colorUtil.parseToFloat; - -var programKeyCache = {}; +var isPowerOfTwo = mathUtil.isPowerOfTwo; -function getDefineCode(defines) { - var defineKeys = Object.keys(defines); - defineKeys.sort(); - var defineStr = []; - // Custom Defines - for (var i = 0; i < defineKeys.length; i++) { - var key = defineKeys[i]; - var value = defines[key]; - if (value === null) { - defineStr.push(key); - } - else{ - defineStr.push(key + ' ' + value.toString()); - } - } - return defineStr.join('\n'); +function nearestPowerOfTwo(val) { + return Math.pow(2, Math.round(Math.log(val) / Math.LN2)); } +function convertTextureToPowerOfTwo(texture, canvas) { + // var canvas = document.createElement('canvas'); + var width = nearestPowerOfTwo(texture.width); + var height = nearestPowerOfTwo(texture.height); + canvas = canvas || document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + var ctx = canvas.getContext('2d'); + ctx.drawImage(texture.image, 0, 0, width, height); -function getProgramKey(vertexDefines, fragmentDefines, enabledTextures) { - enabledTextures.sort(); - var defineStr = []; - for (var i = 0; i < enabledTextures.length; i++) { - var symbol = enabledTextures[i]; - defineStr.push(symbol); - } - var key = getDefineCode(vertexDefines) + '\n' - + getDefineCode(fragmentDefines) + '\n' - + defineStr.join('\n'); - - if (programKeyCache[key]) { - return programKeyCache[key]; - } - - var id = util$1.genGUID(); - programKeyCache[key] = id; - return id; + return canvas; } /** - * Material defines the appearance of mesh surface, like `color`, `roughness`, `metalness`, etc. - * It contains a {@link clay.Shader} and corresponding uniforms. + * @constructor clay.Texture2D + * @extends clay.Texture * - * Here is a basic example to create a standard material -```js -var material = new clay.Material({ - shader: new clay.Shader( - clay.Shader.source('clay.vertex'), - clay.Shader.source('clay.fragment') - ) -}); -``` - * @constructor clay.Material - * @extends clay.core.Base + * @example + * ... + * var mat = new clay.Material({ + * shader: clay.shader.library.get('clay.phong', 'diffuseMap') + * }); + * var diffuseMap = new clay.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 Material = Base.extend(function () { - return /** @lends clay.Material# */ { - /** - * @type {string} - */ - name: '', - - /** - * @type {Object} - */ - // uniforms: null, - - /** - * @type {clay.Shader} - */ - // shader: null, - - /** - * @type {boolean} - */ - depthTest: true, - - /** - * @type {boolean} - */ - depthMask: true, - +var Texture2D = Texture.extend(function () { + return /** @lends clay.Texture2D# */ { /** - * @type {boolean} + * @type {?HTMLImageElement|HTMLCanvasElemnet} */ - transparent: false, + // TODO mark dirty when assigned. + image: null, /** - * 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); - * } + * Pixels data. Will be ignored if image is set. + * @type {?Uint8Array|Float32Array} */ - blend: null, - + pixels: null, /** - * If update texture status automatically. + * @type {Array.} + * @example + * [{ + * image: mipmap0, + * pixels: null + * }, { + * image: mipmap1, + * pixels: null + * }, ....] */ - autoUpdateTextureStatus: true, - - uniforms: {}, - vertexDefines: {}, - fragmentDefines: {}, - _textureStatus: {}, - - // shadowTransparentMap : null + mipmaps: [], - // PENDING enable the uniform that only used in shader. - _enabledUniforms: null, + /** + * If convert texture to power-of-two + * @type {boolean} + */ + convertToPOT: false }; -}, function () { - if (!this.name) { - this.name = 'MATERIAL_' + this.__uid__; - } +}, { - if (this.shader) { - // Keep status, mainly preset uniforms, vertexDefines and fragmentDefines - this.attachShader(this.shader, true); - } -}, -/** @lends clay.Material.prototype */ -{ - precision: 'highp', + textureType: 'texture2D', - /** - * Set material uniform - * @example - * mat.setUniform('color', [1, 1, 1, 1]); - * @param {string} symbol - * @param {number|array|clay.Texture|ArrayBufferView} value - */ - setUniform: function (symbol, value) { - if (value === undefined) { - console.warn('Uniform value "' + symbol + '" is undefined'); - } - var uniform = this.uniforms[symbol]; - if (uniform) { + update: function (renderer) { - if (typeof value === 'string') { - // Try to parse as a color. Invalid color string will return null. - value = parseColor$1(value) || value; - } + var _gl = renderer.gl; + _gl.bindTexture(_gl.TEXTURE_2D, this._cache.get('webgl_texture')); - uniform.value = value; + this.updateCommon(renderer); - if (this.autoUpdateTextureStatus && uniform.type === 't') { - if (value) { - this.enableTexture(symbol); - } - else { - this.disableTexture(symbol); - } - } - } - }, + var glFormat = this.format; + var glType = this.type; - /** - * @param {Object} obj - */ - setUniforms: function(obj) { - for (var key in obj) { - var val = obj[key]; - this.setUniform(key, val); - } - }, + // Convert to pot is only available when using image/canvas/video element. + var convertToPOT = !!(this.convertToPOT + && !this.mipmaps.length && this.image + && (this.wrapS === Texture.REPEAT || this.wrapT === Texture.REPEAT) + && this.NPOT + ); - /** - * @param {string} symbol - * @return {boolean} - */ - isUniformEnabled: function (symbol) { - return this._enabledUniforms.indexOf(symbol) >= 0; - }, + _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, convertToPOT ? this.wrapS : this.getAvailableWrapS()); + _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, convertToPOT ? this.wrapT : this.getAvailableWrapT()); - getEnabledUniforms: function () { - return this._enabledUniforms; - }, - getTextureUniforms: function () { - return this._textureUniforms; - }, + _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, convertToPOT ? this.magFilter : this.getAvailableMagFilter()); + _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, convertToPOT ? this.minFilter : this.getAvailableMinFilter()); - /** - * Alias of setUniform and setUniforms - * @param {object|string} symbol - * @param {number|array|clay.Texture|ArrayBufferView} [value] - */ - set: function (symbol, value) { - if (typeof(symbol) === 'object') { - for (var key in symbol) { - var val = symbol[key]; - this.setUniform(key, val); - } - } - else { - this.setUniform(symbol, value); - } - }, - /** - * Get uniform value - * @param {string} symbol - * @return {number|array|clay.Texture|ArrayBufferView} - */ - get: function (symbol) { - var uniform = this.uniforms[symbol]; - if (uniform) { - return uniform.value; + var anisotropicExt = renderer.getGLExtension('EXT_texture_filter_anisotropic'); + if (anisotropicExt && this.anisotropic > 1) { + _gl.texParameterf(_gl.TEXTURE_2D, anisotropicExt.TEXTURE_MAX_ANISOTROPY_EXT, this.anisotropic); } - }, - /** - * Attach a shader instance - * @param {clay.Shader} shader - * @param {boolean} keepStatus If try to keep uniform and texture - */ - attachShader: function(shader, keepStatus) { - 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(); - this._textureUniforms = this._enabledUniforms.filter(function (uniformName) { - var type = this.uniforms[uniformName].type; - return type === 't' || type === 'tv'; - }, this); - - var originalVertexDefines = this.vertexDefines; - var originalFragmentDefines = this.fragmentDefines; - - this.vertexDefines = util$1.clone(shader.vertexDefines); - this.fragmentDefines = util$1.clone(shader.fragmentDefines); - if (keepStatus) { - for (var symbol in originalUniforms) { - if (uniforms[symbol]) { - uniforms[symbol].value = originalUniforms[symbol].value; - } + // Fallback to float type if browser don't have half float extension + if (glType === 36193) { + var halfFloatExt = renderer.getGLExtension('OES_texture_half_float'); + if (!halfFloatExt) { + glType = glenum.FLOAT; } - - util$1.defaults(this.vertexDefines, originalVertexDefines); - util$1.defaults(this.fragmentDefines, originalFragmentDefines); - } - - var textureStatus = {}; - for (var key in shader.textures) { - textureStatus[key] = { - shaderType: shader.textures[key].shaderType, - type: shader.textures[key].type, - enabled: (keepStatus && this._textureStatus[key]) ? this._textureStatus[key].enabled : false - }; - } - - this._textureStatus = textureStatus; - - this._programKey = ''; - }, - - /** - * Clone a new material and keep uniforms, shader will not be cloned - * @return {clay.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; - material.vertexDefines = util$1.clone(this.vertexDefines); - material.fragmentDefines = util$1.clone(this.fragmentDefines); - material.enableTexture(this.getEnabledTextures()); - material.precision = this.precision; - - return material; - }, - - /** - * Add a #define macro 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._programKey = ''; + 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, false); + width /= 2; + height /= 2; } } - if (shaderType === 'fragment' || shaderType === 'both') { - if (fragmentDefines[symbol] !== val) { - fragmentDefines[symbol] = val; - if (shaderType !== 'both') { - this._programKey = ''; - } + else { + this._updateTextureData(_gl, this, 0, this.width, this.height, glFormat, glType, convertToPOT); + + if (this.useMipmap && (!this.NPOT || convertToPOT)) { + _gl.generateMipmap(_gl.TEXTURE_2D); } } + + _gl.bindTexture(_gl.TEXTURE_2D, null); }, - /** - * Remove a #define macro in shader code - * @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._programKey = ''; + _updateTextureData: function (_gl, data, level, width, height, glFormat, glType, convertToPOT) { + if (data.image) { + var imgData = data.image; + if (convertToPOT) { + this._potCanvas = convertTextureToPowerOfTwo(this, this._potCanvas); + imgData = this._potCanvas; } + _gl.texImage2D(_gl.TEXTURE_2D, level, glFormat, glFormat, glType, imgData); } - if (shaderType === 'fragment' || shaderType === 'both') { - if (this.isDefined('fragment', symbol)) { - delete this.fragmentDefines[symbol]; - if (shaderType !== 'both') { - this._programKey = ''; - } + 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); } } }, /** - * If macro is defined in shader. - * @param {string} shaderType Can be vertex, fragment or both - * @param {string} symbol + * @param {clay.Renderer} renderer + * @memberOf clay.Texture2D.prototype */ - isDefined: function (shaderType, symbol) { - // PENDING hasOwnProperty ? - switch (shaderType) { - case 'vertex': - return this.vertexDefines[symbol] !== undefined; - case 'fragment': - return this.fragmentDefines[symbol] !== undefined; + generateMipmap: function (renderer) { + var _gl = renderer.gl; + if (this.useMipmap && !this.NPOT) { + _gl.bindTexture(_gl.TEXTURE_2D, this._cache.get('webgl_texture')); + _gl.generateMipmap(_gl.TEXTURE_2D); } }, - /** - * Get macro value defined in shader. - * @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]; - } + + isPowerOfTwo: function () { + return isPowerOfTwo(this.width) && isPowerOfTwo(this.height); }, - /** - * Enable a texture, actually it will add a #define macro 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 (Array.isArray(symbol)) { - 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._programKey = ''; - } + isRenderable: function () { + if (this.image) { + return this.image.nodeName === 'CANVAS' + || this.image.nodeName === 'VIDEO' + || this.image.complete; } - }, - /** - * Enable all textures used in the shader - */ - enableTexturesAll: function () { - var textureStatus = this._textureStatus; - for (var symbol in textureStatus) { - textureStatus[symbol].enabled = true; + else { + return !!(this.width && this.height); } + }, - this._programKey = ''; + bind: function (renderer) { + renderer.gl.bindTexture(renderer.gl.TEXTURE_2D, this.getWebGLTexture(renderer)); }, - /** - * Disable a texture, it remove a #define macro in the shader - * @param {string} symbol - */ - disableTexture: function (symbol) { - if (Array.isArray(symbol)) { - 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._programKey = ''; - } - } + unbind: function (renderer) { + renderer.gl.bindTexture(renderer.gl.TEXTURE_2D, null); }, - /** - * Disable all textures used in the shader - */ - disableTexturesAll: function () { - var textureStatus = this._textureStatus; - for (var symbol in textureStatus) { - textureStatus[symbol].enabled = false; + + load: function (src, crossOrigin) { + var image = new Image(); + if (crossOrigin) { + image.crossOrigin = crossOrigin; } + 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; + }; - this._programKey = ''; - }, - /** - * If texture of given type is enabled. - * @param {string} symbol - * @return {boolean} - */ - isTextureEnabled: function (symbol) { - var textureStatus = this._textureStatus; - return !!textureStatus[symbol] - && textureStatus[symbol].enabled; - }, + image.src = src; + this.image = image; - /** - * Get all enabled textures - * @return {string[]} - */ - getEnabledTextures: function () { - var enabledTextures = []; - var textureStatus = this._textureStatus; - for (var symbol in textureStatus) { - if (textureStatus[symbol].enabled) { - enabledTextures.push(symbol); - } + return this; + } +}); + +Object.defineProperty(Texture2D.prototype, 'width', { + get: function () { + if (this.image) { + return this.image.width; } - return enabledTextures; + return this._width; }, - - /** - * Mark defines are updated. - */ - dirtyDefines: function () { - this._programKey = ''; + set: function (value) { + if (this.image) { + console.warn('Texture from image can\'t set width'); + } + else { + if (this._width !== value) { + this.dirty(); + } + this._width = value; + } + } +}); +Object.defineProperty(Texture2D.prototype, 'height', { + get: function () { + if (this.image) { + return this.image.height; + } + return this._height; }, - - getProgramKey: function () { - if (!this._programKey) { - this._programKey = getProgramKey( - this.vertexDefines, this.fragmentDefines, this.getEnabledTextures() - ); + set: function (value) { + if (this.image) { + console.warn('Texture from image can\'t set height'); + } + else { + if (this._height !== value) { + this.dirty(); + } + this._height = value; } - return this._programKey; } }); @@ -13790,7 +14111,7 @@ var lightEssl = [ exportEnd ].join('\n'); -var prezGlsl = "@export clay.prez.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nattribute vec3 position : POSITION;\n@import clay.chunk.skinning_header\nvoid main()\n{\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n}\n@end\n@export clay.prez.fragment\nvoid main()\n{\n gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\n}\n@end"; +var prezGlsl = "@export clay.prez.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n@import clay.chunk.skinning_header\nvarying vec2 v_Texcoord;\nvoid main()\n{\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n v_Texcoord = texcoord;\n}\n@end\n@export clay.prez.fragment\nuniform sampler2D alphaMap;\nuniform float alphaCutoff: 0.0;\nvarying vec2 v_Texcoord;\nvoid main()\n{\n if (alphaCutoff > 0.0) {\n if (texture2D(alphaMap, v_Texcoord).a <= alphaCutoff) {\n discard;\n }\n }\n gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\n}\n@end"; // TODO Resources like shader, texture, geometry reference management // Trace and find out which shader, texture, geometry can be destroyed @@ -13808,6 +14129,12 @@ var errorShader = {}; function defaultGetMaterial(renderable) { return renderable.material; } +function defaultGetUniform(renderable, material, symbol) { + return material.uniforms[symbol].value; +} +function defaultIsMaterialChanged(renderabled, prevRenderable, material, prevMaterial) { + return material !== prevMaterial; +} function noop$1() {} @@ -13923,6 +14250,14 @@ var Renderer = Base.extend(function () { */ viewport: {}, + /** + * Canvas creator + * @type {Function} + */ + createCanvas: function () { + return document.createElement('canvas'); + }, + // Set by FrameBuffer#bind __currentFrameBuffer: null, @@ -13934,7 +14269,7 @@ var Renderer = Base.extend(function () { }, function () { if (!this.canvas) { - this.canvas = document.createElement('canvas'); + this.canvas = this.createCanvas(); } var canvas = this.canvas; try { @@ -13969,6 +14304,13 @@ var Renderer = Base.extend(function () { // Init managers this._programMgr = new ProgramManager(this); + + var blankCanvas = this.createCanvas(); + blankCanvas.width = blankCanvas.height = 1; + var ctx = blankCanvas.getContext('2d'); + this._placeholderTexture = new Texture2D({ + canvas: blankCanvas + }); }, /** @lends clay.Renderer.prototype. **/ { @@ -14198,7 +14540,7 @@ var Renderer = Base.extend(function () { return; } camera.update(); - var renderList = scene.updateRenderList(camera); + var renderList = scene.updateRenderList(camera, true); this._sceneRendering = scene; @@ -14305,6 +14647,8 @@ var Renderer = Base.extend(function () { * @param {clay.Camera} camera * @param {Object} [passConfig] * @param {Function} [passConfig.getMaterial] Get renderable material. + * @param {Function} [passConfig.getUniform] Get material uniform value. + * @param {Function} [passConfig.isMaterialChanged] If material changed. * @param {Function} [passConfig.beforeRender] Before render each renderable. * @param {Function} [passConfig.afterRender] After render each renderable * @param {Function} [passConfig.ifRender] If render the renderable. @@ -14316,6 +14660,9 @@ var Renderer = Base.extend(function () { passConfig = passConfig || {}; passConfig.getMaterial = passConfig.getMaterial || defaultGetMaterial; + passConfig.getUniform = passConfig.getUniform || defaultGetUniform; + // PENDING Better solution? + passConfig.isMaterialChanged = passConfig.isMaterialChanged || defaultIsMaterialChanged; passConfig.beforeRender = passConfig.beforeRender || noop$1; passConfig.afterRender = passConfig.afterRender || noop$1; @@ -14354,6 +14701,7 @@ var Renderer = Base.extend(function () { var prevMaterial; var prevProgram; + var prevRenderable; // Status var depthTest, depthMask; @@ -14431,7 +14779,9 @@ var Renderer = Base.extend(function () { } // Program changes also needs reset the materials. - if (prevMaterial !== material || programChanged) { + if (programChanged || passConfig.isMaterialChanged( + renderable, prevRenderable, material, prevMaterial + )) { if (material.depthTest !== depthTest) { material.depthTest ? _gl.enable(_gl.DEPTH_TEST) : _gl.disable(_gl.DEPTH_TEST); depthTest = material.depthTest; @@ -14456,7 +14806,11 @@ var Renderer = Base.extend(function () { } } - this._bindMaterial(material, program, prevMaterial || null, prevProgram || null); + this._bindMaterial( + renderable, material, program, + prevRenderable || null, prevMaterial || null, prevProgram || null, + passConfig.getUniform + ); prevMaterial = material; } @@ -14496,6 +14850,7 @@ var Renderer = Base.extend(function () { renderable.afterRender(this); prevProgram = program; + prevRenderable = renderable; } // TODO Seems need to be bound to null immediately if vao is changed? @@ -14540,7 +14895,7 @@ var Renderer = Base.extend(function () { } }, - _bindMaterial: function (material, program, prevMaterial, prevProgram) { + _bindMaterial: function (renderable, material, program, prevRenderable, prevMaterial, prevProgram, getUniformValue) { var _gl = this.gl; // PENDING Same texture in different material take different slot? @@ -14550,10 +14905,11 @@ var Renderer = Base.extend(function () { var currentTextureSlot = program.currentTextureSlot(); var enabledUniforms = material.getEnabledUniforms(); var textureUniforms = material.getTextureUniforms(); + var placeholderTexture = this._placeholderTexture; for (var u = 0; u < textureUniforms.length; u++) { var symbol = textureUniforms[u]; - var uniformValue = material.uniforms[symbol].value; + var uniformValue = getUniformValue(renderable, material, symbol); var uniformType = material.uniforms[symbol].type; // Not use `instanceof` to determine if a value is texture in Material#bind. // Use type instead, in some case texture may be in different namespaces. @@ -14564,52 +14920,64 @@ var Renderer = Base.extend(function () { } else if (uniformType === 'tv') { for (var i = 0; i < uniformValue.length; i++) { - if (uniformValue[i] instanceof Texture) { + if (uniformValue[i]) { uniformValue[i].__slot = -1; } } } } + placeholderTexture.__slot = -1; + // Set uniforms for (var u = 0; u < enabledUniforms.length; u++) { var symbol = enabledUniforms[u]; var uniform = material.uniforms[symbol]; - var uniformValue = uniform.value; + var uniformValue = getUniformValue(renderable, material, symbol); + var uniformType = uniform.type; + + if (uniformType === 't') { + if (!uniformValue || !uniformValue.isRenderable()) { + uniformValue = placeholderTexture; + } + } // PENDING // 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 && sameProgram) { - if (prevMaterial.uniforms[symbol].value === uniformValue) { - continue; + var prevUniformValue = getUniformValue(prevRenderable, prevMaterial, symbol); + if (uniformType === 't') { + if (!prevUniformValue || !prevUniformValue.isRenderable()) { + prevUniformValue = placeholderTexture; + } } - } - var uniformType = uniform.type; - 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 = program.currentTextureSlot(); - var res = program.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. + if (prevUniformValue === uniformValue) { + if (uniform.type === 't') { + // Still take the slot to make sure same texture in different materials have same slot. program.takeCurrentTextureSlot(this, null); } + else if (uniformType === 'tv' && uniformValue) { + for (var i = 0; i < uniformValue.length; i++) { + program.takeCurrentTextureSlot(this, null); + } + } + continue; } + } + + if (uniformValue == null) { continue; } else if (uniformType === 't') { if (uniformValue.__slot < 0) { var slot = program.currentTextureSlot(); var res = program.setUniform(_gl, '1i', symbol, slot); - if (!res) { // Texture uniform is not enabled - continue; + if (res) { // Texture uniform is enabled + program.takeCurrentTextureSlot(this, uniformValue); + uniformValue.__slot = slot; } - program.takeCurrentTextureSlot(this, uniformValue); - uniformValue.__slot = slot; } // Multiple uniform use same texture.. else { @@ -14760,6 +15128,29 @@ var Renderer = Base.extend(function () { ifRender: function (renderable) { return !renderable.ignorePreZ; }, + isMaterialChanged: function (renderable, prevRenderable) { + var matA = renderable.material; + var matB = prevRenderable.material; + return matA.get('diffuseMap') !== matB.get('diffuseMap') + || (matA.get('alphaCutoff') || 0) !== (matB.get('alphaCutoff') || 0); + }, + getUniform: function (renderable, depthMaterial, symbol) { + if (symbol === 'alphaMap') { + return renderable.material.get('diffuseMap'); + } + else if (symbol === 'alphaCutoff') { + if (renderable.material.isDefined('fragment', 'ALPHA_TEST') + && renderable.material.get('diffuseMap') + ) { + var alphaCutoff = renderable.material.get('alphaCutoff'); + return alphaCutoff || 0; + } + return 0; + } + else { + return depthMaterial.get(symbol); + } + }, getMaterial: function () { return preZPassMaterial; }, @@ -17512,9 +17903,10 @@ var Scene = Node.extend(function () { * It needs camera for the frustum culling. * * @param {clay.Camera} camera + * @param {boolean} updateSceneBoundingBox * @return {clay.Scene.RenderList} */ - updateRenderList: function (camera) { + updateRenderList: function (camera, updateSceneBoundingBox) { var id = camera.__uid__; var renderList = this._renderLists.get(id); if (!renderList) { @@ -17523,11 +17915,13 @@ var Scene = Node.extend(function () { } renderList.startCount(); - this.viewBoundingBoxLastFrame.min.set(Infinity, Infinity, Infinity); - this.viewBoundingBoxLastFrame.max.set(-Infinity, -Infinity, -Infinity); + if (updateSceneBoundingBox) { + this.viewBoundingBoxLastFrame.min.set(Infinity, Infinity, Infinity); + this.viewBoundingBoxLastFrame.max.set(-Infinity, -Infinity, -Infinity); + } var sceneMaterialTransparent = this.material && this.material.transparent || false; - this._doUpdateRenderList(this, camera, sceneMaterialTransparent, renderList); + this._doUpdateRenderList(this, camera, sceneMaterialTransparent, renderList, updateSceneBoundingBox); renderList.endCount(); @@ -17543,7 +17937,7 @@ var Scene = Node.extend(function () { return this._renderLists.get(camera.__uid__); }, - _doUpdateRenderList: function (parent, camera, sceneMaterialTransparent, renderList) { + _doUpdateRenderList: function (parent, camera, sceneMaterialTransparent, renderList, updateSceneBoundingBox) { if (parent.invisible) { return; } @@ -17557,12 +17951,12 @@ var Scene = Node.extend(function () { var geometry = child.geometry; mat4$3.multiplyAffine(WORLDVIEW, camera.viewMatrix.array, worldM); - if (!geometry.boundingBox || !this.isFrustumCulled(child, camera, WORLDVIEW)) { + if (updateSceneBoundingBox && !geometry.boundingBox || !this.isFrustumCulled(child, camera, WORLDVIEW)) { renderList.add(child, child.material.transparent || sceneMaterialTransparent); } } if (child._children.length > 0) { - this._doUpdateRenderList(child, camera, sceneMaterialTransparent, renderList); + this._doUpdateRenderList(child, camera, sceneMaterialTransparent, renderList, updateSceneBoundingBox); } } }, @@ -19316,337 +19710,65 @@ var ParametricSurface$1 = Geometry.extend( } var xFunc = generator.x; var yFunc = generator.y; - var zFunc = generator.z; - var uRange = generator.u || [0, 1, 0.05]; - var vRange = generator.v || [0, 1, 0.05]; - - var uNum = Math.floor((uRange[1] - uRange[0] + uRange[2]) / uRange[2]); - var vNum = Math.floor((vRange[1] - vRange[0] + vRange[2]) / vRange[2]); - - if (!isFinite(uNum) || !isFinite(vNum)) { - throw new Error('Infinite generator'); - } - - var vertexNum = uNum * vNum; - this.attributes.position.init(vertexNum); - this.attributes.texcoord0.init(vertexNum); - - var pos = []; - var texcoord = []; - var nVertex = 0; - for (var j = 0; j < vNum; j++) { - for (var i = 0; i < uNum; i++) { - var u = i * uRange[2] + uRange[0]; - var v = j * vRange[2] + vRange[0]; - pos[0] = xFunc(u, v); - pos[1] = yFunc(u, v); - pos[2] = zFunc(u, v); - - texcoord[0] = i / (uNum - 1); - texcoord[1] = j / (vNum - 1); - - this.attributes.position.set(nVertex, pos); - this.attributes.texcoord0.set(nVertex, texcoord); - nVertex++; - } - } - - var IndicesCtor = vertexNum > 0xffff ? Uint32Array : Uint16Array; - var nIndices = (uNum - 1) * (vNum - 1) * 6; - var indices = this.indices = new IndicesCtor(nIndices); - - var n = 0; - for (var j = 0; j < vNum - 1; j++) { - for (var i = 0; i < uNum - 1; i++) { - var i2 = j * uNum + i; - var i1 = (j * uNum + i + 1); - var i4 = (j + 1) * uNum + i + 1; - var i3 = (j + 1) * uNum + i; - - indices[n++] = i1; - indices[n++] = i2; - indices[n++] = i4; - - indices[n++] = i2; - indices[n++] = i3; - indices[n++] = i4; - } - } - - this.generateVertexNormals(); - this.updateBoundingBox(); - } -}); - -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 ) ); -}; - -var isPowerOfTwo = mathUtil.isPowerOfTwo; - -function nearestPowerOfTwo(val) { - return Math.pow(2, Math.round(Math.log(val) / Math.LN2)); -} -function convertTextureToPowerOfTwo(texture, canvas) { - // var canvas = document.createElement('canvas'); - var width = nearestPowerOfTwo(texture.width); - var height = nearestPowerOfTwo(texture.height); - canvas = canvas || document.createElement('canvas'); - canvas.width = width; - canvas.height = height; - var ctx = canvas.getContext('2d'); - ctx.drawImage(texture.image, 0, 0, width, height); - - return canvas; -} - -/** - * @constructor clay.Texture2D - * @extends clay.Texture - * - * @example - * ... - * var mat = new clay.Material({ - * shader: clay.shader.library.get('clay.phong', 'diffuseMap') - * }); - * var diffuseMap = new clay.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$1.extend(function () { - return /** @lends clay.Texture2D# */ { - /** - * @type {?HTMLImageElement|HTMLCanvasElemnet} - */ - image: null, - /** - * Pixels data. Will be ignored if image is set. - * @type {?Uint8Array|Float32Array} - */ - pixels: null, - /** - * @type {Array.} - * @example - * [{ - * image: mipmap0, - * pixels: null - * }, { - * image: mipmap1, - * pixels: null - * }, ....] - */ - mipmaps: [], - - /** - * If convert texture to power-of-two - * @type {boolean} - */ - convertToPOT: false - }; -}, { - - textureType: 'texture2D', - - update: function (renderer) { - - var _gl = renderer.gl; - _gl.bindTexture(_gl.TEXTURE_2D, this._cache.get('webgl_texture')); - - this.updateCommon(renderer); - - var glFormat = this.format; - var glType = this.type; - - // Convert to pot is only available when using image/canvas/video element. - var convertToPOT = !!(this.convertToPOT - && !this.mipmaps.length && this.image - && (this.wrapS === Texture$1.REPEAT || this.wrapT === Texture$1.REPEAT) - && this.NPOT - ); - - _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, convertToPOT ? this.wrapS : this.getAvailableWrapS()); - _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, convertToPOT ? this.wrapT : this.getAvailableWrapT()); - - _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, convertToPOT ? this.magFilter : this.getAvailableMagFilter()); - _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, convertToPOT ? this.minFilter : this.getAvailableMinFilter()); - - var anisotropicExt = renderer.getGLExtension('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 = renderer.getGLExtension('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, false); - width /= 2; - height /= 2; - } - } - else { - this._updateTextureData(_gl, this, 0, this.width, this.height, glFormat, glType, convertToPOT); - - if (this.useMipmap && (!this.NPOT || convertToPOT)) { - _gl.generateMipmap(_gl.TEXTURE_2D); - } - } - - _gl.bindTexture(_gl.TEXTURE_2D, null); - }, - - _updateTextureData: function (_gl, data, level, width, height, glFormat, glType, convertToPOT) { - if (data.image) { - var imgData = data.image; - if (convertToPOT) { - this._potCanvas = convertTextureToPowerOfTwo(this, this._potCanvas); - imgData = this._potCanvas; - } - _gl.texImage2D(_gl.TEXTURE_2D, level, glFormat, glFormat, glType, imgData); - } - else { - // Can be used as a blank texture when writing render to texture(RTT) - if ( - glFormat <= Texture$1.COMPRESSED_RGBA_S3TC_DXT5_EXT - && glFormat >= Texture$1.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 {clay.Renderer} renderer - * @memberOf clay.Texture2D.prototype - */ - generateMipmap: function (renderer) { - var _gl = renderer.gl; - if (this.useMipmap && !this.NPOT) { - _gl.bindTexture(_gl.TEXTURE_2D, this._cache.get('webgl_texture')); - _gl.generateMipmap(_gl.TEXTURE_2D); - } - }, + var zFunc = generator.z; + var uRange = generator.u || [0, 1, 0.05]; + var vRange = generator.v || [0, 1, 0.05]; - isPowerOfTwo: function () { - return isPowerOfTwo(this.width) && isPowerOfTwo(this.height); - }, + var uNum = Math.floor((uRange[1] - uRange[0] + uRange[2]) / uRange[2]); + var vNum = Math.floor((vRange[1] - vRange[0] + vRange[2]) / vRange[2]); - isRenderable: function () { - if (this.image) { - return this.image.nodeName === 'CANVAS' - || this.image.nodeName === 'VIDEO' - || this.image.complete; - } - else { - return !!(this.width && this.height); + if (!isFinite(uNum) || !isFinite(vNum)) { + throw new Error('Infinite generator'); } - }, - bind: function (renderer) { - renderer.gl.bindTexture(renderer.gl.TEXTURE_2D, this.getWebGLTexture(renderer)); - }, + var vertexNum = uNum * vNum; + this.attributes.position.init(vertexNum); + this.attributes.texcoord0.init(vertexNum); - unbind: function (renderer) { - renderer.gl.bindTexture(renderer.gl.TEXTURE_2D, null); - }, + var pos = []; + var texcoord = []; + var nVertex = 0; + for (var j = 0; j < vNum; j++) { + for (var i = 0; i < uNum; i++) { + var u = i * uRange[2] + uRange[0]; + var v = j * vRange[2] + vRange[0]; + pos[0] = xFunc(u, v); + pos[1] = yFunc(u, v); + pos[2] = zFunc(u, v); - load: function (src, crossOrigin) { - var image = new Image(); - if (crossOrigin) { - image.crossOrigin = crossOrigin; + texcoord[0] = i / (uNum - 1); + texcoord[1] = j / (vNum - 1); + + this.attributes.position.set(nVertex, pos); + this.attributes.texcoord0.set(nVertex, texcoord); + nVertex++; + } } - 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; + var IndicesCtor = vertexNum > 0xffff ? Uint32Array : Uint16Array; + var nIndices = (uNum - 1) * (vNum - 1) * 6; + var indices = this.indices = new IndicesCtor(nIndices); - return this; - } -}); + var n = 0; + for (var j = 0; j < vNum - 1; j++) { + for (var i = 0; i < uNum - 1; i++) { + var i2 = j * uNum + i; + var i1 = (j * uNum + i + 1); + var i4 = (j + 1) * uNum + i + 1; + var i3 = (j + 1) * uNum + i; -Object.defineProperty(Texture2D.prototype, 'width', { - get: function () { - if (this.image) { - return this.image.width; - } - return this._width; - }, - set: function (value) { - if (this.image) { - console.warn('Texture from image can\'t set width'); - } - else { - if (this._width !== value) { - this.dirty(); - } - this._width = value; - } - } -}); -Object.defineProperty(Texture2D.prototype, 'height', { - get: function () { - if (this.image) { - return this.image.height; - } - return this._height; - }, - set: function (value) { - if (this.image) { - console.warn('Texture from image can\'t set height'); - } - else { - if (this._height !== value) { - this.dirty(); + indices[n++] = i1; + indices[n++] = i2; + indices[n++] = i4; + + indices[n++] = i2; + indices[n++] = i3; + indices[n++] = i4; } - this._height = value; } + + this.generateVertexNormals(); + this.updateBoundingBox(); } }); @@ -19681,7 +19803,7 @@ var targetList = ['px', 'nx', 'py', 'ny', 'pz', 'nz']; * }); * }); */ -var TextureCube = Texture$1.extend(function () { +var TextureCube = Texture.extend(function () { return /** @lends clay.TextureCube# */{ /** @@ -20432,7 +20554,7 @@ var request = { get : get }; -var standardEssl = "\n@export clay.standard.chunk.varying\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\n#if defined(PARALLAXOCCLUSIONMAP_ENABLED) || defined(NORMALMAP_ENABLED)\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n#if defined(AOMAP_ENABLED)\nvarying vec2 v_Texcoord2;\n#endif\n#ifdef VERTEX_COLOR\nvarying vec4 v_Color;\n#endif\n@end\n@export clay.standard.chunk.light_header\n#ifdef AMBIENT_LIGHT_COUNT\n@import clay.header.ambient_light\n#endif\n#ifdef AMBIENT_SH_LIGHT_COUNT\n@import clay.header.ambient_sh_light\n#endif\n#ifdef AMBIENT_CUBEMAP_LIGHT_COUNT\n@import clay.header.ambient_cubemap_light\n#endif\n#ifdef POINT_LIGHT_COUNT\n@import clay.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_COUNT\n@import clay.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_COUNT\n@import clay.header.spot_light\n#endif\n@end\n@export clay.standard.vertex\n#define SHADER_NAME standard\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n#if defined(AOMAP_ENABLED)\nattribute vec2 texcoord2 : TEXCOORD_1;\n#endif\nattribute vec3 normal : NORMAL;\nattribute vec4 tangent : TANGENT;\n#ifdef VERTEX_COLOR\nattribute vec4 a_Color : COLOR;\n#endif\nattribute vec3 barycentric;\n@import clay.standard.chunk.varying\n@import clay.chunk.skinning_header\nvoid main()\n{\n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\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 gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n v_Barycentric = barycentric;\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\n#if defined(PARALLAXOCCLUSIONMAP_ENABLED) || defined(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#ifdef VERTEX_COLOR\n v_Color = a_Color;\n#endif\n#if defined(AOMAP_ENABLED)\n v_Texcoord2 = texcoord2;\n#endif\n}\n@end\n@export clay.standard.fragment\n#define PI 3.14159265358979\n#define GLOSSINESS_CHANNEL 0\n#define ROUGHNESS_CHANNEL 0\n#define METALNESS_CHANNEL 1\n@import clay.standard.chunk.varying\nuniform mat4 viewInverse : VIEWINVERSE;\n#ifdef NORMALMAP_ENABLED\nuniform sampler2D normalMap;\n#endif\n#ifdef DIFFUSEMAP_ENABLED\nuniform sampler2D diffuseMap;\n#endif\n#ifdef SPECULARMAP_ENABLED\nuniform sampler2D specularMap;\n#endif\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 GLOSSINESSMAP_ENABLED\nuniform sampler2D glossinessMap;\n #endif\n#endif\n#ifdef METALNESSMAP_ENABLED\nuniform sampler2D metalnessMap;\n#endif\n#ifdef ENVIRONMENTMAP_ENABLED\nuniform samplerCube environmentMap;\n #ifdef PARALLAX_CORRECTED\nuniform vec3 environmentBoxMin;\nuniform vec3 environmentBoxMax;\n #endif\n#endif\n#ifdef BRDFLOOKUP_ENABLED\nuniform sampler2D brdfLookup;\n#endif\n#ifdef EMISSIVEMAP_ENABLED\nuniform sampler2D emissiveMap;\n#endif\n#ifdef SSAOMAP_ENABLED\nuniform sampler2D ssaoMap;\nuniform vec4 viewport : VIEWPORT;\n#endif\n#ifdef AOMAP_ENABLED\nuniform sampler2D aoMap;\nuniform float aoIntensity;\n#endif\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\n#ifdef ALPHA_TEST\nuniform float alphaCutoff: 0.9;\n#endif\n#ifdef USE_METALNESS\nuniform float metalness : 0.0;\n#else\nuniform vec3 specularColor : [0.1, 0.1, 0.1];\n#endif\nuniform vec3 emission : [0.0, 0.0, 0.0];\nuniform float emissionIntensity: 1;\nuniform float lineWidth : 0.0;\nuniform vec4 lineColor : [0.0, 0.0, 0.0, 0.6];\n#ifdef ENVIRONMENTMAP_PREFILTER\nuniform float maxMipmapLevel: 5;\n#endif\n@import clay.standard.chunk.light_header\n@import clay.util.calculate_attenuation\n@import clay.util.edge_factor\n@import clay.util.rgbm\n@import clay.util.srgb\n@import clay.plugin.compute_shadow_map\n@import clay.util.parallax_correct\n@import clay.util.ACES\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}\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}\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#ifdef PARALLAXOCCLUSIONMAP_ENABLED\nuniform float parallaxOcclusionScale : 0.02;\nuniform float parallaxMaxLayers : 20;\nuniform float parallaxMinLayers : 5;\nuniform sampler2D parallaxOcclusionMap;\nmat3 transpose(in mat3 inMat)\n{\n vec3 i0 = inMat[0];\n vec3 i1 = inMat[1];\n vec3 i2 = inMat[2];\n return mat3(\n vec3(i0.x, i1.x, i2.x),\n vec3(i0.y, i1.y, i2.y),\n vec3(i0.z, i1.z, i2.z)\n );\n}\nvec2 parallaxUv(vec2 uv, vec3 viewDir)\n{\n float numLayers = mix(parallaxMaxLayers, parallaxMinLayers, abs(dot(vec3(0.0, 0.0, 1.0), viewDir)));\n float layerHeight = 1.0 / numLayers;\n float curLayerHeight = 0.0;\n vec2 deltaUv = viewDir.xy * parallaxOcclusionScale / (viewDir.z * numLayers);\n vec2 curUv = uv;\n float height = 1.0 - texture2D(parallaxOcclusionMap, curUv).r;\n for (int i = 0; i < 30; i++) {\n curLayerHeight += layerHeight;\n curUv -= deltaUv;\n height = 1.0 - texture2D(parallaxOcclusionMap, curUv).r;\n if (height < curLayerHeight) {\n break;\n }\n }\n vec2 prevUv = curUv + deltaUv;\n float next = height - curLayerHeight;\n float prev = 1.0 - texture2D(parallaxOcclusionMap, prevUv).r - curLayerHeight + layerHeight;\n return mix(curUv, prevUv, next / (next - prev));\n}\n#endif\nvoid main() {\n vec4 albedoColor = vec4(color, alpha);\n#ifdef VERTEX_COLOR\n albedoColor *= v_Color;\n#endif\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = normalize(eyePos - v_WorldPosition);\n vec2 uv = v_Texcoord;\n#if defined(PARALLAXOCCLUSIONMAP_ENABLED) || defined(NORMALMAP_ENABLED)\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\n#endif\n#ifdef PARALLAXOCCLUSIONMAP_ENABLED\n uv = parallaxUv(v_Texcoord, normalize(transpose(tbn) * -V));\n#endif\n#ifdef DIFFUSEMAP_ENABLED\n vec4 texel = texture2D(diffuseMap, uv);\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#ifdef USE_METALNESS\n float m = metalness;\n #ifdef METALNESSMAP_ENABLED\n float m2 = texture2D(metalnessMap, uv)[METALNESS_CHANNEL];\n m = clamp(m2 + (m - 0.5) * 2.0, 0.0, 1.0);\n #endif\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#ifdef USE_ROUGHNESS\n float g = 1.0 - roughness;\n #ifdef ROUGHNESSMAP_ENABLED\n float g2 = 1.0 - texture2D(roughnessMap, uv)[ROUGHNESS_CHANNEL];\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\n #endif\n#else\n float g = glossiness;\n #ifdef GLOSSINESSMAP_ENABLED\n float g2 = texture2D(glossinessMap, uv)[GLOSSINESS_CHANNEL];\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\n #endif\n#endif\n#ifdef SPECULARMAP_ENABLED\n spec *= sRGBToLinear(texture2D(specularMap, uv)).rgb;\n#endif\n vec3 N = v_Normal;\n#ifdef DOUBLE_SIDED\n if (dot(N, V) < 0.0) {\n N = -N;\n }\n#endif\n#ifdef NORMALMAP_ENABLED\n if (dot(v_Tangent, v_Tangent) > 0.0) {\n vec3 normalTexel = texture2D(normalMap, uv).xyz;\n if (dot(normalTexel, normalTexel) > 0.0) { N = normalTexel * 2.0 - 1.0;\n tbn[1] = -tbn[1];\n N = normalize(tbn * N);\n }\n }\n#endif\n vec3 diffuseTerm = vec3(0.0, 0.0, 0.0);\n vec3 specularTerm = vec3(0.0, 0.0, 0.0);\n float ndv = clamp(dot(N, V), 0.0, 1.0);\n vec3 fresnelTerm = F_Schlick(ndv, spec);\n#ifdef AMBIENT_LIGHT_COUNT\n for(int _idx_ = 0; _idx_ < AMBIENT_LIGHT_COUNT; _idx_++)\n {{\n diffuseTerm += ambientLightColor[_idx_];\n }}\n#endif\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#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 vec3 lightPosition = pointLightPosition[_idx_];\n vec3 lc = pointLightColor[_idx_];\n float range = pointLightRange[_idx_];\n vec3 L = lightPosition - v_WorldPosition;\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 float shadowContrib = 1.0;\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribsPoint[_idx_];\n }\n#endif\n vec3 li = lc * ndl * attenuation * shadowContrib;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\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 _idx_ = 0; _idx_ < DIRECTIONAL_LIGHT_COUNT; _idx_++)\n {{\n vec3 L = -normalize(directionalLightDirection[_idx_]);\n vec3 lc = directionalLightColor[_idx_];\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 shadowContrib = 1.0;\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribsDir[_idx_];\n }\n#endif\n vec3 li = lc * ndl * shadowContrib;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }}\n#endif\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 vec3 L = lightPosition - v_WorldPosition;\n float dist = length(L);\n float attenuation = lightAttenuation(dist, range);\n L /= dist;\n float c = dot(spotLightDirection, L);\n float falloff;\n falloff = clamp((c - a) /(b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\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 shadowContrib = 1.0;\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\n if (shadowEnabled)\n {\n shadowContrib = shadowContribsSpot[i];\n }\n#endif\n vec3 li = lc * attenuation * (1.0 - falloff) * shadowContrib * ndl;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }\n#endif\n vec4 outColor = albedoColor;\n outColor.rgb *= max(diffuseTerm, vec3(0.0));\n outColor.rgb += max(specularTerm, vec3(0.0));\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), 8.12);\n outColor.rgb += ambientCubemapLightColor[_idx_] * envTexel2 * envWeight2;\n }}\n#endif\n#ifdef ENVIRONMENTMAP_ENABLED\n vec3 envWeight = g * fresnelTerm;\n vec3 L = reflect(-V, N);\n #ifdef PARALLAX_CORRECTED\n L = parallaxCorrect(L, v_WorldPosition, environmentBoxMin, environmentBoxMax);\n#endif\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 #ifdef BRDFLOOKUP_ENABLED\n vec2 brdfParam = texture2D(brdfLookup, vec2(rough, ndv)).xy;\n envWeight = spec * brdfParam.x + brdfParam.y;\n #endif\n #else\n vec3 envTexel = textureCube(environmentMap, L).xyz;\n #endif\n outColor.rgb += envTexel * envWeight;\n#endif\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#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 outColor.rgb *= aoFactor;\n vec3 lEmission = emission;\n#ifdef EMISSIVEMAP_ENABLED\n lEmission *= texture2D(emissiveMap, uv).rgb;\n#endif\n outColor.rgb += lEmission * emissionIntensity;\n if(lineWidth > 0.)\n {\n outColor.rgb = mix(outColor.rgb, lineColor.rgb, (1.0 - edgeFactor(lineWidth)) * lineColor.a);\n }\n#ifdef ALPHA_TEST\n if (outColor.a < alphaCutoff) {\n discard;\n }\n#endif\n#ifdef TONEMAPPING\n outColor.rgb = ACESToneMapping(outColor.rgb);\n#endif\n#ifdef SRGB_ENCODE\n outColor = linearTosRGB(outColor);\n#endif\n gl_FragColor = encodeHDR(outColor);\n}\n@end\n@export clay.standardMR.vertex\n@import clay.standard.vertex\n@end\n@export clay.standardMR.fragment\n#define USE_METALNESS\n#define USE_ROUGHNESS\n@import clay.standard.fragment\n@end"; +var standardEssl = "\n@export clay.standard.chunk.varying\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\n#if defined(PARALLAXOCCLUSIONMAP_ENABLED) || defined(NORMALMAP_ENABLED)\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n#if defined(AOMAP_ENABLED)\nvarying vec2 v_Texcoord2;\n#endif\n#ifdef VERTEX_COLOR\nvarying vec4 v_Color;\n#endif\n@end\n@export clay.standard.chunk.light_header\n#ifdef AMBIENT_LIGHT_COUNT\n@import clay.header.ambient_light\n#endif\n#ifdef AMBIENT_SH_LIGHT_COUNT\n@import clay.header.ambient_sh_light\n#endif\n#ifdef AMBIENT_CUBEMAP_LIGHT_COUNT\n@import clay.header.ambient_cubemap_light\n#endif\n#ifdef POINT_LIGHT_COUNT\n@import clay.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_COUNT\n@import clay.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_COUNT\n@import clay.header.spot_light\n#endif\n@end\n@export clay.standard.vertex\n#define SHADER_NAME standard\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n#if defined(AOMAP_ENABLED)\nattribute vec2 texcoord2 : TEXCOORD_1;\n#endif\nattribute vec3 normal : NORMAL;\nattribute vec4 tangent : TANGENT;\n#ifdef VERTEX_COLOR\nattribute vec4 a_Color : COLOR;\n#endif\nattribute vec3 barycentric;\n@import clay.standard.chunk.varying\n@import clay.chunk.skinning_header\nvoid main()\n{\n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\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 gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n v_Barycentric = barycentric;\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\n#if defined(PARALLAXOCCLUSIONMAP_ENABLED) || defined(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#ifdef VERTEX_COLOR\n v_Color = a_Color;\n#endif\n#if defined(AOMAP_ENABLED)\n v_Texcoord2 = texcoord2;\n#endif\n}\n@end\n@export clay.standard.fragment\n#define PI 3.14159265358979\n#define GLOSSINESS_CHANNEL 0\n#define ROUGHNESS_CHANNEL 0\n#define METALNESS_CHANNEL 1\n#define DIFFUSEMAP_ALPHA_ALPHA\n@import clay.standard.chunk.varying\nuniform mat4 viewInverse : VIEWINVERSE;\n#ifdef NORMALMAP_ENABLED\nuniform sampler2D normalMap;\n#endif\n#ifdef DIFFUSEMAP_ENABLED\nuniform sampler2D diffuseMap;\n#endif\n#ifdef SPECULARMAP_ENABLED\nuniform sampler2D specularMap;\n#endif\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 GLOSSINESSMAP_ENABLED\nuniform sampler2D glossinessMap;\n #endif\n#endif\n#ifdef METALNESSMAP_ENABLED\nuniform sampler2D metalnessMap;\n#endif\n#ifdef ENVIRONMENTMAP_ENABLED\nuniform samplerCube environmentMap;\n #ifdef PARALLAX_CORRECTED\nuniform vec3 environmentBoxMin;\nuniform vec3 environmentBoxMax;\n #endif\n#endif\n#ifdef BRDFLOOKUP_ENABLED\nuniform sampler2D brdfLookup;\n#endif\n#ifdef EMISSIVEMAP_ENABLED\nuniform sampler2D emissiveMap;\n#endif\n#ifdef SSAOMAP_ENABLED\nuniform sampler2D ssaoMap;\nuniform vec4 viewport : VIEWPORT;\n#endif\n#ifdef AOMAP_ENABLED\nuniform sampler2D aoMap;\nuniform float aoIntensity;\n#endif\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\n#ifdef ALPHA_TEST\nuniform float alphaCutoff: 0.9;\n#endif\n#ifdef USE_METALNESS\nuniform float metalness : 0.0;\n#else\nuniform vec3 specularColor : [0.1, 0.1, 0.1];\n#endif\nuniform vec3 emission : [0.0, 0.0, 0.0];\nuniform float emissionIntensity: 1;\nuniform float lineWidth : 0.0;\nuniform vec4 lineColor : [0.0, 0.0, 0.0, 0.6];\n#ifdef ENVIRONMENTMAP_PREFILTER\nuniform float maxMipmapLevel: 5;\n#endif\n@import clay.standard.chunk.light_header\n@import clay.util.calculate_attenuation\n@import clay.util.edge_factor\n@import clay.util.rgbm\n@import clay.util.srgb\n@import clay.plugin.compute_shadow_map\n@import clay.util.parallax_correct\n@import clay.util.ACES\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}\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}\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#ifdef PARALLAXOCCLUSIONMAP_ENABLED\nuniform float parallaxOcclusionScale : 0.02;\nuniform float parallaxMaxLayers : 20;\nuniform float parallaxMinLayers : 5;\nuniform sampler2D parallaxOcclusionMap;\nmat3 transpose(in mat3 inMat)\n{\n vec3 i0 = inMat[0];\n vec3 i1 = inMat[1];\n vec3 i2 = inMat[2];\n return mat3(\n vec3(i0.x, i1.x, i2.x),\n vec3(i0.y, i1.y, i2.y),\n vec3(i0.z, i1.z, i2.z)\n );\n}\nvec2 parallaxUv(vec2 uv, vec3 viewDir)\n{\n float numLayers = mix(parallaxMaxLayers, parallaxMinLayers, abs(dot(vec3(0.0, 0.0, 1.0), viewDir)));\n float layerHeight = 1.0 / numLayers;\n float curLayerHeight = 0.0;\n vec2 deltaUv = viewDir.xy * parallaxOcclusionScale / (viewDir.z * numLayers);\n vec2 curUv = uv;\n float height = 1.0 - texture2D(parallaxOcclusionMap, curUv).r;\n for (int i = 0; i < 30; i++) {\n curLayerHeight += layerHeight;\n curUv -= deltaUv;\n height = 1.0 - texture2D(parallaxOcclusionMap, curUv).r;\n if (height < curLayerHeight) {\n break;\n }\n }\n vec2 prevUv = curUv + deltaUv;\n float next = height - curLayerHeight;\n float prev = 1.0 - texture2D(parallaxOcclusionMap, prevUv).r - curLayerHeight + layerHeight;\n return mix(curUv, prevUv, next / (next - prev));\n}\n#endif\nvoid main() {\n vec4 albedoColor = vec4(color, alpha);\n#ifdef VERTEX_COLOR\n albedoColor *= v_Color;\n#endif\n#ifdef SRGB_DECODE\n albedoColor = sRGBToLinear(albedoColor);\n#endif\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = normalize(eyePos - v_WorldPosition);\n vec2 uv = v_Texcoord;\n#if defined(PARALLAXOCCLUSIONMAP_ENABLED) || defined(NORMALMAP_ENABLED)\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\n#endif\n#ifdef PARALLAXOCCLUSIONMAP_ENABLED\n uv = parallaxUv(v_Texcoord, normalize(transpose(tbn) * -V));\n#endif\n#ifdef DIFFUSEMAP_ENABLED\n vec4 texel = texture2D(diffuseMap, uv);\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#ifdef USE_METALNESS\n float m = metalness;\n #ifdef METALNESSMAP_ENABLED\n float m2 = texture2D(metalnessMap, uv)[METALNESS_CHANNEL];\n m = clamp(m2 + (m - 0.5) * 2.0, 0.0, 1.0);\n #endif\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#ifdef USE_ROUGHNESS\n float g = clamp(1.0 - roughness, 0.0, 1.0);\n #ifdef ROUGHNESSMAP_ENABLED\n float g2 = 1.0 - texture2D(roughnessMap, uv)[ROUGHNESS_CHANNEL];\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\n #endif\n#else\n float g = glossiness;\n #ifdef GLOSSINESSMAP_ENABLED\n float g2 = texture2D(glossinessMap, uv)[GLOSSINESS_CHANNEL];\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\n #endif\n#endif\n#ifdef SPECULARMAP_ENABLED\n spec *= sRGBToLinear(texture2D(specularMap, uv)).rgb;\n#endif\n vec3 N = v_Normal;\n#ifdef DOUBLE_SIDED\n if (dot(N, V) < 0.0) {\n N = -N;\n }\n#endif\n#ifdef NORMALMAP_ENABLED\n if (dot(v_Tangent, v_Tangent) > 0.0) {\n vec3 normalTexel = texture2D(normalMap, uv).xyz;\n if (dot(normalTexel, normalTexel) > 0.0) { N = normalTexel * 2.0 - 1.0;\n tbn[1] = -tbn[1];\n N = normalize(tbn * N);\n }\n }\n#endif\n vec3 diffuseTerm = vec3(0.0, 0.0, 0.0);\n vec3 specularTerm = vec3(0.0, 0.0, 0.0);\n float ndv = clamp(dot(N, V), 0.0, 1.0);\n vec3 fresnelTerm = F_Schlick(ndv, spec);\n#ifdef AMBIENT_LIGHT_COUNT\n for(int _idx_ = 0; _idx_ < AMBIENT_LIGHT_COUNT; _idx_++)\n {{\n diffuseTerm += ambientLightColor[_idx_];\n }}\n#endif\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#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 vec3 lightPosition = pointLightPosition[_idx_];\n vec3 lc = pointLightColor[_idx_];\n float range = pointLightRange[_idx_];\n vec3 L = lightPosition - v_WorldPosition;\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 float shadowContrib = 1.0;\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribsPoint[_idx_];\n }\n#endif\n vec3 li = lc * ndl * attenuation * shadowContrib;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\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 _idx_ = 0; _idx_ < DIRECTIONAL_LIGHT_COUNT; _idx_++)\n {{\n vec3 L = -normalize(directionalLightDirection[_idx_]);\n vec3 lc = directionalLightColor[_idx_];\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 shadowContrib = 1.0;\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribsDir[_idx_];\n }\n#endif\n vec3 li = lc * ndl * shadowContrib;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }}\n#endif\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 vec3 L = lightPosition - v_WorldPosition;\n float dist = length(L);\n float attenuation = lightAttenuation(dist, range);\n L /= dist;\n float c = dot(spotLightDirection, L);\n float falloff;\n falloff = clamp((c - a) /(b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\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 shadowContrib = 1.0;\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\n if (shadowEnabled)\n {\n shadowContrib = shadowContribsSpot[i];\n }\n#endif\n vec3 li = lc * attenuation * (1.0 - falloff) * shadowContrib * ndl;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }\n#endif\n vec4 outColor = albedoColor;\n outColor.rgb *= max(diffuseTerm, vec3(0.0));\n outColor.rgb += max(specularTerm, vec3(0.0));\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), 8.12);\n outColor.rgb += ambientCubemapLightColor[_idx_] * envTexel2 * envWeight2;\n }}\n#endif\n#ifdef ENVIRONMENTMAP_ENABLED\n vec3 envWeight = g * fresnelTerm;\n vec3 L = reflect(-V, N);\n #ifdef PARALLAX_CORRECTED\n L = parallaxCorrect(L, v_WorldPosition, environmentBoxMin, environmentBoxMax);\n#endif\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 #ifdef BRDFLOOKUP_ENABLED\n vec2 brdfParam = texture2D(brdfLookup, vec2(rough, ndv)).xy;\n envWeight = spec * brdfParam.x + brdfParam.y;\n #endif\n #else\n vec3 envTexel = textureCube(environmentMap, L).xyz;\n #endif\n outColor.rgb += envTexel * envWeight;\n#endif\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#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 outColor.rgb *= aoFactor;\n vec3 lEmission = emission;\n#ifdef EMISSIVEMAP_ENABLED\n lEmission *= texture2D(emissiveMap, uv).rgb;\n#endif\n outColor.rgb += lEmission * emissionIntensity;\n if(lineWidth > 0.)\n {\n outColor.rgb = mix(outColor.rgb, lineColor.rgb, (1.0 - edgeFactor(lineWidth)) * lineColor.a);\n }\n#ifdef ALPHA_TEST\n if (outColor.a < alphaCutoff) {\n discard;\n }\n#endif\n#ifdef TONEMAPPING\n outColor.rgb = ACESToneMapping(outColor.rgb);\n#endif\n#ifdef SRGB_ENCODE\n outColor = linearTosRGB(outColor);\n#endif\n gl_FragColor = encodeHDR(outColor);\n}\n@end\n@export clay.standardMR.vertex\n@import clay.standard.vertex\n@end\n@export clay.standardMR.fragment\n#define USE_METALNESS\n#define USE_ROUGHNESS\n@import clay.standard.fragment\n@end"; // Import standard shader Shader['import'](standardEssl); @@ -20469,7 +20591,7 @@ var standardShader; */ var StandardMaterial = Material.extend(function () { if (!standardShader) { - standardShader = new Shader(Shader.source('clay.standard.vertex'), Shader.source('clay.standard.fragment')); + standardShader = new Shader(Shader.source('clay.standardMR.vertex'), Shader.source('clay.standardMR.fragment')); } return /** @lends clay.StandardMaterial# */ { shader: standardShader @@ -20535,42 +20657,51 @@ var StandardMaterial = Material.extend(function () { /** * @type {clay.Texture2D} */ + diffuseMap: null, /** * @type {clay.Texture2D} */ + normalMap: null, /** * @type {clay.Texture2D} */ + roughnessMap: null, /** * @type {clay.Texture2D} */ + metalnessMap: null, /** * @type {clay.Texture2D} */ + emissiveMap: null, /** * @type {clay.TextureCube} */ + environmentMap: null, /** * @type {clay.BoundingBox} */ - + environmentBox: null, /** * BRDF Lookup is generated by clay.util.cubemap.integrateBrdf * @type {clay.Texture2D} */ + brdfLookup: null, /** * @type {clay.Texture2D} */ + ssaoMap: null, /** * @type {clay.Texture2D} */ + aoMap: null, /** * @type {Array.} @@ -20619,9 +20750,6 @@ var StandardMaterial = Material.extend(function () { */ metalnessChannel: 1 }); - - this.define('fragment', 'USE_METALNESS'); - this.define('fragment', 'USE_ROUGHNESS'); }, { clone: function () { var material = new StandardMaterial({ @@ -21023,9 +21151,9 @@ var Skeleton = Base.extend(function () { var utilGlsl = "\n@export clay.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@export clay.util.calculate_attenuation\nuniform float attenuationFactor : 5.0;\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@end\n@export clay.util.edge_factor\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@end\n@export clay.util.encode_float\nvec4 encodeFloat(const in float depth)\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 return res;\n}\n@end\n@export clay.util.decode_float\nfloat decodeFloat(const in vec4 color)\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@export clay.util.float\n@import clay.util.encode_float\n@import clay.util.decode_float\n@end\n@export clay.util.rgbm_decode\nvec3 RGBMDecode(vec4 rgbm, float range) {\n return range * rgbm.rgb * rgbm.a;\n}\n@end\n@export clay.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@export clay.util.rgbm\n@import clay.util.rgbm_decode\n@import clay.util.rgbm_encode\nvec4 decodeHDR(vec4 color)\n{\n#if defined(RGBM_DECODE) || defined(RGBM)\n return vec4(RGBMDecode(color, 8.12), 1.0);\n#else\n return color;\n#endif\n}\nvec4 encodeHDR(vec4 color)\n{\n#if defined(RGBM_ENCODE) || defined(RGBM)\n return RGBMEncode(color.xyz, 8.12);\n#else\n return color;\n#endif\n}\n@end\n@export clay.util.srgb\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}\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@export clay.chunk.skinning_header\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\nuniform mat4 skinMatrix[JOINT_COUNT] : SKIN_MATRIX;\nmat4 getSkinMatrix(float idx) {\n return skinMatrix[int(idx)];\n}\n#endif\n@end\n@export clay.chunk.skin_matrix\nmat4 skinMatrixWS = getSkinMatrix(joint.x) * weight.x;\nif (weight.y > 1e-4)\n{\n skinMatrixWS += getSkinMatrix(joint.y) * weight.y;\n}\nif (weight.z > 1e-4)\n{\n skinMatrixWS += getSkinMatrix(joint.z) * weight.z;\n}\nfloat weightW = 1.0-weight.x-weight.y-weight.z;\nif (weightW > 1e-4)\n{\n skinMatrixWS += getSkinMatrix(joint.w) * weightW;\n}\n@end\n@export clay.util.parallax_correct\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 vec3 further = max(first, second);\n float dist = min(further.x, min(further.y, further.z));\n vec3 fixedPos = pos + dir * dist;\n vec3 boxCenter = (boxMax + boxMin) * 0.5;\n return normalize(fixedPos - boxCenter);\n}\n@end\n@export clay.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\n@export clay.util.ACES\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@end"; -var basicEssl = "@export clay.basic.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 position : POSITION;\nattribute vec3 barycentric;\n@import clay.chunk.skinning_header\nvarying vec2 v_Texcoord;\nvarying vec3 v_Barycentric;\n#ifdef VERTEX_COLOR\nattribute vec4 a_Color : COLOR;\nvarying vec4 v_Color;\n#endif\nvoid main()\n{\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_Barycentric = barycentric;\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n#ifdef VERTEX_COLOR\n v_Color = a_Color;\n#endif\n}\n@end\n@export clay.basic.fragment\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#ifdef ALPHA_TEST\nuniform float alphaCutoff: 0.9;\n#endif\n#ifdef VERTEX_COLOR\nvarying vec4 v_Color;\n#endif\nuniform float lineWidth : 0.0;\nuniform vec4 lineColor : [0.0, 0.0, 0.0, 0.6];\nvarying vec3 v_Barycentric;\n@import clay.util.edge_factor\n@import clay.util.rgbm\n@import clay.util.srgb\n@import clay.util.ACES\nvoid main()\n{\n#ifdef RENDER_TEXCOORD\n gl_FragColor = vec4(v_Texcoord, 1.0, 1.0);\n return;\n#endif\n gl_FragColor = vec4(color, alpha);\n#ifdef VERTEX_COLOR\n gl_FragColor *= v_Color;\n#endif\n#ifdef DIFFUSEMAP_ENABLED\n vec4 texel = decodeHDR(texture2D(diffuseMap, v_Texcoord));\n#ifdef SRGB_DECODE\n texel = sRGBToLinear(texel);\n#endif\n#if defined(DIFFUSEMAP_ALPHA_ALPHA)\n gl_FragColor.a = texel.a;\n#endif\n gl_FragColor.rgb *= texel.rgb;\n#endif\n gl_FragColor.rgb += emission;\n if( lineWidth > 0.)\n {\n gl_FragColor.rgb = mix(gl_FragColor.rgb, lineColor.rgb, (1.0 - edgeFactor(lineWidth)) * lineColor.a);\n }\n#ifdef ALPHA_TEST\n if (gl_FragColor.a < alphaCutoff) {\n discard;\n }\n#endif\n#ifdef TONEMAPPING\n gl_FragColor.rgb = ACESToneMapping(gl_FragColor.rgb);\n#endif\n#ifdef SRGB_ENCODE\n gl_FragColor = linearTosRGB(gl_FragColor);\n#endif\n gl_FragColor = encodeHDR(gl_FragColor);\n}\n@end"; +var basicEssl = "@export clay.basic.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 position : POSITION;\nattribute vec3 barycentric;\n@import clay.chunk.skinning_header\nvarying vec2 v_Texcoord;\nvarying vec3 v_Barycentric;\n#ifdef VERTEX_COLOR\nattribute vec4 a_Color : COLOR;\nvarying vec4 v_Color;\n#endif\nvoid main()\n{\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_Barycentric = barycentric;\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n#ifdef VERTEX_COLOR\n v_Color = a_Color;\n#endif\n}\n@end\n@export clay.basic.fragment\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#ifdef ALPHA_TEST\nuniform float alphaCutoff: 0.9;\n#endif\n#ifdef VERTEX_COLOR\nvarying vec4 v_Color;\n#endif\nuniform float lineWidth : 0.0;\nuniform vec4 lineColor : [0.0, 0.0, 0.0, 0.6];\nvarying vec3 v_Barycentric;\n@import clay.util.edge_factor\n@import clay.util.rgbm\n@import clay.util.srgb\n@import clay.util.ACES\nvoid main()\n{\n gl_FragColor = vec4(color, alpha);\n#ifdef VERTEX_COLOR\n gl_FragColor *= v_Color;\n#endif\n#ifdef SRGB_DECODE\n gl_FragColor = sRGBToLinear(gl_FragColor);\n#endif\n#ifdef DIFFUSEMAP_ENABLED\n vec4 texel = decodeHDR(texture2D(diffuseMap, v_Texcoord));\n#ifdef SRGB_DECODE\n texel = sRGBToLinear(texel);\n#endif\n#if defined(DIFFUSEMAP_ALPHA_ALPHA)\n gl_FragColor.a = texel.a;\n#endif\n gl_FragColor.rgb *= texel.rgb;\n#endif\n gl_FragColor.rgb += emission;\n if( lineWidth > 0.)\n {\n gl_FragColor.rgb = mix(gl_FragColor.rgb, lineColor.rgb, (1.0 - edgeFactor(lineWidth)) * lineColor.a);\n }\n#ifdef ALPHA_TEST\n if (gl_FragColor.a < alphaCutoff) {\n discard;\n }\n#endif\n#ifdef TONEMAPPING\n gl_FragColor.rgb = ACESToneMapping(gl_FragColor.rgb);\n#endif\n#ifdef SRGB_ENCODE\n gl_FragColor = linearTosRGB(gl_FragColor);\n#endif\n gl_FragColor = encodeHDR(gl_FragColor);\n}\n@end"; -var lambertEssl = "\n@export clay.lambert.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 normal : NORMAL;\nattribute vec3 barycentric;\n#ifdef VERTEX_COLOR\nattribute vec4 a_Color : COLOR;\nvarying vec4 v_Color;\n#endif\n@import clay.chunk.skinning_header\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\nvoid main()\n{\n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n#endif\n gl_Position = worldViewProjection * vec4( skinnedPosition, 1.0 );\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 v_Barycentric = barycentric;\n#ifdef VERTEX_COLOR\n v_Color = a_Color;\n#endif\n}\n@end\n@export clay.lambert.fragment\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nuniform sampler2D diffuseMap;\nuniform sampler2D alphaMap;\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform vec3 emission : [0.0, 0.0, 0.0];\nuniform float alpha : 1.0;\n#ifdef ALPHA_TEST\nuniform float alphaCutoff: 0.9;\n#endif\nuniform float lineWidth : 0.0;\nuniform vec4 lineColor : [0.0, 0.0, 0.0, 0.6];\nvarying vec3 v_Barycentric;\n#ifdef VERTEX_COLOR\nvarying vec4 v_Color;\n#endif\n#ifdef AMBIENT_LIGHT_COUNT\n@import clay.header.ambient_light\n#endif\n#ifdef AMBIENT_SH_LIGHT_COUNT\n@import clay.header.ambient_sh_light\n#endif\n#ifdef POINT_LIGHT_COUNT\n@import clay.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_COUNT\n@import clay.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_COUNT\n@import clay.header.spot_light\n#endif\n@import clay.util.calculate_attenuation\n@import clay.util.edge_factor\n@import clay.util.rgbm\n@import clay.plugin.compute_shadow_map\n@import clay.util.ACES\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 gl_FragColor = vec4(color, alpha);\n#ifdef VERTEX_COLOR\n gl_FragColor *= v_Color;\n#endif\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 vec3 diffuseColor = vec3(0.0, 0.0, 0.0);\n#ifdef AMBIENT_LIGHT_COUNT\n for(int _idx_ = 0; _idx_ < AMBIENT_LIGHT_COUNT; _idx_++)\n {\n diffuseColor += ambientLightColor[_idx_];\n }\n#endif\n#ifdef AMBIENT_SH_LIGHT_COUNT\n for(int _idx_ = 0; _idx_ < AMBIENT_SH_LIGHT_COUNT; _idx_++)\n {{\n diffuseColor += calcAmbientSHLight(_idx_, v_Normal) * ambientSHLightColor[_idx_];\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 vec3 lightPosition = pointLightPosition[i];\n vec3 lightColor = pointLightColor[i];\n float range = pointLightRange[i];\n vec3 lightDirection = lightPosition - v_WorldPosition;\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range);\n lightDirection /= dist;\n float ndl = dot( v_Normal, lightDirection );\n float shadowContrib = 1.0;\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribsPoint[i];\n }\n#endif\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 float ndl = dot(v_Normal, normalize(lightDirection));\n float shadowContrib = 1.0;\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribsDir[i];\n }\n#endif\n diffuseColor += lightColor * clamp(ndl, 0.0, 1.0) * shadowContrib;\n }\n#endif\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 vec3 lightDirection = lightPosition - v_WorldPosition;\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range);\n lightDirection /= dist;\n float c = dot(spotLightDirection, lightDirection);\n float falloff;\n falloff = clamp((c - a) /( b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n float ndl = dot(v_Normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\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 gl_FragColor.rgb *= diffuseColor;\n gl_FragColor.rgb += emission;\n if(lineWidth > 0.)\n {\n gl_FragColor.rgb = mix(gl_FragColor.rgb, lineColor.rgb, (1.0 - edgeFactor(lineWidth)) * lineColor.a);\n }\n#ifdef ALPHA_TEST\n if (gl_FragColor.a < alphaCutoff) {\n discard;\n }\n#endif\n#ifdef TONEMAPPING\n gl_FragColor.rgb = ACESToneMapping(gl_FragColor.rgb);\n#endif\n#ifdef SRGB_ENCODE\n gl_FragColor = linearTosRGB(gl_FragColor);\n#endif\n gl_FragColor = encodeHDR(gl_FragColor);\n}\n@end"; +var lambertEssl = "\n@export clay.lambert.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 normal : NORMAL;\nattribute vec3 barycentric;\n#ifdef VERTEX_COLOR\nattribute vec4 a_Color : COLOR;\nvarying vec4 v_Color;\n#endif\n@import clay.chunk.skinning_header\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\nvoid main()\n{\n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n#endif\n gl_Position = worldViewProjection * vec4( skinnedPosition, 1.0 );\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 v_Barycentric = barycentric;\n#ifdef VERTEX_COLOR\n v_Color = a_Color;\n#endif\n}\n@end\n@export clay.lambert.fragment\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nuniform sampler2D diffuseMap;\nuniform sampler2D alphaMap;\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform vec3 emission : [0.0, 0.0, 0.0];\nuniform float alpha : 1.0;\n#ifdef ALPHA_TEST\nuniform float alphaCutoff: 0.9;\n#endif\nuniform float lineWidth : 0.0;\nuniform vec4 lineColor : [0.0, 0.0, 0.0, 0.6];\nvarying vec3 v_Barycentric;\n#ifdef VERTEX_COLOR\nvarying vec4 v_Color;\n#endif\n#ifdef AMBIENT_LIGHT_COUNT\n@import clay.header.ambient_light\n#endif\n#ifdef AMBIENT_SH_LIGHT_COUNT\n@import clay.header.ambient_sh_light\n#endif\n#ifdef POINT_LIGHT_COUNT\n@import clay.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_COUNT\n@import clay.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_COUNT\n@import clay.header.spot_light\n#endif\n@import clay.util.calculate_attenuation\n@import clay.util.edge_factor\n@import clay.util.rgbm\n@import clay.plugin.compute_shadow_map\n@import clay.util.ACES\nvoid main()\n{\n gl_FragColor = vec4(color, alpha);\n#ifdef VERTEX_COLOR\n gl_FragColor *= v_Color;\n#endif\n#ifdef SRGB_DECODE\n gl_FragColor = sRGBToLinear(gl_FragColor);\n#endif\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 vec3 diffuseColor = vec3(0.0, 0.0, 0.0);\n#ifdef AMBIENT_LIGHT_COUNT\n for(int _idx_ = 0; _idx_ < AMBIENT_LIGHT_COUNT; _idx_++)\n {\n diffuseColor += ambientLightColor[_idx_];\n }\n#endif\n#ifdef AMBIENT_SH_LIGHT_COUNT\n for(int _idx_ = 0; _idx_ < AMBIENT_SH_LIGHT_COUNT; _idx_++)\n {{\n diffuseColor += calcAmbientSHLight(_idx_, v_Normal) * ambientSHLightColor[_idx_];\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 vec3 lightPosition = pointLightPosition[i];\n vec3 lightColor = pointLightColor[i];\n float range = pointLightRange[i];\n vec3 lightDirection = lightPosition - v_WorldPosition;\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range);\n lightDirection /= dist;\n float ndl = dot( v_Normal, lightDirection );\n float shadowContrib = 1.0;\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribsPoint[i];\n }\n#endif\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 float ndl = dot(v_Normal, normalize(lightDirection));\n float shadowContrib = 1.0;\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribsDir[i];\n }\n#endif\n diffuseColor += lightColor * clamp(ndl, 0.0, 1.0) * shadowContrib;\n }\n#endif\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 vec3 lightDirection = lightPosition - v_WorldPosition;\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range);\n lightDirection /= dist;\n float c = dot(spotLightDirection, lightDirection);\n float falloff;\n falloff = clamp((c - a) /( b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n float ndl = dot(v_Normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\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 gl_FragColor.rgb *= diffuseColor;\n gl_FragColor.rgb += emission;\n if(lineWidth > 0.)\n {\n gl_FragColor.rgb = mix(gl_FragColor.rgb, lineColor.rgb, (1.0 - edgeFactor(lineWidth)) * lineColor.a);\n }\n#ifdef ALPHA_TEST\n if (gl_FragColor.a < alphaCutoff) {\n discard;\n }\n#endif\n#ifdef TONEMAPPING\n gl_FragColor.rgb = ACESToneMapping(gl_FragColor.rgb);\n#endif\n#ifdef SRGB_ENCODE\n gl_FragColor = linearTosRGB(gl_FragColor);\n#endif\n gl_FragColor = encodeHDR(gl_FragColor);\n}\n@end"; var wireframeEssl = "@export clay.wireframe.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world : WORLD;\nattribute vec3 position : POSITION;\nattribute vec3 barycentric;\n@import clay.chunk.skinning_header\nvarying vec3 v_Barycentric;\nvoid main()\n{\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0 );\n v_Barycentric = barycentric;\n}\n@end\n@export clay.wireframe.fragment\nuniform vec3 color : [0.0, 0.0, 0.0];\nuniform float alpha : 1.0;\nuniform float lineWidth : 1.0;\nvarying vec3 v_Barycentric;\n@import clay.util.edge_factor\nvoid main()\n{\n gl_FragColor.rgb = color;\n gl_FragColor.a = (1.0-edgeFactor(lineWidth)) * alpha;\n}\n@end"; @@ -21561,8 +21689,8 @@ function () { } }); util$1.defaults(parameters, { - wrapS: Texture$1.REPEAT, - wrapT: Texture$1.REPEAT, + wrapS: Texture.REPEAT, + wrapT: Texture.REPEAT, flipY: this.textureFlipY, convertToPOT: this.textureConvertToPOT }); @@ -22332,9 +22460,7 @@ var PointLight = Light.extend(/** @lends clay.light.Point# */ { * @constructor clay.light.Spot * @extends clay.Light */ -var SpotLight = Light.extend( -/**@lends clay.light.Spot */ -{ +var SpotLight = Light.extend(/**@lends clay.light.Spot */ { /** * @type {number} */ @@ -22354,12 +22480,12 @@ var SpotLight = Light.extend( /** * @type {number} */ - shadowBias: 0.0002, + shadowBias: 0.001, /** * @type {number} */ shadowSlopeScale: 2.0 -},{ +}, { type: 'SPOT_LIGHT', @@ -22635,7 +22761,7 @@ var FrameBuffer = Base.extend( var texture = obj.texture; // FIXME some texture format can't generate mipmap if (!texture.NPOT && texture.useMipmap - && texture.minFilter === Texture$1.LINEAR_MIPMAP_LINEAR) { + && texture.minFilter === Texture.LINEAR_MIPMAP_LINEAR) { var target = texture.textureType === 'textureCube' ? glenum.TEXTURE_CUBE_MAP : glenum.TEXTURE_2D; _gl.bindTexture(target, texture.getWebGLTexture(renderer)); _gl.generateMipmap(target); @@ -22864,17 +22990,17 @@ var Pass = Base.extend(function () { * @type {string} */ // PENDING shader or fragment ? - fragment : '', + fragment: '', /** * @type {Object} */ - outputs : null, + outputs: null, /** * @type {clay.Material} */ - material : null, + material: null, /** * @type {Boolean} @@ -23446,15 +23572,15 @@ var ret = { switch(fourCC) { case FOURCC_DXT1: blockBytes = 8; - internalFormat = Texture$1.COMPRESSED_RGB_S3TC_DXT1_EXT; + internalFormat = Texture.COMPRESSED_RGB_S3TC_DXT1_EXT; break; case FOURCC_DXT3: blockBytes = 16; - internalFormat = Texture$1.COMPRESSED_RGBA_S3TC_DXT3_EXT; + internalFormat = Texture.COMPRESSED_RGBA_S3TC_DXT3_EXT; break; case FOURCC_DXT5: blockBytes = 16; - internalFormat = Texture$1.COMPRESSED_RGBA_S3TC_DXT5_EXT; + internalFormat = Texture.COMPRESSED_RGBA_S3TC_DXT5_EXT; break; default: return null; @@ -23671,7 +23797,7 @@ var ret$1 = { texture.height = height; texture.pixels = pixels; // HALF_FLOAT can't use Float32Array - texture.type = Texture$1.FLOAT; + texture.type = Texture.FLOAT; return texture; }, @@ -23983,7 +24109,7 @@ var textureUtil = { var integrateBRDFShaderCode = "#define SAMPLE_NUMBER 1024\n#define PI 3.14159265358979\nuniform sampler2D normalDistribution;\nuniform vec2 viewportSize : [512, 256];\nconst vec3 N = vec3(0.0, 0.0, 1.0);\nconst float fSampleNumber = float(SAMPLE_NUMBER);\nvec3 importanceSampleNormal(float i, float roughness, vec3 N) {\n vec3 H = texture2D(normalDistribution, vec2(roughness, i)).rgb;\n vec3 upVector = abs(N.y) > 0.999 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);\n vec3 tangentX = normalize(cross(N, upVector));\n vec3 tangentZ = cross(N, tangentX);\n return normalize(tangentX * H.x + N * H.y + tangentZ * H.z);\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}\nvoid main() {\n vec2 uv = gl_FragCoord.xy / viewportSize;\n float NoV = uv.x;\n float roughness = uv.y;\n vec3 V;\n V.x = sqrt(1.0 - NoV * NoV);\n V.y = 0.0;\n V.z = NoV;\n float A = 0.0;\n float B = 0.0;\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 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 gl_FragColor = vec4(vec2(A, B) / fSampleNumber, 0.0, 1.0);\n}\n"; -var prefilterFragCode = "#define SHADER_NAME prefilter\n#define SAMPLE_NUMBER 1024\n#define PI 3.14159265358979\nuniform mat4 viewInverse : VIEWINVERSE;\nuniform samplerCube environmentMap;\nuniform sampler2D normalDistribution;\nuniform float roughness : 0.5;\nuniform int maxSampleNumber: 1024\nvarying vec2 v_Texcoord;\nvarying vec3 v_WorldPosition;\n@import clay.util.rgbm\nvec3 importanceSampleNormal(float i, float roughness, vec3 N) {\n vec3 H = texture2D(normalDistribution, vec2(roughness, i)).rgb;\n vec3 upVector = abs(N.y) > 0.999 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);\n vec3 tangentX = normalize(cross(N, upVector));\n vec3 tangentZ = cross(N, tangentX);\n return normalize(tangentX * H.x + N * H.y + tangentZ * H.z);\n}\nvoid main() {\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = normalize(v_WorldPosition - eyePos);\n vec3 N = V;\n vec3 prefilteredColor = vec3(0.0);\n float totalWeight = 0.0;\n float fMaxSampleNumber = float(maxSampleNumber);\n for (int i = 0; i < SAMPLE_NUMBER; i++) {\n if (i > maxSampleNumber) {\n break;\n }\n vec3 H = importanceSampleNormal(float(i) / fMaxSampleNumber, roughness, N);\n vec3 L = reflect(-V, H);\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 gl_FragColor = encodeHDR(vec4(prefilteredColor / totalWeight, 1.0));\n}\n"; +var prefilterFragCode = "#define SHADER_NAME prefilter\n#define SAMPLE_NUMBER 1024\n#define PI 3.14159265358979\nuniform mat4 viewInverse : VIEWINVERSE;\nuniform samplerCube environmentMap;\nuniform sampler2D normalDistribution;\nuniform float roughness : 0.5;\nvarying vec2 v_Texcoord;\nvarying vec3 v_WorldPosition;\n@import clay.util.rgbm\nvec3 importanceSampleNormal(float i, float roughness, vec3 N) {\n vec3 H = texture2D(normalDistribution, vec2(roughness, i)).rgb;\n vec3 upVector = abs(N.y) > 0.999 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);\n vec3 tangentX = normalize(cross(N, upVector));\n vec3 tangentZ = cross(N, tangentX);\n return normalize(tangentX * H.x + N * H.y + tangentZ * H.z);\n}\nvoid main() {\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = normalize(v_WorldPosition - eyePos);\n vec3 N = V;\n vec3 prefilteredColor = vec3(0.0);\n float totalWeight = 0.0;\n float fMaxSampleNumber = float(SAMPLE_NUMBER);\n for (int i = 0; i < SAMPLE_NUMBER; i++) {\n vec3 H = importanceSampleNormal(float(i) / fMaxSampleNumber, roughness, N);\n vec3 L = reflect(-V, H);\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 gl_FragColor = encodeHDR(vec4(prefilteredColor / totalWeight, 1.0));\n}\n"; // Cubemap prefilter utility // http://www.unrealengine.com/files/downloads/2013SiggraphPresentationsNotes.pdf @@ -24057,8 +24183,8 @@ cubemapUtil.prefilterEnvironmentMap = function ( width: width, height: height, // FIXME FLOAT type will cause GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT error on iOS - type: textureType === Texture$1.FLOAT ? - Texture$1.HALF_FLOAT : textureType + type: textureType === Texture.FLOAT ? + Texture.HALF_FLOAT : textureType }); textureUtil.panoramaToCubeMap(renderer, envMap, envCubemap, { // PENDING encodeRGBM so it can be decoded as RGBM @@ -24078,7 +24204,7 @@ cubemapUtil.prefilterEnvironmentMap = function ( // Force to be UNSIGNED_BYTE if (textureOpts.encodeRGBM) { - textureType = prefilteredCubeMap.type = Texture$1.UNSIGNED_BYTE; + textureType = prefilteredCubeMap.type = Texture.UNSIGNED_BYTE; } var renderTargetTmp = new Texture2D({ @@ -24089,18 +24215,13 @@ cubemapUtil.prefilterEnvironmentMap = function ( var frameBuffer = new FrameBuffer({ depthBuffer: false }); - var ArrayCtor = vendor[textureType === Texture$1.UNSIGNED_BYTE ? 'Uint8Array' : 'Float32Array']; + var ArrayCtor = vendor[textureType === Texture.UNSIGNED_BYTE ? 'Uint8Array' : 'Float32Array']; for (var i = 0; i < mipmapNum; i++) { // console.time('prefilter'); prefilteredCubeMap.mipmaps[i] = { pixels: {} }; - skyEnv.material.set('roughness', i / (targets.length - 1)); - var maxSampleNumber = renderTargetTmp.width * renderTargetTmp.height; - if (renderTargetTmp.width >= 32) { - maxSampleNumber /= 4; - } - skyEnv.material.set('maxSampleNumber', Math.min(maxSampleNumber, 1024)); + skyEnv.material.set('roughness', i / (mipmapNum - 1)); // Tweak fov // http://the-witness.net/news/2012/02/seamless-cube-map-filtering/ @@ -24117,7 +24238,7 @@ cubemapUtil.prefilterEnvironmentMap = function ( renderer.render(dummyScene, camera); renderer.gl.readPixels( 0, 0, renderTargetTmp.width, renderTargetTmp.height, - Texture$1.RGBA, textureType, pixels + Texture.RGBA, textureType, pixels ); // var canvas = document.createElement('canvas'); @@ -24169,9 +24290,11 @@ cubemapUtil.integrateBRDF = function (renderer, normalDistribution) { var texture = new Texture2D({ width: 512, height: 256, - type: Texture$1.HALF_FLOAT, - minFilter: Texture$1.NEAREST, - magFilter: Texture$1.NEAREST, + type: Texture.HALF_FLOAT, + wrapS: Texture.CLAMP_TO_EDGE, + wrapT: Texture.CLAMP_TO_EDGE, + minFilter: Texture.NEAREST, + magFilter: Texture.NEAREST, useMipmap: false }); pass.setUniform('normalDistribution', normalDistribution); @@ -24207,11 +24330,11 @@ cubemapUtil.generateNormalDistribution = function (roughnessLevels, sampleSize) var normalDistribution = new Texture2D({ width: roughnessLevels, height: sampleSize, - type: Texture$1.FLOAT, - minFilter: Texture$1.NEAREST, - magFilter: Texture$1.NEAREST, - wrapS: Texture$1.CLAMP_TO_EDGE, - wrapT: Texture$1.CLAMP_TO_EDGE, + type: Texture.FLOAT, + minFilter: Texture.NEAREST, + magFilter: Texture.NEAREST, + wrapS: Texture.CLAMP_TO_EDGE, + wrapT: Texture.CLAMP_TO_EDGE, useMipmap: false }); var pixels = new Float32Array(sampleSize * roughnessLevels * 4); @@ -24312,6 +24435,10 @@ var AmbientCubemapLight = Light.extend({ cubemap.dispose(renderer); }, + getBRDFLookup: function () { + return this._brdfLookup; + }, + uniformTemplates: { ambientCubemapLightColor: { type: '3f', @@ -24499,7 +24626,7 @@ function isPowerOfTwo$2(width, height) { (height & (height-1)) === 0; } -var shadowmapEssl = "@export clay.sm.depth.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nattribute vec3 position : POSITION;\n#ifdef SHADOW_TRANSPARENT\nattribute vec2 texcoord : TEXCOORD_0;\n#endif\n@import clay.chunk.skinning_header\nvarying vec4 v_ViewPosition;\n#ifdef SHADOW_TRANSPARENT\nvarying vec2 v_Texcoord;\n#endif\nvoid main(){\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n v_ViewPosition = worldViewProjection * vec4(skinnedPosition, 1.0);\n gl_Position = v_ViewPosition;\n#ifdef SHADOW_TRANSPARENT\n v_Texcoord = texcoord;\n#endif\n}\n@end\n@export clay.sm.depth.fragment\nvarying vec4 v_ViewPosition;\n#ifdef SHADOW_TRANSPARENT\nvarying vec2 v_Texcoord;\n#endif\nuniform float bias : 0.001;\nuniform float slopeScale : 1.0;\n#ifdef SHADOW_TRANSPARENT\nuniform sampler2D transparentMap;\n#endif\n@import clay.util.encode_float\nvoid main(){\n float depth = v_ViewPosition.z / v_ViewPosition.w;\n#ifdef USE_VSM\n depth = depth * 0.5 + 0.5;\n float moment1 = depth;\n float moment2 = depth * depth;\n float dx = dFdx(depth);\n float dy = dFdy(depth);\n moment2 += 0.25*(dx*dx+dy*dy);\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#ifdef SHADOW_TRANSPARENT\n if (texture2D(transparentMap, v_Texcoord).a <= 0.1) {\n gl_FragColor = encodeFloat(0.9999);\n return;\n }\n#endif\n gl_FragColor = encodeFloat(depth * 0.5 + 0.5);\n#endif\n}\n@end\n@export clay.sm.debug_depth\nuniform sampler2D depthMap;\nvarying vec2 v_Texcoord;\n@import clay.util.decode_float\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@end\n@export clay.sm.distance.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world : WORLD;\nattribute vec3 position : POSITION;\n@import clay.chunk.skinning_header\nvarying vec3 v_WorldPosition;\nvoid main (){\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n gl_Position = worldViewProjection * vec4(skinnedPosition , 1.0);\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n}\n@end\n@export clay.sm.distance.fragment\nuniform vec3 lightPosition;\nuniform float range : 100;\nvarying vec3 v_WorldPosition;\n@import clay.util.encode_float\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@export clay.plugin.shadow_map_common\n@import clay.util.decode_float\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}\nfloat pcf(sampler2D map, vec2 uv, float z, float textureSize, vec2 scale) {\n float shadowContrib = tapShadowMap(map, uv, z);\n vec2 offset = vec2(1.0 / textureSize) * scale;\n#ifdef PCF_KERNEL_SIZE\n for (int _idx_ = 0; _idx_ < PCF_KERNEL_SIZE; _idx_++) {{\n shadowContrib += tapShadowMap(map, uv + offset * pcfKernel[_idx_], z);\n }}\n return shadowContrib / float(PCF_KERNEL_SIZE + 1);\n#else\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 return shadowContrib / 9.0;\n#endif\n}\nfloat pcf(sampler2D map, vec2 uv, float z, float textureSize) {\n return pcf(map, uv, z, textureSize, vec2(1.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 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 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 #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}\nfloat computeShadowContrib(sampler2D map, mat4 lightVPM, vec3 position, float textureSize) {\n return computeShadowContrib(map, lightVPM, position, textureSize, vec2(1.0), vec2(0.0));\n}\nfloat computeShadowContribOmni(samplerCube map, vec3 direction, float range)\n{\n float dist = length(direction);\n vec4 shadowTex = textureCube(map, direction);\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@end\n@export clay.plugin.compute_shadow_map\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT) || defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT) || defined(POINT_LIGHT_SHADOWMAP_COUNT)\n#ifdef SPOT_LIGHT_SHADOWMAP_COUNT\nuniform sampler2D spotLightShadowMaps[SPOT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\nuniform mat4 spotLightMatrices[SPOT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\nuniform float spotLightShadowMapSizes[SPOT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\n#endif\n#ifdef DIRECTIONAL_LIGHT_SHADOWMAP_COUNT\n#if defined(SHADOW_CASCADE)\nuniform sampler2D directionalLightShadowMaps[1]:unconfigurable;\nuniform mat4 directionalLightMatrices[SHADOW_CASCADE]:unconfigurable;\nuniform float directionalLightShadowMapSizes[1]:unconfigurable;\nuniform float shadowCascadeClipsNear[SHADOW_CASCADE]:unconfigurable;\nuniform float shadowCascadeClipsFar[SHADOW_CASCADE]:unconfigurable;\n#else\nuniform sampler2D directionalLightShadowMaps[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\nuniform mat4 directionalLightMatrices[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\nuniform float directionalLightShadowMapSizes[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\n#endif\n#endif\n#ifdef POINT_LIGHT_SHADOWMAP_COUNT\nuniform samplerCube pointLightShadowMaps[POINT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\n#endif\nuniform bool shadowEnabled : true;\n#ifdef PCF_KERNEL_SIZE\nuniform vec2 pcfKernel[PCF_KERNEL_SIZE];\n#endif\n@import clay.plugin.shadow_map_common\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\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#endif\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n#ifdef SHADOW_CASCADE\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 float shadowContrib;\n shadowContribs[0] = 1.0;\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#else\nvoid computeShadowOfDirectionalLights(vec3 position, inout float shadowContribs[DIRECTIONAL_LIGHT_COUNT]){\n float shadowContrib;\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#endif\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\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#endif\n#endif\n@end"; +var shadowmapEssl = "@export clay.sm.depth.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n@import clay.chunk.skinning_header\nvarying vec4 v_ViewPosition;\nvarying vec2 v_Texcoord;\nvoid main(){\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n v_ViewPosition = worldViewProjection * vec4(skinnedPosition, 1.0);\n gl_Position = v_ViewPosition;\n v_Texcoord = texcoord;\n}\n@end\n@export clay.sm.depth.fragment\nvarying vec4 v_ViewPosition;\nvarying vec2 v_Texcoord;\nuniform float bias : 0.001;\nuniform float slopeScale : 1.0;\nuniform sampler2D alphaMap;\nuniform float alphaCutoff: 0.0;\n@import clay.util.encode_float\nvoid main(){\n float depth = v_ViewPosition.z / v_ViewPosition.w;\n if (alphaCutoff > 0.0) {\n if (texture2D(alphaMap, v_Texcoord).a <= alphaCutoff) {\n discard;\n }\n }\n#ifdef USE_VSM\n depth = depth * 0.5 + 0.5;\n float moment1 = depth;\n float moment2 = depth * depth;\n float dx = dFdx(depth);\n float dy = dFdy(depth);\n moment2 += 0.25*(dx*dx+dy*dy);\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 gl_FragColor = encodeFloat(depth * 0.5 + 0.5);\n#endif\n}\n@end\n@export clay.sm.debug_depth\nuniform sampler2D depthMap;\nvarying vec2 v_Texcoord;\n@import clay.util.decode_float\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@end\n@export clay.sm.distance.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world : WORLD;\nattribute vec3 position : POSITION;\n@import clay.chunk.skinning_header\nvarying vec3 v_WorldPosition;\nvoid main (){\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n gl_Position = worldViewProjection * vec4(skinnedPosition , 1.0);\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n}\n@end\n@export clay.sm.distance.fragment\nuniform vec3 lightPosition;\nuniform float range : 100;\nvarying vec3 v_WorldPosition;\n@import clay.util.encode_float\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@export clay.plugin.shadow_map_common\n@import clay.util.decode_float\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}\nfloat pcf(sampler2D map, vec2 uv, float z, float textureSize, vec2 scale) {\n float shadowContrib = tapShadowMap(map, uv, z);\n vec2 offset = vec2(1.0 / textureSize) * scale;\n#ifdef PCF_KERNEL_SIZE\n for (int _idx_ = 0; _idx_ < PCF_KERNEL_SIZE; _idx_++) {{\n shadowContrib += tapShadowMap(map, uv + offset * pcfKernel[_idx_], z);\n }}\n return shadowContrib / float(PCF_KERNEL_SIZE + 1);\n#else\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 return shadowContrib / 9.0;\n#endif\n}\nfloat pcf(sampler2D map, vec2 uv, float z, float textureSize) {\n return pcf(map, uv, z, textureSize, vec2(1.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 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 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 #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}\nfloat computeShadowContrib(sampler2D map, mat4 lightVPM, vec3 position, float textureSize) {\n return computeShadowContrib(map, lightVPM, position, textureSize, vec2(1.0), vec2(0.0));\n}\nfloat computeShadowContribOmni(samplerCube map, vec3 direction, float range)\n{\n float dist = length(direction);\n vec4 shadowTex = textureCube(map, direction);\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@end\n@export clay.plugin.compute_shadow_map\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT) || defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT) || defined(POINT_LIGHT_SHADOWMAP_COUNT)\n#ifdef SPOT_LIGHT_SHADOWMAP_COUNT\nuniform sampler2D spotLightShadowMaps[SPOT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\nuniform mat4 spotLightMatrices[SPOT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\nuniform float spotLightShadowMapSizes[SPOT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\n#endif\n#ifdef DIRECTIONAL_LIGHT_SHADOWMAP_COUNT\n#if defined(SHADOW_CASCADE)\nuniform sampler2D directionalLightShadowMaps[1]:unconfigurable;\nuniform mat4 directionalLightMatrices[SHADOW_CASCADE]:unconfigurable;\nuniform float directionalLightShadowMapSizes[1]:unconfigurable;\nuniform float shadowCascadeClipsNear[SHADOW_CASCADE]:unconfigurable;\nuniform float shadowCascadeClipsFar[SHADOW_CASCADE]:unconfigurable;\n#else\nuniform sampler2D directionalLightShadowMaps[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\nuniform mat4 directionalLightMatrices[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\nuniform float directionalLightShadowMapSizes[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\n#endif\n#endif\n#ifdef POINT_LIGHT_SHADOWMAP_COUNT\nuniform samplerCube pointLightShadowMaps[POINT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\n#endif\nuniform bool shadowEnabled : true;\n#ifdef PCF_KERNEL_SIZE\nuniform vec2 pcfKernel[PCF_KERNEL_SIZE];\n#endif\n@import clay.plugin.shadow_map_common\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\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#endif\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n#ifdef SHADOW_CASCADE\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 float shadowContrib;\n shadowContribs[0] = 1.0;\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#else\nvoid computeShadowOfDirectionalLights(vec3 position, inout float shadowContribs[DIRECTIONAL_LIGHT_COUNT]){\n float shadowContrib;\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#endif\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\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#endif\n#endif\n@end"; var mat4$8 = glmatrix.mat4; @@ -24507,6 +24634,31 @@ var targets$2 = ['px', 'nx', 'py', 'ny', 'pz', 'nz']; Shader['import'](shadowmapEssl); +function getDepthMaterialUniform(renderable, depthMaterial, symbol) { + if (symbol === 'alphaMap') { + return renderable.material.get('diffuseMap'); + } + else if (symbol === 'alphaCutoff') { + if (renderable.material.isDefined('fragment', 'ALPHA_TEST') + && renderable.material.get('diffuseMap') + ) { + var alphaCutoff = renderable.material.get('alphaCutoff'); + return alphaCutoff || 0; + } + return 0; + } + else { + return depthMaterial.get(symbol); + } +} + +function isDepthMaterialChanged(renderable, prevRenderable) { + var matA = renderable.material; + var matB = prevRenderable.material; + return matA.get('diffuseMap') !== matB.get('diffuseMap') + || (matA.get('alphaCutoff') || 0) !== (matB.get('alphaCutoff') || 0); +} + /** * Pass rendering shadow map. * @@ -24856,6 +25008,8 @@ var ShadowMapPass = Base.extend(function () { getMaterial: function (renderable) { return renderable.shadowDepthMaterial || defaultShadowMaterial; }, + isMaterialChanged: isDepthMaterialChanged, + getUniform: getDepthMaterialUniform, ifRender: function (renderable) { return renderable.castShadow; }, @@ -24973,6 +25127,8 @@ var ShadowMapPass = Base.extend(function () { getMaterial: function (renderable) { return renderable.shadowDepthMaterial || defaultShadowMaterial; }, + isMaterialChanged: isDepthMaterialChanged, + getUniform: getDepthMaterialUniform, ifRender: function (renderable) { return renderable.castShadow; }, @@ -25008,6 +25164,7 @@ var ShadowMapPass = Base.extend(function () { getMaterial: function (renderable) { return renderable.shadowDepthMaterial || defaultShadowMaterial; }, + getUniform: getDepthMaterialUniform, sortCompare: Renderer.opaqueSortCompare }; @@ -25127,7 +25284,7 @@ var ShadowMapPass = Base.extend(function () { var parameter = { width: size, height: size, - type: Texture$1.FLOAT + type: Texture.FLOAT }; var tmpTexture = this._texturePool.get(parameter); @@ -25163,7 +25320,7 @@ var ShadowMapPass = Base.extend(function () { texture.width = resolution * cascade; texture.height = resolution; if (this.softShadow === ShadowMapPass.VSM) { - texture.type = Texture$1.FLOAT; + texture.type = Texture.FLOAT; texture.anisotropic = 4; } else { @@ -25761,7 +25918,7 @@ sh.projectEnvironmentMap = function (renderer, envMap, opts) { renderer.render(dummyScene, camera); renderer.gl.readPixels( 0, 0, width, height, - Texture$1.RGBA, Texture$1.UNSIGNED_BYTE, cubePixels[targets$3[i]] + Texture.RGBA, Texture.UNSIGNED_BYTE, cubePixels[targets$3[i]] ); framebuffer.unbind(renderer); } @@ -25793,6 +25950,9 @@ var EVE_NAMES = ['click', 'dblclick', 'mouseover', 'mouseout', 'mousemove', /** * @typedef {string|HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} ImageLike */ +/** + * @typedef {string|HTMLCanvasElement|HTMLImageElement|HTMLVideoElement|clay.Texture2D} TextureLike + */ /** * @typedef {string|Array.} Color */ @@ -25805,11 +25965,13 @@ var EVE_NAMES = ['click', 'dblclick', 'mouseover', 'mouseout', 'mousemove', * @property {Function} init Initialization callback that will be called when initing app. * You can return a promise in init to start the loop asynchronously when the promise is resolved. * @property {Function} loop Loop callback that will be called each frame. - * @property {Function} beforeRender - * @property {Function} afterRender + * @property {boolean} [autoRender=true] If render automatically each frame. + * @property {Function} [beforeRender] + * @property {Function} [afterRender] * @property {number} [width] Container width. * @property {number} [height] Container height. * @property {number} [devicePixelRatio] + * @property {Object.} [methods] Methods that will be injected to App3D#methods. * @property {Object} [graphic] Graphic configuration including shadow, color space. * @property {boolean} [graphic.shadow=false] If enable shadow * @property {boolean} [graphic.linear=false] If use linear color space @@ -25817,6 +25979,23 @@ var EVE_NAMES = ['click', 'dblclick', 'mouseover', 'mouseout', 'mousemove', * @property {boolean} [event=false] If enable mouse/touch event. It will slow down the system if geometries are complex. */ +/** + * @typedef {Object} StandardMaterialMRConfig + * @property {string} [shader='standardMR'] + * @property {Color} [color] + * @property {number} [alpha] + * @property {number} [metalness] + * @property {number} [roughness] + * @property {Color} [emission] + * @property {number} [emissionIntensity] + * @property {boolean} [transparent] + * @property {TextureLike} [diffuseMap] + * @property {TextureLike} [normalMap] + * @property {TextureLike} [roughnessMap] + * @property {TextureLike} [metalnessMap] + * @property {TextureLike} [emissiveMap] + */ + /** * Using App3D is a much more convenient way to create and manage your 3D application. * @@ -25859,6 +26038,10 @@ function App3D(dom, appNS) { appNS = appNS || {}; appNS.graphic = appNS.graphic || {}; + if (appNS.autoRender == null) { + appNS.autoRender = true; + } + if (typeof dom === 'string') { dom = document.querySelector(dom); } @@ -25891,6 +26074,11 @@ function App3D(dom, appNS) { gTimeline.start(); + var userMethods = {}; + for (var key in appNS.methods) { + userMethods[key] = appNS.methods[key].bind(appNS, this); + } + Object.defineProperties(this, { /** * Container dom element @@ -25924,7 +26112,31 @@ function App3D(dom, appNS) { * @name clay.application.App3D#elapsedTime * @type {number} */ - elapsedTime: { get: function () { return gElapsedTime; }} + elapsedTime: { get: function () { return gElapsedTime; }}, + + /** + * Width of viewport. + * @name clay.application.App3D#width + * @type {number} + */ + width: { get: function () { return gRenderer.getWidth(); }}, + /** + * Height of viewport. + * @name clay.application.App3D#height + * @type {number} + */ + height: { get: function () { return gRenderer.getHeight(); }}, + + /** + * Methods from + * @name clay.application.App3D#height + * @type {number} + */ + methods: { get: function () { return userMethods; } }, + + _shadowPass: { get: function () { return gShadowPass; } }, + + _appNS: { get: function () { return appNS; } }, }); /** @@ -25965,60 +26177,47 @@ function App3D(dom, appNS) { this._geoCache = new LRU$1(20); this._texCache = new LRU$1(20); + // GPU Resources. + this._texturesList = {}; + this._geometriesList = {}; + // Do init the application. var initPromise = Promise.resolve(appNS.init && appNS.init(this)); // Use the inited camera. gRayPicking && (gRayPicking.camera = gScene.getMainCamera()); - var gTexturesList = {}; - var gGeometriesList = {}; - if (!appNS.loop) { console.warn('Miss loop method.'); } var self = this; initPromise.then(function () { - appNS.loop && gTimeline.on('frame', function (frameTime) { + gTimeline.on('frame', function (frameTime) { gFrameTime = frameTime; gElapsedTime += frameTime; - appNS.loop(self); - gScene.update(); - var skyboxList = []; - gScene.skybox && skyboxList.push(gScene.skybox); - gScene.skydome && skyboxList.push(gScene.skydome); - self._updateGraphicOptions(appNS.graphic, skyboxList, true); - - gRayPicking && (gRayPicking.camera = gScene.getMainCamera()); - // Render shadow pass - gShadowPass && gShadowPass.render(gRenderer, gScene, null, true); - - appNS.beforeRender && appNS.beforeRender(self); - self._doRender(gRenderer, gScene, true); - appNS.afterRender && appNS.afterRender(self); - - // Mark all resources unused; - markUnused(gTexturesList); - markUnused(gGeometriesList); + var camera = gScene.getMainCamera(); + if (camera) { + camera.aspect = gRenderer.getViewportAspect(); + } + gRayPicking && (gRayPicking.camera = camera); - // Collect resources used in this frame. - var newTexturesList = []; - var newGeometriesList = []; - collectResources(gScene, newTexturesList, newGeometriesList); + appNS.loop && appNS.loop(self); - // Dispose those unsed resources. - checkAndDispose(gRenderer, gTexturesList); - checkAndDispose(gRenderer, gGeometriesList); + if (appNS.autoRender) { + self.render(); + } - gTexturesList = newTexturesList; - gGeometriesList = newGeometriesList; - }); + self.collectResources(); + }, this); }); gScene.on('beforerender', function (renderer, scene, camera, renderList) { - this._updateGraphicOptions(appNS.graphic, renderList.opaque, false); - this._updateGraphicOptions(appNS.graphic, renderList.transparent, false); + if (this._inRender) { + // Only update graphic options when using #render function. + this._updateGraphicOptions(appNS.graphic, renderList.opaque, false); + this._updateGraphicOptions(appNS.graphic, renderList.transparent, false); + } }, this); } @@ -26164,10 +26363,58 @@ App3D.prototype._updateGraphicOptions = function (graphicOpts, list, isSkybox) { App3D.prototype._doRender = function (renderer, scene) { var camera = scene.getMainCamera(); - camera.aspect = renderer.getViewportAspect(); renderer.render(scene, camera, true); }; +/** + * Do render + */ +App3D.prototype.render = function () { + this._inRender = true; + var appNS = this._appNS; + appNS.beforeRender && appNS.beforeRender(self); + + var scene = this.scene; + var renderer = this.renderer; + var shadowPass = this._shadowPass; + + scene.update(); + var skyboxList = []; + scene.skybox && skyboxList.push(scene.skybox); + scene.skydome && skyboxList.push(scene.skydome); + + this._updateGraphicOptions(appNS.graphic, skyboxList, true); + // Render shadow pass + shadowPass && shadowPass.render(renderer, scene, null, true); + + this._doRender(renderer, scene, true); + + appNS.afterRender && appNS.afterRender(self); + this._inRender = false; +}; + +App3D.prototype.collectResources = function () { + var renderer = this.renderer; + var scene = this.scene; + var texturesList = this._texturesList; + var geometriesList = this._geometriesList; + // Mark all resources unused; + markUnused(texturesList); + markUnused(geometriesList); + + // Collect resources used in this frame. + var newTexturesList = []; + var newGeometriesList = []; + collectResources(scene, newTexturesList, newGeometriesList); + + // Dispose those unsed resources. + checkAndDispose(renderer, texturesList); + checkAndDispose(renderer, geometriesList); + + this._texturesList = newTexturesList; + this._geometriesList = newGeometriesList; +}; + function markUnused(resourceList) { for (var i = 0; i < resourceList.length; i++) { @@ -26399,11 +26646,11 @@ App3D.prototype.loadTextureCubeSync = function (imgList, opts) { /** * Create a material. - * @param {Object} materialConfig. materialConfig contains `shader`, `transparent` and uniforms that used in corresponding uniforms. + * @param {Object|StandardMaterialMRConfig} materialConfig. materialConfig contains `shader`, `transparent` and uniforms that used in corresponding uniforms. * Uniforms can be `color`, `alpha` `diffuseMap` etc. * @param {string|clay.Shader} [shader='clay.standardMR'] Default to be standard shader with metalness and roughness workflow. * @param {boolean} [transparent=false] If material is transparent. - * @param {boolean} [convertTextureToPOT=false] Force convert None Power of Two texture to Power of two so it can be tiled. + * @param {boolean} [textureConvertToPOT=false] Force convert None Power of Two texture to Power of two so it can be tiled. * @param {boolean} [textureFlipY=true] If flip y of texture. * @return {clay.Material} */ @@ -26423,11 +26670,11 @@ App3D.prototype.createMaterial = function (matConfig) { if (material.uniforms[key]) { var val = matConfig[key]; if ((material.uniforms[key].type === 't' || isImageLikeElement(val)) - && !(val instanceof Texture$1) + && !(val instanceof Texture) ) { // Try to load a texture. this.loadTexture(val, { - convertToPOT: matConfig.convertTextureToPOT || false, + convertToPOT: matConfig.textureConvertToPOT || false, flipY: matConfig.textureFlipY == null ? true : matConfig.textureFlipY }).then(makeTextureSetter(key)); } @@ -26861,7 +27108,7 @@ App3D.prototype.createAmbientCubemapLight = function (envImage, specIntensity, d * @param {boolean} [opts.autoPlayAnimation=true] If autoplay the animation of model. * @param {boolean} [opts.upAxis='y'] Change model to y up if upAxis is 'z' * @param {boolean} [opts.textureFlipY=false] - * @param {boolean} [opts.convertTextureToPOT=false] If convert texture to power-of-two + * @param {boolean} [opts.textureConvertToPOT=false] If convert texture to power-of-two * @param {string} [opts.textureRootPath] Root path of texture. Default to be relative with glTF file. * @param {clay.Node} [parentNode] Parent node that model will be mounted. Default to be scene * @return {Promise} @@ -26883,7 +27130,7 @@ App3D.prototype.loadModel = function (url, opts, parentNode) { textureRootPath: opts.textureRootPath, crossOrigin: 'Anonymous', textureFlipY: opts.textureFlipY, - convertTextureToPOT: opts.convertTextureToPOT + textureConvertToPOT: opts.textureConvertToPOT }; if (opts.upAxis && opts.upAxis.toLowerCase() === 'z') { loaderOpts.rootNode.rotation.identity().rotateX(-Math.PI / 2); @@ -28227,7 +28474,7 @@ var downsampleEssl = "@export clay.compositor.downsample\nuniform sampler2D text var upsampleEssl = "\n@export clay.compositor.upsample\n#define HIGH_QUALITY\nuniform sampler2D texture;\nuniform vec2 textureSize : [512, 512];\nuniform float sampleScale: 0.5;\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\n@import clay.util.clamp_sample\nvoid main()\n{\n#ifdef HIGH_QUALITY\n vec4 d = vec4(1.0, 1.0, -1.0, 0.0) / textureSize.xyxy * sampleScale;\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 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 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 gl_FragColor = encodeHDR(s / 16.0);\n#else\n vec4 d = vec4(-1.0, -1.0, +1.0, +1.0) / textureSize.xyxy;\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 gl_FragColor = encodeHDR(s / 4.0);\n#endif\n}\n@end"; -var hdrEssl = "@export clay.compositor.hdr.composite\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#ifdef LUM_ENABLED\nuniform sampler2D lum;\n#endif\n#ifdef LUT_ENABLED\nuniform sampler2D lut;\n#endif\n#ifdef COLOR_CORRECTION\nuniform float brightness : 0.0;\nuniform float contrast : 1.0;\nuniform float saturation : 1.0;\n#endif\n#ifdef VIGNETTE\nuniform float vignetteDarkness: 1.0;\nuniform float vignetteOffset: 1.0;\n#endif\nuniform float exposure : 1.0;\nuniform float bloomIntensity : 0.25;\nuniform float lensflareIntensity : 1;\nvarying vec2 v_Texcoord;\n@import clay.util.srgb\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}\nfloat eyeAdaption(float fLum)\n{\n return mix(0.2, fLum, 0.5);\n}\n#ifdef LUT_ENABLED\nvec3 lutTransform(vec3 color) {\n float blueColor = color.b * 63.0;\n vec2 quad1;\n quad1.y = floor(floor(blueColor) / 8.0);\n quad1.x = floor(blueColor) - (quad1.y * 8.0);\n vec2 quad2;\n quad2.y = floor(ceil(blueColor) / 8.0);\n quad2.x = ceil(blueColor) - (quad2.y * 8.0);\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 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 vec4 newColor1 = texture2D(lut, texPos1);\n vec4 newColor2 = texture2D(lut, texPos2);\n vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n return newColor.rgb;\n}\n#endif\n@import clay.util.rgbm\nvoid main()\n{\n vec4 texel = vec4(0.0);\n vec4 originalTexel = vec4(0.0);\n#ifdef TEXTURE_ENABLED\n texel = decodeHDR(texture2D(texture, v_Texcoord));\n originalTexel = texel;\n#endif\n#ifdef BLOOM_ENABLED\n vec4 bloomTexel = decodeHDR(texture2D(bloom, v_Texcoord));\n texel.rgb += bloomTexel.rgb * bloomIntensity;\n texel.a += bloomTexel.a * bloomIntensity;\n#endif\n#ifdef LENSFLARE_ENABLED\n texel += decodeHDR(texture2D(lensflare, v_Texcoord)) * texture2D(lensdirt, v_Texcoord) * lensflareIntensity;\n#endif\n texel.a = min(texel.a, 1.0);\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 texel.rgb = ACESToneMapping(texel.rgb);\n texel = linearTosRGB(texel);\n#ifdef LUT_ENABLED\n texel.rgb = lutTransform(clamp(texel.rgb,vec3(0.0),vec3(1.0)));\n#endif\n#ifdef COLOR_CORRECTION\n texel.rgb = clamp(texel.rgb + vec3(brightness), 0.0, 1.0);\n texel.rgb = clamp((texel.rgb - vec3(0.5))*contrast+vec3(0.5), 0.0, 1.0);\n float lum = dot(texel.rgb, vec3(0.2125, 0.7154, 0.0721));\n texel.rgb = mix(vec3(lum), texel.rgb, saturation);\n#endif\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 gl_FragColor = encodeHDR(texel);\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)) * bloomIntensity);\n #elif DEBUG == 3\n gl_FragColor = encodeHDR(decodeHDR(texture2D(lensflare, v_Texcoord) * lensflareIntensity));\n #endif\n#endif\n if (originalTexel.a <= 0.01 && gl_FragColor.a > 1e-5) {\n gl_FragColor.a = dot(gl_FragColor.rgb, vec3(0.2125, 0.7154, 0.0721));\n }\n#ifdef PREMULTIPLY_ALPHA\n gl_FragColor.rgb *= gl_FragColor.a;\n#endif\n}\n@end"; +var hdrEssl = "@export clay.compositor.hdr.composite\n#define TONEMAPPING\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#ifdef LUM_ENABLED\nuniform sampler2D lum;\n#endif\n#ifdef LUT_ENABLED\nuniform sampler2D lut;\n#endif\n#ifdef COLOR_CORRECTION\nuniform float brightness : 0.0;\nuniform float contrast : 1.0;\nuniform float saturation : 1.0;\n#endif\n#ifdef VIGNETTE\nuniform float vignetteDarkness: 1.0;\nuniform float vignetteOffset: 1.0;\n#endif\nuniform float exposure : 1.0;\nuniform float bloomIntensity : 0.25;\nuniform float lensflareIntensity : 1;\nvarying vec2 v_Texcoord;\n@import clay.util.srgb\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}\nfloat eyeAdaption(float fLum)\n{\n return mix(0.2, fLum, 0.5);\n}\n#ifdef LUT_ENABLED\nvec3 lutTransform(vec3 color) {\n float blueColor = color.b * 63.0;\n vec2 quad1;\n quad1.y = floor(floor(blueColor) / 8.0);\n quad1.x = floor(blueColor) - (quad1.y * 8.0);\n vec2 quad2;\n quad2.y = floor(ceil(blueColor) / 8.0);\n quad2.x = ceil(blueColor) - (quad2.y * 8.0);\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 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 vec4 newColor1 = texture2D(lut, texPos1);\n vec4 newColor2 = texture2D(lut, texPos2);\n vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n return newColor.rgb;\n}\n#endif\n@import clay.util.rgbm\nvoid main()\n{\n vec4 texel = vec4(0.0);\n vec4 originalTexel = vec4(0.0);\n#ifdef TEXTURE_ENABLED\n texel = decodeHDR(texture2D(texture, v_Texcoord));\n originalTexel = texel;\n#endif\n#ifdef BLOOM_ENABLED\n vec4 bloomTexel = decodeHDR(texture2D(bloom, v_Texcoord));\n texel.rgb += bloomTexel.rgb * bloomIntensity;\n texel.a += bloomTexel.a * bloomIntensity;\n#endif\n#ifdef LENSFLARE_ENABLED\n texel += decodeHDR(texture2D(lensflare, v_Texcoord)) * texture2D(lensdirt, v_Texcoord) * lensflareIntensity;\n#endif\n texel.a = min(texel.a, 1.0);\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#ifdef TONEMAPPING\n texel.rgb *= exposureBias;\n texel.rgb = ACESToneMapping(texel.rgb);\n#endif\n texel = linearTosRGB(texel);\n#ifdef LUT_ENABLED\n texel.rgb = lutTransform(clamp(texel.rgb,vec3(0.0),vec3(1.0)));\n#endif\n#ifdef COLOR_CORRECTION\n texel.rgb = clamp(texel.rgb + vec3(brightness), 0.0, 1.0);\n texel.rgb = clamp((texel.rgb - vec3(0.5))*contrast+vec3(0.5), 0.0, 1.0);\n float lum = dot(texel.rgb, vec3(0.2125, 0.7154, 0.0721));\n texel.rgb = mix(vec3(lum), texel.rgb, saturation);\n#endif\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 gl_FragColor = encodeHDR(texel);\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)) * bloomIntensity);\n #elif DEBUG == 3\n gl_FragColor = encodeHDR(decodeHDR(texture2D(lensflare, v_Texcoord) * lensflareIntensity));\n #endif\n#endif\n if (originalTexel.a <= 0.01 && gl_FragColor.a > 1e-5) {\n gl_FragColor.a = dot(gl_FragColor.rgb, vec3(0.2125, 0.7154, 0.0721));\n }\n#ifdef PREMULTIPLY_ALPHA\n gl_FragColor.rgb *= gl_FragColor.a;\n#endif\n}\n@end"; var dofEssl = "@export clay.compositor.dof.coc\nuniform sampler2D depth;\nuniform float zNear: 0.1;\nuniform float zFar: 2000;\nuniform float focalDist: 3;\nuniform float focalRange: 1;\nuniform float focalLength: 30;\nuniform float fstop: 2.8;\nvarying vec2 v_Texcoord;\n@import clay.util.encode_float\nvoid main()\n{\n float z = texture2D(depth, v_Texcoord).r * 2.0 - 1.0;\n float dist = 2.0 * zNear * zFar / (zFar + zNear - z * (zFar - zNear));\n float aperture = focalLength / fstop;\n float coc;\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 coc = abs(aperture * (focalLength * (dist - focalAdjusted)) / (dist * (focalAdjusted - focalLength)));\n coc = clamp(coc, 0.0, 0.4) / 0.4000001;\n if (dist < lower) {\n coc = -coc;\n }\n coc = coc * 0.5 + 0.5;\n }\n gl_FragColor = encodeFloat(coc);\n}\n@end\n@export clay.compositor.dof.premultiply\nuniform sampler2D texture;\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\n@import clay.util.decode_float\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@export clay.compositor.dof.min_coc\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform vec2 textureSize : [512.0, 512.0];\n@import clay.util.float\nvoid main()\n{\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\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 gl_FragColor = encodeFloat(fCoc);\n}\n@end\n@export clay.compositor.dof.max_coc\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform vec2 textureSize : [512.0, 512.0];\n@import clay.util.float\nvoid main()\n{\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\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 gl_FragColor = encodeFloat(fCoc);\n}\n@end\n@export clay.compositor.dof.coc_upsample\n#define HIGH_QUALITY\nuniform sampler2D coc;\nuniform vec2 textureSize : [512, 512];\nuniform float sampleScale: 0.5;\nvarying vec2 v_Texcoord;\n@import clay.util.float\nvoid main()\n{\n#ifdef HIGH_QUALITY\n vec4 d = vec4(1.0, 1.0, -1.0, 0.0) / textureSize.xyxy * sampleScale;\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 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 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 gl_FragColor = encodeFloat(s / 16.0);\n#else\n vec4 d = vec4(-1.0, -1.0, +1.0, +1.0) / textureSize.xyxy;\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 gl_FragColor = encodeFloat(s / 4.0);\n#endif\n}\n@end\n@export clay.compositor.dof.upsample\n#define HIGH_QUALITY\nuniform sampler2D coc;\nuniform sampler2D texture;\nuniform vec2 textureSize : [512, 512];\nuniform float sampleScale: 0.5;\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\n@import clay.util.decode_float\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}\nvoid main()\n{\n#ifdef HIGH_QUALITY\n vec4 d = vec4(1.0, 1.0, -1.0, 0.0) / textureSize.xyxy * sampleScale;\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 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 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 gl_FragColor = encodeHDR(color / w);\n#else\n vec4 d = vec4(-1.0, -1.0, +1.0, +1.0) / textureSize.xyxy;\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 gl_FragColor = encodeHDR(color / w);\n#endif\n}\n@end\n@export clay.compositor.dof.downsample\nuniform sampler2D texture;\nuniform sampler2D coc;\nuniform vec2 textureSize : [512, 512];\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\n@import clay.util.decode_float\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}\nvoid main()\n{\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\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 gl_FragColor = encodeHDR(color);\n}\n@end\n@export clay.compositor.dof.hexagonal_blur_frag\n@import clay.util.float\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 float weightSum = 0.0;\n float kernelWeight = 1.0 / float(KERNEL_SIZE);\n for (int i = 0; i < KERNEL_SIZE; i++) {\n vec2 coord = v_Texcoord + offset * float(i);\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 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@end\n@export clay.compositor.dof.hexagonal_blur_1\n#define KERNEL_SIZE 5\nuniform sampler2D texture;\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform float blurSize : 1.0;\nuniform vec2 textureSize : [512.0, 512.0];\n@import clay.util.rgbm\n@import clay.compositor.dof.hexagonal_blur_frag\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 gl_FragColor = doBlur(texture, vec2(0.0, offset.y));\n#if !defined(BLUR_COC)\n gl_FragColor = encodeHDR(gl_FragColor);\n#endif\n}\n@end\n@export clay.compositor.dof.hexagonal_blur_2\n#define KERNEL_SIZE 5\nuniform sampler2D texture;\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform float blurSize : 1.0;\nuniform vec2 textureSize : [512.0, 512.0];\n@import clay.util.rgbm\n@import clay.compositor.dof.hexagonal_blur_frag\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 offset.y /= 2.0;\n gl_FragColor = doBlur(texture, -offset);\n#if !defined(BLUR_COC)\n gl_FragColor = encodeHDR(gl_FragColor);\n#endif\n}\n@end\n@export clay.compositor.dof.hexagonal_blur_3\n#define KERNEL_SIZE 5\nuniform sampler2D texture1;\nuniform sampler2D texture2;\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform float blurSize : 1.0;\nuniform vec2 textureSize : [512.0, 512.0];\n@import clay.util.rgbm\n@import clay.compositor.dof.hexagonal_blur_frag\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 offset.y /= 2.0;\n vec2 vDownRight = vec2(offset.x, -offset.y);\n vec4 texel1 = doBlur(texture1, -offset);\n vec4 texel2 = doBlur(texture1, vDownRight);\n vec4 texel3 = doBlur(texture2, vDownRight);\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#else\n vec4 color = (texel1 + texel2 + texel3) / 3.0;\n gl_FragColor = encodeHDR(color);\n#endif\n}\n@end\n@export clay.compositor.dof.composite\n#define DEBUG 0\nuniform sampler2D original;\nuniform sampler2D blurred;\nuniform sampler2D nearfield;\nuniform sampler2D coc;\nuniform sampler2D nearcoc;\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\n@import clay.util.float\nvoid main()\n{\n vec4 blurredColor = decodeHDR(texture2D(blurred, v_Texcoord));\n vec4 originalColor = decodeHDR(texture2D(original, v_Texcoord));\n float fCoc = decodeFloat(texture2D(coc, v_Texcoord));\n fCoc = abs(fCoc * 2.0 - 1.0);\n float weight = smoothstep(0.0, 1.0, fCoc);\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 gl_FragColor = encodeHDR(\n mix(\n nearfieldColor, mix(originalColor, blurredColor, weight),\n pow(1.0 - fNearCoc, 4.0)\n )\n );\n#else\n gl_FragColor = encodeHDR(mix(originalColor, blurredColor, weight));\n#endif\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@end"; @@ -28418,7 +28665,7 @@ function convertParameter(paramInfo) { if (val != null) { // Convert string to enum if (typeof val === 'string') { - val = Texture$1[val]; + val = Texture[val]; } param[name] = val; } @@ -28533,7 +28780,7 @@ function tryConvertExpr(string) { // Alias -var gbufferEssl = "@export clay.deferred.gbuffer.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\nuniform vec2 uvRepeat;\nuniform vec2 uvOffset;\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n#ifdef FIRST_PASS\nattribute vec3 normal : NORMAL;\n#endif\n@import clay.chunk.skinning_header\n#ifdef FIRST_PASS\nvarying vec3 v_Normal;\nattribute vec4 tangent : TANGENT;\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\nvarying vec3 v_WorldPosition;\n#endif\nvarying vec2 v_Texcoord;\nvoid main()\n{\n vec3 skinnedPosition = position;\n#ifdef FIRST_PASS\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n bool hasTangent = dot(tangent, tangent) > 0.0;\n#endif\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\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 gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n#ifdef FIRST_PASS\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\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 v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n#endif\n}\n@end\n@export clay.deferred.gbuffer1.fragment\nuniform mat4 viewInverse : VIEWINVERSE;\nuniform float glossiness;\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nuniform sampler2D normalMap;\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\nuniform sampler2D roughGlossMap;\nuniform bool useRoughGlossMap;\nuniform bool useRoughness;\nuniform bool doubleSided;\nuniform int roughGlossChannel: 0;\nfloat indexingTexel(in vec4 texel, in int idx) {\n if (idx == 3) return texel.a;\n else if (idx == 1) return texel.g;\n else if (idx == 2) return texel.b;\n else return texel.r;\n}\nvoid main()\n{\n vec3 N = v_Normal;\n if (doubleSided) {\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = eyePos - v_WorldPosition;\n if (dot(N, V) < 0.0) {\n N = -N;\n }\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 gl_FragColor.rgb = (N + 1.0) * 0.5;\n float g = glossiness;\n if (useRoughGlossMap) {\n float g2 = indexingTexel(texture2D(roughGlossMap, v_Texcoord), roughGlossChannel);\n if (useRoughness) {\n g2 = 1.0 - g2;\n }\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\n }\n gl_FragColor.a = g + 0.005;\n}\n@end\n@export clay.deferred.gbuffer2.fragment\nuniform sampler2D diffuseMap;\nuniform sampler2D metalnessMap;\nuniform vec3 color;\nuniform float metalness;\nuniform bool useMetalnessMap;\nuniform bool linear;\nvarying vec2 v_Texcoord;\n@import clay.util.srgb\nvoid main ()\n{\n float m = metalness;\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 gl_FragColor.rgb = texel.rgb * color;\n gl_FragColor.a = m + 0.005;\n}\n@end\n@export clay.deferred.gbuffer.debug\n@import clay.deferred.chunk.light_head\nuniform int debug: 0;\nvoid main ()\n{\n @import clay.deferred.chunk.gbuffer_read\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"; +var gbufferEssl = "@export clay.deferred.gbuffer.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\nuniform vec2 uvRepeat;\nuniform vec2 uvOffset;\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n#ifdef FIRST_PASS\nattribute vec3 normal : NORMAL;\n#endif\n@import clay.chunk.skinning_header\n#ifdef FIRST_PASS\nvarying vec3 v_Normal;\nattribute vec4 tangent : TANGENT;\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\nvarying vec3 v_WorldPosition;\n#endif\nvarying vec2 v_Texcoord;\nvoid main()\n{\n vec3 skinnedPosition = position;\n#ifdef FIRST_PASS\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n bool hasTangent = dot(tangent, tangent) > 0.0;\n#endif\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\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 gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n#ifdef FIRST_PASS\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\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 v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n#endif\n}\n@end\n@export clay.deferred.gbuffer1.fragment\nuniform mat4 viewInverse : VIEWINVERSE;\nuniform float glossiness;\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nuniform sampler2D normalMap;\nuniform sampler2D diffuseMap;\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\nuniform sampler2D roughGlossMap;\nuniform bool useRoughGlossMap;\nuniform bool useRoughness;\nuniform bool doubleSided;\nuniform float alphaCutoff: 0.0;\nuniform float alpha: 1.0;\nuniform int roughGlossChannel: 0;\nfloat indexingTexel(in vec4 texel, in int idx) {\n if (idx == 3) return texel.a;\n else if (idx == 1) return texel.g;\n else if (idx == 2) return texel.b;\n else return texel.r;\n}\nvoid main()\n{\n vec3 N = v_Normal;\n if (doubleSided) {\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = eyePos - v_WorldPosition;\n if (dot(N, V) < 0.0) {\n N = -N;\n }\n }\n if (alphaCutoff > 0.0) {\n float a = texture2D(diffuseMap, v_Texcoord).a * alpha;\n if (a < alphaCutoff) {\n discard;\n }\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 gl_FragColor.rgb = (N + 1.0) * 0.5;\n float g = glossiness;\n if (useRoughGlossMap) {\n float g2 = indexingTexel(texture2D(roughGlossMap, v_Texcoord), roughGlossChannel);\n if (useRoughness) {\n g2 = 1.0 - g2;\n }\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\n }\n gl_FragColor.a = g + 0.005;\n}\n@end\n@export clay.deferred.gbuffer2.fragment\nuniform sampler2D diffuseMap;\nuniform sampler2D metalnessMap;\nuniform vec3 color;\nuniform float metalness;\nuniform bool useMetalnessMap;\nuniform bool linear;\nuniform float alphaCutoff: 0.0;\nuniform float alpha: 1.0;\nvarying vec2 v_Texcoord;\n@import clay.util.srgb\nvoid main ()\n{\n float m = metalness;\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 if (alphaCutoff > 0.0) {\n float a = texel.a * alpha;\n if (a < alphaCutoff) {\n discard;\n }\n }\n gl_FragColor.rgb = texel.rgb * color;\n gl_FragColor.a = m + 0.005;\n}\n@end\n@export clay.deferred.gbuffer.debug\n@import clay.deferred.chunk.light_head\nuniform int debug: 0;\nvoid main ()\n{\n @import clay.deferred.chunk.gbuffer_read\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"; var chunkEssl = "@export clay.deferred.chunk.light_head\nuniform sampler2D gBufferTexture1;\nuniform sampler2D gBufferTexture2;\nuniform sampler2D gBufferTexture3;\nuniform vec2 windowSize: WINDOW_SIZE;\nuniform vec4 viewport: VIEWPORT;\nuniform mat4 viewProjectionInv;\n#ifdef DEPTH_ENCODED\n@import clay.util.decode_float\n#endif\n@end\n@export clay.deferred.chunk.gbuffer_read\n vec2 uv = gl_FragCoord.xy / windowSize;\n vec2 uv2 = (gl_FragCoord.xy - viewport.xy) / viewport.zw;\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 float glossiness = texel1.a;\n float metalness = texel3.a;\n vec3 N = texel1.rgb * 2.0 - 1.0;\n float z = texture2D(gBufferTexture2, uv).r * 2.0 - 1.0;\n vec2 xy = uv2 * 2.0 - 1.0;\n vec4 projectedPos = vec4(xy, z, 1.0);\n vec4 p4 = viewProjectionInv * projectedPos;\n vec3 position = p4.xyz / p4.w;\n vec3 albedo = texel3.rgb;\n vec3 diffuseColor = albedo * (1.0 - metalness);\n vec3 specularColor = mix(vec3(0.04), albedo, metalness);\n@end\n@export clay.deferred.chunk.light_equation\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}\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}\nvec3 F_Schlick(in float ndv, vec3 spec) {\n return spec + (1.0 - spec) * pow(1.0 - ndv, 5.0);\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@end"; @@ -28550,161 +28797,80 @@ function createFillCanvas(color) { return canvas; } -function attachTextureToSlot(renderer, program, symbol, texture, slot) { - var gl = renderer.gl; - program.setUniform(gl, '1i', symbol, slot); - - gl.activeTexture(gl.TEXTURE0 + slot); - // Maybe texture is not loaded yet; - if (texture.isRenderable()) { - texture.bind(renderer); - } - else { - // Bind texture to null - texture.unbind(renderer); - } -} - -// TODO Use globalShader insteadof globalMaterial? -function getBeforeRenderHook1 (gl, defaultNormalMap, defaultRoughnessMap) { - - var previousNormalMap; - var previousRougGlossMap; - var previousRenderable; - - return function (renderable, gBufferMat, prevMaterial) { - // Material not change - if (previousRenderable && previousRenderable.material === renderable.material) { - return; - } +// TODO specularColor +// TODO Performance improvement +function getGetUniformHook1(gl, defaultNormalMap, defaultRoughnessMap, defaultDiffuseMap) { + return function (renderable, gBufferMat, symbol) { var standardMaterial = renderable.material; - var program = renderable.__program; - - var glossiness; - var roughGlossMap; - var useRoughnessWorkflow = standardMaterial.isDefined('fragment', 'USE_ROUGHNESS'); - var doubleSided = standardMaterial.isDefined('fragment', 'DOUBLE_SIDED'); - var roughGlossChannel; - if (useRoughnessWorkflow) { - glossiness = 1.0 - standardMaterial.get('roughness'); - roughGlossMap = standardMaterial.get('roughnessMap'); - roughGlossChannel = standardMaterial.getDefine('fragment', 'ROUGHNESS_CHANNEL'); + if (symbol === 'doubleSided') { + return standardMaterial.isDefined('fragment', 'DOUBLE_SIDED'); } - else { - glossiness = standardMaterial.get('glossiness'); - roughGlossMap = standardMaterial.get('glossinessMap'); - roughGlossChannel = standardMaterial.getDefine('fragment', 'GLOSSINESS_CHANNEL'); + else if (symbol === 'uvRepeat' || symbol === 'uvOffset' || symbol === 'alpha') { + return standardMaterial.get(symbol); } - var useRoughGlossMap = !!roughGlossMap; - - var normalMap = standardMaterial.get('normalMap') || defaultNormalMap; - var uvRepeat = standardMaterial.get('uvRepeat'); - var uvOffset = standardMaterial.get('uvOffset'); - - roughGlossMap = roughGlossMap || defaultRoughnessMap; - - if (prevMaterial !== gBufferMat) { - gBufferMat.set('glossiness', glossiness); - gBufferMat.set('normalMap', normalMap); - gBufferMat.set('roughGlossMap', roughGlossMap); - gBufferMat.set('useRoughGlossMap', +useRoughGlossMap); - gBufferMat.set('useRoughness', +useRoughnessWorkflow); - gBufferMat.set('doubleSided', +doubleSided); - gBufferMat.set('roughGlossChannel', +roughGlossChannel || 0); - gBufferMat.set('uvRepeat', uvRepeat); - gBufferMat.set('uvOffset', uvOffset); + else if (symbol === 'normalMap') { + return standardMaterial.get(symbol) || defaultNormalMap; } - else { - program.setUniform( - gl, '1f', 'glossiness', glossiness - ); - - if (previousNormalMap !== normalMap) { - attachTextureToSlot(this, program, 'normalMap', normalMap, 0); - } - if (previousRougGlossMap !== roughGlossMap) { - attachTextureToSlot(this, program, 'roughGlossMap', roughGlossMap, 1); - } - program.setUniform(gl, '1i', 'useRoughGlossMap', +useRoughGlossMap); - program.setUniform(gl, '1i', 'useRoughness', +useRoughnessWorkflow); - program.setUniform(gl, '1i', 'doubleSided', +doubleSided); - program.setUniform(gl, '1i', 'roughGlossChannel', +roughGlossChannel || 0); - if (uvRepeat != null) { - program.setUniform(gl, '2f', 'uvRepeat', uvRepeat); + else if (symbol === 'diffuseMap') { + return standardMaterial.get(symbol) || defaultDiffuseMap; + } + else if (symbol === 'alphaCutoff') { + // TODO DIFFUSEMAP_ALPHA_ALPHA + if (standardMaterial.isDefined('fragment', 'ALPHA_TEST')) { + var alphaCutoff = standardMaterial.get('alphaCutoff'); + return alphaCutoff || 0; } - if (uvOffset != null) { - program.setUniform(gl, '2f', 'uvOffset', uvOffset); + return 0; + } + else { + var useRoughnessWorkflow = standardMaterial.isDefined('fragment', 'USE_ROUGHNESS'); + var roughGlossMap = useRoughnessWorkflow ? standardMaterial.get('roughnessMap') : standardMaterial.get('glossinessMap'); + switch (symbol) { + case 'glossiness': + return useRoughnessWorkflow ? (1.0 - standardMaterial.get('roughness')) : standardMaterial.get('glossiness'); + case 'roughGlossMap': + return roughGlossMap; + case 'useRoughGlossMap': + return !!roughGlossMap; + case 'useRoughness': + return useRoughnessWorkflow; + case 'roughGlossChannel': + return useRoughnessWorkflow + ? standardMaterial.getDefine('fragment', 'ROUGHNESS_CHANNEL') + : standardMaterial.getDefine('fragment', 'GLOSSINESS_CHANNEL'); } } - - previousNormalMap = normalMap; - previousRougGlossMap = roughGlossMap; - - previousRenderable = renderable; }; } -function getBeforeRenderHook2(gl, defaultDiffuseMap, defaultMetalnessMap) { - var previousDiffuseMap; - var previousRenderable; - var previousMetalnessMap; - - return function (renderable, gBufferMat, prevMaterial) { - // Material not change - if (previousRenderable && previousRenderable.material === renderable.material) { - return; - } - - var program = renderable.__program; +function getGetUniformHook2(gl, defaultDiffuseMap, defaultMetalnessMap) { + return function (renderable, gBufferMat, symbol) { var standardMaterial = 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); - // TODO - gBufferMat.set('linear', +standardMaterial.linear || 0); - } - else { - program.setUniform(gl, '1f', 'metalness', metalness); - - program.setUniform(gl, '3f', 'color', color); - if (previousDiffuseMap !== diffuseMap) { - attachTextureToSlot(this, program, 'diffuseMap', diffuseMap, 0); - } - if (previousMetalnessMap !== metalnessMap) { - attachTextureToSlot(this, program, 'metalnessMap', metalnessMap, 1); - } - program.setUniform(gl, '1i', 'useMetalnessMap', +useMetalnessMap); - program.setUniform(gl, '2f', 'uvRepeat', uvRepeat); - program.setUniform(gl, '2f', 'uvOffset', uvOffset); - - program.setUniform(gl, '1i', 'linear', +standardMaterial.linear || 0); + switch (symbol) { + case 'color': + case 'uvRepeat': + case 'uvOffset': + case 'alpha': + return standardMaterial.get(symbol); + case 'metalness': + return standardMaterial.get('metalness') || 0; + case 'diffuseMap': + return standardMaterial.get(symbol) || defaultDiffuseMap; + case 'metalnessMap': + return standardMaterial.get(symbol) || defaultMetalnessMap; + case 'useMetalnessMap': + return !!standardMaterial.get('metalnessMap'); + case 'linear': + return standardMaterial.isDefined('SRGB_DECODE'); + case 'alphaCutoff': + // TODO DIFFUSEMAP_ALPHA_ALPHA + if (standardMaterial.isDefined('fragment', 'ALPHA_TEST')) { + var alphaCutoff = standardMaterial.get('alphaCutoff'); + return alphaCutoff || 0.0; + } + return 0.0; } - - previousDiffuseMap = diffuseMap; - previousMetalnessMap = metalnessMap; - - previousRenderable = renderable; }; } @@ -28720,7 +28886,7 @@ function getBeforeRenderHook2(gl, defaultDiffuseMap, defaultMetalnessMap) { */ var GBuffer = Base.extend(function () { - return { + return /** @lends clay.deferred.GBuffer# */ { enableTargetTexture1: true, @@ -28736,21 +28902,21 @@ var GBuffer = Base.extend(function () { // - B: normal.z // - A: glossiness _gBufferTex1: new Texture2D({ - minFilter: Texture$1.NEAREST, - magFilter: Texture$1.NEAREST, + minFilter: Texture.NEAREST, + magFilter: Texture.NEAREST, // PENDING - type: Texture$1.HALF_FLOAT + type: Texture.HALF_FLOAT }), // - R: depth _gBufferTex2: new Texture2D({ - minFilter: Texture$1.NEAREST, - magFilter: Texture$1.NEAREST, + minFilter: Texture.NEAREST, + magFilter: Texture.NEAREST, // format: Texture.DEPTH_COMPONENT, // type: Texture.UNSIGNED_INT - format: Texture$1.DEPTH_STENCIL, - type: Texture$1.UNSIGNED_INT_24_8_WEBGL + format: Texture.DEPTH_STENCIL, + type: Texture.UNSIGNED_INT_24_8_WEBGL }), // - R: albedo.r @@ -28758,8 +28924,8 @@ var GBuffer = Base.extend(function () { // - B: albedo.b // - A: metalness _gBufferTex3: new Texture2D({ - minFilter: Texture$1.NEAREST, - magFilter: Texture$1.NEAREST + minFilter: Texture.NEAREST, + magFilter: Texture.NEAREST }), _defaultNormalMap: new Texture2D({ @@ -28854,10 +29020,10 @@ var GBuffer = Base.extend(function () { }, /** - * Update G Buffer + * Update GBuffer * @param {clay.Renderer} renderer * @param {clay.Scene} scene - * @param {clay.camera.Perspective} camera + * @param {clay.Camera} camera */ update: function (renderer, scene, camera) { @@ -28866,7 +29032,7 @@ var GBuffer = Base.extend(function () { var frameBuffer = this._frameBuffer; var viewport = frameBuffer.viewport; - var renderList = scene.updateRenderList(camera); + var renderList = scene.updateRenderList(camera, true); var opaqueList = renderList.opaque; var transparentList = renderList.transparent; @@ -28926,7 +29092,10 @@ var GBuffer = Base.extend(function () { getMaterial: function () { return gBufferMaterial1; }, - beforeRender: getBeforeRenderHook1(gl, this._defaultNormalMap, this._defaultRoughnessMap), + getUniform: getGetUniformHook1(gl, this._defaultNormalMap, this._defaultRoughnessMap, this._defaultDiffuseMap), + isMaterialChanged: function (renderable, prevRenderable, material, prevMaterial) { + return renderable.material !== prevRenderable.material; + }, sortCompare: renderer.opaqueSortCompare }; // FIXME Use MRT if possible @@ -28955,7 +29124,10 @@ var GBuffer = Base.extend(function () { getMaterial: function () { return gBufferMaterial2; }, - beforeRender: getBeforeRenderHook2(gl, this._defaultDiffuseMap, this._defaultMetalnessMap), + getUniform: getGetUniformHook2(gl, this._defaultDiffuseMap, this._defaultMetalnessMap), + isMaterialChanged: function (renderable, prevRenderable, material, prevMaterial) { + return !prevRenderable || renderable.material !== prevRenderable.material; + }, sortCompare: renderer.opaqueSortCompare }; renderer.renderPass(gBufferRenderList, camera, passConfig); @@ -28965,6 +29137,20 @@ var GBuffer = Base.extend(function () { frameBuffer.unbind(renderer); }, + /** + * Debug output of gBuffer. Use `type` parameter to choos the debug output type, which can be: + * + * + 'normal' + * + 'depth' + * + 'position' + * + 'glossiness' + * + 'metalness' + * + 'albedo' + * + * @param {clay.Renderer} renderer + * @param {clay.Camera} camera + * @param {string} [type='normal'] + */ renderDebug: function (renderer, camera, type, viewport) { var debugTypes = { normal: 0, @@ -29044,6 +29230,15 @@ var GBuffer = Base.extend(function () { * @param {clay.Renderer} renderer */ dispose: function (renderer) { + this._gBufferTex1.dispose(renderer); + this._gBufferTex2.dispose(renderer); + this._gBufferTex3.dispose(renderer); + + this._defaultNormalMap.dispose(renderer); + this._defaultRoughnessMap.dispose(renderer); + this._defaultMetalnessMap.dispose(renderer); + this._defaultDiffuseMap.dispose(renderer); + this._frameBuffer.dispose(renderer); } }); @@ -29060,9 +29255,7 @@ var vec2$1 = glmatrix.vec2; * @param {number} [opt.capSegments] * @param {number} [opt.heightSegments] */ -var Cone$1 = Geometry.extend( -/** @lends clay.geometry.Cone# */ -{ +var Cone$1 = Geometry.extend(/** @lends clay.geometry.Cone# */ { dynamic: false, /** * @type {number} @@ -29294,7 +29487,7 @@ var DeferredRenderer = Base.extend(function () { var lightAccumulateBlendFunc = function (gl) { gl.blendEquation(gl.FUNC_ADD); - gl.blendFunc(gl.ONE, gl.ONE); + gl.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.ONE); }; var createLightPassMat = function (shader) { @@ -29346,15 +29539,13 @@ var DeferredRenderer = Base.extend(function () { _gBuffer: new GBuffer(), - _lightAccumFrameBuffer: new FrameBuffer({ - depthBuffer: false - }), + _lightAccumFrameBuffer: new FrameBuffer(), _lightAccumTex: new Texture2D({ // FIXME Device not support float texture - type: Texture$1.HALF_FLOAT, - minFilter: Texture$1.NEAREST, - magFilter: Texture$1.NEAREST + type: Texture.HALF_FLOAT, + minFilter: Texture.NEAREST, + magFilter: Texture.NEAREST }), _fullQuadPass: new Pass({ @@ -29414,6 +29605,7 @@ var DeferredRenderer = Base.extend(function () { scene.update(false, true); } scene.updateLights(); + // Render list will be updated in gbuffer. camera.update(true); @@ -29815,14 +30007,8 @@ var DeferredRenderer = Base.extend(function () { return function (renderer, scene, 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); - gl.clear(gl.DEPTH_BUFFER_BIT); - for (var i = 0; i < volumeMeshList.length; i++) { var volumeMesh = volumeMeshList[i]; @@ -29844,8 +30030,8 @@ var DeferredRenderer = Base.extend(function () { // Render light gl.colorMask(true, true, true, true); - gl.depthMask(false); + volumeMesh.material.depthMask = true; renderer.renderPass([volumeMesh], camera); } @@ -32580,7 +32766,7 @@ function unpackID(r, g, b){ * domElement: renderer.canvas * }); * ... - * animation.on('frame', function(frameTime) { + * timeline.on('frame', function(frameTime) { * control.update(frameTime); * renderer.render(scene, camera); * }); @@ -32626,7 +32812,7 @@ var FreeControl = Base.extend(function() { /** * @type {clay.Timeline} */ - animation: null, + timeline: null, _moveForward: false, _moveBackward: false, @@ -32643,15 +32829,15 @@ var FreeControl = Base.extend(function() { this._mouseMove = this._mouseMove.bind(this); if (this.domElement) { - this.enable(); + this.init(); } }, /** @lends clay.plugin.FreeControl.prototype */ { /** - * Enable control + * init control */ - enable: function() { + init: function() { // Use pointer lock // http://www.html5rocks.com/en/tutorials/pointerlock/intro/ var el = this.domElement; @@ -32667,17 +32853,15 @@ var FreeControl = Base.extend(function() { document.addEventListener('keydown', this._keyDown); document.addEventListener('keyup', this._keyUp); - if (this.animation) { - this.animation.on('frame', this._detectMovementChange, this); + if (this.timeline) { + this.timeline.on('frame', this._detectMovementChange, this); } }, /** - * Disable control + * Dispose control */ - disable: function() { - - this.target.off('beforeupdate', this._beforeUpdateCamera); + dispose: function() { var el = this.domElement; @@ -32698,8 +32882,8 @@ var FreeControl = Base.extend(function() { document.removeEventListener('keydown', this._keyDown); document.removeEventListener('keyup', this._keyUp); - if (this.animation) { - this.animation.off('frame', this._detectMovementChange); + if (this.timeline) { + this.timeline.off('frame', this._detectMovementChange); } }, @@ -32716,7 +32900,7 @@ var FreeControl = Base.extend(function() { * Control update. Should be invoked every frame * @param {number} frameTime Frame time */ - update: function(frameTime) { + update: function (frameTime) { var target = this.target; var position = this.target.position; @@ -32758,7 +32942,8 @@ var FreeControl = Base.extend(function() { || document.webkitPointerLockElement === this.domElement ) { document.addEventListener('mousemove', this._mouseMove, false); - } else { + } + else { document.removeEventListener('mousemove', this._mouseMove); } }, @@ -34237,7 +34422,7 @@ function copyIfNecessary(arr, shallow) { /** * @name clay.version */ -var version = '1.1.0'; +var version = '1.1.1'; var outputEssl$1 = "@export clay.vr.disorter.output.vertex\nattribute vec2 texcoord: TEXCOORD_0;\nattribute vec3 position: POSITION;\nvarying vec2 v_Texcoord;\nvoid main()\n{\n v_Texcoord = texcoord;\n gl_Position = vec4(position.xy, 0.5, 1.0);\n}\n@end\n@export clay.vr.disorter.output.fragment\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\nvoid main()\n{\n gl_FragColor = texture2D(texture, v_Texcoord);\n}\n@end"; @@ -34686,4 +34871,4 @@ var vr = { StereoCamera : StereoCamera }; -export { animation, application, async, Camera, camera, compositor, core, createCompositor, deferred, dep, FrameBuffer, Geometry, geometry, Joint, Light, light, loader, Material, math, BoundingBox, Frustum, Matrix2, Matrix2d, Matrix3, Matrix4, Plane$1 as Plane, Quaternion, Ray, Value, Vector2, Vector3, Vector4, Mesh, Node, particle, picking, plugin, prePass, Renderable, Renderer, Scene, Shader, shader, Skeleton, StandardMaterial, StaticGeometry, Texture$1 as Texture, Texture2D, TextureCube, Timeline, util, version, vr }; +export { animation, application, async, Camera, camera, compositor, core, createCompositor, deferred, dep, FrameBuffer, Geometry, geometry, Joint, Light, light, loader, Material, math, BoundingBox, Frustum, Matrix2, Matrix2d, Matrix3, Matrix4, Plane$1 as Plane, Quaternion, Ray, Value, Vector2, Vector3, Vector4, Mesh, Node, particle, picking, plugin, prePass, Renderable, Renderer, Scene, Shader, shader, Skeleton, StandardMaterial, StaticGeometry, Texture, Texture2D, TextureCube, Timeline, util, version, vr }; diff --git a/dist/claygl.js b/dist/claygl.js index af54ad59c..bcbd0a11b 100644 --- a/dist/claygl.js +++ b/dist/claygl.js @@ -9649,39 +9649,87 @@ Vector3.eulerFromMat3 = function (out, m, order) { return out; }; -// TODO return new. -/** - * @type {clay.Vector3} - */ -Vector3.POSITIVE_X = new Vector3(1, 0, 0); -/** - * @type {clay.Vector3} - */ -Vector3.NEGATIVE_X = new Vector3(-1, 0, 0); -/** - * @type {clay.Vector3} - */ -Vector3.POSITIVE_Y = new Vector3(0, 1, 0); -/** - * @type {clay.Vector3} - */ -Vector3.NEGATIVE_Y = new Vector3(0, -1, 0); -/** - * @type {clay.Vector3} - */ -Vector3.POSITIVE_Z = new Vector3(0, 0, 1); -/** - * @type {clay.Vector3} - */ -Vector3.NEGATIVE_Z = new Vector3(0, 0, -1); -/** - * @type {clay.Vector3} - */ -Vector3.UP = new Vector3(0, 1, 0); -/** - * @type {clay.Vector3} - */ -Vector3.ZERO = new Vector3(0, 0, 0); +Object.defineProperties(Vector3, { + /** + * @type {clay.Vector3} + * @readOnly + * @memberOf clay.Vector3 + */ + POSITIVE_X: { + get: function () { + return new Vector3(1, 0, 0); + } + }, + /** + * @type {clay.Vector3} + * @readOnly + * @memberOf clay.Vector3 + */ + NEGATIVE_X: { + get: function () { + return new Vector3(-1, 0, 0); + } + }, + /** + * @type {clay.Vector3} + * @readOnly + * @memberOf clay.Vector3 + */ + POSITIVE_Y: { + get: function () { + return new Vector3(0, 1, 0); + } + }, + /** + * @type {clay.Vector3} + * @readOnly + * @memberOf clay.Vector3 + */ + NEGATIVE_Y: { + get: function () { + return new Vector3(0, -1, 0); + } + }, + /** + * @type {clay.Vector3} + * @readOnly + * @memberOf clay.Vector3 + */ + POSITIVE_Z: { + get: function () { + return new Vector3(0, 0, 1); + } + }, + /** + * @type {clay.Vector3} + * @readOnly + */ + NEGATIVE_Z: { + get: function () { + return new Vector3(0, 0, -1); + } + }, + /** + * @type {clay.Vector3} + * @readOnly + * @memberOf clay.Vector3 + */ + UP: { + get: function () { + return new Vector3(0, 1, 0); + } + }, + /** + * @type {clay.Vector3} + * @readOnly + * @memberOf clay.Vector3 + */ + ZERO: { + get: function () { + return new Vector3(); + } + } +}); var vec3$3 = glmatrix.vec3; @@ -10708,1891 +10756,2164 @@ Matrix4.translate = function(out, a, v) { return out; }; -var DIRTY_PREFIX = '__dt__'; - -var Cache = function () { +/** + * Simple double linked list. Compared with array, it has O(1) remove operation. + * @constructor + * @alias clay.core.LinkedList + */ +var LinkedList = function () { - this._contextId = 0; + /** + * @type {clay.core.LinkedList.Entry} + */ + this.head = null; - this._caches = []; + /** + * @type {clay.core.LinkedList.Entry} + */ + this.tail = null; - this._context = {}; + this._length = 0; }; -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); - }, +/** + * Insert a new value at the tail + * @param {} val + * @return {clay.core.LinkedList.Entry} + */ +LinkedList.prototype.insert = function (val) { + var entry = new LinkedList.Entry(val); + this.insertEntry(entry); + return entry; +}; - 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; - } +/** + * Insert a new value at idx + * @param {number} idx + * @param {} val + * @return {clay.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; } - }, - - 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; - } + else { + prev.next = entry; + entry.prev = prev; } - }, - - 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); + entry.next = next; + next.prev = entry; + } + else { + this.insert(val); } }; -Cache.prototype.constructor = Cache; +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++; +}; /** - * Base class for all textures like compressed texture, texture2d, texturecube - * TODO mapping - */ -/** - * @constructor - * @alias clay.Texture - * @extends clay.core.Base + * Insert an entry at the tail + * @param {clay.core.LinkedList.Entry} entry */ -var Texture$1 = Base.extend( /** @lends clay.Texture# */ { - /** - * Texture width, readonly when the texture source is image - * @type {number} - */ - width: 512, - /** - * Texture height, readonly when the texture source is image - * @type {number} - */ - height: 512, - /** - * Texel data type. - * Possible values: - * + {@link clay.Texture.UNSIGNED_BYTE} - * + {@link clay.Texture.HALF_FLOAT} - * + {@link clay.Texture.FLOAT} - * + {@link clay.Texture.UNSIGNED_INT_24_8_WEBGL} - * + {@link clay.Texture.UNSIGNED_INT} - * @type {number} - */ - type: glenum.UNSIGNED_BYTE, - /** - * Format of texel data - * Possible values: - * + {@link clay.Texture.RGBA} - * + {@link clay.Texture.DEPTH_COMPONENT} - * + {@link clay.Texture.DEPTH_STENCIL} - * @type {number} - */ - format: glenum.RGBA, - /** - * Texture wrap. Default to be REPEAT. - * Possible values: - * + {@link clay.Texture.CLAMP_TO_EDGE} - * + {@link clay.Texture.REPEAT} - * + {@link clay.Texture.MIRRORED_REPEAT} - * @type {number} - */ - wrapS: glenum.REPEAT, - /** - * Texture wrap. Default to be REPEAT. - * Possible values: - * + {@link clay.Texture.CLAMP_TO_EDGE} - * + {@link clay.Texture.REPEAT} - * + {@link clay.Texture.MIRRORED_REPEAT} - * @type {number} - */ - wrapT: glenum.REPEAT, +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 {clay.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) { /** - * Possible values: - * + {@link clay.Texture.NEAREST} - * + {@link clay.Texture.LINEAR} - * + {@link clay.Texture.NEAREST_MIPMAP_NEAREST} - * + {@link clay.Texture.LINEAR_MIPMAP_NEAREST} - * + {@link clay.Texture.NEAREST_MIPMAP_LINEAR} - * + {@link clay.Texture.LINEAR_MIPMAP_LINEAR} - * @type {number} + * @type {} */ - minFilter: glenum.LINEAR_MIPMAP_LINEAR, + this.value = val; + /** - * Possible values: - * + {@link clay.Texture.NEAREST} - * + {@link clay.Texture.LINEAR} - * @type {number} + * @type {clay.core.LinkedList.Entry} */ - magFilter: glenum.LINEAR, + this.next = null; + /** - * If enable mimap. - * @type {boolean} + * @type {clay.core.LinkedList.Entry} */ - useMipmap: true, + this.prev = null; +}; + +/** + * LRU Cache + * @constructor + * @alias clay.core.LRU + */ +var LRU$1 = function (maxSize) { + + this._list = new LinkedList(); + + this._map = {}; + + this._maxSize = maxSize || 10; +}; + +/** + * Set cache max size + * @param {number} size + */ +LRU$1.prototype.setMaxSize = function (size) { + this._maxSize = size; +}; + +/** + * @param {string} key + * @param {} value + */ +LRU$1.prototype.put = function (key, value) { + if (!this._map.hasOwnProperty(key)) { + 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$1.prototype.get = function (key) { + var entry = this._map[key]; + if (this._map.hasOwnProperty(key)) { + // 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$1.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$1.prototype.clear = function () { + this._list.clear(); + this._map = {}; +}; + +/** + * @namespace clay.core.color + */ +var colorUtil = {}; + +var kCSSColorTable = { + 'transparent': [0,0,0,0], 'aliceblue': [240,248,255,1], + 'antiquewhite': [250,235,215,1], 'aqua': [0,255,255,1], + 'aquamarine': [127,255,212,1], 'azure': [240,255,255,1], + 'beige': [245,245,220,1], 'bisque': [255,228,196,1], + 'black': [0,0,0,1], 'blanchedalmond': [255,235,205,1], + 'blue': [0,0,255,1], 'blueviolet': [138,43,226,1], + 'brown': [165,42,42,1], 'burlywood': [222,184,135,1], + 'cadetblue': [95,158,160,1], 'chartreuse': [127,255,0,1], + 'chocolate': [210,105,30,1], 'coral': [255,127,80,1], + 'cornflowerblue': [100,149,237,1], 'cornsilk': [255,248,220,1], + 'crimson': [220,20,60,1], 'cyan': [0,255,255,1], + 'darkblue': [0,0,139,1], 'darkcyan': [0,139,139,1], + 'darkgoldenrod': [184,134,11,1], 'darkgray': [169,169,169,1], + 'darkgreen': [0,100,0,1], 'darkgrey': [169,169,169,1], + 'darkkhaki': [189,183,107,1], 'darkmagenta': [139,0,139,1], + 'darkolivegreen': [85,107,47,1], 'darkorange': [255,140,0,1], + 'darkorchid': [153,50,204,1], 'darkred': [139,0,0,1], + 'darksalmon': [233,150,122,1], 'darkseagreen': [143,188,143,1], + 'darkslateblue': [72,61,139,1], 'darkslategray': [47,79,79,1], + 'darkslategrey': [47,79,79,1], 'darkturquoise': [0,206,209,1], + 'darkviolet': [148,0,211,1], 'deeppink': [255,20,147,1], + 'deepskyblue': [0,191,255,1], 'dimgray': [105,105,105,1], + 'dimgrey': [105,105,105,1], 'dodgerblue': [30,144,255,1], + 'firebrick': [178,34,34,1], 'floralwhite': [255,250,240,1], + 'forestgreen': [34,139,34,1], 'fuchsia': [255,0,255,1], + 'gainsboro': [220,220,220,1], 'ghostwhite': [248,248,255,1], + 'gold': [255,215,0,1], 'goldenrod': [218,165,32,1], + 'gray': [128,128,128,1], 'green': [0,128,0,1], + 'greenyellow': [173,255,47,1], 'grey': [128,128,128,1], + 'honeydew': [240,255,240,1], 'hotpink': [255,105,180,1], + 'indianred': [205,92,92,1], 'indigo': [75,0,130,1], + 'ivory': [255,255,240,1], 'khaki': [240,230,140,1], + 'lavender': [230,230,250,1], 'lavenderblush': [255,240,245,1], + 'lawngreen': [124,252,0,1], 'lemonchiffon': [255,250,205,1], + 'lightblue': [173,216,230,1], 'lightcoral': [240,128,128,1], + 'lightcyan': [224,255,255,1], 'lightgoldenrodyellow': [250,250,210,1], + 'lightgray': [211,211,211,1], 'lightgreen': [144,238,144,1], + 'lightgrey': [211,211,211,1], 'lightpink': [255,182,193,1], + 'lightsalmon': [255,160,122,1], 'lightseagreen': [32,178,170,1], + 'lightskyblue': [135,206,250,1], 'lightslategray': [119,136,153,1], + 'lightslategrey': [119,136,153,1], 'lightsteelblue': [176,196,222,1], + 'lightyellow': [255,255,224,1], 'lime': [0,255,0,1], + 'limegreen': [50,205,50,1], 'linen': [250,240,230,1], + 'magenta': [255,0,255,1], 'maroon': [128,0,0,1], + 'mediumaquamarine': [102,205,170,1], 'mediumblue': [0,0,205,1], + 'mediumorchid': [186,85,211,1], 'mediumpurple': [147,112,219,1], + 'mediumseagreen': [60,179,113,1], 'mediumslateblue': [123,104,238,1], + 'mediumspringgreen': [0,250,154,1], 'mediumturquoise': [72,209,204,1], + 'mediumvioletred': [199,21,133,1], 'midnightblue': [25,25,112,1], + 'mintcream': [245,255,250,1], 'mistyrose': [255,228,225,1], + 'moccasin': [255,228,181,1], 'navajowhite': [255,222,173,1], + 'navy': [0,0,128,1], 'oldlace': [253,245,230,1], + 'olive': [128,128,0,1], 'olivedrab': [107,142,35,1], + 'orange': [255,165,0,1], 'orangered': [255,69,0,1], + 'orchid': [218,112,214,1], 'palegoldenrod': [238,232,170,1], + 'palegreen': [152,251,152,1], 'paleturquoise': [175,238,238,1], + 'palevioletred': [219,112,147,1], 'papayawhip': [255,239,213,1], + 'peachpuff': [255,218,185,1], 'peru': [205,133,63,1], + 'pink': [255,192,203,1], 'plum': [221,160,221,1], + 'powderblue': [176,224,230,1], 'purple': [128,0,128,1], + 'red': [255,0,0,1], 'rosybrown': [188,143,143,1], + 'royalblue': [65,105,225,1], 'saddlebrown': [139,69,19,1], + 'salmon': [250,128,114,1], 'sandybrown': [244,164,96,1], + 'seagreen': [46,139,87,1], 'seashell': [255,245,238,1], + 'sienna': [160,82,45,1], 'silver': [192,192,192,1], + 'skyblue': [135,206,235,1], 'slateblue': [106,90,205,1], + 'slategray': [112,128,144,1], 'slategrey': [112,128,144,1], + 'snow': [255,250,250,1], 'springgreen': [0,255,127,1], + 'steelblue': [70,130,180,1], 'tan': [210,180,140,1], + 'teal': [0,128,128,1], 'thistle': [216,191,216,1], + 'tomato': [255,99,71,1], 'turquoise': [64,224,208,1], + 'violet': [238,130,238,1], 'wheat': [245,222,179,1], + 'white': [255,255,255,1], 'whitesmoke': [245,245,245,1], + 'yellow': [255,255,0,1], 'yellowgreen': [154,205,50,1] +}; + +function clampCssByte(i) { // Clamp to integer 0 .. 255. + i = Math.round(i); // Seems to be what Chrome does (vs truncation). + return i < 0 ? 0 : i > 255 ? 255 : i; +} + +function clampCssAngle(i) { // Clamp to integer 0 .. 360. + i = Math.round(i); // Seems to be what Chrome does (vs truncation). + return i < 0 ? 0 : i > 360 ? 360 : i; +} + +function clampCssFloat(f) { // Clamp to float 0.0 .. 1.0. + return f < 0 ? 0 : f > 1 ? 1 : f; +} + +function parseCssInt(str) { // int or percentage. + if (str.length && str.charAt(str.length - 1) === '%') { + return clampCssByte(parseFloat(str) / 100 * 255); + } + return clampCssByte(parseInt(str, 10)); +} + +function parseCssFloat(str) { // float or percentage. + if (str.length && str.charAt(str.length - 1) === '%') { + return clampCssFloat(parseFloat(str) / 100); + } + return clampCssFloat(parseFloat(str)); +} + +function cssHueToRgb(m1, m2, h) { + if (h < 0) { + h += 1; + } + else if (h > 1) { + h -= 1; + } + + if (h * 6 < 1) { + return m1 + (m2 - m1) * h * 6; + } + if (h * 2 < 1) { + return m2; + } + if (h * 3 < 2) { + return m1 + (m2 - m1) * (2/3 - h) * 6; + } + return m1; +} + +function lerpNumber(a, b, p) { + return a + (b - a) * p; +} + +function setRgba(out, r, g, b, a) { + out[0] = r; out[1] = g; out[2] = b; out[3] = a; + return out; +} +function copyRgba(out, a) { + out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3]; + return out; +} + +var colorCache = new LRU$1(20); +var lastRemovedArr = null; + +function putToCache(colorStr, rgbaArr) { + // Reuse removed array + if (lastRemovedArr) { + copyRgba(lastRemovedArr, rgbaArr); + } + lastRemovedArr = colorCache.put(colorStr, lastRemovedArr || (rgbaArr.slice())); +} + +/** + * @name clay.core.color.parse + * @param {string} colorStr + * @param {Array.} out + * @return {Array.} + */ +colorUtil.parse = function (colorStr, rgbaArr) { + if (!colorStr) { + return; + } + rgbaArr = rgbaArr || []; + + var cached = colorCache.get(colorStr); + if (cached) { + return copyRgba(rgbaArr, cached); + } + + // colorStr may be not string + colorStr = colorStr + ''; + // Remove all whitespace, not compliant, but should just be more accepting. + var str = colorStr.replace(/ /g, '').toLowerCase(); + + // Color keywords (and transparent) lookup. + if (str in kCSSColorTable) { + copyRgba(rgbaArr, kCSSColorTable[str]); + putToCache(colorStr, rgbaArr); + return rgbaArr; + } + + // #abc and #abc123 syntax. + if (str.charAt(0) === '#') { + if (str.length === 4) { + var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing. + if (!(iv >= 0 && iv <= 0xfff)) { + setRgba(rgbaArr, 0, 0, 0, 1); + return; // Covers NaN. + } + setRgba(rgbaArr, + ((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8), + (iv & 0xf0) | ((iv & 0xf0) >> 4), + (iv & 0xf) | ((iv & 0xf) << 4), + 1 + ); + putToCache(colorStr, rgbaArr); + return rgbaArr; + } + else if (str.length === 7) { + var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing. + if (!(iv >= 0 && iv <= 0xffffff)) { + setRgba(rgbaArr, 0, 0, 0, 1); + return; // Covers NaN. + } + setRgba(rgbaArr, + (iv & 0xff0000) >> 16, + (iv & 0xff00) >> 8, + iv & 0xff, + 1 + ); + putToCache(colorStr, rgbaArr); + return rgbaArr; + } + + return; + } + var op = str.indexOf('('), ep = str.indexOf(')'); + if (op !== -1 && ep + 1 === str.length) { + var fname = str.substr(0, op); + var params = str.substr(op + 1, ep - (op + 1)).split(','); + var alpha = 1; // To allow case fallthrough. + switch (fname) { + case 'rgba': + if (params.length !== 4) { + setRgba(rgbaArr, 0, 0, 0, 1); + return; + } + alpha = parseCssFloat(params.pop()); // jshint ignore:line + // Fall through. + case 'rgb': + if (params.length !== 3) { + setRgba(rgbaArr, 0, 0, 0, 1); + return; + } + setRgba(rgbaArr, + parseCssInt(params[0]), + parseCssInt(params[1]), + parseCssInt(params[2]), + alpha + ); + putToCache(colorStr, rgbaArr); + return rgbaArr; + case 'hsla': + if (params.length !== 4) { + setRgba(rgbaArr, 0, 0, 0, 1); + return; + } + params[3] = parseCssFloat(params[3]); + hsla2rgba(params, rgbaArr); + putToCache(colorStr, rgbaArr); + return rgbaArr; + case 'hsl': + if (params.length !== 3) { + setRgba(rgbaArr, 0, 0, 0, 1); + return; + } + hsla2rgba(params, rgbaArr); + putToCache(colorStr, rgbaArr); + return rgbaArr; + default: + return; + } + } + + setRgba(rgbaArr, 0, 0, 0, 1); + return; +}; + +colorUtil.parseToFloat = function (colorStr, rgbaArr) { + rgbaArr = colorUtil.parse(colorStr, rgbaArr); + if (!rgbaArr) { + return; + } + rgbaArr[0] /= 255; + rgbaArr[1] /= 255; + rgbaArr[2] /= 255; + return rgbaArr; +}; + +/** + * @name clay.core.color.hsla2rgba + * @param {Array.} hsla + * @param {Array.} rgba + * @return {Array.} rgba + */ +function hsla2rgba(hsla, rgba) { + var h = (((parseFloat(hsla[0]) % 360) + 360) % 360) / 360; // 0 .. 1 + // NOTE(deanm): According to the CSS spec s/l should only be + // percentages, but we don't bother and let float or percentage. + var s = parseCssFloat(hsla[1]); + var l = parseCssFloat(hsla[2]); + var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; + var m1 = l * 2 - m2; + + rgba = rgba || []; + setRgba(rgba, + clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255), + clampCssByte(cssHueToRgb(m1, m2, h) * 255), + clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255), + 1 + ); + + if (hsla.length === 4) { + rgba[3] = hsla[3]; + } + + return rgba; +} + +/** + * @name clay.core.color.rgba2hsla + * @param {Array.} rgba + * @return {Array.} hsla + */ +function rgba2hsla(rgba) { + if (!rgba) { + return; + } + + // RGB from 0 to 255 + var R = rgba[0] / 255; + var G = rgba[1] / 255; + var B = rgba[2] / 255; + + var vMin = Math.min(R, G, B); // Min. value of RGB + var vMax = Math.max(R, G, B); // Max. value of RGB + var delta = vMax - vMin; // Delta RGB value + + var L = (vMax + vMin) / 2; + var H; + var S; + // HSL results from 0 to 1 + if (delta === 0) { + H = 0; + S = 0; + } + else { + if (L < 0.5) { + S = delta / (vMax + vMin); + } + else { + S = delta / (2 - vMax - vMin); + } + + var deltaR = (((vMax - R) / 6) + (delta / 2)) / delta; + var deltaG = (((vMax - G) / 6) + (delta / 2)) / delta; + var deltaB = (((vMax - B) / 6) + (delta / 2)) / delta; + + if (R === vMax) { + H = deltaB - deltaG; + } + else if (G === vMax) { + H = (1 / 3) + deltaR - deltaB; + } + else if (B === vMax) { + H = (2 / 3) + deltaG - deltaR; + } + + if (H < 0) { + H += 1; + } + + if (H > 1) { + H -= 1; + } + } + + var hsla = [H * 360, S, L]; + + if (rgba[3] != null) { + hsla.push(rgba[3]); + } + + return hsla; +} + +/** + * @name clay.core.color.lift + * @param {string} color + * @param {number} level + * @return {string} + */ +colorUtil.lift = function (color, level) { + var colorArr = colorUtil.parse(color); + if (colorArr) { + for (var i = 0; i < 3; i++) { + if (level < 0) { + colorArr[i] = colorArr[i] * (1 - level) | 0; + } + else { + colorArr[i] = ((255 - colorArr[i]) * level + colorArr[i]) | 0; + } + } + return colorUtil.stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb'); + } +}; + +/** + * @name clay.core.color.toHex + * @param {string} color + * @return {string} + */ +colorUtil.toHex = function (color) { + var colorArr = colorUtil.parse(color); + if (colorArr) { + return ((1 << 24) + (colorArr[0] << 16) + (colorArr[1] << 8) + (+colorArr[2])).toString(16).slice(1); + } +}; + +/** + * Map value to color. Faster than lerp methods because color is represented by rgba array. + * @name clay.core.color + * @param {number} normalizedValue A float between 0 and 1. + * @param {Array.>} colors List of rgba color array + * @param {Array.} [out] Mapped gba color array + * @return {Array.} will be null/undefined if input illegal. + */ +colorUtil.fastLerp = function (normalizedValue, colors, out) { + if (!(colors && colors.length) + || !(normalizedValue >= 0 && normalizedValue <= 1) + ) { + return; + } + + out = out || []; + + var value = normalizedValue * (colors.length - 1); + var leftIndex = Math.floor(value); + var rightIndex = Math.ceil(value); + var leftColor = colors[leftIndex]; + var rightColor = colors[rightIndex]; + var dv = value - leftIndex; + out[0] = clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)); + out[1] = clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)); + out[2] = clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)); + out[3] = clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv)); + + return out; +}; + +colorUtil.fastMapToColor = colorUtil.fastLerp; + +/** + * @param {number} normalizedValue A float between 0 and 1. + * @param {Array.} colors Color list. + * @param {boolean=} fullOutput Default false. + * @return {(string|Object)} Result color. If fullOutput, + * return {color: ..., leftIndex: ..., rightIndex: ..., value: ...}, + */ +colorUtil.lerp = function (normalizedValue, colors, fullOutput) { + if (!(colors && colors.length) + || !(normalizedValue >= 0 && normalizedValue <= 1) + ) { + return; + } + + var value = normalizedValue * (colors.length - 1); + var leftIndex = Math.floor(value); + var rightIndex = Math.ceil(value); + var leftColor = colorUtil.parse(colors[leftIndex]); + var rightColor = colorUtil.parse(colors[rightIndex]); + var dv = value - leftIndex; + + var color = colorUtil.stringify( + [ + clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)), + clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)), + clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)), + clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv)) + ], + 'rgba' + ); + + return fullOutput + ? { + color: color, + leftIndex: leftIndex, + rightIndex: rightIndex, + value: value + } + : color; +}; + +/** + * @deprecated + */ +colorUtil.mapToColor = colorUtil.lerp; + +/** + * @name clay.core.color + * @param {string} color + * @param {number=} h 0 ~ 360, ignore when null. + * @param {number=} s 0 ~ 1, ignore when null. + * @param {number=} l 0 ~ 1, ignore when null. + * @return {string} Color string in rgba format. + */ +colorUtil.modifyHSL = function (color, h, s, l) { + color = colorUtil.parse(color); - /** - * Anisotropic filtering, enabled if value is larger than 1 - * @see https://developer.mozilla.org/en-US/docs/Web/API/EXT_texture_filter_anisotropic - * @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 - /** - * If flip in y axis for given image source - * @type {boolean} - * @default true - */ - flipY: true, + if (color) { + color = rgba2hsla(color); + h != null && (color[0] = clampCssAngle(h)); + s != null && (color[1] = parseCssFloat(s)); + l != null && (color[2] = parseCssFloat(l)); - /** - * A flag to indicate if texture source is sRGB - */ - sRGB: true, - /** - * @type {number} - * @default 4 - */ - unpackAlignment: 4, - /** - * @type {boolean} - * @default false - */ - premultiplyAlpha: false, + return colorUtil.stringify(hsla2rgba(color), 'rgba'); + } +}; - /** - * Dynamic option for texture like video - * @type {boolean} - */ - dynamic: false, +/** + * @param {string} color + * @param {number=} alpha 0 ~ 1 + * @return {string} Color string in rgba format. + */ +colorUtil.modifyAlpha = function (color, alpha) { + color = colorUtil.parse(color); - NPOT: false, + if (color && alpha != null) { + color[3] = clampCssFloat(alpha); + return colorUtil.stringify(color, 'rgba'); + } +}; - // PENDING - // Init it here to avoid deoptimization when it's assigned in application dynamically - __used: 0 +/** + * @param {Array.} arrColor like [12,33,44,0.4] + * @param {string} type 'rgba', 'hsva', ... + * @return {string} Result color. (If input illegal, return undefined). + */ +colorUtil.stringify = function (arrColor, type) { + if (!arrColor || !arrColor.length) { + return; + } + var colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2]; + if (type === 'rgba' || type === 'hsva' || type === 'hsla') { + colorStr += ',' + arrColor[3]; + } + return type + '(' + colorStr + ')'; +}; -}, function () { - this._cache = new Cache(); -}, -/** @lends clay.Texture.prototype */ -{ +var parseColor$1 = colorUtil.parseToFloat; - getWebGLTexture: function (renderer) { - var _gl = renderer.gl; - var cache = this._cache; - cache.use(renderer.__uid__); +var programKeyCache = {}; - 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(renderer); +function getDefineCode(defines) { + var defineKeys = Object.keys(defines); + defineKeys.sort(); + var defineStr = []; + // Custom Defines + for (var i = 0; i < defineKeys.length; i++) { + var key = defineKeys[i]; + var value = defines[key]; + if (value === null) { + defineStr.push(key); } - else if (cache.isDirty()) { - this.update(renderer); - cache.fresh(); + else{ + defineStr.push(key + ' ' + value.toString()); } + } + return defineStr.join('\n'); +} - return cache.get('webgl_texture'); - }, +function getProgramKey(vertexDefines, fragmentDefines, enabledTextures) { + enabledTextures.sort(); + var defineStr = []; + for (var i = 0; i < enabledTextures.length; i++) { + var symbol = enabledTextures[i]; + defineStr.push(symbol); + } + var key = getDefineCode(vertexDefines) + '\n' + + getDefineCode(fragmentDefines) + '\n' + + defineStr.join('\n'); - bind: function () {}, - unbind: function () {}, + if (programKeyCache[key]) { + return programKeyCache[key]; + } - /** - * Mark texture is dirty and update in the next frame - */ - dirty: function () { - if (this._cache) { - this._cache.dirtyAll(); - } - }, + var id = util$1.genGUID(); + programKeyCache[key] = id; + return id; +} - update: function (renderer) {}, +/** + * Material defines the appearance of mesh surface, like `color`, `roughness`, `metalness`, etc. + * It contains a {@link clay.Shader} and corresponding uniforms. + * + * Here is a basic example to create a standard material +```js +var material = new clay.Material({ + shader: new clay.Shader( + clay.Shader.source('clay.vertex'), + clay.Shader.source('clay.fragment') + ) +}); +``` + * @constructor clay.Material + * @extends clay.core.Base + */ +var Material = Base.extend(function () { + return /** @lends clay.Material# */ { + /** + * @type {string} + */ + name: '', - // Update the common parameters of texture - updateCommon: function (renderer) { - var _gl = renderer.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); + /** + * @type {Object} + */ + // uniforms: null, - // Use of none-power of two texture - // http://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences - if (this.format === glenum.DEPTH_COMPONENT) { - this.useMipmap = false; + /** + * @type {clay.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, + + /** + * If update texture status automatically. + */ + autoUpdateTextureStatus: true, + + uniforms: {}, + vertexDefines: {}, + fragmentDefines: {}, + _textureStatus: {}, + + // shadowTransparentMap : null + + // PENDING enable the uniform that only used in shader. + _enabledUniforms: null, + }; +}, function () { + if (!this.name) { + this.name = 'MATERIAL_' + this.__uid__; + } + + if (this.shader) { + // Keep status, mainly preset uniforms, vertexDefines and fragmentDefines + this.attachShader(this.shader, true); + } +}, +/** @lends clay.Material.prototype */ +{ + precision: 'highp', + + /** + * Set material uniform + * @example + * mat.setUniform('color', [1, 1, 1, 1]); + * @param {string} symbol + * @param {number|array|clay.Texture|ArrayBufferView} value + */ + setUniform: function (symbol, value) { + if (value === undefined) { + console.warn('Uniform value "' + symbol + '" is undefined'); } + var uniform = this.uniforms[symbol]; + if (uniform) { - var sRGBExt = renderer.getGLExtension('EXT_sRGB'); - // Fallback - if (this.format === Texture$1.SRGB && !sRGBExt) { - this.format = Texture$1.RGB; - } - if (this.format === Texture$1.SRGB_ALPHA && !sRGBExt) { - this.format = Texture$1.RGBA; - } + if (typeof value === 'string') { + // Try to parse as a color. Invalid color string will return null. + value = parseColor$1(value) || value; + } - this.NPOT = !this.isPowerOfTwo(); - }, + uniform.value = value; - getAvailableWrapS: function () { - if (this.NPOT) { - return glenum.CLAMP_TO_EDGE; - } - return this.wrapS; - }, - getAvailableWrapT: function () { - if (this.NPOT) { - return glenum.CLAMP_TO_EDGE; - } - return this.wrapT; - }, - getAvailableMinFilter: function () { - var minFilter = this.minFilter; - if (this.NPOT || !this.useMipmap) { - if (minFilter === glenum.NEAREST_MIPMAP_NEAREST || - minFilter === glenum.NEAREST_MIPMAP_LINEAR - ) { - return glenum.NEAREST; - } - else if (minFilter === glenum.LINEAR_MIPMAP_LINEAR || - minFilter === glenum.LINEAR_MIPMAP_NEAREST - ) { - return glenum.LINEAR; - } - else { - return minFilter; + if (this.autoUpdateTextureStatus && uniform.type === 't') { + if (value) { + this.enableTexture(symbol); + } + else { + this.disableTexture(symbol); + } } } - else { - return minFilter; - } - }, - getAvailableMagFilter: function () { - return this.magFilter; }, - nextHighestPowerOfTwo: function (x) { - --x; - for (var i = 1; i < 32; i <<= 1) { - x = x | x >> i; - } - return x + 1; - }, /** - * @param {clay.Renderer} renderer + * @param {Object} obj */ - dispose: function (renderer) { - - var cache = this._cache; - - cache.use(renderer.__uid__); - - var webglTexture = cache.get('webgl_texture'); - if (webglTexture){ - renderer.gl.deleteTexture(webglTexture); + setUniforms: function(obj) { + for (var key in obj) { + var val = obj[key]; + this.setUniform(key, val); } - cache.deleteContext(renderer.__uid__); - }, - /** - * Test if image of texture is valid and loaded. - * @return {boolean} - */ - isRenderable: function () {}, /** - * Test if texture size is power of two + * @param {string} symbol * @return {boolean} */ - isPowerOfTwo: function () {} -}); + isUniformEnabled: function (symbol) { + return this._enabledUniforms.indexOf(symbol) >= 0; + }, -Object.defineProperty(Texture$1.prototype, 'width', { - get: function () { - return this._width; + getEnabledUniforms: function () { + return this._enabledUniforms; }, - set: function (value) { - this._width = value; - } -}); -Object.defineProperty(Texture$1.prototype, 'height', { - get: function () { - return this._height; + getTextureUniforms: function () { + return this._textureUniforms; }, - set: function (value) { - this._height = value; - } -}); - -/* DataType */ - -/** - * @type {number} - */ -Texture$1.BYTE = glenum.BYTE; -/** - * @type {number} - */ -Texture$1.UNSIGNED_BYTE = glenum.UNSIGNED_BYTE; -/** - * @type {number} - */ -Texture$1.SHORT = glenum.SHORT; -/** - * @type {number} - */ -Texture$1.UNSIGNED_SHORT = glenum.UNSIGNED_SHORT; -/** - * @type {number} - */ -Texture$1.INT = glenum.INT; -/** - * @type {number} - */ -Texture$1.UNSIGNED_INT = glenum.UNSIGNED_INT; -/** - * @type {number} - */ -Texture$1.FLOAT = glenum.FLOAT; -/** - * @type {number} - */ -Texture$1.HALF_FLOAT = 0x8D61; - -/** - * UNSIGNED_INT_24_8_WEBGL for WEBGL_depth_texture extension - * @type {number} - */ -Texture$1.UNSIGNED_INT_24_8_WEBGL = 34042; - -/* PixelFormat */ -/** - * @type {number} - */ -Texture$1.DEPTH_COMPONENT = glenum.DEPTH_COMPONENT; -/** - * @type {number} - */ -Texture$1.DEPTH_STENCIL = glenum.DEPTH_STENCIL; -/** - * @type {number} - */ -Texture$1.ALPHA = glenum.ALPHA; -/** - * @type {number} - */ -Texture$1.RGB = glenum.RGB; -/** - * @type {number} - */ -Texture$1.RGBA = glenum.RGBA; -/** - * @type {number} - */ -Texture$1.LUMINANCE = glenum.LUMINANCE; -/** - * @type {number} - */ -Texture$1.LUMINANCE_ALPHA = glenum.LUMINANCE_ALPHA; - -/** - * @see https://www.khronos.org/registry/webgl/extensions/EXT_sRGB/ - * @type {number} - */ -Texture$1.SRGB = 0x8C40; -/** - * @see https://www.khronos.org/registry/webgl/extensions/EXT_sRGB/ - * @type {number} - */ -Texture$1.SRGB_ALPHA = 0x8C42; - -/* Compressed Texture */ -Texture$1.COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0; -Texture$1.COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1; -Texture$1.COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2; -Texture$1.COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3; - -/* TextureMagFilter */ -/** - * @type {number} - */ -Texture$1.NEAREST = glenum.NEAREST; -/** - * @type {number} - */ -Texture$1.LINEAR = glenum.LINEAR; - -/* TextureMinFilter */ -/** - * @type {number} - */ -Texture$1.NEAREST_MIPMAP_NEAREST = glenum.NEAREST_MIPMAP_NEAREST; -/** - * @type {number} - */ -Texture$1.LINEAR_MIPMAP_NEAREST = glenum.LINEAR_MIPMAP_NEAREST; -/** - * @type {number} - */ -Texture$1.NEAREST_MIPMAP_LINEAR = glenum.NEAREST_MIPMAP_LINEAR; -/** - * @type {number} - */ -Texture$1.LINEAR_MIPMAP_LINEAR = glenum.LINEAR_MIPMAP_LINEAR; - -/* TextureWrapMode */ -/** - * @type {number} - */ -Texture$1.REPEAT = glenum.REPEAT; -/** - * @type {number} - */ -Texture$1.CLAMP_TO_EDGE = glenum.CLAMP_TO_EDGE; -/** - * @type {number} - */ -Texture$1.MIRRORED_REPEAT = glenum.MIRRORED_REPEAT; - -/** - * Simple double linked list. Compared with array, it has O(1) remove operation. - * @constructor - * @alias clay.core.LinkedList - */ -var LinkedList = function () { - - /** - * @type {clay.core.LinkedList.Entry} - */ - this.head = null; /** - * @type {clay.core.LinkedList.Entry} - */ - this.tail = null; - - this._length = 0; -}; - -/** - * Insert a new value at the tail - * @param {} val - * @return {clay.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 {clay.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; + * Alias of setUniform and setUniforms + * @param {object|string} symbol + * @param {number|array|clay.Texture|ArrayBufferView} [value] + */ + set: function (symbol, value) { + if (typeof(symbol) === 'object') { + for (var key in symbol) { + var val = symbol[key]; + this.setUniform(key, val); + } } else { - prev.next = entry; - entry.prev = prev; + this.setUniform(symbol, value); } - entry.next = next; - next.prev = entry; - } - else { - this.insert(val); - } -}; + }, + /** + * Get uniform value + * @param {string} symbol + * @return {number|array|clay.Texture|ArrayBufferView} + */ + get: function (symbol) { + var uniform = this.uniforms[symbol]; + if (uniform) { + return uniform.value; + } + }, + /** + * Attach a shader instance + * @param {clay.Shader} shader + * @param {boolean} keepStatus If try to keep uniform and texture + */ + attachShader: function(shader, keepStatus) { + var originalUniforms = this.uniforms; -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; + // Ignore if uniform can use in shader. + this.uniforms = shader.createUniforms(); + this.shader = shader; - this._length++; -}; + 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(); + this._textureUniforms = this._enabledUniforms.filter(function (uniformName) { + var type = this.uniforms[uniformName].type; + return type === 't' || type === 'tv'; + }, this); -/** - * Insert an entry at the tail - * @param {clay.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++; -}; + var originalVertexDefines = this.vertexDefines; + var originalFragmentDefines = this.fragmentDefines; -/** - * Remove entry. - * @param {clay.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--; -}; + this.vertexDefines = util$1.clone(shader.vertexDefines); + this.fragmentDefines = util$1.clone(shader.fragmentDefines); -/** - * 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; -}; + if (keepStatus) { + for (var symbol in originalUniforms) { + if (uniforms[symbol]) { + uniforms[symbol].value = originalUniforms[symbol].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; + util$1.defaults(this.vertexDefines, originalVertexDefines); + util$1.defaults(this.fragmentDefines, originalFragmentDefines); } - curr = curr.next; - cursor++; - } -}; -/** - * @return {number} - */ -LinkedList.prototype.length = function () { - return this._length; -}; + var textureStatus = {}; + for (var key in shader.textures) { + textureStatus[key] = { + shaderType: shader.textures[key].shaderType, + type: shader.textures[key].type, + enabled: (keepStatus && this._textureStatus[key]) ? this._textureStatus[key].enabled : false + }; + } -/** - * If list is empty - */ -LinkedList.prototype.isEmpty = function () { - return this._length === 0; -}; + this._textureStatus = textureStatus; -/** - * @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); + this._programKey = ''; + }, + + /** + * Clone a new material and keep uniforms, shader will not be cloned + * @return {clay.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; + + material.vertexDefines = util$1.clone(this.vertexDefines); + material.fragmentDefines = util$1.clone(this.fragmentDefines); + material.enableTexture(this.getEnabledTextures()); + material.precision = this.precision; + + return material; + }, + + /** + * Add a #define macro 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'; } - else { - cb(curr.value, idx); + val = val != null ? val : null; + if (shaderType === 'vertex' || shaderType === 'both') { + if (vertexDefines[symbol] !== val) { + vertexDefines[symbol] = val; + // Mark as dirty + this._programKey = ''; + } } - curr = curr.next; - idx++; - } -}; - -/** - * Clear the list - */ -LinkedList.prototype.clear = function () { - this.tail = this.head = null; - this._length = 0; -}; + if (shaderType === 'fragment' || shaderType === 'both') { + if (fragmentDefines[symbol] !== val) { + fragmentDefines[symbol] = val; + if (shaderType !== 'both') { + this._programKey = ''; + } + } + } + }, -/** - * @constructor - * @param {} val - */ -LinkedList.Entry = function (val) { /** - * @type {} + * Remove a #define macro in shader code + * @param {string} shaderType Can be vertex, fragment or both + * @param {string} symbol */ - this.value = val; + 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._programKey = ''; + } + } + if (shaderType === 'fragment' || shaderType === 'both') { + if (this.isDefined('fragment', symbol)) { + delete this.fragmentDefines[symbol]; + if (shaderType !== 'both') { + this._programKey = ''; + } + } + } + }, /** - * @type {clay.core.LinkedList.Entry} + * If macro is defined in shader. + * @param {string} shaderType Can be vertex, fragment or both + * @param {string} symbol */ - this.next = null; - + isDefined: function (shaderType, symbol) { + // PENDING hasOwnProperty ? + switch (shaderType) { + case 'vertex': + return this.vertexDefines[symbol] !== undefined; + case 'fragment': + return this.fragmentDefines[symbol] !== undefined; + } + }, /** - * @type {clay.core.LinkedList.Entry} + * Get macro value defined in shader. + * @param {string} shaderType Can be vertex, fragment or both + * @param {string} symbol */ - this.prev = null; -}; - -/** - * LRU Cache - * @constructor - * @alias clay.core.LRU - */ -var LRU$1 = function(maxSize) { - - this._list = new LinkedList(); - - this._map = {}; - - this._maxSize = maxSize || 10; -}; - -/** - * Set cache max size - * @param {number} size - */ -LRU$1.prototype.setMaxSize = function(size) { - this._maxSize = size; -}; - -/** - * @param {string} key - * @param {} value - */ -LRU$1.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]; + getDefine: function (shaderType, symbol) { + switch(shaderType) { + case 'vertex': + return this.vertexDefines[symbol]; + case 'fragment': + return this.fragmentDefines[symbol]; } - - var entry = this._list.insert(value); - entry.key = key; - this._map[key] = entry; - } -}; - -/** - * @param {string} key - * @return {} - */ -LRU$1.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); + }, + /** + * Enable a texture, actually it will add a #define macro 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 (Array.isArray(symbol)) { + for (var i = 0; i < symbol.length; i++) { + this.enableTexture(symbol[i]); + } + return; } - return entry.value; - } -}; - -/** - * @param {string} key - */ -LRU$1.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$1.prototype.clear = function() { - this._list.clear(); - this._map = {}; -}; - -/** - * @namespace clay.core.color - */ -var colorUtil = {}; - -var kCSSColorTable = { - 'transparent': [0,0,0,0], 'aliceblue': [240,248,255,1], - 'antiquewhite': [250,235,215,1], 'aqua': [0,255,255,1], - 'aquamarine': [127,255,212,1], 'azure': [240,255,255,1], - 'beige': [245,245,220,1], 'bisque': [255,228,196,1], - 'black': [0,0,0,1], 'blanchedalmond': [255,235,205,1], - 'blue': [0,0,255,1], 'blueviolet': [138,43,226,1], - 'brown': [165,42,42,1], 'burlywood': [222,184,135,1], - 'cadetblue': [95,158,160,1], 'chartreuse': [127,255,0,1], - 'chocolate': [210,105,30,1], 'coral': [255,127,80,1], - 'cornflowerblue': [100,149,237,1], 'cornsilk': [255,248,220,1], - 'crimson': [220,20,60,1], 'cyan': [0,255,255,1], - 'darkblue': [0,0,139,1], 'darkcyan': [0,139,139,1], - 'darkgoldenrod': [184,134,11,1], 'darkgray': [169,169,169,1], - 'darkgreen': [0,100,0,1], 'darkgrey': [169,169,169,1], - 'darkkhaki': [189,183,107,1], 'darkmagenta': [139,0,139,1], - 'darkolivegreen': [85,107,47,1], 'darkorange': [255,140,0,1], - 'darkorchid': [153,50,204,1], 'darkred': [139,0,0,1], - 'darksalmon': [233,150,122,1], 'darkseagreen': [143,188,143,1], - 'darkslateblue': [72,61,139,1], 'darkslategray': [47,79,79,1], - 'darkslategrey': [47,79,79,1], 'darkturquoise': [0,206,209,1], - 'darkviolet': [148,0,211,1], 'deeppink': [255,20,147,1], - 'deepskyblue': [0,191,255,1], 'dimgray': [105,105,105,1], - 'dimgrey': [105,105,105,1], 'dodgerblue': [30,144,255,1], - 'firebrick': [178,34,34,1], 'floralwhite': [255,250,240,1], - 'forestgreen': [34,139,34,1], 'fuchsia': [255,0,255,1], - 'gainsboro': [220,220,220,1], 'ghostwhite': [248,248,255,1], - 'gold': [255,215,0,1], 'goldenrod': [218,165,32,1], - 'gray': [128,128,128,1], 'green': [0,128,0,1], - 'greenyellow': [173,255,47,1], 'grey': [128,128,128,1], - 'honeydew': [240,255,240,1], 'hotpink': [255,105,180,1], - 'indianred': [205,92,92,1], 'indigo': [75,0,130,1], - 'ivory': [255,255,240,1], 'khaki': [240,230,140,1], - 'lavender': [230,230,250,1], 'lavenderblush': [255,240,245,1], - 'lawngreen': [124,252,0,1], 'lemonchiffon': [255,250,205,1], - 'lightblue': [173,216,230,1], 'lightcoral': [240,128,128,1], - 'lightcyan': [224,255,255,1], 'lightgoldenrodyellow': [250,250,210,1], - 'lightgray': [211,211,211,1], 'lightgreen': [144,238,144,1], - 'lightgrey': [211,211,211,1], 'lightpink': [255,182,193,1], - 'lightsalmon': [255,160,122,1], 'lightseagreen': [32,178,170,1], - 'lightskyblue': [135,206,250,1], 'lightslategray': [119,136,153,1], - 'lightslategrey': [119,136,153,1], 'lightsteelblue': [176,196,222,1], - 'lightyellow': [255,255,224,1], 'lime': [0,255,0,1], - 'limegreen': [50,205,50,1], 'linen': [250,240,230,1], - 'magenta': [255,0,255,1], 'maroon': [128,0,0,1], - 'mediumaquamarine': [102,205,170,1], 'mediumblue': [0,0,205,1], - 'mediumorchid': [186,85,211,1], 'mediumpurple': [147,112,219,1], - 'mediumseagreen': [60,179,113,1], 'mediumslateblue': [123,104,238,1], - 'mediumspringgreen': [0,250,154,1], 'mediumturquoise': [72,209,204,1], - 'mediumvioletred': [199,21,133,1], 'midnightblue': [25,25,112,1], - 'mintcream': [245,255,250,1], 'mistyrose': [255,228,225,1], - 'moccasin': [255,228,181,1], 'navajowhite': [255,222,173,1], - 'navy': [0,0,128,1], 'oldlace': [253,245,230,1], - 'olive': [128,128,0,1], 'olivedrab': [107,142,35,1], - 'orange': [255,165,0,1], 'orangered': [255,69,0,1], - 'orchid': [218,112,214,1], 'palegoldenrod': [238,232,170,1], - 'palegreen': [152,251,152,1], 'paleturquoise': [175,238,238,1], - 'palevioletred': [219,112,147,1], 'papayawhip': [255,239,213,1], - 'peachpuff': [255,218,185,1], 'peru': [205,133,63,1], - 'pink': [255,192,203,1], 'plum': [221,160,221,1], - 'powderblue': [176,224,230,1], 'purple': [128,0,128,1], - 'red': [255,0,0,1], 'rosybrown': [188,143,143,1], - 'royalblue': [65,105,225,1], 'saddlebrown': [139,69,19,1], - 'salmon': [250,128,114,1], 'sandybrown': [244,164,96,1], - 'seagreen': [46,139,87,1], 'seashell': [255,245,238,1], - 'sienna': [160,82,45,1], 'silver': [192,192,192,1], - 'skyblue': [135,206,235,1], 'slateblue': [106,90,205,1], - 'slategray': [112,128,144,1], 'slategrey': [112,128,144,1], - 'snow': [255,250,250,1], 'springgreen': [0,255,127,1], - 'steelblue': [70,130,180,1], 'tan': [210,180,140,1], - 'teal': [0,128,128,1], 'thistle': [216,191,216,1], - 'tomato': [255,99,71,1], 'turquoise': [64,224,208,1], - 'violet': [238,130,238,1], 'wheat': [245,222,179,1], - 'white': [255,255,255,1], 'whitesmoke': [245,245,245,1], - 'yellow': [255,255,0,1], 'yellowgreen': [154,205,50,1] -}; - -function clampCssByte(i) { // Clamp to integer 0 .. 255. - i = Math.round(i); // Seems to be what Chrome does (vs truncation). - return i < 0 ? 0 : i > 255 ? 255 : i; -} + var status = this._textureStatus[symbol]; + if (status) { + var isEnabled = status.enabled; + if (!isEnabled) { + status.enabled = true; + this._programKey = ''; + } + } + }, + /** + * Enable all textures used in the shader + */ + enableTexturesAll: function () { + var textureStatus = this._textureStatus; + for (var symbol in textureStatus) { + textureStatus[symbol].enabled = true; + } -function clampCssAngle(i) { // Clamp to integer 0 .. 360. - i = Math.round(i); // Seems to be what Chrome does (vs truncation). - return i < 0 ? 0 : i > 360 ? 360 : i; -} + this._programKey = ''; + }, + /** + * Disable a texture, it remove a #define macro in the shader + * @param {string} symbol + */ + disableTexture: function (symbol) { + if (Array.isArray(symbol)) { + for (var i = 0; i < symbol.length; i++) { + this.disableTexture(symbol[i]); + } + return; + } -function clampCssFloat(f) { // Clamp to float 0.0 .. 1.0. - return f < 0 ? 0 : f > 1 ? 1 : f; -} + var status = this._textureStatus[symbol]; + if (status) { + var isDisabled = ! status.enabled; + if (!isDisabled) { + status.enabled = false; + this._programKey = ''; + } + } + }, + /** + * Disable all textures used in the shader + */ + disableTexturesAll: function () { + var textureStatus = this._textureStatus; + for (var symbol in textureStatus) { + textureStatus[symbol].enabled = false; + } -function parseCssInt(str) { // int or percentage. - if (str.length && str.charAt(str.length - 1) === '%') { - return clampCssByte(parseFloat(str) / 100 * 255); - } - return clampCssByte(parseInt(str, 10)); -} + this._programKey = ''; + }, + /** + * If texture of given type is enabled. + * @param {string} symbol + * @return {boolean} + */ + isTextureEnabled: function (symbol) { + var textureStatus = this._textureStatus; + return !!textureStatus[symbol] + && textureStatus[symbol].enabled; + }, -function parseCssFloat(str) { // float or percentage. - if (str.length && str.charAt(str.length - 1) === '%') { - return clampCssFloat(parseFloat(str) / 100); - } - return clampCssFloat(parseFloat(str)); -} + /** + * Get all enabled textures + * @return {string[]} + */ + getEnabledTextures: function () { + var enabledTextures = []; + var textureStatus = this._textureStatus; + for (var symbol in textureStatus) { + if (textureStatus[symbol].enabled) { + enabledTextures.push(symbol); + } + } + return enabledTextures; + }, -function cssHueToRgb(m1, m2, h) { - if (h < 0) { - h += 1; - } - else if (h > 1) { - h -= 1; - } + /** + * Mark defines are updated. + */ + dirtyDefines: function () { + this._programKey = ''; + }, - if (h * 6 < 1) { - return m1 + (m2 - m1) * h * 6; - } - if (h * 2 < 1) { - return m2; - } - if (h * 3 < 2) { - return m1 + (m2 - m1) * (2/3 - h) * 6; + getProgramKey: function () { + if (!this._programKey) { + this._programKey = getProgramKey( + this.vertexDefines, this.fragmentDefines, this.getEnabledTextures() + ); + } + return this._programKey; } - return m1; -} - -function lerpNumber(a, b, p) { - return a + (b - a) * p; -} +}); -function setRgba(out, r, g, b, a) { - out[0] = r; out[1] = g; out[2] = b; out[3] = a; - return out; -} -function copyRgba(out, a) { - out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3]; - return out; -} +var DIRTY_PREFIX = '__dt__'; -var colorCache = new LRU$1(20); -var lastRemovedArr = null; +var Cache = function () { -function putToCache(colorStr, rgbaArr) { - // Reuse removed array - if (lastRemovedArr) { - copyRgba(lastRemovedArr, rgbaArr); - } - lastRemovedArr = colorCache.put(colorStr, lastRemovedArr || (rgbaArr.slice())); -} + this._contextId = 0; -/** - * @name clay.core.color.parse - * @param {string} colorStr - * @param {Array.} out - * @return {Array.} - */ -colorUtil.parse = function (colorStr, rgbaArr) { - if (!colorStr) { - return; - } - rgbaArr = rgbaArr || []; + this._caches = []; - var cached = colorCache.get(colorStr); - if (cached) { - return copyRgba(rgbaArr, cached); - } + this._context = {}; +}; - // colorStr may be not string - colorStr = colorStr + ''; - // Remove all whitespace, not compliant, but should just be more accepting. - var str = colorStr.replace(/ /g, '').toLowerCase(); +Cache.prototype = { - // Color keywords (and transparent) lookup. - if (str in kCSSColorTable) { - copyRgba(rgbaArr, kCSSColorTable[str]); - putToCache(colorStr, rgbaArr); - return rgbaArr; - } + use: function (contextId, documentSchema) { + var caches = this._caches; + if (!caches[contextId]) { + caches[contextId] = {}; - // #abc and #abc123 syntax. - if (str.charAt(0) === '#') { - if (str.length === 4) { - var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing. - if (!(iv >= 0 && iv <= 0xfff)) { - setRgba(rgbaArr, 0, 0, 0, 1); - return; // Covers NaN. + if (documentSchema) { + caches[contextId] = documentSchema(); } - setRgba(rgbaArr, - ((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8), - (iv & 0xf0) | ((iv & 0xf0) >> 4), - (iv & 0xf) | ((iv & 0xf) << 4), - 1 - ); - putToCache(colorStr, rgbaArr); - return rgbaArr; } - else if (str.length === 7) { - var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing. - if (!(iv >= 0 && iv <= 0xffffff)) { - setRgba(rgbaArr, 0, 0, 0, 1); - return; // Covers NaN. + 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; } - setRgba(rgbaArr, - (iv & 0xff0000) >> 16, - (iv & 0xff00) >> 8, - iv & 0xff, - 1 - ); - putToCache(colorStr, rgbaArr); - return rgbaArr; } + }, - return; - } - var op = str.indexOf('('), ep = str.indexOf(')'); - if (op !== -1 && ep + 1 === str.length) { - var fname = str.substr(0, op); - var params = str.substr(op + 1, ep - (op + 1)).split(','); - var alpha = 1; // To allow case fallthrough. - switch (fname) { - case 'rgba': - if (params.length !== 4) { - setRgba(rgbaArr, 0, 0, 0, 1); - return; - } - alpha = parseCssFloat(params.pop()); // jshint ignore:line - // Fall through. - case 'rgb': - if (params.length !== 3) { - setRgba(rgbaArr, 0, 0, 0, 1); - return; - } - setRgba(rgbaArr, - parseCssInt(params[0]), - parseCssInt(params[1]), - parseCssInt(params[2]), - alpha - ); - putToCache(colorStr, rgbaArr); - return rgbaArr; - case 'hsla': - if (params.length !== 4) { - setRgba(rgbaArr, 0, 0, 0, 1); - return; - } - params[3] = parseCssFloat(params[3]); - hsla2rgba(params, rgbaArr); - putToCache(colorStr, rgbaArr); - return rgbaArr; - case 'hsl': - if (params.length !== 3) { - setRgba(rgbaArr, 0, 0, 0, 1); - return; - } - hsla2rgba(params, rgbaArr); - putToCache(colorStr, rgbaArr); - return rgbaArr; - default: - return; - } - } + isDirty: function (field) { + field = field || ''; + var key = DIRTY_PREFIX + field; + var context = this._context; + return !context.hasOwnProperty(key) + || context[key] === true; + }, - setRgba(rgbaArr, 0, 0, 0, 1); - return; -}; + deleteContext: function (contextId) { + delete this._caches[contextId]; + this._context = {}; + }, -colorUtil.parseToFloat = function (colorStr, rgbaArr) { - rgbaArr = colorUtil.parse(colorStr, rgbaArr); - if (!rgbaArr) { - return; + 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); } - rgbaArr[0] /= 255; - rgbaArr[1] /= 255; - rgbaArr[2] /= 255; - return rgbaArr; }; +Cache.prototype.constructor = Cache; + /** - * @name clay.core.color.hsla2rgba - * @param {Array.} hsla - * @param {Array.} rgba - * @return {Array.} rgba + * Base class for all textures like compressed texture, texture2d, texturecube + * TODO mapping */ -function hsla2rgba(hsla, rgba) { - var h = (((parseFloat(hsla[0]) % 360) + 360) % 360) / 360; // 0 .. 1 - // NOTE(deanm): According to the CSS spec s/l should only be - // percentages, but we don't bother and let float or percentage. - var s = parseCssFloat(hsla[1]); - var l = parseCssFloat(hsla[2]); - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; +/** + * @constructor + * @alias clay.Texture + * @extends clay.core.Base + */ +var Texture = Base.extend( /** @lends clay.Texture# */ { + /** + * Texture width, readonly when the texture source is image + * @type {number} + */ + width: 512, + /** + * Texture height, readonly when the texture source is image + * @type {number} + */ + height: 512, + /** + * Texel data type. + * Possible values: + * + {@link clay.Texture.UNSIGNED_BYTE} + * + {@link clay.Texture.HALF_FLOAT} + * + {@link clay.Texture.FLOAT} + * + {@link clay.Texture.UNSIGNED_INT_24_8_WEBGL} + * + {@link clay.Texture.UNSIGNED_INT} + * @type {number} + */ + type: glenum.UNSIGNED_BYTE, + /** + * Format of texel data + * Possible values: + * + {@link clay.Texture.RGBA} + * + {@link clay.Texture.DEPTH_COMPONENT} + * + {@link clay.Texture.DEPTH_STENCIL} + * @type {number} + */ + format: glenum.RGBA, + /** + * Texture wrap. Default to be REPEAT. + * Possible values: + * + {@link clay.Texture.CLAMP_TO_EDGE} + * + {@link clay.Texture.REPEAT} + * + {@link clay.Texture.MIRRORED_REPEAT} + * @type {number} + */ + wrapS: glenum.REPEAT, + /** + * Texture wrap. Default to be REPEAT. + * Possible values: + * + {@link clay.Texture.CLAMP_TO_EDGE} + * + {@link clay.Texture.REPEAT} + * + {@link clay.Texture.MIRRORED_REPEAT} + * @type {number} + */ + wrapT: glenum.REPEAT, + /** + * Possible values: + * + {@link clay.Texture.NEAREST} + * + {@link clay.Texture.LINEAR} + * + {@link clay.Texture.NEAREST_MIPMAP_NEAREST} + * + {@link clay.Texture.LINEAR_MIPMAP_NEAREST} + * + {@link clay.Texture.NEAREST_MIPMAP_LINEAR} + * + {@link clay.Texture.LINEAR_MIPMAP_LINEAR} + * @type {number} + */ + minFilter: glenum.LINEAR_MIPMAP_LINEAR, + /** + * Possible values: + * + {@link clay.Texture.NEAREST} + * + {@link clay.Texture.LINEAR} + * @type {number} + */ + magFilter: glenum.LINEAR, + /** + * If enable mimap. + * @type {boolean} + */ + useMipmap: true, - rgba = rgba || []; - setRgba(rgba, - clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255), - clampCssByte(cssHueToRgb(m1, m2, h) * 255), - clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255), - 1 - ); + /** + * Anisotropic filtering, enabled if value is larger than 1 + * @see https://developer.mozilla.org/en-US/docs/Web/API/EXT_texture_filter_anisotropic + * @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 + /** + * If flip in y axis for given image source + * @type {boolean} + * @default true + */ + flipY: true, + + /** + * A flag to indicate if texture source is sRGB + */ + sRGB: true, + /** + * @type {number} + * @default 4 + */ + unpackAlignment: 4, + /** + * @type {boolean} + * @default false + */ + premultiplyAlpha: false, + + /** + * Dynamic option for texture like video + * @type {boolean} + */ + dynamic: false, + + NPOT: false, + + // PENDING + // Init it here to avoid deoptimization when it's assigned in application dynamically + __used: 0 + +}, function () { + this._cache = new Cache(); +}, +/** @lends clay.Texture.prototype */ +{ + + getWebGLTexture: function (renderer) { + var _gl = renderer.gl; + var cache = this._cache; + cache.use(renderer.__uid__); + + 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(renderer); + } + else if (cache.isDirty()) { + this.update(renderer); + cache.fresh(); + } - if (hsla.length === 4) { - rgba[3] = hsla[3]; - } + return cache.get('webgl_texture'); + }, - return rgba; -} + bind: function () {}, + unbind: function () {}, -/** - * @name clay.core.color.rgba2hsla - * @param {Array.} rgba - * @return {Array.} hsla - */ -function rgba2hsla(rgba) { - if (!rgba) { - return; - } + /** + * Mark texture is dirty and update in the next frame + */ + dirty: function () { + if (this._cache) { + this._cache.dirtyAll(); + } + }, - // RGB from 0 to 255 - var R = rgba[0] / 255; - var G = rgba[1] / 255; - var B = rgba[2] / 255; + update: function (renderer) {}, - var vMin = Math.min(R, G, B); // Min. value of RGB - var vMax = Math.max(R, G, B); // Max. value of RGB - var delta = vMax - vMin; // Delta RGB value + // Update the common parameters of texture + updateCommon: function (renderer) { + var _gl = renderer.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); - var L = (vMax + vMin) / 2; - var H; - var S; - // HSL results from 0 to 1 - if (delta === 0) { - H = 0; - S = 0; - } - else { - if (L < 0.5) { - S = delta / (vMax + vMin); + // Use of none-power of two texture + // http://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences + if (this.format === glenum.DEPTH_COMPONENT) { + this.useMipmap = false; } - else { - S = delta / (2 - vMax - vMin); + + var sRGBExt = renderer.getGLExtension('EXT_sRGB'); + // Fallback + if (this.format === Texture.SRGB && !sRGBExt) { + this.format = Texture.RGB; + } + if (this.format === Texture.SRGB_ALPHA && !sRGBExt) { + this.format = Texture.RGBA; } - var deltaR = (((vMax - R) / 6) + (delta / 2)) / delta; - var deltaG = (((vMax - G) / 6) + (delta / 2)) / delta; - var deltaB = (((vMax - B) / 6) + (delta / 2)) / delta; + this.NPOT = !this.isPowerOfTwo(); + }, - if (R === vMax) { - H = deltaB - deltaG; + getAvailableWrapS: function () { + if (this.NPOT) { + return glenum.CLAMP_TO_EDGE; } - else if (G === vMax) { - H = (1 / 3) + deltaR - deltaB; + return this.wrapS; + }, + getAvailableWrapT: function () { + if (this.NPOT) { + return glenum.CLAMP_TO_EDGE; } - else if (B === vMax) { - H = (2 / 3) + deltaG - deltaR; + return this.wrapT; + }, + getAvailableMinFilter: function () { + var minFilter = this.minFilter; + if (this.NPOT || !this.useMipmap) { + if (minFilter === glenum.NEAREST_MIPMAP_NEAREST || + minFilter === glenum.NEAREST_MIPMAP_LINEAR + ) { + return glenum.NEAREST; + } + else if (minFilter === glenum.LINEAR_MIPMAP_LINEAR || + minFilter === glenum.LINEAR_MIPMAP_NEAREST + ) { + return glenum.LINEAR; + } + else { + return minFilter; + } + } + else { + return minFilter; } + }, + getAvailableMagFilter: function () { + return this.magFilter; + }, - if (H < 0) { - H += 1; + nextHighestPowerOfTwo: function (x) { + --x; + for (var i = 1; i < 32; i <<= 1) { + x = x | x >> i; } + return x + 1; + }, + /** + * @param {clay.Renderer} renderer + */ + dispose: function (renderer) { - if (H > 1) { - H -= 1; + var cache = this._cache; + + cache.use(renderer.__uid__); + + var webglTexture = cache.get('webgl_texture'); + if (webglTexture){ + renderer.gl.deleteTexture(webglTexture); } - } + cache.deleteContext(renderer.__uid__); - var hsla = [H * 360, S, L]; + }, + /** + * Test if image of texture is valid and loaded. + * @return {boolean} + */ + isRenderable: function () {}, - if (rgba[3] != null) { - hsla.push(rgba[3]); + /** + * Test if texture size is power of two + * @return {boolean} + */ + isPowerOfTwo: function () {} +}); + +Object.defineProperty(Texture.prototype, 'width', { + get: function () { + return this._width; + }, + set: function (value) { + this._width = value; + } +}); +Object.defineProperty(Texture.prototype, 'height', { + get: function () { + return this._height; + }, + set: function (value) { + this._height = value; } +}); - return hsla; -} +/* DataType */ /** - * @name clay.core.color.lift - * @param {string} color - * @param {number} level - * @return {string} + * @type {number} */ -colorUtil.lift = function (color, level) { - var colorArr = colorUtil.parse(color); - if (colorArr) { - for (var i = 0; i < 3; i++) { - if (level < 0) { - colorArr[i] = colorArr[i] * (1 - level) | 0; - } - else { - colorArr[i] = ((255 - colorArr[i]) * level + colorArr[i]) | 0; - } - } - return colorUtil.stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb'); - } -}; - +Texture.BYTE = glenum.BYTE; /** - * @name clay.core.color.toHex - * @param {string} color - * @return {string} + * @type {number} */ -colorUtil.toHex = function (color) { - var colorArr = colorUtil.parse(color); - if (colorArr) { - return ((1 << 24) + (colorArr[0] << 16) + (colorArr[1] << 8) + (+colorArr[2])).toString(16).slice(1); - } -}; - +Texture.UNSIGNED_BYTE = glenum.UNSIGNED_BYTE; /** - * Map value to color. Faster than lerp methods because color is represented by rgba array. - * @name clay.core.color - * @param {number} normalizedValue A float between 0 and 1. - * @param {Array.>} colors List of rgba color array - * @param {Array.} [out] Mapped gba color array - * @return {Array.} will be null/undefined if input illegal. + * @type {number} */ -colorUtil.fastLerp = function (normalizedValue, colors, out) { - if (!(colors && colors.length) - || !(normalizedValue >= 0 && normalizedValue <= 1) - ) { - return; - } - - out = out || []; - - var value = normalizedValue * (colors.length - 1); - var leftIndex = Math.floor(value); - var rightIndex = Math.ceil(value); - var leftColor = colors[leftIndex]; - var rightColor = colors[rightIndex]; - var dv = value - leftIndex; - out[0] = clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)); - out[1] = clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)); - out[2] = clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)); - out[3] = clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv)); - - return out; -}; +Texture.SHORT = glenum.SHORT; +/** + * @type {number} + */ +Texture.UNSIGNED_SHORT = glenum.UNSIGNED_SHORT; +/** + * @type {number} + */ +Texture.INT = glenum.INT; +/** + * @type {number} + */ +Texture.UNSIGNED_INT = glenum.UNSIGNED_INT; +/** + * @type {number} + */ +Texture.FLOAT = glenum.FLOAT; +/** + * @type {number} + */ +Texture.HALF_FLOAT = 0x8D61; -colorUtil.fastMapToColor = colorUtil.fastLerp; +/** + * UNSIGNED_INT_24_8_WEBGL for WEBGL_depth_texture extension + * @type {number} + */ +Texture.UNSIGNED_INT_24_8_WEBGL = 34042; +/* PixelFormat */ +/** + * @type {number} + */ +Texture.DEPTH_COMPONENT = glenum.DEPTH_COMPONENT; +/** + * @type {number} + */ +Texture.DEPTH_STENCIL = glenum.DEPTH_STENCIL; +/** + * @type {number} + */ +Texture.ALPHA = glenum.ALPHA; +/** + * @type {number} + */ +Texture.RGB = glenum.RGB; +/** + * @type {number} + */ +Texture.RGBA = glenum.RGBA; /** - * @param {number} normalizedValue A float between 0 and 1. - * @param {Array.} colors Color list. - * @param {boolean=} fullOutput Default false. - * @return {(string|Object)} Result color. If fullOutput, - * return {color: ..., leftIndex: ..., rightIndex: ..., value: ...}, + * @type {number} */ -colorUtil.lerp = function (normalizedValue, colors, fullOutput) { - if (!(colors && colors.length) - || !(normalizedValue >= 0 && normalizedValue <= 1) - ) { - return; - } +Texture.LUMINANCE = glenum.LUMINANCE; +/** + * @type {number} + */ +Texture.LUMINANCE_ALPHA = glenum.LUMINANCE_ALPHA; - var value = normalizedValue * (colors.length - 1); - var leftIndex = Math.floor(value); - var rightIndex = Math.ceil(value); - var leftColor = colorUtil.parse(colors[leftIndex]); - var rightColor = colorUtil.parse(colors[rightIndex]); - var dv = value - leftIndex; +/** + * @see https://www.khronos.org/registry/webgl/extensions/EXT_sRGB/ + * @type {number} + */ +Texture.SRGB = 0x8C40; +/** + * @see https://www.khronos.org/registry/webgl/extensions/EXT_sRGB/ + * @type {number} + */ +Texture.SRGB_ALPHA = 0x8C42; - var color = colorUtil.stringify( - [ - clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)), - clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)), - clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)), - clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv)) - ], - 'rgba' - ); +/* 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; - return fullOutput - ? { - color: color, - leftIndex: leftIndex, - rightIndex: rightIndex, - value: value - } - : color; -}; +/* TextureMagFilter */ +/** + * @type {number} + */ +Texture.NEAREST = glenum.NEAREST; +/** + * @type {number} + */ +Texture.LINEAR = glenum.LINEAR; +/* TextureMinFilter */ /** - * @deprecated + * @type {number} */ -colorUtil.mapToColor = colorUtil.lerp; +Texture.NEAREST_MIPMAP_NEAREST = glenum.NEAREST_MIPMAP_NEAREST; +/** + * @type {number} + */ +Texture.LINEAR_MIPMAP_NEAREST = glenum.LINEAR_MIPMAP_NEAREST; +/** + * @type {number} + */ +Texture.NEAREST_MIPMAP_LINEAR = glenum.NEAREST_MIPMAP_LINEAR; +/** + * @type {number} + */ +Texture.LINEAR_MIPMAP_LINEAR = glenum.LINEAR_MIPMAP_LINEAR; +/* TextureWrapMode */ /** - * @name clay.core.color - * @param {string} color - * @param {number=} h 0 ~ 360, ignore when null. - * @param {number=} s 0 ~ 1, ignore when null. - * @param {number=} l 0 ~ 1, ignore when null. - * @return {string} Color string in rgba format. + * @type {number} */ -colorUtil.modifyHSL = function (color, h, s, l) { - color = colorUtil.parse(color); +Texture.REPEAT = glenum.REPEAT; +/** + * @type {number} + */ +Texture.CLAMP_TO_EDGE = glenum.CLAMP_TO_EDGE; +/** + * @type {number} + */ +Texture.MIRRORED_REPEAT = glenum.MIRRORED_REPEAT; - if (color) { - color = rgba2hsla(color); - h != null && (color[0] = clampCssAngle(h)); - s != null && (color[1] = parseCssFloat(s)); - l != null && (color[2] = parseCssFloat(l)); +var mathUtil = {}; - return colorUtil.stringify(hsla2rgba(color), 'rgba'); - } +mathUtil.isPowerOfTwo = function (value) { + return (value & (value - 1)) === 0; }; -/** - * @param {string} color - * @param {number=} alpha 0 ~ 1 - * @return {string} Color string in rgba format. - */ -colorUtil.modifyAlpha = function (color, alpha) { - color = colorUtil.parse(color); +mathUtil.nextPowerOfTwo = function (value) { + value --; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value ++; - if (color && alpha != null) { - color[3] = clampCssFloat(alpha); - return colorUtil.stringify(color, 'rgba'); - } + return value; }; -/** - * @param {Array.} arrColor like [12,33,44,0.4] - * @param {string} type 'rgba', 'hsva', ... - * @return {string} Result color. (If input illegal, return undefined). - */ -colorUtil.stringify = function (arrColor, type) { - if (!arrColor || !arrColor.length) { - return; - } - var colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2]; - if (type === 'rgba' || type === 'hsva' || type === 'hsla') { - colorStr += ',' + arrColor[3]; - } - return type + '(' + colorStr + ')'; +mathUtil.nearestPowerOfTwo = function (value) { + return Math.pow( 2, Math.round( Math.log( value ) / Math.LN2 ) ); }; -var parseColor$1 = colorUtil.parseToFloat; - -var programKeyCache = {}; +var isPowerOfTwo = mathUtil.isPowerOfTwo; -function getDefineCode(defines) { - var defineKeys = Object.keys(defines); - defineKeys.sort(); - var defineStr = []; - // Custom Defines - for (var i = 0; i < defineKeys.length; i++) { - var key = defineKeys[i]; - var value = defines[key]; - if (value === null) { - defineStr.push(key); - } - else{ - defineStr.push(key + ' ' + value.toString()); - } - } - return defineStr.join('\n'); +function nearestPowerOfTwo(val) { + return Math.pow(2, Math.round(Math.log(val) / Math.LN2)); } +function convertTextureToPowerOfTwo(texture, canvas) { + // var canvas = document.createElement('canvas'); + var width = nearestPowerOfTwo(texture.width); + var height = nearestPowerOfTwo(texture.height); + canvas = canvas || document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + var ctx = canvas.getContext('2d'); + ctx.drawImage(texture.image, 0, 0, width, height); -function getProgramKey(vertexDefines, fragmentDefines, enabledTextures) { - enabledTextures.sort(); - var defineStr = []; - for (var i = 0; i < enabledTextures.length; i++) { - var symbol = enabledTextures[i]; - defineStr.push(symbol); - } - var key = getDefineCode(vertexDefines) + '\n' - + getDefineCode(fragmentDefines) + '\n' - + defineStr.join('\n'); - - if (programKeyCache[key]) { - return programKeyCache[key]; - } - - var id = util$1.genGUID(); - programKeyCache[key] = id; - return id; + return canvas; } /** - * Material defines the appearance of mesh surface, like `color`, `roughness`, `metalness`, etc. - * It contains a {@link clay.Shader} and corresponding uniforms. + * @constructor clay.Texture2D + * @extends clay.Texture * - * Here is a basic example to create a standard material -```js -var material = new clay.Material({ - shader: new clay.Shader( - clay.Shader.source('clay.vertex'), - clay.Shader.source('clay.fragment') - ) -}); -``` - * @constructor clay.Material - * @extends clay.core.Base + * @example + * ... + * var mat = new clay.Material({ + * shader: clay.shader.library.get('clay.phong', 'diffuseMap') + * }); + * var diffuseMap = new clay.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 Material = Base.extend(function () { - return /** @lends clay.Material# */ { - /** - * @type {string} - */ - name: '', - - /** - * @type {Object} - */ - // uniforms: null, - - /** - * @type {clay.Shader} - */ - // shader: null, - - /** - * @type {boolean} - */ - depthTest: true, - - /** - * @type {boolean} - */ - depthMask: true, - +var Texture2D = Texture.extend(function () { + return /** @lends clay.Texture2D# */ { /** - * @type {boolean} + * @type {?HTMLImageElement|HTMLCanvasElemnet} */ - transparent: false, + // TODO mark dirty when assigned. + image: null, /** - * 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); - * } + * Pixels data. Will be ignored if image is set. + * @type {?Uint8Array|Float32Array} */ - blend: null, - + pixels: null, /** - * If update texture status automatically. + * @type {Array.} + * @example + * [{ + * image: mipmap0, + * pixels: null + * }, { + * image: mipmap1, + * pixels: null + * }, ....] */ - autoUpdateTextureStatus: true, - - uniforms: {}, - vertexDefines: {}, - fragmentDefines: {}, - _textureStatus: {}, - - // shadowTransparentMap : null + mipmaps: [], - // PENDING enable the uniform that only used in shader. - _enabledUniforms: null, + /** + * If convert texture to power-of-two + * @type {boolean} + */ + convertToPOT: false }; -}, function () { - if (!this.name) { - this.name = 'MATERIAL_' + this.__uid__; - } +}, { - if (this.shader) { - // Keep status, mainly preset uniforms, vertexDefines and fragmentDefines - this.attachShader(this.shader, true); - } -}, -/** @lends clay.Material.prototype */ -{ - precision: 'highp', + textureType: 'texture2D', - /** - * Set material uniform - * @example - * mat.setUniform('color', [1, 1, 1, 1]); - * @param {string} symbol - * @param {number|array|clay.Texture|ArrayBufferView} value - */ - setUniform: function (symbol, value) { - if (value === undefined) { - console.warn('Uniform value "' + symbol + '" is undefined'); - } - var uniform = this.uniforms[symbol]; - if (uniform) { + update: function (renderer) { - if (typeof value === 'string') { - // Try to parse as a color. Invalid color string will return null. - value = parseColor$1(value) || value; - } + var _gl = renderer.gl; + _gl.bindTexture(_gl.TEXTURE_2D, this._cache.get('webgl_texture')); - uniform.value = value; + this.updateCommon(renderer); - if (this.autoUpdateTextureStatus && uniform.type === 't') { - if (value) { - this.enableTexture(symbol); - } - else { - this.disableTexture(symbol); - } - } - } - }, + var glFormat = this.format; + var glType = this.type; - /** - * @param {Object} obj - */ - setUniforms: function(obj) { - for (var key in obj) { - var val = obj[key]; - this.setUniform(key, val); - } - }, + // Convert to pot is only available when using image/canvas/video element. + var convertToPOT = !!(this.convertToPOT + && !this.mipmaps.length && this.image + && (this.wrapS === Texture.REPEAT || this.wrapT === Texture.REPEAT) + && this.NPOT + ); - /** - * @param {string} symbol - * @return {boolean} - */ - isUniformEnabled: function (symbol) { - return this._enabledUniforms.indexOf(symbol) >= 0; - }, + _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, convertToPOT ? this.wrapS : this.getAvailableWrapS()); + _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, convertToPOT ? this.wrapT : this.getAvailableWrapT()); - getEnabledUniforms: function () { - return this._enabledUniforms; - }, - getTextureUniforms: function () { - return this._textureUniforms; - }, + _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, convertToPOT ? this.magFilter : this.getAvailableMagFilter()); + _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, convertToPOT ? this.minFilter : this.getAvailableMinFilter()); - /** - * Alias of setUniform and setUniforms - * @param {object|string} symbol - * @param {number|array|clay.Texture|ArrayBufferView} [value] - */ - set: function (symbol, value) { - if (typeof(symbol) === 'object') { - for (var key in symbol) { - var val = symbol[key]; - this.setUniform(key, val); - } - } - else { - this.setUniform(symbol, value); - } - }, - /** - * Get uniform value - * @param {string} symbol - * @return {number|array|clay.Texture|ArrayBufferView} - */ - get: function (symbol) { - var uniform = this.uniforms[symbol]; - if (uniform) { - return uniform.value; + var anisotropicExt = renderer.getGLExtension('EXT_texture_filter_anisotropic'); + if (anisotropicExt && this.anisotropic > 1) { + _gl.texParameterf(_gl.TEXTURE_2D, anisotropicExt.TEXTURE_MAX_ANISOTROPY_EXT, this.anisotropic); } - }, - /** - * Attach a shader instance - * @param {clay.Shader} shader - * @param {boolean} keepStatus If try to keep uniform and texture - */ - attachShader: function(shader, keepStatus) { - 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(); - this._textureUniforms = this._enabledUniforms.filter(function (uniformName) { - var type = this.uniforms[uniformName].type; - return type === 't' || type === 'tv'; - }, this); - - var originalVertexDefines = this.vertexDefines; - var originalFragmentDefines = this.fragmentDefines; - - this.vertexDefines = util$1.clone(shader.vertexDefines); - this.fragmentDefines = util$1.clone(shader.fragmentDefines); - if (keepStatus) { - for (var symbol in originalUniforms) { - if (uniforms[symbol]) { - uniforms[symbol].value = originalUniforms[symbol].value; - } + // Fallback to float type if browser don't have half float extension + if (glType === 36193) { + var halfFloatExt = renderer.getGLExtension('OES_texture_half_float'); + if (!halfFloatExt) { + glType = glenum.FLOAT; } - - util$1.defaults(this.vertexDefines, originalVertexDefines); - util$1.defaults(this.fragmentDefines, originalFragmentDefines); - } - - var textureStatus = {}; - for (var key in shader.textures) { - textureStatus[key] = { - shaderType: shader.textures[key].shaderType, - type: shader.textures[key].type, - enabled: (keepStatus && this._textureStatus[key]) ? this._textureStatus[key].enabled : false - }; - } - - this._textureStatus = textureStatus; - - this._programKey = ''; - }, - - /** - * Clone a new material and keep uniforms, shader will not be cloned - * @return {clay.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; - material.vertexDefines = util$1.clone(this.vertexDefines); - material.fragmentDefines = util$1.clone(this.fragmentDefines); - material.enableTexture(this.getEnabledTextures()); - material.precision = this.precision; - - return material; - }, - - /** - * Add a #define macro 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._programKey = ''; + 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, false); + width /= 2; + height /= 2; } } - if (shaderType === 'fragment' || shaderType === 'both') { - if (fragmentDefines[symbol] !== val) { - fragmentDefines[symbol] = val; - if (shaderType !== 'both') { - this._programKey = ''; - } + else { + this._updateTextureData(_gl, this, 0, this.width, this.height, glFormat, glType, convertToPOT); + + if (this.useMipmap && (!this.NPOT || convertToPOT)) { + _gl.generateMipmap(_gl.TEXTURE_2D); } } + + _gl.bindTexture(_gl.TEXTURE_2D, null); }, - /** - * Remove a #define macro in shader code - * @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._programKey = ''; + _updateTextureData: function (_gl, data, level, width, height, glFormat, glType, convertToPOT) { + if (data.image) { + var imgData = data.image; + if (convertToPOT) { + this._potCanvas = convertTextureToPowerOfTwo(this, this._potCanvas); + imgData = this._potCanvas; } + _gl.texImage2D(_gl.TEXTURE_2D, level, glFormat, glFormat, glType, imgData); } - if (shaderType === 'fragment' || shaderType === 'both') { - if (this.isDefined('fragment', symbol)) { - delete this.fragmentDefines[symbol]; - if (shaderType !== 'both') { - this._programKey = ''; - } + 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); } } }, /** - * If macro is defined in shader. - * @param {string} shaderType Can be vertex, fragment or both - * @param {string} symbol + * @param {clay.Renderer} renderer + * @memberOf clay.Texture2D.prototype */ - isDefined: function (shaderType, symbol) { - // PENDING hasOwnProperty ? - switch (shaderType) { - case 'vertex': - return this.vertexDefines[symbol] !== undefined; - case 'fragment': - return this.fragmentDefines[symbol] !== undefined; + generateMipmap: function (renderer) { + var _gl = renderer.gl; + if (this.useMipmap && !this.NPOT) { + _gl.bindTexture(_gl.TEXTURE_2D, this._cache.get('webgl_texture')); + _gl.generateMipmap(_gl.TEXTURE_2D); } }, - /** - * Get macro value defined in shader. - * @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]; - } + + isPowerOfTwo: function () { + return isPowerOfTwo(this.width) && isPowerOfTwo(this.height); }, - /** - * Enable a texture, actually it will add a #define macro 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 (Array.isArray(symbol)) { - 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._programKey = ''; - } + isRenderable: function () { + if (this.image) { + return this.image.nodeName === 'CANVAS' + || this.image.nodeName === 'VIDEO' + || this.image.complete; } - }, - /** - * Enable all textures used in the shader - */ - enableTexturesAll: function () { - var textureStatus = this._textureStatus; - for (var symbol in textureStatus) { - textureStatus[symbol].enabled = true; + else { + return !!(this.width && this.height); } + }, - this._programKey = ''; + bind: function (renderer) { + renderer.gl.bindTexture(renderer.gl.TEXTURE_2D, this.getWebGLTexture(renderer)); }, - /** - * Disable a texture, it remove a #define macro in the shader - * @param {string} symbol - */ - disableTexture: function (symbol) { - if (Array.isArray(symbol)) { - 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._programKey = ''; - } - } + unbind: function (renderer) { + renderer.gl.bindTexture(renderer.gl.TEXTURE_2D, null); }, - /** - * Disable all textures used in the shader - */ - disableTexturesAll: function () { - var textureStatus = this._textureStatus; - for (var symbol in textureStatus) { - textureStatus[symbol].enabled = false; + + load: function (src, crossOrigin) { + var image = new Image(); + if (crossOrigin) { + image.crossOrigin = crossOrigin; } + 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; + }; - this._programKey = ''; - }, - /** - * If texture of given type is enabled. - * @param {string} symbol - * @return {boolean} - */ - isTextureEnabled: function (symbol) { - var textureStatus = this._textureStatus; - return !!textureStatus[symbol] - && textureStatus[symbol].enabled; - }, + image.src = src; + this.image = image; - /** - * Get all enabled textures - * @return {string[]} - */ - getEnabledTextures: function () { - var enabledTextures = []; - var textureStatus = this._textureStatus; - for (var symbol in textureStatus) { - if (textureStatus[symbol].enabled) { - enabledTextures.push(symbol); - } + return this; + } +}); + +Object.defineProperty(Texture2D.prototype, 'width', { + get: function () { + if (this.image) { + return this.image.width; } - return enabledTextures; + return this._width; }, - - /** - * Mark defines are updated. - */ - dirtyDefines: function () { - this._programKey = ''; + set: function (value) { + if (this.image) { + console.warn('Texture from image can\'t set width'); + } + else { + if (this._width !== value) { + this.dirty(); + } + this._width = value; + } + } +}); +Object.defineProperty(Texture2D.prototype, 'height', { + get: function () { + if (this.image) { + return this.image.height; + } + return this._height; }, - - getProgramKey: function () { - if (!this._programKey) { - this._programKey = getProgramKey( - this.vertexDefines, this.fragmentDefines, this.getEnabledTextures() - ); + set: function (value) { + if (this.image) { + console.warn('Texture from image can\'t set height'); + } + else { + if (this._height !== value) { + this.dirty(); + } + this._height = value; } - return this._programKey; } }); @@ -13796,7 +14117,7 @@ var lightEssl = [ exportEnd ].join('\n'); -var prezGlsl = "@export clay.prez.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nattribute vec3 position : POSITION;\n@import clay.chunk.skinning_header\nvoid main()\n{\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n}\n@end\n@export clay.prez.fragment\nvoid main()\n{\n gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\n}\n@end"; +var prezGlsl = "@export clay.prez.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n@import clay.chunk.skinning_header\nvarying vec2 v_Texcoord;\nvoid main()\n{\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n v_Texcoord = texcoord;\n}\n@end\n@export clay.prez.fragment\nuniform sampler2D alphaMap;\nuniform float alphaCutoff: 0.0;\nvarying vec2 v_Texcoord;\nvoid main()\n{\n if (alphaCutoff > 0.0) {\n if (texture2D(alphaMap, v_Texcoord).a <= alphaCutoff) {\n discard;\n }\n }\n gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\n}\n@end"; // TODO Resources like shader, texture, geometry reference management // Trace and find out which shader, texture, geometry can be destroyed @@ -13814,6 +14135,12 @@ var errorShader = {}; function defaultGetMaterial(renderable) { return renderable.material; } +function defaultGetUniform(renderable, material, symbol) { + return material.uniforms[symbol].value; +} +function defaultIsMaterialChanged(renderabled, prevRenderable, material, prevMaterial) { + return material !== prevMaterial; +} function noop$1() {} @@ -13929,6 +14256,14 @@ var Renderer = Base.extend(function () { */ viewport: {}, + /** + * Canvas creator + * @type {Function} + */ + createCanvas: function () { + return document.createElement('canvas'); + }, + // Set by FrameBuffer#bind __currentFrameBuffer: null, @@ -13940,7 +14275,7 @@ var Renderer = Base.extend(function () { }, function () { if (!this.canvas) { - this.canvas = document.createElement('canvas'); + this.canvas = this.createCanvas(); } var canvas = this.canvas; try { @@ -13975,6 +14310,13 @@ var Renderer = Base.extend(function () { // Init managers this._programMgr = new ProgramManager(this); + + var blankCanvas = this.createCanvas(); + blankCanvas.width = blankCanvas.height = 1; + var ctx = blankCanvas.getContext('2d'); + this._placeholderTexture = new Texture2D({ + canvas: blankCanvas + }); }, /** @lends clay.Renderer.prototype. **/ { @@ -14204,7 +14546,7 @@ var Renderer = Base.extend(function () { return; } camera.update(); - var renderList = scene.updateRenderList(camera); + var renderList = scene.updateRenderList(camera, true); this._sceneRendering = scene; @@ -14311,6 +14653,8 @@ var Renderer = Base.extend(function () { * @param {clay.Camera} camera * @param {Object} [passConfig] * @param {Function} [passConfig.getMaterial] Get renderable material. + * @param {Function} [passConfig.getUniform] Get material uniform value. + * @param {Function} [passConfig.isMaterialChanged] If material changed. * @param {Function} [passConfig.beforeRender] Before render each renderable. * @param {Function} [passConfig.afterRender] After render each renderable * @param {Function} [passConfig.ifRender] If render the renderable. @@ -14322,6 +14666,9 @@ var Renderer = Base.extend(function () { passConfig = passConfig || {}; passConfig.getMaterial = passConfig.getMaterial || defaultGetMaterial; + passConfig.getUniform = passConfig.getUniform || defaultGetUniform; + // PENDING Better solution? + passConfig.isMaterialChanged = passConfig.isMaterialChanged || defaultIsMaterialChanged; passConfig.beforeRender = passConfig.beforeRender || noop$1; passConfig.afterRender = passConfig.afterRender || noop$1; @@ -14360,6 +14707,7 @@ var Renderer = Base.extend(function () { var prevMaterial; var prevProgram; + var prevRenderable; // Status var depthTest, depthMask; @@ -14437,7 +14785,9 @@ var Renderer = Base.extend(function () { } // Program changes also needs reset the materials. - if (prevMaterial !== material || programChanged) { + if (programChanged || passConfig.isMaterialChanged( + renderable, prevRenderable, material, prevMaterial + )) { if (material.depthTest !== depthTest) { material.depthTest ? _gl.enable(_gl.DEPTH_TEST) : _gl.disable(_gl.DEPTH_TEST); depthTest = material.depthTest; @@ -14462,7 +14812,11 @@ var Renderer = Base.extend(function () { } } - this._bindMaterial(material, program, prevMaterial || null, prevProgram || null); + this._bindMaterial( + renderable, material, program, + prevRenderable || null, prevMaterial || null, prevProgram || null, + passConfig.getUniform + ); prevMaterial = material; } @@ -14502,6 +14856,7 @@ var Renderer = Base.extend(function () { renderable.afterRender(this); prevProgram = program; + prevRenderable = renderable; } // TODO Seems need to be bound to null immediately if vao is changed? @@ -14546,7 +14901,7 @@ var Renderer = Base.extend(function () { } }, - _bindMaterial: function (material, program, prevMaterial, prevProgram) { + _bindMaterial: function (renderable, material, program, prevRenderable, prevMaterial, prevProgram, getUniformValue) { var _gl = this.gl; // PENDING Same texture in different material take different slot? @@ -14556,10 +14911,11 @@ var Renderer = Base.extend(function () { var currentTextureSlot = program.currentTextureSlot(); var enabledUniforms = material.getEnabledUniforms(); var textureUniforms = material.getTextureUniforms(); + var placeholderTexture = this._placeholderTexture; for (var u = 0; u < textureUniforms.length; u++) { var symbol = textureUniforms[u]; - var uniformValue = material.uniforms[symbol].value; + var uniformValue = getUniformValue(renderable, material, symbol); var uniformType = material.uniforms[symbol].type; // Not use `instanceof` to determine if a value is texture in Material#bind. // Use type instead, in some case texture may be in different namespaces. @@ -14570,52 +14926,64 @@ var Renderer = Base.extend(function () { } else if (uniformType === 'tv') { for (var i = 0; i < uniformValue.length; i++) { - if (uniformValue[i] instanceof Texture) { + if (uniformValue[i]) { uniformValue[i].__slot = -1; } } } } + placeholderTexture.__slot = -1; + // Set uniforms for (var u = 0; u < enabledUniforms.length; u++) { var symbol = enabledUniforms[u]; var uniform = material.uniforms[symbol]; - var uniformValue = uniform.value; + var uniformValue = getUniformValue(renderable, material, symbol); + var uniformType = uniform.type; + + if (uniformType === 't') { + if (!uniformValue || !uniformValue.isRenderable()) { + uniformValue = placeholderTexture; + } + } // PENDING // 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 && sameProgram) { - if (prevMaterial.uniforms[symbol].value === uniformValue) { - continue; + var prevUniformValue = getUniformValue(prevRenderable, prevMaterial, symbol); + if (uniformType === 't') { + if (!prevUniformValue || !prevUniformValue.isRenderable()) { + prevUniformValue = placeholderTexture; + } } - } - var uniformType = uniform.type; - 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 = program.currentTextureSlot(); - var res = program.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. + if (prevUniformValue === uniformValue) { + if (uniform.type === 't') { + // Still take the slot to make sure same texture in different materials have same slot. program.takeCurrentTextureSlot(this, null); } + else if (uniformType === 'tv' && uniformValue) { + for (var i = 0; i < uniformValue.length; i++) { + program.takeCurrentTextureSlot(this, null); + } + } + continue; } + } + + if (uniformValue == null) { continue; } else if (uniformType === 't') { if (uniformValue.__slot < 0) { var slot = program.currentTextureSlot(); var res = program.setUniform(_gl, '1i', symbol, slot); - if (!res) { // Texture uniform is not enabled - continue; + if (res) { // Texture uniform is enabled + program.takeCurrentTextureSlot(this, uniformValue); + uniformValue.__slot = slot; } - program.takeCurrentTextureSlot(this, uniformValue); - uniformValue.__slot = slot; } // Multiple uniform use same texture.. else { @@ -14766,6 +15134,29 @@ var Renderer = Base.extend(function () { ifRender: function (renderable) { return !renderable.ignorePreZ; }, + isMaterialChanged: function (renderable, prevRenderable) { + var matA = renderable.material; + var matB = prevRenderable.material; + return matA.get('diffuseMap') !== matB.get('diffuseMap') + || (matA.get('alphaCutoff') || 0) !== (matB.get('alphaCutoff') || 0); + }, + getUniform: function (renderable, depthMaterial, symbol) { + if (symbol === 'alphaMap') { + return renderable.material.get('diffuseMap'); + } + else if (symbol === 'alphaCutoff') { + if (renderable.material.isDefined('fragment', 'ALPHA_TEST') + && renderable.material.get('diffuseMap') + ) { + var alphaCutoff = renderable.material.get('alphaCutoff'); + return alphaCutoff || 0; + } + return 0; + } + else { + return depthMaterial.get(symbol); + } + }, getMaterial: function () { return preZPassMaterial; }, @@ -17518,9 +17909,10 @@ var Scene = Node.extend(function () { * It needs camera for the frustum culling. * * @param {clay.Camera} camera + * @param {boolean} updateSceneBoundingBox * @return {clay.Scene.RenderList} */ - updateRenderList: function (camera) { + updateRenderList: function (camera, updateSceneBoundingBox) { var id = camera.__uid__; var renderList = this._renderLists.get(id); if (!renderList) { @@ -17529,11 +17921,13 @@ var Scene = Node.extend(function () { } renderList.startCount(); - this.viewBoundingBoxLastFrame.min.set(Infinity, Infinity, Infinity); - this.viewBoundingBoxLastFrame.max.set(-Infinity, -Infinity, -Infinity); + if (updateSceneBoundingBox) { + this.viewBoundingBoxLastFrame.min.set(Infinity, Infinity, Infinity); + this.viewBoundingBoxLastFrame.max.set(-Infinity, -Infinity, -Infinity); + } var sceneMaterialTransparent = this.material && this.material.transparent || false; - this._doUpdateRenderList(this, camera, sceneMaterialTransparent, renderList); + this._doUpdateRenderList(this, camera, sceneMaterialTransparent, renderList, updateSceneBoundingBox); renderList.endCount(); @@ -17549,7 +17943,7 @@ var Scene = Node.extend(function () { return this._renderLists.get(camera.__uid__); }, - _doUpdateRenderList: function (parent, camera, sceneMaterialTransparent, renderList) { + _doUpdateRenderList: function (parent, camera, sceneMaterialTransparent, renderList, updateSceneBoundingBox) { if (parent.invisible) { return; } @@ -17563,12 +17957,12 @@ var Scene = Node.extend(function () { var geometry = child.geometry; mat4$3.multiplyAffine(WORLDVIEW, camera.viewMatrix.array, worldM); - if (!geometry.boundingBox || !this.isFrustumCulled(child, camera, WORLDVIEW)) { + if (updateSceneBoundingBox && !geometry.boundingBox || !this.isFrustumCulled(child, camera, WORLDVIEW)) { renderList.add(child, child.material.transparent || sceneMaterialTransparent); } } if (child._children.length > 0) { - this._doUpdateRenderList(child, camera, sceneMaterialTransparent, renderList); + this._doUpdateRenderList(child, camera, sceneMaterialTransparent, renderList, updateSceneBoundingBox); } } }, @@ -19322,337 +19716,65 @@ var ParametricSurface$1 = Geometry.extend( } var xFunc = generator.x; var yFunc = generator.y; - var zFunc = generator.z; - var uRange = generator.u || [0, 1, 0.05]; - var vRange = generator.v || [0, 1, 0.05]; - - var uNum = Math.floor((uRange[1] - uRange[0] + uRange[2]) / uRange[2]); - var vNum = Math.floor((vRange[1] - vRange[0] + vRange[2]) / vRange[2]); - - if (!isFinite(uNum) || !isFinite(vNum)) { - throw new Error('Infinite generator'); - } - - var vertexNum = uNum * vNum; - this.attributes.position.init(vertexNum); - this.attributes.texcoord0.init(vertexNum); - - var pos = []; - var texcoord = []; - var nVertex = 0; - for (var j = 0; j < vNum; j++) { - for (var i = 0; i < uNum; i++) { - var u = i * uRange[2] + uRange[0]; - var v = j * vRange[2] + vRange[0]; - pos[0] = xFunc(u, v); - pos[1] = yFunc(u, v); - pos[2] = zFunc(u, v); - - texcoord[0] = i / (uNum - 1); - texcoord[1] = j / (vNum - 1); - - this.attributes.position.set(nVertex, pos); - this.attributes.texcoord0.set(nVertex, texcoord); - nVertex++; - } - } - - var IndicesCtor = vertexNum > 0xffff ? Uint32Array : Uint16Array; - var nIndices = (uNum - 1) * (vNum - 1) * 6; - var indices = this.indices = new IndicesCtor(nIndices); - - var n = 0; - for (var j = 0; j < vNum - 1; j++) { - for (var i = 0; i < uNum - 1; i++) { - var i2 = j * uNum + i; - var i1 = (j * uNum + i + 1); - var i4 = (j + 1) * uNum + i + 1; - var i3 = (j + 1) * uNum + i; - - indices[n++] = i1; - indices[n++] = i2; - indices[n++] = i4; - - indices[n++] = i2; - indices[n++] = i3; - indices[n++] = i4; - } - } - - this.generateVertexNormals(); - this.updateBoundingBox(); - } -}); - -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 ) ); -}; - -var isPowerOfTwo = mathUtil.isPowerOfTwo; - -function nearestPowerOfTwo(val) { - return Math.pow(2, Math.round(Math.log(val) / Math.LN2)); -} -function convertTextureToPowerOfTwo(texture, canvas) { - // var canvas = document.createElement('canvas'); - var width = nearestPowerOfTwo(texture.width); - var height = nearestPowerOfTwo(texture.height); - canvas = canvas || document.createElement('canvas'); - canvas.width = width; - canvas.height = height; - var ctx = canvas.getContext('2d'); - ctx.drawImage(texture.image, 0, 0, width, height); - - return canvas; -} - -/** - * @constructor clay.Texture2D - * @extends clay.Texture - * - * @example - * ... - * var mat = new clay.Material({ - * shader: clay.shader.library.get('clay.phong', 'diffuseMap') - * }); - * var diffuseMap = new clay.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$1.extend(function () { - return /** @lends clay.Texture2D# */ { - /** - * @type {?HTMLImageElement|HTMLCanvasElemnet} - */ - image: null, - /** - * Pixels data. Will be ignored if image is set. - * @type {?Uint8Array|Float32Array} - */ - pixels: null, - /** - * @type {Array.} - * @example - * [{ - * image: mipmap0, - * pixels: null - * }, { - * image: mipmap1, - * pixels: null - * }, ....] - */ - mipmaps: [], - - /** - * If convert texture to power-of-two - * @type {boolean} - */ - convertToPOT: false - }; -}, { - - textureType: 'texture2D', - - update: function (renderer) { - - var _gl = renderer.gl; - _gl.bindTexture(_gl.TEXTURE_2D, this._cache.get('webgl_texture')); - - this.updateCommon(renderer); - - var glFormat = this.format; - var glType = this.type; - - // Convert to pot is only available when using image/canvas/video element. - var convertToPOT = !!(this.convertToPOT - && !this.mipmaps.length && this.image - && (this.wrapS === Texture$1.REPEAT || this.wrapT === Texture$1.REPEAT) - && this.NPOT - ); - - _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, convertToPOT ? this.wrapS : this.getAvailableWrapS()); - _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, convertToPOT ? this.wrapT : this.getAvailableWrapT()); - - _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, convertToPOT ? this.magFilter : this.getAvailableMagFilter()); - _gl.texParameteri(_gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, convertToPOT ? this.minFilter : this.getAvailableMinFilter()); - - var anisotropicExt = renderer.getGLExtension('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 = renderer.getGLExtension('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, false); - width /= 2; - height /= 2; - } - } - else { - this._updateTextureData(_gl, this, 0, this.width, this.height, glFormat, glType, convertToPOT); - - if (this.useMipmap && (!this.NPOT || convertToPOT)) { - _gl.generateMipmap(_gl.TEXTURE_2D); - } - } - - _gl.bindTexture(_gl.TEXTURE_2D, null); - }, - - _updateTextureData: function (_gl, data, level, width, height, glFormat, glType, convertToPOT) { - if (data.image) { - var imgData = data.image; - if (convertToPOT) { - this._potCanvas = convertTextureToPowerOfTwo(this, this._potCanvas); - imgData = this._potCanvas; - } - _gl.texImage2D(_gl.TEXTURE_2D, level, glFormat, glFormat, glType, imgData); - } - else { - // Can be used as a blank texture when writing render to texture(RTT) - if ( - glFormat <= Texture$1.COMPRESSED_RGBA_S3TC_DXT5_EXT - && glFormat >= Texture$1.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 {clay.Renderer} renderer - * @memberOf clay.Texture2D.prototype - */ - generateMipmap: function (renderer) { - var _gl = renderer.gl; - if (this.useMipmap && !this.NPOT) { - _gl.bindTexture(_gl.TEXTURE_2D, this._cache.get('webgl_texture')); - _gl.generateMipmap(_gl.TEXTURE_2D); - } - }, + var zFunc = generator.z; + var uRange = generator.u || [0, 1, 0.05]; + var vRange = generator.v || [0, 1, 0.05]; - isPowerOfTwo: function () { - return isPowerOfTwo(this.width) && isPowerOfTwo(this.height); - }, + var uNum = Math.floor((uRange[1] - uRange[0] + uRange[2]) / uRange[2]); + var vNum = Math.floor((vRange[1] - vRange[0] + vRange[2]) / vRange[2]); - isRenderable: function () { - if (this.image) { - return this.image.nodeName === 'CANVAS' - || this.image.nodeName === 'VIDEO' - || this.image.complete; - } - else { - return !!(this.width && this.height); + if (!isFinite(uNum) || !isFinite(vNum)) { + throw new Error('Infinite generator'); } - }, - bind: function (renderer) { - renderer.gl.bindTexture(renderer.gl.TEXTURE_2D, this.getWebGLTexture(renderer)); - }, + var vertexNum = uNum * vNum; + this.attributes.position.init(vertexNum); + this.attributes.texcoord0.init(vertexNum); - unbind: function (renderer) { - renderer.gl.bindTexture(renderer.gl.TEXTURE_2D, null); - }, + var pos = []; + var texcoord = []; + var nVertex = 0; + for (var j = 0; j < vNum; j++) { + for (var i = 0; i < uNum; i++) { + var u = i * uRange[2] + uRange[0]; + var v = j * vRange[2] + vRange[0]; + pos[0] = xFunc(u, v); + pos[1] = yFunc(u, v); + pos[2] = zFunc(u, v); - load: function (src, crossOrigin) { - var image = new Image(); - if (crossOrigin) { - image.crossOrigin = crossOrigin; + texcoord[0] = i / (uNum - 1); + texcoord[1] = j / (vNum - 1); + + this.attributes.position.set(nVertex, pos); + this.attributes.texcoord0.set(nVertex, texcoord); + nVertex++; + } } - 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; + var IndicesCtor = vertexNum > 0xffff ? Uint32Array : Uint16Array; + var nIndices = (uNum - 1) * (vNum - 1) * 6; + var indices = this.indices = new IndicesCtor(nIndices); - return this; - } -}); + var n = 0; + for (var j = 0; j < vNum - 1; j++) { + for (var i = 0; i < uNum - 1; i++) { + var i2 = j * uNum + i; + var i1 = (j * uNum + i + 1); + var i4 = (j + 1) * uNum + i + 1; + var i3 = (j + 1) * uNum + i; -Object.defineProperty(Texture2D.prototype, 'width', { - get: function () { - if (this.image) { - return this.image.width; - } - return this._width; - }, - set: function (value) { - if (this.image) { - console.warn('Texture from image can\'t set width'); - } - else { - if (this._width !== value) { - this.dirty(); - } - this._width = value; - } - } -}); -Object.defineProperty(Texture2D.prototype, 'height', { - get: function () { - if (this.image) { - return this.image.height; - } - return this._height; - }, - set: function (value) { - if (this.image) { - console.warn('Texture from image can\'t set height'); - } - else { - if (this._height !== value) { - this.dirty(); + indices[n++] = i1; + indices[n++] = i2; + indices[n++] = i4; + + indices[n++] = i2; + indices[n++] = i3; + indices[n++] = i4; } - this._height = value; } + + this.generateVertexNormals(); + this.updateBoundingBox(); } }); @@ -19687,7 +19809,7 @@ var targetList = ['px', 'nx', 'py', 'ny', 'pz', 'nz']; * }); * }); */ -var TextureCube = Texture$1.extend(function () { +var TextureCube = Texture.extend(function () { return /** @lends clay.TextureCube# */{ /** @@ -20438,7 +20560,7 @@ var request = { get : get }; -var standardEssl = "\n@export clay.standard.chunk.varying\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\n#if defined(PARALLAXOCCLUSIONMAP_ENABLED) || defined(NORMALMAP_ENABLED)\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n#if defined(AOMAP_ENABLED)\nvarying vec2 v_Texcoord2;\n#endif\n#ifdef VERTEX_COLOR\nvarying vec4 v_Color;\n#endif\n@end\n@export clay.standard.chunk.light_header\n#ifdef AMBIENT_LIGHT_COUNT\n@import clay.header.ambient_light\n#endif\n#ifdef AMBIENT_SH_LIGHT_COUNT\n@import clay.header.ambient_sh_light\n#endif\n#ifdef AMBIENT_CUBEMAP_LIGHT_COUNT\n@import clay.header.ambient_cubemap_light\n#endif\n#ifdef POINT_LIGHT_COUNT\n@import clay.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_COUNT\n@import clay.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_COUNT\n@import clay.header.spot_light\n#endif\n@end\n@export clay.standard.vertex\n#define SHADER_NAME standard\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n#if defined(AOMAP_ENABLED)\nattribute vec2 texcoord2 : TEXCOORD_1;\n#endif\nattribute vec3 normal : NORMAL;\nattribute vec4 tangent : TANGENT;\n#ifdef VERTEX_COLOR\nattribute vec4 a_Color : COLOR;\n#endif\nattribute vec3 barycentric;\n@import clay.standard.chunk.varying\n@import clay.chunk.skinning_header\nvoid main()\n{\n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\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 gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n v_Barycentric = barycentric;\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\n#if defined(PARALLAXOCCLUSIONMAP_ENABLED) || defined(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#ifdef VERTEX_COLOR\n v_Color = a_Color;\n#endif\n#if defined(AOMAP_ENABLED)\n v_Texcoord2 = texcoord2;\n#endif\n}\n@end\n@export clay.standard.fragment\n#define PI 3.14159265358979\n#define GLOSSINESS_CHANNEL 0\n#define ROUGHNESS_CHANNEL 0\n#define METALNESS_CHANNEL 1\n@import clay.standard.chunk.varying\nuniform mat4 viewInverse : VIEWINVERSE;\n#ifdef NORMALMAP_ENABLED\nuniform sampler2D normalMap;\n#endif\n#ifdef DIFFUSEMAP_ENABLED\nuniform sampler2D diffuseMap;\n#endif\n#ifdef SPECULARMAP_ENABLED\nuniform sampler2D specularMap;\n#endif\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 GLOSSINESSMAP_ENABLED\nuniform sampler2D glossinessMap;\n #endif\n#endif\n#ifdef METALNESSMAP_ENABLED\nuniform sampler2D metalnessMap;\n#endif\n#ifdef ENVIRONMENTMAP_ENABLED\nuniform samplerCube environmentMap;\n #ifdef PARALLAX_CORRECTED\nuniform vec3 environmentBoxMin;\nuniform vec3 environmentBoxMax;\n #endif\n#endif\n#ifdef BRDFLOOKUP_ENABLED\nuniform sampler2D brdfLookup;\n#endif\n#ifdef EMISSIVEMAP_ENABLED\nuniform sampler2D emissiveMap;\n#endif\n#ifdef SSAOMAP_ENABLED\nuniform sampler2D ssaoMap;\nuniform vec4 viewport : VIEWPORT;\n#endif\n#ifdef AOMAP_ENABLED\nuniform sampler2D aoMap;\nuniform float aoIntensity;\n#endif\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\n#ifdef ALPHA_TEST\nuniform float alphaCutoff: 0.9;\n#endif\n#ifdef USE_METALNESS\nuniform float metalness : 0.0;\n#else\nuniform vec3 specularColor : [0.1, 0.1, 0.1];\n#endif\nuniform vec3 emission : [0.0, 0.0, 0.0];\nuniform float emissionIntensity: 1;\nuniform float lineWidth : 0.0;\nuniform vec4 lineColor : [0.0, 0.0, 0.0, 0.6];\n#ifdef ENVIRONMENTMAP_PREFILTER\nuniform float maxMipmapLevel: 5;\n#endif\n@import clay.standard.chunk.light_header\n@import clay.util.calculate_attenuation\n@import clay.util.edge_factor\n@import clay.util.rgbm\n@import clay.util.srgb\n@import clay.plugin.compute_shadow_map\n@import clay.util.parallax_correct\n@import clay.util.ACES\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}\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}\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#ifdef PARALLAXOCCLUSIONMAP_ENABLED\nuniform float parallaxOcclusionScale : 0.02;\nuniform float parallaxMaxLayers : 20;\nuniform float parallaxMinLayers : 5;\nuniform sampler2D parallaxOcclusionMap;\nmat3 transpose(in mat3 inMat)\n{\n vec3 i0 = inMat[0];\n vec3 i1 = inMat[1];\n vec3 i2 = inMat[2];\n return mat3(\n vec3(i0.x, i1.x, i2.x),\n vec3(i0.y, i1.y, i2.y),\n vec3(i0.z, i1.z, i2.z)\n );\n}\nvec2 parallaxUv(vec2 uv, vec3 viewDir)\n{\n float numLayers = mix(parallaxMaxLayers, parallaxMinLayers, abs(dot(vec3(0.0, 0.0, 1.0), viewDir)));\n float layerHeight = 1.0 / numLayers;\n float curLayerHeight = 0.0;\n vec2 deltaUv = viewDir.xy * parallaxOcclusionScale / (viewDir.z * numLayers);\n vec2 curUv = uv;\n float height = 1.0 - texture2D(parallaxOcclusionMap, curUv).r;\n for (int i = 0; i < 30; i++) {\n curLayerHeight += layerHeight;\n curUv -= deltaUv;\n height = 1.0 - texture2D(parallaxOcclusionMap, curUv).r;\n if (height < curLayerHeight) {\n break;\n }\n }\n vec2 prevUv = curUv + deltaUv;\n float next = height - curLayerHeight;\n float prev = 1.0 - texture2D(parallaxOcclusionMap, prevUv).r - curLayerHeight + layerHeight;\n return mix(curUv, prevUv, next / (next - prev));\n}\n#endif\nvoid main() {\n vec4 albedoColor = vec4(color, alpha);\n#ifdef VERTEX_COLOR\n albedoColor *= v_Color;\n#endif\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = normalize(eyePos - v_WorldPosition);\n vec2 uv = v_Texcoord;\n#if defined(PARALLAXOCCLUSIONMAP_ENABLED) || defined(NORMALMAP_ENABLED)\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\n#endif\n#ifdef PARALLAXOCCLUSIONMAP_ENABLED\n uv = parallaxUv(v_Texcoord, normalize(transpose(tbn) * -V));\n#endif\n#ifdef DIFFUSEMAP_ENABLED\n vec4 texel = texture2D(diffuseMap, uv);\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#ifdef USE_METALNESS\n float m = metalness;\n #ifdef METALNESSMAP_ENABLED\n float m2 = texture2D(metalnessMap, uv)[METALNESS_CHANNEL];\n m = clamp(m2 + (m - 0.5) * 2.0, 0.0, 1.0);\n #endif\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#ifdef USE_ROUGHNESS\n float g = 1.0 - roughness;\n #ifdef ROUGHNESSMAP_ENABLED\n float g2 = 1.0 - texture2D(roughnessMap, uv)[ROUGHNESS_CHANNEL];\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\n #endif\n#else\n float g = glossiness;\n #ifdef GLOSSINESSMAP_ENABLED\n float g2 = texture2D(glossinessMap, uv)[GLOSSINESS_CHANNEL];\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\n #endif\n#endif\n#ifdef SPECULARMAP_ENABLED\n spec *= sRGBToLinear(texture2D(specularMap, uv)).rgb;\n#endif\n vec3 N = v_Normal;\n#ifdef DOUBLE_SIDED\n if (dot(N, V) < 0.0) {\n N = -N;\n }\n#endif\n#ifdef NORMALMAP_ENABLED\n if (dot(v_Tangent, v_Tangent) > 0.0) {\n vec3 normalTexel = texture2D(normalMap, uv).xyz;\n if (dot(normalTexel, normalTexel) > 0.0) { N = normalTexel * 2.0 - 1.0;\n tbn[1] = -tbn[1];\n N = normalize(tbn * N);\n }\n }\n#endif\n vec3 diffuseTerm = vec3(0.0, 0.0, 0.0);\n vec3 specularTerm = vec3(0.0, 0.0, 0.0);\n float ndv = clamp(dot(N, V), 0.0, 1.0);\n vec3 fresnelTerm = F_Schlick(ndv, spec);\n#ifdef AMBIENT_LIGHT_COUNT\n for(int _idx_ = 0; _idx_ < AMBIENT_LIGHT_COUNT; _idx_++)\n {{\n diffuseTerm += ambientLightColor[_idx_];\n }}\n#endif\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#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 vec3 lightPosition = pointLightPosition[_idx_];\n vec3 lc = pointLightColor[_idx_];\n float range = pointLightRange[_idx_];\n vec3 L = lightPosition - v_WorldPosition;\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 float shadowContrib = 1.0;\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribsPoint[_idx_];\n }\n#endif\n vec3 li = lc * ndl * attenuation * shadowContrib;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\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 _idx_ = 0; _idx_ < DIRECTIONAL_LIGHT_COUNT; _idx_++)\n {{\n vec3 L = -normalize(directionalLightDirection[_idx_]);\n vec3 lc = directionalLightColor[_idx_];\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 shadowContrib = 1.0;\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribsDir[_idx_];\n }\n#endif\n vec3 li = lc * ndl * shadowContrib;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }}\n#endif\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 vec3 L = lightPosition - v_WorldPosition;\n float dist = length(L);\n float attenuation = lightAttenuation(dist, range);\n L /= dist;\n float c = dot(spotLightDirection, L);\n float falloff;\n falloff = clamp((c - a) /(b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\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 shadowContrib = 1.0;\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\n if (shadowEnabled)\n {\n shadowContrib = shadowContribsSpot[i];\n }\n#endif\n vec3 li = lc * attenuation * (1.0 - falloff) * shadowContrib * ndl;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }\n#endif\n vec4 outColor = albedoColor;\n outColor.rgb *= max(diffuseTerm, vec3(0.0));\n outColor.rgb += max(specularTerm, vec3(0.0));\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), 8.12);\n outColor.rgb += ambientCubemapLightColor[_idx_] * envTexel2 * envWeight2;\n }}\n#endif\n#ifdef ENVIRONMENTMAP_ENABLED\n vec3 envWeight = g * fresnelTerm;\n vec3 L = reflect(-V, N);\n #ifdef PARALLAX_CORRECTED\n L = parallaxCorrect(L, v_WorldPosition, environmentBoxMin, environmentBoxMax);\n#endif\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 #ifdef BRDFLOOKUP_ENABLED\n vec2 brdfParam = texture2D(brdfLookup, vec2(rough, ndv)).xy;\n envWeight = spec * brdfParam.x + brdfParam.y;\n #endif\n #else\n vec3 envTexel = textureCube(environmentMap, L).xyz;\n #endif\n outColor.rgb += envTexel * envWeight;\n#endif\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#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 outColor.rgb *= aoFactor;\n vec3 lEmission = emission;\n#ifdef EMISSIVEMAP_ENABLED\n lEmission *= texture2D(emissiveMap, uv).rgb;\n#endif\n outColor.rgb += lEmission * emissionIntensity;\n if(lineWidth > 0.)\n {\n outColor.rgb = mix(outColor.rgb, lineColor.rgb, (1.0 - edgeFactor(lineWidth)) * lineColor.a);\n }\n#ifdef ALPHA_TEST\n if (outColor.a < alphaCutoff) {\n discard;\n }\n#endif\n#ifdef TONEMAPPING\n outColor.rgb = ACESToneMapping(outColor.rgb);\n#endif\n#ifdef SRGB_ENCODE\n outColor = linearTosRGB(outColor);\n#endif\n gl_FragColor = encodeHDR(outColor);\n}\n@end\n@export clay.standardMR.vertex\n@import clay.standard.vertex\n@end\n@export clay.standardMR.fragment\n#define USE_METALNESS\n#define USE_ROUGHNESS\n@import clay.standard.fragment\n@end"; +var standardEssl = "\n@export clay.standard.chunk.varying\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\n#if defined(PARALLAXOCCLUSIONMAP_ENABLED) || defined(NORMALMAP_ENABLED)\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n#if defined(AOMAP_ENABLED)\nvarying vec2 v_Texcoord2;\n#endif\n#ifdef VERTEX_COLOR\nvarying vec4 v_Color;\n#endif\n@end\n@export clay.standard.chunk.light_header\n#ifdef AMBIENT_LIGHT_COUNT\n@import clay.header.ambient_light\n#endif\n#ifdef AMBIENT_SH_LIGHT_COUNT\n@import clay.header.ambient_sh_light\n#endif\n#ifdef AMBIENT_CUBEMAP_LIGHT_COUNT\n@import clay.header.ambient_cubemap_light\n#endif\n#ifdef POINT_LIGHT_COUNT\n@import clay.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_COUNT\n@import clay.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_COUNT\n@import clay.header.spot_light\n#endif\n@end\n@export clay.standard.vertex\n#define SHADER_NAME standard\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n#if defined(AOMAP_ENABLED)\nattribute vec2 texcoord2 : TEXCOORD_1;\n#endif\nattribute vec3 normal : NORMAL;\nattribute vec4 tangent : TANGENT;\n#ifdef VERTEX_COLOR\nattribute vec4 a_Color : COLOR;\n#endif\nattribute vec3 barycentric;\n@import clay.standard.chunk.varying\n@import clay.chunk.skinning_header\nvoid main()\n{\n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\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 gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n v_Barycentric = barycentric;\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\n#if defined(PARALLAXOCCLUSIONMAP_ENABLED) || defined(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#ifdef VERTEX_COLOR\n v_Color = a_Color;\n#endif\n#if defined(AOMAP_ENABLED)\n v_Texcoord2 = texcoord2;\n#endif\n}\n@end\n@export clay.standard.fragment\n#define PI 3.14159265358979\n#define GLOSSINESS_CHANNEL 0\n#define ROUGHNESS_CHANNEL 0\n#define METALNESS_CHANNEL 1\n#define DIFFUSEMAP_ALPHA_ALPHA\n@import clay.standard.chunk.varying\nuniform mat4 viewInverse : VIEWINVERSE;\n#ifdef NORMALMAP_ENABLED\nuniform sampler2D normalMap;\n#endif\n#ifdef DIFFUSEMAP_ENABLED\nuniform sampler2D diffuseMap;\n#endif\n#ifdef SPECULARMAP_ENABLED\nuniform sampler2D specularMap;\n#endif\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 GLOSSINESSMAP_ENABLED\nuniform sampler2D glossinessMap;\n #endif\n#endif\n#ifdef METALNESSMAP_ENABLED\nuniform sampler2D metalnessMap;\n#endif\n#ifdef ENVIRONMENTMAP_ENABLED\nuniform samplerCube environmentMap;\n #ifdef PARALLAX_CORRECTED\nuniform vec3 environmentBoxMin;\nuniform vec3 environmentBoxMax;\n #endif\n#endif\n#ifdef BRDFLOOKUP_ENABLED\nuniform sampler2D brdfLookup;\n#endif\n#ifdef EMISSIVEMAP_ENABLED\nuniform sampler2D emissiveMap;\n#endif\n#ifdef SSAOMAP_ENABLED\nuniform sampler2D ssaoMap;\nuniform vec4 viewport : VIEWPORT;\n#endif\n#ifdef AOMAP_ENABLED\nuniform sampler2D aoMap;\nuniform float aoIntensity;\n#endif\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\n#ifdef ALPHA_TEST\nuniform float alphaCutoff: 0.9;\n#endif\n#ifdef USE_METALNESS\nuniform float metalness : 0.0;\n#else\nuniform vec3 specularColor : [0.1, 0.1, 0.1];\n#endif\nuniform vec3 emission : [0.0, 0.0, 0.0];\nuniform float emissionIntensity: 1;\nuniform float lineWidth : 0.0;\nuniform vec4 lineColor : [0.0, 0.0, 0.0, 0.6];\n#ifdef ENVIRONMENTMAP_PREFILTER\nuniform float maxMipmapLevel: 5;\n#endif\n@import clay.standard.chunk.light_header\n@import clay.util.calculate_attenuation\n@import clay.util.edge_factor\n@import clay.util.rgbm\n@import clay.util.srgb\n@import clay.plugin.compute_shadow_map\n@import clay.util.parallax_correct\n@import clay.util.ACES\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}\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}\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#ifdef PARALLAXOCCLUSIONMAP_ENABLED\nuniform float parallaxOcclusionScale : 0.02;\nuniform float parallaxMaxLayers : 20;\nuniform float parallaxMinLayers : 5;\nuniform sampler2D parallaxOcclusionMap;\nmat3 transpose(in mat3 inMat)\n{\n vec3 i0 = inMat[0];\n vec3 i1 = inMat[1];\n vec3 i2 = inMat[2];\n return mat3(\n vec3(i0.x, i1.x, i2.x),\n vec3(i0.y, i1.y, i2.y),\n vec3(i0.z, i1.z, i2.z)\n );\n}\nvec2 parallaxUv(vec2 uv, vec3 viewDir)\n{\n float numLayers = mix(parallaxMaxLayers, parallaxMinLayers, abs(dot(vec3(0.0, 0.0, 1.0), viewDir)));\n float layerHeight = 1.0 / numLayers;\n float curLayerHeight = 0.0;\n vec2 deltaUv = viewDir.xy * parallaxOcclusionScale / (viewDir.z * numLayers);\n vec2 curUv = uv;\n float height = 1.0 - texture2D(parallaxOcclusionMap, curUv).r;\n for (int i = 0; i < 30; i++) {\n curLayerHeight += layerHeight;\n curUv -= deltaUv;\n height = 1.0 - texture2D(parallaxOcclusionMap, curUv).r;\n if (height < curLayerHeight) {\n break;\n }\n }\n vec2 prevUv = curUv + deltaUv;\n float next = height - curLayerHeight;\n float prev = 1.0 - texture2D(parallaxOcclusionMap, prevUv).r - curLayerHeight + layerHeight;\n return mix(curUv, prevUv, next / (next - prev));\n}\n#endif\nvoid main() {\n vec4 albedoColor = vec4(color, alpha);\n#ifdef VERTEX_COLOR\n albedoColor *= v_Color;\n#endif\n#ifdef SRGB_DECODE\n albedoColor = sRGBToLinear(albedoColor);\n#endif\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = normalize(eyePos - v_WorldPosition);\n vec2 uv = v_Texcoord;\n#if defined(PARALLAXOCCLUSIONMAP_ENABLED) || defined(NORMALMAP_ENABLED)\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\n#endif\n#ifdef PARALLAXOCCLUSIONMAP_ENABLED\n uv = parallaxUv(v_Texcoord, normalize(transpose(tbn) * -V));\n#endif\n#ifdef DIFFUSEMAP_ENABLED\n vec4 texel = texture2D(diffuseMap, uv);\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#ifdef USE_METALNESS\n float m = metalness;\n #ifdef METALNESSMAP_ENABLED\n float m2 = texture2D(metalnessMap, uv)[METALNESS_CHANNEL];\n m = clamp(m2 + (m - 0.5) * 2.0, 0.0, 1.0);\n #endif\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#ifdef USE_ROUGHNESS\n float g = clamp(1.0 - roughness, 0.0, 1.0);\n #ifdef ROUGHNESSMAP_ENABLED\n float g2 = 1.0 - texture2D(roughnessMap, uv)[ROUGHNESS_CHANNEL];\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\n #endif\n#else\n float g = glossiness;\n #ifdef GLOSSINESSMAP_ENABLED\n float g2 = texture2D(glossinessMap, uv)[GLOSSINESS_CHANNEL];\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\n #endif\n#endif\n#ifdef SPECULARMAP_ENABLED\n spec *= sRGBToLinear(texture2D(specularMap, uv)).rgb;\n#endif\n vec3 N = v_Normal;\n#ifdef DOUBLE_SIDED\n if (dot(N, V) < 0.0) {\n N = -N;\n }\n#endif\n#ifdef NORMALMAP_ENABLED\n if (dot(v_Tangent, v_Tangent) > 0.0) {\n vec3 normalTexel = texture2D(normalMap, uv).xyz;\n if (dot(normalTexel, normalTexel) > 0.0) { N = normalTexel * 2.0 - 1.0;\n tbn[1] = -tbn[1];\n N = normalize(tbn * N);\n }\n }\n#endif\n vec3 diffuseTerm = vec3(0.0, 0.0, 0.0);\n vec3 specularTerm = vec3(0.0, 0.0, 0.0);\n float ndv = clamp(dot(N, V), 0.0, 1.0);\n vec3 fresnelTerm = F_Schlick(ndv, spec);\n#ifdef AMBIENT_LIGHT_COUNT\n for(int _idx_ = 0; _idx_ < AMBIENT_LIGHT_COUNT; _idx_++)\n {{\n diffuseTerm += ambientLightColor[_idx_];\n }}\n#endif\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#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 vec3 lightPosition = pointLightPosition[_idx_];\n vec3 lc = pointLightColor[_idx_];\n float range = pointLightRange[_idx_];\n vec3 L = lightPosition - v_WorldPosition;\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 float shadowContrib = 1.0;\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribsPoint[_idx_];\n }\n#endif\n vec3 li = lc * ndl * attenuation * shadowContrib;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\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 _idx_ = 0; _idx_ < DIRECTIONAL_LIGHT_COUNT; _idx_++)\n {{\n vec3 L = -normalize(directionalLightDirection[_idx_]);\n vec3 lc = directionalLightColor[_idx_];\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 shadowContrib = 1.0;\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribsDir[_idx_];\n }\n#endif\n vec3 li = lc * ndl * shadowContrib;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }}\n#endif\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 vec3 L = lightPosition - v_WorldPosition;\n float dist = length(L);\n float attenuation = lightAttenuation(dist, range);\n L /= dist;\n float c = dot(spotLightDirection, L);\n float falloff;\n falloff = clamp((c - a) /(b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\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 shadowContrib = 1.0;\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\n if (shadowEnabled)\n {\n shadowContrib = shadowContribsSpot[i];\n }\n#endif\n vec3 li = lc * attenuation * (1.0 - falloff) * shadowContrib * ndl;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }\n#endif\n vec4 outColor = albedoColor;\n outColor.rgb *= max(diffuseTerm, vec3(0.0));\n outColor.rgb += max(specularTerm, vec3(0.0));\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), 8.12);\n outColor.rgb += ambientCubemapLightColor[_idx_] * envTexel2 * envWeight2;\n }}\n#endif\n#ifdef ENVIRONMENTMAP_ENABLED\n vec3 envWeight = g * fresnelTerm;\n vec3 L = reflect(-V, N);\n #ifdef PARALLAX_CORRECTED\n L = parallaxCorrect(L, v_WorldPosition, environmentBoxMin, environmentBoxMax);\n#endif\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 #ifdef BRDFLOOKUP_ENABLED\n vec2 brdfParam = texture2D(brdfLookup, vec2(rough, ndv)).xy;\n envWeight = spec * brdfParam.x + brdfParam.y;\n #endif\n #else\n vec3 envTexel = textureCube(environmentMap, L).xyz;\n #endif\n outColor.rgb += envTexel * envWeight;\n#endif\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#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 outColor.rgb *= aoFactor;\n vec3 lEmission = emission;\n#ifdef EMISSIVEMAP_ENABLED\n lEmission *= texture2D(emissiveMap, uv).rgb;\n#endif\n outColor.rgb += lEmission * emissionIntensity;\n if(lineWidth > 0.)\n {\n outColor.rgb = mix(outColor.rgb, lineColor.rgb, (1.0 - edgeFactor(lineWidth)) * lineColor.a);\n }\n#ifdef ALPHA_TEST\n if (outColor.a < alphaCutoff) {\n discard;\n }\n#endif\n#ifdef TONEMAPPING\n outColor.rgb = ACESToneMapping(outColor.rgb);\n#endif\n#ifdef SRGB_ENCODE\n outColor = linearTosRGB(outColor);\n#endif\n gl_FragColor = encodeHDR(outColor);\n}\n@end\n@export clay.standardMR.vertex\n@import clay.standard.vertex\n@end\n@export clay.standardMR.fragment\n#define USE_METALNESS\n#define USE_ROUGHNESS\n@import clay.standard.fragment\n@end"; // Import standard shader Shader['import'](standardEssl); @@ -20475,7 +20597,7 @@ var standardShader; */ var StandardMaterial = Material.extend(function () { if (!standardShader) { - standardShader = new Shader(Shader.source('clay.standard.vertex'), Shader.source('clay.standard.fragment')); + standardShader = new Shader(Shader.source('clay.standardMR.vertex'), Shader.source('clay.standardMR.fragment')); } return /** @lends clay.StandardMaterial# */ { shader: standardShader @@ -20541,42 +20663,51 @@ var StandardMaterial = Material.extend(function () { /** * @type {clay.Texture2D} */ + diffuseMap: null, /** * @type {clay.Texture2D} */ + normalMap: null, /** * @type {clay.Texture2D} */ + roughnessMap: null, /** * @type {clay.Texture2D} */ + metalnessMap: null, /** * @type {clay.Texture2D} */ + emissiveMap: null, /** * @type {clay.TextureCube} */ + environmentMap: null, /** * @type {clay.BoundingBox} */ - + environmentBox: null, /** * BRDF Lookup is generated by clay.util.cubemap.integrateBrdf * @type {clay.Texture2D} */ + brdfLookup: null, /** * @type {clay.Texture2D} */ + ssaoMap: null, /** * @type {clay.Texture2D} */ + aoMap: null, /** * @type {Array.} @@ -20625,9 +20756,6 @@ var StandardMaterial = Material.extend(function () { */ metalnessChannel: 1 }); - - this.define('fragment', 'USE_METALNESS'); - this.define('fragment', 'USE_ROUGHNESS'); }, { clone: function () { var material = new StandardMaterial({ @@ -21029,9 +21157,9 @@ var Skeleton = Base.extend(function () { var utilGlsl = "\n@export clay.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@export clay.util.calculate_attenuation\nuniform float attenuationFactor : 5.0;\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@end\n@export clay.util.edge_factor\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@end\n@export clay.util.encode_float\nvec4 encodeFloat(const in float depth)\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 return res;\n}\n@end\n@export clay.util.decode_float\nfloat decodeFloat(const in vec4 color)\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@export clay.util.float\n@import clay.util.encode_float\n@import clay.util.decode_float\n@end\n@export clay.util.rgbm_decode\nvec3 RGBMDecode(vec4 rgbm, float range) {\n return range * rgbm.rgb * rgbm.a;\n}\n@end\n@export clay.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@export clay.util.rgbm\n@import clay.util.rgbm_decode\n@import clay.util.rgbm_encode\nvec4 decodeHDR(vec4 color)\n{\n#if defined(RGBM_DECODE) || defined(RGBM)\n return vec4(RGBMDecode(color, 8.12), 1.0);\n#else\n return color;\n#endif\n}\nvec4 encodeHDR(vec4 color)\n{\n#if defined(RGBM_ENCODE) || defined(RGBM)\n return RGBMEncode(color.xyz, 8.12);\n#else\n return color;\n#endif\n}\n@end\n@export clay.util.srgb\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}\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@export clay.chunk.skinning_header\n#ifdef SKINNING\nattribute vec3 weight : WEIGHT;\nattribute vec4 joint : JOINT;\nuniform mat4 skinMatrix[JOINT_COUNT] : SKIN_MATRIX;\nmat4 getSkinMatrix(float idx) {\n return skinMatrix[int(idx)];\n}\n#endif\n@end\n@export clay.chunk.skin_matrix\nmat4 skinMatrixWS = getSkinMatrix(joint.x) * weight.x;\nif (weight.y > 1e-4)\n{\n skinMatrixWS += getSkinMatrix(joint.y) * weight.y;\n}\nif (weight.z > 1e-4)\n{\n skinMatrixWS += getSkinMatrix(joint.z) * weight.z;\n}\nfloat weightW = 1.0-weight.x-weight.y-weight.z;\nif (weightW > 1e-4)\n{\n skinMatrixWS += getSkinMatrix(joint.w) * weightW;\n}\n@end\n@export clay.util.parallax_correct\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 vec3 further = max(first, second);\n float dist = min(further.x, min(further.y, further.z));\n vec3 fixedPos = pos + dir * dist;\n vec3 boxCenter = (boxMax + boxMin) * 0.5;\n return normalize(fixedPos - boxCenter);\n}\n@end\n@export clay.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\n@export clay.util.ACES\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@end"; -var basicEssl = "@export clay.basic.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 position : POSITION;\nattribute vec3 barycentric;\n@import clay.chunk.skinning_header\nvarying vec2 v_Texcoord;\nvarying vec3 v_Barycentric;\n#ifdef VERTEX_COLOR\nattribute vec4 a_Color : COLOR;\nvarying vec4 v_Color;\n#endif\nvoid main()\n{\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_Barycentric = barycentric;\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n#ifdef VERTEX_COLOR\n v_Color = a_Color;\n#endif\n}\n@end\n@export clay.basic.fragment\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#ifdef ALPHA_TEST\nuniform float alphaCutoff: 0.9;\n#endif\n#ifdef VERTEX_COLOR\nvarying vec4 v_Color;\n#endif\nuniform float lineWidth : 0.0;\nuniform vec4 lineColor : [0.0, 0.0, 0.0, 0.6];\nvarying vec3 v_Barycentric;\n@import clay.util.edge_factor\n@import clay.util.rgbm\n@import clay.util.srgb\n@import clay.util.ACES\nvoid main()\n{\n#ifdef RENDER_TEXCOORD\n gl_FragColor = vec4(v_Texcoord, 1.0, 1.0);\n return;\n#endif\n gl_FragColor = vec4(color, alpha);\n#ifdef VERTEX_COLOR\n gl_FragColor *= v_Color;\n#endif\n#ifdef DIFFUSEMAP_ENABLED\n vec4 texel = decodeHDR(texture2D(diffuseMap, v_Texcoord));\n#ifdef SRGB_DECODE\n texel = sRGBToLinear(texel);\n#endif\n#if defined(DIFFUSEMAP_ALPHA_ALPHA)\n gl_FragColor.a = texel.a;\n#endif\n gl_FragColor.rgb *= texel.rgb;\n#endif\n gl_FragColor.rgb += emission;\n if( lineWidth > 0.)\n {\n gl_FragColor.rgb = mix(gl_FragColor.rgb, lineColor.rgb, (1.0 - edgeFactor(lineWidth)) * lineColor.a);\n }\n#ifdef ALPHA_TEST\n if (gl_FragColor.a < alphaCutoff) {\n discard;\n }\n#endif\n#ifdef TONEMAPPING\n gl_FragColor.rgb = ACESToneMapping(gl_FragColor.rgb);\n#endif\n#ifdef SRGB_ENCODE\n gl_FragColor = linearTosRGB(gl_FragColor);\n#endif\n gl_FragColor = encodeHDR(gl_FragColor);\n}\n@end"; +var basicEssl = "@export clay.basic.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 position : POSITION;\nattribute vec3 barycentric;\n@import clay.chunk.skinning_header\nvarying vec2 v_Texcoord;\nvarying vec3 v_Barycentric;\n#ifdef VERTEX_COLOR\nattribute vec4 a_Color : COLOR;\nvarying vec4 v_Color;\n#endif\nvoid main()\n{\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_Barycentric = barycentric;\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n#ifdef VERTEX_COLOR\n v_Color = a_Color;\n#endif\n}\n@end\n@export clay.basic.fragment\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#ifdef ALPHA_TEST\nuniform float alphaCutoff: 0.9;\n#endif\n#ifdef VERTEX_COLOR\nvarying vec4 v_Color;\n#endif\nuniform float lineWidth : 0.0;\nuniform vec4 lineColor : [0.0, 0.0, 0.0, 0.6];\nvarying vec3 v_Barycentric;\n@import clay.util.edge_factor\n@import clay.util.rgbm\n@import clay.util.srgb\n@import clay.util.ACES\nvoid main()\n{\n gl_FragColor = vec4(color, alpha);\n#ifdef VERTEX_COLOR\n gl_FragColor *= v_Color;\n#endif\n#ifdef SRGB_DECODE\n gl_FragColor = sRGBToLinear(gl_FragColor);\n#endif\n#ifdef DIFFUSEMAP_ENABLED\n vec4 texel = decodeHDR(texture2D(diffuseMap, v_Texcoord));\n#ifdef SRGB_DECODE\n texel = sRGBToLinear(texel);\n#endif\n#if defined(DIFFUSEMAP_ALPHA_ALPHA)\n gl_FragColor.a = texel.a;\n#endif\n gl_FragColor.rgb *= texel.rgb;\n#endif\n gl_FragColor.rgb += emission;\n if( lineWidth > 0.)\n {\n gl_FragColor.rgb = mix(gl_FragColor.rgb, lineColor.rgb, (1.0 - edgeFactor(lineWidth)) * lineColor.a);\n }\n#ifdef ALPHA_TEST\n if (gl_FragColor.a < alphaCutoff) {\n discard;\n }\n#endif\n#ifdef TONEMAPPING\n gl_FragColor.rgb = ACESToneMapping(gl_FragColor.rgb);\n#endif\n#ifdef SRGB_ENCODE\n gl_FragColor = linearTosRGB(gl_FragColor);\n#endif\n gl_FragColor = encodeHDR(gl_FragColor);\n}\n@end"; -var lambertEssl = "\n@export clay.lambert.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 normal : NORMAL;\nattribute vec3 barycentric;\n#ifdef VERTEX_COLOR\nattribute vec4 a_Color : COLOR;\nvarying vec4 v_Color;\n#endif\n@import clay.chunk.skinning_header\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\nvoid main()\n{\n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n#endif\n gl_Position = worldViewProjection * vec4( skinnedPosition, 1.0 );\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 v_Barycentric = barycentric;\n#ifdef VERTEX_COLOR\n v_Color = a_Color;\n#endif\n}\n@end\n@export clay.lambert.fragment\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nuniform sampler2D diffuseMap;\nuniform sampler2D alphaMap;\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform vec3 emission : [0.0, 0.0, 0.0];\nuniform float alpha : 1.0;\n#ifdef ALPHA_TEST\nuniform float alphaCutoff: 0.9;\n#endif\nuniform float lineWidth : 0.0;\nuniform vec4 lineColor : [0.0, 0.0, 0.0, 0.6];\nvarying vec3 v_Barycentric;\n#ifdef VERTEX_COLOR\nvarying vec4 v_Color;\n#endif\n#ifdef AMBIENT_LIGHT_COUNT\n@import clay.header.ambient_light\n#endif\n#ifdef AMBIENT_SH_LIGHT_COUNT\n@import clay.header.ambient_sh_light\n#endif\n#ifdef POINT_LIGHT_COUNT\n@import clay.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_COUNT\n@import clay.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_COUNT\n@import clay.header.spot_light\n#endif\n@import clay.util.calculate_attenuation\n@import clay.util.edge_factor\n@import clay.util.rgbm\n@import clay.plugin.compute_shadow_map\n@import clay.util.ACES\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 gl_FragColor = vec4(color, alpha);\n#ifdef VERTEX_COLOR\n gl_FragColor *= v_Color;\n#endif\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 vec3 diffuseColor = vec3(0.0, 0.0, 0.0);\n#ifdef AMBIENT_LIGHT_COUNT\n for(int _idx_ = 0; _idx_ < AMBIENT_LIGHT_COUNT; _idx_++)\n {\n diffuseColor += ambientLightColor[_idx_];\n }\n#endif\n#ifdef AMBIENT_SH_LIGHT_COUNT\n for(int _idx_ = 0; _idx_ < AMBIENT_SH_LIGHT_COUNT; _idx_++)\n {{\n diffuseColor += calcAmbientSHLight(_idx_, v_Normal) * ambientSHLightColor[_idx_];\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 vec3 lightPosition = pointLightPosition[i];\n vec3 lightColor = pointLightColor[i];\n float range = pointLightRange[i];\n vec3 lightDirection = lightPosition - v_WorldPosition;\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range);\n lightDirection /= dist;\n float ndl = dot( v_Normal, lightDirection );\n float shadowContrib = 1.0;\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribsPoint[i];\n }\n#endif\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 float ndl = dot(v_Normal, normalize(lightDirection));\n float shadowContrib = 1.0;\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribsDir[i];\n }\n#endif\n diffuseColor += lightColor * clamp(ndl, 0.0, 1.0) * shadowContrib;\n }\n#endif\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 vec3 lightDirection = lightPosition - v_WorldPosition;\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range);\n lightDirection /= dist;\n float c = dot(spotLightDirection, lightDirection);\n float falloff;\n falloff = clamp((c - a) /( b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n float ndl = dot(v_Normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\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 gl_FragColor.rgb *= diffuseColor;\n gl_FragColor.rgb += emission;\n if(lineWidth > 0.)\n {\n gl_FragColor.rgb = mix(gl_FragColor.rgb, lineColor.rgb, (1.0 - edgeFactor(lineWidth)) * lineColor.a);\n }\n#ifdef ALPHA_TEST\n if (gl_FragColor.a < alphaCutoff) {\n discard;\n }\n#endif\n#ifdef TONEMAPPING\n gl_FragColor.rgb = ACESToneMapping(gl_FragColor.rgb);\n#endif\n#ifdef SRGB_ENCODE\n gl_FragColor = linearTosRGB(gl_FragColor);\n#endif\n gl_FragColor = encodeHDR(gl_FragColor);\n}\n@end"; +var lambertEssl = "\n@export clay.lambert.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 normal : NORMAL;\nattribute vec3 barycentric;\n#ifdef VERTEX_COLOR\nattribute vec4 a_Color : COLOR;\nvarying vec4 v_Color;\n#endif\n@import clay.chunk.skinning_header\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\nvoid main()\n{\n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n#endif\n gl_Position = worldViewProjection * vec4( skinnedPosition, 1.0 );\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 v_Barycentric = barycentric;\n#ifdef VERTEX_COLOR\n v_Color = a_Color;\n#endif\n}\n@end\n@export clay.lambert.fragment\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nuniform sampler2D diffuseMap;\nuniform sampler2D alphaMap;\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform vec3 emission : [0.0, 0.0, 0.0];\nuniform float alpha : 1.0;\n#ifdef ALPHA_TEST\nuniform float alphaCutoff: 0.9;\n#endif\nuniform float lineWidth : 0.0;\nuniform vec4 lineColor : [0.0, 0.0, 0.0, 0.6];\nvarying vec3 v_Barycentric;\n#ifdef VERTEX_COLOR\nvarying vec4 v_Color;\n#endif\n#ifdef AMBIENT_LIGHT_COUNT\n@import clay.header.ambient_light\n#endif\n#ifdef AMBIENT_SH_LIGHT_COUNT\n@import clay.header.ambient_sh_light\n#endif\n#ifdef POINT_LIGHT_COUNT\n@import clay.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_COUNT\n@import clay.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_COUNT\n@import clay.header.spot_light\n#endif\n@import clay.util.calculate_attenuation\n@import clay.util.edge_factor\n@import clay.util.rgbm\n@import clay.plugin.compute_shadow_map\n@import clay.util.ACES\nvoid main()\n{\n gl_FragColor = vec4(color, alpha);\n#ifdef VERTEX_COLOR\n gl_FragColor *= v_Color;\n#endif\n#ifdef SRGB_DECODE\n gl_FragColor = sRGBToLinear(gl_FragColor);\n#endif\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 vec3 diffuseColor = vec3(0.0, 0.0, 0.0);\n#ifdef AMBIENT_LIGHT_COUNT\n for(int _idx_ = 0; _idx_ < AMBIENT_LIGHT_COUNT; _idx_++)\n {\n diffuseColor += ambientLightColor[_idx_];\n }\n#endif\n#ifdef AMBIENT_SH_LIGHT_COUNT\n for(int _idx_ = 0; _idx_ < AMBIENT_SH_LIGHT_COUNT; _idx_++)\n {{\n diffuseColor += calcAmbientSHLight(_idx_, v_Normal) * ambientSHLightColor[_idx_];\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 vec3 lightPosition = pointLightPosition[i];\n vec3 lightColor = pointLightColor[i];\n float range = pointLightRange[i];\n vec3 lightDirection = lightPosition - v_WorldPosition;\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range);\n lightDirection /= dist;\n float ndl = dot( v_Normal, lightDirection );\n float shadowContrib = 1.0;\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribsPoint[i];\n }\n#endif\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 float ndl = dot(v_Normal, normalize(lightDirection));\n float shadowContrib = 1.0;\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribsDir[i];\n }\n#endif\n diffuseColor += lightColor * clamp(ndl, 0.0, 1.0) * shadowContrib;\n }\n#endif\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 vec3 lightDirection = lightPosition - v_WorldPosition;\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range);\n lightDirection /= dist;\n float c = dot(spotLightDirection, lightDirection);\n float falloff;\n falloff = clamp((c - a) /( b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n float ndl = dot(v_Normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\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 gl_FragColor.rgb *= diffuseColor;\n gl_FragColor.rgb += emission;\n if(lineWidth > 0.)\n {\n gl_FragColor.rgb = mix(gl_FragColor.rgb, lineColor.rgb, (1.0 - edgeFactor(lineWidth)) * lineColor.a);\n }\n#ifdef ALPHA_TEST\n if (gl_FragColor.a < alphaCutoff) {\n discard;\n }\n#endif\n#ifdef TONEMAPPING\n gl_FragColor.rgb = ACESToneMapping(gl_FragColor.rgb);\n#endif\n#ifdef SRGB_ENCODE\n gl_FragColor = linearTosRGB(gl_FragColor);\n#endif\n gl_FragColor = encodeHDR(gl_FragColor);\n}\n@end"; var wireframeEssl = "@export clay.wireframe.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world : WORLD;\nattribute vec3 position : POSITION;\nattribute vec3 barycentric;\n@import clay.chunk.skinning_header\nvarying vec3 v_Barycentric;\nvoid main()\n{\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0 );\n v_Barycentric = barycentric;\n}\n@end\n@export clay.wireframe.fragment\nuniform vec3 color : [0.0, 0.0, 0.0];\nuniform float alpha : 1.0;\nuniform float lineWidth : 1.0;\nvarying vec3 v_Barycentric;\n@import clay.util.edge_factor\nvoid main()\n{\n gl_FragColor.rgb = color;\n gl_FragColor.a = (1.0-edgeFactor(lineWidth)) * alpha;\n}\n@end"; @@ -21567,8 +21695,8 @@ function () { } }); util$1.defaults(parameters, { - wrapS: Texture$1.REPEAT, - wrapT: Texture$1.REPEAT, + wrapS: Texture.REPEAT, + wrapT: Texture.REPEAT, flipY: this.textureFlipY, convertToPOT: this.textureConvertToPOT }); @@ -22338,9 +22466,7 @@ var PointLight = Light.extend(/** @lends clay.light.Point# */ { * @constructor clay.light.Spot * @extends clay.Light */ -var SpotLight = Light.extend( -/**@lends clay.light.Spot */ -{ +var SpotLight = Light.extend(/**@lends clay.light.Spot */ { /** * @type {number} */ @@ -22360,12 +22486,12 @@ var SpotLight = Light.extend( /** * @type {number} */ - shadowBias: 0.0002, + shadowBias: 0.001, /** * @type {number} */ shadowSlopeScale: 2.0 -},{ +}, { type: 'SPOT_LIGHT', @@ -22641,7 +22767,7 @@ var FrameBuffer = Base.extend( var texture = obj.texture; // FIXME some texture format can't generate mipmap if (!texture.NPOT && texture.useMipmap - && texture.minFilter === Texture$1.LINEAR_MIPMAP_LINEAR) { + && texture.minFilter === Texture.LINEAR_MIPMAP_LINEAR) { var target = texture.textureType === 'textureCube' ? glenum.TEXTURE_CUBE_MAP : glenum.TEXTURE_2D; _gl.bindTexture(target, texture.getWebGLTexture(renderer)); _gl.generateMipmap(target); @@ -22870,17 +22996,17 @@ var Pass = Base.extend(function () { * @type {string} */ // PENDING shader or fragment ? - fragment : '', + fragment: '', /** * @type {Object} */ - outputs : null, + outputs: null, /** * @type {clay.Material} */ - material : null, + material: null, /** * @type {Boolean} @@ -23452,15 +23578,15 @@ var ret = { switch(fourCC) { case FOURCC_DXT1: blockBytes = 8; - internalFormat = Texture$1.COMPRESSED_RGB_S3TC_DXT1_EXT; + internalFormat = Texture.COMPRESSED_RGB_S3TC_DXT1_EXT; break; case FOURCC_DXT3: blockBytes = 16; - internalFormat = Texture$1.COMPRESSED_RGBA_S3TC_DXT3_EXT; + internalFormat = Texture.COMPRESSED_RGBA_S3TC_DXT3_EXT; break; case FOURCC_DXT5: blockBytes = 16; - internalFormat = Texture$1.COMPRESSED_RGBA_S3TC_DXT5_EXT; + internalFormat = Texture.COMPRESSED_RGBA_S3TC_DXT5_EXT; break; default: return null; @@ -23677,7 +23803,7 @@ var ret$1 = { texture.height = height; texture.pixels = pixels; // HALF_FLOAT can't use Float32Array - texture.type = Texture$1.FLOAT; + texture.type = Texture.FLOAT; return texture; }, @@ -23989,7 +24115,7 @@ var textureUtil = { var integrateBRDFShaderCode = "#define SAMPLE_NUMBER 1024\n#define PI 3.14159265358979\nuniform sampler2D normalDistribution;\nuniform vec2 viewportSize : [512, 256];\nconst vec3 N = vec3(0.0, 0.0, 1.0);\nconst float fSampleNumber = float(SAMPLE_NUMBER);\nvec3 importanceSampleNormal(float i, float roughness, vec3 N) {\n vec3 H = texture2D(normalDistribution, vec2(roughness, i)).rgb;\n vec3 upVector = abs(N.y) > 0.999 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);\n vec3 tangentX = normalize(cross(N, upVector));\n vec3 tangentZ = cross(N, tangentX);\n return normalize(tangentX * H.x + N * H.y + tangentZ * H.z);\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}\nvoid main() {\n vec2 uv = gl_FragCoord.xy / viewportSize;\n float NoV = uv.x;\n float roughness = uv.y;\n vec3 V;\n V.x = sqrt(1.0 - NoV * NoV);\n V.y = 0.0;\n V.z = NoV;\n float A = 0.0;\n float B = 0.0;\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 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 gl_FragColor = vec4(vec2(A, B) / fSampleNumber, 0.0, 1.0);\n}\n"; -var prefilterFragCode = "#define SHADER_NAME prefilter\n#define SAMPLE_NUMBER 1024\n#define PI 3.14159265358979\nuniform mat4 viewInverse : VIEWINVERSE;\nuniform samplerCube environmentMap;\nuniform sampler2D normalDistribution;\nuniform float roughness : 0.5;\nuniform int maxSampleNumber: 1024\nvarying vec2 v_Texcoord;\nvarying vec3 v_WorldPosition;\n@import clay.util.rgbm\nvec3 importanceSampleNormal(float i, float roughness, vec3 N) {\n vec3 H = texture2D(normalDistribution, vec2(roughness, i)).rgb;\n vec3 upVector = abs(N.y) > 0.999 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);\n vec3 tangentX = normalize(cross(N, upVector));\n vec3 tangentZ = cross(N, tangentX);\n return normalize(tangentX * H.x + N * H.y + tangentZ * H.z);\n}\nvoid main() {\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = normalize(v_WorldPosition - eyePos);\n vec3 N = V;\n vec3 prefilteredColor = vec3(0.0);\n float totalWeight = 0.0;\n float fMaxSampleNumber = float(maxSampleNumber);\n for (int i = 0; i < SAMPLE_NUMBER; i++) {\n if (i > maxSampleNumber) {\n break;\n }\n vec3 H = importanceSampleNormal(float(i) / fMaxSampleNumber, roughness, N);\n vec3 L = reflect(-V, H);\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 gl_FragColor = encodeHDR(vec4(prefilteredColor / totalWeight, 1.0));\n}\n"; +var prefilterFragCode = "#define SHADER_NAME prefilter\n#define SAMPLE_NUMBER 1024\n#define PI 3.14159265358979\nuniform mat4 viewInverse : VIEWINVERSE;\nuniform samplerCube environmentMap;\nuniform sampler2D normalDistribution;\nuniform float roughness : 0.5;\nvarying vec2 v_Texcoord;\nvarying vec3 v_WorldPosition;\n@import clay.util.rgbm\nvec3 importanceSampleNormal(float i, float roughness, vec3 N) {\n vec3 H = texture2D(normalDistribution, vec2(roughness, i)).rgb;\n vec3 upVector = abs(N.y) > 0.999 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);\n vec3 tangentX = normalize(cross(N, upVector));\n vec3 tangentZ = cross(N, tangentX);\n return normalize(tangentX * H.x + N * H.y + tangentZ * H.z);\n}\nvoid main() {\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = normalize(v_WorldPosition - eyePos);\n vec3 N = V;\n vec3 prefilteredColor = vec3(0.0);\n float totalWeight = 0.0;\n float fMaxSampleNumber = float(SAMPLE_NUMBER);\n for (int i = 0; i < SAMPLE_NUMBER; i++) {\n vec3 H = importanceSampleNormal(float(i) / fMaxSampleNumber, roughness, N);\n vec3 L = reflect(-V, H);\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 gl_FragColor = encodeHDR(vec4(prefilteredColor / totalWeight, 1.0));\n}\n"; // Cubemap prefilter utility // http://www.unrealengine.com/files/downloads/2013SiggraphPresentationsNotes.pdf @@ -24063,8 +24189,8 @@ cubemapUtil.prefilterEnvironmentMap = function ( width: width, height: height, // FIXME FLOAT type will cause GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT error on iOS - type: textureType === Texture$1.FLOAT ? - Texture$1.HALF_FLOAT : textureType + type: textureType === Texture.FLOAT ? + Texture.HALF_FLOAT : textureType }); textureUtil.panoramaToCubeMap(renderer, envMap, envCubemap, { // PENDING encodeRGBM so it can be decoded as RGBM @@ -24084,7 +24210,7 @@ cubemapUtil.prefilterEnvironmentMap = function ( // Force to be UNSIGNED_BYTE if (textureOpts.encodeRGBM) { - textureType = prefilteredCubeMap.type = Texture$1.UNSIGNED_BYTE; + textureType = prefilteredCubeMap.type = Texture.UNSIGNED_BYTE; } var renderTargetTmp = new Texture2D({ @@ -24095,18 +24221,13 @@ cubemapUtil.prefilterEnvironmentMap = function ( var frameBuffer = new FrameBuffer({ depthBuffer: false }); - var ArrayCtor = vendor[textureType === Texture$1.UNSIGNED_BYTE ? 'Uint8Array' : 'Float32Array']; + var ArrayCtor = vendor[textureType === Texture.UNSIGNED_BYTE ? 'Uint8Array' : 'Float32Array']; for (var i = 0; i < mipmapNum; i++) { // console.time('prefilter'); prefilteredCubeMap.mipmaps[i] = { pixels: {} }; - skyEnv.material.set('roughness', i / (targets.length - 1)); - var maxSampleNumber = renderTargetTmp.width * renderTargetTmp.height; - if (renderTargetTmp.width >= 32) { - maxSampleNumber /= 4; - } - skyEnv.material.set('maxSampleNumber', Math.min(maxSampleNumber, 1024)); + skyEnv.material.set('roughness', i / (mipmapNum - 1)); // Tweak fov // http://the-witness.net/news/2012/02/seamless-cube-map-filtering/ @@ -24123,7 +24244,7 @@ cubemapUtil.prefilterEnvironmentMap = function ( renderer.render(dummyScene, camera); renderer.gl.readPixels( 0, 0, renderTargetTmp.width, renderTargetTmp.height, - Texture$1.RGBA, textureType, pixels + Texture.RGBA, textureType, pixels ); // var canvas = document.createElement('canvas'); @@ -24175,9 +24296,11 @@ cubemapUtil.integrateBRDF = function (renderer, normalDistribution) { var texture = new Texture2D({ width: 512, height: 256, - type: Texture$1.HALF_FLOAT, - minFilter: Texture$1.NEAREST, - magFilter: Texture$1.NEAREST, + type: Texture.HALF_FLOAT, + wrapS: Texture.CLAMP_TO_EDGE, + wrapT: Texture.CLAMP_TO_EDGE, + minFilter: Texture.NEAREST, + magFilter: Texture.NEAREST, useMipmap: false }); pass.setUniform('normalDistribution', normalDistribution); @@ -24213,11 +24336,11 @@ cubemapUtil.generateNormalDistribution = function (roughnessLevels, sampleSize) var normalDistribution = new Texture2D({ width: roughnessLevels, height: sampleSize, - type: Texture$1.FLOAT, - minFilter: Texture$1.NEAREST, - magFilter: Texture$1.NEAREST, - wrapS: Texture$1.CLAMP_TO_EDGE, - wrapT: Texture$1.CLAMP_TO_EDGE, + type: Texture.FLOAT, + minFilter: Texture.NEAREST, + magFilter: Texture.NEAREST, + wrapS: Texture.CLAMP_TO_EDGE, + wrapT: Texture.CLAMP_TO_EDGE, useMipmap: false }); var pixels = new Float32Array(sampleSize * roughnessLevels * 4); @@ -24318,6 +24441,10 @@ var AmbientCubemapLight = Light.extend({ cubemap.dispose(renderer); }, + getBRDFLookup: function () { + return this._brdfLookup; + }, + uniformTemplates: { ambientCubemapLightColor: { type: '3f', @@ -24505,7 +24632,7 @@ function isPowerOfTwo$2(width, height) { (height & (height-1)) === 0; } -var shadowmapEssl = "@export clay.sm.depth.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nattribute vec3 position : POSITION;\n#ifdef SHADOW_TRANSPARENT\nattribute vec2 texcoord : TEXCOORD_0;\n#endif\n@import clay.chunk.skinning_header\nvarying vec4 v_ViewPosition;\n#ifdef SHADOW_TRANSPARENT\nvarying vec2 v_Texcoord;\n#endif\nvoid main(){\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n v_ViewPosition = worldViewProjection * vec4(skinnedPosition, 1.0);\n gl_Position = v_ViewPosition;\n#ifdef SHADOW_TRANSPARENT\n v_Texcoord = texcoord;\n#endif\n}\n@end\n@export clay.sm.depth.fragment\nvarying vec4 v_ViewPosition;\n#ifdef SHADOW_TRANSPARENT\nvarying vec2 v_Texcoord;\n#endif\nuniform float bias : 0.001;\nuniform float slopeScale : 1.0;\n#ifdef SHADOW_TRANSPARENT\nuniform sampler2D transparentMap;\n#endif\n@import clay.util.encode_float\nvoid main(){\n float depth = v_ViewPosition.z / v_ViewPosition.w;\n#ifdef USE_VSM\n depth = depth * 0.5 + 0.5;\n float moment1 = depth;\n float moment2 = depth * depth;\n float dx = dFdx(depth);\n float dy = dFdy(depth);\n moment2 += 0.25*(dx*dx+dy*dy);\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#ifdef SHADOW_TRANSPARENT\n if (texture2D(transparentMap, v_Texcoord).a <= 0.1) {\n gl_FragColor = encodeFloat(0.9999);\n return;\n }\n#endif\n gl_FragColor = encodeFloat(depth * 0.5 + 0.5);\n#endif\n}\n@end\n@export clay.sm.debug_depth\nuniform sampler2D depthMap;\nvarying vec2 v_Texcoord;\n@import clay.util.decode_float\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@end\n@export clay.sm.distance.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world : WORLD;\nattribute vec3 position : POSITION;\n@import clay.chunk.skinning_header\nvarying vec3 v_WorldPosition;\nvoid main (){\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n gl_Position = worldViewProjection * vec4(skinnedPosition , 1.0);\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n}\n@end\n@export clay.sm.distance.fragment\nuniform vec3 lightPosition;\nuniform float range : 100;\nvarying vec3 v_WorldPosition;\n@import clay.util.encode_float\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@export clay.plugin.shadow_map_common\n@import clay.util.decode_float\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}\nfloat pcf(sampler2D map, vec2 uv, float z, float textureSize, vec2 scale) {\n float shadowContrib = tapShadowMap(map, uv, z);\n vec2 offset = vec2(1.0 / textureSize) * scale;\n#ifdef PCF_KERNEL_SIZE\n for (int _idx_ = 0; _idx_ < PCF_KERNEL_SIZE; _idx_++) {{\n shadowContrib += tapShadowMap(map, uv + offset * pcfKernel[_idx_], z);\n }}\n return shadowContrib / float(PCF_KERNEL_SIZE + 1);\n#else\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 return shadowContrib / 9.0;\n#endif\n}\nfloat pcf(sampler2D map, vec2 uv, float z, float textureSize) {\n return pcf(map, uv, z, textureSize, vec2(1.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 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 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 #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}\nfloat computeShadowContrib(sampler2D map, mat4 lightVPM, vec3 position, float textureSize) {\n return computeShadowContrib(map, lightVPM, position, textureSize, vec2(1.0), vec2(0.0));\n}\nfloat computeShadowContribOmni(samplerCube map, vec3 direction, float range)\n{\n float dist = length(direction);\n vec4 shadowTex = textureCube(map, direction);\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@end\n@export clay.plugin.compute_shadow_map\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT) || defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT) || defined(POINT_LIGHT_SHADOWMAP_COUNT)\n#ifdef SPOT_LIGHT_SHADOWMAP_COUNT\nuniform sampler2D spotLightShadowMaps[SPOT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\nuniform mat4 spotLightMatrices[SPOT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\nuniform float spotLightShadowMapSizes[SPOT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\n#endif\n#ifdef DIRECTIONAL_LIGHT_SHADOWMAP_COUNT\n#if defined(SHADOW_CASCADE)\nuniform sampler2D directionalLightShadowMaps[1]:unconfigurable;\nuniform mat4 directionalLightMatrices[SHADOW_CASCADE]:unconfigurable;\nuniform float directionalLightShadowMapSizes[1]:unconfigurable;\nuniform float shadowCascadeClipsNear[SHADOW_CASCADE]:unconfigurable;\nuniform float shadowCascadeClipsFar[SHADOW_CASCADE]:unconfigurable;\n#else\nuniform sampler2D directionalLightShadowMaps[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\nuniform mat4 directionalLightMatrices[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\nuniform float directionalLightShadowMapSizes[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\n#endif\n#endif\n#ifdef POINT_LIGHT_SHADOWMAP_COUNT\nuniform samplerCube pointLightShadowMaps[POINT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\n#endif\nuniform bool shadowEnabled : true;\n#ifdef PCF_KERNEL_SIZE\nuniform vec2 pcfKernel[PCF_KERNEL_SIZE];\n#endif\n@import clay.plugin.shadow_map_common\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\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#endif\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n#ifdef SHADOW_CASCADE\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 float shadowContrib;\n shadowContribs[0] = 1.0;\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#else\nvoid computeShadowOfDirectionalLights(vec3 position, inout float shadowContribs[DIRECTIONAL_LIGHT_COUNT]){\n float shadowContrib;\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#endif\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\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#endif\n#endif\n@end"; +var shadowmapEssl = "@export clay.sm.depth.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n@import clay.chunk.skinning_header\nvarying vec4 v_ViewPosition;\nvarying vec2 v_Texcoord;\nvoid main(){\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n v_ViewPosition = worldViewProjection * vec4(skinnedPosition, 1.0);\n gl_Position = v_ViewPosition;\n v_Texcoord = texcoord;\n}\n@end\n@export clay.sm.depth.fragment\nvarying vec4 v_ViewPosition;\nvarying vec2 v_Texcoord;\nuniform float bias : 0.001;\nuniform float slopeScale : 1.0;\nuniform sampler2D alphaMap;\nuniform float alphaCutoff: 0.0;\n@import clay.util.encode_float\nvoid main(){\n float depth = v_ViewPosition.z / v_ViewPosition.w;\n if (alphaCutoff > 0.0) {\n if (texture2D(alphaMap, v_Texcoord).a <= alphaCutoff) {\n discard;\n }\n }\n#ifdef USE_VSM\n depth = depth * 0.5 + 0.5;\n float moment1 = depth;\n float moment2 = depth * depth;\n float dx = dFdx(depth);\n float dy = dFdy(depth);\n moment2 += 0.25*(dx*dx+dy*dy);\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 gl_FragColor = encodeFloat(depth * 0.5 + 0.5);\n#endif\n}\n@end\n@export clay.sm.debug_depth\nuniform sampler2D depthMap;\nvarying vec2 v_Texcoord;\n@import clay.util.decode_float\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@end\n@export clay.sm.distance.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world : WORLD;\nattribute vec3 position : POSITION;\n@import clay.chunk.skinning_header\nvarying vec3 v_WorldPosition;\nvoid main (){\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n gl_Position = worldViewProjection * vec4(skinnedPosition , 1.0);\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n}\n@end\n@export clay.sm.distance.fragment\nuniform vec3 lightPosition;\nuniform float range : 100;\nvarying vec3 v_WorldPosition;\n@import clay.util.encode_float\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@export clay.plugin.shadow_map_common\n@import clay.util.decode_float\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}\nfloat pcf(sampler2D map, vec2 uv, float z, float textureSize, vec2 scale) {\n float shadowContrib = tapShadowMap(map, uv, z);\n vec2 offset = vec2(1.0 / textureSize) * scale;\n#ifdef PCF_KERNEL_SIZE\n for (int _idx_ = 0; _idx_ < PCF_KERNEL_SIZE; _idx_++) {{\n shadowContrib += tapShadowMap(map, uv + offset * pcfKernel[_idx_], z);\n }}\n return shadowContrib / float(PCF_KERNEL_SIZE + 1);\n#else\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 return shadowContrib / 9.0;\n#endif\n}\nfloat pcf(sampler2D map, vec2 uv, float z, float textureSize) {\n return pcf(map, uv, z, textureSize, vec2(1.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 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 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 #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}\nfloat computeShadowContrib(sampler2D map, mat4 lightVPM, vec3 position, float textureSize) {\n return computeShadowContrib(map, lightVPM, position, textureSize, vec2(1.0), vec2(0.0));\n}\nfloat computeShadowContribOmni(samplerCube map, vec3 direction, float range)\n{\n float dist = length(direction);\n vec4 shadowTex = textureCube(map, direction);\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@end\n@export clay.plugin.compute_shadow_map\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT) || defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT) || defined(POINT_LIGHT_SHADOWMAP_COUNT)\n#ifdef SPOT_LIGHT_SHADOWMAP_COUNT\nuniform sampler2D spotLightShadowMaps[SPOT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\nuniform mat4 spotLightMatrices[SPOT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\nuniform float spotLightShadowMapSizes[SPOT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\n#endif\n#ifdef DIRECTIONAL_LIGHT_SHADOWMAP_COUNT\n#if defined(SHADOW_CASCADE)\nuniform sampler2D directionalLightShadowMaps[1]:unconfigurable;\nuniform mat4 directionalLightMatrices[SHADOW_CASCADE]:unconfigurable;\nuniform float directionalLightShadowMapSizes[1]:unconfigurable;\nuniform float shadowCascadeClipsNear[SHADOW_CASCADE]:unconfigurable;\nuniform float shadowCascadeClipsFar[SHADOW_CASCADE]:unconfigurable;\n#else\nuniform sampler2D directionalLightShadowMaps[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\nuniform mat4 directionalLightMatrices[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\nuniform float directionalLightShadowMapSizes[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\n#endif\n#endif\n#ifdef POINT_LIGHT_SHADOWMAP_COUNT\nuniform samplerCube pointLightShadowMaps[POINT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\n#endif\nuniform bool shadowEnabled : true;\n#ifdef PCF_KERNEL_SIZE\nuniform vec2 pcfKernel[PCF_KERNEL_SIZE];\n#endif\n@import clay.plugin.shadow_map_common\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\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#endif\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n#ifdef SHADOW_CASCADE\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 float shadowContrib;\n shadowContribs[0] = 1.0;\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#else\nvoid computeShadowOfDirectionalLights(vec3 position, inout float shadowContribs[DIRECTIONAL_LIGHT_COUNT]){\n float shadowContrib;\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#endif\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\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#endif\n#endif\n@end"; var mat4$8 = glmatrix.mat4; @@ -24513,6 +24640,31 @@ var targets$2 = ['px', 'nx', 'py', 'ny', 'pz', 'nz']; Shader['import'](shadowmapEssl); +function getDepthMaterialUniform(renderable, depthMaterial, symbol) { + if (symbol === 'alphaMap') { + return renderable.material.get('diffuseMap'); + } + else if (symbol === 'alphaCutoff') { + if (renderable.material.isDefined('fragment', 'ALPHA_TEST') + && renderable.material.get('diffuseMap') + ) { + var alphaCutoff = renderable.material.get('alphaCutoff'); + return alphaCutoff || 0; + } + return 0; + } + else { + return depthMaterial.get(symbol); + } +} + +function isDepthMaterialChanged(renderable, prevRenderable) { + var matA = renderable.material; + var matB = prevRenderable.material; + return matA.get('diffuseMap') !== matB.get('diffuseMap') + || (matA.get('alphaCutoff') || 0) !== (matB.get('alphaCutoff') || 0); +} + /** * Pass rendering shadow map. * @@ -24862,6 +25014,8 @@ var ShadowMapPass = Base.extend(function () { getMaterial: function (renderable) { return renderable.shadowDepthMaterial || defaultShadowMaterial; }, + isMaterialChanged: isDepthMaterialChanged, + getUniform: getDepthMaterialUniform, ifRender: function (renderable) { return renderable.castShadow; }, @@ -24979,6 +25133,8 @@ var ShadowMapPass = Base.extend(function () { getMaterial: function (renderable) { return renderable.shadowDepthMaterial || defaultShadowMaterial; }, + isMaterialChanged: isDepthMaterialChanged, + getUniform: getDepthMaterialUniform, ifRender: function (renderable) { return renderable.castShadow; }, @@ -25014,6 +25170,7 @@ var ShadowMapPass = Base.extend(function () { getMaterial: function (renderable) { return renderable.shadowDepthMaterial || defaultShadowMaterial; }, + getUniform: getDepthMaterialUniform, sortCompare: Renderer.opaqueSortCompare }; @@ -25133,7 +25290,7 @@ var ShadowMapPass = Base.extend(function () { var parameter = { width: size, height: size, - type: Texture$1.FLOAT + type: Texture.FLOAT }; var tmpTexture = this._texturePool.get(parameter); @@ -25169,7 +25326,7 @@ var ShadowMapPass = Base.extend(function () { texture.width = resolution * cascade; texture.height = resolution; if (this.softShadow === ShadowMapPass.VSM) { - texture.type = Texture$1.FLOAT; + texture.type = Texture.FLOAT; texture.anisotropic = 4; } else { @@ -25767,7 +25924,7 @@ sh.projectEnvironmentMap = function (renderer, envMap, opts) { renderer.render(dummyScene, camera); renderer.gl.readPixels( 0, 0, width, height, - Texture$1.RGBA, Texture$1.UNSIGNED_BYTE, cubePixels[targets$3[i]] + Texture.RGBA, Texture.UNSIGNED_BYTE, cubePixels[targets$3[i]] ); framebuffer.unbind(renderer); } @@ -25799,6 +25956,9 @@ var EVE_NAMES = ['click', 'dblclick', 'mouseover', 'mouseout', 'mousemove', /** * @typedef {string|HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} ImageLike */ +/** + * @typedef {string|HTMLCanvasElement|HTMLImageElement|HTMLVideoElement|clay.Texture2D} TextureLike + */ /** * @typedef {string|Array.} Color */ @@ -25811,11 +25971,13 @@ var EVE_NAMES = ['click', 'dblclick', 'mouseover', 'mouseout', 'mousemove', * @property {Function} init Initialization callback that will be called when initing app. * You can return a promise in init to start the loop asynchronously when the promise is resolved. * @property {Function} loop Loop callback that will be called each frame. - * @property {Function} beforeRender - * @property {Function} afterRender + * @property {boolean} [autoRender=true] If render automatically each frame. + * @property {Function} [beforeRender] + * @property {Function} [afterRender] * @property {number} [width] Container width. * @property {number} [height] Container height. * @property {number} [devicePixelRatio] + * @property {Object.} [methods] Methods that will be injected to App3D#methods. * @property {Object} [graphic] Graphic configuration including shadow, color space. * @property {boolean} [graphic.shadow=false] If enable shadow * @property {boolean} [graphic.linear=false] If use linear color space @@ -25823,6 +25985,23 @@ var EVE_NAMES = ['click', 'dblclick', 'mouseover', 'mouseout', 'mousemove', * @property {boolean} [event=false] If enable mouse/touch event. It will slow down the system if geometries are complex. */ +/** + * @typedef {Object} StandardMaterialMRConfig + * @property {string} [shader='standardMR'] + * @property {Color} [color] + * @property {number} [alpha] + * @property {number} [metalness] + * @property {number} [roughness] + * @property {Color} [emission] + * @property {number} [emissionIntensity] + * @property {boolean} [transparent] + * @property {TextureLike} [diffuseMap] + * @property {TextureLike} [normalMap] + * @property {TextureLike} [roughnessMap] + * @property {TextureLike} [metalnessMap] + * @property {TextureLike} [emissiveMap] + */ + /** * Using App3D is a much more convenient way to create and manage your 3D application. * @@ -25865,6 +26044,10 @@ function App3D(dom, appNS) { appNS = appNS || {}; appNS.graphic = appNS.graphic || {}; + if (appNS.autoRender == null) { + appNS.autoRender = true; + } + if (typeof dom === 'string') { dom = document.querySelector(dom); } @@ -25897,6 +26080,11 @@ function App3D(dom, appNS) { gTimeline.start(); + var userMethods = {}; + for (var key in appNS.methods) { + userMethods[key] = appNS.methods[key].bind(appNS, this); + } + Object.defineProperties(this, { /** * Container dom element @@ -25930,7 +26118,31 @@ function App3D(dom, appNS) { * @name clay.application.App3D#elapsedTime * @type {number} */ - elapsedTime: { get: function () { return gElapsedTime; }} + elapsedTime: { get: function () { return gElapsedTime; }}, + + /** + * Width of viewport. + * @name clay.application.App3D#width + * @type {number} + */ + width: { get: function () { return gRenderer.getWidth(); }}, + /** + * Height of viewport. + * @name clay.application.App3D#height + * @type {number} + */ + height: { get: function () { return gRenderer.getHeight(); }}, + + /** + * Methods from + * @name clay.application.App3D#height + * @type {number} + */ + methods: { get: function () { return userMethods; } }, + + _shadowPass: { get: function () { return gShadowPass; } }, + + _appNS: { get: function () { return appNS; } }, }); /** @@ -25971,60 +26183,47 @@ function App3D(dom, appNS) { this._geoCache = new LRU$1(20); this._texCache = new LRU$1(20); + // GPU Resources. + this._texturesList = {}; + this._geometriesList = {}; + // Do init the application. var initPromise = Promise.resolve(appNS.init && appNS.init(this)); // Use the inited camera. gRayPicking && (gRayPicking.camera = gScene.getMainCamera()); - var gTexturesList = {}; - var gGeometriesList = {}; - if (!appNS.loop) { console.warn('Miss loop method.'); } var self = this; initPromise.then(function () { - appNS.loop && gTimeline.on('frame', function (frameTime) { + gTimeline.on('frame', function (frameTime) { gFrameTime = frameTime; gElapsedTime += frameTime; - appNS.loop(self); - gScene.update(); - var skyboxList = []; - gScene.skybox && skyboxList.push(gScene.skybox); - gScene.skydome && skyboxList.push(gScene.skydome); - self._updateGraphicOptions(appNS.graphic, skyboxList, true); - - gRayPicking && (gRayPicking.camera = gScene.getMainCamera()); - // Render shadow pass - gShadowPass && gShadowPass.render(gRenderer, gScene, null, true); - - appNS.beforeRender && appNS.beforeRender(self); - self._doRender(gRenderer, gScene, true); - appNS.afterRender && appNS.afterRender(self); - - // Mark all resources unused; - markUnused(gTexturesList); - markUnused(gGeometriesList); + var camera = gScene.getMainCamera(); + if (camera) { + camera.aspect = gRenderer.getViewportAspect(); + } + gRayPicking && (gRayPicking.camera = camera); - // Collect resources used in this frame. - var newTexturesList = []; - var newGeometriesList = []; - collectResources(gScene, newTexturesList, newGeometriesList); + appNS.loop && appNS.loop(self); - // Dispose those unsed resources. - checkAndDispose(gRenderer, gTexturesList); - checkAndDispose(gRenderer, gGeometriesList); + if (appNS.autoRender) { + self.render(); + } - gTexturesList = newTexturesList; - gGeometriesList = newGeometriesList; - }); + self.collectResources(); + }, this); }); gScene.on('beforerender', function (renderer, scene, camera, renderList) { - this._updateGraphicOptions(appNS.graphic, renderList.opaque, false); - this._updateGraphicOptions(appNS.graphic, renderList.transparent, false); + if (this._inRender) { + // Only update graphic options when using #render function. + this._updateGraphicOptions(appNS.graphic, renderList.opaque, false); + this._updateGraphicOptions(appNS.graphic, renderList.transparent, false); + } }, this); } @@ -26170,10 +26369,58 @@ App3D.prototype._updateGraphicOptions = function (graphicOpts, list, isSkybox) { App3D.prototype._doRender = function (renderer, scene) { var camera = scene.getMainCamera(); - camera.aspect = renderer.getViewportAspect(); renderer.render(scene, camera, true); }; +/** + * Do render + */ +App3D.prototype.render = function () { + this._inRender = true; + var appNS = this._appNS; + appNS.beforeRender && appNS.beforeRender(self); + + var scene = this.scene; + var renderer = this.renderer; + var shadowPass = this._shadowPass; + + scene.update(); + var skyboxList = []; + scene.skybox && skyboxList.push(scene.skybox); + scene.skydome && skyboxList.push(scene.skydome); + + this._updateGraphicOptions(appNS.graphic, skyboxList, true); + // Render shadow pass + shadowPass && shadowPass.render(renderer, scene, null, true); + + this._doRender(renderer, scene, true); + + appNS.afterRender && appNS.afterRender(self); + this._inRender = false; +}; + +App3D.prototype.collectResources = function () { + var renderer = this.renderer; + var scene = this.scene; + var texturesList = this._texturesList; + var geometriesList = this._geometriesList; + // Mark all resources unused; + markUnused(texturesList); + markUnused(geometriesList); + + // Collect resources used in this frame. + var newTexturesList = []; + var newGeometriesList = []; + collectResources(scene, newTexturesList, newGeometriesList); + + // Dispose those unsed resources. + checkAndDispose(renderer, texturesList); + checkAndDispose(renderer, geometriesList); + + this._texturesList = newTexturesList; + this._geometriesList = newGeometriesList; +}; + function markUnused(resourceList) { for (var i = 0; i < resourceList.length; i++) { @@ -26405,11 +26652,11 @@ App3D.prototype.loadTextureCubeSync = function (imgList, opts) { /** * Create a material. - * @param {Object} materialConfig. materialConfig contains `shader`, `transparent` and uniforms that used in corresponding uniforms. + * @param {Object|StandardMaterialMRConfig} materialConfig. materialConfig contains `shader`, `transparent` and uniforms that used in corresponding uniforms. * Uniforms can be `color`, `alpha` `diffuseMap` etc. * @param {string|clay.Shader} [shader='clay.standardMR'] Default to be standard shader with metalness and roughness workflow. * @param {boolean} [transparent=false] If material is transparent. - * @param {boolean} [convertTextureToPOT=false] Force convert None Power of Two texture to Power of two so it can be tiled. + * @param {boolean} [textureConvertToPOT=false] Force convert None Power of Two texture to Power of two so it can be tiled. * @param {boolean} [textureFlipY=true] If flip y of texture. * @return {clay.Material} */ @@ -26429,11 +26676,11 @@ App3D.prototype.createMaterial = function (matConfig) { if (material.uniforms[key]) { var val = matConfig[key]; if ((material.uniforms[key].type === 't' || isImageLikeElement(val)) - && !(val instanceof Texture$1) + && !(val instanceof Texture) ) { // Try to load a texture. this.loadTexture(val, { - convertToPOT: matConfig.convertTextureToPOT || false, + convertToPOT: matConfig.textureConvertToPOT || false, flipY: matConfig.textureFlipY == null ? true : matConfig.textureFlipY }).then(makeTextureSetter(key)); } @@ -26867,7 +27114,7 @@ App3D.prototype.createAmbientCubemapLight = function (envImage, specIntensity, d * @param {boolean} [opts.autoPlayAnimation=true] If autoplay the animation of model. * @param {boolean} [opts.upAxis='y'] Change model to y up if upAxis is 'z' * @param {boolean} [opts.textureFlipY=false] - * @param {boolean} [opts.convertTextureToPOT=false] If convert texture to power-of-two + * @param {boolean} [opts.textureConvertToPOT=false] If convert texture to power-of-two * @param {string} [opts.textureRootPath] Root path of texture. Default to be relative with glTF file. * @param {clay.Node} [parentNode] Parent node that model will be mounted. Default to be scene * @return {Promise} @@ -26889,7 +27136,7 @@ App3D.prototype.loadModel = function (url, opts, parentNode) { textureRootPath: opts.textureRootPath, crossOrigin: 'Anonymous', textureFlipY: opts.textureFlipY, - convertTextureToPOT: opts.convertTextureToPOT + textureConvertToPOT: opts.textureConvertToPOT }; if (opts.upAxis && opts.upAxis.toLowerCase() === 'z') { loaderOpts.rootNode.rotation.identity().rotateX(-Math.PI / 2); @@ -28233,7 +28480,7 @@ var downsampleEssl = "@export clay.compositor.downsample\nuniform sampler2D text var upsampleEssl = "\n@export clay.compositor.upsample\n#define HIGH_QUALITY\nuniform sampler2D texture;\nuniform vec2 textureSize : [512, 512];\nuniform float sampleScale: 0.5;\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\n@import clay.util.clamp_sample\nvoid main()\n{\n#ifdef HIGH_QUALITY\n vec4 d = vec4(1.0, 1.0, -1.0, 0.0) / textureSize.xyxy * sampleScale;\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 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 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 gl_FragColor = encodeHDR(s / 16.0);\n#else\n vec4 d = vec4(-1.0, -1.0, +1.0, +1.0) / textureSize.xyxy;\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 gl_FragColor = encodeHDR(s / 4.0);\n#endif\n}\n@end"; -var hdrEssl = "@export clay.compositor.hdr.composite\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#ifdef LUM_ENABLED\nuniform sampler2D lum;\n#endif\n#ifdef LUT_ENABLED\nuniform sampler2D lut;\n#endif\n#ifdef COLOR_CORRECTION\nuniform float brightness : 0.0;\nuniform float contrast : 1.0;\nuniform float saturation : 1.0;\n#endif\n#ifdef VIGNETTE\nuniform float vignetteDarkness: 1.0;\nuniform float vignetteOffset: 1.0;\n#endif\nuniform float exposure : 1.0;\nuniform float bloomIntensity : 0.25;\nuniform float lensflareIntensity : 1;\nvarying vec2 v_Texcoord;\n@import clay.util.srgb\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}\nfloat eyeAdaption(float fLum)\n{\n return mix(0.2, fLum, 0.5);\n}\n#ifdef LUT_ENABLED\nvec3 lutTransform(vec3 color) {\n float blueColor = color.b * 63.0;\n vec2 quad1;\n quad1.y = floor(floor(blueColor) / 8.0);\n quad1.x = floor(blueColor) - (quad1.y * 8.0);\n vec2 quad2;\n quad2.y = floor(ceil(blueColor) / 8.0);\n quad2.x = ceil(blueColor) - (quad2.y * 8.0);\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 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 vec4 newColor1 = texture2D(lut, texPos1);\n vec4 newColor2 = texture2D(lut, texPos2);\n vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n return newColor.rgb;\n}\n#endif\n@import clay.util.rgbm\nvoid main()\n{\n vec4 texel = vec4(0.0);\n vec4 originalTexel = vec4(0.0);\n#ifdef TEXTURE_ENABLED\n texel = decodeHDR(texture2D(texture, v_Texcoord));\n originalTexel = texel;\n#endif\n#ifdef BLOOM_ENABLED\n vec4 bloomTexel = decodeHDR(texture2D(bloom, v_Texcoord));\n texel.rgb += bloomTexel.rgb * bloomIntensity;\n texel.a += bloomTexel.a * bloomIntensity;\n#endif\n#ifdef LENSFLARE_ENABLED\n texel += decodeHDR(texture2D(lensflare, v_Texcoord)) * texture2D(lensdirt, v_Texcoord) * lensflareIntensity;\n#endif\n texel.a = min(texel.a, 1.0);\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 texel.rgb = ACESToneMapping(texel.rgb);\n texel = linearTosRGB(texel);\n#ifdef LUT_ENABLED\n texel.rgb = lutTransform(clamp(texel.rgb,vec3(0.0),vec3(1.0)));\n#endif\n#ifdef COLOR_CORRECTION\n texel.rgb = clamp(texel.rgb + vec3(brightness), 0.0, 1.0);\n texel.rgb = clamp((texel.rgb - vec3(0.5))*contrast+vec3(0.5), 0.0, 1.0);\n float lum = dot(texel.rgb, vec3(0.2125, 0.7154, 0.0721));\n texel.rgb = mix(vec3(lum), texel.rgb, saturation);\n#endif\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 gl_FragColor = encodeHDR(texel);\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)) * bloomIntensity);\n #elif DEBUG == 3\n gl_FragColor = encodeHDR(decodeHDR(texture2D(lensflare, v_Texcoord) * lensflareIntensity));\n #endif\n#endif\n if (originalTexel.a <= 0.01 && gl_FragColor.a > 1e-5) {\n gl_FragColor.a = dot(gl_FragColor.rgb, vec3(0.2125, 0.7154, 0.0721));\n }\n#ifdef PREMULTIPLY_ALPHA\n gl_FragColor.rgb *= gl_FragColor.a;\n#endif\n}\n@end"; +var hdrEssl = "@export clay.compositor.hdr.composite\n#define TONEMAPPING\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#ifdef LUM_ENABLED\nuniform sampler2D lum;\n#endif\n#ifdef LUT_ENABLED\nuniform sampler2D lut;\n#endif\n#ifdef COLOR_CORRECTION\nuniform float brightness : 0.0;\nuniform float contrast : 1.0;\nuniform float saturation : 1.0;\n#endif\n#ifdef VIGNETTE\nuniform float vignetteDarkness: 1.0;\nuniform float vignetteOffset: 1.0;\n#endif\nuniform float exposure : 1.0;\nuniform float bloomIntensity : 0.25;\nuniform float lensflareIntensity : 1;\nvarying vec2 v_Texcoord;\n@import clay.util.srgb\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}\nfloat eyeAdaption(float fLum)\n{\n return mix(0.2, fLum, 0.5);\n}\n#ifdef LUT_ENABLED\nvec3 lutTransform(vec3 color) {\n float blueColor = color.b * 63.0;\n vec2 quad1;\n quad1.y = floor(floor(blueColor) / 8.0);\n quad1.x = floor(blueColor) - (quad1.y * 8.0);\n vec2 quad2;\n quad2.y = floor(ceil(blueColor) / 8.0);\n quad2.x = ceil(blueColor) - (quad2.y * 8.0);\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 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 vec4 newColor1 = texture2D(lut, texPos1);\n vec4 newColor2 = texture2D(lut, texPos2);\n vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n return newColor.rgb;\n}\n#endif\n@import clay.util.rgbm\nvoid main()\n{\n vec4 texel = vec4(0.0);\n vec4 originalTexel = vec4(0.0);\n#ifdef TEXTURE_ENABLED\n texel = decodeHDR(texture2D(texture, v_Texcoord));\n originalTexel = texel;\n#endif\n#ifdef BLOOM_ENABLED\n vec4 bloomTexel = decodeHDR(texture2D(bloom, v_Texcoord));\n texel.rgb += bloomTexel.rgb * bloomIntensity;\n texel.a += bloomTexel.a * bloomIntensity;\n#endif\n#ifdef LENSFLARE_ENABLED\n texel += decodeHDR(texture2D(lensflare, v_Texcoord)) * texture2D(lensdirt, v_Texcoord) * lensflareIntensity;\n#endif\n texel.a = min(texel.a, 1.0);\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#ifdef TONEMAPPING\n texel.rgb *= exposureBias;\n texel.rgb = ACESToneMapping(texel.rgb);\n#endif\n texel = linearTosRGB(texel);\n#ifdef LUT_ENABLED\n texel.rgb = lutTransform(clamp(texel.rgb,vec3(0.0),vec3(1.0)));\n#endif\n#ifdef COLOR_CORRECTION\n texel.rgb = clamp(texel.rgb + vec3(brightness), 0.0, 1.0);\n texel.rgb = clamp((texel.rgb - vec3(0.5))*contrast+vec3(0.5), 0.0, 1.0);\n float lum = dot(texel.rgb, vec3(0.2125, 0.7154, 0.0721));\n texel.rgb = mix(vec3(lum), texel.rgb, saturation);\n#endif\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 gl_FragColor = encodeHDR(texel);\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)) * bloomIntensity);\n #elif DEBUG == 3\n gl_FragColor = encodeHDR(decodeHDR(texture2D(lensflare, v_Texcoord) * lensflareIntensity));\n #endif\n#endif\n if (originalTexel.a <= 0.01 && gl_FragColor.a > 1e-5) {\n gl_FragColor.a = dot(gl_FragColor.rgb, vec3(0.2125, 0.7154, 0.0721));\n }\n#ifdef PREMULTIPLY_ALPHA\n gl_FragColor.rgb *= gl_FragColor.a;\n#endif\n}\n@end"; var dofEssl = "@export clay.compositor.dof.coc\nuniform sampler2D depth;\nuniform float zNear: 0.1;\nuniform float zFar: 2000;\nuniform float focalDist: 3;\nuniform float focalRange: 1;\nuniform float focalLength: 30;\nuniform float fstop: 2.8;\nvarying vec2 v_Texcoord;\n@import clay.util.encode_float\nvoid main()\n{\n float z = texture2D(depth, v_Texcoord).r * 2.0 - 1.0;\n float dist = 2.0 * zNear * zFar / (zFar + zNear - z * (zFar - zNear));\n float aperture = focalLength / fstop;\n float coc;\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 coc = abs(aperture * (focalLength * (dist - focalAdjusted)) / (dist * (focalAdjusted - focalLength)));\n coc = clamp(coc, 0.0, 0.4) / 0.4000001;\n if (dist < lower) {\n coc = -coc;\n }\n coc = coc * 0.5 + 0.5;\n }\n gl_FragColor = encodeFloat(coc);\n}\n@end\n@export clay.compositor.dof.premultiply\nuniform sampler2D texture;\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\n@import clay.util.decode_float\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@export clay.compositor.dof.min_coc\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform vec2 textureSize : [512.0, 512.0];\n@import clay.util.float\nvoid main()\n{\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\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 gl_FragColor = encodeFloat(fCoc);\n}\n@end\n@export clay.compositor.dof.max_coc\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform vec2 textureSize : [512.0, 512.0];\n@import clay.util.float\nvoid main()\n{\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\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 gl_FragColor = encodeFloat(fCoc);\n}\n@end\n@export clay.compositor.dof.coc_upsample\n#define HIGH_QUALITY\nuniform sampler2D coc;\nuniform vec2 textureSize : [512, 512];\nuniform float sampleScale: 0.5;\nvarying vec2 v_Texcoord;\n@import clay.util.float\nvoid main()\n{\n#ifdef HIGH_QUALITY\n vec4 d = vec4(1.0, 1.0, -1.0, 0.0) / textureSize.xyxy * sampleScale;\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 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 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 gl_FragColor = encodeFloat(s / 16.0);\n#else\n vec4 d = vec4(-1.0, -1.0, +1.0, +1.0) / textureSize.xyxy;\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 gl_FragColor = encodeFloat(s / 4.0);\n#endif\n}\n@end\n@export clay.compositor.dof.upsample\n#define HIGH_QUALITY\nuniform sampler2D coc;\nuniform sampler2D texture;\nuniform vec2 textureSize : [512, 512];\nuniform float sampleScale: 0.5;\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\n@import clay.util.decode_float\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}\nvoid main()\n{\n#ifdef HIGH_QUALITY\n vec4 d = vec4(1.0, 1.0, -1.0, 0.0) / textureSize.xyxy * sampleScale;\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 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 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 gl_FragColor = encodeHDR(color / w);\n#else\n vec4 d = vec4(-1.0, -1.0, +1.0, +1.0) / textureSize.xyxy;\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 gl_FragColor = encodeHDR(color / w);\n#endif\n}\n@end\n@export clay.compositor.dof.downsample\nuniform sampler2D texture;\nuniform sampler2D coc;\nuniform vec2 textureSize : [512, 512];\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\n@import clay.util.decode_float\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}\nvoid main()\n{\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\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 gl_FragColor = encodeHDR(color);\n}\n@end\n@export clay.compositor.dof.hexagonal_blur_frag\n@import clay.util.float\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 float weightSum = 0.0;\n float kernelWeight = 1.0 / float(KERNEL_SIZE);\n for (int i = 0; i < KERNEL_SIZE; i++) {\n vec2 coord = v_Texcoord + offset * float(i);\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 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@end\n@export clay.compositor.dof.hexagonal_blur_1\n#define KERNEL_SIZE 5\nuniform sampler2D texture;\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform float blurSize : 1.0;\nuniform vec2 textureSize : [512.0, 512.0];\n@import clay.util.rgbm\n@import clay.compositor.dof.hexagonal_blur_frag\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 gl_FragColor = doBlur(texture, vec2(0.0, offset.y));\n#if !defined(BLUR_COC)\n gl_FragColor = encodeHDR(gl_FragColor);\n#endif\n}\n@end\n@export clay.compositor.dof.hexagonal_blur_2\n#define KERNEL_SIZE 5\nuniform sampler2D texture;\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform float blurSize : 1.0;\nuniform vec2 textureSize : [512.0, 512.0];\n@import clay.util.rgbm\n@import clay.compositor.dof.hexagonal_blur_frag\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 offset.y /= 2.0;\n gl_FragColor = doBlur(texture, -offset);\n#if !defined(BLUR_COC)\n gl_FragColor = encodeHDR(gl_FragColor);\n#endif\n}\n@end\n@export clay.compositor.dof.hexagonal_blur_3\n#define KERNEL_SIZE 5\nuniform sampler2D texture1;\nuniform sampler2D texture2;\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform float blurSize : 1.0;\nuniform vec2 textureSize : [512.0, 512.0];\n@import clay.util.rgbm\n@import clay.compositor.dof.hexagonal_blur_frag\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 offset.y /= 2.0;\n vec2 vDownRight = vec2(offset.x, -offset.y);\n vec4 texel1 = doBlur(texture1, -offset);\n vec4 texel2 = doBlur(texture1, vDownRight);\n vec4 texel3 = doBlur(texture2, vDownRight);\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#else\n vec4 color = (texel1 + texel2 + texel3) / 3.0;\n gl_FragColor = encodeHDR(color);\n#endif\n}\n@end\n@export clay.compositor.dof.composite\n#define DEBUG 0\nuniform sampler2D original;\nuniform sampler2D blurred;\nuniform sampler2D nearfield;\nuniform sampler2D coc;\nuniform sampler2D nearcoc;\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\n@import clay.util.float\nvoid main()\n{\n vec4 blurredColor = decodeHDR(texture2D(blurred, v_Texcoord));\n vec4 originalColor = decodeHDR(texture2D(original, v_Texcoord));\n float fCoc = decodeFloat(texture2D(coc, v_Texcoord));\n fCoc = abs(fCoc * 2.0 - 1.0);\n float weight = smoothstep(0.0, 1.0, fCoc);\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 gl_FragColor = encodeHDR(\n mix(\n nearfieldColor, mix(originalColor, blurredColor, weight),\n pow(1.0 - fNearCoc, 4.0)\n )\n );\n#else\n gl_FragColor = encodeHDR(mix(originalColor, blurredColor, weight));\n#endif\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@end"; @@ -28424,7 +28671,7 @@ function convertParameter(paramInfo) { if (val != null) { // Convert string to enum if (typeof val === 'string') { - val = Texture$1[val]; + val = Texture[val]; } param[name] = val; } @@ -28539,7 +28786,7 @@ function tryConvertExpr(string) { // Alias -var gbufferEssl = "@export clay.deferred.gbuffer.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\nuniform vec2 uvRepeat;\nuniform vec2 uvOffset;\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n#ifdef FIRST_PASS\nattribute vec3 normal : NORMAL;\n#endif\n@import clay.chunk.skinning_header\n#ifdef FIRST_PASS\nvarying vec3 v_Normal;\nattribute vec4 tangent : TANGENT;\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\nvarying vec3 v_WorldPosition;\n#endif\nvarying vec2 v_Texcoord;\nvoid main()\n{\n vec3 skinnedPosition = position;\n#ifdef FIRST_PASS\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n bool hasTangent = dot(tangent, tangent) > 0.0;\n#endif\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\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 gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n#ifdef FIRST_PASS\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\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 v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n#endif\n}\n@end\n@export clay.deferred.gbuffer1.fragment\nuniform mat4 viewInverse : VIEWINVERSE;\nuniform float glossiness;\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nuniform sampler2D normalMap;\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\nuniform sampler2D roughGlossMap;\nuniform bool useRoughGlossMap;\nuniform bool useRoughness;\nuniform bool doubleSided;\nuniform int roughGlossChannel: 0;\nfloat indexingTexel(in vec4 texel, in int idx) {\n if (idx == 3) return texel.a;\n else if (idx == 1) return texel.g;\n else if (idx == 2) return texel.b;\n else return texel.r;\n}\nvoid main()\n{\n vec3 N = v_Normal;\n if (doubleSided) {\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = eyePos - v_WorldPosition;\n if (dot(N, V) < 0.0) {\n N = -N;\n }\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 gl_FragColor.rgb = (N + 1.0) * 0.5;\n float g = glossiness;\n if (useRoughGlossMap) {\n float g2 = indexingTexel(texture2D(roughGlossMap, v_Texcoord), roughGlossChannel);\n if (useRoughness) {\n g2 = 1.0 - g2;\n }\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\n }\n gl_FragColor.a = g + 0.005;\n}\n@end\n@export clay.deferred.gbuffer2.fragment\nuniform sampler2D diffuseMap;\nuniform sampler2D metalnessMap;\nuniform vec3 color;\nuniform float metalness;\nuniform bool useMetalnessMap;\nuniform bool linear;\nvarying vec2 v_Texcoord;\n@import clay.util.srgb\nvoid main ()\n{\n float m = metalness;\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 gl_FragColor.rgb = texel.rgb * color;\n gl_FragColor.a = m + 0.005;\n}\n@end\n@export clay.deferred.gbuffer.debug\n@import clay.deferred.chunk.light_head\nuniform int debug: 0;\nvoid main ()\n{\n @import clay.deferred.chunk.gbuffer_read\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"; +var gbufferEssl = "@export clay.deferred.gbuffer.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\nuniform vec2 uvRepeat;\nuniform vec2 uvOffset;\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n#ifdef FIRST_PASS\nattribute vec3 normal : NORMAL;\n#endif\n@import clay.chunk.skinning_header\n#ifdef FIRST_PASS\nvarying vec3 v_Normal;\nattribute vec4 tangent : TANGENT;\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\nvarying vec3 v_WorldPosition;\n#endif\nvarying vec2 v_Texcoord;\nvoid main()\n{\n vec3 skinnedPosition = position;\n#ifdef FIRST_PASS\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n bool hasTangent = dot(tangent, tangent) > 0.0;\n#endif\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\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 gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n#ifdef FIRST_PASS\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\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 v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n#endif\n}\n@end\n@export clay.deferred.gbuffer1.fragment\nuniform mat4 viewInverse : VIEWINVERSE;\nuniform float glossiness;\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nuniform sampler2D normalMap;\nuniform sampler2D diffuseMap;\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\nuniform sampler2D roughGlossMap;\nuniform bool useRoughGlossMap;\nuniform bool useRoughness;\nuniform bool doubleSided;\nuniform float alphaCutoff: 0.0;\nuniform float alpha: 1.0;\nuniform int roughGlossChannel: 0;\nfloat indexingTexel(in vec4 texel, in int idx) {\n if (idx == 3) return texel.a;\n else if (idx == 1) return texel.g;\n else if (idx == 2) return texel.b;\n else return texel.r;\n}\nvoid main()\n{\n vec3 N = v_Normal;\n if (doubleSided) {\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = eyePos - v_WorldPosition;\n if (dot(N, V) < 0.0) {\n N = -N;\n }\n }\n if (alphaCutoff > 0.0) {\n float a = texture2D(diffuseMap, v_Texcoord).a * alpha;\n if (a < alphaCutoff) {\n discard;\n }\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 gl_FragColor.rgb = (N + 1.0) * 0.5;\n float g = glossiness;\n if (useRoughGlossMap) {\n float g2 = indexingTexel(texture2D(roughGlossMap, v_Texcoord), roughGlossChannel);\n if (useRoughness) {\n g2 = 1.0 - g2;\n }\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\n }\n gl_FragColor.a = g + 0.005;\n}\n@end\n@export clay.deferred.gbuffer2.fragment\nuniform sampler2D diffuseMap;\nuniform sampler2D metalnessMap;\nuniform vec3 color;\nuniform float metalness;\nuniform bool useMetalnessMap;\nuniform bool linear;\nuniform float alphaCutoff: 0.0;\nuniform float alpha: 1.0;\nvarying vec2 v_Texcoord;\n@import clay.util.srgb\nvoid main ()\n{\n float m = metalness;\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 if (alphaCutoff > 0.0) {\n float a = texel.a * alpha;\n if (a < alphaCutoff) {\n discard;\n }\n }\n gl_FragColor.rgb = texel.rgb * color;\n gl_FragColor.a = m + 0.005;\n}\n@end\n@export clay.deferred.gbuffer.debug\n@import clay.deferred.chunk.light_head\nuniform int debug: 0;\nvoid main ()\n{\n @import clay.deferred.chunk.gbuffer_read\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"; var chunkEssl = "@export clay.deferred.chunk.light_head\nuniform sampler2D gBufferTexture1;\nuniform sampler2D gBufferTexture2;\nuniform sampler2D gBufferTexture3;\nuniform vec2 windowSize: WINDOW_SIZE;\nuniform vec4 viewport: VIEWPORT;\nuniform mat4 viewProjectionInv;\n#ifdef DEPTH_ENCODED\n@import clay.util.decode_float\n#endif\n@end\n@export clay.deferred.chunk.gbuffer_read\n vec2 uv = gl_FragCoord.xy / windowSize;\n vec2 uv2 = (gl_FragCoord.xy - viewport.xy) / viewport.zw;\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 float glossiness = texel1.a;\n float metalness = texel3.a;\n vec3 N = texel1.rgb * 2.0 - 1.0;\n float z = texture2D(gBufferTexture2, uv).r * 2.0 - 1.0;\n vec2 xy = uv2 * 2.0 - 1.0;\n vec4 projectedPos = vec4(xy, z, 1.0);\n vec4 p4 = viewProjectionInv * projectedPos;\n vec3 position = p4.xyz / p4.w;\n vec3 albedo = texel3.rgb;\n vec3 diffuseColor = albedo * (1.0 - metalness);\n vec3 specularColor = mix(vec3(0.04), albedo, metalness);\n@end\n@export clay.deferred.chunk.light_equation\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}\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}\nvec3 F_Schlick(in float ndv, vec3 spec) {\n return spec + (1.0 - spec) * pow(1.0 - ndv, 5.0);\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@end"; @@ -28556,161 +28803,80 @@ function createFillCanvas(color) { return canvas; } -function attachTextureToSlot(renderer, program, symbol, texture, slot) { - var gl = renderer.gl; - program.setUniform(gl, '1i', symbol, slot); - - gl.activeTexture(gl.TEXTURE0 + slot); - // Maybe texture is not loaded yet; - if (texture.isRenderable()) { - texture.bind(renderer); - } - else { - // Bind texture to null - texture.unbind(renderer); - } -} - -// TODO Use globalShader insteadof globalMaterial? -function getBeforeRenderHook1 (gl, defaultNormalMap, defaultRoughnessMap) { - - var previousNormalMap; - var previousRougGlossMap; - var previousRenderable; - - return function (renderable, gBufferMat, prevMaterial) { - // Material not change - if (previousRenderable && previousRenderable.material === renderable.material) { - return; - } +// TODO specularColor +// TODO Performance improvement +function getGetUniformHook1(gl, defaultNormalMap, defaultRoughnessMap, defaultDiffuseMap) { + return function (renderable, gBufferMat, symbol) { var standardMaterial = renderable.material; - var program = renderable.__program; - - var glossiness; - var roughGlossMap; - var useRoughnessWorkflow = standardMaterial.isDefined('fragment', 'USE_ROUGHNESS'); - var doubleSided = standardMaterial.isDefined('fragment', 'DOUBLE_SIDED'); - var roughGlossChannel; - if (useRoughnessWorkflow) { - glossiness = 1.0 - standardMaterial.get('roughness'); - roughGlossMap = standardMaterial.get('roughnessMap'); - roughGlossChannel = standardMaterial.getDefine('fragment', 'ROUGHNESS_CHANNEL'); + if (symbol === 'doubleSided') { + return standardMaterial.isDefined('fragment', 'DOUBLE_SIDED'); } - else { - glossiness = standardMaterial.get('glossiness'); - roughGlossMap = standardMaterial.get('glossinessMap'); - roughGlossChannel = standardMaterial.getDefine('fragment', 'GLOSSINESS_CHANNEL'); + else if (symbol === 'uvRepeat' || symbol === 'uvOffset' || symbol === 'alpha') { + return standardMaterial.get(symbol); } - var useRoughGlossMap = !!roughGlossMap; - - var normalMap = standardMaterial.get('normalMap') || defaultNormalMap; - var uvRepeat = standardMaterial.get('uvRepeat'); - var uvOffset = standardMaterial.get('uvOffset'); - - roughGlossMap = roughGlossMap || defaultRoughnessMap; - - if (prevMaterial !== gBufferMat) { - gBufferMat.set('glossiness', glossiness); - gBufferMat.set('normalMap', normalMap); - gBufferMat.set('roughGlossMap', roughGlossMap); - gBufferMat.set('useRoughGlossMap', +useRoughGlossMap); - gBufferMat.set('useRoughness', +useRoughnessWorkflow); - gBufferMat.set('doubleSided', +doubleSided); - gBufferMat.set('roughGlossChannel', +roughGlossChannel || 0); - gBufferMat.set('uvRepeat', uvRepeat); - gBufferMat.set('uvOffset', uvOffset); + else if (symbol === 'normalMap') { + return standardMaterial.get(symbol) || defaultNormalMap; } - else { - program.setUniform( - gl, '1f', 'glossiness', glossiness - ); - - if (previousNormalMap !== normalMap) { - attachTextureToSlot(this, program, 'normalMap', normalMap, 0); - } - if (previousRougGlossMap !== roughGlossMap) { - attachTextureToSlot(this, program, 'roughGlossMap', roughGlossMap, 1); - } - program.setUniform(gl, '1i', 'useRoughGlossMap', +useRoughGlossMap); - program.setUniform(gl, '1i', 'useRoughness', +useRoughnessWorkflow); - program.setUniform(gl, '1i', 'doubleSided', +doubleSided); - program.setUniform(gl, '1i', 'roughGlossChannel', +roughGlossChannel || 0); - if (uvRepeat != null) { - program.setUniform(gl, '2f', 'uvRepeat', uvRepeat); + else if (symbol === 'diffuseMap') { + return standardMaterial.get(symbol) || defaultDiffuseMap; + } + else if (symbol === 'alphaCutoff') { + // TODO DIFFUSEMAP_ALPHA_ALPHA + if (standardMaterial.isDefined('fragment', 'ALPHA_TEST')) { + var alphaCutoff = standardMaterial.get('alphaCutoff'); + return alphaCutoff || 0; } - if (uvOffset != null) { - program.setUniform(gl, '2f', 'uvOffset', uvOffset); + return 0; + } + else { + var useRoughnessWorkflow = standardMaterial.isDefined('fragment', 'USE_ROUGHNESS'); + var roughGlossMap = useRoughnessWorkflow ? standardMaterial.get('roughnessMap') : standardMaterial.get('glossinessMap'); + switch (symbol) { + case 'glossiness': + return useRoughnessWorkflow ? (1.0 - standardMaterial.get('roughness')) : standardMaterial.get('glossiness'); + case 'roughGlossMap': + return roughGlossMap; + case 'useRoughGlossMap': + return !!roughGlossMap; + case 'useRoughness': + return useRoughnessWorkflow; + case 'roughGlossChannel': + return useRoughnessWorkflow + ? standardMaterial.getDefine('fragment', 'ROUGHNESS_CHANNEL') + : standardMaterial.getDefine('fragment', 'GLOSSINESS_CHANNEL'); } } - - previousNormalMap = normalMap; - previousRougGlossMap = roughGlossMap; - - previousRenderable = renderable; }; } -function getBeforeRenderHook2(gl, defaultDiffuseMap, defaultMetalnessMap) { - var previousDiffuseMap; - var previousRenderable; - var previousMetalnessMap; - - return function (renderable, gBufferMat, prevMaterial) { - // Material not change - if (previousRenderable && previousRenderable.material === renderable.material) { - return; - } - - var program = renderable.__program; +function getGetUniformHook2(gl, defaultDiffuseMap, defaultMetalnessMap) { + return function (renderable, gBufferMat, symbol) { var standardMaterial = 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); - // TODO - gBufferMat.set('linear', +standardMaterial.linear || 0); - } - else { - program.setUniform(gl, '1f', 'metalness', metalness); - - program.setUniform(gl, '3f', 'color', color); - if (previousDiffuseMap !== diffuseMap) { - attachTextureToSlot(this, program, 'diffuseMap', diffuseMap, 0); - } - if (previousMetalnessMap !== metalnessMap) { - attachTextureToSlot(this, program, 'metalnessMap', metalnessMap, 1); - } - program.setUniform(gl, '1i', 'useMetalnessMap', +useMetalnessMap); - program.setUniform(gl, '2f', 'uvRepeat', uvRepeat); - program.setUniform(gl, '2f', 'uvOffset', uvOffset); - - program.setUniform(gl, '1i', 'linear', +standardMaterial.linear || 0); + switch (symbol) { + case 'color': + case 'uvRepeat': + case 'uvOffset': + case 'alpha': + return standardMaterial.get(symbol); + case 'metalness': + return standardMaterial.get('metalness') || 0; + case 'diffuseMap': + return standardMaterial.get(symbol) || defaultDiffuseMap; + case 'metalnessMap': + return standardMaterial.get(symbol) || defaultMetalnessMap; + case 'useMetalnessMap': + return !!standardMaterial.get('metalnessMap'); + case 'linear': + return standardMaterial.isDefined('SRGB_DECODE'); + case 'alphaCutoff': + // TODO DIFFUSEMAP_ALPHA_ALPHA + if (standardMaterial.isDefined('fragment', 'ALPHA_TEST')) { + var alphaCutoff = standardMaterial.get('alphaCutoff'); + return alphaCutoff || 0.0; + } + return 0.0; } - - previousDiffuseMap = diffuseMap; - previousMetalnessMap = metalnessMap; - - previousRenderable = renderable; }; } @@ -28726,7 +28892,7 @@ function getBeforeRenderHook2(gl, defaultDiffuseMap, defaultMetalnessMap) { */ var GBuffer = Base.extend(function () { - return { + return /** @lends clay.deferred.GBuffer# */ { enableTargetTexture1: true, @@ -28742,21 +28908,21 @@ var GBuffer = Base.extend(function () { // - B: normal.z // - A: glossiness _gBufferTex1: new Texture2D({ - minFilter: Texture$1.NEAREST, - magFilter: Texture$1.NEAREST, + minFilter: Texture.NEAREST, + magFilter: Texture.NEAREST, // PENDING - type: Texture$1.HALF_FLOAT + type: Texture.HALF_FLOAT }), // - R: depth _gBufferTex2: new Texture2D({ - minFilter: Texture$1.NEAREST, - magFilter: Texture$1.NEAREST, + minFilter: Texture.NEAREST, + magFilter: Texture.NEAREST, // format: Texture.DEPTH_COMPONENT, // type: Texture.UNSIGNED_INT - format: Texture$1.DEPTH_STENCIL, - type: Texture$1.UNSIGNED_INT_24_8_WEBGL + format: Texture.DEPTH_STENCIL, + type: Texture.UNSIGNED_INT_24_8_WEBGL }), // - R: albedo.r @@ -28764,8 +28930,8 @@ var GBuffer = Base.extend(function () { // - B: albedo.b // - A: metalness _gBufferTex3: new Texture2D({ - minFilter: Texture$1.NEAREST, - magFilter: Texture$1.NEAREST + minFilter: Texture.NEAREST, + magFilter: Texture.NEAREST }), _defaultNormalMap: new Texture2D({ @@ -28860,10 +29026,10 @@ var GBuffer = Base.extend(function () { }, /** - * Update G Buffer + * Update GBuffer * @param {clay.Renderer} renderer * @param {clay.Scene} scene - * @param {clay.camera.Perspective} camera + * @param {clay.Camera} camera */ update: function (renderer, scene, camera) { @@ -28872,7 +29038,7 @@ var GBuffer = Base.extend(function () { var frameBuffer = this._frameBuffer; var viewport = frameBuffer.viewport; - var renderList = scene.updateRenderList(camera); + var renderList = scene.updateRenderList(camera, true); var opaqueList = renderList.opaque; var transparentList = renderList.transparent; @@ -28932,7 +29098,10 @@ var GBuffer = Base.extend(function () { getMaterial: function () { return gBufferMaterial1; }, - beforeRender: getBeforeRenderHook1(gl, this._defaultNormalMap, this._defaultRoughnessMap), + getUniform: getGetUniformHook1(gl, this._defaultNormalMap, this._defaultRoughnessMap, this._defaultDiffuseMap), + isMaterialChanged: function (renderable, prevRenderable, material, prevMaterial) { + return renderable.material !== prevRenderable.material; + }, sortCompare: renderer.opaqueSortCompare }; // FIXME Use MRT if possible @@ -28961,7 +29130,10 @@ var GBuffer = Base.extend(function () { getMaterial: function () { return gBufferMaterial2; }, - beforeRender: getBeforeRenderHook2(gl, this._defaultDiffuseMap, this._defaultMetalnessMap), + getUniform: getGetUniformHook2(gl, this._defaultDiffuseMap, this._defaultMetalnessMap), + isMaterialChanged: function (renderable, prevRenderable, material, prevMaterial) { + return !prevRenderable || renderable.material !== prevRenderable.material; + }, sortCompare: renderer.opaqueSortCompare }; renderer.renderPass(gBufferRenderList, camera, passConfig); @@ -28971,6 +29143,20 @@ var GBuffer = Base.extend(function () { frameBuffer.unbind(renderer); }, + /** + * Debug output of gBuffer. Use `type` parameter to choos the debug output type, which can be: + * + * + 'normal' + * + 'depth' + * + 'position' + * + 'glossiness' + * + 'metalness' + * + 'albedo' + * + * @param {clay.Renderer} renderer + * @param {clay.Camera} camera + * @param {string} [type='normal'] + */ renderDebug: function (renderer, camera, type, viewport) { var debugTypes = { normal: 0, @@ -29050,6 +29236,15 @@ var GBuffer = Base.extend(function () { * @param {clay.Renderer} renderer */ dispose: function (renderer) { + this._gBufferTex1.dispose(renderer); + this._gBufferTex2.dispose(renderer); + this._gBufferTex3.dispose(renderer); + + this._defaultNormalMap.dispose(renderer); + this._defaultRoughnessMap.dispose(renderer); + this._defaultMetalnessMap.dispose(renderer); + this._defaultDiffuseMap.dispose(renderer); + this._frameBuffer.dispose(renderer); } }); @@ -29066,9 +29261,7 @@ var vec2$1 = glmatrix.vec2; * @param {number} [opt.capSegments] * @param {number} [opt.heightSegments] */ -var Cone$1 = Geometry.extend( -/** @lends clay.geometry.Cone# */ -{ +var Cone$1 = Geometry.extend(/** @lends clay.geometry.Cone# */ { dynamic: false, /** * @type {number} @@ -29300,7 +29493,7 @@ var DeferredRenderer = Base.extend(function () { var lightAccumulateBlendFunc = function (gl) { gl.blendEquation(gl.FUNC_ADD); - gl.blendFunc(gl.ONE, gl.ONE); + gl.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.ONE); }; var createLightPassMat = function (shader) { @@ -29352,15 +29545,13 @@ var DeferredRenderer = Base.extend(function () { _gBuffer: new GBuffer(), - _lightAccumFrameBuffer: new FrameBuffer({ - depthBuffer: false - }), + _lightAccumFrameBuffer: new FrameBuffer(), _lightAccumTex: new Texture2D({ // FIXME Device not support float texture - type: Texture$1.HALF_FLOAT, - minFilter: Texture$1.NEAREST, - magFilter: Texture$1.NEAREST + type: Texture.HALF_FLOAT, + minFilter: Texture.NEAREST, + magFilter: Texture.NEAREST }), _fullQuadPass: new Pass({ @@ -29420,6 +29611,7 @@ var DeferredRenderer = Base.extend(function () { scene.update(false, true); } scene.updateLights(); + // Render list will be updated in gbuffer. camera.update(true); @@ -29821,14 +30013,8 @@ var DeferredRenderer = Base.extend(function () { return function (renderer, scene, 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); - gl.clear(gl.DEPTH_BUFFER_BIT); - for (var i = 0; i < volumeMeshList.length; i++) { var volumeMesh = volumeMeshList[i]; @@ -29850,8 +30036,8 @@ var DeferredRenderer = Base.extend(function () { // Render light gl.colorMask(true, true, true, true); - gl.depthMask(false); + volumeMesh.material.depthMask = true; renderer.renderPass([volumeMesh], camera); } @@ -32586,7 +32772,7 @@ function unpackID(r, g, b){ * domElement: renderer.canvas * }); * ... - * animation.on('frame', function(frameTime) { + * timeline.on('frame', function(frameTime) { * control.update(frameTime); * renderer.render(scene, camera); * }); @@ -32632,7 +32818,7 @@ var FreeControl = Base.extend(function() { /** * @type {clay.Timeline} */ - animation: null, + timeline: null, _moveForward: false, _moveBackward: false, @@ -32649,15 +32835,15 @@ var FreeControl = Base.extend(function() { this._mouseMove = this._mouseMove.bind(this); if (this.domElement) { - this.enable(); + this.init(); } }, /** @lends clay.plugin.FreeControl.prototype */ { /** - * Enable control + * init control */ - enable: function() { + init: function() { // Use pointer lock // http://www.html5rocks.com/en/tutorials/pointerlock/intro/ var el = this.domElement; @@ -32673,17 +32859,15 @@ var FreeControl = Base.extend(function() { document.addEventListener('keydown', this._keyDown); document.addEventListener('keyup', this._keyUp); - if (this.animation) { - this.animation.on('frame', this._detectMovementChange, this); + if (this.timeline) { + this.timeline.on('frame', this._detectMovementChange, this); } }, /** - * Disable control + * Dispose control */ - disable: function() { - - this.target.off('beforeupdate', this._beforeUpdateCamera); + dispose: function() { var el = this.domElement; @@ -32704,8 +32888,8 @@ var FreeControl = Base.extend(function() { document.removeEventListener('keydown', this._keyDown); document.removeEventListener('keyup', this._keyUp); - if (this.animation) { - this.animation.off('frame', this._detectMovementChange); + if (this.timeline) { + this.timeline.off('frame', this._detectMovementChange); } }, @@ -32722,7 +32906,7 @@ var FreeControl = Base.extend(function() { * Control update. Should be invoked every frame * @param {number} frameTime Frame time */ - update: function(frameTime) { + update: function (frameTime) { var target = this.target; var position = this.target.position; @@ -32764,7 +32948,8 @@ var FreeControl = Base.extend(function() { || document.webkitPointerLockElement === this.domElement ) { document.addEventListener('mousemove', this._mouseMove, false); - } else { + } + else { document.removeEventListener('mousemove', this._mouseMove); } }, @@ -34243,7 +34428,7 @@ function copyIfNecessary(arr, shallow) { /** * @name clay.version */ -var version = '1.1.0'; +var version = '1.1.1'; var outputEssl$1 = "@export clay.vr.disorter.output.vertex\nattribute vec2 texcoord: TEXCOORD_0;\nattribute vec3 position: POSITION;\nvarying vec2 v_Texcoord;\nvoid main()\n{\n v_Texcoord = texcoord;\n gl_Position = vec4(position.xy, 0.5, 1.0);\n}\n@end\n@export clay.vr.disorter.output.fragment\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\nvoid main()\n{\n gl_FragColor = texture2D(texture, v_Texcoord);\n}\n@end"; @@ -34738,7 +34923,7 @@ exports.shader = shader; exports.Skeleton = Skeleton; exports.StandardMaterial = StandardMaterial; exports.StaticGeometry = StaticGeometry; -exports.Texture = Texture$1; +exports.Texture = Texture; exports.Texture2D = Texture2D; exports.TextureCube = TextureCube; exports.Timeline = Timeline; diff --git a/dist/claygl.min.js b/dist/claygl.min.js index 407cc9c88..72a3e4bea 100644 --- a/dist/claygl.min.js +++ b/dist/claygl.min.js @@ -1,14 +1,14 @@ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e(t.clay={})}(this,function(t){"use strict";function e(){}function r(t,e){return t[e]}function n(t,e,r){t[e]=r}function i(t,e,r){return(e-t)*r+t}function a(t,e,r,n,a){var o=t.length;if(1==a)for(var s=0;si)t.length=i;else for(var a=n;a=0&&!(x[w]<=e);w--);w=Math.min(w,v-2)}else{for(w=O;we);w++);w=Math.min(w-1,v-2)}O=w,B=e;var r=x[w+1]-x[w];0!==r&&(L=(e-x[w])/r,L=Math.max(Math.min(1,L),0),L=b[w+1](L),_?(P=E[w],N=E[0===w?w:w-1],I=E[w>v-2?v-1:w+1],D=E[w>v-3?v-1:w+2],f?p(t,s,f(m(t,s),N,P,I,D,L)):y?u(N,P,I,D,L,L*L,L*L*L,m(t,s),T):p(t,s,l(N,P,I,D,L,L*L,L*L*L))):f?p(t,s,f(m(t,s),E[w],E[w+1],L)):y?a(E[w],E[w+1],L,m(t,s),T):p(t,s,i(E[w],E[w+1],L)))},F=new ae({target:t._target,life:d,loop:t._loop,delay:t._delay,onframe:U,onfinish:r});return e&&"spline"!==e&&F.setEasing(e),F}}}function d(t,e,i,a,o){this._tracks={},this._target=t,this._loop=e||!1,this._getter=i||r,this._setter=a||n,this._interpolater=o||null,this._delay=0,this._doneList=[],this._onframeList=[],this._clipList=[],this._maxTime=0,this._lastKFTime=0}function m(t){return t}function p(t){var e,r,n,i,a,o,s=Number.POSITIVE_INFINITY,u=Number.POSITIVE_INFINITY,l=Number.NEGATIVE_INFINITY,c=Number.NEGATIVE_INFINITY;for(e=t.length;e--;)t[e][0]l&&(l=t[e][0]),t[e][1]c&&(c=t[e][1]);return r=l-s,n=c-u,i=Math.max(r,n),a=s+.5*r,o=u+.5*n,[[a-20*i,o-i],[a,o+20*i],[a+20*i,o-i]]}function _(t,e,r,n){var i,a,o,s,u,l,c,h,f,d,m=t[e][0],p=t[e][1],_=t[r][0],v=t[r][1],g=t[n][0],y=t[n][1],T=Math.abs(p-v),x=Math.abs(v-y);if(Tx?o*(i-u)+c:s*(i-l)+h),f=_-i,d=v-a,{i:e,j:r,k:n,x:i,y:a,r:f*f+d*d}}function v(t){var e,r,n,i,a,o;for(r=t.length;r;)for(i=t[--r],n=t[--r],e=r;e;)if(o=t[--e],a=t[--e],n===a&&i===o||n===o&&i===a){t.splice(r,2),t.splice(e,2);break}}function g(t,e,r,n,i,a){var o=e[i],s=e[i+1],u=e[i+2];return t[0]=o+n*(r[a]-o),t[1]=s+n*(r[a+1]-s),t[2]=u+n*(r[a+2]-u),t}function y(t,e,r,n,i,a){var o,s,u,l,c,h=e[0+i],f=e[1+i],d=e[2+i],m=e[3+i],p=r[0+a],_=r[1+a],v=r[2+a],g=r[3+a];return s=h*p+f*_+d*v+m*g,s<0&&(s=-s,p=-p,_=-_,v=-v,g=-g),1-s>1e-6?(o=Math.acos(s),u=Math.sin(o),l=Math.sin((1-n)*o)/u,c=Math.sin(n*o)/u):(l=1-n,c=n),t[0]=l*h+c*p,t[1]=l*f+c*_,t[2]=l*d+c*v,t[3]=l*m+c*g,t}function T(t,e,r){"object"==typeof e&&(r=e,e=null);var n,i=this;if(!(t instanceof Function)){n=[];for(var a in t)t.hasOwnProperty(a)&&n.push(a)}var o=function(e){if(i.apply(this,arguments),t instanceof Function?x(this,t.call(this,e)):E(this,t,n),this.constructor===o)for(var r=o.__initializers__,a=0;ar?r:t}function w(t){return t=Math.round(t),t<0?0:t>255?255:t}function C(t){return t=Math.round(t),t<0?0:t>360?360:t}function M(t){return t<0?0:t>1?1:t}function R(t){return w(t.length&&"%"===t.charAt(t.length-1)?parseFloat(t)/100*255:parseInt(t,10))}function L(t){return M(t.length&&"%"===t.charAt(t.length-1)?parseFloat(t)/100:parseFloat(t))}function N(t,e,r){return r<0?r+=1:r>1&&(r-=1),6*r<1?t+(e-t)*r*6:2*r<1?e:3*r<2?t+(e-t)*(2/3-r)*6:t}function P(t,e,r){return t+(e-t)*r}function I(t,e,r,n,i){return t[0]=e,t[1]=r,t[2]=n,t[3]=i,t}function D(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t}function O(t,e){cr&&D(cr,e),cr=lr.put(t,cr||e.slice())}function B(t,e){var r=(parseFloat(t[0])%360+360)%360/360,n=L(t[1]),i=L(t[2]),a=i<=.5?i*(n+1):i+n-i*n,o=2*i-a;return e=e||[],I(e,w(255*N(o,a,r+1/3)),w(255*N(o,a,r)),w(255*N(o,a,r-1/3)),1),4===t.length&&(e[3]=t[3]),e}function U(t){if(t){var e,r,n=t[0]/255,i=t[1]/255,a=t[2]/255,o=Math.min(n,i,a),s=Math.max(n,i,a),u=s-o,l=(s+o)/2;if(0===u)e=0,r=0;else{r=l<.5?u/(s+o):u/(2-s-o);var c=((s-n)/6+u/2)/u,h=((s-i)/6+u/2)/u,f=((s-a)/6+u/2)/u;n===s?e=f-h:i===s?e=1/3+c-f:a===s&&(e=2/3+h-c),e<0&&(e+=1),e>1&&(e-=1)}var d=[360*e,r,l];return null!=t[3]&&d.push(t[3]),d}}function F(t){var e=Object.keys(t);e.sort();for(var r=[],n=0;n0&&n.push("#define "+i.toUpperCase()+"_COUNT "+a)}if(r)for(var o=0;o=0){if(1!==u&&4!==u){K();break}u=2,c=[]}else if(1!==u)if(4!==u)r(h),u=0;else{var f=h;wr.indexOf(f)>=0||Cr.indexOf(f)>=0||Mr.indexOf(f)>=0?l[s].semantic=f:"ignore"===f||"unconfigurable"===f?l[s].ignore=!0:l[s].value="bool"===t?"true"===f:parseFloat(f)}else l[s].value="bool"===t?"true"===h:parseFloat(h),c=null;else{if(2!==u){K();break}if(!(c instanceof Array)){K();break}c.push(+i[++o])}else l[s].value=new Fe.Float32Array(c),c=null,u=5;else if(2===u){if(!(c instanceof Array)){K();break}c.push(+i[++o])}else u=5;else u=4;else{if(0!==u&&3!==u){K();break}u=1}}return l}function J(t,e){"object"==typeof t&&(e=t.fragment,t=t.vertex),t=Y(t),e=Y(e),this._shaderID=q(t,e),this._vertexCode=J.parseImport(t),this._fragmentCode=J.parseImport(e),this.attributeSemantics={},this.matrixSemantics={},this.uniformSemantics={},this.matrixSemanticKeys=[],this.uniformTemplates={},this.attributes={},this.textures={},this.vertexDefines={},this.fragmentDefines={},this._parseAttributes(),this._parseUniforms(),this._parseDefines()}function Q(t){return t.material}function $(){}function tt(t,e,r){this.availableAttributes=t,this.availableAttributeSymbols=e,this.indicesBuffer=r,this.vao=null}function et(t){var e=[],r=Object.keys(t);r.sort();for(var n=0;n=400?t.onerror&&t.onerror():t.onload&&t.onload(e.response)},t.onerror&&(e.onerror=t.onerror),e.send(null)}function _t(t,e,r,n){var i=t.accessors[r],a=e.bufferViews[i.bufferView],o=i.byteOffset||0,s=si[i.componentType]||Fe.Float32Array,u=ui[i.type];null==u&&n&&(u=1);var l=new s(a,o,u*i.count),c=i.extensions&&i.extensions.WEB3D_quantized_attributes;if(c){for(var h,f,d=new Fe.Float32Array(u*i.count),m=c.decodeMatrix,h=new Array(u),f=new Array(u),p=0;p0){var i=Math.pow(2,t[3]-128-8+n);e[r+0]=t[0]*i,e[r+1]=t[1]*i,e[r+2]=t[2]*i}else e[r+0]=0,e[r+1]=0,e[r+2]=0;return e[r+3]=1,e}function yt(t,e,r){for(var n="",i=e;i0;)if(t[a][0]=e[r++],t[a][1]=e[r++],t[a][2]=e[r++],t[a][3]=e[r++],1===t[a][0]&&1===t[a][1]&&1===t[a][2]){for(var s=t[a][3]<>>0;s>0;s--)Tt(t[a-1],t[a]),a++,o--;i+=8}else a++,o--,i=0;return r}function Et(t,e,r,n){if(nIi)return xt(t,e,r,n);var i=e[r++];if(2!=i)return xt(t,e,r-1,n);if(t[0][1]=e[r++],t[0][2]=e[r++],i=e[r++],(t[0][2]<<8>>>0|i)>>>0!==n)return null;for(var i=0;i<4;i++)for(var a=0;a128){o=(127&o)>>>0;for(var s=e[r++];o--;)t[a++][i]=s}else for(;o--;)t[a++][i]=e[r++]}return r}function bt(t){Se.defaultsWithPropList(t,Gi,Vi),At(t);for(var e="",r=0;r>16,r=t-(e<<8)>>8;return[e,r,t-(e<<16)-(r<<8)]}function Jt(t,e,r){return(t<<16)+(e<<8)+r}function Qt(t){var e=t[1][0]-t[0][0],r=t[1][1]-t[0][1];return Math.sqrt(e*e+r*r)}function $t(t){return[(t[0][0]+t[1][0])/2,(t[0][1]+t[1][1])/2]}function te(t){return Array.isArray(t)||(t=[t,t]),t}function ee(t,e){return{name:t.name,type:t.type,size:t.size,semantic:t.semantic,value:re(t.value,e)}}function re(t,e){return e?t:new t.constructor(t)}function ne(t,e,r){return t*(1-r)+e*r}var ie={linear:function(t){return t},quadraticIn:function(t){return t*t},quadraticOut:function(t){return t*(2-t)},quadraticInOut:function(t){return(t*=2)<1?.5*t*t:-.5*(--t*(t-2)-1)},cubicIn:function(t){return t*t*t},cubicOut:function(t){return--t*t*t+1},cubicInOut:function(t){return(t*=2)<1?.5*t*t*t:.5*((t-=2)*t*t+2)},quarticIn:function(t){return t*t*t*t},quarticOut:function(t){return 1- --t*t*t*t},quarticInOut:function(t){return(t*=2)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2)},quinticIn:function(t){return t*t*t*t*t},quinticOut:function(t){return--t*t*t*t*t+1},quinticInOut:function(t){return(t*=2)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)},sinusoidalIn:function(t){return 1-Math.cos(t*Math.PI/2)},sinusoidalOut:function(t){return Math.sin(t*Math.PI/2)},sinusoidalInOut:function(t){return.5*(1-Math.cos(Math.PI*t))},exponentialIn:function(t){return 0===t?0:Math.pow(1024,t-1)},exponentialOut:function(t){return 1===t?1:1-Math.pow(2,-10*t)},exponentialInOut:function(t){return 0===t?0:1===t?1:(t*=2)<1?.5*Math.pow(1024,t-1):.5*(2-Math.pow(2,-10*(t-1)))},circularIn:function(t){return 1-Math.sqrt(1-t*t)},circularOut:function(t){return Math.sqrt(1- --t*t)},circularInOut:function(t){return(t*=2)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},elasticIn:function(t){var e,r=.1;return 0===t?0:1===t?1:(!r||r<1?(r=1,e=.1):e=.4*Math.asin(1/r)/(2*Math.PI),-r*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/.4))},elasticOut:function(t){var e,r=.1;return 0===t?0:1===t?1:(!r||r<1?(r=1,e=.1):e=.4*Math.asin(1/r)/(2*Math.PI),r*Math.pow(2,-10*t)*Math.sin((t-e)*(2*Math.PI)/.4)+1)},elasticInOut:function(t){var e,r=.1;return 0===t?0:1===t?1:(!r||r<1?(r=1,e=.1):e=.4*Math.asin(1/r)/(2*Math.PI),(t*=2)<1?r*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/.4)*-.5:r*Math.pow(2,-10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/.4)*.5+1)},backIn:function(t){var e=1.70158;return t*t*((e+1)*t-e)},backOut:function(t){var e=1.70158;return--t*t*((e+1)*t+e)+1},backInOut:function(t){var e=2.5949095;return(t*=2)<1?t*t*((e+1)*t-e)*.5:.5*((t-=2)*t*((e+1)*t+e)+2)},bounceIn:function(t){return 1-ie.bounceOut(1-t)},bounceOut:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},bounceInOut:function(t){return t<.5?.5*ie.bounceIn(2*t):.5*ie.bounceOut(2*t-1)+.5}},ae=function(t){t=t||{},this.name=t.name||"",this.target=t.target,this.life=t.life||1e3,this.delay=t.delay||0,this.gap=t.gap||0,this.playbackRate=t.playbackRate||1,this._initialized=!1,this._elapsedTime=0,this._loop=null!=t.loop&&t.loop,this.setLoop(this._loop),null!=t.easing&&this.setEasing(t.easing),this.onframe=t.onframe||e,this.onfinish=t.onfinish||e,this.onrestart=t.onrestart||e,this._paused=!1};ae.prototype={gap:0,life:0,delay:0,setLoop:function(t){this._loop=t,t&&(this._loopRemained="number"==typeof t?t:1/0)},setEasing:function(t){"string"==typeof t&&(t=ie[t]),this.easing=t},step:function(t,e,r){if(this._initialized||(this._startTime=t+this.delay,this._initialized=!0),null!=this._currentTime&&(e=t-this._currentTime),this._currentTime=t,this._paused)return"paused";if(!(t0?(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&&(this._elapse(t),e=this._elapsedTime%this.life),t=t||Date.now(),this._startTime=t-e+this.delay,this._elapsedTime=0,this._needsRemove=!1,this._paused=!1},getElapsedTime:function(){return this._elapsedTime},_restartInLoop:function(t){this._startTime=t+this.gap,this._elapsedTime=0},_elapse:function(t,e){this._elapsedTime+=e*this.playbackRate},fire:function(t,e){var r="on"+t;this[r]&&this[r](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},pause:function(){this._paused=!0},resume:function(){this._paused=!1}},ae.prototype.constructor=ae;var oe=Array.prototype.slice;d.prototype={constructor:d,when:function(t,e,r){this._maxTime=Math.max(t,this._maxTime),r=("function"==typeof r?r:ie[r])||m;for(var n in e)this._tracks[n]||(this._tracks[n]=[],0!==t&&this._tracks[n].push({time:0,value:s(this._getter(this._target,n)),easing:r})),this._tracks[n].push({time:parseInt(t),value:e[n],easing:r});return this},then:function(t,e,r){return this.when(t+this._lastKFTime,e,r),this._lastKFTime+=t,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,r=0;rt)this.inputs.unshift(n);else if(this.inputs[i-1].position<=t)this.inputs.push(n);else{var a=this._findKey(t);this.inputs.splice(a,n)}return n},ue.prototype.step=function(t,e,r){var n=ae.prototype.step.call(this,t);return"finish"!==n&&this.setTime(this.getElapsedTime()),r||"paused"===n||this.fire("frame"),n},ue.prototype.setTime=function(t){var e=this.position,r=this.inputs,n=r.length,i=r[0].position,a=r[n-1].position;if(e<=i||e>=a){var o=e<=i?r[0]:r[n-1],s=o.clip,u=o.offset;s.setTime((t+u)%s.life),s.output instanceof ae?this.output.copy(s.output):this.output.copy(s)}else{var l=this._findKey(e),c=r[l],h=r[l+1],f=c.clip,d=h.clip;f.setTime((t+c.offset)%f.life),d.setTime((t+h.offset)%d.life);var m=(this.position-c.position)/(h.position-c.position),p=f.output instanceof ae?f.output:f,_=d.output instanceof ae?d.output:d;this.output.blend1D(p,_,m)}},ue.prototype.clone=function(t){var e=ae.prototype.clone.call(this);e.output=this.output.clone();for(var r=0;r=r[i].position&&t=0;i--)t>=r[i].position&&t=0&&(this._cacheKey=e,this._cachePosition=t),e};var le=1/1048576,ce={triangulate:function(t,e){var r,n,i,a,o,s,u,l,c,h,f,d,m=t.length;if(m<3)return[];if(t=t.slice(0),e)for(r=m;r--;)t[r]=t[r][e];for(i=new Array(m),r=m;r--;)i[r]=r;for(i.sort(function(e,r){var n=t[r][0]-t[e][0];return 0!==n?n:e-r}),a=p(t),t.push(a[0],a[1],a[2]),o=[_(t,m+0,m+1,m+2)],s=[],u=[],r=i.length;r--;u.length=0){for(d=i[r],n=o.length;n--;)l=t[d][0]-o[n].x,l>0&&l*l>o[n].r?(s.push(o[n]),o.splice(n,1)):(c=t[d][1]-o[n].y,l*l+c*c-o[n].r>le||(u.push(o[n].i,o[n].j,o[n].j,o[n].k,o[n].k,o[n].i),o.splice(n,1)));for(v(u),n=u.length;n;)f=u[--n],h=u[--n],o.push(_(t,h,f,d))}for(r=o.length;r--;)s.push(o[r]);for(o.length=0, -r=s.length;r--;)s[r].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 r=t[1][0]-t[0][0],n=t[2][0]-t[0][0],i=t[1][1]-t[0][1],a=t[2][1]-t[0][1],o=r*a-n*i;if(0===o)return null;var s=(a*(e[0]-t[0][0])-n*(e[1]-t[0][1]))/o,u=(r*(e[1]-t[0][1])-i*(e[0]-t[0][0]))/o;return s<0||u<0||s+u>1?null:[s,u]}},he="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},fe=function(t,e){return e={exports:{}},t(e,e.exports),e.exports}(function(t,e){!function(t){var r={};r.exports=e,function(t){var e="undefined"==typeof window?he:window,r=e.GLMAT_EPSILON;null==r&&(r=1e-6);var n=e.GLMAT_ARRAY_TYPE||Array,i=e.GLMAT_RANDOM;i||(i=Math.random);var a={};a.setMatrixArrayType=function(t){n=t},void 0!==t&&(t.glMatrix=a);var o=Math.PI/180;a.toRadian=function(t){return t*o};var s={};s.create=function(){var t=new n(2);return t[0]=0,t[1]=0,t},s.clone=function(t){var e=new n(2);return e[0]=t[0],e[1]=t[1],e},s.fromValues=function(t,e){var r=new n(2);return r[0]=t,r[1]=e,r},s.copy=function(t,e){return t[0]=e[0],t[1]=e[1],t},s.set=function(t,e,r){return t[0]=e,t[1]=r,t},s.add=function(t,e,r){return t[0]=e[0]+r[0],t[1]=e[1]+r[1],t},s.subtract=function(t,e,r){return t[0]=e[0]-r[0],t[1]=e[1]-r[1],t},s.sub=s.subtract,s.multiply=function(t,e,r){return t[0]=e[0]*r[0],t[1]=e[1]*r[1],t},s.mul=s.multiply,s.divide=function(t,e,r){return t[0]=e[0]/r[0],t[1]=e[1]/r[1],t},s.div=s.divide,s.min=function(t,e,r){return t[0]=Math.min(e[0],r[0]),t[1]=Math.min(e[1],r[1]),t},s.max=function(t,e,r){return t[0]=Math.max(e[0],r[0]),t[1]=Math.max(e[1],r[1]),t},s.scale=function(t,e,r){return t[0]=e[0]*r,t[1]=e[1]*r,t},s.scaleAndAdd=function(t,e,r,n){return t[0]=e[0]+r[0]*n,t[1]=e[1]+r[1]*n,t},s.distance=function(t,e){var r=e[0]-t[0],n=e[1]-t[1];return Math.sqrt(r*r+n*n)},s.dist=s.distance,s.squaredDistance=function(t,e){var r=e[0]-t[0],n=e[1]-t[1];return r*r+n*n},s.sqrDist=s.squaredDistance,s.length=function(t){var e=t[0],r=t[1];return Math.sqrt(e*e+r*r)},s.len=s.length,s.squaredLength=function(t){var e=t[0],r=t[1];return e*e+r*r},s.sqrLen=s.squaredLength,s.negate=function(t,e){return t[0]=-e[0],t[1]=-e[1],t},s.inverse=function(t,e){return t[0]=1/e[0],t[1]=1/e[1],t},s.normalize=function(t,e){var r=e[0],n=e[1],i=r*r+n*n;return i>0&&(i=1/Math.sqrt(i),t[0]=e[0]*i,t[1]=e[1]*i),t},s.dot=function(t,e){return t[0]*e[0]+t[1]*e[1]},s.cross=function(t,e,r){var n=e[0]*r[1]-e[1]*r[0];return t[0]=t[1]=0,t[2]=n,t},s.lerp=function(t,e,r,n){var i=e[0],a=e[1];return t[0]=i+n*(r[0]-i),t[1]=a+n*(r[1]-a),t},s.random=function(t,e){e=e||1;var r=2*i()*Math.PI;return t[0]=Math.cos(r)*e,t[1]=Math.sin(r)*e,t},s.transformMat2=function(t,e,r){var n=e[0],i=e[1];return t[0]=r[0]*n+r[2]*i,t[1]=r[1]*n+r[3]*i,t},s.transformMat2d=function(t,e,r){var n=e[0],i=e[1];return t[0]=r[0]*n+r[2]*i+r[4],t[1]=r[1]*n+r[3]*i+r[5],t},s.transformMat3=function(t,e,r){var n=e[0],i=e[1];return t[0]=r[0]*n+r[3]*i+r[6],t[1]=r[1]*n+r[4]*i+r[7],t},s.transformMat4=function(t,e,r){var n=e[0],i=e[1];return t[0]=r[0]*n+r[4]*i+r[12],t[1]=r[1]*n+r[5]*i+r[13],t},s.forEach=function(){var t=s.create();return function(e,r,n,i,a,o){var s,u;for(r||(r=2),n||(n=0),u=i?Math.min(i*r+n,e.length):e.length,s=n;s0&&(a=1/Math.sqrt(a),t[0]=e[0]*a,t[1]=e[1]*a,t[2]=e[2]*a),t},u.dot=function(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]},u.cross=function(t,e,r){var n=e[0],i=e[1],a=e[2],o=r[0],s=r[1],u=r[2];return t[0]=i*u-a*s,t[1]=a*o-n*u,t[2]=n*s-i*o,t},u.lerp=function(t,e,r,n){var i=e[0],a=e[1],o=e[2];return t[0]=i+n*(r[0]-i),t[1]=a+n*(r[1]-a),t[2]=o+n*(r[2]-o),t},u.random=function(t,e){e=e||1;var r=2*i()*Math.PI,n=2*i()-1,a=Math.sqrt(1-n*n)*e;return t[0]=Math.cos(r)*a,t[1]=Math.sin(r)*a,t[2]=n*e,t},u.transformMat4=function(t,e,r){var n=e[0],i=e[1],a=e[2],o=r[3]*n+r[7]*i+r[11]*a+r[15];return o=o||1,t[0]=(r[0]*n+r[4]*i+r[8]*a+r[12])/o,t[1]=(r[1]*n+r[5]*i+r[9]*a+r[13])/o,t[2]=(r[2]*n+r[6]*i+r[10]*a+r[14])/o,t},u.transformMat3=function(t,e,r){var n=e[0],i=e[1],a=e[2];return t[0]=n*r[0]+i*r[3]+a*r[6],t[1]=n*r[1]+i*r[4]+a*r[7],t[2]=n*r[2]+i*r[5]+a*r[8],t},u.transformQuat=function(t,e,r){var n=e[0],i=e[1],a=e[2],o=r[0],s=r[1],u=r[2],l=r[3],c=l*n+s*a-u*i,h=l*i+u*n-o*a,f=l*a+o*i-s*n,d=-o*n-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.rotateX=function(t,e,r,n){var i=[],a=[];return i[0]=e[0]-r[0],i[1]=e[1]-r[1],i[2]=e[2]-r[2],a[0]=i[0],a[1]=i[1]*Math.cos(n)-i[2]*Math.sin(n),a[2]=i[1]*Math.sin(n)+i[2]*Math.cos(n),t[0]=a[0]+r[0],t[1]=a[1]+r[1],t[2]=a[2]+r[2],t},u.rotateY=function(t,e,r,n){var i=[],a=[];return i[0]=e[0]-r[0],i[1]=e[1]-r[1],i[2]=e[2]-r[2],a[0]=i[2]*Math.sin(n)+i[0]*Math.cos(n),a[1]=i[1],a[2]=i[2]*Math.cos(n)-i[0]*Math.sin(n),t[0]=a[0]+r[0],t[1]=a[1]+r[1],t[2]=a[2]+r[2],t},u.rotateZ=function(t,e,r,n){var i=[],a=[];return i[0]=e[0]-r[0],i[1]=e[1]-r[1],i[2]=e[2]-r[2],a[0]=i[0]*Math.cos(n)-i[1]*Math.sin(n),a[1]=i[0]*Math.sin(n)+i[1]*Math.cos(n),a[2]=i[2],t[0]=a[0]+r[0],t[1]=a[1]+r[1],t[2]=a[2]+r[2],t},u.forEach=function(){var t=u.create();return function(e,r,n,i,a,o){var s,u;for(r||(r=3),n||(n=0),u=i?Math.min(i*r+n,e.length):e.length,s=n;s1?0:Math.acos(i)},u.str=function(t){return"vec3("+t[0]+", "+t[1]+", "+t[2]+")"},void 0!==t&&(t.vec3=u);var l={};l.create=function(){var t=new n(4);return t[0]=0,t[1]=0,t[2]=0,t[3]=0,t},l.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},l.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},l.copy=function(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t},l.set=function(t,e,r,n,i){return t[0]=e,t[1]=r,t[2]=n,t[3]=i,t},l.add=function(t,e,r){return t[0]=e[0]+r[0],t[1]=e[1]+r[1],t[2]=e[2]+r[2],t[3]=e[3]+r[3],t},l.subtract=function(t,e,r){return t[0]=e[0]-r[0],t[1]=e[1]-r[1],t[2]=e[2]-r[2],t[3]=e[3]-r[3],t},l.sub=l.subtract,l.multiply=function(t,e,r){return t[0]=e[0]*r[0],t[1]=e[1]*r[1],t[2]=e[2]*r[2],t[3]=e[3]*r[3],t},l.mul=l.multiply,l.divide=function(t,e,r){return t[0]=e[0]/r[0],t[1]=e[1]/r[1],t[2]=e[2]/r[2],t[3]=e[3]/r[3],t},l.div=l.divide,l.min=function(t,e,r){return t[0]=Math.min(e[0],r[0]),t[1]=Math.min(e[1],r[1]),t[2]=Math.min(e[2],r[2]),t[3]=Math.min(e[3],r[3]),t},l.max=function(t,e,r){return t[0]=Math.max(e[0],r[0]),t[1]=Math.max(e[1],r[1]),t[2]=Math.max(e[2],r[2]),t[3]=Math.max(e[3],r[3]),t},l.scale=function(t,e,r){return t[0]=e[0]*r,t[1]=e[1]*r,t[2]=e[2]*r,t[3]=e[3]*r,t},l.scaleAndAdd=function(t,e,r,n){return t[0]=e[0]+r[0]*n,t[1]=e[1]+r[1]*n,t[2]=e[2]+r[2]*n,t[3]=e[3]+r[3]*n,t},l.distance=function(t,e){var r=e[0]-t[0],n=e[1]-t[1],i=e[2]-t[2],a=e[3]-t[3];return Math.sqrt(r*r+n*n+i*i+a*a)},l.dist=l.distance,l.squaredDistance=function(t,e){var r=e[0]-t[0],n=e[1]-t[1],i=e[2]-t[2],a=e[3]-t[3];return r*r+n*n+i*i+a*a},l.sqrDist=l.squaredDistance,l.length=function(t){var e=t[0],r=t[1],n=t[2],i=t[3];return Math.sqrt(e*e+r*r+n*n+i*i)},l.len=l.length,l.squaredLength=function(t){var e=t[0],r=t[1],n=t[2],i=t[3];return e*e+r*r+n*n+i*i},l.sqrLen=l.squaredLength,l.negate=function(t,e){return t[0]=-e[0],t[1]=-e[1],t[2]=-e[2],t[3]=-e[3],t},l.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},l.normalize=function(t,e){var r=e[0],n=e[1],i=e[2],a=e[3],o=r*r+n*n+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},l.dot=function(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]+t[3]*e[3]},l.lerp=function(t,e,r,n){var i=e[0],a=e[1],o=e[2],s=e[3];return t[0]=i+n*(r[0]-i),t[1]=a+n*(r[1]-a),t[2]=o+n*(r[2]-o),t[3]=s+n*(r[3]-s),t},l.random=function(t,e){return e=e||1,t[0]=i(),t[1]=i(),t[2]=i(),t[3]=i(),l.normalize(t,t),l.scale(t,t,e),t},l.transformMat4=function(t,e,r){var n=e[0],i=e[1],a=e[2],o=e[3];return t[0]=r[0]*n+r[4]*i+r[8]*a+r[12]*o,t[1]=r[1]*n+r[5]*i+r[9]*a+r[13]*o,t[2]=r[2]*n+r[6]*i+r[10]*a+r[14]*o,t[3]=r[3]*n+r[7]*i+r[11]*a+r[15]*o,t},l.transformQuat=function(t,e,r){var n=e[0],i=e[1],a=e[2],o=r[0],s=r[1],u=r[2],l=r[3],c=l*n+s*a-u*i,h=l*i+u*n-o*a,f=l*a+o*i-s*n,d=-o*n-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},l.forEach=function(){var t=l.create();return function(e,r,n,i,a,o){var s,u;for(r||(r=4),n||(n=0),u=i?Math.min(i*r+n,e.length):e.length,s=n;s.999999?(n[0]=0,n[1]=0,n[2]=0,n[3]=1,n):(u.cross(t,i,a),n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=1+o,m.normalize(n,n))}}(),m.setAxes=function(){var t=f.create();return function(e,r,n,i){return t[0]=n[0],t[3]=n[1],t[6]=n[2],t[1]=i[0],t[4]=i[1],t[7]=i[2],t[2]=-r[0],t[5]=-r[1],t[8]=-r[2],m.normalize(e,m.fromMat3(e,t))}}(),m.clone=l.clone,m.fromValues=l.fromValues,m.copy=l.copy,m.set=l.set,m.identity=function(t){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t},m.setAxisAngle=function(t,e,r){r*=.5;var n=Math.sin(r);return t[0]=n*e[0],t[1]=n*e[1],t[2]=n*e[2],t[3]=Math.cos(r),t},m.add=l.add,m.multiply=function(t,e,r){var n=e[0],i=e[1],a=e[2],o=e[3],s=r[0],u=r[1],l=r[2],c=r[3];return t[0]=n*c+o*s+i*l-a*u,t[1]=i*c+o*u+a*s-n*l,t[2]=a*c+o*l+n*u-i*s,t[3]=o*c-n*s-i*u-a*l,t},m.mul=m.multiply,m.scale=l.scale,m.rotateX=function(t,e,r){r*=.5;var n=e[0],i=e[1],a=e[2],o=e[3],s=Math.sin(r),u=Math.cos(r);return t[0]=n*u+o*s,t[1]=i*u+a*s,t[2]=a*u-i*s,t[3]=o*u-n*s,t},m.rotateY=function(t,e,r){r*=.5;var n=e[0],i=e[1],a=e[2],o=e[3],s=Math.sin(r),u=Math.cos(r);return t[0]=n*u-a*s,t[1]=i*u+o*s,t[2]=a*u+n*s,t[3]=o*u-i*s,t},m.rotateZ=function(t,e,r){r*=.5;var n=e[0],i=e[1],a=e[2],o=e[3],s=Math.sin(r),u=Math.cos(r);return t[0]=n*u+i*s,t[1]=i*u-n*s,t[2]=a*u+o*s,t[3]=o*u-a*s,t},m.calculateW=function(t,e){var r=e[0],n=e[1],i=e[2];return t[0]=r,t[1]=n,t[2]=i,t[3]=Math.sqrt(Math.abs(1-r*r-n*n-i*i)),t},m.dot=l.dot,m.lerp=l.lerp,m.slerp=function(t,e,r,n){var i,a,o,s,u,l=e[0],c=e[1],h=e[2],f=e[3],d=r[0],m=r[1],p=r[2],_=r[3];return a=l*d+c*m+h*p+f*_,a<0&&(a=-a,d=-d,m=-m,p=-p,_=-_),1-a>1e-6?(i=Math.acos(a),o=Math.sin(i),s=Math.sin((1-n)*i)/o,u=Math.sin(n*i)/o):(s=1-n,u=n),t[0]=s*l+u*d,t[1]=s*c+u*m,t[2]=s*h+u*p,t[3]=s*f+u*_,t},m.invert=function(t,e){var r=e[0],n=e[1],i=e[2],a=e[3],o=r*r+n*n+i*i+a*a,s=o?1/o:0;return t[0]=-r*s,t[1]=-n*s,t[2]=-i*s,t[3]=a*s,t},m.conjugate=function(t,e){return t[0]=-e[0],t[1]=-e[1],t[2]=-e[2],t[3]=e[3],t},m.length=l.length,m.len=m.length,m.squaredLength=l.squaredLength,m.sqrLen=m.squaredLength,m.normalize=l.normalize,m.fromMat3=function(t,e){var r,n=e[0]+e[4]+e[8];if(n>0)r=Math.sqrt(n+1),t[3]=.5*r,r=.5/r,t[0]=(e[5]-e[7])*r,t[1]=(e[6]-e[2])*r,t[2]=(e[1]-e[3])*r;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;r=Math.sqrt(e[3*i+i]-e[3*a+a]-e[3*o+o]+1),t[i]=.5*r,r=.5/r,t[3]=(e[3*a+o]-e[3*o+a])*r,t[a]=(e[3*a+i]+e[3*i+a])*r,t[o]=(e[3*o+i]+e[3*i+o])*r}return t},m.str=function(t){return"quat("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},void 0!==t&&(t.quat=m)}(r.exports)}()}),de=fe.vec2,me=function(t,e){t=t||0,e=e||0,this.array=de.fromValues(t,e),this._dirty=!0};if(me.prototype={constructor:me,add:function(t){return de.add(this.array,this.array,t.array),this._dirty=!0,this},set:function(t,e){return this.array[0]=t,this.array[1]=e,this._dirty=!0,this},setArray:function(t){return this.array[0]=t[0],this.array[1]=t[1],this._dirty=!0,this},clone:function(){return new me(this.x,this.y)},copy:function(t){return de.copy(this.array,t.array),this._dirty=!0,this},cross:function(t,e){return de.cross(t.array,this.array,e.array),t._dirty=!0,this},dist:function(t){return de.dist(this.array,t.array)},distance:function(t){return de.distance(this.array,t.array)},div:function(t){return de.div(this.array,this.array,t.array),this._dirty=!0,this},divide:function(t){return de.divide(this.array,this.array,t.array),this._dirty=!0,this},dot:function(t){return de.dot(this.array,t.array)},len:function(){return de.len(this.array)},length:function(){return de.length(this.array)},lerp:function(t,e,r){return de.lerp(this.array,t.array,e.array,r),this._dirty=!0,this},min:function(t){return de.min(this.array,this.array,t.array),this._dirty=!0,this},max:function(t){return de.max(this.array,this.array,t.array),this._dirty=!0,this},mul:function(t){return de.mul(this.array,this.array,t.array),this._dirty=!0,this},multiply:function(t){return de.multiply(this.array,this.array,t.array),this._dirty=!0,this},negate:function(){return de.negate(this.array,this.array),this._dirty=!0,this},normalize:function(){return de.normalize(this.array,this.array),this._dirty=!0,this},random:function(t){return de.random(this.array,t),this._dirty=!0,this},scale:function(t){return de.scale(this.array,this.array,t),this._dirty=!0,this},scaleAndAdd:function(t,e){return de.scaleAndAdd(this.array,this.array,t.array,e),this._dirty=!0,this},sqrDist:function(t){return de.sqrDist(this.array,t.array)},squaredDistance:function(t){return de.squaredDistance(this.array,t.array)},sqrLen:function(){return de.sqrLen(this.array)},squaredLength:function(){return de.squaredLength(this.array)},sub:function(t){return de.sub(this.array,this.array,t.array),this._dirty=!0,this},subtract:function(t){return de.subtract(this.array,this.array,t.array),this._dirty=!0,this},transformMat2:function(t){return de.transformMat2(this.array,this.array,t.array),this._dirty=!0,this},transformMat2d:function(t){return de.transformMat2d(this.array,this.array,t.array),this._dirty=!0,this},transformMat3:function(t){return de.transformMat3(this.array,this.array,t.array),this._dirty=!0,this},transformMat4:function(t){return de.transformMat4(this.array,this.array,t.array),this._dirty=!0,this},toString:function(){return"["+Array.prototype.join.call(this.array,",")+"]"},toArray:function(){return Array.prototype.slice.call(this.array)}},Object.defineProperty){var pe=me.prototype;Object.defineProperty(pe,"x",{get:function(){return this.array[0]},set:function(t){this.array[0]=t,this._dirty=!0}}),Object.defineProperty(pe,"y",{get:function(){return this.array[1]},set:function(t){this.array[1]=t,this._dirty=!0}})}me.add=function(t,e,r){ -return de.add(t.array,e.array,r.array),t._dirty=!0,t},me.set=function(t,e,r){return de.set(t.array,e,r),t._dirty=!0,t},me.copy=function(t,e){return de.copy(t.array,e.array),t._dirty=!0,t},me.cross=function(t,e,r){return de.cross(t.array,e.array,r.array),t._dirty=!0,t},me.dist=function(t,e){return de.distance(t.array,e.array)},me.distance=me.dist,me.div=function(t,e,r){return de.divide(t.array,e.array,r.array),t._dirty=!0,t},me.divide=me.div,me.dot=function(t,e){return de.dot(t.array,e.array)},me.len=function(t){return de.length(t.array)},me.lerp=function(t,e,r,n){return de.lerp(t.array,e.array,r.array,n),t._dirty=!0,t},me.min=function(t,e,r){return de.min(t.array,e.array,r.array),t._dirty=!0,t},me.max=function(t,e,r){return de.max(t.array,e.array,r.array),t._dirty=!0,t},me.mul=function(t,e,r){return de.multiply(t.array,e.array,r.array),t._dirty=!0,t},me.multiply=me.mul,me.negate=function(t,e){return de.negate(t.array,e.array),t._dirty=!0,t},me.normalize=function(t,e){return de.normalize(t.array,e.array),t._dirty=!0,t},me.random=function(t,e){return de.random(t.array,e),t._dirty=!0,t},me.scale=function(t,e,r){return de.scale(t.array,e.array,r),t._dirty=!0,t},me.scaleAndAdd=function(t,e,r,n){return de.scaleAndAdd(t.array,e.array,r.array,n),t._dirty=!0,t},me.sqrDist=function(t,e){return de.sqrDist(t.array,e.array)},me.squaredDistance=me.sqrDist,me.sqrLen=function(t){return de.sqrLen(t.array)},me.squaredLength=me.sqrLen,me.sub=function(t,e,r){return de.subtract(t.array,e.array,r.array),t._dirty=!0,t},me.subtract=me.sub,me.transformMat2=function(t,e,r){return de.transformMat2(t.array,e.array,r.array),t._dirty=!0,t},me.transformMat2d=function(t,e,r){return de.transformMat2d(t.array,e.array,r.array),t._dirty=!0,t},me.transformMat3=function(t,e,r){return de.transformMat3(t.array,e.array,r.array),t._dirty=!0,t},me.transformMat4=function(t,e,r){return de.transformMat4(t.array,e.array,r.array),t._dirty=!0,t};var _e=function(t){t=t||{},ae.call(this,t),this.output=t.output||null,this.inputs=t.inputs||[],this.position=new me,this._cacheTriangle=null,this._triangles=[],this._updateTriangles()};_e.prototype=new ae,_e.prototype.constructor=_e,_e.prototype.addInput=function(t,e,r){var n={position:t,clip:e,offset:r||0};return this.inputs.push(n),this.life=Math.max(e.life,this.life),this._updateTriangles(),n},_e.prototype._updateTriangles=function(){var t=this.inputs.map(function(t){return t.position});this._triangles=ce.triangulate(t,"array")},_e.prototype.step=function(t,e,r){var n=ae.prototype.step.call(this,t);return"finish"!==n&&this.setTime(this.getElapsedTime()),r||"paused"===n||this.fire("frame"),n},_e.prototype.setTime=function(t){var e=this._findTriangle(this.position);if(e){var r=e[1],n=e[2],i=e[0],a=this.inputs[i.indices[0]],o=this.inputs[i.indices[1]],s=this.inputs[i.indices[2]],u=a.clip,l=o.clip,c=s.clip;u.setTime((t+a.offset)%u.life),l.setTime((t+o.offset)%l.life),c.setTime((t+s.offset)%c.life);var h=u.output instanceof ae?u.output:u,f=l.output instanceof ae?l.output:l,d=c.output instanceof ae?c.output:c;this.output.blend2D(h,f,d,r,n)}},_e.prototype.clone=function(t){var e=ae.prototype.clone.call(this);e.output=this.output.clone();for(var r=0;r=e.time[r-1])t=e.time[r-1],n=r-2;else if(t=0;a--)if(e.time[a-1]<=t&&e.time[a]>t){n=a-1;break}}else for(var a=this._cacheKey;at){n=a;break}if(n>-1){this._cacheKey=n,this._cacheTime=t;var o=n,s=n+1,u=e.time[o],l=e.time[s],c=l-u,h=0===c?0:(t-u)/c;e.rotation&&y(this.rotation,e.rotation,e.rotation,h,4*o,4*s),e.position&&g(this.position,e.position,e.position,h,3*o,3*s),e.scale&&g(this.scale,e.scale,e.scale,h,3*o,3*s)}n===r-2&&(this._cacheKey=0,this._cacheTime=0),this.updateTarget()}},ye.prototype.updateTarget=function(){var t=this.channels;this.target&&(t.position&&this.target.position.setArray(this.position),t.rotation&&this.target.rotation.setArray(this.rotation),t.scale&&this.target.scale.setArray(this.scale))},ye.prototype.getMaxTime=function(){return this.channels.time[this.channels.time.length-1]},ye.prototype.getSubTrack=function(t,e){var r=new ye({name:this.name}),n=this.channels.time[0];t=Math.min(Math.max(t,n),this.life),e=Math.min(Math.max(e,n),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&&(r.channels.rotation=new Float32Array(4*o)),this.channels.position&&(r.channels.position=new Float32Array(3*o)),this.channels.scale&&(r.channels.scale=new Float32Array(3*o)),this.channels.time&&(r.channels.time=new Float32Array(o)),this.setTime(t);for(var s=0;s<3;s++)r.channels.rotation[s]=this.rotation[s],r.channels.position[s]=this.position[s],r.channels.scale[s]=this.scale[s];r.channels.time[0]=0,r.channels.rotation[3]=this.rotation[3];for(var s=1;st&&(n=i);var a=0;if(n>=0)var o=e.time[n],s=e.time[n+1],a=(t-o)/(s-o);return[n,a]},ye.prototype.blend1D=function(t,e,r){ge.lerp(this.position,t.position,e.position,r),ge.lerp(this.scale,t.scale,e.scale,r),ve.slerp(this.rotation,t.rotation,e.rotation,r)},ye.prototype.blend2D=function(){var t=ve.create(),e=ve.create();return function(r,n,i,a,o){var s=1-a-o;this.position[0]=r.position[0]*s+n.position[0]*a+i.position[0]*o,this.position[1]=r.position[1]*s+n.position[1]*a+i.position[1]*o,this.position[2]=r.position[2]*s+n.position[2]*a+i.position[2]*o,this.scale[0]=r.scale[0]*s+n.scale[0]*a+i.scale[0]*o,this.scale[1]=r.scale[1]*s+n.scale[1]*a+i.scale[1]*o,this.scale[2]=r.scale[2]*s+n.scale[2]*a+i.scale[2]*o;var u=a+o;0===u?ve.copy(this.rotation,r.rotation):(ve.slerp(t,r.rotation,n.rotation,u),ve.slerp(e,r.rotation,i.rotation,u),ve.slerp(this.rotation,t,e,o/u))}}(),ye.prototype.additiveBlend=function(t,e){ge.add(this.position,t.position,e.position),ge.add(this.scale,t.scale,e.scale),ve.multiply(this.rotation,e.rotation,t.rotation)},ye.prototype.subtractiveBlend=function(t,e){ge.sub(this.position,t.position,e.position),ge.sub(this.scale,t.scale,e.scale),ve.invert(this.rotation,e.rotation),ve.multiply(this.rotation,this.rotation,t.rotation)},ye.prototype.clone=function(){var t=ye.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},ge.copy(t.position,this.position),ve.copy(t.rotation,this.rotation),ge.copy(t.scale,this.scale),t.target=this.target,t.updateTarget(),t};var Te={extend:T,derive:T},xe={trigger:function(t){if(this.hasOwnProperty("__handlers__")&&this.__handlers__.hasOwnProperty(t)){var e=this.__handlers__[t],r=e.length,n=-1,i=arguments;switch(i.length){case 1:for(;++n=0&&this._clips.splice(e,1)},removeAnimator:function(t){for(var e=t.getClips(),r=0;r=0&&this.tracks.splice(e,1)},Pe.prototype.getSubClip=function(t,e,r){for(var n=new Pe({name:this.name}),i=0;i0){var e=this.min,r=this.max,n=e.array,i=r.array;qe(n,t[0]),qe(i,t[0]);for(var a=1;ai[0]&&(i[0]=o[0]),o[1]>i[1]&&(i[1]=o[1]),o[2]>i[2]&&(i[2]=o[2])}e._dirty=!0,r._dirty=!0}},union:function(t){var e=this.min,r=this.max;return je.min(e.array,e.array,t.min.array),je.max(r.array,r.array,t.max.array),e._dirty=!0,r._dirty=!0,this},intersection:function(t){var e=this.min,r=this.max;return je.max(e.array,e.array,t.min.array),je.min(r.array,r.array,t.max.array),e._dirty=!0,r._dirty=!0,this},intersectBoundingBox:function(t){var e=this.min.array,r=this.max.array,n=t.min.array,i=t.max.array;return!(e[0]>i[0]||e[1]>i[1]||e[2]>i[2]||r[0]=i[0]&&r[1]>=i[1]&&r[2]>=i[2]},containPoint:function(t){var e=this.min.array,r=this.max.array,n=t.array;return e[0]<=n[0]&&e[1]<=n[1]&&e[2]<=n[2]&&r[0]>=n[0]&&r[1]>=n[1]&&r[2]>=n[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(t){this.transformFrom(this,t)},transformFrom:function(){var t=je.create(),e=je.create(),r=je.create(),n=je.create(),i=je.create(),a=je.create();return function(o,s){var u=o.min.array,l=o.max.array,c=s.array;return t[0]=c[0]*u[0],t[1]=c[1]*u[0],t[2]=c[2]*u[0],e[0]=c[0]*l[0],e[1]=c[1]*l[0],e[2]=c[2]*l[0],r[0]=c[4]*u[1],r[1]=c[5]*u[1],r[2]=c[6]*u[1],n[0]=c[4]*l[1],n[1]=c[5]*l[1],n[2]=c[6]*l[1],i[0]=c[8]*u[2],i[1]=c[9]*u[2],i[2]=c[10]*u[2],a[0]=c[8]*l[2],a[1]=c[9]*l[2],a[2]=c[10]*l[2],u=this.min.array,l=this.max.array,u[0]=Math.min(t[0],e[0])+Math.min(r[0],n[0])+Math.min(i[0],a[0])+c[12],u[1]=Math.min(t[1],e[1])+Math.min(r[1],n[1])+Math.min(i[1],a[1])+c[13],u[2]=Math.min(t[2],e[2])+Math.min(r[2],n[2])+Math.min(i[2],a[2])+c[14],l[0]=Math.max(t[0],e[0])+Math.max(r[0],n[0])+Math.max(i[0],a[0])+c[12],l[1]=Math.max(t[1],e[1])+Math.max(r[1],n[1])+Math.max(i[1],a[1])+c[13],l[2]=Math.max(t[2],e[2])+Math.max(r[2],n[2])+Math.max(i[2],a[2])+c[14],this.min._dirty=!0,this.max._dirty=!0,this}}(),applyProjection:function(t){var e=this.min.array,r=this.max.array,n=t.array,i=e[0],a=e[1],o=e[2],s=r[0],u=r[1],l=e[2],c=r[0],h=r[1],f=r[2];if(1===n[15])e[0]=n[0]*i+n[12],e[1]=n[5]*a+n[13],r[2]=n[10]*o+n[14],r[0]=n[0]*c+n[12],r[1]=n[5]*h+n[13],e[2]=n[10]*f+n[14];else{var d=-1/o;e[0]=n[0]*i*d,e[1]=n[5]*a*d,r[2]=(n[10]*o+n[14])*d,d=-1/l, -r[0]=n[0]*s*d,r[1]=n[5]*u*d,d=-1/f,e[2]=(n[10]*f+n[14])*d}return this.min._dirty=!0,this.max._dirty=!0,this},updateVertices:function(){var t=this.vertices;if(!t){t=[];for(var e=0;e<8;e++)t[e]=je.fromValues(0,0,0);this.vertices=t}var r=this.min.array,n=this.max.array;return Ye(t[0],r[0],r[1],r[2]),Ye(t[1],r[0],n[1],r[2]),Ye(t[2],n[0],r[1],r[2]),Ye(t[3],n[0],n[1],r[2]),Ye(t[4],r[0],r[1],n[2]),Ye(t[5],r[0],n[1],n[2]),Ye(t[6],n[0],r[1],n[2]),Ye(t[7],n[0],n[1],n[2]),this},copy:function(t){var e=this.min,r=this.max;return qe(e.array,t.min.array),qe(r.array,t.max.array),e._dirty=!0,r._dirty=!0,this},clone:function(){var t=new Ke;return t.copy(this),t}};var Ze=fe.mat4,Je=fe.vec3,Qe=fe.mat3,$e=fe.quat,tr=function(){this._axisX=new He,this._axisY=new He,this._axisZ=new He,this.array=Ze.create(),this._dirty=!0};tr.prototype={constructor:tr,setArray:function(t){for(var e=0;e>e;return t+1},dispose:function(t){var e=this._cache;e.use(t.__uid__);var r=e.get("webgl_texture");r&&t.gl.deleteTexture(r),e.deleteContext(t.__uid__)},isRenderable:function(){},isPowerOfTwo:function(){}});Object.defineProperty(ir.prototype,"width",{get:function(){return this._width},set:function(t){this._width=t}}),Object.defineProperty(ir.prototype,"height",{get:function(){return this._height},set:function(t){this._height=t}}),ir.BYTE=Oe.BYTE,ir.UNSIGNED_BYTE=Oe.UNSIGNED_BYTE,ir.SHORT=Oe.SHORT,ir.UNSIGNED_SHORT=Oe.UNSIGNED_SHORT,ir.INT=Oe.INT,ir.UNSIGNED_INT=Oe.UNSIGNED_INT,ir.FLOAT=Oe.FLOAT,ir.HALF_FLOAT=36193,ir.UNSIGNED_INT_24_8_WEBGL=34042,ir.DEPTH_COMPONENT=Oe.DEPTH_COMPONENT,ir.DEPTH_STENCIL=Oe.DEPTH_STENCIL,ir.ALPHA=Oe.ALPHA,ir.RGB=Oe.RGB,ir.RGBA=Oe.RGBA,ir.LUMINANCE=Oe.LUMINANCE,ir.LUMINANCE_ALPHA=Oe.LUMINANCE_ALPHA,ir.SRGB=35904,ir.SRGB_ALPHA=35906,ir.COMPRESSED_RGB_S3TC_DXT1_EXT=33776,ir.COMPRESSED_RGBA_S3TC_DXT1_EXT=33777,ir.COMPRESSED_RGBA_S3TC_DXT3_EXT=33778,ir.COMPRESSED_RGBA_S3TC_DXT5_EXT=33779,ir.NEAREST=Oe.NEAREST,ir.LINEAR=Oe.LINEAR,ir.NEAREST_MIPMAP_NEAREST=Oe.NEAREST_MIPMAP_NEAREST,ir.LINEAR_MIPMAP_NEAREST=Oe.LINEAR_MIPMAP_NEAREST,ir.NEAREST_MIPMAP_LINEAR=Oe.NEAREST_MIPMAP_LINEAR,ir.LINEAR_MIPMAP_LINEAR=Oe.LINEAR_MIPMAP_LINEAR,ir.REPEAT=Oe.REPEAT,ir.CLAMP_TO_EDGE=Oe.CLAMP_TO_EDGE,ir.MIRRORED_REPEAT=Oe.MIRRORED_REPEAT;var ar=function(){this.head=null,this.tail=null,this._length=0};ar.prototype.insert=function(t){var e=new ar.Entry(t);return this.insertEntry(e),e},ar.prototype.insertAt=function(t,e){if(!(t<0)){for(var r=this.head,n=0;r&&n!=t;)r=r.next,n++;if(r){var i=new ar.Entry(e),a=r.prev;a?(a.next=i,i.prev=a):this.head=i,i.next=r,r.prev=i}else this.insert(e)}},ar.prototype.insertBeforeEntry=function(t,e){var r=new ar.Entry(t),n=e.prev;n?(n.next=r,r.prev=n):this.head=r,r.next=e,e.prev=r,this._length++},ar.prototype.insertEntry=function(t){this.head?(this.tail.next=t,t.prev=this.tail,this.tail=t):this.head=this.tail=t,this._length++},ar.prototype.remove=function(t){var e=t.prev,r=t.next;e?e.next=r:this.head=r,r?r.prev=e:this.tail=e,t.next=t.prev=null,this._length--},ar.prototype.removeAt=function(t){if(!(t<0)){for(var e=this.head,r=0;e&&r!=t;)e=e.next,r++;return e?(this.remove(e),e.value):void 0}},ar.prototype.getHead=function(){if(this.head)return this.head.value},ar.prototype.getTail=function(){if(this.tail)return this.tail.value},ar.prototype.getAt=function(t){if(!(t<0)){for(var e=this.head,r=0;e&&r!=t;)e=e.next,r++;return e.value}},ar.prototype.indexOf=function(t){for(var e=this.head,r=0;e;){if(e.value===t)return r;e=e.next,r++}},ar.prototype.length=function(){return this._length},ar.prototype.isEmpty=function(){return 0===this._length},ar.prototype.forEach=function(t,e){for(var r=this.head,n=0,i=void 0!==e;r;)i?t.call(e,r.value,n):t(r.value,n),r=r.next,n++},ar.prototype.clear=function(){this.tail=this.head=null,this._length=0},ar.Entry=function(t){this.value=t,this.next=null,this.prev=null};var or=function(t){this._list=new ar,this._map={},this._maxSize=t||10};or.prototype.setMaxSize=function(t){this._maxSize=t},or.prototype.put=function(t,e){if(void 0===this._map[t]){var r=this._list.length();if(r>=this._maxSize&&r>0){var n=this._list.head;this._list.remove(n),delete this._map[n.key]}var i=this._list.insert(e);i.key=t,this._map[t]=i}},or.prototype.get=function(t){var e=this._map[t];if(void 0!==e)return e!==this._list.tail&&(this._list.remove(e),this._list.insertEntry(e)),e.value},or.prototype.remove=function(t){var e=this._map[t];void 0!==e&&(delete this._map[t],this._list.remove(e))},or.prototype.clear=function(){this._list.clear(),this._map={}};var sr={},ur={transparent:[0,0,0,0],aliceblue:[240,248,255,1],antiquewhite:[250,235,215,1],aqua:[0,255,255,1],aquamarine:[127,255,212,1],azure:[240,255,255,1],beige:[245,245,220,1],bisque:[255,228,196,1],black:[0,0,0,1],blanchedalmond:[255,235,205,1],blue:[0,0,255,1],blueviolet:[138,43,226,1],brown:[165,42,42,1],burlywood:[222,184,135,1],cadetblue:[95,158,160,1],chartreuse:[127,255,0,1],chocolate:[210,105,30,1],coral:[255,127,80,1],cornflowerblue:[100,149,237,1],cornsilk:[255,248,220,1],crimson:[220,20,60,1],cyan:[0,255,255,1],darkblue:[0,0,139,1],darkcyan:[0,139,139,1],darkgoldenrod:[184,134,11,1],darkgray:[169,169,169,1],darkgreen:[0,100,0,1],darkgrey:[169,169,169,1],darkkhaki:[189,183,107,1],darkmagenta:[139,0,139,1],darkolivegreen:[85,107,47,1],darkorange:[255,140,0,1],darkorchid:[153,50,204,1],darkred:[139,0,0,1],darksalmon:[233,150,122,1],darkseagreen:[143,188,143,1],darkslateblue:[72,61,139,1],darkslategray:[47,79,79,1],darkslategrey:[47,79,79,1],darkturquoise:[0,206,209,1],darkviolet:[148,0,211,1],deeppink:[255,20,147,1],deepskyblue:[0,191,255,1],dimgray:[105,105,105,1],dimgrey:[105,105,105,1],dodgerblue:[30,144,255,1],firebrick:[178,34,34,1],floralwhite:[255,250,240,1],forestgreen:[34,139,34,1],fuchsia:[255,0,255,1],gainsboro:[220,220,220,1],ghostwhite:[248,248,255,1],gold:[255,215,0,1],goldenrod:[218,165,32,1],gray:[128,128,128,1],green:[0,128,0,1],greenyellow:[173,255,47,1],grey:[128,128,128,1],honeydew:[240,255,240,1],hotpink:[255,105,180,1],indianred:[205,92,92,1],indigo:[75,0,130,1],ivory:[255,255,240,1],khaki:[240,230,140,1],lavender:[230,230,250,1],lavenderblush:[255,240,245,1],lawngreen:[124,252,0,1],lemonchiffon:[255,250,205,1],lightblue:[173,216,230,1],lightcoral:[240,128,128,1],lightcyan:[224,255,255,1],lightgoldenrodyellow:[250,250,210,1],lightgray:[211,211,211,1],lightgreen:[144,238,144,1],lightgrey:[211,211,211,1],lightpink:[255,182,193,1],lightsalmon:[255,160,122,1],lightseagreen:[32,178,170,1],lightskyblue:[135,206,250,1],lightslategray:[119,136,153,1],lightslategrey:[119,136,153,1],lightsteelblue:[176,196,222,1],lightyellow:[255,255,224,1],lime:[0,255,0,1],limegreen:[50,205,50,1],linen:[250,240,230,1],magenta:[255,0,255,1],maroon:[128,0,0,1],mediumaquamarine:[102,205,170,1],mediumblue:[0,0,205,1],mediumorchid:[186,85,211,1],mediumpurple:[147,112,219,1],mediumseagreen:[60,179,113,1],mediumslateblue:[123,104,238,1],mediumspringgreen:[0,250,154,1],mediumturquoise:[72,209,204,1],mediumvioletred:[199,21,133,1],midnightblue:[25,25,112,1],mintcream:[245,255,250,1],mistyrose:[255,228,225,1],moccasin:[255,228,181,1],navajowhite:[255,222,173,1],navy:[0,0,128,1],oldlace:[253,245,230,1],olive:[128,128,0,1],olivedrab:[107,142,35,1],orange:[255,165,0,1],orangered:[255,69,0,1],orchid:[218,112,214,1],palegoldenrod:[238,232,170,1],palegreen:[152,251,152,1],paleturquoise:[175,238,238,1],palevioletred:[219,112,147,1],papayawhip:[255,239,213,1],peachpuff:[255,218,185,1],peru:[205,133,63,1],pink:[255,192,203,1],plum:[221,160,221,1],powderblue:[176,224,230,1],purple:[128,0,128,1],red:[255,0,0,1],rosybrown:[188,143,143,1],royalblue:[65,105,225,1],saddlebrown:[139,69,19,1],salmon:[250,128,114,1],sandybrown:[244,164,96,1],seagreen:[46,139,87,1],seashell:[255,245,238,1],sienna:[160,82,45,1],silver:[192,192,192,1],skyblue:[135,206,235,1],slateblue:[106,90,205,1],slategray:[112,128,144,1],slategrey:[112,128,144,1],snow:[255,250,250,1],springgreen:[0,255,127,1],steelblue:[70,130,180,1],tan:[210,180,140,1],teal:[0,128,128,1],thistle:[216,191,216,1],tomato:[255,99,71,1],turquoise:[64,224,208,1],violet:[238,130,238,1],wheat:[245,222,179,1],white:[255,255,255,1],whitesmoke:[245,245,245,1],yellow:[255,255,0,1],yellowgreen:[154,205,50,1]},lr=new or(20),cr=null;sr.parse=function(t,e){if(t){e=e||[];var r=lr.get(t);if(r)return D(e,r);t+="";var n=t.replace(/ /g,"").toLowerCase();if(n in ur)return D(e,ur[n]),O(t,e),e;if("#"!==n.charAt(0)){var i=n.indexOf("("),a=n.indexOf(")");if(-1!==i&&a+1===n.length){var o=n.substr(0,i),s=n.substr(i+1,a-(i+1)).split(","),u=1;switch(o){case"rgba":if(4!==s.length)return void I(e,0,0,0,1);u=L(s.pop());case"rgb":return 3!==s.length?void I(e,0,0,0,1):(I(e,R(s[0]),R(s[1]),R(s[2]),u),O(t,e),e);case"hsla":return 4!==s.length?void I(e,0,0,0,1):(s[3]=L(s[3]),B(s,e),O(t,e),e);case"hsl":return 3!==s.length?void I(e,0,0,0,1):(B(s,e),O(t,e),e);default:return}}I(e,0,0,0,1)}else{if(4===n.length){var l=parseInt(n.substr(1),16);return l>=0&&l<=4095?(I(e,(3840&l)>>4|(3840&l)>>8,240&l|(240&l)>>4,15&l|(15&l)<<4,1),O(t,e),e):void I(e,0,0,0,1)}if(7===n.length){var l=parseInt(n.substr(1),16);return l>=0&&l<=16777215?(I(e,(16711680&l)>>16,(65280&l)>>8,255&l,1),O(t,e),e):void I(e,0,0,0,1)}}}},sr.parseToFloat=function(t,e){if(e=sr.parse(t,e))return e[0]/=255,e[1]/=255,e[2]/=255,e},sr.lift=function(t,e){var r=sr.parse(t);if(r){for(var n=0;n<3;n++)r[n]=e<0?r[n]*(1-e)|0:(255-r[n])*e+r[n]|0;return sr.stringify(r,4===r.length?"rgba":"rgb")}},sr.toHex=function(t){var e=sr.parse(t);if(e)return((1<<24)+(e[0]<<16)+(e[1]<<8)+ +e[2]).toString(16).slice(1)},sr.fastLerp=function(t,e,r){if(e&&e.length&&t>=0&&t<=1){r=r||[];var n=t*(e.length-1),i=Math.floor(n),a=Math.ceil(n),o=e[i],s=e[a],u=n-i;return r[0]=w(P(o[0],s[0],u)),r[1]=w(P(o[1],s[1],u)),r[2]=w(P(o[2],s[2],u)),r[3]=M(P(o[3],s[3],u)),r}},sr.fastMapToColor=sr.fastLerp,sr.lerp=function(t,e,r){if(e&&e.length&&t>=0&&t<=1){var n=t*(e.length-1),i=Math.floor(n),a=Math.ceil(n),o=sr.parse(e[i]),s=sr.parse(e[a]),u=n-i,l=sr.stringify([w(P(o[0],s[0],u)),w(P(o[1],s[1],u)),w(P(o[2],s[2],u)),M(P(o[3],s[3],u))],"rgba");return r?{color:l,leftIndex:i,rightIndex:a,value:n}:l}},sr.mapToColor=sr.lerp,sr.modifyHSL=function(t,e,r,n){if(t=sr.parse(t))return t=U(t),null!=e&&(t[0]=C(e)),null!=r&&(t[1]=L(r)),null!=n&&(t[2]=L(n)),sr.stringify(B(t),"rgba")},sr.modifyAlpha=function(t,e){if((t=sr.parse(t))&&null!=e)return t[3]=M(e),sr.stringify(t,"rgba")},sr.stringify=function(t,e){if(t&&t.length){var r=t[0]+","+t[1]+","+t[2];return"rgba"!==e&&"hsva"!==e&&"hsla"!==e||(r+=","+t[3]),e+"("+r+")"}};var hr=sr.parseToFloat,fr={},dr=we.extend(function(){return{name:"",depthTest:!0,depthMask:!0,transparent:!1,blend:null,autoUpdateTextureStatus:!0,uniforms:{},vertexDefines:{},fragmentDefines:{},_textureStatus:{},_enabledUniforms:null}},function(){this.name||(this.name="MATERIAL_"+this.__uid__),this.shader&&this.attachShader(this.shader,!0)},{precision:"highp",setUniform:function(t,e){void 0===e&&console.warn('Uniform value "'+t+'" is undefined');var r=this.uniforms[t];r&&("string"==typeof e&&(e=hr(e)||e),r.value=e,this.autoUpdateTextureStatus&&"t"===r.type&&(e?this.enableTexture(t):this.disableTexture(t)))},setUniforms:function(t){for(var e in t){var r=t[e];this.setUniform(e,r)}},isUniformEnabled:function(t){return this._enabledUniforms.indexOf(t)>=0},getEnabledUniforms:function(){return this._enabledUniforms},getTextureUniforms:function(){return this._textureUniforms},set:function(t,e){if("object"==typeof t)for(var r in t){var n=t[r];this.setUniform(r,n)}else this.setUniform(t,e)},get:function(t){var e=this.uniforms[t];if(e)return e.value},attachShader:function(t,e){var r=this.uniforms;this.uniforms=t.createUniforms(),this.shader=t;var n=this.uniforms;this._enabledUniforms=Object.keys(n),this._enabledUniforms.sort(),this._textureUniforms=this._enabledUniforms.filter(function(t){var e=this.uniforms[t].type;return"t"===e||"tv"===e},this);var i=this.vertexDefines,a=this.fragmentDefines;if(this.vertexDefines=Se.clone(t.vertexDefines),this.fragmentDefines=Se.clone(t.fragmentDefines),e){for(var o in r)n[o]&&(n[o].value=r[o].value);Se.defaults(this.vertexDefines,i),Se.defaults(this.fragmentDefines,a)}var s={};for(var u in t.textures)s[u]={shaderType:t.textures[u].shaderType,type:t.textures[u].type,enabled:!(!e||!this._textureStatus[u])&&this._textureStatus[u].enabled};this._textureStatus=s,this._programKey=""},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.vertexDefines=Se.clone(this.vertexDefines),t.fragmentDefines=Se.clone(this.fragmentDefines),t.enableTexture(this.getEnabledTextures()),t.precision=this.precision,t},define:function(t,e,r){var n=this.vertexDefines,i=this.fragmentDefines;"vertex"!==t&&"fragment"!==t&&"both"!==t&&arguments.length<3&&(r=e,e=t,t="both"),r=null!=r?r:null,"vertex"!==t&&"both"!==t||n[e]!==r&&(n[e]=r,this._programKey=""),"fragment"!==t&&"both"!==t||i[e]!==r&&(i[e]=r,"both"!==t&&(this._programKey=""))},undefine:function(t,e){"vertex"!==t&&"fragment"!==t&&"both"!==t&&arguments.length<2&&(e=t,t="both"),"vertex"!==t&&"both"!==t||this.isDefined("vertex",e)&&(delete this.vertexDefines[e],this._programKey=""),"fragment"!==t&&"both"!==t||this.isDefined("fragment",e)&&(delete this.fragmentDefines[e],"both"!==t&&(this._programKey=""))},isDefined:function(t,e){switch(t){case"vertex":return void 0!==this.vertexDefines[e];case"fragment":return void 0!==this.fragmentDefines[e]}},getDefine:function(t,e){switch(t){case"vertex":return this.vertexDefines[e];case"fragment":return this.fragmentDefines[e]}},enableTexture:function(t){if(Array.isArray(t))for(var e=0;e=0)this.attributeSemantics[r]={symbol:t,type:e};else if(Mr.indexOf(r)>=0){var n=!1,i=r;r.match(/TRANSPOSE$/)&&(n=!0,i=r.slice(0,-9)),this.matrixSemantics[r]={symbol:t,type:e,isTranspose:n,semanticNoTranspose:i}}else Cr.indexOf(r)>=0&&(this.uniformSemantics[r]={symbol:t,type:e})},_addMaterialUniform:function(t,e,r,n,i,a){a[t]={type:r,value:i?Sr.array:n||Sr[e],semantic:null}},_parseUniforms:function(){function t(t){return null!=t?function(){return t}:null}function e(e,a,o){var s=Z(a,o),u=[];for(var l in s){var c=s[l],h=c.semantic,f=l,d=Ar[a],m=t(s[l].value);s[l].isArray&&(f+="["+s[l].arraySize+"]",d+="v"),u.push(f),n._uniformList.push(l),c.ignore||("sampler2D"!==a&&"samplerCube"!==a||(n.textures[l]={shaderType:i,type:a}),h?n._addSemanticUniform(l,d,h):n._addMaterialUniform(l,a,d,m,s[l].isArray,r))}return u.length>0?"uniform "+a+" "+u.join(",")+";\n":""}var r={},n=this,i="vertex";this._uniformList=[],this._vertexCode=this._vertexCode.replace(xr,e),i="fragment",this._fragmentCode=this._fragmentCode.replace(xr,e),n.matrixSemanticKeys=Object.keys(this.matrixSemantics),this.uniformTemplates=r},_parseAttributes:function(){function t(t,n,i){var a=Z(n,i),o=Rr[n]||1,s=[];for(var u in a){var l=a[u].semantic;if(e[u]={type:"float",size:o,semantic:l||null},l){if(wr.indexOf(l)<0)throw new Error('Unkown semantic "'+l+'"');r.attributeSemantics[l]={symbol:u,type:n}}s.push(u)}return"attribute "+n+" "+s.join(",")+";\n"}var e={},r=this;this._vertexCode=this._vertexCode.replace(Er,t),this.attributes=e},_parseDefines:function(){function t(t,n,i){var a="vertex"===r?e.vertexDefines:e.fragmentDefines;return a[n]||(a[n]="false"!==i&&("true"===i||(i?isNaN(parseFloat(i))?i.trim():parseFloat(i):null))),""}var e=this,r="vertex";this._vertexCode=this._vertexCode.replace(br,t),r="fragment",this._fragmentCode=this._fragmentCode.replace(br,t)},clone:function(){var t=Nr[this._shaderID];return new J(t.vertex,t.fragment)}},Object.defineProperty&&(Object.defineProperty(J.prototype,"shaderID",{get:function(){return this._shaderID}}),Object.defineProperty(J.prototype,"vertex",{get:function(){return this._vertexCode}}),Object.defineProperty(J.prototype,"fragment",{get:function(){return this._fragmentCode}}),Object.defineProperty(J.prototype,"uniforms",{get:function(){return this._uniformList}}));var Pr=/(@import)\s*([0-9a-zA-Z_\-\.]*)/g;J.parseImport=function(t){return t=t.replace(Pr,function(t,e,r){var t=J.source(r);return t?J.parseImport(t):(console.error('Shader chunk "'+r+'" not existed in library'),"")})};var Ir=/(@export)\s*([0-9a-zA-Z_\-\.]*)\s*\n([\s\S]*?)@end/g;J.import=function(t){t.replace(Ir,function(t,e,r,n){var n=n.replace(/(^[\s\t\xa0\u3000]+)|([\u3000\xa0\s\t]+\x24)/g,"");if(n){for(var i,a=r.split("."),o=J.codes,s=0;s0&&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},render:function(t,e,r,n){var i=this.gl,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)}if(r||t.update(!1),t.updateLights(),!(e=e||t.getMainCamera()))return void console.error("Can't find camera in the scene.");e.update();var l=t.updateRenderList(e);this._sceneRendering=t;var c=l.opaque,h=l.transparent,f=t.material;t.trigger("beforerender",this,t,e,l),n?(this.renderPreZ(c,t,e),i.depthFunc(i.LEQUAL)):i.depthFunc(i.LESS);for(var d=kr(),m=Fr.create(),p=0;p0){var s=t[i-1],u=s.joints?s.joints.length:0;if((a.joints?a.joints.length:0)===u&&a.material===s.material&&a.lightGroup===s.lightGroup){a.__program=s.__program;continue}}var l=this._programMgr.getProgram(a,o,e);this.validateProgram(l),a.__program=l}},renderPass:function(t,e,r){this.trigger("beforerenderpass",this,t,e,r),r=r||{},r.getMaterial=r.getMaterial||Q,r.beforeRender=r.beforeRender||$,r.afterRender=r.afterRender||$,this.updatePrograms(t,this._sceneRendering,r),r.sortCompare&&t.sort(r.sortCompare);var n=this.viewport,i=n.devicePixelRatio,a=[n.x*i,n.y*i,n.width*i,n.height*i],o=this.devicePixelRatio,s=this.__currentFrameBuffer?[this.__currentFrameBuffer.getTextureWidth(),this.__currentFrameBuffer.getTextureHeight()]:[this._width*o,this._height*o],u=[a[2],a[3]],l=Date.now();Ur.copy(Wr.VIEW,e.viewMatrix.array),Ur.copy(Wr.PROJECTION,e.projectionMatrix.array),Ur.multiply(Wr.VIEWPROJECTION,e.projectionMatrix.array,Wr.VIEW),Ur.copy(Wr.VIEWINVERSE,e.worldTransform.array),Ur.invert(Wr.PROJECTIONINVERSE,Wr.PROJECTION),Ur.invert(Wr.VIEWPROJECTIONINVERSE,Wr.VIEWPROJECTION);for(var c,h,f,d,m,p,_,v,g,y,T=this.gl,x=this._sceneRendering,E=this.getGLExtension("OES_vertex_array_object"),b=0;bthis.distance,i=1;i<8;i++)if(Qr.dot(e[i].array,r)>this.distance!=n)return!0},intersectLine:function(){var t=Qr.create();return function(e,r,n){var i=this.distanceToPoint(e),a=this.distanceToPoint(r);if(i>0&&a>0||i<0&&a<0)return null;var o=this.normal.array,s=this.distance,u=e.array;Qr.sub(t,r.array,e.array),Qr.normalize(t,t);var l=Qr.dot(o,t);if(0===l)return null;n||(n=new He);var c=(Qr.dot(o,u)-s)/l;return Qr.scaleAndAdd(n.array,u,t,-c),n._dirty=!0,n}}(),applyTransform:function(){var t=$r.create(),e=tn.create(),r=tn.create();return r[3]=1,function(n){n=n.array,Qr.scale(r,this.normal.array,this.distance),tn.transformMat4(r,r,n),this.distance=Qr.dot(r,this.normal.array),$r.invert(t,n),$r.transpose(t,t),e[3]=0,Qr.copy(e,this.normal.array),tn.transformMat4(e,e,t),Qr.copy(this.normal.array,e)}}(),copy:function(t){Qr.copy(this.normal.array,t.normal.array),this.normal._dirty=!0,this.distance=t.distance},clone:function(){var t=new en;return t.copy(this),t}};var rn=fe.vec3,nn=rn.set,an=rn.copy,on=rn.transformMat4,sn=Math.min,un=Math.max,ln=function(){this.planes=[];for(var t=0;t<6;t++)this.planes.push(new en);this.boundingBox=new Ke,this.vertices=[];for(var t=0;t<8;t++)this.vertices[t]=rn.fromValues(0,0,0)};ln.prototype={setFromProjection:function(t){var e=this.planes,r=t.array,n=r[0],i=r[1],a=r[2],o=r[3],s=r[4],u=r[5],l=r[6],c=r[7],h=r[8],f=r[9],d=r[10],m=r[11],p=r[12],_=r[13],v=r[14],g=r[15];nn(e[0].normal.array,o-n,c-s,m-h),e[0].distance=-(g-p),e[0].normalize(),nn(e[1].normal.array,o+n,c+s,m+h),e[1].distance=-(g+p),e[1].normalize(),nn(e[2].normal.array,o+i,c+u,m+f),e[2].distance=-(g+_),e[2].normalize(),nn(e[3].normal.array,o-i,c-u,m-f),e[3].distance=-(g-_),e[3].normalize(),nn(e[4].normal.array,o-a,c-l,m-d),e[4].distance=-(g-v),e[4].normalize(),nn(e[5].normal.array,o+a,c+l,m+d),e[5].distance=-(g+v),e[5].normalize();var y=this.boundingBox,T=this.vertices;if(0===g){var x=u/n,E=-v/(d-1),b=-v/(d+1),A=-b/u,S=-E/u;y.min.set(-A*x,-A,b),y.max.set(A*x,A,E),nn(T[0],-A*x,-A,b),nn(T[1],-A*x,A,b),nn(T[2],A*x,-A,b),nn(T[3],A*x,A,b),nn(T[4],-S*x,-S,E),nn(T[5],-S*x,S,E),nn(T[6],S*x,-S,E),nn(T[7],S*x,S,E)}else{var w=(-1-p)/n,C=(1-p)/n,M=(1-_)/u,R=(-1-_)/u,L=(-1-v)/d,N=(1-v)/d;y.min.set(Math.min(w,C),Math.min(R,M),Math.min(N,L)),y.max.set(Math.max(C,w),Math.max(M,R),Math.max(L,N));var P=y.min.array,I=y.max.array;nn(T[0],P[0],P[1],P[2]),nn(T[1],P[0],I[1],P[2]),nn(T[2],I[0],P[1],P[2]),nn(T[3],I[0],I[1],P[2]),nn(T[4],P[0],P[1],I[2]),nn(T[5],P[0],I[1],I[2]),nn(T[6],I[0],P[1],I[2]),nn(T[7],I[0],I[1],I[2])}},getTransformedBoundingBox:function(){var t=rn.create();return function(e,r){var n=this.vertices,i=r.array,a=e.min,o=e.max,s=a.array,u=o.array,l=n[0];on(t,l,i),an(s,t),an(u,t);for(var c=1;c<8;c++)l=n[c],on(t,l,i),s[0]=sn(t[0],s[0]),s[1]=sn(t[1],s[1]),s[2]=sn(t[2],s[2]),u[0]=un(t[0],u[0]),u[1]=un(t[1],u[1]),u[2]=un(t[2],u[2]);return a._dirty=!0,o._dirty=!0,e}}()};var cn=fe.vec3,hn=function(t,e){this.origin=t||new He,this.direction=e||new He};hn.prototype={constructor:hn,intersectPlane:function(t,e){var r=t.normal.array,n=t.distance,i=this.origin.array,a=this.direction.array,o=cn.dot(r,a);if(0===o)return null;e||(e=new He);var s=(cn.dot(r,i)-n)/o;return cn.scaleAndAdd(e.array,i,a,-s),e._dirty=!0,e},mirrorAgainstPlane:function(t){var e=cn.dot(t.normal.array,this.direction.array);cn.scaleAndAdd(this.direction.array,this.direction.array,t.normal.array,2*-e),this.direction._dirty=!0},distanceToPoint:function(){var t=cn.create();return function(e){cn.sub(t,e,this.origin.array);var r=cn.dot(t,this.direction.array);if(r<0)return cn.distance(this.origin.array,e);var n=cn.lenSquared(t);return Math.sqrt(n-r*r)}}(),intersectSphere:function(){var t=cn.create();return function(e,r,n){var i=this.origin.array,a=this.direction.array;e=e.array,cn.sub(t,e,i);var o=cn.dot(t,a),s=cn.squaredLength(t),u=s-o*o,l=r*r;if(!(u>l)){var c=Math.sqrt(l-u),h=o-c,f=o+c;return n||(n=new He),h<0?f<0?null:(cn.scaleAndAdd(n.array,i,a,f),n):(cn.scaleAndAdd(n.array,i,a,h),n)}}}(),intersectBoundingBox:function(t,e){var r,n,i,a,o,s,u=this.direction.array,l=this.origin.array,c=t.min.array,h=t.max.array,f=1/u[0],d=1/u[1],m=1/u[2];if(f>=0?(r=(c[0]-l[0])*f,n=(h[0]-l[0])*f):(n=(c[0]-l[0])*f,r=(h[0]-l[0])*f),d>=0?(i=(c[1]-l[1])*d,a=(h[1]-l[1])*d):(a=(c[1]-l[1])*d,i=(h[1]-l[1])*d),r>a||i>n)return null;if((i>r||r!==r)&&(r=i),(a=0?(o=(c[2]-l[2])*m,s=(h[2]-l[2])*m):(s=(c[2]-l[2])*m,o=(h[2]-l[2])*m),r>s||o>n)return null;if((o>r||r!==r)&&(r=o),(s=0?r:n;return e||(e=new He),cn.scaleAndAdd(e.array,l,u,p),e},intersectTriangle:function(){var t=cn.create(),e=cn.create(),r=cn.create(),n=cn.create();return function(i,a,o,s,u,l){var c=this.direction.array,h=this.origin.array;i=i.array,a=a.array,o=o.array,cn.sub(t,a,i),cn.sub(e,o,i),cn.cross(n,e,c);var f=cn.dot(t,n);if(s){if(f>-1e-5)return null}else if(f>-1e-5&&f<1e-5)return null;cn.sub(r,h,i);var d=cn.dot(n,r)/f;if(d<0||d>1)return null;cn.cross(n,t,r);var m=cn.dot(c,n)/f;if(m<0||m>1||d+m>1)return null;cn.cross(n,t,e);var p=-cn.dot(r,n)/f;return p<0?null:(u||(u=new He), -l&&He.set(l,1-d-m,d,m),cn.scaleAndAdd(u.array,h,c,p),u)}}(),applyTransform:function(t){He.add(this.direction,this.direction,this.origin),He.transformMat4(this.origin,this.origin,t),He.transformMat4(this.direction,this.direction,t),He.sub(this.direction,this.direction,this.origin),He.normalize(this.direction,this.direction)},copy:function(t){He.copy(this.origin,t.origin),He.copy(this.direction,t.direction)},clone:function(){var t=new hn;return t.copy(this),t}};var fn=fe.vec3,dn=fe.vec4,mn=Zr.extend(function(){return{projectionMatrix:new tr,invProjectionMatrix:new tr,viewMatrix:new tr,frustum:new ln}},function(){this.update(!0)},{update:function(t){Zr.prototype.update.call(this,t),tr.invert(this.viewMatrix,this.worldTransform),this.updateProjectionMatrix(),tr.invert(this.invProjectionMatrix,this.projectionMatrix),this.frustum.setFromProjection(this.projectionMatrix)},setViewMatrix:function(t){tr.copy(this.viewMatrix,t),tr.invert(this.worldTransform,t),this.decomposeWorldTransform()},decomposeProjectionMatrix:function(){},setProjectionMatrix:function(t){tr.copy(this.projectionMatrix,t),tr.invert(this.invProjectionMatrix,t),this.decomposeProjectionMatrix()},updateProjectionMatrix:function(){},castRay:function(){var t=dn.create();return function(e,r){var n=void 0!==r?r:new hn,i=e.array[0],a=e.array[1];return dn.set(t,i,a,-1,1),dn.transformMat4(t,t,this.invProjectionMatrix.array),dn.transformMat4(t,t,this.worldTransform.array),fn.scale(n.origin.array,t,1/t[3]),dn.set(t,i,a,1,1),dn.transformMat4(t,t,this.invProjectionMatrix.array),dn.transformMat4(t,t,this.worldTransform.array),fn.scale(t,t,1/t[3]),fn.sub(n.direction.array,t,n.origin.array),fn.normalize(n.direction.array,n.direction.array),n.direction._dirty=!0,n.origin._dirty=!0,n}}()}),pn=fe.mat4,_n=pn.create(),vn=pn.create(),gn={};rt.prototype.startCount=function(){this._opaqueCount=0,this._transparentCount=0},rt.prototype.add=function(t,e){e?this.transparent[this._transparentCount++]=t:this.opaque[this._opaqueCount++]=t},rt.prototype.endCount=function(){this.transparent.length=this._transparentCount,this.opaque.length=this._opaqueCount};var yn=Zr.extend(function(){return{material:null,lights:[],viewBoundingBoxLastFrame:new Ke,shadowUniforms:{},_cameraList:[],_lightUniforms:{},_previousLightNumber:{},_lightNumber:{},_lightProgramKeys:{},_nodeRepository:{},_renderLists:new or(20)}},function(){this._scene=this},{addToScene:function(t){t instanceof mn?(this._cameraList.length>0&&console.warn("Found multiple camera in one scene. Use the fist one."),this._cameraList.push(t)):t instanceof Jr&&this.lights.push(t),t.name&&(this._nodeRepository[t.name]=t)},removeFromScene:function(t){var e;t instanceof mn?(e=this._cameraList.indexOf(t))>=0&&this._cameraList.splice(e,1):t instanceof Jr&&(e=this.lights.indexOf(t))>=0&&this.lights.splice(e,1),t.name&&delete this._nodeRepository[t.name]},getNode:function(t){return this._nodeRepository[t]},setMainCamera:function(t){var e=this._cameraList.indexOf(t);e>=0&&this._cameraList.splice(e,1),this._cameraList.unshift(t)},getMainCamera:function(){return this._cameraList[0]},getLights:function(){return this.lights},updateLights:function(){var t=this.lights;this._previousLightNumber=this._lightNumber;for(var e={},r=0;r0&&this._doUpdateRenderList(a,e,r,n)}},isFrustumCulled:function(){var t=new Ke,e=new tr;return function(r,n,i){var a=r.boundingBox||r.geometry.boundingBox;if(e.array=i,t.transformFrom(a,e),r.castShadow&&this.viewBoundingBoxLastFrame.union(t),r.frustumCulling&&!r.isSkinnedMesh()){if(!t.intersectBoundingBox(n.frustum.boundingBox))return!0;e.array=n.projectionMatrix.array,t.max.array[2]>0&&t.min.array[2]<0&&(t.max.array[2]=-1e-20),t.applyProjection(e);var o=t.min.array,s=t.max.array;if(s[0]<-1||o[0]>1||s[1]<-1||o[1]>1||s[2]<-1||o[2]>1)return!0}return!1}}(),_updateLightUniforms:function(){var t=this.lights;t.sort(nt);var e=this._lightUniforms;for(var r in e)for(var n in e[r])e[r][n].value.length=0;for(var i=0;ia[0]&&(a[0]=s),u>a[1]&&(a[1]=u),l>a[2]&&(a[2]=l)}r._dirty=!0,n._dirty=!0}},dirty:function(){for(var t=this.getEnabledAttributes(),e=0;e=0){e||(e=En());var r=this.indices;return e[0]=r[3*t],e[1]=r[3*t+1],e[2]=r[3*t+2],e}},setTriangleIndices:function(t,e){var r=this.indices;r[3*t]=e[0],r[3*t+1]=e[1],r[3*t+2]=e[2]},isUseIndices:function(){return!!this.indices},initIndicesFromArray:function(t){var e,r=this.vertexCount>65535?Fe.Uint32Array:Fe.Uint16Array;if(t[0]&&t[0].length){var n=0;e=new r(3*t.length);for(var i=0;i=0&&(e.splice(r,1),delete this.attributes[t],!0)},getAttribute:function(t){return this.attributes[t]},getEnabledAttributes:function(){var t=this._enabledAttributes,e=this._attributeList;if(t)return t;for(var r=[],n=this.vertexCount,i=0;i65535&&(this.indices=new Fe.Uint32Array(this.indices));for(var t=this.attributes,e=this.indices,r=this.getEnabledAttributes(),n={},i=0;i65535?Uint32Array:Uint16Array,m=this.indices=new d(e*t*6),p=this.radius,_=this.phiStart,v=this.phiLength,g=this.thetaStart,y=this.thetaLength,p=this.radius,T=[],x=[],E=0,b=1/p;for(f=0;f<=t;f++)for(h=0;h<=e;h++)l=h/e,c=f/t,o=-p*Math.cos(_+l*v)*Math.sin(g+c*y),s=p*Math.cos(g+c*y),u=p*Math.sin(_+l*v)*Math.sin(g+c*y),T[0]=o,T[1]=s,T[2]=u,x[0]=l,x[1]=c,r.set(E,T),n.set(E,x),T[0]*=b,T[1]*=b,T[2]*=b,i.set(E,T),E++;var A,S,w,C,M=e+1,R=0;for(f=0;f65535?Uint32Array:Uint16Array,v=(o-1)*(s-1)*6,g=this.indices=new _(v),y=0,f=0;f>1,t|=t>>2,t|=t>>4,t|=t>>8,t|=t>>16,++t},Nn.nearestPowerOfTwo=function(t){return Math.pow(2,Math.round(Math.log(t)/Math.LN2))};var Pn=Nn.isPowerOfTwo,In=ir.extend(function(){return{image:null,pixels:null,mipmaps:[],convertToPOT:!1}},{textureType:"texture2D",update:function(t){var e=t.gl;e.bindTexture(e.TEXTURE_2D,this._cache.get("webgl_texture")),this.updateCommon(t);var r=this.format,n=this.type,i=!(!this.convertToPOT||this.mipmaps.length||!this.image||this.wrapS!==ir.REPEAT&&this.wrapT!==ir.REPEAT||!this.NPOT);e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,i?this.wrapS:this.getAvailableWrapS()),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,i?this.wrapT:this.getAvailableWrapT()),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,i?this.magFilter:this.getAvailableMagFilter()),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,i?this.minFilter:this.getAvailableMinFilter());var a=t.getGLExtension("EXT_texture_filter_anisotropic");if(a&&this.anisotropic>1&&e.texParameterf(e.TEXTURE_2D,a.TEXTURE_MAX_ANISOTROPY_EXT,this.anisotropic),36193===n){t.getGLExtension("OES_texture_half_float")||(n=Oe.FLOAT)}if(this.mipmaps.length)for(var o=this.width,s=this.height,u=0;u=ir.COMPRESSED_RGB_S3TC_DXT1_EXT?t.compressedTexImage2D(t.TEXTURE_2D,r,a,n,i,0,e.pixels):t.texImage2D(t.TEXTURE_2D,r,a,n,i,0,a,o,e.pixels)},generateMipmap:function(t){var e=t.gl;this.useMipmap&&!this.NPOT&&(e.bindTexture(e.TEXTURE_2D,this._cache.get("webgl_texture")),e.generateMipmap(e.TEXTURE_2D))},isPowerOfTwo:function(){return Pn(this.width)&&Pn(this.height)},isRenderable:function(){return this.image?"CANVAS"===this.image.nodeName||"VIDEO"===this.image.nodeName||this.image.complete:!(!this.width||!this.height)},bind:function(t){t.gl.bindTexture(t.gl.TEXTURE_2D,this.getWebGLTexture(t))},unbind:function(t){t.gl.bindTexture(t.gl.TEXTURE_2D,null)},load:function(t,e){var r=new Image;e&&(r.crossOrigin=e);var n=this;return r.onload=function(){n.dirty(),n.trigger("success",n),r.onload=null},r.onerror=function(){n.trigger("error",n),r.onerror=null},r.src=t,this.image=r,this}});Object.defineProperty(In.prototype,"width",{get:function(){return this.image?this.image.width:this._width},set:function(t){this.image?console.warn("Texture from image can't set width"):(this._width!==t&&this.dirty(),this._width=t)}}),Object.defineProperty(In.prototype,"height",{get:function(){return this.image?this.image.height:this._height},set:function(t){this.image?console.warn("Texture from image can't set height"):(this._height!==t&&this.dirty(),this._height=t)}});var Dn=Nn.isPowerOfTwo,On=["px","nx","py","ny","pz","nz"],Bn=ir.extend(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:[]}},{textureType:"textureCube",update:function(t){var e=t.gl;e.bindTexture(e.TEXTURE_CUBE_MAP,this._cache.get("webgl_texture")),this.updateCommon(t);var r=this.format,n=this.type;e.texParameteri(e.TEXTURE_CUBE_MAP,e.TEXTURE_WRAP_S,this.getAvailableWrapS()),e.texParameteri(e.TEXTURE_CUBE_MAP,e.TEXTURE_WRAP_T,this.getAvailableWrapT()),e.texParameteri(e.TEXTURE_CUBE_MAP,e.TEXTURE_MAG_FILTER,this.getAvailableMagFilter()),e.texParameteri(e.TEXTURE_CUBE_MAP,e.TEXTURE_MIN_FILTER,this.getAvailableMinFilter());var i=t.getGLExtension("EXT_texture_filter_anisotropic");if(i&&this.anisotropic>1&&e.texParameterf(e.TEXTURE_CUBE_MAP,i.TEXTURE_MAX_ANISOTROPY_EXT,this.anisotropic),36193===n){t.getGLExtension("OES_texture_half_float")||(n=Oe.FLOAT)}if(this.mipmaps.length)for(var a=this.width,o=this.height,s=0;s0},beforeRender:function(t){},afterRender:function(t,e){},getBoundingBox:function(t,e){return e=Zr.prototype.getBoundingBox.call(this,t,e),this.geometry&&this.geometry.boundingBox&&e.union(this.geometry.boundingBox),e},clone:function(){var t=["castShadow","receiveShadow","mode","culling","cullFace","frontFace","frustumCulling","renderOrder","lineWidth","ignorePicking","ignorePreZ","ignoreGBuffer"];return function(){var e=Zr.prototype.clone.call(this);e.geometry=this.geometry,e.material=this.material;for(var r=0;r0)},getSkinMatricesTexture:function(){return this._skinMatricesTexture=this._skinMatricesTexture||new In({type:Oe.FLOAT,minFilter:Oe.NEAREST,magFilter:Oe.NEAREST,useMipmap:!1,flipY:!1}),this._skinMatricesTexture},clone:function(){var t=Hn.prototype.clone.call(this);return t.skeleton=this.skeleton,this.joints&&(t.joints=this.joints.slice()),t}});Gn.POINTS=Oe.POINTS,Gn.LINES=Oe.LINES,Gn.LINE_LOOP=Oe.LINE_LOOP,Gn.LINE_STRIP=Oe.LINE_STRIP,Gn.TRIANGLES=Oe.TRIANGLES,Gn.TRIANGLE_STRIP=Oe.TRIANGLE_STRIP,Gn.TRIANGLE_FAN=Oe.TRIANGLE_FAN,Gn.BACK=Oe.BACK,Gn.FRONT=Oe.FRONT,Gn.FRONT_AND_BACK=Oe.FRONT_AND_BACK,Gn.CW=Oe.CW,Gn.CCW=Oe.CCW;var Vn=mn.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=mn.prototype.clone.call(this);return t.fov=this.fov,t.aspect=this.aspect,t.near=this.near,t.far=this.far,t}}),Wn=mn.extend({left:-1,right:1,near:-1,far:1,top:1,bottom:-1},{updateProjectionMatrix:function(){this.projectionMatrix.ortho(this.left,this.right,this.bottom,this.top,this.near,this.far)},decomposeProjectionMatrix:function(){var t=this.projectionMatrix.array;this.left=(-1-t[12])/t[0],this.right=(1-t[12])/t[0],this.top=(1-t[13])/t[5],this.bottom=(-1-t[13])/t[5],this.near=-(-1-t[14])/t[10],this.far=-(1-t[14])/t[10]},clone:function(){var t=mn.prototype.clone.call(this);return t.left=this.left,t.right=this.right,t.near=this.near,t.far=this.far,t.top=this.top,t.bottom=this.bottom,t}}),zn={get:pt -},Xn="\n@export clay.standard.chunk.varying\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\n#if defined(PARALLAXOCCLUSIONMAP_ENABLED) || defined(NORMALMAP_ENABLED)\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n#if defined(AOMAP_ENABLED)\nvarying vec2 v_Texcoord2;\n#endif\n#ifdef VERTEX_COLOR\nvarying vec4 v_Color;\n#endif\n@end\n@export clay.standard.chunk.light_header\n#ifdef AMBIENT_LIGHT_COUNT\n@import clay.header.ambient_light\n#endif\n#ifdef AMBIENT_SH_LIGHT_COUNT\n@import clay.header.ambient_sh_light\n#endif\n#ifdef AMBIENT_CUBEMAP_LIGHT_COUNT\n@import clay.header.ambient_cubemap_light\n#endif\n#ifdef POINT_LIGHT_COUNT\n@import clay.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_COUNT\n@import clay.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_COUNT\n@import clay.header.spot_light\n#endif\n@end\n@export clay.standard.vertex\n#define SHADER_NAME standard\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n#if defined(AOMAP_ENABLED)\nattribute vec2 texcoord2 : TEXCOORD_1;\n#endif\nattribute vec3 normal : NORMAL;\nattribute vec4 tangent : TANGENT;\n#ifdef VERTEX_COLOR\nattribute vec4 a_Color : COLOR;\n#endif\nattribute vec3 barycentric;\n@import clay.standard.chunk.varying\n@import clay.chunk.skinning_header\nvoid main()\n{\n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\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 gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n v_Barycentric = barycentric;\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\n#if defined(PARALLAXOCCLUSIONMAP_ENABLED) || defined(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#ifdef VERTEX_COLOR\n v_Color = a_Color;\n#endif\n#if defined(AOMAP_ENABLED)\n v_Texcoord2 = texcoord2;\n#endif\n}\n@end\n@export clay.standard.fragment\n#define PI 3.14159265358979\n#define GLOSSINESS_CHANNEL 0\n#define ROUGHNESS_CHANNEL 0\n#define METALNESS_CHANNEL 1\n@import clay.standard.chunk.varying\nuniform mat4 viewInverse : VIEWINVERSE;\n#ifdef NORMALMAP_ENABLED\nuniform sampler2D normalMap;\n#endif\n#ifdef DIFFUSEMAP_ENABLED\nuniform sampler2D diffuseMap;\n#endif\n#ifdef SPECULARMAP_ENABLED\nuniform sampler2D specularMap;\n#endif\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 GLOSSINESSMAP_ENABLED\nuniform sampler2D glossinessMap;\n #endif\n#endif\n#ifdef METALNESSMAP_ENABLED\nuniform sampler2D metalnessMap;\n#endif\n#ifdef ENVIRONMENTMAP_ENABLED\nuniform samplerCube environmentMap;\n #ifdef PARALLAX_CORRECTED\nuniform vec3 environmentBoxMin;\nuniform vec3 environmentBoxMax;\n #endif\n#endif\n#ifdef BRDFLOOKUP_ENABLED\nuniform sampler2D brdfLookup;\n#endif\n#ifdef EMISSIVEMAP_ENABLED\nuniform sampler2D emissiveMap;\n#endif\n#ifdef SSAOMAP_ENABLED\nuniform sampler2D ssaoMap;\nuniform vec4 viewport : VIEWPORT;\n#endif\n#ifdef AOMAP_ENABLED\nuniform sampler2D aoMap;\nuniform float aoIntensity;\n#endif\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\n#ifdef ALPHA_TEST\nuniform float alphaCutoff: 0.9;\n#endif\n#ifdef USE_METALNESS\nuniform float metalness : 0.0;\n#else\nuniform vec3 specularColor : [0.1, 0.1, 0.1];\n#endif\nuniform vec3 emission : [0.0, 0.0, 0.0];\nuniform float emissionIntensity: 1;\nuniform float lineWidth : 0.0;\nuniform vec4 lineColor : [0.0, 0.0, 0.0, 0.6];\n#ifdef ENVIRONMENTMAP_PREFILTER\nuniform float maxMipmapLevel: 5;\n#endif\n@import clay.standard.chunk.light_header\n@import clay.util.calculate_attenuation\n@import clay.util.edge_factor\n@import clay.util.rgbm\n@import clay.util.srgb\n@import clay.plugin.compute_shadow_map\n@import clay.util.parallax_correct\n@import clay.util.ACES\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}\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}\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#ifdef PARALLAXOCCLUSIONMAP_ENABLED\nuniform float parallaxOcclusionScale : 0.02;\nuniform float parallaxMaxLayers : 20;\nuniform float parallaxMinLayers : 5;\nuniform sampler2D parallaxOcclusionMap;\nmat3 transpose(in mat3 inMat)\n{\n vec3 i0 = inMat[0];\n vec3 i1 = inMat[1];\n vec3 i2 = inMat[2];\n return mat3(\n vec3(i0.x, i1.x, i2.x),\n vec3(i0.y, i1.y, i2.y),\n vec3(i0.z, i1.z, i2.z)\n );\n}\nvec2 parallaxUv(vec2 uv, vec3 viewDir)\n{\n float numLayers = mix(parallaxMaxLayers, parallaxMinLayers, abs(dot(vec3(0.0, 0.0, 1.0), viewDir)));\n float layerHeight = 1.0 / numLayers;\n float curLayerHeight = 0.0;\n vec2 deltaUv = viewDir.xy * parallaxOcclusionScale / (viewDir.z * numLayers);\n vec2 curUv = uv;\n float height = 1.0 - texture2D(parallaxOcclusionMap, curUv).r;\n for (int i = 0; i < 30; i++) {\n curLayerHeight += layerHeight;\n curUv -= deltaUv;\n height = 1.0 - texture2D(parallaxOcclusionMap, curUv).r;\n if (height < curLayerHeight) {\n break;\n }\n }\n vec2 prevUv = curUv + deltaUv;\n float next = height - curLayerHeight;\n float prev = 1.0 - texture2D(parallaxOcclusionMap, prevUv).r - curLayerHeight + layerHeight;\n return mix(curUv, prevUv, next / (next - prev));\n}\n#endif\nvoid main() {\n vec4 albedoColor = vec4(color, alpha);\n#ifdef VERTEX_COLOR\n albedoColor *= v_Color;\n#endif\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = normalize(eyePos - v_WorldPosition);\n vec2 uv = v_Texcoord;\n#if defined(PARALLAXOCCLUSIONMAP_ENABLED) || defined(NORMALMAP_ENABLED)\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\n#endif\n#ifdef PARALLAXOCCLUSIONMAP_ENABLED\n uv = parallaxUv(v_Texcoord, normalize(transpose(tbn) * -V));\n#endif\n#ifdef DIFFUSEMAP_ENABLED\n vec4 texel = texture2D(diffuseMap, uv);\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#ifdef USE_METALNESS\n float m = metalness;\n #ifdef METALNESSMAP_ENABLED\n float m2 = texture2D(metalnessMap, uv)[METALNESS_CHANNEL];\n m = clamp(m2 + (m - 0.5) * 2.0, 0.0, 1.0);\n #endif\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#ifdef USE_ROUGHNESS\n float g = 1.0 - roughness;\n #ifdef ROUGHNESSMAP_ENABLED\n float g2 = 1.0 - texture2D(roughnessMap, uv)[ROUGHNESS_CHANNEL];\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\n #endif\n#else\n float g = glossiness;\n #ifdef GLOSSINESSMAP_ENABLED\n float g2 = texture2D(glossinessMap, uv)[GLOSSINESS_CHANNEL];\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\n #endif\n#endif\n#ifdef SPECULARMAP_ENABLED\n spec *= sRGBToLinear(texture2D(specularMap, uv)).rgb;\n#endif\n vec3 N = v_Normal;\n#ifdef DOUBLE_SIDED\n if (dot(N, V) < 0.0) {\n N = -N;\n }\n#endif\n#ifdef NORMALMAP_ENABLED\n if (dot(v_Tangent, v_Tangent) > 0.0) {\n vec3 normalTexel = texture2D(normalMap, uv).xyz;\n if (dot(normalTexel, normalTexel) > 0.0) { N = normalTexel * 2.0 - 1.0;\n tbn[1] = -tbn[1];\n N = normalize(tbn * N);\n }\n }\n#endif\n vec3 diffuseTerm = vec3(0.0, 0.0, 0.0);\n vec3 specularTerm = vec3(0.0, 0.0, 0.0);\n float ndv = clamp(dot(N, V), 0.0, 1.0);\n vec3 fresnelTerm = F_Schlick(ndv, spec);\n#ifdef AMBIENT_LIGHT_COUNT\n for(int _idx_ = 0; _idx_ < AMBIENT_LIGHT_COUNT; _idx_++)\n {{\n diffuseTerm += ambientLightColor[_idx_];\n }}\n#endif\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#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 vec3 lightPosition = pointLightPosition[_idx_];\n vec3 lc = pointLightColor[_idx_];\n float range = pointLightRange[_idx_];\n vec3 L = lightPosition - v_WorldPosition;\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 float shadowContrib = 1.0;\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribsPoint[_idx_];\n }\n#endif\n vec3 li = lc * ndl * attenuation * shadowContrib;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\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 _idx_ = 0; _idx_ < DIRECTIONAL_LIGHT_COUNT; _idx_++)\n {{\n vec3 L = -normalize(directionalLightDirection[_idx_]);\n vec3 lc = directionalLightColor[_idx_];\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 shadowContrib = 1.0;\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribsDir[_idx_];\n }\n#endif\n vec3 li = lc * ndl * shadowContrib;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }}\n#endif\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 vec3 L = lightPosition - v_WorldPosition;\n float dist = length(L);\n float attenuation = lightAttenuation(dist, range);\n L /= dist;\n float c = dot(spotLightDirection, L);\n float falloff;\n falloff = clamp((c - a) /(b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\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 shadowContrib = 1.0;\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\n if (shadowEnabled)\n {\n shadowContrib = shadowContribsSpot[i];\n }\n#endif\n vec3 li = lc * attenuation * (1.0 - falloff) * shadowContrib * ndl;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }\n#endif\n vec4 outColor = albedoColor;\n outColor.rgb *= max(diffuseTerm, vec3(0.0));\n outColor.rgb += max(specularTerm, vec3(0.0));\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), 8.12);\n outColor.rgb += ambientCubemapLightColor[_idx_] * envTexel2 * envWeight2;\n }}\n#endif\n#ifdef ENVIRONMENTMAP_ENABLED\n vec3 envWeight = g * fresnelTerm;\n vec3 L = reflect(-V, N);\n #ifdef PARALLAX_CORRECTED\n L = parallaxCorrect(L, v_WorldPosition, environmentBoxMin, environmentBoxMax);\n#endif\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 #ifdef BRDFLOOKUP_ENABLED\n vec2 brdfParam = texture2D(brdfLookup, vec2(rough, ndv)).xy;\n envWeight = spec * brdfParam.x + brdfParam.y;\n #endif\n #else\n vec3 envTexel = textureCube(environmentMap, L).xyz;\n #endif\n outColor.rgb += envTexel * envWeight;\n#endif\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#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 outColor.rgb *= aoFactor;\n vec3 lEmission = emission;\n#ifdef EMISSIVEMAP_ENABLED\n lEmission *= texture2D(emissiveMap, uv).rgb;\n#endif\n outColor.rgb += lEmission * emissionIntensity;\n if(lineWidth > 0.)\n {\n outColor.rgb = mix(outColor.rgb, lineColor.rgb, (1.0 - edgeFactor(lineWidth)) * lineColor.a);\n }\n#ifdef ALPHA_TEST\n if (outColor.a < alphaCutoff) {\n discard;\n }\n#endif\n#ifdef TONEMAPPING\n outColor.rgb = ACESToneMapping(outColor.rgb);\n#endif\n#ifdef SRGB_ENCODE\n outColor = linearTosRGB(outColor);\n#endif\n gl_FragColor = encodeHDR(outColor);\n}\n@end\n@export clay.standardMR.vertex\n@import clay.standard.vertex\n@end\n@export clay.standardMR.fragment\n#define USE_METALNESS\n#define USE_ROUGHNESS\n@import clay.standard.fragment\n@end";J.import(Xn);var jn,qn=["diffuseMap","normalMap","roughnessMap","metalnessMap","emissiveMap","environmentMap","brdfLookup","ssaoMap","aoMap"],Yn=["color","emission","emissionIntensity","alpha","roughness","metalness","uvRepeat","uvOffset","aoIntensity","alphaCutoff"],Kn=["linear","encodeRGBM","decodeRGBM","doubleSided","alphaTest","roughnessChannel","metalnessChannel","environmentMapPrefiltered"],Zn={roughnessChannel:"ROUGHNESS_CHANNEL",metalnessChannel:"METALNESS_CHANNEL"},Jn={linear:"SRGB_DECODE",encodeRGBM:"RGBM_ENCODE",decodeRGBM:"RGBM_DECODE",doubleSided:"DOUBLE_SIDED",alphaTest:"ALPHA_TEST",environmentMapPrefiltered:"ENVIRONMENTMAP_PREFILTER"},Qn=dr.extend(function(){return jn||(jn=new J(J.source("clay.standard.vertex"),J.source("clay.standard.fragment"))),{shader:jn}},function(t){Se.extend(this,t),Se.defaults(this,{color:[1,1,1],emission:[0,0,0],emissionIntensity:0,roughness:.5,metalness:0,alpha:1,alphaTest:!1,alphaCutoff:.9,doubleSided:!1,uvRepeat:[1,1],uvOffset:[0,0],aoIntensity:1,environmentMapPrefiltered:!1,linear:!1,encodeRGBM:!1,decodeRGBM:!1,roughnessChannel:0,metalnessChannel:1}),this.define("fragment","USE_METALNESS"),this.define("fragment","USE_ROUGHNESS")},{clone:function(){var t=new Qn({name:this.name});return qn.forEach(function(e){this[e]&&(t[e]=this[e])},this),Yn.concat(Kn).forEach(function(e){t[e]=this[e]},this),t}});Yn.forEach(function(t){Object.defineProperty(Qn.prototype,t,{get:function(){return this.get(t)},set:function(e){this.setUniform(t,e)}})}),qn.forEach(function(t){Object.defineProperty(Qn.prototype,t,{get:function(){return this.get(t)},set:function(e){this.setUniform(t,e)}})}),Kn.forEach(function(t){var e="_"+t;Object.defineProperty(Qn.prototype,t,{get:function(){return this[e]},set:function(r){if(this[e]=r,t in Zn){var n=Zn[t];this.define("fragment",n,r)}else{var n=Jn[t];r?this.define("fragment",n):this.undefine("fragment",n)}}})}),Object.defineProperty(Qn.prototype,"environmentBox",{get:function(){var t=this._environmentBox;return t&&(t.min.setArray(this.get("environmentBoxMin")),t.max.setArray(this.get("environmentBoxMax"))),t},set:function(t){this._environmentBox=t;var e=this.uniforms=this.uniforms||{};e.environmentBoxMin=e.environmentBoxMin||{value:null},e.environmentBoxMax=e.environmentBoxMax||{value:null},t&&(this.setUniform("environmentBoxMin",t.min.array),this.setUniform("environmentBoxMax",t.max.array)),t?this.define("fragment","PARALLAX_CORRECTED"):this.undefine("fragment","PARALLAX_CORRECTED")}});var $n=we.extend({name:"",index:-1,node:null}),ti=fe.quat,ei=fe.vec3,ri=fe.mat4,ni=we.extend(function(){return{relativeRootNode:null,name:"",joints:[],_clips:[],_invBindPoseMatricesArray:null,_jointMatricesSubArrays:[],_skinMatricesArray:null,_skinMatricesSubArrays:[],_subSkinMatricesArray:{}}},{addClip:function(t,e){for(var r=0;r0&&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=ri.create();return function(){this._invBindPoseMatricesArray=new Float32Array(16*this.joints.length),this._skinMatricesArray=new Float32Array(16*this.joints.length);for(var e=0;e 0.)\n {\n gl_FragColor.rgb = mix(gl_FragColor.rgb, lineColor.rgb, (1.0 - edgeFactor(lineWidth)) * lineColor.a);\n }\n#ifdef ALPHA_TEST\n if (gl_FragColor.a < alphaCutoff) {\n discard;\n }\n#endif\n#ifdef TONEMAPPING\n gl_FragColor.rgb = ACESToneMapping(gl_FragColor.rgb);\n#endif\n#ifdef SRGB_ENCODE\n gl_FragColor = linearTosRGB(gl_FragColor);\n#endif\n gl_FragColor = encodeHDR(gl_FragColor);\n}\n@end"), -J.import("\n@export clay.lambert.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 normal : NORMAL;\nattribute vec3 barycentric;\n#ifdef VERTEX_COLOR\nattribute vec4 a_Color : COLOR;\nvarying vec4 v_Color;\n#endif\n@import clay.chunk.skinning_header\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\nvoid main()\n{\n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n#endif\n gl_Position = worldViewProjection * vec4( skinnedPosition, 1.0 );\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 v_Barycentric = barycentric;\n#ifdef VERTEX_COLOR\n v_Color = a_Color;\n#endif\n}\n@end\n@export clay.lambert.fragment\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nuniform sampler2D diffuseMap;\nuniform sampler2D alphaMap;\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform vec3 emission : [0.0, 0.0, 0.0];\nuniform float alpha : 1.0;\n#ifdef ALPHA_TEST\nuniform float alphaCutoff: 0.9;\n#endif\nuniform float lineWidth : 0.0;\nuniform vec4 lineColor : [0.0, 0.0, 0.0, 0.6];\nvarying vec3 v_Barycentric;\n#ifdef VERTEX_COLOR\nvarying vec4 v_Color;\n#endif\n#ifdef AMBIENT_LIGHT_COUNT\n@import clay.header.ambient_light\n#endif\n#ifdef AMBIENT_SH_LIGHT_COUNT\n@import clay.header.ambient_sh_light\n#endif\n#ifdef POINT_LIGHT_COUNT\n@import clay.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_COUNT\n@import clay.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_COUNT\n@import clay.header.spot_light\n#endif\n@import clay.util.calculate_attenuation\n@import clay.util.edge_factor\n@import clay.util.rgbm\n@import clay.plugin.compute_shadow_map\n@import clay.util.ACES\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 gl_FragColor = vec4(color, alpha);\n#ifdef VERTEX_COLOR\n gl_FragColor *= v_Color;\n#endif\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 vec3 diffuseColor = vec3(0.0, 0.0, 0.0);\n#ifdef AMBIENT_LIGHT_COUNT\n for(int _idx_ = 0; _idx_ < AMBIENT_LIGHT_COUNT; _idx_++)\n {\n diffuseColor += ambientLightColor[_idx_];\n }\n#endif\n#ifdef AMBIENT_SH_LIGHT_COUNT\n for(int _idx_ = 0; _idx_ < AMBIENT_SH_LIGHT_COUNT; _idx_++)\n {{\n diffuseColor += calcAmbientSHLight(_idx_, v_Normal) * ambientSHLightColor[_idx_];\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 vec3 lightPosition = pointLightPosition[i];\n vec3 lightColor = pointLightColor[i];\n float range = pointLightRange[i];\n vec3 lightDirection = lightPosition - v_WorldPosition;\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range);\n lightDirection /= dist;\n float ndl = dot( v_Normal, lightDirection );\n float shadowContrib = 1.0;\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribsPoint[i];\n }\n#endif\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 float ndl = dot(v_Normal, normalize(lightDirection));\n float shadowContrib = 1.0;\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribsDir[i];\n }\n#endif\n diffuseColor += lightColor * clamp(ndl, 0.0, 1.0) * shadowContrib;\n }\n#endif\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 vec3 lightDirection = lightPosition - v_WorldPosition;\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range);\n lightDirection /= dist;\n float c = dot(spotLightDirection, lightDirection);\n float falloff;\n falloff = clamp((c - a) /( b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n float ndl = dot(v_Normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\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 gl_FragColor.rgb *= diffuseColor;\n gl_FragColor.rgb += emission;\n if(lineWidth > 0.)\n {\n gl_FragColor.rgb = mix(gl_FragColor.rgb, lineColor.rgb, (1.0 - edgeFactor(lineWidth)) * lineColor.a);\n }\n#ifdef ALPHA_TEST\n if (gl_FragColor.a < alphaCutoff) {\n discard;\n }\n#endif\n#ifdef TONEMAPPING\n gl_FragColor.rgb = ACESToneMapping(gl_FragColor.rgb);\n#endif\n#ifdef SRGB_ENCODE\n gl_FragColor = linearTosRGB(gl_FragColor);\n#endif\n gl_FragColor = encodeHDR(gl_FragColor);\n}\n@end"),J.import(Xn),J.import("@export clay.wireframe.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world : WORLD;\nattribute vec3 position : POSITION;\nattribute vec3 barycentric;\n@import clay.chunk.skinning_header\nvarying vec3 v_Barycentric;\nvoid main()\n{\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0 );\n v_Barycentric = barycentric;\n}\n@end\n@export clay.wireframe.fragment\nuniform vec3 color : [0.0, 0.0, 0.0];\nuniform float alpha : 1.0;\nuniform float lineWidth : 1.0;\nvarying vec3 v_Barycentric;\n@import clay.util.edge_factor\nvoid main()\n{\n gl_FragColor.rgb = color;\n gl_FragColor.a = (1.0-edgeFactor(lineWidth)) * alpha;\n}\n@end"),J.import(ai),J.import(Br),kn.template("clay.basic",J.source("clay.basic.vertex"),J.source("clay.basic.fragment")),kn.template("clay.lambert",J.source("clay.lambert.vertex"),J.source("clay.lambert.fragment")),kn.template("clay.wireframe",J.source("clay.wireframe.vertex"),J.source("clay.wireframe.fragment")),kn.template("clay.skybox",J.source("clay.skybox.vertex"),J.source("clay.skybox.fragment")),kn.template("clay.prez",J.source("clay.prez.vertex"),J.source("clay.prez.fragment")),kn.template("clay.standard",J.source("clay.standard.vertex"),J.source("clay.standard.fragment")),kn.template("clay.standardMR",J.source("clay.standardMR.vertex"),J.source("clay.standardMR.fragment"));var oi={NORMAL:"normal",POSITION:"position",TEXCOORD_0:"texcoord0",TEXCOORD_1:"texcoord1",WEIGHTS_0:"weight",JOINTS_0:"joint",COLOR_0:"color"},si={5120:Fe.Int8Array,5121:Fe.Uint8Array,5122:Fe.Int16Array,5123:Fe.Uint16Array,5125:Fe.Uint32Array,5126:Fe.Float32Array},ui={SCALAR:1,VEC2:2,VEC3:3,VEC4:4,MAT2:4,MAT3:9,MAT4:16},li=we.extend({rootNode:null,rootPath:null,textureRootPath:null,bufferRootPath:null,shader:"clay.standard",useStandardMaterial:!1,includeCamera:!0,includeAnimation:!0,includeMesh:!0,includeTexture:!0,crossOrigin:"",textureFlipY:!1,textureConvertToPOT:!1,shaderLibrary:null},function(){this.shaderLibrary||(this.shaderLibrary=kn.createLibrary())},{load:function(t){var e=this,r=t.endsWith(".glb");null==this.rootPath&&(this.rootPath=t.slice(0,t.lastIndexOf("/"))),zn.get({url:t,onprogress:function(t,r,n){e.trigger("progress",t,r,n)},onerror:function(t){e.trigger("error",t)},responseType:r?"arraybuffer":"text",onload:function(t){r?e.parseBinary(t):e.parse(JSON.parse(t))}})},parseBinary:function(t){var e=new Uint32Array(t,0,4);if(1179937895!==e[0])return void this.trigger("error","Invalid glTF binary format: Invalid header");if(e[0]<2)return void this.trigger("error","Only glTF2.0 is supported.");for(var r,n=new DataView(t,12),i=[],a=0;a=r.COLOR_ATTACHMENT0&&a<=r.COLOR_ATTACHMENT0+8&&i.push(a);n.drawBuffersEXT(i)}}this.trigger("beforerender",this,t);var o=this.clearDepth?r.DEPTH_BUFFER_BIT:0;if(r.depthMask(!0),this.clearColor){o|=r.COLOR_BUFFER_BIT,r.colorMask(!0,!0,!0,!0);var s=this.clearColor;Array.isArray(s)&&r.clearColor(s[0],s[1],s[2],s[3])}r.clear(o),this.blendWithPrevious?(r.enable(r.BLEND),this.material.transparent=!0):(r.disable(r.BLEND),this.material.transparent=!1),this.renderQuad(t),this.trigger("afterrender",this,t),e&&this.unbind(t,e)},renderQuad:function(t){Ti.material=this.material,t.renderPass([Ti],xi)},dispose:function(t){}});J.import(ai);var bi=Gn.extend(function(){var t=new J({vertex:J.source("clay.skybox.vertex"),fragment:J.source("clay.skybox.fragment")}),e=new dr({shader:t,depthMask:!1});return{scene:null,geometry:new Mn,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(),t.skybox=this,this.scene=t,t.on("beforerender",this._beforeRenderScene,this)},detachScene:function(){this.scene&&(this.scene.off("beforerender",this._beforeRenderScene),this.scene.skybox=null),this.scene=null},dispose:function(t){this.detachScene(),this.geometry.dispose(t)},setEnvironmentMap:function(t){this.material.set("environmentMap",t)},getEnvironmentMap:function(){return this.material.get("environmentMap")},_beforeRenderScene:function(t,e,r){this.renderSkybox(t,r)},renderSkybox:function(t,e){this.position.copy(e.getWorldPosition()),this.update(),t.gl.disable(t.gl.BLEND),this.material.get("lod")>0?this.material.define("fragment","LOD"):this.material.undefine("fragment","LOD"),t.renderPass([this],e)}}),Ai=["px","nx","py","ny","pz","nz"],Si=we.extend(function(){var t={position:new He,far:1e3,near:.1,texture:null,shadowMapPass:null},e=t._cameras={px:new Vn({fov:90}),nx:new Vn({fov:90}),py:new Vn({fov:90}),ny:new Vn({fov:90}),pz:new Vn({fov:90}),nz:new Vn({fov:90})};return e.px.lookAt(He.POSITIVE_X,He.NEGATIVE_Y),e.nx.lookAt(He.NEGATIVE_X,He.NEGATIVE_Y),e.py.lookAt(He.POSITIVE_Y,He.POSITIVE_Z), -e.ny.lookAt(He.NEGATIVE_Y,He.NEGATIVE_Z),e.pz.lookAt(He.POSITIVE_Z,He.NEGATIVE_Y),e.nz.lookAt(He.NEGATIVE_Z,He.NEGATIVE_Y),t._frameBuffer=new gi,t},{getCamera:function(t){return this._cameras[t]},render:function(t,e,r){var n=t.gl;r||e.update();for(var i=this.texture.width,a=2*Math.atan(i/(i-.5))/Math.PI*180,o=0;o<6;o++){var s=Ai[o],u=this._cameras[s];if(He.copy(u.position,this.position),u.far=this.far,u.near=this.near,u.fov=a,this.shadowMapPass){u.update();var l=e.getBoundingBox();l.applyTransform(u.viewMatrix),e.viewBoundingBoxLastFrame.copy(l),this.shadowMapPass.render(t,e,u,!0)}this._frameBuffer.attach(this.texture,n.COLOR_ATTACHMENT0,n.TEXTURE_CUBE_MAP_POSITIVE_X+o),this._frameBuffer.bind(t),t.render(e,u,!0),this._frameBuffer.unbind(t)}},dispose:function(t){this._frameBuffer.dispose(t)}});J.import("@export clay.skydome.vertex\n#define SHADER_NAME skydome\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 position : POSITION;\nvarying vec2 v_Texcoord;\nvoid main()\n{\n gl_Position = worldViewProjection * vec4(position, 1.0);\n v_Texcoord = texcoord;\n}\n@end\n@export clay.skydome.fragment\nuniform sampler2D environmentMap;\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\n@import clay.util.srgb\n@import clay.util.ACES\nvoid main()\n{\n vec4 texel = decodeHDR(texture2D(environmentMap, v_Texcoord));\n#ifdef SRGB_DECODE\n texel = sRGBToLinear(texel);\n#endif\n#ifdef TONEMAPPING\n texel.rgb = ACESToneMapping(texel.rgb);\n#endif\n#ifdef SRGB_ENCODE\n texel = linearTosRGB(texel);\n#endif\n gl_FragColor = encodeHDR(vec4(texel.rgb, 1.0));\n}\n@end");var wi=Gn.extend(function(){var t=new J(J.source("clay.skydome.vertex"),J.source("clay.skydome.fragment")),e=new dr({shader:t,depthMask:!1});return{scene:null,geometry:new Rn({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(),t.skydome=this,this.scene=t,t.on("beforerender",this._beforeRenderScene,this)},detachScene:function(){this.scene&&(this.scene.off("beforerender",this._beforeRenderScene),this.scene.skydome=null),this.scene=null},_beforeRenderScene:function(t,e,r){this.position.copy(r.getWorldPosition()),this.update(),t.renderPass([this],r)},setEnvironmentMap:function(t){this.material.set("environmentMap",t)},getEnvironmentMap:function(){return this.material.get("environmentMap")},dispose:function(t){this.detachScene(),this.geometry.dispose(t)}}),Ci=vt("DXT1"),Mi=vt("DXT3"),Ri=vt("DXT5"),Li={parse:function(t,e){var r=new Int32Array(t,0,31);if(542327876!==r[0])return null;if(4&!r(20))return null;var n,i,a=r(21),o=r[4],s=r[3],u=512&r[28],l=131072&r[2];switch(a){case Ci:n=8,i=ir.COMPRESSED_RGB_S3TC_DXT1_EXT;break;case Mi:n=16,i=ir.COMPRESSED_RGBA_S3TC_DXT3_EXT;break;case Ri:n=16,i=ir.COMPRESSED_RGBA_S3TC_DXT5_EXT;break;default:return null}var c=r[1]+4,h=u?6:1,f=1;l&&(f=Math.max(1,r[7]));for(var d=[],m=0;m=i)){a+=2;for(var o="";a20)return console.warn("Given image is not a height map"),t}var f,d,m,p;u%(4*n)==0?(f=o.data[u],m=o.data[u+4]):u%(4*n)==4*(n-1)?(f=o.data[u-4],m=o.data[u]):(f=o.data[u-4],m=o.data[u+4]),u<4*n?(d=o.data[u],p=o.data[u+4*n]):u>n*(i-1)*4?(d=o.data[u-4*n],p=o.data[u]):(d=o.data[u-4*n],p=o.data[u+4*n]),s.data[u]=f-m+127,s.data[u+1]=d-p+127,s.data[u+2]=255,s.data[u+3]=255}return a.putImageData(s,0,0),r},isHeightImage:function(t,e,r){if(!t||!t.width||!t.height)return!1;var n=document.createElement("canvas"),i=n.getContext("2d"),a=e||32;r=r||20,n.width=n.height=a,i.drawImage(t,0,0,a,a);for(var o=i.getImageData(0,0,a,a),s=0;sr)return!1}return!0},_fetchTexture:function(t,e,r){zn.get({url:t,responseType:"arraybuffer",onload:e,onerror:r})},createChessboard:function(t,e,r,n){t=t||512,e=e||64,r=r||"black",n=n||"white";var i=Math.ceil(t/e),a=document.createElement("canvas");a.width=t,a.height=t;var o=a.getContext("2d");o.fillStyle=n,o.fillRect(0,0,t,t),o.fillStyle=r;for(var s=0;s 0.999 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);\n vec3 tangentX = normalize(cross(N, upVector));\n vec3 tangentZ = cross(N, tangentX);\n return normalize(tangentX * H.x + N * H.y + tangentZ * H.z);\n}\nvoid main() {\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = normalize(v_WorldPosition - eyePos);\n vec3 N = V;\n vec3 prefilteredColor = vec3(0.0);\n float totalWeight = 0.0;\n float fMaxSampleNumber = float(maxSampleNumber);\n for (int i = 0; i < SAMPLE_NUMBER; i++) {\n if (i > maxSampleNumber) {\n break;\n }\n vec3 H = importanceSampleNormal(float(i) / fMaxSampleNumber, roughness, N);\n vec3 L = reflect(-V, H);\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 gl_FragColor = encodeHDR(vec4(prefilteredColor / totalWeight, 1.0));\n}\n"})});h.set("normalDistribution",n),r.encodeRGBM&&h.define("fragment","RGBM_ENCODE"),r.decodeRGBM&&h.define("fragment","RGBM_DECODE");var f,d=new yn;if("texture2D"===e.textureType){var m=new Bn({width:a,height:o,type:s===ir.FLOAT?ir.HALF_FLOAT:s});Oi.panoramaToCubeMap(t,e,m,{encodeRGBM:r.decodeRGBM}),e=m}f=new bi({scene:d,material:h}),f.material.set("environmentMap",e);var p=new Si({texture:u});r.encodeRGBM&&(s=u.type=ir.UNSIGNED_BYTE);for(var _=new In({width:a,height:o,type:s}),v=new gi({depthBuffer:!1}),g=Fe[s===ir.UNSIGNED_BYTE?"Uint8Array":"Float32Array"],y=0;y=32&&(T/=4),f.material.set("maxSampleNumber",Math.min(T,1024));for(var x=_.width,E=2*Math.atan(x/(x-.5))/Math.PI*180,b=0;b 0.999 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);\n vec3 tangentX = normalize(cross(N, upVector));\n vec3 tangentZ = cross(N, tangentX);\n return normalize(tangentX * H.x + N * H.y + tangentZ * H.z);\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}\nvoid main() {\n vec2 uv = gl_FragCoord.xy / viewportSize;\n float NoV = uv.x;\n float roughness = uv.y;\n vec3 V;\n V.x = sqrt(1.0 - NoV * NoV);\n V.y = 0.0;\n V.z = NoV;\n float A = 0.0;\n float B = 0.0;\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 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 gl_FragColor = vec4(vec2(A, B) / fSampleNumber, 0.0, 1.0);\n}\n"}),i=new In({width:512,height:256,type:ir.HALF_FLOAT,minFilter:ir.NEAREST,magFilter:ir.NEAREST,useMipmap:!1});return n.setUniform("normalDistribution",e),n.setUniform("viewportSize",[512,256]),n.attachOutput(i),n.render(t,r),r.dispose(t),i},Bi.generateNormalDistribution=function(t,e){for(var t=t||256,e=e||1024,r=new In({width:t,height:e,type:ir.FLOAT,minFilter:ir.NEAREST,magFilter:ir.NEAREST,wrapS:ir.CLAMP_TO_EDGE,wrapT:ir.CLAMP_TO_EDGE,useMipmap:!1}),n=new Float32Array(e*t*4),i=[],a=0;a>>16)>>>0;l=((1431655765&l)<<1|(2863311530&l)>>>1)>>>0,l=((858993459&l)<<2|(3435973836&l)>>>2)>>>0,l=((252645135&l)<<4|(4042322160&l)>>>4)>>>0,l=(((16711935&l)<<8|(4278255360&l)>>>8)>>>0)/4294967296;var c=Math.sqrt((1-l)/(1+(s*s-1)*l));i[u]=c}for(var u=0;u= 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#else\nvoid computeShadowOfDirectionalLights(vec3 position, inout float shadowContribs[DIRECTIONAL_LIGHT_COUNT]){\n float shadowContrib;\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#endif\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\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#endif\n#endif\n@end");var Xi=we.extend(function(){return{softShadow:Xi.PCF,shadowBlur:1,lightFrustumBias:"auto",kernelPCF:new Float32Array([1,0,1,1,-1,1,0,1,-1,0,-1,-1,1,-1,0,-1]),precision:"highp",_lastRenderNotCastShadow:!1,_frameBuffer:new gi,_textures:{},_shadowMapNumber:{POINT_LIGHT:0,DIRECTIONAL_LIGHT:0,SPOT_LIGHT:0},_depthMaterials:{},_distanceMaterials:{},_receivers:[],_lightsCastShadow:[],_lightCameras:{},_lightMaterials:{},_texturePool:new Hi}},function(){this._gaussianPassH=new Ei({fragment:J.source("clay.compositor.gaussian_blur")}),this._gaussianPassV=new Ei({fragment:J.source("clay.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 Ei({fragment:J.source("clay.sm.debug_depth")})},{render:function(t,e,r,n){r||(r=e.getMainCamera()),this.trigger("beforerender",this,t,e,r),this._renderShadowPass(t,e,r,n),this.trigger("afterrender",this,t,e,r)},renderDebug:function(t,e){t.saveClear();var r=t.viewport,n=0,i=e||r.width/4,a=i;this.softShadow===Xi.VSM?this._outputDepthPass.material.define("fragment","USE_VSM"):this._outputDepthPass.material.undefine("fragment","USE_VSM");for(var o in this._textures){var s=this._textures[o];t.setViewport(n,0,i*s.width/s.height,a),this._outputDepthPass.setUniform("depthMap",s),this._outputDepthPass.render(t),n+=i*s.width/s.height}t.setViewport(r),t.restoreClear()},_updateReceivers:function(t,e){if(e.receiveShadow?(this._receivers.push(e),e.material.set("shadowEnabled",1),e.material.set("pcfKernel",this.kernelPCF)):e.material.set("shadowEnabled",0),this.softShadow===Xi.VSM)e.material.define("fragment","USE_VSM"),e.material.undefine("fragment","PCF_KERNEL_SIZE");else{e.material.undefine("fragment","USE_VSM");var r=this.kernelPCF;r&&r.length?e.material.define("fragment","PCF_KERNEL_SIZE",r.length/2):e.material.undefine("fragment","PCF_KERNEL_SIZE")}},_update:function(t,e){var r=this;e.traverse(function(e){e.isRenderable()&&r._updateReceivers(t,e)});for(var n=0;n4){console.warn("Support at most 4 cascade");continue}p.shadowCascade>1&&(s=p),this.renderDirectionalLightShadow(t,e,r,p,f,h,c)}else"SPOT_LIGHT"===p.type?this.renderSpotLightShadow(t,e,p,l,u):"POINT_LIGHT"===p.type&&this.renderPointLightShadow(t,e,p,d);this._shadowMapNumber[p.type]++}for(var _ in this._shadowMapNumber)for(var v=this._shadowMapNumber[_],g=_+"_SHADOWMAP_COUNT",m=0;m0?T.define("fragment",g,v):T.isDefined("fragment",g)&&T.undefine("fragment",g))}for(var m=0;m0){var E=c.map(i);if(x.directionalLightShadowMaps={value:c,type:"tv"},x.directionalLightMatrices={value:h,type:"m4v"},x.directionalLightShadowMapSizes={value:E,type:"1fv"},s){var b=f.slice(),A=f.slice();b.pop(),A.shift(),b.reverse(),A.reverse(),h.reverse(),x.shadowCascadeClipsNear={value:b,type:"1fv"},x.shadowCascadeClipsFar={value:A,type:"1fv"}}}if(u.length>0){var S=u.map(i),x=e.shadowUniforms;x.spotLightShadowMaps={value:u,type:"tv"},x.spotLightMatrices={value:l,type:"m4v"},x.spotLightShadowMapSizes={value:S,type:"1fv"}}d.length>0&&(x.pointLightShadowMaps={value:d,type:"tv"})}},renderDirectionalLightShadow:function(){var t=new ln,e=new tr,r=new Ke,n=new tr,i=new tr,a=new tr,o=new tr;return function(s,u,l,c,h,f,d){var m=this._getDepthMaterial(c),p={getMaterial:function(t){return t.shadowDepthMaterial||m},ifRender:function(t){return t.castShadow},sortCompare:Vr.opaqueSortCompare};if(!u.viewBoundingBoxLastFrame.isFinite()){var _=u.getBoundingBox();u.viewBoundingBoxLastFrame.copy(_).applyTransform(l.viewMatrix)}var v=Math.min(-u.viewBoundingBoxLastFrame.min.z,l.far),g=Math.max(-u.viewBoundingBoxLastFrame.max.z,l.near),y=this._getDirectionalLightCamera(c,u,l),T=a.array;o.copy(y.projectionMatrix),Wi.invert(i.array,y.worldTransform.array),Wi.multiply(i.array,i.array,l.worldTransform.array),Wi.multiply(T,o.array,i.array);for(var x=[],E=l instanceof Vn,b=(l.near+l.far)/(l.near-l.far),A=2*l.near*l.far/(l.near-l.far),S=0;S<=c.shadowCascade;S++){var w=g*Math.pow(v/g,S/c.shadowCascade),C=g+(v-g)*S/c.shadowCascade,M=w*c.cascadeSplitLogFactor+C*(1-c.cascadeSplitLogFactor);x.push(M),h.push(-(-M*b+A)/-M)}var R=this._getTexture(c,c.shadowCascade);d.push(R);var L=s.viewport,N=s.gl;this._frameBuffer.attach(R),this._frameBuffer.bind(s),N.clear(N.COLOR_BUFFER_BIT|N.DEPTH_BUFFER_BIT);for(var S=0;Sf?s>d?m[i>0?"px":"nx"]=!0:m[o>0?"pz":"nz"]=!0:f>d?m[a>0?"py":"ny"]=!0:m[o>0?"pz":"nz"]=!0}for(var r=0;r=0&&T[g]>1e-4&&(ji.transformMat4(b,y,_[x[g]]),ji.scaleAndAdd(E,E,b,T[g]));A.set(v,E)}}for(var v=0;v=0){var l="touchend"!==u?i.targetTouches[0]:i.changedTouches[0];"touchstart"===u?u="mousedown":"touchend"===u?u="mouseup":"touchmove"===u&&(u="mousemove"),a=l.clientX-s.left,o=l.clientY-s.top}else a=i.clientX-s.left,o=i.clientY-s.top;var c,h=t.pick(a,o);if("DOMMouseScroll"!==u&&"mousewheel"!==u||(c=i.wheelDelta?i.wheelDelta/120:-(i.detail||0)/3),h){if(h.target.silent)return;if("mousemove"===u){var f=h.target!==r;f&&r&&It(r,Pt("mouseout",{target:r},a,o)),It(h.target,Pt("mousemove",h,a,o)),f&&It(h.target,Pt("mouseover",h,a,o))}else It(h.target,Pt(u,h,a,o,c));r=h.target}else r&&(It(r,Pt("mouseout",{target:r},a,o)),r=null)}})},this)},Mt.prototype._updateGraphicOptions=function(t,e,r){for(var n,i=!!t.tonemapping,a=!!t.linear,o=0;o0){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])}}}),aa=we.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&&u<=e.COLOR_ATTACHMENT0+8&&c.push(u);l.drawBuffersEXT(c)}t.saveClear(),t.clearBit=Oe.DEPTH_BUFFER_BIT|Oe.COLOR_BUFFER_BIT,r=t.render(this.scene,this.camera,!this.autoUpdateScene,this.preZ),t.restoreClear(),n.unbind(t)}else r=t.render(this.scene,this.camera,!this.autoUpdateScene,this.preZ);this.trigger("afterrender",r),this._rendering=!1,this._rendered=!0}}),ua=ia.extend(function(){return{texture:null,outputs:{color:{}}}},function(){},{getOutput:function(t,e){return this.texture},beforeFrame:function(){},afterFrame:function(){}}),la=ia.extend(function(){return{name:"",inputs:{},outputs:null,shader:"",inputLinks:{},outputLinks:{},pass:null,_prevOutputTextures:{},_outputTextures:{},_outputReferences:{},_rendering:!1,_rendered:!1,_compositor:null}},function(){var t=new Ei({fragment:this.shader});this.pass=t},{render:function(t,e){this.trigger("beforerender",t),this._rendering=!0;var r=t.gl;for(var n in this.inputLinks){var i=this.inputLinks[n],a=i.node.getOutput(t,i.pin);this.pass.setUniform(n,a)}if(this.outputs){this.pass.outputs={};var o={};for(var s in this.outputs){var u=this.updateParameter(s,t);isNaN(u.width)&&this.updateParameter(s,t);var l=this.outputs[s],c=this._compositor.allocateTexture(u);this._outputTextures[s]=c;var h=l.attachment||r.COLOR_ATTACHMENT0;"string"==typeof h&&(h=r[h]),o[h]=c}this._compositor.getFrameBuffer().bind(t);for(var h in o)this._compositor.getFrameBuffer().attach(o[h],h);this.pass.render(t),this._compositor.getFrameBuffer().updateMipmap(t.gl)}else this.pass.outputs=null,this._compositor.getFrameBuffer().unbind(t),this.pass.render(t,e);for(var n in this.inputLinks){var i=this.inputLinks[n];i.node.removeReference(i.pin)}this._rendering=!1,this._rendered=!0,this.trigger("afterrender",t)},updateParameter:function(t,e){var r=this.outputs[t],n=r.parameters,i=r._parametersCopy;if(i||(i=r._parametersCopy={}),n)for(var a in n)"width"!==a&&"height"!==a&&(i[a]=n[a]);var o,s;return o=n.width instanceof Function?n.width.call(this,e):n.width,s=n.height instanceof Function?n.height.call(this,e):n.height,i.width===o&&i.height===s||this._outputTextures[t]&&this._outputTextures[t].dispose(e),i.width=o,i.height=s,i},setParameter:function(t,e){this.pass.setUniform(t,e)},getParameter:function(t){return this.pass.getUniform(t)},setParameters:function(t){for(var e in t)this.setParameter(e,t[e])},define:function(t,e){this.pass.material.define("fragment",t,e)},undefine:function(t){this.pass.material.undefine("fragment",t)},removeReference:function(t){if(0===--this._outputReferences[t]){this.outputs[t].keepLastFrame?(this._prevOutputTextures[t]&&this._compositor.releaseTexture(this._prevOutputTextures[t]),this._prevOutputTextures[t]=this._outputTextures[t]):this._compositor.releaseTexture(this._outputTextures[t])}},clear:function(){ia.prototype.clear.call(this),this.pass.material.disableTexturesAll()}});J.import("@export clay.compositor.coloradjust\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\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;\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord);\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 gl_FragColor = vec4(color, tex.a);\n}\n@end\n@export clay.compositor.brightness\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\nuniform float brightness : 0.0;\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@export clay.compositor.contrast\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\nuniform float contrast : 1.0;\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@export clay.compositor.exposure\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\nuniform float exposure : 0.0;\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@export clay.compositor.gamma\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\nuniform float gamma : 1.0;\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@export clay.compositor.saturation\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\nuniform float saturation : 1.0;\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\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"),J.import("@export clay.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@export clay.compositor.kernel.gaussian_13\nfloat gaussianKernel[13];\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@end\n@export clay.compositor.gaussian_blur\n#define SHADER_NAME gaussian_blur\nuniform sampler2D texture;varying vec2 v_Texcoord;\nuniform float blurSize : 2.0;\nuniform vec2 textureSize : [512.0, 512.0];\nuniform float blurDir : 0.0;\n@import clay.util.rgbm\n@import clay.util.clamp_sample\nvoid main (void)\n{\n @import clay.compositor.kernel.gaussian_9\n vec2 off = blurSize / textureSize;\n off *= vec2(1.0 - blurDir, blurDir);\n vec4 sum = vec4(0.0);\n float weightAll = 0.0;\n for (int i = 0; i < 9; i++) {\n float w = gaussianKernel[i];\n vec4 texel = decodeHDR(clampSample(texture, v_Texcoord + float(i - 4) * off));\n sum += texel * w;\n weightAll += w;\n }\n gl_FragColor = encodeHDR(sum / max(weightAll, 0.01));\n}\n@end\n"),J.import("@export clay.compositor.hdr.log_lum\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n@import clay.util.rgbm\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 gl_FragColor = encodeHDR(vec4(vec3(luminance), 1.0));\n}\n@end\n@export clay.compositor.hdr.lum_adaption\nvarying vec2 v_Texcoord;\nuniform sampler2D adaptedLum;\nuniform sampler2D currentLum;\nuniform float frameTime : 0.02;\n@import clay.util.rgbm\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 fAdaptedLum += (fCurrentLum - fAdaptedLum) * (1.0 - pow(0.98, 30.0 * frameTime));\n gl_FragColor = encodeHDR(vec4(vec3(fAdaptedLum), 1.0));\n}\n@end\n@export clay.compositor.lum\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord );\n float luminance = dot(tex.rgb, w);\n gl_FragColor = vec4(vec3(luminance), 1.0);\n}\n@end"),J.import("\n@export clay.compositor.lut\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\nuniform sampler2D lookup;\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n float blueColor = tex.b * 63.0;\n vec2 quad1;\n quad1.y = floor(floor(blueColor) / 8.0);\n quad1.x = floor(blueColor) - (quad1.y * 8.0);\n vec2 quad2;\n quad2.y = floor(ceil(blueColor) / 8.0);\n quad2.x = ceil(blueColor) - (quad2.y * 8.0);\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 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 vec4 newColor1 = texture2D(lookup, texPos1);\n vec4 newColor2 = texture2D(lookup, texPos2);\n vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n gl_FragColor = vec4(newColor.rgb, tex.w);\n}\n@end"), -J.import("@export clay.compositor.vignette\n#define OUTPUT_ALPHA\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\nuniform float darkness: 1;\nuniform float offset: 1;\n@import clay.util.rgbm\nvoid main()\n{\n vec4 texel = decodeHDR(texture2D(texture, v_Texcoord));\n gl_FragColor.rgb = texel.rgb;\n vec2 uv = (v_Texcoord - vec2(0.5)) * vec2(offset);\n gl_FragColor = encodeHDR(vec4(mix(texel.rgb, vec3(1.0 - darkness), dot(uv, uv)), texel.a));\n}\n@end"),J.import("@export clay.compositor.output\n#define OUTPUT_ALPHA\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n@import clay.util.rgbm\nvoid main()\n{\n vec4 tex = decodeHDR(texture2D(texture, v_Texcoord));\n gl_FragColor.rgb = tex.rgb;\n#ifdef OUTPUT_ALPHA\n gl_FragColor.a = tex.a;\n#else\n gl_FragColor.a = 1.0;\n#endif\n gl_FragColor = encodeHDR(gl_FragColor);\n#ifdef PREMULTIPLY_ALPHA\n gl_FragColor.rgb *= gl_FragColor.a;\n#endif\n}\n@end"),J.import("@export clay.compositor.bright\nuniform sampler2D texture;\nuniform float threshold : 1;\nuniform float scale : 1.0;\nuniform vec2 textureSize: [512, 512];\nvarying vec2 v_Texcoord;\nconst vec3 lumWeight = vec3(0.2125, 0.7154, 0.0721);\n@import clay.util.rgbm\nvec4 median(vec4 a, vec4 b, vec4 c)\n{\n return a + b + c - min(min(a, b), c) - max(max(a, b), c);\n}\nvoid main()\n{\n vec4 texel = decodeHDR(texture2D(texture, v_Texcoord));\n#ifdef ANTI_FLICKER\n vec3 d = 1.0 / textureSize.xyx * vec3(1.0, 1.0, 0.0);\n vec4 s1 = decodeHDR(texture2D(texture, v_Texcoord - d.xz));\n vec4 s2 = decodeHDR(texture2D(texture, v_Texcoord + d.xz));\n vec4 s3 = decodeHDR(texture2D(texture, v_Texcoord - d.zy));\n vec4 s4 = decodeHDR(texture2D(texture, v_Texcoord + d.zy));\n texel = median(median(texel, s1, s2), s3, s4);\n#endif\n float lum = dot(texel.rgb , lumWeight);\n vec4 color;\n if (lum > threshold && texel.a > 0.0)\n {\n color = vec4(texel.rgb * scale, texel.a * scale);\n }\n else\n {\n color = vec4(0.0);\n }\n gl_FragColor = encodeHDR(color);\n}\n@end\n"),J.import("@export clay.compositor.downsample\nuniform sampler2D texture;\nuniform vec2 textureSize : [512, 512];\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\nfloat brightness(vec3 c)\n{\n return max(max(c.r, c.g), c.b);\n}\n@import clay.util.clamp_sample\nvoid main()\n{\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\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 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 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 gl_FragColor = encodeHDR(color);\n}\n@end"),J.import("\n@export clay.compositor.upsample\n#define HIGH_QUALITY\nuniform sampler2D texture;\nuniform vec2 textureSize : [512, 512];\nuniform float sampleScale: 0.5;\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\n@import clay.util.clamp_sample\nvoid main()\n{\n#ifdef HIGH_QUALITY\n vec4 d = vec4(1.0, 1.0, -1.0, 0.0) / textureSize.xyxy * sampleScale;\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 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 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 gl_FragColor = encodeHDR(s / 16.0);\n#else\n vec4 d = vec4(-1.0, -1.0, +1.0, +1.0) / textureSize.xyxy;\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 gl_FragColor = encodeHDR(s / 4.0);\n#endif\n}\n@end"),J.import("@export clay.compositor.hdr.composite\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#ifdef LUM_ENABLED\nuniform sampler2D lum;\n#endif\n#ifdef LUT_ENABLED\nuniform sampler2D lut;\n#endif\n#ifdef COLOR_CORRECTION\nuniform float brightness : 0.0;\nuniform float contrast : 1.0;\nuniform float saturation : 1.0;\n#endif\n#ifdef VIGNETTE\nuniform float vignetteDarkness: 1.0;\nuniform float vignetteOffset: 1.0;\n#endif\nuniform float exposure : 1.0;\nuniform float bloomIntensity : 0.25;\nuniform float lensflareIntensity : 1;\nvarying vec2 v_Texcoord;\n@import clay.util.srgb\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}\nfloat eyeAdaption(float fLum)\n{\n return mix(0.2, fLum, 0.5);\n}\n#ifdef LUT_ENABLED\nvec3 lutTransform(vec3 color) {\n float blueColor = color.b * 63.0;\n vec2 quad1;\n quad1.y = floor(floor(blueColor) / 8.0);\n quad1.x = floor(blueColor) - (quad1.y * 8.0);\n vec2 quad2;\n quad2.y = floor(ceil(blueColor) / 8.0);\n quad2.x = ceil(blueColor) - (quad2.y * 8.0);\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 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 vec4 newColor1 = texture2D(lut, texPos1);\n vec4 newColor2 = texture2D(lut, texPos2);\n vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n return newColor.rgb;\n}\n#endif\n@import clay.util.rgbm\nvoid main()\n{\n vec4 texel = vec4(0.0);\n vec4 originalTexel = vec4(0.0);\n#ifdef TEXTURE_ENABLED\n texel = decodeHDR(texture2D(texture, v_Texcoord));\n originalTexel = texel;\n#endif\n#ifdef BLOOM_ENABLED\n vec4 bloomTexel = decodeHDR(texture2D(bloom, v_Texcoord));\n texel.rgb += bloomTexel.rgb * bloomIntensity;\n texel.a += bloomTexel.a * bloomIntensity;\n#endif\n#ifdef LENSFLARE_ENABLED\n texel += decodeHDR(texture2D(lensflare, v_Texcoord)) * texture2D(lensdirt, v_Texcoord) * lensflareIntensity;\n#endif\n texel.a = min(texel.a, 1.0);\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 texel.rgb = ACESToneMapping(texel.rgb);\n texel = linearTosRGB(texel);\n#ifdef LUT_ENABLED\n texel.rgb = lutTransform(clamp(texel.rgb,vec3(0.0),vec3(1.0)));\n#endif\n#ifdef COLOR_CORRECTION\n texel.rgb = clamp(texel.rgb + vec3(brightness), 0.0, 1.0);\n texel.rgb = clamp((texel.rgb - vec3(0.5))*contrast+vec3(0.5), 0.0, 1.0);\n float lum = dot(texel.rgb, vec3(0.2125, 0.7154, 0.0721));\n texel.rgb = mix(vec3(lum), texel.rgb, saturation);\n#endif\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 gl_FragColor = encodeHDR(texel);\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)) * bloomIntensity);\n #elif DEBUG == 3\n gl_FragColor = encodeHDR(decodeHDR(texture2D(lensflare, v_Texcoord) * lensflareIntensity));\n #endif\n#endif\n if (originalTexel.a <= 0.01 && gl_FragColor.a > 1e-5) {\n gl_FragColor.a = dot(gl_FragColor.rgb, vec3(0.2125, 0.7154, 0.0721));\n }\n#ifdef PREMULTIPLY_ALPHA\n gl_FragColor.rgb *= gl_FragColor.a;\n#endif\n}\n@end"),J.import("@export clay.compositor.dof.coc\nuniform sampler2D depth;\nuniform float zNear: 0.1;\nuniform float zFar: 2000;\nuniform float focalDist: 3;\nuniform float focalRange: 1;\nuniform float focalLength: 30;\nuniform float fstop: 2.8;\nvarying vec2 v_Texcoord;\n@import clay.util.encode_float\nvoid main()\n{\n float z = texture2D(depth, v_Texcoord).r * 2.0 - 1.0;\n float dist = 2.0 * zNear * zFar / (zFar + zNear - z * (zFar - zNear));\n float aperture = focalLength / fstop;\n float coc;\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 coc = abs(aperture * (focalLength * (dist - focalAdjusted)) / (dist * (focalAdjusted - focalLength)));\n coc = clamp(coc, 0.0, 0.4) / 0.4000001;\n if (dist < lower) {\n coc = -coc;\n }\n coc = coc * 0.5 + 0.5;\n }\n gl_FragColor = encodeFloat(coc);\n}\n@end\n@export clay.compositor.dof.premultiply\nuniform sampler2D texture;\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\n@import clay.util.decode_float\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@export clay.compositor.dof.min_coc\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform vec2 textureSize : [512.0, 512.0];\n@import clay.util.float\nvoid main()\n{\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\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 gl_FragColor = encodeFloat(fCoc);\n}\n@end\n@export clay.compositor.dof.max_coc\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform vec2 textureSize : [512.0, 512.0];\n@import clay.util.float\nvoid main()\n{\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\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 gl_FragColor = encodeFloat(fCoc);\n}\n@end\n@export clay.compositor.dof.coc_upsample\n#define HIGH_QUALITY\nuniform sampler2D coc;\nuniform vec2 textureSize : [512, 512];\nuniform float sampleScale: 0.5;\nvarying vec2 v_Texcoord;\n@import clay.util.float\nvoid main()\n{\n#ifdef HIGH_QUALITY\n vec4 d = vec4(1.0, 1.0, -1.0, 0.0) / textureSize.xyxy * sampleScale;\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 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 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 gl_FragColor = encodeFloat(s / 16.0);\n#else\n vec4 d = vec4(-1.0, -1.0, +1.0, +1.0) / textureSize.xyxy;\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 gl_FragColor = encodeFloat(s / 4.0);\n#endif\n}\n@end\n@export clay.compositor.dof.upsample\n#define HIGH_QUALITY\nuniform sampler2D coc;\nuniform sampler2D texture;\nuniform vec2 textureSize : [512, 512];\nuniform float sampleScale: 0.5;\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\n@import clay.util.decode_float\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}\nvoid main()\n{\n#ifdef HIGH_QUALITY\n vec4 d = vec4(1.0, 1.0, -1.0, 0.0) / textureSize.xyxy * sampleScale;\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 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 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 gl_FragColor = encodeHDR(color / w);\n#else\n vec4 d = vec4(-1.0, -1.0, +1.0, +1.0) / textureSize.xyxy;\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 gl_FragColor = encodeHDR(color / w);\n#endif\n}\n@end\n@export clay.compositor.dof.downsample\nuniform sampler2D texture;\nuniform sampler2D coc;\nuniform vec2 textureSize : [512, 512];\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\n@import clay.util.decode_float\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}\nvoid main()\n{\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\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 gl_FragColor = encodeHDR(color);\n}\n@end\n@export clay.compositor.dof.hexagonal_blur_frag\n@import clay.util.float\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 float weightSum = 0.0;\n float kernelWeight = 1.0 / float(KERNEL_SIZE);\n for (int i = 0; i < KERNEL_SIZE; i++) {\n vec2 coord = v_Texcoord + offset * float(i);\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 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@end\n@export clay.compositor.dof.hexagonal_blur_1\n#define KERNEL_SIZE 5\nuniform sampler2D texture;\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform float blurSize : 1.0;\nuniform vec2 textureSize : [512.0, 512.0];\n@import clay.util.rgbm\n@import clay.compositor.dof.hexagonal_blur_frag\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 gl_FragColor = doBlur(texture, vec2(0.0, offset.y));\n#if !defined(BLUR_COC)\n gl_FragColor = encodeHDR(gl_FragColor);\n#endif\n}\n@end\n@export clay.compositor.dof.hexagonal_blur_2\n#define KERNEL_SIZE 5\nuniform sampler2D texture;\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform float blurSize : 1.0;\nuniform vec2 textureSize : [512.0, 512.0];\n@import clay.util.rgbm\n@import clay.compositor.dof.hexagonal_blur_frag\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 offset.y /= 2.0;\n gl_FragColor = doBlur(texture, -offset);\n#if !defined(BLUR_COC)\n gl_FragColor = encodeHDR(gl_FragColor);\n#endif\n}\n@end\n@export clay.compositor.dof.hexagonal_blur_3\n#define KERNEL_SIZE 5\nuniform sampler2D texture1;\nuniform sampler2D texture2;\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform float blurSize : 1.0;\nuniform vec2 textureSize : [512.0, 512.0];\n@import clay.util.rgbm\n@import clay.compositor.dof.hexagonal_blur_frag\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 offset.y /= 2.0;\n vec2 vDownRight = vec2(offset.x, -offset.y);\n vec4 texel1 = doBlur(texture1, -offset);\n vec4 texel2 = doBlur(texture1, vDownRight);\n vec4 texel3 = doBlur(texture2, vDownRight);\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#else\n vec4 color = (texel1 + texel2 + texel3) / 3.0;\n gl_FragColor = encodeHDR(color);\n#endif\n}\n@end\n@export clay.compositor.dof.composite\n#define DEBUG 0\nuniform sampler2D original;\nuniform sampler2D blurred;\nuniform sampler2D nearfield;\nuniform sampler2D coc;\nuniform sampler2D nearcoc;\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\n@import clay.util.float\nvoid main()\n{\n vec4 blurredColor = decodeHDR(texture2D(blurred, v_Texcoord));\n vec4 originalColor = decodeHDR(texture2D(original, v_Texcoord));\n float fCoc = decodeFloat(texture2D(coc, v_Texcoord));\n fCoc = abs(fCoc * 2.0 - 1.0);\n float weight = smoothstep(0.0, 1.0, fCoc);\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 gl_FragColor = encodeHDR(\n mix(\n nearfieldColor, mix(originalColor, blurredColor, weight),\n pow(1.0 - fNearCoc, 4.0)\n )\n );\n#else\n gl_FragColor = encodeHDR(mix(originalColor, blurredColor, weight));\n#endif\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@end"),J.import("@export clay.compositor.lensflare\n#define SAMPLE_NUMBER 8\nuniform sampler2D texture;\nuniform sampler2D lenscolor;\nuniform vec2 textureSize : [512, 512];\nuniform float dispersal : 0.3;\nuniform float haloWidth : 0.4;\nuniform float distortion : 1.0;\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\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}\nvoid main()\n{\n vec2 texcoord = -v_Texcoord + vec2(1.0); vec2 textureOffset = 1.0 / textureSize;\n vec2 ghostVec = (vec2(0.5) - texcoord) * dispersal;\n vec2 haloVec = normalize(ghostVec) * haloWidth;\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 float weight = length(vec2(0.5) - offset) / length(vec2(0.5));\n weight = pow(1.0 - weight, 10.0);\n result += textureDistorted(offset, normalize(ghostVec), distortion) * weight;\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 gl_FragColor = result;\n}\n@end"),J.import("@export clay.compositor.blend\n#define SHADER_NAME 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\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\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 gl_FragColor = encodeHDR(tex);\n}\n@end"),J.import("@export clay.compositor.fxaa\nuniform sampler2D texture;\nuniform vec4 viewport : VIEWPORT;\nvarying vec2 v_Texcoord;\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@import clay.util.rgbm\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 vec3 luma = vec3( 0.299, 0.587, 0.114 );\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 vec2 dir;\n dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n float dirReduce = max( ( lumaNW + lumaNE + lumaSW + lumaSE ) * ( 0.25 * FXAA_REDUCE_MUL ), FXAA_REDUCE_MIN );\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 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 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 float lumaB = dot( rgbB, luma );\n if ( ( lumaB < lumaMin ) || ( lumaB > lumaMax ) )\n {\n gl_FragColor = vec4( rgbA, opacity );\n }\n else {\n gl_FragColor = vec4( rgbB, opacity );\n }\n}\n@end");var ca=/^#source\((.*?)\)/;J.import("@export clay.deferred.gbuffer.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\nuniform vec2 uvRepeat;\nuniform vec2 uvOffset;\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n#ifdef FIRST_PASS\nattribute vec3 normal : NORMAL;\n#endif\n@import clay.chunk.skinning_header\n#ifdef FIRST_PASS\nvarying vec3 v_Normal;\nattribute vec4 tangent : TANGENT;\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\nvarying vec3 v_WorldPosition;\n#endif\nvarying vec2 v_Texcoord;\nvoid main()\n{\n vec3 skinnedPosition = position;\n#ifdef FIRST_PASS\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n bool hasTangent = dot(tangent, tangent) > 0.0;\n#endif\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\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 gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n#ifdef FIRST_PASS\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\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 v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n#endif\n}\n@end\n@export clay.deferred.gbuffer1.fragment\nuniform mat4 viewInverse : VIEWINVERSE;\nuniform float glossiness;\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nuniform sampler2D normalMap;\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\nuniform sampler2D roughGlossMap;\nuniform bool useRoughGlossMap;\nuniform bool useRoughness;\nuniform bool doubleSided;\nuniform int roughGlossChannel: 0;\nfloat indexingTexel(in vec4 texel, in int idx) {\n if (idx == 3) return texel.a;\n else if (idx == 1) return texel.g;\n else if (idx == 2) return texel.b;\n else return texel.r;\n}\nvoid main()\n{\n vec3 N = v_Normal;\n if (doubleSided) {\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = eyePos - v_WorldPosition;\n if (dot(N, V) < 0.0) {\n N = -N;\n }\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 gl_FragColor.rgb = (N + 1.0) * 0.5;\n float g = glossiness;\n if (useRoughGlossMap) {\n float g2 = indexingTexel(texture2D(roughGlossMap, v_Texcoord), roughGlossChannel);\n if (useRoughness) {\n g2 = 1.0 - g2;\n }\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\n }\n gl_FragColor.a = g + 0.005;\n}\n@end\n@export clay.deferred.gbuffer2.fragment\nuniform sampler2D diffuseMap;\nuniform sampler2D metalnessMap;\nuniform vec3 color;\nuniform float metalness;\nuniform bool useMetalnessMap;\nuniform bool linear;\nvarying vec2 v_Texcoord;\n@import clay.util.srgb\nvoid main ()\n{\n float m = metalness;\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 gl_FragColor.rgb = texel.rgb * color;\n gl_FragColor.a = m + 0.005;\n}\n@end\n@export clay.deferred.gbuffer.debug\n@import clay.deferred.chunk.light_head\nuniform int debug: 0;\nvoid main ()\n{\n @import clay.deferred.chunk.gbuffer_read\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"),J.import("@export clay.deferred.chunk.light_head\nuniform sampler2D gBufferTexture1;\nuniform sampler2D gBufferTexture2;\nuniform sampler2D gBufferTexture3;\nuniform vec2 windowSize: WINDOW_SIZE;\nuniform vec4 viewport: VIEWPORT;\nuniform mat4 viewProjectionInv;\n#ifdef DEPTH_ENCODED\n@import clay.util.decode_float\n#endif\n@end\n@export clay.deferred.chunk.gbuffer_read\n vec2 uv = gl_FragCoord.xy / windowSize;\n vec2 uv2 = (gl_FragCoord.xy - viewport.xy) / viewport.zw;\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 float glossiness = texel1.a;\n float metalness = texel3.a;\n vec3 N = texel1.rgb * 2.0 - 1.0;\n float z = texture2D(gBufferTexture2, uv).r * 2.0 - 1.0;\n vec2 xy = uv2 * 2.0 - 1.0;\n vec4 projectedPos = vec4(xy, z, 1.0);\n vec4 p4 = viewProjectionInv * projectedPos;\n vec3 position = p4.xyz / p4.w;\n vec3 albedo = texel3.rgb;\n vec3 diffuseColor = albedo * (1.0 - metalness);\n vec3 specularColor = mix(vec3(0.04), albedo, metalness);\n@end\n@export clay.deferred.chunk.light_equation\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}\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}\nvec3 F_Schlick(in float ndv, vec3 spec) {\n return spec + (1.0 - spec) * pow(1.0 - ndv, 5.0);\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@end");var ha=we.extend(function(){return{enableTargetTexture1:!0,enableTargetTexture2:!0,enableTargetTexture3:!0,renderTransparent:!1,_gBufferRenderList:[],_gBufferTex1:new In({minFilter:ir.NEAREST,magFilter:ir.NEAREST,type:ir.HALF_FLOAT}),_gBufferTex2:new In({minFilter:ir.NEAREST,magFilter:ir.NEAREST,format:ir.DEPTH_STENCIL,type:ir.UNSIGNED_INT_24_8_WEBGL}),_gBufferTex3:new In({minFilter:ir.NEAREST,magFilter:ir.NEAREST}),_defaultNormalMap:new In({image:jt("#000")}),_defaultRoughnessMap:new In({image:jt("#fff")}),_defaultMetalnessMap:new In({image:jt("#fff")}),_defaultDiffuseMap:new In({image:jt("#fff")}),_frameBuffer:new gi,_gBufferMaterial1:new dr({shader:new J(J.source("clay.deferred.gbuffer.vertex"),J.source("clay.deferred.gbuffer1.fragment")),vertexDefines:{FIRST_PASS:null},fragmentDefines:{FIRST_PASS:null}}),_gBufferMaterial2:new dr({shader:new J(J.source("clay.deferred.gbuffer.vertex"),J.source("clay.deferred.gbuffer2.fragment"))}),_debugPass:new Ei({fragment:J.source("clay.deferred.gbuffer.debug")})}},{resize:function(t,e){this._gBufferTex1.width===t&&this._gBufferTex1.height===e||(this._gBufferTex1.width=t,this._gBufferTex1.height=e,this._gBufferTex2.width=t,this._gBufferTex2.height=e,this._gBufferTex3.width=t,this._gBufferTex3.height=e)},setViewport:function(t,e,r,n,i){var a;a="object"==typeof t?t:{x:t,y:e,width:r,height:n,devicePixelRatio:i||1},this._frameBuffer.viewport=a},getViewport:function(){return this._frameBuffer.viewport?this._frameBuffer.viewport:{x:0,y:0,width:this._gBufferTex1.width,height:this._gBufferTex1.height,devicePixelRatio:1}},update:function(t,e,r){for(var n=t.gl,i=this._frameBuffer,a=i.viewport,o=e.updateRenderList(r),s=o.opaque,u=o.transparent,l=0,c=this._gBufferRenderList,h=0;h= 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 gl_FragColor.rgb *= shadowContrib;\n#endif\n gl_FragColor.a = 1.0;\n}\n@end\n"),J.import("@export clay.deferred.ambient_light\nuniform sampler2D gBufferTexture1;\nuniform sampler2D gBufferTexture3;\nuniform vec3 lightColor;\nuniform vec2 windowSize: WINDOW_SIZE;\nvoid main()\n{\n vec2 uv = gl_FragCoord.xy / windowSize;\n vec4 texel1 = texture2D(gBufferTexture1, uv);\n if (dot(texel1.rgb, vec3(1.0)) == 0.0) {\n discard;\n }\n vec3 albedo = texture2D(gBufferTexture3, uv).rgb;\n gl_FragColor.rgb = lightColor * albedo;\n gl_FragColor.a = 1.0;\n}\n@end"),J.import("@export clay.deferred.ambient_sh_light\nuniform sampler2D gBufferTexture1;\nuniform sampler2D gBufferTexture3;\nuniform vec3 lightColor;\nuniform vec3 lightCoefficients[9];\nuniform vec2 windowSize: WINDOW_SIZE;\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}\nvoid main()\n{\n vec2 uv = gl_FragCoord.xy / windowSize;\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"),J.import("@export clay.deferred.ambient_cubemap_light\n@import clay.deferred.chunk.light_head\nuniform vec3 lightColor;\nuniform samplerCube lightCubemap;\nuniform sampler2D brdfLookup;\nuniform vec3 eyePosition;\n@import clay.util.rgbm\nvoid main()\n{\n @import clay.deferred.chunk.gbuffer_read\n vec3 V = normalize(eyePosition - position);\n vec3 L = reflect(-V, 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 vec3 envTexel = RGBMDecode(textureCubeLodEXT(lightCubemap, L, bias), 8.12);\n gl_FragColor.rgb = lightColor * envTexel * envWeight;\n gl_FragColor.a = 1.0;\n}\n@end"),J.import("@export clay.deferred.point_light\n@import clay.deferred.chunk.light_head\n@import clay.util.calculate_attenuation\n@import clay.deferred.chunk.light_equation\nuniform vec3 lightPosition;\nuniform vec3 lightColor;\nuniform float lightRange;\nuniform vec3 eyePosition;\n#ifdef SHADOWMAP_ENABLED\nuniform samplerCube lightShadowMap;\nuniform float lightShadowMapSize;\n#endif\nvarying vec3 v_Position;\n@import clay.plugin.shadow_map_common\nvoid main()\n{\n @import clay.deferred.chunk.gbuffer_read\n vec3 L = lightPosition - position;\n vec3 V = normalize(eyePosition - position);\n float dist = length(L);\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 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#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 gl_FragColor.a = 1.0;\n}\n@end"),J.import("@export clay.deferred.sphere_light\n@import clay.deferred.chunk.light_head\n@import clay.util.calculate_attenuation\n@import clay.deferred.chunk.light_equation\nuniform vec3 lightPosition;\nuniform vec3 lightColor;\nuniform float lightRange;\nuniform float lightRadius;\nuniform vec3 eyePosition;\nvarying vec3 v_Position;\nvoid main()\n{\n @import clay.deferred.chunk.gbuffer_read\n vec3 L = lightPosition - position;\n vec3 V = normalize(eyePosition - position);\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 L = normalize(L);\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 float attenuation = lightAttenuation(dist, lightRange);\n gl_FragColor.rgb = lightColor * ndl * attenuation;\n glossiness = clamp(glossiness - lightRadius / 2.0 / dist, 0.0, 1.0);\n gl_FragColor.rgb = attenuation * lightEquation(\n lightColor, diffuseColor, specularColor, ndl, ndh, ndv, glossiness\n );\n gl_FragColor.a = 1.0;\n}\n@end"),J.import("@export clay.deferred.tube_light\n@import clay.deferred.chunk.light_head\n@import clay.util.calculate_attenuation\n@import clay.deferred.chunk.light_equation\nuniform vec3 lightPosition;\nuniform vec3 lightColor;\nuniform float lightRange;\nuniform vec3 lightExtend;\nuniform vec3 eyePosition;\nvarying vec3 v_Position;\nvoid main()\n{\n @import clay.deferred.chunk.gbuffer_read\n vec3 L = lightPosition - position;\n vec3 V = normalize(eyePosition - position);\n vec3 R = reflect(V, N);\n vec3 L0 = lightPosition - lightExtend - position;\n vec3 L1 = lightPosition + lightExtend - position;\n vec3 LD = L1 - L0;\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 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 vec3 H = normalize(L + V);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n float ndv = clamp(dot(N, V), 0.0, 1.0);\n glossiness = clamp(glossiness - 0.0 / 2.0 / dist, 0.0, 1.0);\n gl_FragColor.rgb = lightColor * irra * lightAttenuation(dist, lightRange)\n * (diffuseColor + D_Phong(glossiness, ndh) * F_Schlick(ndv, specularColor));\n gl_FragColor.a = 1.0;\n}\n@end"),J.import(Br);var _a=we.extend(function(){var t=J.source("clay.compositor.vertex"),e=J.source("clay.deferred.light_volume.vertex"),r=new J(t,J.source("clay.deferred.directional_light")),n=function(t){t.blendEquation(t.FUNC_ADD),t.blendFunc(t.ONE,t.ONE)},i=function(t){return new dr({shader:t,blend:n,transparent:!0,depthMask:!1})},a=function(t){return new J(e,J.source("clay.deferred."+t))},o=new ma({capSegments:10}),s=new tr;s.rotateX(Math.PI/2).translate(new He(0,-1,0)),o.applyTransform(s);var u=new pa({capSegments:10});return s.identity().rotateZ(Math.PI/2),u.applyTransform(s),{shadowMapPass:null,autoResize:!0,_createLightPassMat:i,_gBuffer:new ha,_lightAccumFrameBuffer:new gi({depthBuffer:!1}),_lightAccumTex:new In({type:ir.HALF_FLOAT,minFilter:ir.NEAREST,magFilter:ir.NEAREST}),_fullQuadPass:new Ei({blendWithPrevious:!0}),_directionalLightMat:i(r),_ambientMat:i(new J(t,J.source("clay.deferred.ambient_light"))),_ambientSHMat:i(new J(t,J.source("clay.deferred.ambient_sh_light"))),_ambientCubemapMat:i(new J(t,J.source("clay.deferred.ambient_cubemap_light"))),_spotLightShader:a("spot_light"),_pointLightShader:a("point_light"),_sphereLightShader:a("sphere_light"),_tubeLightShader:a("tube_light"),_lightSphereGeo:new Rn({widthSegments:10,heightSegements:10}),_lightConeGeo:o,_lightCylinderGeo:u,_outputPass:new Ei({fragment:J.source("clay.compositor.output")})}},{render:function(t,e,r,n){n=n||{},n.renderToTarget=n.renderToTarget||!1,n.notUpdateShadow=n.notUpdateShadow||!1,n.notUpdateScene=n.notUpdateScene||!1,n.notUpdateScene||e.update(!1,!0),e.updateLights(),r.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,r),this._accumulateLightBuffer(t,e,r,!n.notUpdateShadow),n.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,r,n,i){this._gBuffer.setViewport(t,e,r,n,i),this._lightAccumFrameBuffer.viewport=this._gBuffer.getViewport()},resize:function(t,e){this._lightAccumTex.width=t,this._lightAccumTex.height=e,this._gBuffer.resize(t,e)},_accumulateLightBuffer:function(t,e,r,n){for(var i=t.gl,a=this._lightAccumTex,o=this._lightAccumFrameBuffer,s=r.getWorldPosition().array,u=0;u0&&Ha.scaleAndAdd(t.array,t.array,this.force.array,n/r)}});J.import("@export clay.particle.vertex\nuniform mat4 worldView : WORLDVIEW;\nuniform mat4 projection : PROJECTION;\nattribute vec3 position : POSITION;\nattribute vec3 normal : NORMAL;\n#ifdef UV_ANIMATION\nattribute vec2 texcoord0 : TEXCOORD_0;\nattribute vec2 texcoord1 : TEXCOORD_1;\nvarying vec2 v_Uv0;\nvarying vec2 v_Uv1;\n#endif\nvarying float v_Age;\nvoid main() {\n v_Age = normal.x;\n float rotation = normal.y;\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 #ifdef UV_ANIMATION\n v_Uv0 = texcoord0;\n v_Uv1 = texcoord1;\n #endif\n}\n@end\n@export clay.particle.fragment\nuniform sampler2D sprite;\nuniform sampler2D gradient;\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\nvarying float v_Age;\n#ifdef UV_ANIMATION\nvarying vec2 v_Uv0;\nvarying vec2 v_Uv1;\n#endif\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@end");var Va=new J(J.source("clay.particle.vertex"),J.source("clay.particle.fragment")),Wa=Hn.extend({loop:!0,oneshot:!1,duration:1,spriteAnimationTileX:1,spriteAnimationTileY:1,spriteAnimationRepeat:0,mode:Hn.POINTS,ignorePicking:!0,_elapsedTime:0,_emitting:!0},function(){this.geometry=new Sn({dynamic:!0}),this.material||(this.material=new dr({shader:Va,transparent:!0,depthMask:!1}),this.material.enableTexture("sprite")),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[r]=e[n-1],e.pop(),n--):r++}for(var r=0;r0)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;e1&&n&&n.length>1){var a=Qt(n)/Qt(i);!isFinite(a)&&(a=1),e.pinchScale=a;var o=$t(n);return e.pinchX=o[0],e.pinchY=o[1],{type:"pinch",target:t[0].target,event:e}}}}},Ya=[[0,0],[0,1],[1,1],[1,0]],Ka=[0,1,2,2,3,0],Za=Gn.extend({camera:null,plane:null,maxGrid:0,frustumCulling:!1},function(){var t=this.geometry=new Sn({dynamic:!0});t.attributes.position.init(6),t.attributes.normal.init(6),t.attributes.texcoord0.init(6),t.indices=new Uint16Array(6),this.plane=new en},{updateGeometry:function(){var t=this._unProjectGrid();if(t){for(var e=this.geometry.attributes.position,r=this.geometry.attributes.normal,n=this.geometry.attributes.texcoord0,i=this.geometry.indices,a=0;a<6;a++){var o=Ka[a];e.set(a,t[o].array),r.set(a,this.plane.normal.array),n.set(a,Ya[o]),i[a]=a}this.geometry.dirty()}},_unProjectGrid:function(){for(var t=new en,e=[0,1,0,2,1,3,2,3,4,5,4,6,5,7,6,7,0,4,1,5,2,6,3,7],r=new He,n=new He,i=[],a=[],o=0;o<4;o++)a[o]=new He(0,0);var s=new hn;return function(){t.copy(this.plane),t.applyTransform(this.camera.viewMatrix);for(var o=this.camera.frustum.vertices,u=0,l=0;l<12;l++){r.array=o[e[2*l]],n.array=o[e[2*l+1]];var c=t.intersectLine(r,n,i[u]);c&&(i[u]||(i[u]=c),u++)}if(0!==u){for(var l=0;l0},update:function(t){if(t=t||16,this._rotating){var e=("cw"===this.autoRotateDirection?1:-1)*this.autoRotateSpeed/180*Math.PI;this._phi-=e*t/1e3,this._needsUpdate=!0}else this._rotateVelocity.len()>0&&(this._needsUpdate=!0);(Math.abs(this._zoomSpeed)>.01||this._panVelocity.len()>0)&&(this._needsUpdate=!0),this._needsUpdate&&(this._updateDistance(Math.min(t,50)),this._updatePan(Math.min(t,50)),this._updateRotate(Math.min(t,50)),this._updateTransform(),this.target.update(),this.trigger("update"),this._needsUpdate=!1)},_updateRotate:function(t){var e=this._rotateVelocity;this._phi=e.y*t/20+this._phi,this._theta=e.x*t/20+this._theta,this.setAlpha(this.getAlpha()),this.setBeta(this.getBeta()),this._vectorDamping(e,this.damping),e.x=e.y=0},_updateDistance:function(t){this._setDistance(this._distance+this._zoomSpeed*t/20),this._zoomSpeed*=this.damping},_setDistance:function(t){this._distance=Math.max(Math.min(t,this.maxDistance),this.minDistance)},_updatePan:function(t){var e=this._panVelocity,r=this._distance,n=this.target,i=n.worldTransform.y,a=n.worldTransform.x;this._center.scaleAndAdd(a,-e.x*r/200).scaleAndAdd(i,-e.y*r/200),this._vectorDamping(e,0),e.x=e.y=0},_updateTransform:function(){var t=this.target,e=new He,r=this._theta+Math.PI/2,n=this._phi+Math.PI/2,i=Math.sin(r);e.x=i*Math.cos(n),e.y=-Math.cos(r),e.z=i*Math.sin(n),t.position.copy(this._center).scaleAndAdd(e,this._distance),t.rotation.identity().rotateY(-this._phi).rotateX(-this._theta)},_startCountingStill:function(){clearTimeout(this._stillTimeout);var t=this.autoRotateAfterStill,e=this;!isNaN(t)&&t>0&&(this._stillTimeout=setTimeout(function(){e._rotating=!0},1e3*t))},_vectorDamping:function(t,e){var r=t.len();r*=e,r<1e-4&&(r=0),t.normalize().scale(r)},decomposeTransform:function(){if(this.target){this.target.updateWorldTransform();var t=this.target.worldTransform.z,e=Math.asin(t.y),r=Math.atan2(t.x,t.z);this._theta=e,this._phi=-r,this.setBeta(this.getBeta()),this.setAlpha(this.getAlpha()),this._setDistance(this.target.position.dist(this._center))}},_mouseDownHandler:function(t){if(!this._isAnimating()){var e=t.clientX,r=t.clientY;if(t.targetTouches){var n=t.targetTouches[0];e=n.clientX,r=n.clientY,this._mode="rotate",this._processGesture(t,"start")}var i=this.domElement;i.addEventListener("touchmove",this._mouseMoveHandler),i.addEventListener("touchend",this._mouseUpHandler),i.addEventListener("mousemove",this._mouseMoveHandler),i.addEventListener("mouseup",this._mouseUpHandler),i.addEventListener("mouseout",this._mouseUpHandler),0===t.button?this._mode="rotate":1===t.button?this._mode="pan":this._mode=null,this._rotateVelocity.set(0,0),this._rotating=!1,this.autoRotate&&this._startCountingStill(),this._mouseX=e,this._mouseY=r}},_mouseMoveHandler:function(t){if(!this._isAnimating()){var e,r=t.clientX,n=t.clientY;if(t.targetTouches){var i=t.targetTouches[0];r=i.clientX,n=i.clientY,e=this._processGesture(t,"change")}var a=te(this.panSensitivity),o=te(this.rotateSensitivity);e||("rotate"===this._mode?(this._rotateVelocity.y+=(r-this._mouseX)/this.domElement.clientWidth*2*o[0],this._rotateVelocity.x+=(n-this._mouseY)/this.domElement.clientHeight*2*o[1]):"pan"===this._mode&&(this._panVelocity.x+=(r-this._mouseX)/this.domElement.clientWidth*a[0]*400,this._panVelocity.y+=(-n+this._mouseY)/this.domElement.clientHeight*a[1]*400)),this._mouseX=r,this._mouseY=n,t.preventDefault()}},_mouseWheelHandler:function(t){if(!this._isAnimating()){var e=t.wheelDelta||-t.detail;0!==e&&this._zoomHandler(t,e>0?-1:1)}},_pinchHandler:function(t){this._isAnimating()||this._zoomHandler(t,t.pinchScale>1?-.4:.4)},_zoomHandler:function(t,e){var r=Math.max(Math.min(this._distance-this.minDistance,this.maxDistance-this._distance));this._zoomSpeed=e*Math.max(r/40*this.zoomSensitivity,.2),this._rotating=!1,this.autoRotate&&"rotate"===this._mode&&this._startCountingStill(),t.preventDefault()},_mouseUpHandler:function(t){var e=this.domElement;e.removeEventListener("touchmove",this._mouseMoveHandler),e.removeEventListener("touchend",this._mouseUpHandler),e.removeEventListener("mousemove",this._mouseMoveHandler),e.removeEventListener("mouseup",this._mouseUpHandler),e.removeEventListener("mouseout",this._mouseUpHandler),this._processGesture(t,"end")},_addAnimator:function(t){var e=this._animators;return e.push(t),t.done(function(){var r=e.indexOf(t);r>=0&&e.splice(r,1)}),t},_processGesture:function(t,e){var r=this._gestureMgr;"start"===e&&r.clear();var n=r.recognize(t,null,this.domElement);if("end"===e&&r.clear(),n){var i=n.type;t.gestureEvent=i,this._pinchHandler(n.event)}return n}});Object.defineProperty(Ja.prototype,"autoRotate",{get:function(){return this._autoRotate},set:function(t){this._autoRotate=t,this._rotating=t}}),Object.defineProperty(Ja.prototype,"target",{get:function(){return this._target},set:function(t){t&&t.target&&this.setCenter(t.target.toArray()),this._target=t,this.decomposeTransform()}});var Qa=Sn.extend({dynamic:!1}),$a=fe.mat4,to=fe.vec3,eo={merge:function(t,e){if(t.length){var r=t[0],n=r.geometry,i=r.material,a=new Sn({dynamic:!1});a.boundingBox=new Ke;for(var o=n.getEnabledAttributes(),s=0;s=65535?new Uint32Array(3*f):new Uint16Array(3*f);for(var _=0,v=0,g=n.isUseIndices(),y=0;y0;){for(var _=[],v=[],g=[],y=0,f=0;f=0&&-1===v[S]&&(y65535?new Uint32Array(3*L.triangles.length):new Uint16Array(3*L.triangles.length);var H=0;D=0;for(var f=0;f=0?N[S]:-1}D++}P.indices[H++]=M[b]}P.updateBoundingBox(),w.add(I)}for(var X=t.children(),f=0;f0&&(r.indices=re(t.indices,e),n.push(r.indices.buffer)),r.attributes={};for(var i in t.attributes)if(t.attributes.hasOwnProperty(i)){var a=t.attributes[i];a&&a.value&&a.value.length>0&&(a=r.attributes[i]=ee(a,e),n.push(a.value.buffer))}return{data:r,buffers:n}},toGeometry:function(t){if(!t)return null;if(t.data&&t.buffers)return no.toGeometry(t.data);if(!t.metadata||t.metadata.generator!==ro.generator)throw new Error("[util.transferable.toGeometry] the object is not generated by util.transferable.");var e={dynamic:t.dynamic,indices:t.indices};if(t.boundingBox){var r=(new He).setArray(t.boundingBox.min),n=(new He).setArray(t.boundingBox.max);e.boundingBox=new Ke(r,n)}var i=new Sn(e);for(var a in t.attributes)if(t.attributes.hasOwnProperty(a)){var o=t.attributes[a];i.attributes[a]=new Sn.Attribute(o.name,o.type,o.size,o.semantic),i.attributes[a].value=o.value}return i}} -;J.import("@export clay.vr.disorter.output.vertex\nattribute vec2 texcoord: TEXCOORD_0;\nattribute vec3 position: POSITION;\nvarying vec2 v_Texcoord;\nvoid main()\n{\n v_Texcoord = texcoord;\n gl_Position = vec4(position.xy, 0.5, 1.0);\n}\n@end\n@export clay.vr.disorter.output.fragment\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\nvoid main()\n{\n gl_FragColor = texture2D(texture, v_Texcoord);\n}\n@end");var io=we.extend(function(){return{clearColor:[0,0,0,1],_mesh:new Gn({geometry:new Sn({dynamic:!0}),culling:!1,material:new dr({depthTest:!1,shader:new J({vertex:J.source("clay.vr.disorter.output.vertex"),fragment:J.source("clay.vr.disorter.output.fragment")})})}),_fakeCamera:new Vn}},{render:function(t,e){var r=this.clearColor,n=t.gl;n.clearColor(r[0],r[1],r[2],r[3]),n.clear(n.COLOR_BUFFER_BIT),n.disable(n.BLEND),this._mesh.material.set("texture",e),t.saveViewport(),t.setViewport(0,0,t.getWidth(),t.getHeight()),t.renderPass([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,r){var n=this._mesh.geometry.attributes.position,i=this._mesh.geometry.attributes.texcoord0;n.init(2*t*e),i.init(2*t*e);for(var a=r.getLeftEyeVisibleTanAngles(),o=r.getLeftEyeNoLensTanAngles(),s=r.getLeftEyeVisibleScreenRect(o),u=0,l=[],c=[],h=0;h<2;h++){for(var f=0;fi)t.length=i;else for(var a=n;a=0&&!(T[w]<=e);w--);w=Math.min(w,g-2)}else{for(w=O;we);w++);w=Math.min(w-1,g-2)}O=w,B=e;var r=T[w+1]-T[w];0!==r&&(L=(e-T[w])/r,L=Math.max(Math.min(1,L),0),L=b[w+1](L),_?(P=E[w],N=E[0===w?w:w-1],I=E[w>g-2?g-1:w+1],D=E[w>g-3?g-1:w+2],f?m(t,s,f(p(t,s),N,P,I,D,L)):y?u(N,P,I,D,L,L*L,L*L*L,p(t,s),x):m(t,s,l(N,P,I,D,L,L*L,L*L*L))):f?m(t,s,f(p(t,s),E[w],E[w+1],L)):y?a(E[w],E[w+1],L,p(t,s),x):m(t,s,i(E[w],E[w+1],L)))},F=new ue({target:t._target,life:d,loop:t._loop,delay:t._delay,onframe:U,onfinish:r});return e&&"spline"!==e&&F.setEasing(e),F}}}function d(t,e,i,a,o){this._tracks={},this._target=t,this._loop=e||!1,this._getter=i||r,this._setter=a||n,this._interpolater=o||null,this._delay=0,this._doneList=[],this._onframeList=[],this._clipList=[],this._maxTime=0,this._lastKFTime=0}function p(t){return t}function m(t){var e,r,n,i,a,o,s=Number.POSITIVE_INFINITY,u=Number.POSITIVE_INFINITY,l=Number.NEGATIVE_INFINITY,c=Number.NEGATIVE_INFINITY;for(e=t.length;e--;)t[e][0]l&&(l=t[e][0]),t[e][1]c&&(c=t[e][1]);return r=l-s,n=c-u,i=Math.max(r,n),a=s+.5*r,o=u+.5*n,[[a-20*i,o-i],[a,o+20*i],[a+20*i,o-i]]}function _(t,e,r,n){var i,a,o,s,u,l,c,h,f,d,p=t[e][0],m=t[e][1],_=t[r][0],g=t[r][1],v=t[n][0],y=t[n][1],x=Math.abs(m-g),T=Math.abs(g-y);if(xT?o*(i-u)+c:s*(i-l)+h),f=_-i,d=g-a,{i:e,j:r,k:n,x:i,y:a,r:f*f+d*d}}function g(t){var e,r,n,i,a,o;for(r=t.length;r;)for(i=t[--r],n=t[--r],e=r;e;)if(o=t[--e],a=t[--e],n===a&&i===o||n===o&&i===a){t.splice(r,2),t.splice(e,2);break}}function v(t,e,r,n,i,a){var o=e[i],s=e[i+1],u=e[i+2];return t[0]=o+n*(r[a]-o),t[1]=s+n*(r[a+1]-s),t[2]=u+n*(r[a+2]-u),t}function y(t,e,r,n,i,a){var o,s,u,l,c,h=e[0+i],f=e[1+i],d=e[2+i],p=e[3+i],m=r[0+a],_=r[1+a],g=r[2+a],v=r[3+a];return s=h*m+f*_+d*g+p*v,s<0&&(s=-s,m=-m,_=-_,g=-g,v=-v),1-s>1e-6?(o=Math.acos(s),u=Math.sin(o),l=Math.sin((1-n)*o)/u,c=Math.sin(n*o)/u):(l=1-n,c=n),t[0]=l*h+c*m,t[1]=l*f+c*_,t[2]=l*d+c*g,t[3]=l*p+c*v,t}function x(t,e,r){"object"==typeof e&&(r=e,e=null);var n,i=this;if(!(t instanceof Function)){n=[];for(var a in t)t.hasOwnProperty(a)&&n.push(a)}var o=function(e){if(i.apply(this,arguments),t instanceof Function?T(this,t.call(this,e)):E(this,t,n),this.constructor===o)for(var r=o.__initializers__,a=0;ar?r:t}function w(t){return t=Math.round(t),t<0?0:t>255?255:t}function C(t){return t=Math.round(t),t<0?0:t>360?360:t}function M(t){return t<0?0:t>1?1:t}function R(t){return w(t.length&&"%"===t.charAt(t.length-1)?parseFloat(t)/100*255:parseInt(t,10))}function L(t){return M(t.length&&"%"===t.charAt(t.length-1)?parseFloat(t)/100:parseFloat(t))}function N(t,e,r){return r<0?r+=1:r>1&&(r-=1),6*r<1?t+(e-t)*r*6:2*r<1?e:3*r<2?t+(e-t)*(2/3-r)*6:t}function P(t,e,r){return t+(e-t)*r}function I(t,e,r,n,i){return t[0]=e,t[1]=r,t[2]=n,t[3]=i,t}function D(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t}function O(t,e){hr&&D(hr,e),hr=cr.put(t,hr||e.slice())}function B(t,e){var r=(parseFloat(t[0])%360+360)%360/360,n=L(t[1]),i=L(t[2]),a=i<=.5?i*(n+1):i+n-i*n,o=2*i-a;return e=e||[],I(e,w(255*N(o,a,r+1/3)),w(255*N(o,a,r)),w(255*N(o,a,r-1/3)),1),4===t.length&&(e[3]=t[3]),e}function U(t){if(t){var e,r,n=t[0]/255,i=t[1]/255,a=t[2]/255,o=Math.min(n,i,a),s=Math.max(n,i,a),u=s-o,l=(s+o)/2;if(0===u)e=0,r=0;else{r=l<.5?u/(s+o):u/(2-s-o);var c=((s-n)/6+u/2)/u,h=((s-i)/6+u/2)/u,f=((s-a)/6+u/2)/u;n===s?e=f-h:i===s?e=1/3+c-f:a===s&&(e=2/3+h-c),e<0&&(e+=1),e>1&&(e-=1)}var d=[360*e,r,l];return null!=t[3]&&d.push(t[3]),d}}function F(t){var e=Object.keys(t);e.sort();for(var r=[],n=0;n0&&n.push("#define "+i.toUpperCase()+"_COUNT "+a)}if(r)for(var o=0;o=0){if(1!==u&&4!==u){J();break}u=2,c=[]}else if(1!==u)if(4!==u)r(h),u=0;else{var f=h;Pr.indexOf(f)>=0||Ir.indexOf(f)>=0||Dr.indexOf(f)>=0?l[s].semantic=f:"ignore"===f||"unconfigurable"===f?l[s].ignore=!0:l[s].value="bool"===t?"true"===f:parseFloat(f)}else l[s].value="bool"===t?"true"===h:parseFloat(h),c=null;else{if(2!==u){J();break}if(!(c instanceof Array)){J();break}c.push(+i[++o])}else l[s].value=new Ge.Float32Array(c),c=null,u=5;else if(2===u){if(!(c instanceof Array)){J();break}c.push(+i[++o])}else u=5;else u=4;else{if(0!==u&&3!==u){J();break}u=1}}return l}function $(t,e){"object"==typeof t&&(e=t.fragment,t=t.vertex),t=Z(t),e=Z(e),this._shaderID=K(t,e),this._vertexCode=$.parseImport(t),this._fragmentCode=$.parseImport(e),this.attributeSemantics={},this.matrixSemantics={},this.uniformSemantics={},this.matrixSemanticKeys=[],this.uniformTemplates={},this.attributes={},this.textures={},this.vertexDefines={},this.fragmentDefines={},this._parseAttributes(),this._parseUniforms(),this._parseDefines()}function tt(t){return t.material}function et(t,e,r){return e.uniforms[r].value}function rt(t,e,r,n){return r!==n}function nt(){}function it(t,e,r){this.availableAttributes=t,this.availableAttributeSymbols=e,this.indicesBuffer=r,this.vao=null}function at(t){var e=[],r=Object.keys(t);r.sort();for(var n=0;n=400?t.onerror&&t.onerror():t.onload&&t.onload(e.response)},t.onerror&&(e.onerror=t.onerror),e.send(null)}function vt(t,e,r,n){var i=t.accessors[r],a=e.bufferViews[i.bufferView],o=i.byteOffset||0,s=ci[i.componentType]||Ge.Float32Array,u=hi[i.type];null==u&&n&&(u=1);var l=new s(a,o,u*i.count),c=i.extensions&&i.extensions.WEB3D_quantized_attributes;if(c){for(var h,f,d=new Ge.Float32Array(u*i.count),p=c.decodeMatrix,h=new Array(u),f=new Array(u),m=0;m0){var i=Math.pow(2,t[3]-128-8+n);e[r+0]=t[0]*i,e[r+1]=t[1]*i,e[r+2]=t[2]*i}else e[r+0]=0,e[r+1]=0,e[r+2]=0;return e[r+3]=1,e}function Tt(t,e,r){for(var n="",i=e;i0;)if(t[a][0]=e[r++],t[a][1]=e[r++],t[a][2]=e[r++],t[a][3]=e[r++],1===t[a][0]&&1===t[a][1]&&1===t[a][2]){for(var s=t[a][3]<>>0;s>0;s--)Et(t[a-1],t[a]),a++,o--;i+=8}else a++,o--,i=0;return r}function At(t,e,r,n){if(nBi)return bt(t,e,r,n);var i=e[r++];if(2!=i)return bt(t,e,r-1,n);if(t[0][1]=e[r++],t[0][2]=e[r++],i=e[r++],(t[0][2]<<8>>>0|i)>>>0!==n)return null;for(var i=0;i<4;i++)for(var a=0;a128){o=(127&o)>>>0;for(var s=e[r++];o--;)t[a++][i]=s}else for(;o--;)t[a++][i]=e[r++]}return r}function St(t){Me.defaultsWithPropList(t,Wi,Xi),wt(t);for(var e="",r=0;r>16,r=t-(e<<8)>>8;return[e,r,t-(e<<16)-(r<<8)]}function te(t,e,r){return(t<<16)+(e<<8)+r}function ee(t){var e=t[1][0]-t[0][0],r=t[1][1]-t[0][1];return Math.sqrt(e*e+r*r)}function re(t){return[(t[0][0]+t[1][0])/2,(t[0][1]+t[1][1])/2]}function ne(t){return Array.isArray(t)||(t=[t,t]),t}function ie(t,e){return{name:t.name,type:t.type,size:t.size,semantic:t.semantic,value:ae(t.value,e)}}function ae(t,e){return e?t:new t.constructor(t)}function oe(t,e,r){return t*(1-r)+e*r}var se={linear:function(t){return t},quadraticIn:function(t){return t*t},quadraticOut:function(t){return t*(2-t)},quadraticInOut:function(t){return(t*=2)<1?.5*t*t:-.5*(--t*(t-2)-1)},cubicIn:function(t){return t*t*t},cubicOut:function(t){return--t*t*t+1},cubicInOut:function(t){return(t*=2)<1?.5*t*t*t:.5*((t-=2)*t*t+2)},quarticIn:function(t){return t*t*t*t},quarticOut:function(t){return 1- --t*t*t*t},quarticInOut:function(t){return(t*=2)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2)},quinticIn:function(t){return t*t*t*t*t},quinticOut:function(t){return--t*t*t*t*t+1},quinticInOut:function(t){return(t*=2)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)},sinusoidalIn:function(t){return 1-Math.cos(t*Math.PI/2)},sinusoidalOut:function(t){return Math.sin(t*Math.PI/2)},sinusoidalInOut:function(t){return.5*(1-Math.cos(Math.PI*t))},exponentialIn:function(t){return 0===t?0:Math.pow(1024,t-1)},exponentialOut:function(t){return 1===t?1:1-Math.pow(2,-10*t)},exponentialInOut:function(t){return 0===t?0:1===t?1:(t*=2)<1?.5*Math.pow(1024,t-1):.5*(2-Math.pow(2,-10*(t-1)))},circularIn:function(t){return 1-Math.sqrt(1-t*t)},circularOut:function(t){return Math.sqrt(1- --t*t)},circularInOut:function(t){return(t*=2)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},elasticIn:function(t){var e,r=.1;return 0===t?0:1===t?1:(!r||r<1?(r=1,e=.1):e=.4*Math.asin(1/r)/(2*Math.PI),-r*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/.4))},elasticOut:function(t){var e,r=.1;return 0===t?0:1===t?1:(!r||r<1?(r=1,e=.1):e=.4*Math.asin(1/r)/(2*Math.PI),r*Math.pow(2,-10*t)*Math.sin((t-e)*(2*Math.PI)/.4)+1)},elasticInOut:function(t){var e,r=.1;return 0===t?0:1===t?1:(!r||r<1?(r=1,e=.1):e=.4*Math.asin(1/r)/(2*Math.PI),(t*=2)<1?r*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/.4)*-.5:r*Math.pow(2,-10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/.4)*.5+1)},backIn:function(t){var e=1.70158;return t*t*((e+1)*t-e)},backOut:function(t){var e=1.70158;return--t*t*((e+1)*t+e)+1},backInOut:function(t){var e=2.5949095;return(t*=2)<1?t*t*((e+1)*t-e)*.5:.5*((t-=2)*t*((e+1)*t+e)+2)},bounceIn:function(t){return 1-se.bounceOut(1-t)},bounceOut:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},bounceInOut:function(t){return t<.5?.5*se.bounceIn(2*t):.5*se.bounceOut(2*t-1)+.5}},ue=function(t){t=t||{},this.name=t.name||"",this.target=t.target,this.life=t.life||1e3,this.delay=t.delay||0,this.gap=t.gap||0,this.playbackRate=t.playbackRate||1,this._initialized=!1,this._elapsedTime=0,this._loop=null!=t.loop&&t.loop,this.setLoop(this._loop),null!=t.easing&&this.setEasing(t.easing),this.onframe=t.onframe||e,this.onfinish=t.onfinish||e,this.onrestart=t.onrestart||e,this._paused=!1};ue.prototype={gap:0,life:0,delay:0,setLoop:function(t){this._loop=t,t&&(this._loopRemained="number"==typeof t?t:1/0)},setEasing:function(t){"string"==typeof t&&(t=se[t]),this.easing=t},step:function(t,e,r){if(this._initialized||(this._startTime=t+this.delay,this._initialized=!0),null!=this._currentTime&&(e=t-this._currentTime),this._currentTime=t,this._paused)return"paused";if(!(t0?(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&&(this._elapse(t),e=this._elapsedTime%this.life),t=t||Date.now(),this._startTime=t-e+this.delay,this._elapsedTime=0,this._needsRemove=!1,this._paused=!1},getElapsedTime:function(){return this._elapsedTime},_restartInLoop:function(t){this._startTime=t+this.gap,this._elapsedTime=0},_elapse:function(t,e){this._elapsedTime+=e*this.playbackRate},fire:function(t,e){var r="on"+t;this[r]&&this[r](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},pause:function(){this._paused=!0},resume:function(){this._paused=!1}},ue.prototype.constructor=ue;var le=Array.prototype.slice;d.prototype={constructor:d,when:function(t,e,r){this._maxTime=Math.max(t,this._maxTime),r=("function"==typeof r?r:se[r])||p;for(var n in e)this._tracks[n]||(this._tracks[n]=[],0!==t&&this._tracks[n].push({time:0,value:s(this._getter(this._target,n)),easing:r})),this._tracks[n].push({time:parseInt(t),value:e[n],easing:r});return this},then:function(t,e,r){return this.when(t+this._lastKFTime,e,r),this._lastKFTime+=t,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,r=0;rt)this.inputs.unshift(n);else if(this.inputs[i-1].position<=t)this.inputs.push(n);else{var a=this._findKey(t);this.inputs.splice(a,n)}return n},he.prototype.step=function(t,e,r){var n=ue.prototype.step.call(this,t);return"finish"!==n&&this.setTime(this.getElapsedTime()),r||"paused"===n||this.fire("frame"),n},he.prototype.setTime=function(t){var e=this.position,r=this.inputs,n=r.length,i=r[0].position,a=r[n-1].position;if(e<=i||e>=a){var o=e<=i?r[0]:r[n-1],s=o.clip,u=o.offset;s.setTime((t+u)%s.life),s.output instanceof ue?this.output.copy(s.output):this.output.copy(s)}else{var l=this._findKey(e),c=r[l],h=r[l+1],f=c.clip,d=h.clip;f.setTime((t+c.offset)%f.life),d.setTime((t+h.offset)%d.life);var p=(this.position-c.position)/(h.position-c.position),m=f.output instanceof ue?f.output:f,_=d.output instanceof ue?d.output:d;this.output.blend1D(m,_,p)}},he.prototype.clone=function(t){var e=ue.prototype.clone.call(this);e.output=this.output.clone();for(var r=0;r=r[i].position&&t=0;i--)t>=r[i].position&&t=0&&(this._cacheKey=e,this._cachePosition=t),e};var fe=1/1048576,de={triangulate:function(t,e){var r,n,i,a,o,s,u,l,c,h,f,d,p=t.length;if(p<3)return[];if(t=t.slice(0),e)for(r=p;r--;)t[r]=t[r][e];for(i=new Array(p),r=p;r--;)i[r]=r;for(i.sort(function(e,r){var n=t[r][0]-t[e][0];return 0!==n?n:e-r}),a=m(t),t.push(a[0],a[1],a[2]),o=[_(t,p+0,p+1,p+2)],s=[],u=[],r=i.length;r--;u.length=0){for(d=i[r],n=o.length;n--;)l=t[d][0]-o[n].x,l>0&&l*l>o[n].r?(s.push(o[n]),o.splice(n,1)):(c=t[d][1]-o[n].y,l*l+c*c-o[n].r>fe||(u.push(o[n].i,o[n].j,o[n].j,o[n].k,o[n].k,o[n].i),o.splice(n,1)));for(g(u),n=u.length;n;)f=u[--n],h=u[--n],o.push(_(t,h,f,d))}for(r=o.length;r--;)s.push(o[r]);for(o.length=0,r=s.length;r--;)s[r].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 r=t[1][0]-t[0][0],n=t[2][0]-t[0][0],i=t[1][1]-t[0][1],a=t[2][1]-t[0][1],o=r*a-n*i;if(0===o)return null;var s=(a*(e[0]-t[0][0])-n*(e[1]-t[0][1]))/o,u=(r*(e[1]-t[0][1])-i*(e[0]-t[0][0]))/o;return s<0||u<0||s+u>1?null:[s,u]}},pe="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},me=function(t,e){return e={exports:{}},t(e,e.exports),e.exports}(function(t,e){!function(t){var r={};r.exports=e,function(t){var e="undefined"==typeof window?pe:window,r=e.GLMAT_EPSILON;null==r&&(r=1e-6);var n=e.GLMAT_ARRAY_TYPE||Array,i=e.GLMAT_RANDOM;i||(i=Math.random);var a={};a.setMatrixArrayType=function(t){n=t},void 0!==t&&(t.glMatrix=a);var o=Math.PI/180;a.toRadian=function(t){return t*o};var s={};s.create=function(){var t=new n(2);return t[0]=0,t[1]=0,t},s.clone=function(t){var e=new n(2);return e[0]=t[0],e[1]=t[1],e},s.fromValues=function(t,e){var r=new n(2);return r[0]=t,r[1]=e,r},s.copy=function(t,e){return t[0]=e[0],t[1]=e[1],t},s.set=function(t,e,r){return t[0]=e,t[1]=r,t},s.add=function(t,e,r){return t[0]=e[0]+r[0],t[1]=e[1]+r[1],t},s.subtract=function(t,e,r){return t[0]=e[0]-r[0],t[1]=e[1]-r[1],t},s.sub=s.subtract,s.multiply=function(t,e,r){return t[0]=e[0]*r[0],t[1]=e[1]*r[1],t},s.mul=s.multiply,s.divide=function(t,e,r){return t[0]=e[0]/r[0],t[1]=e[1]/r[1],t},s.div=s.divide,s.min=function(t,e,r){return t[0]=Math.min(e[0],r[0]),t[1]=Math.min(e[1],r[1]),t},s.max=function(t,e,r){return t[0]=Math.max(e[0],r[0]),t[1]=Math.max(e[1],r[1]),t},s.scale=function(t,e,r){return t[0]=e[0]*r,t[1]=e[1]*r,t},s.scaleAndAdd=function(t,e,r,n){return t[0]=e[0]+r[0]*n,t[1]=e[1]+r[1]*n,t},s.distance=function(t,e){var r=e[0]-t[0],n=e[1]-t[1];return Math.sqrt(r*r+n*n)},s.dist=s.distance,s.squaredDistance=function(t,e){var r=e[0]-t[0],n=e[1]-t[1];return r*r+n*n},s.sqrDist=s.squaredDistance,s.length=function(t){var e=t[0],r=t[1];return Math.sqrt(e*e+r*r)},s.len=s.length,s.squaredLength=function(t){var e=t[0],r=t[1];return e*e+r*r},s.sqrLen=s.squaredLength,s.negate=function(t,e){return t[0]=-e[0],t[1]=-e[1],t},s.inverse=function(t,e){return t[0]=1/e[0],t[1]=1/e[1],t},s.normalize=function(t,e){var r=e[0],n=e[1],i=r*r+n*n;return i>0&&(i=1/Math.sqrt(i),t[0]=e[0]*i,t[1]=e[1]*i),t},s.dot=function(t,e){return t[0]*e[0]+t[1]*e[1]},s.cross=function(t,e,r){var n=e[0]*r[1]-e[1]*r[0];return t[0]=t[1]=0,t[2]=n,t},s.lerp=function(t,e,r,n){var i=e[0],a=e[1];return t[0]=i+n*(r[0]-i),t[1]=a+n*(r[1]-a),t},s.random=function(t,e){e=e||1;var r=2*i()*Math.PI;return t[0]=Math.cos(r)*e,t[1]=Math.sin(r)*e,t},s.transformMat2=function(t,e,r){var n=e[0],i=e[1];return t[0]=r[0]*n+r[2]*i,t[1]=r[1]*n+r[3]*i,t},s.transformMat2d=function(t,e,r){var n=e[0],i=e[1];return t[0]=r[0]*n+r[2]*i+r[4],t[1]=r[1]*n+r[3]*i+r[5],t},s.transformMat3=function(t,e,r){var n=e[0],i=e[1];return t[0]=r[0]*n+r[3]*i+r[6],t[1]=r[1]*n+r[4]*i+r[7],t},s.transformMat4=function(t,e,r){var n=e[0],i=e[1];return t[0]=r[0]*n+r[4]*i+r[12],t[1]=r[1]*n+r[5]*i+r[13],t},s.forEach=function(){var t=s.create();return function(e,r,n,i,a,o){var s,u;for(r||(r=2),n||(n=0),u=i?Math.min(i*r+n,e.length):e.length,s=n;s0&&(a=1/Math.sqrt(a),t[0]=e[0]*a,t[1]=e[1]*a,t[2]=e[2]*a),t},u.dot=function(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]},u.cross=function(t,e,r){var n=e[0],i=e[1],a=e[2],o=r[0],s=r[1],u=r[2];return t[0]=i*u-a*s,t[1]=a*o-n*u,t[2]=n*s-i*o,t},u.lerp=function(t,e,r,n){var i=e[0],a=e[1],o=e[2];return t[0]=i+n*(r[0]-i),t[1]=a+n*(r[1]-a),t[2]=o+n*(r[2]-o),t},u.random=function(t,e){e=e||1;var r=2*i()*Math.PI,n=2*i()-1,a=Math.sqrt(1-n*n)*e;return t[0]=Math.cos(r)*a,t[1]=Math.sin(r)*a,t[2]=n*e,t},u.transformMat4=function(t,e,r){var n=e[0],i=e[1],a=e[2],o=r[3]*n+r[7]*i+r[11]*a+r[15];return o=o||1,t[0]=(r[0]*n+r[4]*i+r[8]*a+r[12])/o,t[1]=(r[1]*n+r[5]*i+r[9]*a+r[13])/o,t[2]=(r[2]*n+r[6]*i+r[10]*a+r[14])/o,t},u.transformMat3=function(t,e,r){var n=e[0],i=e[1],a=e[2];return t[0]=n*r[0]+i*r[3]+a*r[6],t[1]=n*r[1]+i*r[4]+a*r[7],t[2]=n*r[2]+i*r[5]+a*r[8],t},u.transformQuat=function(t,e,r){var n=e[0],i=e[1],a=e[2],o=r[0],s=r[1],u=r[2],l=r[3],c=l*n+s*a-u*i,h=l*i+u*n-o*a,f=l*a+o*i-s*n,d=-o*n-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.rotateX=function(t,e,r,n){var i=[],a=[];return i[0]=e[0]-r[0],i[1]=e[1]-r[1],i[2]=e[2]-r[2],a[0]=i[0],a[1]=i[1]*Math.cos(n)-i[2]*Math.sin(n),a[2]=i[1]*Math.sin(n)+i[2]*Math.cos(n),t[0]=a[0]+r[0],t[1]=a[1]+r[1],t[2]=a[2]+r[2],t},u.rotateY=function(t,e,r,n){var i=[],a=[];return i[0]=e[0]-r[0],i[1]=e[1]-r[1],i[2]=e[2]-r[2],a[0]=i[2]*Math.sin(n)+i[0]*Math.cos(n),a[1]=i[1],a[2]=i[2]*Math.cos(n)-i[0]*Math.sin(n),t[0]=a[0]+r[0],t[1]=a[1]+r[1],t[2]=a[2]+r[2],t},u.rotateZ=function(t,e,r,n){var i=[],a=[];return i[0]=e[0]-r[0],i[1]=e[1]-r[1],i[2]=e[2]-r[2],a[0]=i[0]*Math.cos(n)-i[1]*Math.sin(n),a[1]=i[0]*Math.sin(n)+i[1]*Math.cos(n),a[2]=i[2],t[0]=a[0]+r[0],t[1]=a[1]+r[1],t[2]=a[2]+r[2],t},u.forEach=function(){var t=u.create();return function(e,r,n,i,a,o){var s,u;for(r||(r=3),n||(n=0),u=i?Math.min(i*r+n,e.length):e.length,s=n;s1?0:Math.acos(i)},u.str=function(t){return"vec3("+t[0]+", "+t[1]+", "+t[2]+")"},void 0!==t&&(t.vec3=u);var l={};l.create=function(){var t=new n(4);return t[0]=0,t[1]=0,t[2]=0,t[3]=0,t},l.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},l.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},l.copy=function(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[3],t},l.set=function(t,e,r,n,i){return t[0]=e,t[1]=r,t[2]=n,t[3]=i,t},l.add=function(t,e,r){return t[0]=e[0]+r[0],t[1]=e[1]+r[1],t[2]=e[2]+r[2],t[3]=e[3]+r[3],t},l.subtract=function(t,e,r){return t[0]=e[0]-r[0],t[1]=e[1]-r[1],t[2]=e[2]-r[2],t[3]=e[3]-r[3],t},l.sub=l.subtract,l.multiply=function(t,e,r){return t[0]=e[0]*r[0],t[1]=e[1]*r[1],t[2]=e[2]*r[2],t[3]=e[3]*r[3],t},l.mul=l.multiply,l.divide=function(t,e,r){return t[0]=e[0]/r[0],t[1]=e[1]/r[1],t[2]=e[2]/r[2],t[3]=e[3]/r[3],t},l.div=l.divide,l.min=function(t,e,r){return t[0]=Math.min(e[0],r[0]),t[1]=Math.min(e[1],r[1]),t[2]=Math.min(e[2],r[2]),t[3]=Math.min(e[3],r[3]),t},l.max=function(t,e,r){return t[0]=Math.max(e[0],r[0]),t[1]=Math.max(e[1],r[1]),t[2]=Math.max(e[2],r[2]),t[3]=Math.max(e[3],r[3]),t},l.scale=function(t,e,r){return t[0]=e[0]*r,t[1]=e[1]*r,t[2]=e[2]*r,t[3]=e[3]*r,t},l.scaleAndAdd=function(t,e,r,n){return t[0]=e[0]+r[0]*n,t[1]=e[1]+r[1]*n,t[2]=e[2]+r[2]*n,t[3]=e[3]+r[3]*n,t},l.distance=function(t,e){var r=e[0]-t[0],n=e[1]-t[1],i=e[2]-t[2],a=e[3]-t[3];return Math.sqrt(r*r+n*n+i*i+a*a)},l.dist=l.distance,l.squaredDistance=function(t,e){var r=e[0]-t[0],n=e[1]-t[1],i=e[2]-t[2],a=e[3]-t[3];return r*r+n*n+i*i+a*a},l.sqrDist=l.squaredDistance,l.length=function(t){var e=t[0],r=t[1],n=t[2],i=t[3];return Math.sqrt(e*e+r*r+n*n+i*i)},l.len=l.length,l.squaredLength=function(t){var e=t[0],r=t[1],n=t[2],i=t[3];return e*e+r*r+n*n+i*i},l.sqrLen=l.squaredLength,l.negate=function(t,e){return t[0]=-e[0],t[1]=-e[1],t[2]=-e[2],t[3]=-e[3],t},l.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},l.normalize=function(t,e){var r=e[0],n=e[1],i=e[2],a=e[3],o=r*r+n*n+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},l.dot=function(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]+t[3]*e[3]},l.lerp=function(t,e,r,n){var i=e[0],a=e[1],o=e[2],s=e[3];return t[0]=i+n*(r[0]-i),t[1]=a+n*(r[1]-a),t[2]=o+n*(r[2]-o),t[3]=s+n*(r[3]-s),t},l.random=function(t,e){return e=e||1,t[0]=i(),t[1]=i(),t[2]=i(),t[3]=i(),l.normalize(t,t),l.scale(t,t,e),t},l.transformMat4=function(t,e,r){var n=e[0],i=e[1],a=e[2],o=e[3];return t[0]=r[0]*n+r[4]*i+r[8]*a+r[12]*o,t[1]=r[1]*n+r[5]*i+r[9]*a+r[13]*o,t[2]=r[2]*n+r[6]*i+r[10]*a+r[14]*o,t[3]=r[3]*n+r[7]*i+r[11]*a+r[15]*o,t},l.transformQuat=function(t,e,r){var n=e[0],i=e[1],a=e[2],o=r[0],s=r[1],u=r[2],l=r[3],c=l*n+s*a-u*i,h=l*i+u*n-o*a,f=l*a+o*i-s*n,d=-o*n-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},l.forEach=function(){var t=l.create();return function(e,r,n,i,a,o){var s,u;for(r||(r=4),n||(n=0),u=i?Math.min(i*r+n,e.length):e.length,s=n;s.999999?(n[0]=0,n[1]=0,n[2]=0,n[3]=1,n):(u.cross(t,i,a),n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=1+o,p.normalize(n,n))}}(),p.setAxes=function(){var t=f.create();return function(e,r,n,i){return t[0]=n[0],t[3]=n[1],t[6]=n[2],t[1]=i[0],t[4]=i[1],t[7]=i[2],t[2]=-r[0],t[5]=-r[1],t[8]=-r[2],p.normalize(e,p.fromMat3(e,t))}}(),p.clone=l.clone,p.fromValues=l.fromValues,p.copy=l.copy,p.set=l.set,p.identity=function(t){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t},p.setAxisAngle=function(t,e,r){r*=.5;var n=Math.sin(r);return t[0]=n*e[0],t[1]=n*e[1],t[2]=n*e[2],t[3]=Math.cos(r),t},p.add=l.add,p.multiply=function(t,e,r){var n=e[0],i=e[1],a=e[2],o=e[3],s=r[0],u=r[1],l=r[2],c=r[3];return t[0]=n*c+o*s+i*l-a*u,t[1]=i*c+o*u+a*s-n*l,t[2]=a*c+o*l+n*u-i*s,t[3]=o*c-n*s-i*u-a*l,t},p.mul=p.multiply,p.scale=l.scale,p.rotateX=function(t,e,r){r*=.5;var n=e[0],i=e[1],a=e[2],o=e[3],s=Math.sin(r),u=Math.cos(r);return t[0]=n*u+o*s,t[1]=i*u+a*s,t[2]=a*u-i*s,t[3]=o*u-n*s,t},p.rotateY=function(t,e,r){r*=.5;var n=e[0],i=e[1],a=e[2],o=e[3],s=Math.sin(r),u=Math.cos(r);return t[0]=n*u-a*s,t[1]=i*u+o*s,t[2]=a*u+n*s,t[3]=o*u-i*s,t},p.rotateZ=function(t,e,r){r*=.5;var n=e[0],i=e[1],a=e[2],o=e[3],s=Math.sin(r),u=Math.cos(r);return t[0]=n*u+i*s,t[1]=i*u-n*s,t[2]=a*u+o*s,t[3]=o*u-a*s,t},p.calculateW=function(t,e){var r=e[0],n=e[1],i=e[2];return t[0]=r,t[1]=n,t[2]=i,t[3]=Math.sqrt(Math.abs(1-r*r-n*n-i*i)),t},p.dot=l.dot,p.lerp=l.lerp,p.slerp=function(t,e,r,n){var i,a,o,s,u,l=e[0],c=e[1],h=e[2],f=e[3],d=r[0],p=r[1],m=r[2],_=r[3];return a=l*d+c*p+h*m+f*_,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-n)*i)/o,u=Math.sin(n*i)/o):(s=1-n,u=n),t[0]=s*l+u*d,t[1]=s*c+u*p,t[2]=s*h+u*m,t[3]=s*f+u*_,t},p.invert=function(t,e){var r=e[0],n=e[1],i=e[2],a=e[3],o=r*r+n*n+i*i+a*a,s=o?1/o:0;return t[0]=-r*s,t[1]=-n*s,t[2]=-i*s,t[3]=a*s,t},p.conjugate=function(t,e){return t[0]=-e[0],t[1]=-e[1],t[2]=-e[2],t[3]=e[3],t},p.length=l.length,p.len=p.length,p.squaredLength=l.squaredLength,p.sqrLen=p.squaredLength,p.normalize=l.normalize,p.fromMat3=function(t,e){var r,n=e[0]+e[4]+e[8];if(n>0)r=Math.sqrt(n+1),t[3]=.5*r,r=.5/r,t[0]=(e[5]-e[7])*r,t[1]=(e[6]-e[2])*r,t[2]=(e[1]-e[3])*r;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;r=Math.sqrt(e[3*i+i]-e[3*a+a]-e[3*o+o]+1),t[i]=.5*r,r=.5/r,t[3]=(e[3*a+o]-e[3*o+a])*r,t[a]=(e[3*a+i]+e[3*i+a])*r,t[o]=(e[3*o+i]+e[3*i+o])*r}return t},p.str=function(t){return"quat("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},void 0!==t&&(t.quat=p)}(r.exports)}()}),_e=me.vec2,ge=function(t,e){t=t||0,e=e||0,this.array=_e.fromValues(t,e),this._dirty=!0};if(ge.prototype={constructor:ge,add:function(t){return _e.add(this.array,this.array,t.array),this._dirty=!0,this},set:function(t,e){return this.array[0]=t,this.array[1]=e,this._dirty=!0,this},setArray:function(t){return this.array[0]=t[0],this.array[1]=t[1],this._dirty=!0,this},clone:function(){return new ge(this.x,this.y)},copy:function(t){return _e.copy(this.array,t.array),this._dirty=!0,this},cross:function(t,e){return _e.cross(t.array,this.array,e.array),t._dirty=!0,this},dist:function(t){return _e.dist(this.array,t.array)},distance:function(t){return _e.distance(this.array,t.array)},div:function(t){return _e.div(this.array,this.array,t.array),this._dirty=!0,this},divide:function(t){return _e.divide(this.array,this.array,t.array),this._dirty=!0,this},dot:function(t){return _e.dot(this.array,t.array)},len:function(){return _e.len(this.array)},length:function(){return _e.length(this.array)},lerp:function(t,e,r){return _e.lerp(this.array,t.array,e.array,r),this._dirty=!0,this},min:function(t){return _e.min(this.array,this.array,t.array),this._dirty=!0,this},max:function(t){return _e.max(this.array,this.array,t.array),this._dirty=!0,this},mul:function(t){return _e.mul(this.array,this.array,t.array),this._dirty=!0,this},multiply:function(t){return _e.multiply(this.array,this.array,t.array),this._dirty=!0,this},negate:function(){return _e.negate(this.array,this.array),this._dirty=!0,this},normalize:function(){return _e.normalize(this.array,this.array),this._dirty=!0,this},random:function(t){return _e.random(this.array,t),this._dirty=!0,this},scale:function(t){return _e.scale(this.array,this.array,t),this._dirty=!0,this},scaleAndAdd:function(t,e){return _e.scaleAndAdd(this.array,this.array,t.array,e),this._dirty=!0,this},sqrDist:function(t){return _e.sqrDist(this.array,t.array)},squaredDistance:function(t){return _e.squaredDistance(this.array,t.array)},sqrLen:function(){return _e.sqrLen(this.array)},squaredLength:function(){return _e.squaredLength(this.array)},sub:function(t){return _e.sub(this.array,this.array,t.array),this._dirty=!0,this},subtract:function(t){return _e.subtract(this.array,this.array,t.array),this._dirty=!0,this},transformMat2:function(t){return _e.transformMat2(this.array,this.array,t.array),this._dirty=!0,this},transformMat2d:function(t){return _e.transformMat2d(this.array,this.array,t.array),this._dirty=!0,this},transformMat3:function(t){return _e.transformMat3(this.array,this.array,t.array),this._dirty=!0,this},transformMat4:function(t){return _e.transformMat4(this.array,this.array,t.array),this._dirty=!0,this},toString:function(){return"["+Array.prototype.join.call(this.array,",")+"]"},toArray:function(){return Array.prototype.slice.call(this.array)}},Object.defineProperty){var ve=ge.prototype;Object.defineProperty(ve,"x",{get:function(){return this.array[0]},set:function(t){this.array[0]=t,this._dirty=!0}}),Object.defineProperty(ve,"y",{get:function(){return this.array[1]},set:function(t){this.array[1]=t,this._dirty=!0}})}ge.add=function(t,e,r){return _e.add(t.array,e.array,r.array),t._dirty=!0,t},ge.set=function(t,e,r){return _e.set(t.array,e,r),t._dirty=!0,t}, +ge.copy=function(t,e){return _e.copy(t.array,e.array),t._dirty=!0,t},ge.cross=function(t,e,r){return _e.cross(t.array,e.array,r.array),t._dirty=!0,t},ge.dist=function(t,e){return _e.distance(t.array,e.array)},ge.distance=ge.dist,ge.div=function(t,e,r){return _e.divide(t.array,e.array,r.array),t._dirty=!0,t},ge.divide=ge.div,ge.dot=function(t,e){return _e.dot(t.array,e.array)},ge.len=function(t){return _e.length(t.array)},ge.lerp=function(t,e,r,n){return _e.lerp(t.array,e.array,r.array,n),t._dirty=!0,t},ge.min=function(t,e,r){return _e.min(t.array,e.array,r.array),t._dirty=!0,t},ge.max=function(t,e,r){return _e.max(t.array,e.array,r.array),t._dirty=!0,t},ge.mul=function(t,e,r){return _e.multiply(t.array,e.array,r.array),t._dirty=!0,t},ge.multiply=ge.mul,ge.negate=function(t,e){return _e.negate(t.array,e.array),t._dirty=!0,t},ge.normalize=function(t,e){return _e.normalize(t.array,e.array),t._dirty=!0,t},ge.random=function(t,e){return _e.random(t.array,e),t._dirty=!0,t},ge.scale=function(t,e,r){return _e.scale(t.array,e.array,r),t._dirty=!0,t},ge.scaleAndAdd=function(t,e,r,n){return _e.scaleAndAdd(t.array,e.array,r.array,n),t._dirty=!0,t},ge.sqrDist=function(t,e){return _e.sqrDist(t.array,e.array)},ge.squaredDistance=ge.sqrDist,ge.sqrLen=function(t){return _e.sqrLen(t.array)},ge.squaredLength=ge.sqrLen,ge.sub=function(t,e,r){return _e.subtract(t.array,e.array,r.array),t._dirty=!0,t},ge.subtract=ge.sub,ge.transformMat2=function(t,e,r){return _e.transformMat2(t.array,e.array,r.array),t._dirty=!0,t},ge.transformMat2d=function(t,e,r){return _e.transformMat2d(t.array,e.array,r.array),t._dirty=!0,t},ge.transformMat3=function(t,e,r){return _e.transformMat3(t.array,e.array,r.array),t._dirty=!0,t},ge.transformMat4=function(t,e,r){return _e.transformMat4(t.array,e.array,r.array),t._dirty=!0,t};var ye=function(t){t=t||{},ue.call(this,t),this.output=t.output||null,this.inputs=t.inputs||[],this.position=new ge,this._cacheTriangle=null,this._triangles=[],this._updateTriangles()};ye.prototype=new ue,ye.prototype.constructor=ye,ye.prototype.addInput=function(t,e,r){var n={position:t,clip:e,offset:r||0};return this.inputs.push(n),this.life=Math.max(e.life,this.life),this._updateTriangles(),n},ye.prototype._updateTriangles=function(){var t=this.inputs.map(function(t){return t.position});this._triangles=de.triangulate(t,"array")},ye.prototype.step=function(t,e,r){var n=ue.prototype.step.call(this,t);return"finish"!==n&&this.setTime(this.getElapsedTime()),r||"paused"===n||this.fire("frame"),n},ye.prototype.setTime=function(t){var e=this._findTriangle(this.position);if(e){var r=e[1],n=e[2],i=e[0],a=this.inputs[i.indices[0]],o=this.inputs[i.indices[1]],s=this.inputs[i.indices[2]],u=a.clip,l=o.clip,c=s.clip;u.setTime((t+a.offset)%u.life),l.setTime((t+o.offset)%l.life),c.setTime((t+s.offset)%c.life);var h=u.output instanceof ue?u.output:u,f=l.output instanceof ue?l.output:l,d=c.output instanceof ue?c.output:c;this.output.blend2D(h,f,d,r,n)}},ye.prototype.clone=function(t){var e=ue.prototype.clone.call(this);e.output=this.output.clone();for(var r=0;r=e.time[r-1])t=e.time[r-1],n=r-2;else if(t=0;a--)if(e.time[a-1]<=t&&e.time[a]>t){n=a-1;break}}else for(var a=this._cacheKey;at){n=a;break}if(n>-1){this._cacheKey=n,this._cacheTime=t;var o=n,s=n+1,u=e.time[o],l=e.time[s],c=l-u,h=0===c?0:(t-u)/c;e.rotation&&y(this.rotation,e.rotation,e.rotation,h,4*o,4*s),e.position&&v(this.position,e.position,e.position,h,3*o,3*s),e.scale&&v(this.scale,e.scale,e.scale,h,3*o,3*s)}n===r-2&&(this._cacheKey=0,this._cacheTime=0),this.updateTarget()}},Ee.prototype.updateTarget=function(){var t=this.channels;this.target&&(t.position&&this.target.position.setArray(this.position),t.rotation&&this.target.rotation.setArray(this.rotation),t.scale&&this.target.scale.setArray(this.scale))},Ee.prototype.getMaxTime=function(){return this.channels.time[this.channels.time.length-1]},Ee.prototype.getSubTrack=function(t,e){var r=new Ee({name:this.name}),n=this.channels.time[0];t=Math.min(Math.max(t,n),this.life),e=Math.min(Math.max(e,n),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&&(r.channels.rotation=new Float32Array(4*o)),this.channels.position&&(r.channels.position=new Float32Array(3*o)),this.channels.scale&&(r.channels.scale=new Float32Array(3*o)),this.channels.time&&(r.channels.time=new Float32Array(o)),this.setTime(t);for(var s=0;s<3;s++)r.channels.rotation[s]=this.rotation[s],r.channels.position[s]=this.position[s],r.channels.scale[s]=this.scale[s];r.channels.time[0]=0,r.channels.rotation[3]=this.rotation[3];for(var s=1;st&&(n=i);var a=0;if(n>=0)var o=e.time[n],s=e.time[n+1],a=(t-o)/(s-o);return[n,a]},Ee.prototype.blend1D=function(t,e,r){Te.lerp(this.position,t.position,e.position,r),Te.lerp(this.scale,t.scale,e.scale,r),xe.slerp(this.rotation,t.rotation,e.rotation,r)},Ee.prototype.blend2D=function(){var t=xe.create(),e=xe.create();return function(r,n,i,a,o){var s=1-a-o;this.position[0]=r.position[0]*s+n.position[0]*a+i.position[0]*o,this.position[1]=r.position[1]*s+n.position[1]*a+i.position[1]*o,this.position[2]=r.position[2]*s+n.position[2]*a+i.position[2]*o,this.scale[0]=r.scale[0]*s+n.scale[0]*a+i.scale[0]*o,this.scale[1]=r.scale[1]*s+n.scale[1]*a+i.scale[1]*o,this.scale[2]=r.scale[2]*s+n.scale[2]*a+i.scale[2]*o;var u=a+o;0===u?xe.copy(this.rotation,r.rotation):(xe.slerp(t,r.rotation,n.rotation,u),xe.slerp(e,r.rotation,i.rotation,u),xe.slerp(this.rotation,t,e,o/u))}}(),Ee.prototype.additiveBlend=function(t,e){Te.add(this.position,t.position,e.position),Te.add(this.scale,t.scale,e.scale),xe.multiply(this.rotation,e.rotation,t.rotation)},Ee.prototype.subtractiveBlend=function(t,e){Te.sub(this.position,t.position,e.position),Te.sub(this.scale,t.scale,e.scale),xe.invert(this.rotation,e.rotation),xe.multiply(this.rotation,this.rotation,t.rotation)},Ee.prototype.clone=function(){var t=Ee.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},Te.copy(t.position,this.position),xe.copy(t.rotation,this.rotation),Te.copy(t.scale,this.scale),t.target=this.target,t.updateTarget(),t};var be={extend:x,derive:x},Ae={trigger:function(t){if(this.hasOwnProperty("__handlers__")&&this.__handlers__.hasOwnProperty(t)){var e=this.__handlers__[t],r=e.length,n=-1,i=arguments;switch(i.length){case 1:for(;++n=0&&this._clips.splice(e,1)},removeAnimator:function(t){for(var e=t.getClips(),r=0;r=0&&this.tracks.splice(e,1)},Oe.prototype.getSubClip=function(t,e,r){for(var n=new Oe({name:this.name}),i=0;i0){var e=this.min,r=this.max,n=e.array,i=r.array;Ze(n,t[0]),Ze(i,t[0]);for(var a=1;ai[0]&&(i[0]=o[0]),o[1]>i[1]&&(i[1]=o[1]),o[2]>i[2]&&(i[2]=o[2])}e._dirty=!0,r._dirty=!0}},union:function(t){var e=this.min,r=this.max;return Ke.min(e.array,e.array,t.min.array),Ke.max(r.array,r.array,t.max.array),e._dirty=!0,r._dirty=!0,this},intersection:function(t){var e=this.min,r=this.max;return Ke.max(e.array,e.array,t.min.array),Ke.min(r.array,r.array,t.max.array),e._dirty=!0,r._dirty=!0,this},intersectBoundingBox:function(t){var e=this.min.array,r=this.max.array,n=t.min.array,i=t.max.array;return!(e[0]>i[0]||e[1]>i[1]||e[2]>i[2]||r[0]=i[0]&&r[1]>=i[1]&&r[2]>=i[2]},containPoint:function(t){var e=this.min.array,r=this.max.array,n=t.array;return e[0]<=n[0]&&e[1]<=n[1]&&e[2]<=n[2]&&r[0]>=n[0]&&r[1]>=n[1]&&r[2]>=n[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(t){this.transformFrom(this,t)},transformFrom:function(){var t=Ke.create(),e=Ke.create(),r=Ke.create(),n=Ke.create(),i=Ke.create(),a=Ke.create();return function(o,s){var u=o.min.array,l=o.max.array,c=s.array;return t[0]=c[0]*u[0],t[1]=c[1]*u[0],t[2]=c[2]*u[0],e[0]=c[0]*l[0],e[1]=c[1]*l[0],e[2]=c[2]*l[0],r[0]=c[4]*u[1],r[1]=c[5]*u[1],r[2]=c[6]*u[1],n[0]=c[4]*l[1],n[1]=c[5]*l[1],n[2]=c[6]*l[1],i[0]=c[8]*u[2],i[1]=c[9]*u[2],i[2]=c[10]*u[2],a[0]=c[8]*l[2],a[1]=c[9]*l[2],a[2]=c[10]*l[2],u=this.min.array,l=this.max.array,u[0]=Math.min(t[0],e[0])+Math.min(r[0],n[0])+Math.min(i[0],a[0])+c[12],u[1]=Math.min(t[1],e[1])+Math.min(r[1],n[1])+Math.min(i[1],a[1])+c[13],u[2]=Math.min(t[2],e[2])+Math.min(r[2],n[2])+Math.min(i[2],a[2])+c[14],l[0]=Math.max(t[0],e[0])+Math.max(r[0],n[0])+Math.max(i[0],a[0])+c[12],l[1]=Math.max(t[1],e[1])+Math.max(r[1],n[1])+Math.max(i[1],a[1])+c[13],l[2]=Math.max(t[2],e[2])+Math.max(r[2],n[2])+Math.max(i[2],a[2])+c[14],this.min._dirty=!0,this.max._dirty=!0,this}}(),applyProjection:function(t){var e=this.min.array,r=this.max.array,n=t.array,i=e[0],a=e[1],o=e[2],s=r[0],u=r[1],l=e[2],c=r[0],h=r[1],f=r[2];if(1===n[15])e[0]=n[0]*i+n[12],e[1]=n[5]*a+n[13],r[2]=n[10]*o+n[14],r[0]=n[0]*c+n[12],r[1]=n[5]*h+n[13],e[2]=n[10]*f+n[14];else{ +var d=-1/o;e[0]=n[0]*i*d,e[1]=n[5]*a*d,r[2]=(n[10]*o+n[14])*d,d=-1/l,r[0]=n[0]*s*d,r[1]=n[5]*u*d,d=-1/f,e[2]=(n[10]*f+n[14])*d}return this.min._dirty=!0,this.max._dirty=!0,this},updateVertices:function(){var t=this.vertices;if(!t){t=[];for(var e=0;e<8;e++)t[e]=Ke.fromValues(0,0,0);this.vertices=t}var r=this.min.array,n=this.max.array;return Je(t[0],r[0],r[1],r[2]),Je(t[1],r[0],n[1],r[2]),Je(t[2],n[0],r[1],r[2]),Je(t[3],n[0],n[1],r[2]),Je(t[4],r[0],r[1],n[2]),Je(t[5],r[0],n[1],n[2]),Je(t[6],n[0],r[1],n[2]),Je(t[7],n[0],n[1],n[2]),this},copy:function(t){var e=this.min,r=this.max;return Ze(e.array,t.min.array),Ze(r.array,t.max.array),e._dirty=!0,r._dirty=!0,this},clone:function(){var t=new Qe;return t.copy(this),t}};var $e=me.mat4,tr=me.vec3,er=me.mat3,rr=me.quat,nr=function(){this._axisX=new ze,this._axisY=new ze,this._axisZ=new ze,this.array=$e.create(),this._dirty=!0};nr.prototype={constructor:nr,setArray:function(t){for(var e=0;e=this._maxSize&&r>0){var n=this._list.head;this._list.remove(n),delete this._map[n.key]}var i=this._list.insert(e);i.key=t,this._map[t]=i}},sr.prototype.get=function(t){var e=this._map[t];if(this._map.hasOwnProperty(t))return e!==this._list.tail&&(this._list.remove(e),this._list.insertEntry(e)),e.value},sr.prototype.remove=function(t){var e=this._map[t];void 0!==e&&(delete this._map[t],this._list.remove(e))},sr.prototype.clear=function(){this._list.clear(),this._map={}};var ur={},lr={transparent:[0,0,0,0],aliceblue:[240,248,255,1],antiquewhite:[250,235,215,1],aqua:[0,255,255,1],aquamarine:[127,255,212,1],azure:[240,255,255,1],beige:[245,245,220,1],bisque:[255,228,196,1],black:[0,0,0,1],blanchedalmond:[255,235,205,1],blue:[0,0,255,1],blueviolet:[138,43,226,1],brown:[165,42,42,1],burlywood:[222,184,135,1],cadetblue:[95,158,160,1],chartreuse:[127,255,0,1],chocolate:[210,105,30,1],coral:[255,127,80,1],cornflowerblue:[100,149,237,1],cornsilk:[255,248,220,1],crimson:[220,20,60,1],cyan:[0,255,255,1],darkblue:[0,0,139,1],darkcyan:[0,139,139,1],darkgoldenrod:[184,134,11,1],darkgray:[169,169,169,1],darkgreen:[0,100,0,1],darkgrey:[169,169,169,1],darkkhaki:[189,183,107,1],darkmagenta:[139,0,139,1],darkolivegreen:[85,107,47,1],darkorange:[255,140,0,1],darkorchid:[153,50,204,1],darkred:[139,0,0,1],darksalmon:[233,150,122,1],darkseagreen:[143,188,143,1],darkslateblue:[72,61,139,1],darkslategray:[47,79,79,1],darkslategrey:[47,79,79,1],darkturquoise:[0,206,209,1],darkviolet:[148,0,211,1],deeppink:[255,20,147,1],deepskyblue:[0,191,255,1],dimgray:[105,105,105,1],dimgrey:[105,105,105,1],dodgerblue:[30,144,255,1],firebrick:[178,34,34,1],floralwhite:[255,250,240,1],forestgreen:[34,139,34,1],fuchsia:[255,0,255,1],gainsboro:[220,220,220,1],ghostwhite:[248,248,255,1],gold:[255,215,0,1],goldenrod:[218,165,32,1],gray:[128,128,128,1],green:[0,128,0,1],greenyellow:[173,255,47,1],grey:[128,128,128,1],honeydew:[240,255,240,1],hotpink:[255,105,180,1],indianred:[205,92,92,1],indigo:[75,0,130,1],ivory:[255,255,240,1],khaki:[240,230,140,1],lavender:[230,230,250,1],lavenderblush:[255,240,245,1],lawngreen:[124,252,0,1],lemonchiffon:[255,250,205,1],lightblue:[173,216,230,1],lightcoral:[240,128,128,1],lightcyan:[224,255,255,1],lightgoldenrodyellow:[250,250,210,1],lightgray:[211,211,211,1],lightgreen:[144,238,144,1],lightgrey:[211,211,211,1],lightpink:[255,182,193,1],lightsalmon:[255,160,122,1],lightseagreen:[32,178,170,1],lightskyblue:[135,206,250,1],lightslategray:[119,136,153,1],lightslategrey:[119,136,153,1],lightsteelblue:[176,196,222,1],lightyellow:[255,255,224,1],lime:[0,255,0,1],limegreen:[50,205,50,1],linen:[250,240,230,1],magenta:[255,0,255,1],maroon:[128,0,0,1],mediumaquamarine:[102,205,170,1],mediumblue:[0,0,205,1],mediumorchid:[186,85,211,1],mediumpurple:[147,112,219,1],mediumseagreen:[60,179,113,1],mediumslateblue:[123,104,238,1],mediumspringgreen:[0,250,154,1],mediumturquoise:[72,209,204,1],mediumvioletred:[199,21,133,1],midnightblue:[25,25,112,1],mintcream:[245,255,250,1],mistyrose:[255,228,225,1],moccasin:[255,228,181,1],navajowhite:[255,222,173,1],navy:[0,0,128,1],oldlace:[253,245,230,1],olive:[128,128,0,1],olivedrab:[107,142,35,1],orange:[255,165,0,1],orangered:[255,69,0,1],orchid:[218,112,214,1],palegoldenrod:[238,232,170,1],palegreen:[152,251,152,1],paleturquoise:[175,238,238,1],palevioletred:[219,112,147,1],papayawhip:[255,239,213,1],peachpuff:[255,218,185,1],peru:[205,133,63,1],pink:[255,192,203,1],plum:[221,160,221,1],powderblue:[176,224,230,1],purple:[128,0,128,1],red:[255,0,0,1],rosybrown:[188,143,143,1],royalblue:[65,105,225,1],saddlebrown:[139,69,19,1],salmon:[250,128,114,1],sandybrown:[244,164,96,1],seagreen:[46,139,87,1],seashell:[255,245,238,1],sienna:[160,82,45,1],silver:[192,192,192,1],skyblue:[135,206,235,1],slateblue:[106,90,205,1],slategray:[112,128,144,1],slategrey:[112,128,144,1],snow:[255,250,250,1],springgreen:[0,255,127,1],steelblue:[70,130,180,1],tan:[210,180,140,1],teal:[0,128,128,1],thistle:[216,191,216,1],tomato:[255,99,71,1],turquoise:[64,224,208,1],violet:[238,130,238,1],wheat:[245,222,179,1],white:[255,255,255,1],whitesmoke:[245,245,245,1],yellow:[255,255,0,1],yellowgreen:[154,205,50,1]},cr=new sr(20),hr=null;ur.parse=function(t,e){if(t){e=e||[];var r=cr.get(t);if(r)return D(e,r);t+="";var n=t.replace(/ /g,"").toLowerCase();if(n in lr)return D(e,lr[n]),O(t,e),e;if("#"!==n.charAt(0)){var i=n.indexOf("("),a=n.indexOf(")");if(-1!==i&&a+1===n.length){var o=n.substr(0,i),s=n.substr(i+1,a-(i+1)).split(","),u=1;switch(o){case"rgba":if(4!==s.length)return void I(e,0,0,0,1);u=L(s.pop());case"rgb":return 3!==s.length?void I(e,0,0,0,1):(I(e,R(s[0]),R(s[1]),R(s[2]),u),O(t,e),e);case"hsla":return 4!==s.length?void I(e,0,0,0,1):(s[3]=L(s[3]),B(s,e),O(t,e),e);case"hsl":return 3!==s.length?void I(e,0,0,0,1):(B(s,e),O(t,e),e);default:return}}I(e,0,0,0,1)}else{if(4===n.length){var l=parseInt(n.substr(1),16);return l>=0&&l<=4095?(I(e,(3840&l)>>4|(3840&l)>>8,240&l|(240&l)>>4,15&l|(15&l)<<4,1),O(t,e),e):void I(e,0,0,0,1)}if(7===n.length){var l=parseInt(n.substr(1),16);return l>=0&&l<=16777215?(I(e,(16711680&l)>>16,(65280&l)>>8,255&l,1),O(t,e),e):void I(e,0,0,0,1)}}}},ur.parseToFloat=function(t,e){if(e=ur.parse(t,e))return e[0]/=255,e[1]/=255,e[2]/=255,e},ur.lift=function(t,e){var r=ur.parse(t);if(r){for(var n=0;n<3;n++)r[n]=e<0?r[n]*(1-e)|0:(255-r[n])*e+r[n]|0;return ur.stringify(r,4===r.length?"rgba":"rgb")}},ur.toHex=function(t){var e=ur.parse(t);if(e)return((1<<24)+(e[0]<<16)+(e[1]<<8)+ +e[2]).toString(16).slice(1)},ur.fastLerp=function(t,e,r){if(e&&e.length&&t>=0&&t<=1){r=r||[];var n=t*(e.length-1),i=Math.floor(n),a=Math.ceil(n),o=e[i],s=e[a],u=n-i;return r[0]=w(P(o[0],s[0],u)),r[1]=w(P(o[1],s[1],u)),r[2]=w(P(o[2],s[2],u)),r[3]=M(P(o[3],s[3],u)),r}},ur.fastMapToColor=ur.fastLerp,ur.lerp=function(t,e,r){if(e&&e.length&&t>=0&&t<=1){var n=t*(e.length-1),i=Math.floor(n),a=Math.ceil(n),o=ur.parse(e[i]),s=ur.parse(e[a]),u=n-i,l=ur.stringify([w(P(o[0],s[0],u)),w(P(o[1],s[1],u)),w(P(o[2],s[2],u)),M(P(o[3],s[3],u))],"rgba");return r?{color:l,leftIndex:i,rightIndex:a,value:n}:l}},ur.mapToColor=ur.lerp,ur.modifyHSL=function(t,e,r,n){if(t=ur.parse(t))return t=U(t),null!=e&&(t[0]=C(e)),null!=r&&(t[1]=L(r)),null!=n&&(t[2]=L(n)),ur.stringify(B(t),"rgba")},ur.modifyAlpha=function(t,e){if((t=ur.parse(t))&&null!=e)return t[3]=M(e),ur.stringify(t,"rgba")},ur.stringify=function(t,e){if(t&&t.length){var r=t[0]+","+t[1]+","+t[2];return"rgba"!==e&&"hsva"!==e&&"hsla"!==e||(r+=","+t[3]),e+"("+r+")"}};var fr=ur.parseToFloat,dr={},pr=Re.extend(function(){return{name:"",depthTest:!0,depthMask:!0,transparent:!1,blend:null,autoUpdateTextureStatus:!0,uniforms:{},vertexDefines:{},fragmentDefines:{},_textureStatus:{},_enabledUniforms:null}},function(){this.name||(this.name="MATERIAL_"+this.__uid__),this.shader&&this.attachShader(this.shader,!0)},{precision:"highp",setUniform:function(t,e){void 0===e&&console.warn('Uniform value "'+t+'" is undefined');var r=this.uniforms[t];r&&("string"==typeof e&&(e=fr(e)||e),r.value=e,this.autoUpdateTextureStatus&&"t"===r.type&&(e?this.enableTexture(t):this.disableTexture(t)))},setUniforms:function(t){for(var e in t){var r=t[e];this.setUniform(e,r)}},isUniformEnabled:function(t){return this._enabledUniforms.indexOf(t)>=0},getEnabledUniforms:function(){return this._enabledUniforms},getTextureUniforms:function(){return this._textureUniforms},set:function(t,e){if("object"==typeof t)for(var r in t){var n=t[r];this.setUniform(r,n)}else this.setUniform(t,e)},get:function(t){var e=this.uniforms[t];if(e)return e.value},attachShader:function(t,e){var r=this.uniforms;this.uniforms=t.createUniforms(),this.shader=t;var n=this.uniforms;this._enabledUniforms=Object.keys(n),this._enabledUniforms.sort(),this._textureUniforms=this._enabledUniforms.filter(function(t){var e=this.uniforms[t].type;return"t"===e||"tv"===e},this);var i=this.vertexDefines,a=this.fragmentDefines;if(this.vertexDefines=Me.clone(t.vertexDefines),this.fragmentDefines=Me.clone(t.fragmentDefines),e){for(var o in r)n[o]&&(n[o].value=r[o].value);Me.defaults(this.vertexDefines,i),Me.defaults(this.fragmentDefines,a)}var s={};for(var u in t.textures)s[u]={shaderType:t.textures[u].shaderType,type:t.textures[u].type,enabled:!(!e||!this._textureStatus[u])&&this._textureStatus[u].enabled};this._textureStatus=s,this._programKey=""},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.vertexDefines=Me.clone(this.vertexDefines),t.fragmentDefines=Me.clone(this.fragmentDefines),t.enableTexture(this.getEnabledTextures()),t.precision=this.precision,t},define:function(t,e,r){var n=this.vertexDefines,i=this.fragmentDefines;"vertex"!==t&&"fragment"!==t&&"both"!==t&&arguments.length<3&&(r=e,e=t,t="both"),r=null!=r?r:null,"vertex"!==t&&"both"!==t||n[e]!==r&&(n[e]=r,this._programKey=""),"fragment"!==t&&"both"!==t||i[e]!==r&&(i[e]=r,"both"!==t&&(this._programKey=""))},undefine:function(t,e){"vertex"!==t&&"fragment"!==t&&"both"!==t&&arguments.length<2&&(e=t,t="both"),"vertex"!==t&&"both"!==t||this.isDefined("vertex",e)&&(delete this.vertexDefines[e],this._programKey=""),"fragment"!==t&&"both"!==t||this.isDefined("fragment",e)&&(delete this.fragmentDefines[e],"both"!==t&&(this._programKey=""))},isDefined:function(t,e){switch(t){case"vertex":return void 0!==this.vertexDefines[e];case"fragment":return void 0!==this.fragmentDefines[e]}},getDefine:function(t,e){switch(t){case"vertex":return this.vertexDefines[e];case"fragment":return this.fragmentDefines[e]}},enableTexture:function(t){if(Array.isArray(t))for(var e=0;e>e;return t+1},dispose:function(t){var e=this._cache;e.use(t.__uid__);var r=e.get("webgl_texture");r&&t.gl.deleteTexture(r),e.deleteContext(t.__uid__)},isRenderable:function(){},isPowerOfTwo:function(){}});Object.defineProperty(_r.prototype,"width",{get:function(){return this._width},set:function(t){this._width=t}}),Object.defineProperty(_r.prototype,"height",{get:function(){return this._height},set:function(t){this._height=t}}),_r.BYTE=Fe.BYTE,_r.UNSIGNED_BYTE=Fe.UNSIGNED_BYTE,_r.SHORT=Fe.SHORT,_r.UNSIGNED_SHORT=Fe.UNSIGNED_SHORT,_r.INT=Fe.INT,_r.UNSIGNED_INT=Fe.UNSIGNED_INT,_r.FLOAT=Fe.FLOAT,_r.HALF_FLOAT=36193,_r.UNSIGNED_INT_24_8_WEBGL=34042,_r.DEPTH_COMPONENT=Fe.DEPTH_COMPONENT,_r.DEPTH_STENCIL=Fe.DEPTH_STENCIL,_r.ALPHA=Fe.ALPHA,_r.RGB=Fe.RGB,_r.RGBA=Fe.RGBA,_r.LUMINANCE=Fe.LUMINANCE,_r.LUMINANCE_ALPHA=Fe.LUMINANCE_ALPHA,_r.SRGB=35904,_r.SRGB_ALPHA=35906,_r.COMPRESSED_RGB_S3TC_DXT1_EXT=33776,_r.COMPRESSED_RGBA_S3TC_DXT1_EXT=33777,_r.COMPRESSED_RGBA_S3TC_DXT3_EXT=33778,_r.COMPRESSED_RGBA_S3TC_DXT5_EXT=33779,_r.NEAREST=Fe.NEAREST,_r.LINEAR=Fe.LINEAR,_r.NEAREST_MIPMAP_NEAREST=Fe.NEAREST_MIPMAP_NEAREST,_r.LINEAR_MIPMAP_NEAREST=Fe.LINEAR_MIPMAP_NEAREST,_r.NEAREST_MIPMAP_LINEAR=Fe.NEAREST_MIPMAP_LINEAR,_r.LINEAR_MIPMAP_LINEAR=Fe.LINEAR_MIPMAP_LINEAR,_r.REPEAT=Fe.REPEAT,_r.CLAMP_TO_EDGE=Fe.CLAMP_TO_EDGE,_r.MIRRORED_REPEAT=Fe.MIRRORED_REPEAT;var gr={};gr.isPowerOfTwo=function(t){return 0==(t&t-1)},gr.nextPowerOfTwo=function(t){return t--,t|=t>>1,t|=t>>2,t|=t>>4,t|=t>>8,t|=t>>16,++t},gr.nearestPowerOfTwo=function(t){return Math.pow(2,Math.round(Math.log(t)/Math.LN2))};var vr=gr.isPowerOfTwo,yr=_r.extend(function(){return{image:null,pixels:null,mipmaps:[],convertToPOT:!1}},{textureType:"texture2D",update:function(t){var e=t.gl;e.bindTexture(e.TEXTURE_2D,this._cache.get("webgl_texture")),this.updateCommon(t);var r=this.format,n=this.type,i=!(!this.convertToPOT||this.mipmaps.length||!this.image||this.wrapS!==_r.REPEAT&&this.wrapT!==_r.REPEAT||!this.NPOT);e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,i?this.wrapS:this.getAvailableWrapS()),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,i?this.wrapT:this.getAvailableWrapT()),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,i?this.magFilter:this.getAvailableMagFilter()),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,i?this.minFilter:this.getAvailableMinFilter());var a=t.getGLExtension("EXT_texture_filter_anisotropic");if(a&&this.anisotropic>1&&e.texParameterf(e.TEXTURE_2D,a.TEXTURE_MAX_ANISOTROPY_EXT,this.anisotropic),36193===n){t.getGLExtension("OES_texture_half_float")||(n=Fe.FLOAT)}if(this.mipmaps.length)for(var o=this.width,s=this.height,u=0;u=_r.COMPRESSED_RGB_S3TC_DXT1_EXT?t.compressedTexImage2D(t.TEXTURE_2D,r,a,n,i,0,e.pixels):t.texImage2D(t.TEXTURE_2D,r,a,n,i,0,a,o,e.pixels)},generateMipmap:function(t){var e=t.gl;this.useMipmap&&!this.NPOT&&(e.bindTexture(e.TEXTURE_2D,this._cache.get("webgl_texture")),e.generateMipmap(e.TEXTURE_2D))},isPowerOfTwo:function(){return vr(this.width)&&vr(this.height)},isRenderable:function(){return this.image?"CANVAS"===this.image.nodeName||"VIDEO"===this.image.nodeName||this.image.complete:!(!this.width||!this.height)},bind:function(t){t.gl.bindTexture(t.gl.TEXTURE_2D,this.getWebGLTexture(t))},unbind:function(t){t.gl.bindTexture(t.gl.TEXTURE_2D,null)},load:function(t,e){var r=new Image;e&&(r.crossOrigin=e);var n=this;return r.onload=function(){n.dirty(),n.trigger("success",n),r.onload=null},r.onerror=function(){n.trigger("error",n),r.onerror=null},r.src=t,this.image=r,this}});Object.defineProperty(yr.prototype,"width",{get:function(){return this.image?this.image.width:this._width},set:function(t){this.image?console.warn("Texture from image can't set width"):(this._width!==t&&this.dirty(),this._width=t)}}),Object.defineProperty(yr.prototype,"height",{get:function(){return this.image?this.image.height:this._height},set:function(t){this.image?console.warn("Texture from image can't set height"):(this._height!==t&&this.dirty(),this._height=t)}});var xr={},Tr=new Ge.Float32Array(16),Er=Re.extend({uniformSemantics:{},attributes:{}},function(){this._locations={},this._textureSlot=0,this._program=null},{bind:function(t){this._textureSlot=0,t.gl.useProgram(this._program)},hasUniform:function(t){var e=this._locations[t];return null!==e&&void 0!==e},useTextureSlot:function(t,e,r){e&&(t.gl.activeTexture(t.gl.TEXTURE0+r),e.isRenderable()?e.bind(t):e.unbind(t))},currentTextureSlot:function(){return this._textureSlot},resetTextureSlot:function(t){this._textureSlot=t||0},takeCurrentTextureSlot:function(t,e){var r=this._textureSlot;return this.useTextureSlot(t,e,r),this._textureSlot++,r},setUniform:function(t,e,r,n){var i=this._locations,a=i[r];if(null===a||void 0===a)return!1;switch(e){case"m4":if(!(n instanceof Float32Array)){for(var o=0;o=0)this.attributeSemantics[r]={symbol:t,type:e};else if(Dr.indexOf(r)>=0){var n=!1,i=r;r.match(/TRANSPOSE$/)&&(n=!0,i=r.slice(0,-9)),this.matrixSemantics[r]={symbol:t,type:e,isTranspose:n,semanticNoTranspose:i}}else Ir.indexOf(r)>=0&&(this.uniformSemantics[r]={symbol:t,type:e})}, +_addMaterialUniform:function(t,e,r,n,i,a){a[t]={type:r,value:i?Nr.array:n||Nr[e],semantic:null}},_parseUniforms:function(){function t(t){return null!=t?function(){return t}:null}function e(e,a,o){var s=Q(a,o),u=[];for(var l in s){var c=s[l],h=c.semantic,f=l,d=Lr[a],p=t(s[l].value);s[l].isArray&&(f+="["+s[l].arraySize+"]",d+="v"),u.push(f),n._uniformList.push(l),c.ignore||("sampler2D"!==a&&"samplerCube"!==a||(n.textures[l]={shaderType:i,type:a}),h?n._addSemanticUniform(l,d,h):n._addMaterialUniform(l,a,d,p,s[l].isArray,r))}return u.length>0?"uniform "+a+" "+u.join(",")+";\n":""}var r={},n=this,i="vertex";this._uniformList=[],this._vertexCode=this._vertexCode.replace(Cr,e),i="fragment",this._fragmentCode=this._fragmentCode.replace(Cr,e),n.matrixSemanticKeys=Object.keys(this.matrixSemantics),this.uniformTemplates=r},_parseAttributes:function(){function t(t,n,i){var a=Q(n,i),o=Or[n]||1,s=[];for(var u in a){var l=a[u].semantic;if(e[u]={type:"float",size:o,semantic:l||null},l){if(Pr.indexOf(l)<0)throw new Error('Unkown semantic "'+l+'"');r.attributeSemantics[l]={symbol:u,type:n}}s.push(u)}return"attribute "+n+" "+s.join(",")+";\n"}var e={},r=this;this._vertexCode=this._vertexCode.replace(Mr,t),this.attributes=e},_parseDefines:function(){function t(t,n,i){var a="vertex"===r?e.vertexDefines:e.fragmentDefines;return a[n]||(a[n]="false"!==i&&("true"===i||(i?isNaN(parseFloat(i))?i.trim():parseFloat(i):null))),""}var e=this,r="vertex";this._vertexCode=this._vertexCode.replace(Rr,t),r="fragment",this._fragmentCode=this._fragmentCode.replace(Rr,t)},clone:function(){var t=Ur[this._shaderID];return new $(t.vertex,t.fragment)}},Object.defineProperty&&(Object.defineProperty($.prototype,"shaderID",{get:function(){return this._shaderID}}),Object.defineProperty($.prototype,"vertex",{get:function(){return this._vertexCode}}),Object.defineProperty($.prototype,"fragment",{get:function(){return this._fragmentCode}}),Object.defineProperty($.prototype,"uniforms",{get:function(){return this._uniformList}}));var Fr=/(@import)\s*([0-9a-zA-Z_\-\.]*)/g;$.parseImport=function(t){return t=t.replace(Fr,function(t,e,r){var t=$.source(r);return t?$.parseImport(t):(console.error('Shader chunk "'+r+'" not existed in library'),"")})};var kr=/(@export)\s*([0-9a-zA-Z_\-\.]*)\s*\n([\s\S]*?)@end/g;$.import=function(t){t.replace(kr,function(t,e,r,n){var n=n.replace(/(^[\s\t\xa0\u3000]+)|([\u3000\xa0\s\t]+\x24)/g,"");if(n){for(var i,a=r.split("."),o=$.codes,s=0;s0&&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},render:function(t,e,r,n){var i=this.gl,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)}if(r||t.update(!1),t.updateLights(),!(e=e||t.getMainCamera()))return void console.error("Can't find camera in the scene.");e.update();var l=t.updateRenderList(e,!0);this._sceneRendering=t;var c=l.opaque,h=l.transparent,f=t.material;t.trigger("beforerender",this,t,e,l),n?(this.renderPreZ(c,t,e),i.depthFunc(i.LEQUAL)):i.depthFunc(i.LESS);for(var d=Xr(),p=Wr.create(),m=0;m0){var s=t[i-1],u=s.joints?s.joints.length:0;if((a.joints?a.joints.length:0)===u&&a.material===s.material&&a.lightGroup===s.lightGroup){a.__program=s.__program;continue}}var l=this._programMgr.getProgram(a,o,e);this.validateProgram(l),a.__program=l}},renderPass:function(t,e,r){this.trigger("beforerenderpass",this,t,e,r),r=r||{},r.getMaterial=r.getMaterial||tt,r.getUniform=r.getUniform||et,r.isMaterialChanged=r.isMaterialChanged||rt,r.beforeRender=r.beforeRender||nt,r.afterRender=r.afterRender||nt,this.updatePrograms(t,this._sceneRendering,r),r.sortCompare&&t.sort(r.sortCompare);var n=this.viewport,i=n.devicePixelRatio,a=[n.x*i,n.y*i,n.width*i,n.height*i],o=this.devicePixelRatio,s=this.__currentFrameBuffer?[this.__currentFrameBuffer.getTextureWidth(),this.__currentFrameBuffer.getTextureHeight()]:[this._width*o,this._height*o],u=[a[2],a[3]],l=Date.now();zr.copy(Kr.VIEW,e.viewMatrix.array),zr.copy(Kr.PROJECTION,e.projectionMatrix.array),zr.multiply(Kr.VIEWPROJECTION,e.projectionMatrix.array,Kr.VIEW),zr.copy(Kr.VIEWINVERSE,e.worldTransform.array),zr.invert(Kr.PROJECTIONINVERSE,Kr.PROJECTION),zr.invert(Kr.VIEWPROJECTIONINVERSE,Kr.VIEWPROJECTION);for(var c,h,f,d,p,m,_,g,v,y,x,T=this.gl,E=this._sceneRendering,b=this.getGLExtension("OES_vertex_array_object"),A=0;Athis.distance,i=1;i<8;i++)if(an.dot(e[i].array,r)>this.distance!=n)return!0},intersectLine:function(){var t=an.create();return function(e,r,n){var i=this.distanceToPoint(e),a=this.distanceToPoint(r);if(i>0&&a>0||i<0&&a<0)return null;var o=this.normal.array,s=this.distance,u=e.array;an.sub(t,r.array,e.array),an.normalize(t,t);var l=an.dot(o,t);if(0===l)return null;n||(n=new ze);var c=(an.dot(o,u)-s)/l;return an.scaleAndAdd(n.array,u,t,-c),n._dirty=!0,n}}(),applyTransform:function(){var t=on.create(),e=sn.create(),r=sn.create();return r[3]=1,function(n){n=n.array,an.scale(r,this.normal.array,this.distance),sn.transformMat4(r,r,n),this.distance=an.dot(r,this.normal.array),on.invert(t,n),on.transpose(t,t),e[3]=0,an.copy(e,this.normal.array),sn.transformMat4(e,e,t),an.copy(this.normal.array,e)}}(),copy:function(t){an.copy(this.normal.array,t.normal.array),this.normal._dirty=!0,this.distance=t.distance},clone:function(){var t=new un;return t.copy(this),t}};var ln=me.vec3,cn=ln.set,hn=ln.copy,fn=ln.transformMat4,dn=Math.min,pn=Math.max,mn=function(){this.planes=[];for(var t=0;t<6;t++)this.planes.push(new un);this.boundingBox=new Qe,this.vertices=[];for(var t=0;t<8;t++)this.vertices[t]=ln.fromValues(0,0,0)};mn.prototype={setFromProjection:function(t){ +var e=this.planes,r=t.array,n=r[0],i=r[1],a=r[2],o=r[3],s=r[4],u=r[5],l=r[6],c=r[7],h=r[8],f=r[9],d=r[10],p=r[11],m=r[12],_=r[13],g=r[14],v=r[15];cn(e[0].normal.array,o-n,c-s,p-h),e[0].distance=-(v-m),e[0].normalize(),cn(e[1].normal.array,o+n,c+s,p+h),e[1].distance=-(v+m),e[1].normalize(),cn(e[2].normal.array,o+i,c+u,p+f),e[2].distance=-(v+_),e[2].normalize(),cn(e[3].normal.array,o-i,c-u,p-f),e[3].distance=-(v-_),e[3].normalize(),cn(e[4].normal.array,o-a,c-l,p-d),e[4].distance=-(v-g),e[4].normalize(),cn(e[5].normal.array,o+a,c+l,p+d),e[5].distance=-(v+g),e[5].normalize();var y=this.boundingBox,x=this.vertices;if(0===v){var T=u/n,E=-g/(d-1),b=-g/(d+1),A=-b/u,S=-E/u;y.min.set(-A*T,-A,b),y.max.set(A*T,A,E),cn(x[0],-A*T,-A,b),cn(x[1],-A*T,A,b),cn(x[2],A*T,-A,b),cn(x[3],A*T,A,b),cn(x[4],-S*T,-S,E),cn(x[5],-S*T,S,E),cn(x[6],S*T,-S,E),cn(x[7],S*T,S,E)}else{var w=(-1-m)/n,C=(1-m)/n,M=(1-_)/u,R=(-1-_)/u,L=(-1-g)/d,N=(1-g)/d;y.min.set(Math.min(w,C),Math.min(R,M),Math.min(N,L)),y.max.set(Math.max(C,w),Math.max(M,R),Math.max(L,N));var P=y.min.array,I=y.max.array;cn(x[0],P[0],P[1],P[2]),cn(x[1],P[0],I[1],P[2]),cn(x[2],I[0],P[1],P[2]),cn(x[3],I[0],I[1],P[2]),cn(x[4],P[0],P[1],I[2]),cn(x[5],P[0],I[1],I[2]),cn(x[6],I[0],P[1],I[2]),cn(x[7],I[0],I[1],I[2])}},getTransformedBoundingBox:function(){var t=ln.create();return function(e,r){var n=this.vertices,i=r.array,a=e.min,o=e.max,s=a.array,u=o.array,l=n[0];fn(t,l,i),hn(s,t),hn(u,t);for(var c=1;c<8;c++)l=n[c],fn(t,l,i),s[0]=dn(t[0],s[0]),s[1]=dn(t[1],s[1]),s[2]=dn(t[2],s[2]),u[0]=pn(t[0],u[0]),u[1]=pn(t[1],u[1]),u[2]=pn(t[2],u[2]);return a._dirty=!0,o._dirty=!0,e}}()};var _n=me.vec3,gn=function(t,e){this.origin=t||new ze,this.direction=e||new ze};gn.prototype={constructor:gn,intersectPlane:function(t,e){var r=t.normal.array,n=t.distance,i=this.origin.array,a=this.direction.array,o=_n.dot(r,a);if(0===o)return null;e||(e=new ze);var s=(_n.dot(r,i)-n)/o;return _n.scaleAndAdd(e.array,i,a,-s),e._dirty=!0,e},mirrorAgainstPlane:function(t){var e=_n.dot(t.normal.array,this.direction.array);_n.scaleAndAdd(this.direction.array,this.direction.array,t.normal.array,2*-e),this.direction._dirty=!0},distanceToPoint:function(){var t=_n.create();return function(e){_n.sub(t,e,this.origin.array);var r=_n.dot(t,this.direction.array);if(r<0)return _n.distance(this.origin.array,e);var n=_n.lenSquared(t);return Math.sqrt(n-r*r)}}(),intersectSphere:function(){var t=_n.create();return function(e,r,n){var i=this.origin.array,a=this.direction.array;e=e.array,_n.sub(t,e,i);var o=_n.dot(t,a),s=_n.squaredLength(t),u=s-o*o,l=r*r;if(!(u>l)){var c=Math.sqrt(l-u),h=o-c,f=o+c;return n||(n=new ze),h<0?f<0?null:(_n.scaleAndAdd(n.array,i,a,f),n):(_n.scaleAndAdd(n.array,i,a,h),n)}}}(),intersectBoundingBox:function(t,e){var r,n,i,a,o,s,u=this.direction.array,l=this.origin.array,c=t.min.array,h=t.max.array,f=1/u[0],d=1/u[1],p=1/u[2];if(f>=0?(r=(c[0]-l[0])*f,n=(h[0]-l[0])*f):(n=(c[0]-l[0])*f,r=(h[0]-l[0])*f),d>=0?(i=(c[1]-l[1])*d,a=(h[1]-l[1])*d):(a=(c[1]-l[1])*d,i=(h[1]-l[1])*d),r>a||i>n)return null;if((i>r||r!==r)&&(r=i),(a=0?(o=(c[2]-l[2])*p,s=(h[2]-l[2])*p):(s=(c[2]-l[2])*p,o=(h[2]-l[2])*p),r>s||o>n)return null;if((o>r||r!==r)&&(r=o),(s=0?r:n;return e||(e=new ze),_n.scaleAndAdd(e.array,l,u,m),e},intersectTriangle:function(){var t=_n.create(),e=_n.create(),r=_n.create(),n=_n.create();return function(i,a,o,s,u,l){var c=this.direction.array,h=this.origin.array;i=i.array,a=a.array,o=o.array,_n.sub(t,a,i),_n.sub(e,o,i),_n.cross(n,e,c);var f=_n.dot(t,n);if(s){if(f>-1e-5)return null}else if(f>-1e-5&&f<1e-5)return null;_n.sub(r,h,i);var d=_n.dot(n,r)/f;if(d<0||d>1)return null;_n.cross(n,t,r);var p=_n.dot(c,n)/f;if(p<0||p>1||d+p>1)return null;_n.cross(n,t,e);var m=-_n.dot(r,n)/f;return m<0?null:(u||(u=new ze),l&&ze.set(l,1-d-p,d,p),_n.scaleAndAdd(u.array,h,c,m),u)}}(),applyTransform:function(t){ze.add(this.direction,this.direction,this.origin),ze.transformMat4(this.origin,this.origin,t),ze.transformMat4(this.direction,this.direction,t),ze.sub(this.direction,this.direction,this.origin),ze.normalize(this.direction,this.direction)},copy:function(t){ze.copy(this.origin,t.origin),ze.copy(this.direction,t.direction)},clone:function(){var t=new gn;return t.copy(this),t}};var vn=me.vec3,yn=me.vec4,xn=rn.extend(function(){return{projectionMatrix:new nr,invProjectionMatrix:new nr,viewMatrix:new nr,frustum:new mn}},function(){this.update(!0)},{update:function(t){rn.prototype.update.call(this,t),nr.invert(this.viewMatrix,this.worldTransform),this.updateProjectionMatrix(),nr.invert(this.invProjectionMatrix,this.projectionMatrix),this.frustum.setFromProjection(this.projectionMatrix)},setViewMatrix:function(t){nr.copy(this.viewMatrix,t),nr.invert(this.worldTransform,t),this.decomposeWorldTransform()},decomposeProjectionMatrix:function(){},setProjectionMatrix:function(t){nr.copy(this.projectionMatrix,t),nr.invert(this.invProjectionMatrix,t),this.decomposeProjectionMatrix()},updateProjectionMatrix:function(){},castRay:function(){var t=yn.create();return function(e,r){var n=void 0!==r?r:new gn,i=e.array[0],a=e.array[1];return yn.set(t,i,a,-1,1),yn.transformMat4(t,t,this.invProjectionMatrix.array),yn.transformMat4(t,t,this.worldTransform.array),vn.scale(n.origin.array,t,1/t[3]),yn.set(t,i,a,1,1),yn.transformMat4(t,t,this.invProjectionMatrix.array),yn.transformMat4(t,t,this.worldTransform.array),vn.scale(t,t,1/t[3]),vn.sub(n.direction.array,t,n.origin.array),vn.normalize(n.direction.array,n.direction.array),n.direction._dirty=!0,n.origin._dirty=!0,n}}()}),Tn=me.mat4,En=Tn.create(),bn=Tn.create(),An={};ot.prototype.startCount=function(){this._opaqueCount=0,this._transparentCount=0},ot.prototype.add=function(t,e){e?this.transparent[this._transparentCount++]=t:this.opaque[this._opaqueCount++]=t},ot.prototype.endCount=function(){this.transparent.length=this._transparentCount,this.opaque.length=this._opaqueCount};var Sn=rn.extend(function(){return{material:null,lights:[],viewBoundingBoxLastFrame:new Qe,shadowUniforms:{},_cameraList:[],_lightUniforms:{},_previousLightNumber:{},_lightNumber:{},_lightProgramKeys:{},_nodeRepository:{},_renderLists:new sr(20)}},function(){this._scene=this},{addToScene:function(t){t instanceof xn?(this._cameraList.length>0&&console.warn("Found multiple camera in one scene. Use the fist one."),this._cameraList.push(t)):t instanceof nn&&this.lights.push(t),t.name&&(this._nodeRepository[t.name]=t)},removeFromScene:function(t){var e;t instanceof xn?(e=this._cameraList.indexOf(t))>=0&&this._cameraList.splice(e,1):t instanceof nn&&(e=this.lights.indexOf(t))>=0&&this.lights.splice(e,1),t.name&&delete this._nodeRepository[t.name]},getNode:function(t){return this._nodeRepository[t]},setMainCamera:function(t){var e=this._cameraList.indexOf(t);e>=0&&this._cameraList.splice(e,1),this._cameraList.unshift(t)},getMainCamera:function(){return this._cameraList[0]},getLights:function(){return this.lights},updateLights:function(){var t=this.lights;this._previousLightNumber=this._lightNumber;for(var e={},r=0;r0&&this._doUpdateRenderList(o,e,r,n,i)}},isFrustumCulled:function(){var t=new Qe,e=new nr;return function(r,n,i){var a=r.boundingBox||r.geometry.boundingBox;if(e.array=i,t.transformFrom(a,e),r.castShadow&&this.viewBoundingBoxLastFrame.union(t),r.frustumCulling&&!r.isSkinnedMesh()){if(!t.intersectBoundingBox(n.frustum.boundingBox))return!0;e.array=n.projectionMatrix.array,t.max.array[2]>0&&t.min.array[2]<0&&(t.max.array[2]=-1e-20),t.applyProjection(e);var o=t.min.array,s=t.max.array;if(s[0]<-1||o[0]>1||s[1]<-1||o[1]>1||s[2]<-1||o[2]>1)return!0}return!1}}(),_updateLightUniforms:function(){var t=this.lights;t.sort(st);var e=this._lightUniforms;for(var r in e)for(var n in e[r])e[r][n].value.length=0;for(var i=0;ia[0]&&(a[0]=s),u>a[1]&&(a[1]=u),l>a[2]&&(a[2]=l)}r._dirty=!0,n._dirty=!0}},dirty:function(){for(var t=this.getEnabledAttributes(),e=0;e=0){e||(e=Mn());var r=this.indices;return e[0]=r[3*t],e[1]=r[3*t+1],e[2]=r[3*t+2],e}},setTriangleIndices:function(t,e){var r=this.indices;r[3*t]=e[0],r[3*t+1]=e[1],r[3*t+2]=e[2]},isUseIndices:function(){return!!this.indices},initIndicesFromArray:function(t){var e,r=this.vertexCount>65535?Ge.Uint32Array:Ge.Uint16Array;if(t[0]&&t[0].length){var n=0;e=new r(3*t.length);for(var i=0;i=0&&(e.splice(r,1),delete this.attributes[t],!0)},getAttribute:function(t){return this.attributes[t]},getEnabledAttributes:function(){var t=this._enabledAttributes,e=this._attributeList;if(t)return t;for(var r=[],n=this.vertexCount,i=0;i65535&&(this.indices=new Ge.Uint32Array(this.indices));for(var t=this.attributes,e=this.indices,r=this.getEnabledAttributes(),n={},i=0;i65535?Uint32Array:Uint16Array,p=this.indices=new d(e*t*6),m=this.radius,_=this.phiStart,g=this.phiLength,v=this.thetaStart,y=this.thetaLength,m=this.radius,x=[],T=[],E=0,b=1/m;for(f=0;f<=t;f++)for(h=0;h<=e;h++)l=h/e,c=f/t,o=-m*Math.cos(_+l*g)*Math.sin(v+c*y),s=m*Math.cos(v+c*y),u=m*Math.sin(_+l*g)*Math.sin(v+c*y),x[0]=o,x[1]=s,x[2]=u,T[0]=l,T[1]=c,r.set(E,x),n.set(E,T),x[0]*=b,x[1]*=b,x[2]*=b,i.set(E,x),E++;var A,S,w,C,M=e+1,R=0;for(f=0;f65535?Uint32Array:Uint16Array,g=(o-1)*(s-1)*6,v=this.indices=new _(g),y=0,f=0;f1&&e.texParameterf(e.TEXTURE_CUBE_MAP,i.TEXTURE_MAX_ANISOTROPY_EXT,this.anisotropic),36193===n){t.getGLExtension("OES_texture_half_float")||(n=Fe.FLOAT)}if(this.mipmaps.length)for(var a=this.width,o=this.height,s=0;s0},beforeRender:function(t){},afterRender:function(t,e){},getBoundingBox:function(t,e){return e=rn.prototype.getBoundingBox.call(this,t,e),this.geometry&&this.geometry.boundingBox&&e.union(this.geometry.boundingBox),e},clone:function(){var t=["castShadow","receiveShadow","mode","culling","cullFace","frontFace","frustumCulling","renderOrder","lineWidth","ignorePicking","ignorePreZ","ignoreGBuffer"];return function(){var e=rn.prototype.clone.call(this);e.geometry=this.geometry,e.material=this.material;for(var r=0;r0)},getSkinMatricesTexture:function(){return this._skinMatricesTexture=this._skinMatricesTexture||new yr({type:Fe.FLOAT,minFilter:Fe.NEAREST,magFilter:Fe.NEAREST,useMipmap:!1,flipY:!1}),this._skinMatricesTexture},clone:function(){var t=zn.prototype.clone.call(this);return t.skeleton=this.skeleton,this.joints&&(t.joints=this.joints.slice()),t}});Wn.POINTS=Fe.POINTS,Wn.LINES=Fe.LINES,Wn.LINE_LOOP=Fe.LINE_LOOP,Wn.LINE_STRIP=Fe.LINE_STRIP,Wn.TRIANGLES=Fe.TRIANGLES,Wn.TRIANGLE_STRIP=Fe.TRIANGLE_STRIP,Wn.TRIANGLE_FAN=Fe.TRIANGLE_FAN,Wn.BACK=Fe.BACK,Wn.FRONT=Fe.FRONT,Wn.FRONT_AND_BACK=Fe.FRONT_AND_BACK,Wn.CW=Fe.CW,Wn.CCW=Fe.CCW;var Xn=xn.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=xn.prototype.clone.call(this);return t.fov=this.fov,t.aspect=this.aspect,t.near=this.near,t.far=this.far,t}}),jn=xn.extend({left:-1,right:1,near:-1,far:1,top:1,bottom:-1},{updateProjectionMatrix:function(){this.projectionMatrix.ortho(this.left,this.right,this.bottom,this.top,this.near,this.far)},decomposeProjectionMatrix:function(){var t=this.projectionMatrix.array;this.left=(-1-t[12])/t[0],this.right=(1-t[12])/t[0],this.top=(1-t[13])/t[5],this.bottom=(-1-t[13])/t[5],this.near=-(-1-t[14])/t[10],this.far=-(1-t[14])/t[10]},clone:function(){var t=xn.prototype.clone.call(this);return t.left=this.left,t.right=this.right,t.near=this.near,t.far=this.far,t.top=this.top,t.bottom=this.bottom,t}}),qn={get:gt +},Yn="\n@export clay.standard.chunk.varying\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\n#if defined(PARALLAXOCCLUSIONMAP_ENABLED) || defined(NORMALMAP_ENABLED)\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\n#endif\n#if defined(AOMAP_ENABLED)\nvarying vec2 v_Texcoord2;\n#endif\n#ifdef VERTEX_COLOR\nvarying vec4 v_Color;\n#endif\n@end\n@export clay.standard.chunk.light_header\n#ifdef AMBIENT_LIGHT_COUNT\n@import clay.header.ambient_light\n#endif\n#ifdef AMBIENT_SH_LIGHT_COUNT\n@import clay.header.ambient_sh_light\n#endif\n#ifdef AMBIENT_CUBEMAP_LIGHT_COUNT\n@import clay.header.ambient_cubemap_light\n#endif\n#ifdef POINT_LIGHT_COUNT\n@import clay.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_COUNT\n@import clay.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_COUNT\n@import clay.header.spot_light\n#endif\n@end\n@export clay.standard.vertex\n#define SHADER_NAME standard\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n#if defined(AOMAP_ENABLED)\nattribute vec2 texcoord2 : TEXCOORD_1;\n#endif\nattribute vec3 normal : NORMAL;\nattribute vec4 tangent : TANGENT;\n#ifdef VERTEX_COLOR\nattribute vec4 a_Color : COLOR;\n#endif\nattribute vec3 barycentric;\n@import clay.standard.chunk.varying\n@import clay.chunk.skinning_header\nvoid main()\n{\n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\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 gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n v_Barycentric = barycentric;\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\n#if defined(PARALLAXOCCLUSIONMAP_ENABLED) || defined(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#ifdef VERTEX_COLOR\n v_Color = a_Color;\n#endif\n#if defined(AOMAP_ENABLED)\n v_Texcoord2 = texcoord2;\n#endif\n}\n@end\n@export clay.standard.fragment\n#define PI 3.14159265358979\n#define GLOSSINESS_CHANNEL 0\n#define ROUGHNESS_CHANNEL 0\n#define METALNESS_CHANNEL 1\n#define DIFFUSEMAP_ALPHA_ALPHA\n@import clay.standard.chunk.varying\nuniform mat4 viewInverse : VIEWINVERSE;\n#ifdef NORMALMAP_ENABLED\nuniform sampler2D normalMap;\n#endif\n#ifdef DIFFUSEMAP_ENABLED\nuniform sampler2D diffuseMap;\n#endif\n#ifdef SPECULARMAP_ENABLED\nuniform sampler2D specularMap;\n#endif\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 GLOSSINESSMAP_ENABLED\nuniform sampler2D glossinessMap;\n #endif\n#endif\n#ifdef METALNESSMAP_ENABLED\nuniform sampler2D metalnessMap;\n#endif\n#ifdef ENVIRONMENTMAP_ENABLED\nuniform samplerCube environmentMap;\n #ifdef PARALLAX_CORRECTED\nuniform vec3 environmentBoxMin;\nuniform vec3 environmentBoxMax;\n #endif\n#endif\n#ifdef BRDFLOOKUP_ENABLED\nuniform sampler2D brdfLookup;\n#endif\n#ifdef EMISSIVEMAP_ENABLED\nuniform sampler2D emissiveMap;\n#endif\n#ifdef SSAOMAP_ENABLED\nuniform sampler2D ssaoMap;\nuniform vec4 viewport : VIEWPORT;\n#endif\n#ifdef AOMAP_ENABLED\nuniform sampler2D aoMap;\nuniform float aoIntensity;\n#endif\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\n#ifdef ALPHA_TEST\nuniform float alphaCutoff: 0.9;\n#endif\n#ifdef USE_METALNESS\nuniform float metalness : 0.0;\n#else\nuniform vec3 specularColor : [0.1, 0.1, 0.1];\n#endif\nuniform vec3 emission : [0.0, 0.0, 0.0];\nuniform float emissionIntensity: 1;\nuniform float lineWidth : 0.0;\nuniform vec4 lineColor : [0.0, 0.0, 0.0, 0.6];\n#ifdef ENVIRONMENTMAP_PREFILTER\nuniform float maxMipmapLevel: 5;\n#endif\n@import clay.standard.chunk.light_header\n@import clay.util.calculate_attenuation\n@import clay.util.edge_factor\n@import clay.util.rgbm\n@import clay.util.srgb\n@import clay.plugin.compute_shadow_map\n@import clay.util.parallax_correct\n@import clay.util.ACES\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}\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}\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#ifdef PARALLAXOCCLUSIONMAP_ENABLED\nuniform float parallaxOcclusionScale : 0.02;\nuniform float parallaxMaxLayers : 20;\nuniform float parallaxMinLayers : 5;\nuniform sampler2D parallaxOcclusionMap;\nmat3 transpose(in mat3 inMat)\n{\n vec3 i0 = inMat[0];\n vec3 i1 = inMat[1];\n vec3 i2 = inMat[2];\n return mat3(\n vec3(i0.x, i1.x, i2.x),\n vec3(i0.y, i1.y, i2.y),\n vec3(i0.z, i1.z, i2.z)\n );\n}\nvec2 parallaxUv(vec2 uv, vec3 viewDir)\n{\n float numLayers = mix(parallaxMaxLayers, parallaxMinLayers, abs(dot(vec3(0.0, 0.0, 1.0), viewDir)));\n float layerHeight = 1.0 / numLayers;\n float curLayerHeight = 0.0;\n vec2 deltaUv = viewDir.xy * parallaxOcclusionScale / (viewDir.z * numLayers);\n vec2 curUv = uv;\n float height = 1.0 - texture2D(parallaxOcclusionMap, curUv).r;\n for (int i = 0; i < 30; i++) {\n curLayerHeight += layerHeight;\n curUv -= deltaUv;\n height = 1.0 - texture2D(parallaxOcclusionMap, curUv).r;\n if (height < curLayerHeight) {\n break;\n }\n }\n vec2 prevUv = curUv + deltaUv;\n float next = height - curLayerHeight;\n float prev = 1.0 - texture2D(parallaxOcclusionMap, prevUv).r - curLayerHeight + layerHeight;\n return mix(curUv, prevUv, next / (next - prev));\n}\n#endif\nvoid main() {\n vec4 albedoColor = vec4(color, alpha);\n#ifdef VERTEX_COLOR\n albedoColor *= v_Color;\n#endif\n#ifdef SRGB_DECODE\n albedoColor = sRGBToLinear(albedoColor);\n#endif\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = normalize(eyePos - v_WorldPosition);\n vec2 uv = v_Texcoord;\n#if defined(PARALLAXOCCLUSIONMAP_ENABLED) || defined(NORMALMAP_ENABLED)\n mat3 tbn = mat3(v_Tangent, v_Bitangent, v_Normal);\n#endif\n#ifdef PARALLAXOCCLUSIONMAP_ENABLED\n uv = parallaxUv(v_Texcoord, normalize(transpose(tbn) * -V));\n#endif\n#ifdef DIFFUSEMAP_ENABLED\n vec4 texel = texture2D(diffuseMap, uv);\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#ifdef USE_METALNESS\n float m = metalness;\n #ifdef METALNESSMAP_ENABLED\n float m2 = texture2D(metalnessMap, uv)[METALNESS_CHANNEL];\n m = clamp(m2 + (m - 0.5) * 2.0, 0.0, 1.0);\n #endif\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#ifdef USE_ROUGHNESS\n float g = clamp(1.0 - roughness, 0.0, 1.0);\n #ifdef ROUGHNESSMAP_ENABLED\n float g2 = 1.0 - texture2D(roughnessMap, uv)[ROUGHNESS_CHANNEL];\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\n #endif\n#else\n float g = glossiness;\n #ifdef GLOSSINESSMAP_ENABLED\n float g2 = texture2D(glossinessMap, uv)[GLOSSINESS_CHANNEL];\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\n #endif\n#endif\n#ifdef SPECULARMAP_ENABLED\n spec *= sRGBToLinear(texture2D(specularMap, uv)).rgb;\n#endif\n vec3 N = v_Normal;\n#ifdef DOUBLE_SIDED\n if (dot(N, V) < 0.0) {\n N = -N;\n }\n#endif\n#ifdef NORMALMAP_ENABLED\n if (dot(v_Tangent, v_Tangent) > 0.0) {\n vec3 normalTexel = texture2D(normalMap, uv).xyz;\n if (dot(normalTexel, normalTexel) > 0.0) { N = normalTexel * 2.0 - 1.0;\n tbn[1] = -tbn[1];\n N = normalize(tbn * N);\n }\n }\n#endif\n vec3 diffuseTerm = vec3(0.0, 0.0, 0.0);\n vec3 specularTerm = vec3(0.0, 0.0, 0.0);\n float ndv = clamp(dot(N, V), 0.0, 1.0);\n vec3 fresnelTerm = F_Schlick(ndv, spec);\n#ifdef AMBIENT_LIGHT_COUNT\n for(int _idx_ = 0; _idx_ < AMBIENT_LIGHT_COUNT; _idx_++)\n {{\n diffuseTerm += ambientLightColor[_idx_];\n }}\n#endif\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#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 vec3 lightPosition = pointLightPosition[_idx_];\n vec3 lc = pointLightColor[_idx_];\n float range = pointLightRange[_idx_];\n vec3 L = lightPosition - v_WorldPosition;\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 float shadowContrib = 1.0;\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribsPoint[_idx_];\n }\n#endif\n vec3 li = lc * ndl * attenuation * shadowContrib;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\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 _idx_ = 0; _idx_ < DIRECTIONAL_LIGHT_COUNT; _idx_++)\n {{\n vec3 L = -normalize(directionalLightDirection[_idx_]);\n vec3 lc = directionalLightColor[_idx_];\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 shadowContrib = 1.0;\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n if(shadowEnabled)\n {\n shadowContrib = shadowContribsDir[_idx_];\n }\n#endif\n vec3 li = lc * ndl * shadowContrib;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }}\n#endif\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 vec3 L = lightPosition - v_WorldPosition;\n float dist = length(L);\n float attenuation = lightAttenuation(dist, range);\n L /= dist;\n float c = dot(spotLightDirection, L);\n float falloff;\n falloff = clamp((c - a) /(b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\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 shadowContrib = 1.0;\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\n if (shadowEnabled)\n {\n shadowContrib = shadowContribsSpot[i];\n }\n#endif\n vec3 li = lc * attenuation * (1.0 - falloff) * shadowContrib * ndl;\n diffuseTerm += li;\n specularTerm += li * fresnelTerm * D_Phong(g, ndh);\n }\n#endif\n vec4 outColor = albedoColor;\n outColor.rgb *= max(diffuseTerm, vec3(0.0));\n outColor.rgb += max(specularTerm, vec3(0.0));\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), 8.12);\n outColor.rgb += ambientCubemapLightColor[_idx_] * envTexel2 * envWeight2;\n }}\n#endif\n#ifdef ENVIRONMENTMAP_ENABLED\n vec3 envWeight = g * fresnelTerm;\n vec3 L = reflect(-V, N);\n #ifdef PARALLAX_CORRECTED\n L = parallaxCorrect(L, v_WorldPosition, environmentBoxMin, environmentBoxMax);\n#endif\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 #ifdef BRDFLOOKUP_ENABLED\n vec2 brdfParam = texture2D(brdfLookup, vec2(rough, ndv)).xy;\n envWeight = spec * brdfParam.x + brdfParam.y;\n #endif\n #else\n vec3 envTexel = textureCube(environmentMap, L).xyz;\n #endif\n outColor.rgb += envTexel * envWeight;\n#endif\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#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 outColor.rgb *= aoFactor;\n vec3 lEmission = emission;\n#ifdef EMISSIVEMAP_ENABLED\n lEmission *= texture2D(emissiveMap, uv).rgb;\n#endif\n outColor.rgb += lEmission * emissionIntensity;\n if(lineWidth > 0.)\n {\n outColor.rgb = mix(outColor.rgb, lineColor.rgb, (1.0 - edgeFactor(lineWidth)) * lineColor.a);\n }\n#ifdef ALPHA_TEST\n if (outColor.a < alphaCutoff) {\n discard;\n }\n#endif\n#ifdef TONEMAPPING\n outColor.rgb = ACESToneMapping(outColor.rgb);\n#endif\n#ifdef SRGB_ENCODE\n outColor = linearTosRGB(outColor);\n#endif\n gl_FragColor = encodeHDR(outColor);\n}\n@end\n@export clay.standardMR.vertex\n@import clay.standard.vertex\n@end\n@export clay.standardMR.fragment\n#define USE_METALNESS\n#define USE_ROUGHNESS\n@import clay.standard.fragment\n@end";$.import(Yn);var Kn,Zn=["diffuseMap","normalMap","roughnessMap","metalnessMap","emissiveMap","environmentMap","brdfLookup","ssaoMap","aoMap"],Jn=["color","emission","emissionIntensity","alpha","roughness","metalness","uvRepeat","uvOffset","aoIntensity","alphaCutoff"],Qn=["linear","encodeRGBM","decodeRGBM","doubleSided","alphaTest","roughnessChannel","metalnessChannel","environmentMapPrefiltered"],$n={roughnessChannel:"ROUGHNESS_CHANNEL",metalnessChannel:"METALNESS_CHANNEL"},ti={linear:"SRGB_DECODE",encodeRGBM:"RGBM_ENCODE",decodeRGBM:"RGBM_DECODE",doubleSided:"DOUBLE_SIDED",alphaTest:"ALPHA_TEST",environmentMapPrefiltered:"ENVIRONMENTMAP_PREFILTER"},ei=pr.extend(function(){return Kn||(Kn=new $($.source("clay.standardMR.vertex"),$.source("clay.standardMR.fragment"))),{shader:Kn}},function(t){Me.extend(this,t),Me.defaults(this,{color:[1,1,1],emission:[0,0,0],emissionIntensity:0,roughness:.5,metalness:0,alpha:1,alphaTest:!1,alphaCutoff:.9,doubleSided:!1,diffuseMap:null,normalMap:null,roughnessMap:null,metalnessMap:null,emissiveMap:null,environmentMap:null,environmentBox:null,brdfLookup:null,ssaoMap:null,aoMap:null,uvRepeat:[1,1],uvOffset:[0,0],aoIntensity:1,environmentMapPrefiltered:!1,linear:!1,encodeRGBM:!1,decodeRGBM:!1,roughnessChannel:0,metalnessChannel:1})},{clone:function(){var t=new ei({name:this.name});return Zn.forEach(function(e){this[e]&&(t[e]=this[e])},this),Jn.concat(Qn).forEach(function(e){t[e]=this[e]},this),t}});Jn.forEach(function(t){Object.defineProperty(ei.prototype,t,{get:function(){return this.get(t)},set:function(e){this.setUniform(t,e)}})}),Zn.forEach(function(t){Object.defineProperty(ei.prototype,t,{get:function(){return this.get(t)},set:function(e){this.setUniform(t,e)}})}),Qn.forEach(function(t){var e="_"+t;Object.defineProperty(ei.prototype,t,{get:function(){return this[e]},set:function(r){if(this[e]=r,t in $n){var n=$n[t];this.define("fragment",n,r)}else{var n=ti[t];r?this.define("fragment",n):this.undefine("fragment",n)}}})}),Object.defineProperty(ei.prototype,"environmentBox",{get:function(){var t=this._environmentBox;return t&&(t.min.setArray(this.get("environmentBoxMin")),t.max.setArray(this.get("environmentBoxMax"))),t},set:function(t){this._environmentBox=t;var e=this.uniforms=this.uniforms||{};e.environmentBoxMin=e.environmentBoxMin||{value:null},e.environmentBoxMax=e.environmentBoxMax||{value:null},t&&(this.setUniform("environmentBoxMin",t.min.array),this.setUniform("environmentBoxMax",t.max.array)),t?this.define("fragment","PARALLAX_CORRECTED"):this.undefine("fragment","PARALLAX_CORRECTED")}});var ri=Re.extend({name:"",index:-1,node:null}),ni=me.quat,ii=me.vec3,ai=me.mat4,oi=Re.extend(function(){return{relativeRootNode:null,name:"",joints:[],_clips:[],_invBindPoseMatricesArray:null,_jointMatricesSubArrays:[],_skinMatricesArray:null,_skinMatricesSubArrays:[],_subSkinMatricesArray:{}}},{addClip:function(t,e){for(var r=0;r0&&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=ai.create();return function(){this._invBindPoseMatricesArray=new Float32Array(16*this.joints.length),this._skinMatricesArray=new Float32Array(16*this.joints.length);for(var e=0;e 0.)\n {\n gl_FragColor.rgb = mix(gl_FragColor.rgb, lineColor.rgb, (1.0 - edgeFactor(lineWidth)) * lineColor.a);\n }\n#ifdef ALPHA_TEST\n if (gl_FragColor.a < alphaCutoff) {\n discard;\n }\n#endif\n#ifdef TONEMAPPING\n gl_FragColor.rgb = ACESToneMapping(gl_FragColor.rgb);\n#endif\n#ifdef SRGB_ENCODE\n gl_FragColor = linearTosRGB(gl_FragColor);\n#endif\n gl_FragColor = encodeHDR(gl_FragColor);\n}\n@end"), +$.import("\n@export clay.lambert.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\nuniform vec2 uvRepeat : [1.0, 1.0];\nuniform vec2 uvOffset : [0.0, 0.0];\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 normal : NORMAL;\nattribute vec3 barycentric;\n#ifdef VERTEX_COLOR\nattribute vec4 a_Color : COLOR;\nvarying vec4 v_Color;\n#endif\n@import clay.chunk.skinning_header\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nvarying vec3 v_Barycentric;\nvoid main()\n{\n vec3 skinnedPosition = position;\n vec3 skinnedNormal = normal;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n skinnedNormal = (skinMatrixWS * vec4(normal, 0.0)).xyz;\n#endif\n gl_Position = worldViewProjection * vec4( skinnedPosition, 1.0 );\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 v_Barycentric = barycentric;\n#ifdef VERTEX_COLOR\n v_Color = a_Color;\n#endif\n}\n@end\n@export clay.lambert.fragment\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nuniform sampler2D diffuseMap;\nuniform sampler2D alphaMap;\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform vec3 emission : [0.0, 0.0, 0.0];\nuniform float alpha : 1.0;\n#ifdef ALPHA_TEST\nuniform float alphaCutoff: 0.9;\n#endif\nuniform float lineWidth : 0.0;\nuniform vec4 lineColor : [0.0, 0.0, 0.0, 0.6];\nvarying vec3 v_Barycentric;\n#ifdef VERTEX_COLOR\nvarying vec4 v_Color;\n#endif\n#ifdef AMBIENT_LIGHT_COUNT\n@import clay.header.ambient_light\n#endif\n#ifdef AMBIENT_SH_LIGHT_COUNT\n@import clay.header.ambient_sh_light\n#endif\n#ifdef POINT_LIGHT_COUNT\n@import clay.header.point_light\n#endif\n#ifdef DIRECTIONAL_LIGHT_COUNT\n@import clay.header.directional_light\n#endif\n#ifdef SPOT_LIGHT_COUNT\n@import clay.header.spot_light\n#endif\n@import clay.util.calculate_attenuation\n@import clay.util.edge_factor\n@import clay.util.rgbm\n@import clay.plugin.compute_shadow_map\n@import clay.util.ACES\nvoid main()\n{\n gl_FragColor = vec4(color, alpha);\n#ifdef VERTEX_COLOR\n gl_FragColor *= v_Color;\n#endif\n#ifdef SRGB_DECODE\n gl_FragColor = sRGBToLinear(gl_FragColor);\n#endif\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 vec3 diffuseColor = vec3(0.0, 0.0, 0.0);\n#ifdef AMBIENT_LIGHT_COUNT\n for(int _idx_ = 0; _idx_ < AMBIENT_LIGHT_COUNT; _idx_++)\n {\n diffuseColor += ambientLightColor[_idx_];\n }\n#endif\n#ifdef AMBIENT_SH_LIGHT_COUNT\n for(int _idx_ = 0; _idx_ < AMBIENT_SH_LIGHT_COUNT; _idx_++)\n {{\n diffuseColor += calcAmbientSHLight(_idx_, v_Normal) * ambientSHLightColor[_idx_];\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 vec3 lightPosition = pointLightPosition[i];\n vec3 lightColor = pointLightColor[i];\n float range = pointLightRange[i];\n vec3 lightDirection = lightPosition - v_WorldPosition;\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range);\n lightDirection /= dist;\n float ndl = dot( v_Normal, lightDirection );\n float shadowContrib = 1.0;\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribsPoint[i];\n }\n#endif\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 float ndl = dot(v_Normal, normalize(lightDirection));\n float shadowContrib = 1.0;\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n if( shadowEnabled )\n {\n shadowContrib = shadowContribsDir[i];\n }\n#endif\n diffuseColor += lightColor * clamp(ndl, 0.0, 1.0) * shadowContrib;\n }\n#endif\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 vec3 lightDirection = lightPosition - v_WorldPosition;\n float dist = length(lightDirection);\n float attenuation = lightAttenuation(dist, range);\n lightDirection /= dist;\n float c = dot(spotLightDirection, lightDirection);\n float falloff;\n falloff = clamp((c - a) /( b - a), 0.0, 1.0);\n falloff = pow(falloff, falloffFactor);\n float ndl = dot(v_Normal, lightDirection);\n ndl = clamp(ndl, 0.0, 1.0);\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 gl_FragColor.rgb *= diffuseColor;\n gl_FragColor.rgb += emission;\n if(lineWidth > 0.)\n {\n gl_FragColor.rgb = mix(gl_FragColor.rgb, lineColor.rgb, (1.0 - edgeFactor(lineWidth)) * lineColor.a);\n }\n#ifdef ALPHA_TEST\n if (gl_FragColor.a < alphaCutoff) {\n discard;\n }\n#endif\n#ifdef TONEMAPPING\n gl_FragColor.rgb = ACESToneMapping(gl_FragColor.rgb);\n#endif\n#ifdef SRGB_ENCODE\n gl_FragColor = linearTosRGB(gl_FragColor);\n#endif\n gl_FragColor = encodeHDR(gl_FragColor);\n}\n@end"),$.import(Yn),$.import("@export clay.wireframe.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world : WORLD;\nattribute vec3 position : POSITION;\nattribute vec3 barycentric;\n@import clay.chunk.skinning_header\nvarying vec3 v_Barycentric;\nvoid main()\n{\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0 );\n v_Barycentric = barycentric;\n}\n@end\n@export clay.wireframe.fragment\nuniform vec3 color : [0.0, 0.0, 0.0];\nuniform float alpha : 1.0;\nuniform float lineWidth : 1.0;\nvarying vec3 v_Barycentric;\n@import clay.util.edge_factor\nvoid main()\n{\n gl_FragColor.rgb = color;\n gl_FragColor.a = (1.0-edgeFactor(lineWidth)) * alpha;\n}\n@end"),$.import(ui),$.import(Vr),Vn.template("clay.basic",$.source("clay.basic.vertex"),$.source("clay.basic.fragment")),Vn.template("clay.lambert",$.source("clay.lambert.vertex"),$.source("clay.lambert.fragment")),Vn.template("clay.wireframe",$.source("clay.wireframe.vertex"),$.source("clay.wireframe.fragment")),Vn.template("clay.skybox",$.source("clay.skybox.vertex"),$.source("clay.skybox.fragment")),Vn.template("clay.prez",$.source("clay.prez.vertex"),$.source("clay.prez.fragment")),Vn.template("clay.standard",$.source("clay.standard.vertex"),$.source("clay.standard.fragment")),Vn.template("clay.standardMR",$.source("clay.standardMR.vertex"),$.source("clay.standardMR.fragment"));var li={NORMAL:"normal",POSITION:"position",TEXCOORD_0:"texcoord0",TEXCOORD_1:"texcoord1",WEIGHTS_0:"weight",JOINTS_0:"joint",COLOR_0:"color"},ci={5120:Ge.Int8Array,5121:Ge.Uint8Array,5122:Ge.Int16Array,5123:Ge.Uint16Array,5125:Ge.Uint32Array,5126:Ge.Float32Array},hi={SCALAR:1,VEC2:2,VEC3:3,VEC4:4,MAT2:4,MAT3:9,MAT4:16},fi=Re.extend({rootNode:null,rootPath:null,textureRootPath:null,bufferRootPath:null,shader:"clay.standard",useStandardMaterial:!1,includeCamera:!0,includeAnimation:!0,includeMesh:!0,includeTexture:!0,crossOrigin:"",textureFlipY:!1,textureConvertToPOT:!1,shaderLibrary:null},function(){this.shaderLibrary||(this.shaderLibrary=Vn.createLibrary())},{load:function(t){var e=this,r=t.endsWith(".glb");null==this.rootPath&&(this.rootPath=t.slice(0,t.lastIndexOf("/"))),qn.get({url:t,onprogress:function(t,r,n){e.trigger("progress",t,r,n)},onerror:function(t){e.trigger("error",t)},responseType:r?"arraybuffer":"text",onload:function(t){r?e.parseBinary(t):e.parse(JSON.parse(t))}})},parseBinary:function(t){var e=new Uint32Array(t,0,4);if(1179937895!==e[0])return void this.trigger("error","Invalid glTF binary format: Invalid header");if(e[0]<2)return void this.trigger("error","Only glTF2.0 is supported.");for(var r,n=new DataView(t,12),i=[],a=0;a=r.COLOR_ATTACHMENT0&&a<=r.COLOR_ATTACHMENT0+8&&i.push(a);n.drawBuffersEXT(i)}}this.trigger("beforerender",this,t);var o=this.clearDepth?r.DEPTH_BUFFER_BIT:0;if(r.depthMask(!0),this.clearColor){o|=r.COLOR_BUFFER_BIT,r.colorMask(!0,!0,!0,!0);var s=this.clearColor;Array.isArray(s)&&r.clearColor(s[0],s[1],s[2],s[3])}r.clear(o),this.blendWithPrevious?(r.enable(r.BLEND),this.material.transparent=!0):(r.disable(r.BLEND),this.material.transparent=!1),this.renderQuad(t),this.trigger("afterrender",this,t),e&&this.unbind(t,e)},renderQuad:function(t){bi.material=this.material,t.renderPass([bi],Ai)},dispose:function(t){}});$.import(ui);var wi=Wn.extend(function(){var t=new $({vertex:$.source("clay.skybox.vertex"),fragment:$.source("clay.skybox.fragment")}),e=new pr({shader:t,depthMask:!1});return{scene:null,geometry:new Dn,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(),t.skybox=this,this.scene=t,t.on("beforerender",this._beforeRenderScene,this)},detachScene:function(){this.scene&&(this.scene.off("beforerender",this._beforeRenderScene),this.scene.skybox=null),this.scene=null},dispose:function(t){this.detachScene(),this.geometry.dispose(t)},setEnvironmentMap:function(t){this.material.set("environmentMap",t)},getEnvironmentMap:function(){return this.material.get("environmentMap")},_beforeRenderScene:function(t,e,r){this.renderSkybox(t,r)},renderSkybox:function(t,e){this.position.copy(e.getWorldPosition()),this.update(),t.gl.disable(t.gl.BLEND),this.material.get("lod")>0?this.material.define("fragment","LOD"):this.material.undefine("fragment","LOD"),t.renderPass([this],e)}}),Ci=["px","nx","py","ny","pz","nz"],Mi=Re.extend(function(){var t={position:new ze,far:1e3,near:.1,texture:null,shadowMapPass:null},e=t._cameras={px:new Xn({fov:90}),nx:new Xn({fov:90}),py:new Xn({fov:90}),ny:new Xn({fov:90}),pz:new Xn({fov:90}),nz:new Xn({fov:90})};return e.px.lookAt(ze.POSITIVE_X,ze.NEGATIVE_Y),e.nx.lookAt(ze.NEGATIVE_X,ze.NEGATIVE_Y),e.py.lookAt(ze.POSITIVE_Y,ze.POSITIVE_Z),e.ny.lookAt(ze.NEGATIVE_Y,ze.NEGATIVE_Z),e.pz.lookAt(ze.POSITIVE_Z,ze.NEGATIVE_Y),e.nz.lookAt(ze.NEGATIVE_Z,ze.NEGATIVE_Y), +t._frameBuffer=new Ti,t},{getCamera:function(t){return this._cameras[t]},render:function(t,e,r){var n=t.gl;r||e.update();for(var i=this.texture.width,a=2*Math.atan(i/(i-.5))/Math.PI*180,o=0;o<6;o++){var s=Ci[o],u=this._cameras[s];if(ze.copy(u.position,this.position),u.far=this.far,u.near=this.near,u.fov=a,this.shadowMapPass){u.update();var l=e.getBoundingBox();l.applyTransform(u.viewMatrix),e.viewBoundingBoxLastFrame.copy(l),this.shadowMapPass.render(t,e,u,!0)}this._frameBuffer.attach(this.texture,n.COLOR_ATTACHMENT0,n.TEXTURE_CUBE_MAP_POSITIVE_X+o),this._frameBuffer.bind(t),t.render(e,u,!0),this._frameBuffer.unbind(t)}},dispose:function(t){this._frameBuffer.dispose(t)}});$.import("@export clay.skydome.vertex\n#define SHADER_NAME skydome\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nattribute vec2 texcoord : TEXCOORD_0;\nattribute vec3 position : POSITION;\nvarying vec2 v_Texcoord;\nvoid main()\n{\n gl_Position = worldViewProjection * vec4(position, 1.0);\n v_Texcoord = texcoord;\n}\n@end\n@export clay.skydome.fragment\nuniform sampler2D environmentMap;\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\n@import clay.util.srgb\n@import clay.util.ACES\nvoid main()\n{\n vec4 texel = decodeHDR(texture2D(environmentMap, v_Texcoord));\n#ifdef SRGB_DECODE\n texel = sRGBToLinear(texel);\n#endif\n#ifdef TONEMAPPING\n texel.rgb = ACESToneMapping(texel.rgb);\n#endif\n#ifdef SRGB_ENCODE\n texel = linearTosRGB(texel);\n#endif\n gl_FragColor = encodeHDR(vec4(texel.rgb, 1.0));\n}\n@end");var Ri=Wn.extend(function(){var t=new $($.source("clay.skydome.vertex"),$.source("clay.skydome.fragment")),e=new pr({shader:t,depthMask:!1});return{scene:null,geometry:new On({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(),t.skydome=this,this.scene=t,t.on("beforerender",this._beforeRenderScene,this)},detachScene:function(){this.scene&&(this.scene.off("beforerender",this._beforeRenderScene),this.scene.skydome=null),this.scene=null},_beforeRenderScene:function(t,e,r){this.position.copy(r.getWorldPosition()),this.update(),t.renderPass([this],r)},setEnvironmentMap:function(t){this.material.set("environmentMap",t)},getEnvironmentMap:function(){return this.material.get("environmentMap")},dispose:function(t){this.detachScene(),this.geometry.dispose(t)}}),Li=yt("DXT1"),Ni=yt("DXT3"),Pi=yt("DXT5"),Ii={parse:function(t,e){var r=new Int32Array(t,0,31);if(542327876!==r[0])return null;if(4&!r(20))return null;var n,i,a=r(21),o=r[4],s=r[3],u=512&r[28],l=131072&r[2];switch(a){case Li:n=8,i=_r.COMPRESSED_RGB_S3TC_DXT1_EXT;break;case Ni:n=16,i=_r.COMPRESSED_RGBA_S3TC_DXT3_EXT;break;case Pi:n=16,i=_r.COMPRESSED_RGBA_S3TC_DXT5_EXT;break;default:return null}var c=r[1]+4,h=u?6:1,f=1;l&&(f=Math.max(1,r[7]));for(var d=[],p=0;p=i)){a+=2;for(var o="";a20)return console.warn("Given image is not a height map"),t}var f,d,p,m;u%(4*n)==0?(f=o.data[u],p=o.data[u+4]):u%(4*n)==4*(n-1)?(f=o.data[u-4],p=o.data[u]):(f=o.data[u-4],p=o.data[u+4]),u<4*n?(d=o.data[u],m=o.data[u+4*n]):u>n*(i-1)*4?(d=o.data[u-4*n],m=o.data[u]):(d=o.data[u-4*n],m=o.data[u+4*n]),s.data[u]=f-p+127,s.data[u+1]=d-m+127,s.data[u+2]=255,s.data[u+3]=255}return a.putImageData(s,0,0),r},isHeightImage:function(t,e,r){if(!t||!t.width||!t.height)return!1;var n=document.createElement("canvas"),i=n.getContext("2d"),a=e||32;r=r||20,n.width=n.height=a,i.drawImage(t,0,0,a,a);for(var o=i.getImageData(0,0,a,a),s=0;sr)return!1}return!0},_fetchTexture:function(t,e,r){qn.get({url:t,responseType:"arraybuffer",onload:e,onerror:r})},createChessboard:function(t,e,r,n){t=t||512,e=e||64,r=r||"black",n=n||"white";var i=Math.ceil(t/e),a=document.createElement("canvas");a.width=t,a.height=t;var o=a.getContext("2d");o.fillStyle=n,o.fillRect(0,0,t,t),o.fillStyle=r;for(var s=0;s 0.999 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);\n vec3 tangentX = normalize(cross(N, upVector));\n vec3 tangentZ = cross(N, tangentX);\n return normalize(tangentX * H.x + N * H.y + tangentZ * H.z);\n}\nvoid main() {\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = normalize(v_WorldPosition - eyePos);\n vec3 N = V;\n vec3 prefilteredColor = vec3(0.0);\n float totalWeight = 0.0;\n float fMaxSampleNumber = float(SAMPLE_NUMBER);\n for (int i = 0; i < SAMPLE_NUMBER; i++) {\n vec3 H = importanceSampleNormal(float(i) / fMaxSampleNumber, roughness, N);\n vec3 L = reflect(-V, H);\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 gl_FragColor = encodeHDR(vec4(prefilteredColor / totalWeight, 1.0));\n}\n"})});h.set("normalDistribution",n),r.encodeRGBM&&h.define("fragment","RGBM_ENCODE"),r.decodeRGBM&&h.define("fragment","RGBM_DECODE");var f,d=new Sn;if("texture2D"===e.textureType){var p=new kn({width:a,height:o,type:s===_r.FLOAT?_r.HALF_FLOAT:s});Fi.panoramaToCubeMap(t,e,p,{encodeRGBM:r.decodeRGBM}),e=p}f=new wi({scene:d,material:h}),f.material.set("environmentMap",e);var m=new Mi({texture:u});r.encodeRGBM&&(s=u.type=_r.UNSIGNED_BYTE);for(var _=new yr({width:a,height:o,type:s}),g=new Ti({depthBuffer:!1}),v=Ge[s===_r.UNSIGNED_BYTE?"Uint8Array":"Float32Array"],y=0;y 0.999 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);\n vec3 tangentX = normalize(cross(N, upVector));\n vec3 tangentZ = cross(N, tangentX);\n return normalize(tangentX * H.x + N * H.y + tangentZ * H.z);\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}\nvoid main() {\n vec2 uv = gl_FragCoord.xy / viewportSize;\n float NoV = uv.x;\n float roughness = uv.y;\n vec3 V;\n V.x = sqrt(1.0 - NoV * NoV);\n V.y = 0.0;\n V.z = NoV;\n float A = 0.0;\n float B = 0.0;\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 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 gl_FragColor = vec4(vec2(A, B) / fSampleNumber, 0.0, 1.0);\n}\n"}),i=new yr({width:512,height:256,type:_r.HALF_FLOAT,wrapS:_r.CLAMP_TO_EDGE,wrapT:_r.CLAMP_TO_EDGE,minFilter:_r.NEAREST,magFilter:_r.NEAREST,useMipmap:!1});return n.setUniform("normalDistribution",e),n.setUniform("viewportSize",[512,256]),n.attachOutput(i),n.render(t,r),r.dispose(t),i},ki.generateNormalDistribution=function(t,e){for(var t=t||256,e=e||1024,r=new yr({width:t,height:e,type:_r.FLOAT,minFilter:_r.NEAREST,magFilter:_r.NEAREST,wrapS:_r.CLAMP_TO_EDGE,wrapT:_r.CLAMP_TO_EDGE,useMipmap:!1}),n=new Float32Array(e*t*4),i=[],a=0;a>>16)>>>0;l=((1431655765&l)<<1|(2863311530&l)>>>1)>>>0,l=((858993459&l)<<2|(3435973836&l)>>>2)>>>0,l=((252645135&l)<<4|(4042322160&l)>>>4)>>>0,l=(((16711935&l)<<8|(4278255360&l)>>>8)>>>0)/4294967296;var c=Math.sqrt((1-l)/(1+(s*s-1)*l));i[u]=c}for(var u=0;u 0.0) {\n if (texture2D(alphaMap, v_Texcoord).a <= alphaCutoff) {\n discard;\n }\n }\n#ifdef USE_VSM\n depth = depth * 0.5 + 0.5;\n float moment1 = depth;\n float moment2 = depth * depth;\n float dx = dFdx(depth);\n float dy = dFdy(depth);\n moment2 += 0.25*(dx*dx+dy*dy);\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 gl_FragColor = encodeFloat(depth * 0.5 + 0.5);\n#endif\n}\n@end\n@export clay.sm.debug_depth\nuniform sampler2D depthMap;\nvarying vec2 v_Texcoord;\n@import clay.util.decode_float\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@end\n@export clay.sm.distance.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 world : WORLD;\nattribute vec3 position : POSITION;\n@import clay.chunk.skinning_header\nvarying vec3 v_WorldPosition;\nvoid main (){\n vec3 skinnedPosition = position;\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\n#endif\n gl_Position = worldViewProjection * vec4(skinnedPosition , 1.0);\n v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n}\n@end\n@export clay.sm.distance.fragment\nuniform vec3 lightPosition;\nuniform float range : 100;\nvarying vec3 v_WorldPosition;\n@import clay.util.encode_float\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@export clay.plugin.shadow_map_common\n@import clay.util.decode_float\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}\nfloat pcf(sampler2D map, vec2 uv, float z, float textureSize, vec2 scale) {\n float shadowContrib = tapShadowMap(map, uv, z);\n vec2 offset = vec2(1.0 / textureSize) * scale;\n#ifdef PCF_KERNEL_SIZE\n for (int _idx_ = 0; _idx_ < PCF_KERNEL_SIZE; _idx_++) {{\n shadowContrib += tapShadowMap(map, uv + offset * pcfKernel[_idx_], z);\n }}\n return shadowContrib / float(PCF_KERNEL_SIZE + 1);\n#else\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 return shadowContrib / 9.0;\n#endif\n}\nfloat pcf(sampler2D map, vec2 uv, float z, float textureSize) {\n return pcf(map, uv, z, textureSize, vec2(1.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 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 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 #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}\nfloat computeShadowContrib(sampler2D map, mat4 lightVPM, vec3 position, float textureSize) {\n return computeShadowContrib(map, lightVPM, position, textureSize, vec2(1.0), vec2(0.0));\n}\nfloat computeShadowContribOmni(samplerCube map, vec3 direction, float range)\n{\n float dist = length(direction);\n vec4 shadowTex = textureCube(map, direction);\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@end\n@export clay.plugin.compute_shadow_map\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT) || defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT) || defined(POINT_LIGHT_SHADOWMAP_COUNT)\n#ifdef SPOT_LIGHT_SHADOWMAP_COUNT\nuniform sampler2D spotLightShadowMaps[SPOT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\nuniform mat4 spotLightMatrices[SPOT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\nuniform float spotLightShadowMapSizes[SPOT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\n#endif\n#ifdef DIRECTIONAL_LIGHT_SHADOWMAP_COUNT\n#if defined(SHADOW_CASCADE)\nuniform sampler2D directionalLightShadowMaps[1]:unconfigurable;\nuniform mat4 directionalLightMatrices[SHADOW_CASCADE]:unconfigurable;\nuniform float directionalLightShadowMapSizes[1]:unconfigurable;\nuniform float shadowCascadeClipsNear[SHADOW_CASCADE]:unconfigurable;\nuniform float shadowCascadeClipsFar[SHADOW_CASCADE]:unconfigurable;\n#else\nuniform sampler2D directionalLightShadowMaps[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\nuniform mat4 directionalLightMatrices[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\nuniform float directionalLightShadowMapSizes[DIRECTIONAL_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\n#endif\n#endif\n#ifdef POINT_LIGHT_SHADOWMAP_COUNT\nuniform samplerCube pointLightShadowMaps[POINT_LIGHT_SHADOWMAP_COUNT]:unconfigurable;\n#endif\nuniform bool shadowEnabled : true;\n#ifdef PCF_KERNEL_SIZE\nuniform vec2 pcfKernel[PCF_KERNEL_SIZE];\n#endif\n@import clay.plugin.shadow_map_common\n#if defined(SPOT_LIGHT_SHADOWMAP_COUNT)\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#endif\n#if defined(DIRECTIONAL_LIGHT_SHADOWMAP_COUNT)\n#ifdef SHADOW_CASCADE\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 float shadowContrib;\n shadowContribs[0] = 1.0;\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#else\nvoid computeShadowOfDirectionalLights(vec3 position, inout float shadowContribs[DIRECTIONAL_LIGHT_COUNT]){\n float shadowContrib;\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#endif\n#if defined(POINT_LIGHT_SHADOWMAP_COUNT)\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#endif\n#endif\n@end");var Yi=Re.extend(function(){return{softShadow:Yi.PCF,shadowBlur:1,lightFrustumBias:"auto",kernelPCF:new Float32Array([1,0,1,1,-1,1,0,1,-1,0,-1,-1,1,-1,0,-1]),precision:"highp",_lastRenderNotCastShadow:!1,_frameBuffer:new Ti,_textures:{},_shadowMapNumber:{POINT_LIGHT:0,DIRECTIONAL_LIGHT:0,SPOT_LIGHT:0},_depthMaterials:{},_distanceMaterials:{},_receivers:[],_lightsCastShadow:[],_lightCameras:{},_lightMaterials:{},_texturePool:new zi}},function(){this._gaussianPassH=new Si({fragment:$.source("clay.compositor.gaussian_blur")}),this._gaussianPassV=new Si({fragment:$.source("clay.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 Si({fragment:$.source("clay.sm.debug_depth")})},{render:function(t,e,r,n){r||(r=e.getMainCamera()),this.trigger("beforerender",this,t,e,r),this._renderShadowPass(t,e,r,n),this.trigger("afterrender",this,t,e,r)},renderDebug:function(t,e){t.saveClear();var r=t.viewport,n=0,i=e||r.width/4,a=i;this.softShadow===Yi.VSM?this._outputDepthPass.material.define("fragment","USE_VSM"):this._outputDepthPass.material.undefine("fragment","USE_VSM");for(var o in this._textures){var s=this._textures[o];t.setViewport(n,0,i*s.width/s.height,a),this._outputDepthPass.setUniform("depthMap",s),this._outputDepthPass.render(t),n+=i*s.width/s.height}t.setViewport(r),t.restoreClear()},_updateReceivers:function(t,e){if(e.receiveShadow?(this._receivers.push(e),e.material.set("shadowEnabled",1),e.material.set("pcfKernel",this.kernelPCF)):e.material.set("shadowEnabled",0),this.softShadow===Yi.VSM)e.material.define("fragment","USE_VSM"),e.material.undefine("fragment","PCF_KERNEL_SIZE");else{e.material.undefine("fragment","USE_VSM");var r=this.kernelPCF;r&&r.length?e.material.define("fragment","PCF_KERNEL_SIZE",r.length/2):e.material.undefine("fragment","PCF_KERNEL_SIZE")}},_update:function(t,e){var r=this;e.traverse(function(e){e.isRenderable()&&r._updateReceivers(t,e)});for(var n=0;n4){console.warn("Support at most 4 cascade");continue}m.shadowCascade>1&&(s=m),this.renderDirectionalLightShadow(t,e,r,m,f,h,c)}else"SPOT_LIGHT"===m.type?this.renderSpotLightShadow(t,e,m,l,u):"POINT_LIGHT"===m.type&&this.renderPointLightShadow(t,e,m,d);this._shadowMapNumber[m.type]++}for(var _ in this._shadowMapNumber)for(var g=this._shadowMapNumber[_],v=_+"_SHADOWMAP_COUNT",p=0;p0?x.define("fragment",v,g):x.isDefined("fragment",v)&&x.undefine("fragment",v))}for(var p=0;p0){var E=c.map(i);if(T.directionalLightShadowMaps={value:c,type:"tv"},T.directionalLightMatrices={value:h,type:"m4v"},T.directionalLightShadowMapSizes={value:E,type:"1fv"},s){var b=f.slice(),A=f.slice();b.pop(),A.shift(),b.reverse(),A.reverse(),h.reverse(),T.shadowCascadeClipsNear={value:b,type:"1fv"},T.shadowCascadeClipsFar={value:A,type:"1fv"}}}if(u.length>0){var S=u.map(i),T=e.shadowUniforms;T.spotLightShadowMaps={value:u,type:"tv"},T.spotLightMatrices={value:l,type:"m4v"},T.spotLightShadowMapSizes={value:S,type:"1fv"}}d.length>0&&(T.pointLightShadowMaps={value:d,type:"tv"})}},renderDirectionalLightShadow:function(){var t=new mn,e=new nr,r=new Qe,n=new nr,i=new nr,a=new nr,o=new nr;return function(s,u,l,c,h,f,d){var p=this._getDepthMaterial(c),m={getMaterial:function(t){return t.shadowDepthMaterial||p},isMaterialChanged:Rt,getUniform:Mt,ifRender:function(t){return t.castShadow},sortCompare:Yr.opaqueSortCompare};if(!u.viewBoundingBoxLastFrame.isFinite()){var _=u.getBoundingBox();u.viewBoundingBoxLastFrame.copy(_).applyTransform(l.viewMatrix)}var g=Math.min(-u.viewBoundingBoxLastFrame.min.z,l.far),v=Math.max(-u.viewBoundingBoxLastFrame.max.z,l.near),y=this._getDirectionalLightCamera(c,u,l),x=a.array;o.copy(y.projectionMatrix),ji.invert(i.array,y.worldTransform.array),ji.multiply(i.array,i.array,l.worldTransform.array),ji.multiply(x,o.array,i.array);for(var T=[],E=l instanceof Xn,b=(l.near+l.far)/(l.near-l.far),A=2*l.near*l.far/(l.near-l.far),S=0;S<=c.shadowCascade;S++){var w=v*Math.pow(g/v,S/c.shadowCascade),C=v+(g-v)*S/c.shadowCascade,M=w*c.cascadeSplitLogFactor+C*(1-c.cascadeSplitLogFactor);T.push(M),h.push(-(-M*b+A)/-M)}var R=this._getTexture(c,c.shadowCascade);d.push(R);var L=s.viewport,N=s.gl;this._frameBuffer.attach(R),this._frameBuffer.bind(s),N.clear(N.COLOR_BUFFER_BIT|N.DEPTH_BUFFER_BIT);for(var S=0;Sf?s>d?p[i>0?"px":"nx"]=!0:p[o>0?"pz":"nz"]=!0:f>d?p[a>0?"py":"ny"]=!0:p[o>0?"pz":"nz"]=!0}for(var r=0;r=0&&x[v]>1e-4&&(Ki.transformMat4(b,y,_[T[v]]),Ki.scaleAndAdd(E,E,b,x[v]));A.set(g,E)}}for(var g=0;g=0){var l="touchend"!==u?i.targetTouches[0]:i.changedTouches[0];"touchstart"===u?u="mousedown":"touchend"===u?u="mouseup":"touchmove"===u&&(u="mousemove"),a=l.clientX-s.left,o=l.clientY-s.top}else a=i.clientX-s.left,o=i.clientY-s.top;var c,h=t.pick(a,o);if("DOMMouseScroll"!==u&&"mousewheel"!==u||(c=i.wheelDelta?i.wheelDelta/120:-(i.detail||0)/3),h){if(h.target.silent)return;if("mousemove"===u){var f=h.target!==r;f&&r&&Ut(r,Bt("mouseout",{target:r},a,o)),Ut(h.target,Bt("mousemove",h,a,o)),f&&Ut(h.target,Bt("mouseover",h,a,o))}else Ut(h.target,Bt(u,h,a,o,c));r=h.target}else r&&(Ut(r,Bt("mouseout",{target:r},a,o)),r=null)}})},this)},Pt.prototype._updateGraphicOptions=function(t,e,r){for(var n,i=!!t.tonemapping,a=!!t.linear,o=0;o0){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])}}}),ua=Re.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&&u<=e.COLOR_ATTACHMENT0+8&&c.push(u);l.drawBuffersEXT(c)}t.saveClear(),t.clearBit=Fe.DEPTH_BUFFER_BIT|Fe.COLOR_BUFFER_BIT,r=t.render(this.scene,this.camera,!this.autoUpdateScene,this.preZ),t.restoreClear(),n.unbind(t)}else r=t.render(this.scene,this.camera,!this.autoUpdateScene,this.preZ);this.trigger("afterrender",r),this._rendering=!1,this._rendered=!0}}),ha=sa.extend(function(){return{texture:null,outputs:{color:{}}}},function(){},{getOutput:function(t,e){return this.texture},beforeFrame:function(){},afterFrame:function(){}}),fa=sa.extend(function(){return{name:"",inputs:{},outputs:null,shader:"",inputLinks:{},outputLinks:{},pass:null,_prevOutputTextures:{},_outputTextures:{},_outputReferences:{},_rendering:!1,_rendered:!1,_compositor:null}},function(){var t=new Si({fragment:this.shader});this.pass=t},{render:function(t,e){this.trigger("beforerender",t),this._rendering=!0;var r=t.gl;for(var n in this.inputLinks){var i=this.inputLinks[n],a=i.node.getOutput(t,i.pin);this.pass.setUniform(n,a)}if(this.outputs){this.pass.outputs={};var o={};for(var s in this.outputs){var u=this.updateParameter(s,t);isNaN(u.width)&&this.updateParameter(s,t);var l=this.outputs[s],c=this._compositor.allocateTexture(u);this._outputTextures[s]=c;var h=l.attachment||r.COLOR_ATTACHMENT0;"string"==typeof h&&(h=r[h]),o[h]=c}this._compositor.getFrameBuffer().bind(t);for(var h in o)this._compositor.getFrameBuffer().attach(o[h],h);this.pass.render(t),this._compositor.getFrameBuffer().updateMipmap(t.gl)}else this.pass.outputs=null,this._compositor.getFrameBuffer().unbind(t),this.pass.render(t,e);for(var n in this.inputLinks){var i=this.inputLinks[n];i.node.removeReference(i.pin)}this._rendering=!1,this._rendered=!0,this.trigger("afterrender",t)},updateParameter:function(t,e){var r=this.outputs[t],n=r.parameters,i=r._parametersCopy;if(i||(i=r._parametersCopy={}),n)for(var a in n)"width"!==a&&"height"!==a&&(i[a]=n[a]);var o,s;return o=n.width instanceof Function?n.width.call(this,e):n.width,s=n.height instanceof Function?n.height.call(this,e):n.height,i.width===o&&i.height===s||this._outputTextures[t]&&this._outputTextures[t].dispose(e),i.width=o,i.height=s,i},setParameter:function(t,e){this.pass.setUniform(t,e)},getParameter:function(t){return this.pass.getUniform(t)},setParameters:function(t){for(var e in t)this.setParameter(e,t[e])},define:function(t,e){this.pass.material.define("fragment",t,e)},undefine:function(t){this.pass.material.undefine("fragment",t)},removeReference:function(t){if(0===--this._outputReferences[t]){this.outputs[t].keepLastFrame?(this._prevOutputTextures[t]&&this._compositor.releaseTexture(this._prevOutputTextures[t]),this._prevOutputTextures[t]=this._outputTextures[t]):this._compositor.releaseTexture(this._outputTextures[t])}},clear:function(){sa.prototype.clear.call(this),this.pass.material.disableTexturesAll()}});$.import("@export clay.compositor.coloradjust\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\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;\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord);\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 gl_FragColor = vec4(color, tex.a);\n}\n@end\n@export clay.compositor.brightness\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\nuniform float brightness : 0.0;\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@export clay.compositor.contrast\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\nuniform float contrast : 1.0;\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@export clay.compositor.exposure\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\nuniform float exposure : 0.0;\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@export clay.compositor.gamma\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\nuniform float gamma : 1.0;\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@export clay.compositor.saturation\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\nuniform float saturation : 1.0;\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\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"),$.import("@export clay.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@export clay.compositor.kernel.gaussian_13\nfloat gaussianKernel[13];\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@end\n@export clay.compositor.gaussian_blur\n#define SHADER_NAME gaussian_blur\nuniform sampler2D texture;varying vec2 v_Texcoord;\nuniform float blurSize : 2.0;\nuniform vec2 textureSize : [512.0, 512.0];\nuniform float blurDir : 0.0;\n@import clay.util.rgbm\n@import clay.util.clamp_sample\nvoid main (void)\n{\n @import clay.compositor.kernel.gaussian_9\n vec2 off = blurSize / textureSize;\n off *= vec2(1.0 - blurDir, blurDir);\n vec4 sum = vec4(0.0);\n float weightAll = 0.0;\n for (int i = 0; i < 9; i++) {\n float w = gaussianKernel[i];\n vec4 texel = decodeHDR(clampSample(texture, v_Texcoord + float(i - 4) * off));\n sum += texel * w;\n weightAll += w;\n }\n gl_FragColor = encodeHDR(sum / max(weightAll, 0.01));\n}\n@end\n"),$.import("@export clay.compositor.hdr.log_lum\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\n@import clay.util.rgbm\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 gl_FragColor = encodeHDR(vec4(vec3(luminance), 1.0));\n}\n@end\n@export clay.compositor.hdr.lum_adaption\nvarying vec2 v_Texcoord;\nuniform sampler2D adaptedLum;\nuniform sampler2D currentLum;\nuniform float frameTime : 0.02;\n@import clay.util.rgbm\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 fAdaptedLum += (fCurrentLum - fAdaptedLum) * (1.0 - pow(0.98, 30.0 * frameTime));\n gl_FragColor = encodeHDR(vec4(vec3(fAdaptedLum), 1.0));\n}\n@end\n@export clay.compositor.lum\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\nconst vec3 w = vec3(0.2125, 0.7154, 0.0721);\nvoid main()\n{\n vec4 tex = texture2D( texture, v_Texcoord );\n float luminance = dot(tex.rgb, w);\n gl_FragColor = vec4(vec3(luminance), 1.0);\n}\n@end"), +$.import("\n@export clay.compositor.lut\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\nuniform sampler2D lookup;\nvoid main()\n{\n vec4 tex = texture2D(texture, v_Texcoord);\n float blueColor = tex.b * 63.0;\n vec2 quad1;\n quad1.y = floor(floor(blueColor) / 8.0);\n quad1.x = floor(blueColor) - (quad1.y * 8.0);\n vec2 quad2;\n quad2.y = floor(ceil(blueColor) / 8.0);\n quad2.x = ceil(blueColor) - (quad2.y * 8.0);\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 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 vec4 newColor1 = texture2D(lookup, texPos1);\n vec4 newColor2 = texture2D(lookup, texPos2);\n vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n gl_FragColor = vec4(newColor.rgb, tex.w);\n}\n@end"),$.import("@export clay.compositor.vignette\n#define OUTPUT_ALPHA\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\nuniform float darkness: 1;\nuniform float offset: 1;\n@import clay.util.rgbm\nvoid main()\n{\n vec4 texel = decodeHDR(texture2D(texture, v_Texcoord));\n gl_FragColor.rgb = texel.rgb;\n vec2 uv = (v_Texcoord - vec2(0.5)) * vec2(offset);\n gl_FragColor = encodeHDR(vec4(mix(texel.rgb, vec3(1.0 - darkness), dot(uv, uv)), texel.a));\n}\n@end"),$.import("@export clay.compositor.output\n#define OUTPUT_ALPHA\nvarying vec2 v_Texcoord;\nuniform sampler2D texture;\n@import clay.util.rgbm\nvoid main()\n{\n vec4 tex = decodeHDR(texture2D(texture, v_Texcoord));\n gl_FragColor.rgb = tex.rgb;\n#ifdef OUTPUT_ALPHA\n gl_FragColor.a = tex.a;\n#else\n gl_FragColor.a = 1.0;\n#endif\n gl_FragColor = encodeHDR(gl_FragColor);\n#ifdef PREMULTIPLY_ALPHA\n gl_FragColor.rgb *= gl_FragColor.a;\n#endif\n}\n@end"),$.import("@export clay.compositor.bright\nuniform sampler2D texture;\nuniform float threshold : 1;\nuniform float scale : 1.0;\nuniform vec2 textureSize: [512, 512];\nvarying vec2 v_Texcoord;\nconst vec3 lumWeight = vec3(0.2125, 0.7154, 0.0721);\n@import clay.util.rgbm\nvec4 median(vec4 a, vec4 b, vec4 c)\n{\n return a + b + c - min(min(a, b), c) - max(max(a, b), c);\n}\nvoid main()\n{\n vec4 texel = decodeHDR(texture2D(texture, v_Texcoord));\n#ifdef ANTI_FLICKER\n vec3 d = 1.0 / textureSize.xyx * vec3(1.0, 1.0, 0.0);\n vec4 s1 = decodeHDR(texture2D(texture, v_Texcoord - d.xz));\n vec4 s2 = decodeHDR(texture2D(texture, v_Texcoord + d.xz));\n vec4 s3 = decodeHDR(texture2D(texture, v_Texcoord - d.zy));\n vec4 s4 = decodeHDR(texture2D(texture, v_Texcoord + d.zy));\n texel = median(median(texel, s1, s2), s3, s4);\n#endif\n float lum = dot(texel.rgb , lumWeight);\n vec4 color;\n if (lum > threshold && texel.a > 0.0)\n {\n color = vec4(texel.rgb * scale, texel.a * scale);\n }\n else\n {\n color = vec4(0.0);\n }\n gl_FragColor = encodeHDR(color);\n}\n@end\n"),$.import("@export clay.compositor.downsample\nuniform sampler2D texture;\nuniform vec2 textureSize : [512, 512];\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\nfloat brightness(vec3 c)\n{\n return max(max(c.r, c.g), c.b);\n}\n@import clay.util.clamp_sample\nvoid main()\n{\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\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 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 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 gl_FragColor = encodeHDR(color);\n}\n@end"),$.import("\n@export clay.compositor.upsample\n#define HIGH_QUALITY\nuniform sampler2D texture;\nuniform vec2 textureSize : [512, 512];\nuniform float sampleScale: 0.5;\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\n@import clay.util.clamp_sample\nvoid main()\n{\n#ifdef HIGH_QUALITY\n vec4 d = vec4(1.0, 1.0, -1.0, 0.0) / textureSize.xyxy * sampleScale;\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 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 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 gl_FragColor = encodeHDR(s / 16.0);\n#else\n vec4 d = vec4(-1.0, -1.0, +1.0, +1.0) / textureSize.xyxy;\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 gl_FragColor = encodeHDR(s / 4.0);\n#endif\n}\n@end"),$.import("@export clay.compositor.hdr.composite\n#define TONEMAPPING\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#ifdef LUM_ENABLED\nuniform sampler2D lum;\n#endif\n#ifdef LUT_ENABLED\nuniform sampler2D lut;\n#endif\n#ifdef COLOR_CORRECTION\nuniform float brightness : 0.0;\nuniform float contrast : 1.0;\nuniform float saturation : 1.0;\n#endif\n#ifdef VIGNETTE\nuniform float vignetteDarkness: 1.0;\nuniform float vignetteOffset: 1.0;\n#endif\nuniform float exposure : 1.0;\nuniform float bloomIntensity : 0.25;\nuniform float lensflareIntensity : 1;\nvarying vec2 v_Texcoord;\n@import clay.util.srgb\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}\nfloat eyeAdaption(float fLum)\n{\n return mix(0.2, fLum, 0.5);\n}\n#ifdef LUT_ENABLED\nvec3 lutTransform(vec3 color) {\n float blueColor = color.b * 63.0;\n vec2 quad1;\n quad1.y = floor(floor(blueColor) / 8.0);\n quad1.x = floor(blueColor) - (quad1.y * 8.0);\n vec2 quad2;\n quad2.y = floor(ceil(blueColor) / 8.0);\n quad2.x = ceil(blueColor) - (quad2.y * 8.0);\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 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 vec4 newColor1 = texture2D(lut, texPos1);\n vec4 newColor2 = texture2D(lut, texPos2);\n vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n return newColor.rgb;\n}\n#endif\n@import clay.util.rgbm\nvoid main()\n{\n vec4 texel = vec4(0.0);\n vec4 originalTexel = vec4(0.0);\n#ifdef TEXTURE_ENABLED\n texel = decodeHDR(texture2D(texture, v_Texcoord));\n originalTexel = texel;\n#endif\n#ifdef BLOOM_ENABLED\n vec4 bloomTexel = decodeHDR(texture2D(bloom, v_Texcoord));\n texel.rgb += bloomTexel.rgb * bloomIntensity;\n texel.a += bloomTexel.a * bloomIntensity;\n#endif\n#ifdef LENSFLARE_ENABLED\n texel += decodeHDR(texture2D(lensflare, v_Texcoord)) * texture2D(lensdirt, v_Texcoord) * lensflareIntensity;\n#endif\n texel.a = min(texel.a, 1.0);\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#ifdef TONEMAPPING\n texel.rgb *= exposureBias;\n texel.rgb = ACESToneMapping(texel.rgb);\n#endif\n texel = linearTosRGB(texel);\n#ifdef LUT_ENABLED\n texel.rgb = lutTransform(clamp(texel.rgb,vec3(0.0),vec3(1.0)));\n#endif\n#ifdef COLOR_CORRECTION\n texel.rgb = clamp(texel.rgb + vec3(brightness), 0.0, 1.0);\n texel.rgb = clamp((texel.rgb - vec3(0.5))*contrast+vec3(0.5), 0.0, 1.0);\n float lum = dot(texel.rgb, vec3(0.2125, 0.7154, 0.0721));\n texel.rgb = mix(vec3(lum), texel.rgb, saturation);\n#endif\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 gl_FragColor = encodeHDR(texel);\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)) * bloomIntensity);\n #elif DEBUG == 3\n gl_FragColor = encodeHDR(decodeHDR(texture2D(lensflare, v_Texcoord) * lensflareIntensity));\n #endif\n#endif\n if (originalTexel.a <= 0.01 && gl_FragColor.a > 1e-5) {\n gl_FragColor.a = dot(gl_FragColor.rgb, vec3(0.2125, 0.7154, 0.0721));\n }\n#ifdef PREMULTIPLY_ALPHA\n gl_FragColor.rgb *= gl_FragColor.a;\n#endif\n}\n@end"),$.import("@export clay.compositor.dof.coc\nuniform sampler2D depth;\nuniform float zNear: 0.1;\nuniform float zFar: 2000;\nuniform float focalDist: 3;\nuniform float focalRange: 1;\nuniform float focalLength: 30;\nuniform float fstop: 2.8;\nvarying vec2 v_Texcoord;\n@import clay.util.encode_float\nvoid main()\n{\n float z = texture2D(depth, v_Texcoord).r * 2.0 - 1.0;\n float dist = 2.0 * zNear * zFar / (zFar + zNear - z * (zFar - zNear));\n float aperture = focalLength / fstop;\n float coc;\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 coc = abs(aperture * (focalLength * (dist - focalAdjusted)) / (dist * (focalAdjusted - focalLength)));\n coc = clamp(coc, 0.0, 0.4) / 0.4000001;\n if (dist < lower) {\n coc = -coc;\n }\n coc = coc * 0.5 + 0.5;\n }\n gl_FragColor = encodeFloat(coc);\n}\n@end\n@export clay.compositor.dof.premultiply\nuniform sampler2D texture;\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\n@import clay.util.decode_float\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@export clay.compositor.dof.min_coc\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform vec2 textureSize : [512.0, 512.0];\n@import clay.util.float\nvoid main()\n{\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\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 gl_FragColor = encodeFloat(fCoc);\n}\n@end\n@export clay.compositor.dof.max_coc\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform vec2 textureSize : [512.0, 512.0];\n@import clay.util.float\nvoid main()\n{\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\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 gl_FragColor = encodeFloat(fCoc);\n}\n@end\n@export clay.compositor.dof.coc_upsample\n#define HIGH_QUALITY\nuniform sampler2D coc;\nuniform vec2 textureSize : [512, 512];\nuniform float sampleScale: 0.5;\nvarying vec2 v_Texcoord;\n@import clay.util.float\nvoid main()\n{\n#ifdef HIGH_QUALITY\n vec4 d = vec4(1.0, 1.0, -1.0, 0.0) / textureSize.xyxy * sampleScale;\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 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 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 gl_FragColor = encodeFloat(s / 16.0);\n#else\n vec4 d = vec4(-1.0, -1.0, +1.0, +1.0) / textureSize.xyxy;\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 gl_FragColor = encodeFloat(s / 4.0);\n#endif\n}\n@end\n@export clay.compositor.dof.upsample\n#define HIGH_QUALITY\nuniform sampler2D coc;\nuniform sampler2D texture;\nuniform vec2 textureSize : [512, 512];\nuniform float sampleScale: 0.5;\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\n@import clay.util.decode_float\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}\nvoid main()\n{\n#ifdef HIGH_QUALITY\n vec4 d = vec4(1.0, 1.0, -1.0, 0.0) / textureSize.xyxy * sampleScale;\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 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 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 gl_FragColor = encodeHDR(color / w);\n#else\n vec4 d = vec4(-1.0, -1.0, +1.0, +1.0) / textureSize.xyxy;\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 gl_FragColor = encodeHDR(color / w);\n#endif\n}\n@end\n@export clay.compositor.dof.downsample\nuniform sampler2D texture;\nuniform sampler2D coc;\nuniform vec2 textureSize : [512, 512];\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\n@import clay.util.decode_float\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}\nvoid main()\n{\n vec4 d = vec4(-1.0, -1.0, 1.0, 1.0) / textureSize.xyxy;\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 gl_FragColor = encodeHDR(color);\n}\n@end\n@export clay.compositor.dof.hexagonal_blur_frag\n@import clay.util.float\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 float weightSum = 0.0;\n float kernelWeight = 1.0 / float(KERNEL_SIZE);\n for (int i = 0; i < KERNEL_SIZE; i++) {\n vec2 coord = v_Texcoord + offset * float(i);\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 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@end\n@export clay.compositor.dof.hexagonal_blur_1\n#define KERNEL_SIZE 5\nuniform sampler2D texture;\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform float blurSize : 1.0;\nuniform vec2 textureSize : [512.0, 512.0];\n@import clay.util.rgbm\n@import clay.compositor.dof.hexagonal_blur_frag\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 gl_FragColor = doBlur(texture, vec2(0.0, offset.y));\n#if !defined(BLUR_COC)\n gl_FragColor = encodeHDR(gl_FragColor);\n#endif\n}\n@end\n@export clay.compositor.dof.hexagonal_blur_2\n#define KERNEL_SIZE 5\nuniform sampler2D texture;\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform float blurSize : 1.0;\nuniform vec2 textureSize : [512.0, 512.0];\n@import clay.util.rgbm\n@import clay.compositor.dof.hexagonal_blur_frag\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 offset.y /= 2.0;\n gl_FragColor = doBlur(texture, -offset);\n#if !defined(BLUR_COC)\n gl_FragColor = encodeHDR(gl_FragColor);\n#endif\n}\n@end\n@export clay.compositor.dof.hexagonal_blur_3\n#define KERNEL_SIZE 5\nuniform sampler2D texture1;\nuniform sampler2D texture2;\nuniform sampler2D coc;\nvarying vec2 v_Texcoord;\nuniform float blurSize : 1.0;\nuniform vec2 textureSize : [512.0, 512.0];\n@import clay.util.rgbm\n@import clay.compositor.dof.hexagonal_blur_frag\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 offset.y /= 2.0;\n vec2 vDownRight = vec2(offset.x, -offset.y);\n vec4 texel1 = doBlur(texture1, -offset);\n vec4 texel2 = doBlur(texture1, vDownRight);\n vec4 texel3 = doBlur(texture2, vDownRight);\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#else\n vec4 color = (texel1 + texel2 + texel3) / 3.0;\n gl_FragColor = encodeHDR(color);\n#endif\n}\n@end\n@export clay.compositor.dof.composite\n#define DEBUG 0\nuniform sampler2D original;\nuniform sampler2D blurred;\nuniform sampler2D nearfield;\nuniform sampler2D coc;\nuniform sampler2D nearcoc;\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\n@import clay.util.float\nvoid main()\n{\n vec4 blurredColor = decodeHDR(texture2D(blurred, v_Texcoord));\n vec4 originalColor = decodeHDR(texture2D(original, v_Texcoord));\n float fCoc = decodeFloat(texture2D(coc, v_Texcoord));\n fCoc = abs(fCoc * 2.0 - 1.0);\n float weight = smoothstep(0.0, 1.0, fCoc);\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 gl_FragColor = encodeHDR(\n mix(\n nearfieldColor, mix(originalColor, blurredColor, weight),\n pow(1.0 - fNearCoc, 4.0)\n )\n );\n#else\n gl_FragColor = encodeHDR(mix(originalColor, blurredColor, weight));\n#endif\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@end"),$.import("@export clay.compositor.lensflare\n#define SAMPLE_NUMBER 8\nuniform sampler2D texture;\nuniform sampler2D lenscolor;\nuniform vec2 textureSize : [512, 512];\nuniform float dispersal : 0.3;\nuniform float haloWidth : 0.4;\nuniform float distortion : 1.0;\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\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}\nvoid main()\n{\n vec2 texcoord = -v_Texcoord + vec2(1.0); vec2 textureOffset = 1.0 / textureSize;\n vec2 ghostVec = (vec2(0.5) - texcoord) * dispersal;\n vec2 haloVec = normalize(ghostVec) * haloWidth;\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 float weight = length(vec2(0.5) - offset) / length(vec2(0.5));\n weight = pow(1.0 - weight, 10.0);\n result += textureDistorted(offset, normalize(ghostVec), distortion) * weight;\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 gl_FragColor = result;\n}\n@end"),$.import("@export clay.compositor.blend\n#define SHADER_NAME 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\nvarying vec2 v_Texcoord;\n@import clay.util.rgbm\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 gl_FragColor = encodeHDR(tex);\n}\n@end"),$.import("@export clay.compositor.fxaa\nuniform sampler2D texture;\nuniform vec4 viewport : VIEWPORT;\nvarying vec2 v_Texcoord;\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@import clay.util.rgbm\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 vec3 luma = vec3( 0.299, 0.587, 0.114 );\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 vec2 dir;\n dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n float dirReduce = max( ( lumaNW + lumaNE + lumaSW + lumaSE ) * ( 0.25 * FXAA_REDUCE_MUL ), FXAA_REDUCE_MIN );\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 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 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 float lumaB = dot( rgbB, luma );\n if ( ( lumaB < lumaMin ) || ( lumaB > lumaMax ) )\n {\n gl_FragColor = vec4( rgbA, opacity );\n }\n else {\n gl_FragColor = vec4( rgbB, opacity );\n }\n}\n@end");var da=/^#source\((.*?)\)/;$.import("@export clay.deferred.gbuffer.vertex\nuniform mat4 worldViewProjection : WORLDVIEWPROJECTION;\nuniform mat4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\nuniform mat4 world : WORLD;\nuniform vec2 uvRepeat;\nuniform vec2 uvOffset;\nattribute vec3 position : POSITION;\nattribute vec2 texcoord : TEXCOORD_0;\n#ifdef FIRST_PASS\nattribute vec3 normal : NORMAL;\n#endif\n@import clay.chunk.skinning_header\n#ifdef FIRST_PASS\nvarying vec3 v_Normal;\nattribute vec4 tangent : TANGENT;\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\nvarying vec3 v_WorldPosition;\n#endif\nvarying vec2 v_Texcoord;\nvoid main()\n{\n vec3 skinnedPosition = position;\n#ifdef FIRST_PASS\n vec3 skinnedNormal = normal;\n vec3 skinnedTangent = tangent.xyz;\n bool hasTangent = dot(tangent, tangent) > 0.0;\n#endif\n#ifdef SKINNING\n @import clay.chunk.skin_matrix\n skinnedPosition = (skinMatrixWS * vec4(position, 1.0)).xyz;\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 gl_Position = worldViewProjection * vec4(skinnedPosition, 1.0);\n v_Texcoord = texcoord * uvRepeat + uvOffset;\n#ifdef FIRST_PASS\n v_Normal = normalize((worldInverseTranspose * vec4(skinnedNormal, 0.0)).xyz);\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 v_WorldPosition = (world * vec4(skinnedPosition, 1.0)).xyz;\n#endif\n}\n@end\n@export clay.deferred.gbuffer1.fragment\nuniform mat4 viewInverse : VIEWINVERSE;\nuniform float glossiness;\nvarying vec2 v_Texcoord;\nvarying vec3 v_Normal;\nvarying vec3 v_WorldPosition;\nuniform sampler2D normalMap;\nuniform sampler2D diffuseMap;\nvarying vec3 v_Tangent;\nvarying vec3 v_Bitangent;\nuniform sampler2D roughGlossMap;\nuniform bool useRoughGlossMap;\nuniform bool useRoughness;\nuniform bool doubleSided;\nuniform float alphaCutoff: 0.0;\nuniform float alpha: 1.0;\nuniform int roughGlossChannel: 0;\nfloat indexingTexel(in vec4 texel, in int idx) {\n if (idx == 3) return texel.a;\n else if (idx == 1) return texel.g;\n else if (idx == 2) return texel.b;\n else return texel.r;\n}\nvoid main()\n{\n vec3 N = v_Normal;\n if (doubleSided) {\n vec3 eyePos = viewInverse[3].xyz;\n vec3 V = eyePos - v_WorldPosition;\n if (dot(N, V) < 0.0) {\n N = -N;\n }\n }\n if (alphaCutoff > 0.0) {\n float a = texture2D(diffuseMap, v_Texcoord).a * alpha;\n if (a < alphaCutoff) {\n discard;\n }\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 gl_FragColor.rgb = (N + 1.0) * 0.5;\n float g = glossiness;\n if (useRoughGlossMap) {\n float g2 = indexingTexel(texture2D(roughGlossMap, v_Texcoord), roughGlossChannel);\n if (useRoughness) {\n g2 = 1.0 - g2;\n }\n g = clamp(g2 + (g - 0.5) * 2.0, 0.0, 1.0);\n }\n gl_FragColor.a = g + 0.005;\n}\n@end\n@export clay.deferred.gbuffer2.fragment\nuniform sampler2D diffuseMap;\nuniform sampler2D metalnessMap;\nuniform vec3 color;\nuniform float metalness;\nuniform bool useMetalnessMap;\nuniform bool linear;\nuniform float alphaCutoff: 0.0;\nuniform float alpha: 1.0;\nvarying vec2 v_Texcoord;\n@import clay.util.srgb\nvoid main ()\n{\n float m = metalness;\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 if (alphaCutoff > 0.0) {\n float a = texel.a * alpha;\n if (a < alphaCutoff) {\n discard;\n }\n }\n gl_FragColor.rgb = texel.rgb * color;\n gl_FragColor.a = m + 0.005;\n}\n@end\n@export clay.deferred.gbuffer.debug\n@import clay.deferred.chunk.light_head\nuniform int debug: 0;\nvoid main ()\n{\n @import clay.deferred.chunk.gbuffer_read\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"),$.import("@export clay.deferred.chunk.light_head\nuniform sampler2D gBufferTexture1;\nuniform sampler2D gBufferTexture2;\nuniform sampler2D gBufferTexture3;\nuniform vec2 windowSize: WINDOW_SIZE;\nuniform vec4 viewport: VIEWPORT;\nuniform mat4 viewProjectionInv;\n#ifdef DEPTH_ENCODED\n@import clay.util.decode_float\n#endif\n@end\n@export clay.deferred.chunk.gbuffer_read\n vec2 uv = gl_FragCoord.xy / windowSize;\n vec2 uv2 = (gl_FragCoord.xy - viewport.xy) / viewport.zw;\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 float glossiness = texel1.a;\n float metalness = texel3.a;\n vec3 N = texel1.rgb * 2.0 - 1.0;\n float z = texture2D(gBufferTexture2, uv).r * 2.0 - 1.0;\n vec2 xy = uv2 * 2.0 - 1.0;\n vec4 projectedPos = vec4(xy, z, 1.0);\n vec4 p4 = viewProjectionInv * projectedPos;\n vec3 position = p4.xyz / p4.w;\n vec3 albedo = texel3.rgb;\n vec3 diffuseColor = albedo * (1.0 - metalness);\n vec3 specularColor = mix(vec3(0.04), albedo, metalness);\n@end\n@export clay.deferred.chunk.light_equation\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}\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}\nvec3 F_Schlick(in float ndv, vec3 spec) {\n return spec + (1.0 - spec) * pow(1.0 - ndv, 5.0);\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@end");var pa=Re.extend(function(){return{enableTargetTexture1:!0,enableTargetTexture2:!0,enableTargetTexture3:!0,renderTransparent:!1,_gBufferRenderList:[],_gBufferTex1:new yr({minFilter:_r.NEAREST,magFilter:_r.NEAREST,type:_r.HALF_FLOAT}),_gBufferTex2:new yr({minFilter:_r.NEAREST,magFilter:_r.NEAREST,format:_r.DEPTH_STENCIL,type:_r.UNSIGNED_INT_24_8_WEBGL}),_gBufferTex3:new yr({minFilter:_r.NEAREST,magFilter:_r.NEAREST}),_defaultNormalMap:new yr({image:Zt("#000")}),_defaultRoughnessMap:new yr({image:Zt("#fff")}),_defaultMetalnessMap:new yr({ +image:Zt("#fff")}),_defaultDiffuseMap:new yr({image:Zt("#fff")}),_frameBuffer:new Ti,_gBufferMaterial1:new pr({shader:new $($.source("clay.deferred.gbuffer.vertex"),$.source("clay.deferred.gbuffer1.fragment")),vertexDefines:{FIRST_PASS:null},fragmentDefines:{FIRST_PASS:null}}),_gBufferMaterial2:new pr({shader:new $($.source("clay.deferred.gbuffer.vertex"),$.source("clay.deferred.gbuffer2.fragment"))}),_debugPass:new Si({fragment:$.source("clay.deferred.gbuffer.debug")})}},{resize:function(t,e){this._gBufferTex1.width===t&&this._gBufferTex1.height===e||(this._gBufferTex1.width=t,this._gBufferTex1.height=e,this._gBufferTex2.width=t,this._gBufferTex2.height=e,this._gBufferTex3.width=t,this._gBufferTex3.height=e)},setViewport:function(t,e,r,n,i){var a;a="object"==typeof t?t:{x:t,y:e,width:r,height:n,devicePixelRatio:i||1},this._frameBuffer.viewport=a},getViewport:function(){return this._frameBuffer.viewport?this._frameBuffer.viewport:{x:0,y:0,width:this._gBufferTex1.width,height:this._gBufferTex1.height,devicePixelRatio:1}},update:function(t,e,r){for(var n=t.gl,i=this._frameBuffer,a=i.viewport,o=e.updateRenderList(r,!0),s=o.opaque,u=o.transparent,l=0,c=this._gBufferRenderList,h=0;h= 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 gl_FragColor.rgb *= shadowContrib;\n#endif\n gl_FragColor.a = 1.0;\n}\n@end\n"),$.import("@export clay.deferred.ambient_light\nuniform sampler2D gBufferTexture1;\nuniform sampler2D gBufferTexture3;\nuniform vec3 lightColor;\nuniform vec2 windowSize: WINDOW_SIZE;\nvoid main()\n{\n vec2 uv = gl_FragCoord.xy / windowSize;\n vec4 texel1 = texture2D(gBufferTexture1, uv);\n if (dot(texel1.rgb, vec3(1.0)) == 0.0) {\n discard;\n }\n vec3 albedo = texture2D(gBufferTexture3, uv).rgb;\n gl_FragColor.rgb = lightColor * albedo;\n gl_FragColor.a = 1.0;\n}\n@end"),$.import("@export clay.deferred.ambient_sh_light\nuniform sampler2D gBufferTexture1;\nuniform sampler2D gBufferTexture3;\nuniform vec3 lightColor;\nuniform vec3 lightCoefficients[9];\nuniform vec2 windowSize: WINDOW_SIZE;\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}\nvoid main()\n{\n vec2 uv = gl_FragCoord.xy / windowSize;\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"),$.import("@export clay.deferred.ambient_cubemap_light\n@import clay.deferred.chunk.light_head\nuniform vec3 lightColor;\nuniform samplerCube lightCubemap;\nuniform sampler2D brdfLookup;\nuniform vec3 eyePosition;\n@import clay.util.rgbm\nvoid main()\n{\n @import clay.deferred.chunk.gbuffer_read\n vec3 V = normalize(eyePosition - position);\n vec3 L = reflect(-V, 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 vec3 envTexel = RGBMDecode(textureCubeLodEXT(lightCubemap, L, bias), 8.12);\n gl_FragColor.rgb = lightColor * envTexel * envWeight;\n gl_FragColor.a = 1.0;\n}\n@end"),$.import("@export clay.deferred.point_light\n@import clay.deferred.chunk.light_head\n@import clay.util.calculate_attenuation\n@import clay.deferred.chunk.light_equation\nuniform vec3 lightPosition;\nuniform vec3 lightColor;\nuniform float lightRange;\nuniform vec3 eyePosition;\n#ifdef SHADOWMAP_ENABLED\nuniform samplerCube lightShadowMap;\nuniform float lightShadowMapSize;\n#endif\nvarying vec3 v_Position;\n@import clay.plugin.shadow_map_common\nvoid main()\n{\n @import clay.deferred.chunk.gbuffer_read\n vec3 L = lightPosition - position;\n vec3 V = normalize(eyePosition - position);\n float dist = length(L);\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 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#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 gl_FragColor.a = 1.0;\n}\n@end"),$.import("@export clay.deferred.sphere_light\n@import clay.deferred.chunk.light_head\n@import clay.util.calculate_attenuation\n@import clay.deferred.chunk.light_equation\nuniform vec3 lightPosition;\nuniform vec3 lightColor;\nuniform float lightRange;\nuniform float lightRadius;\nuniform vec3 eyePosition;\nvarying vec3 v_Position;\nvoid main()\n{\n @import clay.deferred.chunk.gbuffer_read\n vec3 L = lightPosition - position;\n vec3 V = normalize(eyePosition - position);\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 L = normalize(L);\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 float attenuation = lightAttenuation(dist, lightRange);\n gl_FragColor.rgb = lightColor * ndl * attenuation;\n glossiness = clamp(glossiness - lightRadius / 2.0 / dist, 0.0, 1.0);\n gl_FragColor.rgb = attenuation * lightEquation(\n lightColor, diffuseColor, specularColor, ndl, ndh, ndv, glossiness\n );\n gl_FragColor.a = 1.0;\n}\n@end"),$.import("@export clay.deferred.tube_light\n@import clay.deferred.chunk.light_head\n@import clay.util.calculate_attenuation\n@import clay.deferred.chunk.light_equation\nuniform vec3 lightPosition;\nuniform vec3 lightColor;\nuniform float lightRange;\nuniform vec3 lightExtend;\nuniform vec3 eyePosition;\nvarying vec3 v_Position;\nvoid main()\n{\n @import clay.deferred.chunk.gbuffer_read\n vec3 L = lightPosition - position;\n vec3 V = normalize(eyePosition - position);\n vec3 R = reflect(V, N);\n vec3 L0 = lightPosition - lightExtend - position;\n vec3 L1 = lightPosition + lightExtend - position;\n vec3 LD = L1 - L0;\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 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 vec3 H = normalize(L + V);\n float ndh = clamp(dot(N, H), 0.0, 1.0);\n float ndv = clamp(dot(N, V), 0.0, 1.0);\n glossiness = clamp(glossiness - 0.0 / 2.0 / dist, 0.0, 1.0);\n gl_FragColor.rgb = lightColor * irra * lightAttenuation(dist, lightRange)\n * (diffuseColor + D_Phong(glossiness, ndh) * F_Schlick(ndv, specularColor));\n gl_FragColor.a = 1.0;\n}\n@end"),$.import(Vr);var ya=Re.extend(function(){var t=$.source("clay.compositor.vertex"),e=$.source("clay.deferred.light_volume.vertex"),r=new $(t,$.source("clay.deferred.directional_light")),n=function(t){t.blendEquation(t.FUNC_ADD),t.blendFuncSeparate(t.ONE,t.ONE,t.ONE,t.ONE)},i=function(t){return new pr({shader:t,blend:n,transparent:!0,depthMask:!1})},a=function(t){return new $(e,$.source("clay.deferred."+t))},o=new ga({capSegments:10}),s=new nr;s.rotateX(Math.PI/2).translate(new ze(0,-1,0)),o.applyTransform(s);var u=new va({capSegments:10});return s.identity().rotateZ(Math.PI/2),u.applyTransform(s),{shadowMapPass:null,autoResize:!0,_createLightPassMat:i,_gBuffer:new pa,_lightAccumFrameBuffer:new Ti,_lightAccumTex:new yr({type:_r.HALF_FLOAT,minFilter:_r.NEAREST,magFilter:_r.NEAREST}),_fullQuadPass:new Si({blendWithPrevious:!0}),_directionalLightMat:i(r),_ambientMat:i(new $(t,$.source("clay.deferred.ambient_light"))),_ambientSHMat:i(new $(t,$.source("clay.deferred.ambient_sh_light"))),_ambientCubemapMat:i(new $(t,$.source("clay.deferred.ambient_cubemap_light"))),_spotLightShader:a("spot_light"),_pointLightShader:a("point_light"),_sphereLightShader:a("sphere_light"),_tubeLightShader:a("tube_light"),_lightSphereGeo:new On({widthSegments:10,heightSegements:10}),_lightConeGeo:o,_lightCylinderGeo:u,_outputPass:new Si({fragment:$.source("clay.compositor.output")})}},{render:function(t,e,r,n){n=n||{},n.renderToTarget=n.renderToTarget||!1,n.notUpdateShadow=n.notUpdateShadow||!1,n.notUpdateScene=n.notUpdateScene||!1,n.notUpdateScene||e.update(!1,!0),e.updateLights(),r.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,r),this._accumulateLightBuffer(t,e,r,!n.notUpdateShadow),n.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,r,n,i){this._gBuffer.setViewport(t,e,r,n,i),this._lightAccumFrameBuffer.viewport=this._gBuffer.getViewport()},resize:function(t,e){this._lightAccumTex.width=t,this._lightAccumTex.height=e,this._gBuffer.resize(t,e)},_accumulateLightBuffer:function(t,e,r,n){for(var i=t.gl,a=this._lightAccumTex,o=this._lightAccumFrameBuffer,s=r.getWorldPosition().array,u=0;u0&&za.scaleAndAdd(t.array,t.array,this.force.array,n/r)}});$.import("@export clay.particle.vertex\nuniform mat4 worldView : WORLDVIEW;\nuniform mat4 projection : PROJECTION;\nattribute vec3 position : POSITION;\nattribute vec3 normal : NORMAL;\n#ifdef UV_ANIMATION\nattribute vec2 texcoord0 : TEXCOORD_0;\nattribute vec2 texcoord1 : TEXCOORD_1;\nvarying vec2 v_Uv0;\nvarying vec2 v_Uv1;\n#endif\nvarying float v_Age;\nvoid main() {\n v_Age = normal.x;\n float rotation = normal.y;\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 #ifdef UV_ANIMATION\n v_Uv0 = texcoord0;\n v_Uv1 = texcoord1;\n #endif\n}\n@end\n@export clay.particle.fragment\nuniform sampler2D sprite;\nuniform sampler2D gradient;\nuniform vec3 color : [1.0, 1.0, 1.0];\nuniform float alpha : 1.0;\nvarying float v_Age;\n#ifdef UV_ANIMATION\nvarying vec2 v_Uv0;\nvarying vec2 v_Uv1;\n#endif\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@end");var Xa=new $($.source("clay.particle.vertex"),$.source("clay.particle.fragment")),ja=zn.extend({loop:!0,oneshot:!1,duration:1,spriteAnimationTileX:1,spriteAnimationTileY:1,spriteAnimationRepeat:0,mode:zn.POINTS,ignorePicking:!0,_elapsedTime:0,_emitting:!0},function(){this.geometry=new Nn({dynamic:!0}),this.material||(this.material=new pr({shader:Xa,transparent:!0,depthMask:!1}),this.material.enableTexture("sprite")),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[r]=e[n-1],e.pop(),n--):r++}for(var r=0;r0)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;e1&&n&&n.length>1){var a=ee(n)/ee(i);!isFinite(a)&&(a=1),e.pinchScale=a;var o=re(n);return e.pinchX=o[0],e.pinchY=o[1],{type:"pinch",target:t[0].target,event:e}}}}},Ja=[[0,0],[0,1],[1,1],[1,0]],Qa=[0,1,2,2,3,0],$a=Wn.extend({camera:null,plane:null,maxGrid:0,frustumCulling:!1},function(){var t=this.geometry=new Nn({dynamic:!0});t.attributes.position.init(6),t.attributes.normal.init(6),t.attributes.texcoord0.init(6),t.indices=new Uint16Array(6),this.plane=new un},{updateGeometry:function(){var t=this._unProjectGrid();if(t){for(var e=this.geometry.attributes.position,r=this.geometry.attributes.normal,n=this.geometry.attributes.texcoord0,i=this.geometry.indices,a=0;a<6;a++){var o=Qa[a];e.set(a,t[o].array),r.set(a,this.plane.normal.array),n.set(a,Ja[o]),i[a]=a}this.geometry.dirty()}},_unProjectGrid:function(){for(var t=new un,e=[0,1,0,2,1,3,2,3,4,5,4,6,5,7,6,7,0,4,1,5,2,6,3,7],r=new ze,n=new ze,i=[],a=[],o=0;o<4;o++)a[o]=new ze(0,0);var s=new gn;return function(){t.copy(this.plane),t.applyTransform(this.camera.viewMatrix);for(var o=this.camera.frustum.vertices,u=0,l=0;l<12;l++){r.array=o[e[2*l]],n.array=o[e[2*l+1]];var c=t.intersectLine(r,n,i[u]);c&&(i[u]||(i[u]=c),u++)}if(0!==u){for(var l=0;l0},update:function(t){if(t=t||16,this._rotating){var e=("cw"===this.autoRotateDirection?1:-1)*this.autoRotateSpeed/180*Math.PI;this._phi-=e*t/1e3,this._needsUpdate=!0}else this._rotateVelocity.len()>0&&(this._needsUpdate=!0);(Math.abs(this._zoomSpeed)>.01||this._panVelocity.len()>0)&&(this._needsUpdate=!0),this._needsUpdate&&(this._updateDistance(Math.min(t,50)),this._updatePan(Math.min(t,50)),this._updateRotate(Math.min(t,50)),this._updateTransform(),this.target.update(),this.trigger("update"),this._needsUpdate=!1)},_updateRotate:function(t){var e=this._rotateVelocity;this._phi=e.y*t/20+this._phi,this._theta=e.x*t/20+this._theta,this.setAlpha(this.getAlpha()),this.setBeta(this.getBeta()),this._vectorDamping(e,this.damping),e.x=e.y=0},_updateDistance:function(t){this._setDistance(this._distance+this._zoomSpeed*t/20),this._zoomSpeed*=this.damping},_setDistance:function(t){this._distance=Math.max(Math.min(t,this.maxDistance),this.minDistance)},_updatePan:function(t){var e=this._panVelocity,r=this._distance,n=this.target,i=n.worldTransform.y,a=n.worldTransform.x;this._center.scaleAndAdd(a,-e.x*r/200).scaleAndAdd(i,-e.y*r/200),this._vectorDamping(e,0),e.x=e.y=0},_updateTransform:function(){var t=this.target,e=new ze,r=this._theta+Math.PI/2,n=this._phi+Math.PI/2,i=Math.sin(r);e.x=i*Math.cos(n),e.y=-Math.cos(r),e.z=i*Math.sin(n),t.position.copy(this._center).scaleAndAdd(e,this._distance),t.rotation.identity().rotateY(-this._phi).rotateX(-this._theta)},_startCountingStill:function(){clearTimeout(this._stillTimeout);var t=this.autoRotateAfterStill,e=this;!isNaN(t)&&t>0&&(this._stillTimeout=setTimeout(function(){e._rotating=!0},1e3*t))},_vectorDamping:function(t,e){var r=t.len();r*=e,r<1e-4&&(r=0),t.normalize().scale(r)},decomposeTransform:function(){if(this.target){this.target.updateWorldTransform();var t=this.target.worldTransform.z,e=Math.asin(t.y),r=Math.atan2(t.x,t.z);this._theta=e,this._phi=-r,this.setBeta(this.getBeta()),this.setAlpha(this.getAlpha()),this._setDistance(this.target.position.dist(this._center))}},_mouseDownHandler:function(t){if(!this._isAnimating()){var e=t.clientX,r=t.clientY;if(t.targetTouches){var n=t.targetTouches[0];e=n.clientX,r=n.clientY,this._mode="rotate",this._processGesture(t,"start")}var i=this.domElement;i.addEventListener("touchmove",this._mouseMoveHandler),i.addEventListener("touchend",this._mouseUpHandler),i.addEventListener("mousemove",this._mouseMoveHandler),i.addEventListener("mouseup",this._mouseUpHandler),i.addEventListener("mouseout",this._mouseUpHandler),0===t.button?this._mode="rotate":1===t.button?this._mode="pan":this._mode=null,this._rotateVelocity.set(0,0),this._rotating=!1,this.autoRotate&&this._startCountingStill(),this._mouseX=e,this._mouseY=r}},_mouseMoveHandler:function(t){if(!this._isAnimating()){var e,r=t.clientX,n=t.clientY;if(t.targetTouches){var i=t.targetTouches[0];r=i.clientX,n=i.clientY,e=this._processGesture(t,"change")}var a=ne(this.panSensitivity),o=ne(this.rotateSensitivity);e||("rotate"===this._mode?(this._rotateVelocity.y+=(r-this._mouseX)/this.domElement.clientWidth*2*o[0],this._rotateVelocity.x+=(n-this._mouseY)/this.domElement.clientHeight*2*o[1]):"pan"===this._mode&&(this._panVelocity.x+=(r-this._mouseX)/this.domElement.clientWidth*a[0]*400,this._panVelocity.y+=(-n+this._mouseY)/this.domElement.clientHeight*a[1]*400)),this._mouseX=r,this._mouseY=n,t.preventDefault()}},_mouseWheelHandler:function(t){if(!this._isAnimating()){var e=t.wheelDelta||-t.detail;0!==e&&this._zoomHandler(t,e>0?-1:1)}},_pinchHandler:function(t){this._isAnimating()||this._zoomHandler(t,t.pinchScale>1?-.4:.4)},_zoomHandler:function(t,e){var r=Math.max(Math.min(this._distance-this.minDistance,this.maxDistance-this._distance));this._zoomSpeed=e*Math.max(r/40*this.zoomSensitivity,.2),this._rotating=!1,this.autoRotate&&"rotate"===this._mode&&this._startCountingStill(),t.preventDefault()},_mouseUpHandler:function(t){var e=this.domElement;e.removeEventListener("touchmove",this._mouseMoveHandler),e.removeEventListener("touchend",this._mouseUpHandler),e.removeEventListener("mousemove",this._mouseMoveHandler),e.removeEventListener("mouseup",this._mouseUpHandler),e.removeEventListener("mouseout",this._mouseUpHandler),this._processGesture(t,"end")},_addAnimator:function(t){var e=this._animators;return e.push(t),t.done(function(){var r=e.indexOf(t);r>=0&&e.splice(r,1)}),t},_processGesture:function(t,e){var r=this._gestureMgr;"start"===e&&r.clear();var n=r.recognize(t,null,this.domElement);if("end"===e&&r.clear(),n){var i=n.type;t.gestureEvent=i,this._pinchHandler(n.event)}return n}});Object.defineProperty(to.prototype,"autoRotate",{get:function(){return this._autoRotate},set:function(t){this._autoRotate=t,this._rotating=t}}),Object.defineProperty(to.prototype,"target",{get:function(){return this._target},set:function(t){t&&t.target&&this.setCenter(t.target.toArray()),this._target=t,this.decomposeTransform()}});var eo=Nn.extend({dynamic:!1}),ro=me.mat4,no=me.vec3,io={merge:function(t,e){if(t.length){var r=t[0],n=r.geometry,i=r.material,a=new Nn({dynamic:!1});a.boundingBox=new Qe;for(var o=n.getEnabledAttributes(),s=0;s=65535?new Uint32Array(3*f):new Uint16Array(3*f);for(var _=0,g=0,v=n.isUseIndices(),y=0;y0;){for(var _=[],g=[],v=[],y=0,f=0;f=0&&-1===g[S]&&(y65535?new Uint32Array(3*L.triangles.length):new Uint16Array(3*L.triangles.length);var H=0;D=0;for(var f=0;f=0?N[S]:-1}D++}P.indices[H++]=M[b]}P.updateBoundingBox(),w.add(I)}for(var X=t.children(),f=0;f0&&(r.indices=ae(t.indices,e),n.push(r.indices.buffer)),r.attributes={};for(var i in t.attributes)if(t.attributes.hasOwnProperty(i)){var a=t.attributes[i];a&&a.value&&a.value.length>0&&(a=r.attributes[i]=ie(a,e),n.push(a.value.buffer))}return{data:r,buffers:n}},toGeometry:function(t){if(!t)return null;if(t.data&&t.buffers)return oo.toGeometry(t.data);if(!t.metadata||t.metadata.generator!==ao.generator)throw new Error("[util.transferable.toGeometry] the object is not generated by util.transferable.");var e={dynamic:t.dynamic,indices:t.indices};if(t.boundingBox){var r=(new ze).setArray(t.boundingBox.min),n=(new ze).setArray(t.boundingBox.max);e.boundingBox=new Qe(r,n)}var i=new Nn(e);for(var a in t.attributes)if(t.attributes.hasOwnProperty(a)){var o=t.attributes[a];i.attributes[a]=new Nn.Attribute(o.name,o.type,o.size,o.semantic),i.attributes[a].value=o.value}return i}};$.import("@export clay.vr.disorter.output.vertex\nattribute vec2 texcoord: TEXCOORD_0;\nattribute vec3 position: POSITION;\nvarying vec2 v_Texcoord;\nvoid main()\n{\n v_Texcoord = texcoord;\n gl_Position = vec4(position.xy, 0.5, 1.0);\n}\n@end\n@export clay.vr.disorter.output.fragment\nuniform sampler2D texture;\nvarying vec2 v_Texcoord;\nvoid main()\n{\n gl_FragColor = texture2D(texture, v_Texcoord);\n}\n@end");var so=Re.extend(function(){return{clearColor:[0,0,0,1],_mesh:new Wn({geometry:new Nn({dynamic:!0}),culling:!1,material:new pr({depthTest:!1,shader:new $({vertex:$.source("clay.vr.disorter.output.vertex"),fragment:$.source("clay.vr.disorter.output.fragment")})})}),_fakeCamera:new Xn}},{render:function(t,e){var r=this.clearColor,n=t.gl;n.clearColor(r[0],r[1],r[2],r[3]),n.clear(n.COLOR_BUFFER_BIT),n.disable(n.BLEND),this._mesh.material.set("texture",e),t.saveViewport(),t.setViewport(0,0,t.getWidth(),t.getHeight()),t.renderPass([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,r){var n=this._mesh.geometry.attributes.position,i=this._mesh.geometry.attributes.texcoord0;n.init(2*t*e),i.init(2*t*e);for(var a=r.getLeftEyeVisibleTanAngles(),o=r.getLeftEyeNoLensTanAngles(),s=r.getLeftEyeVisibleScreenRect(o),u=0,l=[],c=[],h=0;h<2;h++){for(var f=0;f